最近打算系統性的學習數據結構與算法這一塊的內容。在此以前其實對這一塊的內容並不陌生,大學中開過這方面的課,可是當時不知道學了有什麼用、不知道在哪用、不知道怎麼用?也就只是學的勉勉強強,哈哈😄。作了幾年開發以後才知道數據結構與算法的重要性,所以打算再系統性的學習(複習)一遍。算法
首先借鑑(抄)一段百度百科上對數據結構的定義。數組
數據結構(data structure)是帶有結構特性的數據元素的集合,它研究的是數據的邏輯結構和數據的物理結構以及它們之間的相互關係,並對這種結構定義相適應的運算,設計出相應的算法,並確保通過這些運算之後所獲得的新結構仍保持原來的結構類型。簡而言之,數據結構是相互之間存在一種或多種特定關係的數據元素的集合,即帶「結構」的數據元素的集合。「結構」就是指數據元素之間存在的關係,分爲邏輯結構和存儲結構。數據結構
從定義上能夠看到,數據結構研究的是數據的邏輯結構和數據的物理結構以及它們之間的相互關係。那麼下面就來看看什麼是邏輯結構,什麼是物理結構。函數
數據的邏輯結構,重點在這邏輯二字上。
邏輯結構,顧名思義,數據真正存儲的結構可能不是這樣的,可是咱們能夠認爲數據就是這樣存儲的。這種結構是咱們人爲賦予數據的,至於意圖,固然就是爲了讓咱們更加容易理解數據的存儲和使用。post
物理結構。物理就是指現實中的物體,在這裏指的是內存或磁盤。
數據的物理結構表明着數據在內存中的結構。物理結構有兩種:學習
int a[10];
算法複雜度分爲時間複雜度和空間複雜度
。時間複雜度
是指執行算法所須要的計算工做量;而空間複雜度
是指執行這個算法所須要的內存空間。ui
算法的時間複雜度是一個函數,它定性描述算法的運行時間。這是一個表明
算法輸入值
的字符串的長度的函數。時間複雜度經常使用大O符號表述,不包括
這個函數的低階項
和首項係數
。使用這種方式時,時間複雜度可被稱爲是漸近的,即考察輸入值大小趨近無窮時
的狀況。spa
只說概念可能會比較困惑,下面看幾個例子就能理解了。設計
printf("------%d-------\n",i);
語句一共會執行n次,且n是輸入的變量,所以這個算法的時間複雜度就能夠表示爲O(n)。void print1(int n) {
// 由於n是變量,因此這個算法的時間複雜度是O(n)
for (int i = 0; i < n; i++) {
printf("------%d-------\n", i);
}
}
複製代碼
printf("++++++%d+++++++\n",i);
語句執行的次數就是固定的100次,100是個常數,所以這個算法的時間複雜度被稱爲是常數階,用O(1)表示。void print2() {
// 由於n是一個常量100,因此這個算法的時間複雜度是O(1)
int n = 100;
for (int i = 0; i < n; i++) {
printf("++++++%d+++++++\n", i);
}
}
複製代碼
printf
語句執行的總次數爲不包括
函數的首項係數
。在這裏,2就屬於首項係數,因此這個算法的時間複雜度就能夠表示爲O(m*n)。void print3(int m, int n) {
// m和n都是變量,因此時間複雜度是O(m*n)
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
printf("-----------\n");
printf("+++++++++++\n");
}
}
}
複製代碼
x *= 2;
一共執行幾回,因而咱們只好來進行一些簡單的計算了。i
次終於不知足x < n
這個條件了,那麼咱們就能夠獲得,當循環結束時,有低階項
,因此-1那個常數階就不要了。void print4(int n) {
/* 設循環i次 則到最後一次 2的(i+1)次方 >= n 因此 i = log2 n - 1 */
int x = 2; // 1次
while (x < n) {
x *= 2; // log以2爲底n的對數 - 1次
}
// 因此時間複雜度是 O(log2 n)
}
複製代碼
printf("xxxxxxx\n");
語句一共執行了低階項
獲得了首項係數
最終獲得void print5(int n) {
for (int i = 0; i < 2n; i++) { // 2n次
for (int j = 0; j < n - 1; j++) { // n - 1次
printf("xxxxxxx\n"); // 2n(n - 1)次
}
}
// 因此時間複雜度是 O(n2)
}
複製代碼
經過上面這幾個例子,我相信你已經可以理解時間複雜度是個什麼東西了。可是仍是須要提醒一句,並非時間複雜度大的算法執行的語句的次數就必定多
,具體執行多少次是要和算法輸入n
有關的。而算法的時間複雜度
只是用來估算
當輸入的n趨於無窮時
算法執行須要時間的多少
。3d
看到這「估算」二字是否是也就理解了上面例子中爲何要忽略低階項
和首項係數
,以及第4個例子中 忽略大於直接去等了?
再借鑑一張圖來總結一下常見的時間複雜度及其專業術語吧。
空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度。
算法佔用的空間的計算方法
分析一個算法所佔用的存儲空間要從各方面綜合考慮。如對於遞歸算法來講,通常都比較簡短,算法自己所佔用的存儲空間較少,但運行時須要一個附加堆棧,從而佔用較多的臨時工做單元;若寫成非遞歸算法,通常可能比較長,算法自己佔用的存儲空間較多,但運行時將可能須要較少的存儲單元。
空間複雜度的計算方法
一個算法的空間複雜度只考慮在運行過程當中爲局部變量分配的存儲空間的大小,它包括爲參數表中形參變量分配的存儲空間和爲在函數體中定義的局部變量分配的存儲空間兩個部分。
從上面概念能夠看出,計算空間複雜度只考慮算法在運行過程當中分配的存儲空間的大小,而不考慮算法自己所佔的內存空間。
概念看的頭疼,仍是來用代碼解釋怎麼計算空間複雜度吧。
void print1(int a, float b, char c) {
int a1 = a;
float b1 = b;
char c1 = c;
printf("%d\n%f\n%c\n", a1, b1, c1);
}
複製代碼
void print2(int n) {
if (n > 0) {
int a = n - 1;
print2(a);
printf("%d\n", a);
}
else {
printf("%d\n", 0);
}
}
複製代碼
本篇文章主要記錄了兩大部分
四大邏輯結構
和兩大物理結構
。時間複雜度
和空間複雜度
及對應的計算方法
。