探索數據結構:解鎖計算世界的密碼


?? 歡迎大家來到貝蒂大講堂??

🎈🎈養成好習慣,先贊后看哦~🎈🎈

所屬專欄:數據結構與算法
貝蒂的主頁:Betty‘s blog

前言

隨著應用程序變得越來越復雜和數據越來越豐富,幾百萬、幾十億甚至幾百億的數據就會出現,而對這么大對數據進行搜索、插入或者排序等的操作就越來越慢,人們為了解決這些問題,提高對數據的管理效率,提出了一門學科即:數據結構與算法

1. 什么是數據結構

**數據結構(Data Structure)**是計算機存儲、組織數據的方式,指相互之間存在一種或多種特定關系的數據元素的集合。

下標是常見的數據結構:

名稱定義
數組(Array)數組是一種聚合數據類型,它是將具有相同類型的若干變量有序地組織在一起的集合。
鏈表(Linked List)鏈表是一種數據元素按照鏈式存儲結構進行存儲的數據結構,這種存儲結構具有在物理上存在非連續的特點。
棧(Stack)棧是一種特殊的線性表,它只能在一個表的一個固定端進行數據結點的插入和刪除操作
隊列(Queue)隊列和棧類似,也是一種特殊的線性表。和棧不同的是,隊列只允許在表的一端進行插入操作,而在另一端進行刪除操作。
樹(Tree)樹是典型的非線性結構,它是包括,2 個結點的有窮集合 K
堆(Heap)堆是一種特殊的樹形數據結構,一般討論的堆都是二叉堆。
圖(Graph)圖是另一種非線性數據結構。在圖結構中,數據結點一般稱為頂點,而邊是頂點的有序偶對

2. 什么是算法

**算法(Algorithm)😗*就是定義良好的計算過程,他取一個或一組的值為輸入,并產生出一個或一組值作為輸出。簡單來說算法就是一系列的計算步驟,用來將輸入數據轉化成輸出結果。

算法一般分為:排序,遞歸與分治,回溯,DP,貪心,搜索算法

  • 算法往往數學密切相關,就如數學題一樣,每道數學題都有不同的解法,算法也是同理。

3. 復雜度分析

3.1 算法評估

我們在進行算法分析時,常常需要完成兩個目標**。一個是找出問題的解決方法,另一個就是找到問題的最優解**。而為了找出最優解,我們就需要從兩個維度分析:

  • 時間效率:算法運行的快慢
  • 空間效率:算法所占空間的大小

3.2 評估方法

評估時間的方法主要分為兩種,一種是實驗分析法,一種是理論分析法

(1) 實驗分析法

實驗分析法簡單來說就是將不同種算法輸入同一臺電腦,統計時間的快慢。但是這種方法有兩大缺陷:

  1. 無法排查實驗自身條件與環境的條件的影響:比如同一種算法在不同配置的電腦上的運算速度可能完全不同,甚至結果完全相反。我們很難排查所有情況。
  2. 成本太高:同一種算法可能在數據少時表現不明顯,在數據多時速率較快
(2) 理論分析法

由于實驗分析法的局限性,就有人提出了一種理論測評的方法,就是漸近復雜度分析(asymptotic complexity analysis),簡稱復雜度分析

這種方法體現算法運行所需的時間(空間)資源與輸入數據大小之間的關系,能有效的反應算法的優劣。

4. 時間復雜度與空間復雜度

4.1 時間復雜度

一個算法所花費的時間與其中語句的執行次數成正比例,算法中的基本操作的執行次數,為算法的時間復雜度。

為了準確的表述一段代表所需時間,我們先假設賦值(=)與加號(+)所需時間為1ns,乘號(×)所需時間為2ns,打印所需為3ns。

讓我們計算如下代碼所需總時間:

int main()
{int i = 1;//1nsint n = 0;//1nsscanf("%d", &n);for (i = 0; i < n; i++){printf("%d ", i);//3ns}return 0;
}

計算時間如下:
T ( n ) = 1 + 1 + 3 × n = 3 n + 2 T(n)=1+1+3×n=3n+2 T(n)=1+1+3×n=3n+2

但是實際上統計每一項所需時間是不現實的,并且由于是理論分析,當n—>∞時,其余項皆可忽略,T(n)的數量級由最高階決定。所以我們計算時間復雜度時,可以簡化為兩個步驟:

  1. 忽略除最高階以外的所有項。
  2. 忽略所有系數。

而上述代碼時間可以記為O(n),這種方法被稱為大O的漸進表示法。如果計算機結果全是常數,則記為O(1)。

  • 并且計算復雜度時,有時候會出現不同情況的結果,這是應該以最壞的結果考慮。

4.2 空間復雜度

空間復雜度也是一個數學表達式,是對一個算法在運行過程中臨時占用存儲空間大小的量度 。空間復雜度的表示也遵循大O的漸進表示法

讓我們計算一下冒泡排序的空間復雜度

// 計算BubbleSort的空間復雜度?
void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}
  • 通過觀察我們可以看出,冒泡排序并沒有開辟多余的空間,所以空間復雜度為O(1).

5. 復雜度分類

算法的復雜度有幾個量級,表示如下:
O ( 1 ) < O ( l o g N ) < O ( N ) < O ( N l o g N ) < O ( N 2 ) < O ( 2 N ) < O ( N ! ) O(1) < O( log N) < O(N) < O(Nlog N) < O(N 2 ) < O(2^N) < O(N!) O(1)<O(logN)<O(N)<O(NlogN)<O(N2)<O(2N)<O(N!)

  • 從左到右復雜度依次遞增,算法的缺點也就越明顯

圖示如下:

5.1 常數O(1)階

常數階是一種非常快速的算法,但是在實際應用中非常難實現

以下是一種時間復雜度與空間復雜度皆為O(1)的算法:

int main()
{int a = 0;int b = 1;int c = a + b;printf("兩數之和為%d\n", c);return 9;
}

5.2 對數階O(logN)

對數階是一種比較快的算法,它一般每次減少一半的數據。我們常用的二分查找算法的時間復雜度就為O(logN)

二分查找如下:

int binary_search(int nums[], int size, int target) //nums是數組,size是數組的大小,target是需要查找的值
{int left = 0;int right = size - 1;	// 定義了target在左閉右閉的區間內,[left, right]while (left <= right) {	//當left == right時,區間[left, right]仍然有效int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出if (nums[middle] > target) {right = middle - 1;	//target在左區間,所以[left, middle - 1]} else if (nums[middle] < target) {left = middle + 1;	//target在右區間,所以[middle + 1, right]} else {	//既不在左邊,也不在右邊,那就是找到答案了return middle;}}//沒有找到目標值return -1;
}

空間復雜度為O(logN)的算法,一般為分治算法

比如用遞歸實現二分算法:

int binary_search(int ar[], int low, int high, int key)
{if(low > high)//查找不到return -1;int mid = (low+high)/2;if(key == ar[mid])//查找到return mid;else if(key < ar[mid])return Search(ar,low,mid-1,key);elsereturn Search(ar,mid+1,high,key);
}

每一次執行遞歸都會對應開辟一個空間,也被稱為棧幀

5.3 線性階O(N)

線性階算法,時間復雜度與空間復雜度隨著數量均勻變化。

遍歷數組或者鏈表是常見的線性階算法,以下為時間復雜度為O(N)的算法:

int main()
{int n = 0;int count = 0;scanf("%d", &n);for (int i = 0; i < n; i++){count += i;//計算0~9的和}return 0;
}

以下為空間復雜度為O(N)的算法

int main()
{int n = 0;int count = 0;scanf("%d", &n);int* p = (int*)malloc(sizeof(int) * n);//開辟大小為n的空間if (p == NULL){perror("malloc fail");return -1;}free(p);p=NULL;return 0;
}

5.4 線性對數階O(NlogN)

無論是時間復雜度還是空間復雜度,線性對數階一般出現在嵌套循環中,即一層的復雜度為O(N),另一層為O(logN)

比如說循環使用二分查找打印:

int binary_search(int nums[], int size, int target) //nums是數組,size是數組的大小,target是需要查找的值
{int left = 0;int right = size - 1;	// 定義了target在左閉右閉的區間內,[left, right]while (left <= right) {	//當left == right時,區間[left, right]仍然有效int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出if (nums[middle] > target) {right = middle - 1;	//target在左區間,所以[left, middle - 1]}else if (nums[middle] < target) {left = middle + 1;	//target在右區間,所以[middle + 1, right]}else {	//既不在左邊,也不在右邊,那就是找到答案了printf("%d ", nums[middle]);}}//沒有找到目標值return -1;
}
void func(int nums[], int size, int target)
{for (int i = 0; i < size; i++){binary_search(nums, size, target);}
}

空間復雜度為O(NlogN)的算法,最常見的莫非歸并排序

void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex){int i = startIndex, j=midIndex+1, k = startIndex;while(i!=midIndex+1 && j!=endIndex+1) {if(sourceArr[i] > sourceArr[j])tempArr[k++] = sourceArr[j++];elsetempArr[k++] = sourceArr[i++];}while(i != midIndex+1)tempArr[k++] = sourceArr[i++];while(j != endIndex+1)tempArr[k++] = sourceArr[j++];for(i=startIndex; i<=endIndex; i++)sourceArr[i] = tempArr[i];
}//內部使用遞歸
void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex) {int midIndex;if(startIndex < endIndex) {midIndex = startIndex + (endIndex-startIndex) / 2;//避免溢出intMergeSort(sourceArr, tempArr, startIndex, midIndex);MergeSort(sourceArr, tempArr, midIndex+1, endIndex);Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);}
}

5.5 平方階O(N2)

平方階與線性對數階相似,常見于嵌套循環中,每層循環的復雜度為O(N)

時間復雜度為O(N2),最常見的就是冒泡排序

void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

計算過程如下;
T ( N ) = 1 + 2 + 3 + . . . . . . + n ? 1 = ( n 2 ? n ) / 2 = O ( n 2 ) T(N)=1+2+3+......+n-1=(n^2-n)/2=O(n^2) T(N)=1+2+3+......+n?1=(n2?n)/2=O(n2)

空間復雜度為O(N2),最簡單的就是動態開辟。

{int n = 0;int count = 0;scanf("%d", &n);int* p = (int*)malloc(sizeof(int) * n*n);//開辟大小為n的空間if (p == NULL){perror("malloc fail");return -1;}free(p);p=NULL;return 0;
}

5.6 指數階O(2N)

指數階的算法效率低,并不常用。

常見的時間復雜度為O(2N)的算法就是遞歸實現斐波拉契數列:

int Fib1(int n)
{if (n == 1 || n == 2){return 1;}else{return Fib1(n - 1) + Fib1(n - 2);}
}

粗略估計
T ( n ) = 2 0 + 2 1 + 2 2 + . . . . . + 2 ( n ? 1 ) = 2 n ? 1 = O ( 2 N ) T(n)=2^0+2^1+2^2+.....+2^(n-1)=2^n-1=O(2^N) T(n)=20+21+22+.....+2(n?1)=2n?1=O(2N)

  • 值得一提的是斐波拉契的空間復雜度為O(N),因為在遞歸至最深處后往回歸的過程中,后續空間都在銷毀的空間上建立的,這樣能大大提高空間的利用率。

空間復雜度為O(2N)的算法一般與樹有關,比如建立滿二叉樹

TreeNode* buildTree(int n) {if (n == 0)return NULL;TreeNode* root = newTreeNode(0);root->left = buildTree(n - 1);root->right = buildTree(n - 1);return root;
}

5.7 階乘階O(N!)

階乘階的算法復雜度最高,幾乎不會采用該類型的算法。

這是一個時間復雜度為階乘階O(N!)的算法

int func(int n)
{if (n == 0)return 1;int count = 0;for (int i = 0; i < n; i++) {count += func(n - 1);}return count;
}

示意圖:

  • 空間復雜度為階乘階O(N!)的算法并不常見,這里就不在一一列舉。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712273.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712273.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712273.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

600萬訂單每秒Disruptor +SpringBoot,如何解決消息不丟失?

尼恩說在前面 在40歲老架構師 尼恩的讀者交流群(50)中&#xff0c;最近有小伙伴拿到了一線互聯網企業如得物、阿里、滴滴、極兔、有贊、shein 希音、百度、網易的面試資格&#xff0c;遇到很多很重要的面試題&#xff1a; Disruptor 官方說能達到每秒600w OPS訂單處理能力&…

Java——Object

1.Object萬類之祖 1.1 Object類型的概述 Object類是所有類型的頂層父類&#xff0c;所有類型的直接或者間接的父類&#xff1b;所有的類型中都含有Object類中的所有方法。 隨意定義一個類型,不手動顯式定義其父類&#xff0c;那么這個類的父類就是Object類 public Object() …

【C語言】指針初階2.0版本

這篇博文我們來繼續學習指針的其他內容 指針2.0 傳值調用與傳址調用傳值調用傳址調用 一維數組與指針理解數組名使用指針深入理解一維數組 二級指針指針數組二維數組與指針 傳值調用與傳址調用 在開始之前&#xff0c;我們需要先了解這個概念&#xff0c;后面才能夠正常的學習…

利用 Python 抓取數據探索汽車市場趨勢

一、引言 隨著全球對環境保護意識的增強和技術的進步&#xff0c;新能源汽車作為一種環保、高效的交通工具&#xff0c;正逐漸受到人們的關注和青睞。在這個背景下&#xff0c;對汽車市場的數據進行分析和研究顯得尤為重要。 本文將介紹如何利用 Python 編程語言&#xff0c;結…

VSCode上搭建C/C++開發環境(vscode配置c/c++環境)Windows系統---保姆級教程

引言勸退 VSCode&#xff0c;全稱為Visual Studio Code&#xff0c;是由微軟開發的一款輕量級&#xff0c;跨平臺的代碼編輯器。大家能來搜用VSCode配置c/c&#xff0c;想必也知道VSCode的強大&#xff0c;可以手握一個VSCode同時編寫如C&#xff0c;C&#xff0c;C#&#xff…

微服務day02-Ribbon負載均衡與Nacos安裝與入門

一.Ribbon負載均衡 在上一節中&#xff0c;我們通過在RestTemplte實例中加上了注解 LoadBalanced,表示將來由RestTemplate發起的請求會被Ribbon攔截和處理&#xff0c;實現了訪問服務時的負載均衡&#xff0c;那么他是如何實現的呢&#xff1f; 1.1 Ribbon負載均衡的原理 Rib…

鏈表的歸并排序-LeetCode(Python版)

雙指針歸并排序&#xff01;圖解排序鏈表&#xff01;-知乎 class ListNode(object):def __init__(self, val0, nextNone):self.val valself.next nextclass Solution(object):def find_mid(self, head): # 快慢指針slow, fast head, headwhile fast.next and fast.next.n…

linux 硬盤存儲剩余容量自動化監控+報警通知

linux 硬盤存儲剩余容量自動化監控報警通知 編寫shell腳本 #!/bin/bash# 獲取系統存儲大小&#xff08;單位為GB&#xff09; storage_size$(df -h / | awk NR2 {print $4} | sed s/G//)# 閾值&#xff08;小于10GB觸發報警&#xff09; threshold10# 釘釘機器人 Webhook UR…

LabVIEW非接觸式電阻抗層析成像系統

LabVIEW非接觸式電阻抗層析成像系統 非接觸式電阻抗層析成像&#xff08;NEIT&#xff09;技術以其無輻射、非接觸、響應速度快的特點&#xff0c;為實時監測提供了新的解決方案。基于LabVIEW的電阻抗層析成像系統&#xff0c;實現了數據的在線采集及實時成像&#xff0c;提高…

代碼隨想錄算法訓練營第四十四天|139.單詞拆分、56.攜帶礦石資源

139.單詞拆分 思路&#xff1a;將字符串s看作為背包容量&#xff0c;從字符串中獲取物品&#xff0c;剛好滿足背包容量的過程&#xff0c;因為可以從字符串中多次取值&#xff0c;相當于物品的數量是不限制&#xff0c;這就是一個完全背包的問題&#xff01;這個題有個關鍵點&a…

Python中的windows路徑問題

在Python中處理Windows路徑時,經常會遇到一些特殊的問題。這主要是因為Windows和大多數其他操作系統(如Linux和macOS)使用不同的路徑分隔符。在Windows中,路徑使用反斜杠(\)作為分隔符,而在其他操作系統中,路徑使用正斜杠(/)作為分隔符。 以下是在Python中處理Windo…

Java SE:多線程(Thread)

1. 線程兩個基本概念 并發&#xff1a;即線程交替運行多個指令并行&#xff1a;即多個線程同時運行指令 并發并行不矛盾&#xff0c;兩者可同時發生&#xff0c;即多個線程交替運行指令 2. 多線程3種實現方式 2.1 直接創建線程對象 /*** 方式1&#xff1a;* 1. 創建thread類的…

mybatis plus 深入學習 【Base Mapper】的方法 【IService】的方法

mybatis plus 深入學習 常見注解 1.TableName 描述&#xff1a;表名注解&#xff0c;標識實體類對應的表使用位置&#xff1a;實體類 TableName("sys_user") public class User {private Long id;private String name;private Integer age;private String email;…

【Linux系統化學習】信號的保存

目錄 阻塞信號 信號處理常見方式概覽 信號的其他相關概念 在內核中的表示 sigset_t 信號集操作函數 sigprocmask函數 sigpending函數 信號的捕捉 內核如何實現信號的捕捉 sigaction函數 可重入函數 volatile 阻塞信號 信號處理常見方式概覽 當信號來臨時&#x…

c++算法入門教程(2)

C是一種功能強大且廣泛應用的編程語言&#xff0c;對于想要深入學習編程和算法的人來說&#xff0c;掌握C是一個重要的里程碑。本文將帶你逐步了解C編程的基礎知識&#xff0c;并介紹一些常見的算法和編程技巧幫你入門c算法。 ?在c算法入門教程(1) 中&#xff0c;我講解了什么…

GEE:使用Sigmoid激活函數對單波段圖像進行變換(以NDVI為例)

作者:CSDN @ _養樂多_ 本文將介紹在 Google Earth Engine (GEE)平臺上,對任意單波段影像進行 Sigmoid 變換的代碼。并以對 NDVI 影像像素值的變換為例。 文章目錄 一、Sigmoid激活函數1.1 什么是 Sigmoid 激活函數1.2 用到遙感圖像上有什么用?二、代碼鏈接三、完整代碼一…

查詢每個會話使用內存大小(DM8達夢數據庫)

DM8達夢數據庫查詢每個會話使用內存大小 1 環境介紹2 查詢每個sql會話使用內存大小3 達夢數據庫學習使用列表 1 環境介紹 在某些環境數據庫內存增長到服務器內存用完,發生OOM事件,可以分析sql會話使用內存大小; 2 查詢每個sql會話使用內存大小 --創建SQL會話占用內存記錄表 …

共享棧的C語言實現

共享棧&#xff1a;所謂共享棧就是為了節省空間&#xff0c;讓兩個棧共享一片連續的存儲空間&#xff0c;兩個棧從這片連續的共享空間的兩端向中間擴充自己的存儲空間&#xff0c;設這片存儲空間的大小為maxSize&#xff0c;采用棧頂指針始終指向當前棧頂元素的方式來實現共享棧…

簡單認識算法的復雜度

時間復雜度與空間復雜度 1.算法的復雜度 ? 算法在編寫成可執行程序后&#xff0c;運行時需要耗費時間資源和空間(內存)資源 。因此衡量一個算法的好壞&#xff0c;一般是從時間和空間兩個維度來衡量的&#xff0c;即時間復雜度和空間復雜度。 ? 時間復雜度主要衡量一個算法…

MYSQL02高級_目錄結構、默認數據庫、表文件、系統獨立表空間

文章目錄 ①. MySQL目錄結構②. 查看默認數據庫③. MYSQL5.7和8表文件③. 系統、獨立表空間 ①. MySQL目錄結構 ①. 如何查看關聯mysql目錄 [rootmysql8 ~]# find / -name mysql /var/lib/mysql /var/lib/mysql/mysql /etc/selinux/targeted/tmp/modules/100/mysql /etc/seli…