數據結構與算法1 -- 數據結構及算法複雜度

前言

最近打算系統性的學習數據結構與算法這一塊的內容。在此以前其實對這一塊的內容並不陌生,大學中開過這方面的課,可是當時不知道學了有什麼用、不知道在哪用、不知道怎麼用?也就只是學的勉勉強強,哈哈😄。作了幾年開發以後才知道數據結構與算法的重要性,所以打算再系統性的學習(複習)一遍。算法

數據結構

首先借鑑(抄)一段百度百科上對數據結構的定義。數組

數據結構(data structure)是帶有結構特性的數據元素的集合,它研究的是數據的邏輯結構和數據的物理結構以及它們之間的相互關係,並對這種結構定義相適應的運算,設計出相應的算法,並確保通過這些運算之後所獲得的新結構仍保持原來的結構類型。簡而言之,數據結構是相互之間存在一種或多種特定關係的數據元素的集合,即帶「結構」的數據元素的集合。「結構」就是指數據元素之間存在的關係,分爲邏輯結構和存儲結構。數據結構

從定義上能夠看到,數據結構研究的是數據的邏輯結構和數據的物理結構以及它們之間的相互關係。那麼下面就來看看什麼是邏輯結構,什麼是物理結構。函數

邏輯結構

數據的邏輯結構,重點在這邏輯二字上。
邏輯結構,顧名思義,數據真正存儲的結構可能不是這樣的,可是咱們能夠認爲數據就是這樣存儲的。這種結構是咱們人爲賦予數據的,至於意圖,固然就是爲了讓咱們更加容易理解數據的存儲和使用。post

有哪些邏輯結構

  1. 集合結構。集合中任何兩個數據元素之間都沒有邏輯關係,互不干擾。1對0。
    舉例:int a = 2, b = 3;那麼a和b之間沒有任何關係,它們之間就屬於集合結構。
    集合結構
  2. 線性結構。數據元素按照順序鏈接成一大串。1對1。
    舉例:數組、鏈表、隊列等這樣線性排列的數據集合。
    線性結構
  3. 樹形結構。樹形結構具備分支、層次特性,其形態像現實中的樹,所以叫樹形結構。1對n,即1對多。
    舉例:二叉樹、B樹、B+樹等。
    樹形結構
  4. 圖形結構。圖形結構中的節點按邏輯關係互相纏繞,任何兩個節點之間均可以相互鏈接。m對n,即多對多。
    舉例:互聯網。
    圖形結構

物理結構

物理結構。物理就是指現實中的物體,在這裏指的是內存或磁盤。
數據的物理結構表明着數據在內存中的結構。物理結構有兩種:學習

  1. 順序結構。即數據在內存中佔用的內存空間是連續的。例如數組int a[10];
    順序結構
  2. 鏈式結構。即數據在內存中佔用的內存空間是不連續的。例如鏈表。
    鏈式結構

算法複雜度

算法複雜度分爲時間複雜度和空間複雜度時間複雜度是指執行算法所須要的計算工做量;而空間複雜度是指執行這個算法所須要的內存空間。ui

時間複雜度

算法的時間複雜度是一個函數,它定性描述算法的運行時間。這是一個表明算法輸入值的字符串的長度的函數。時間複雜度經常使用大O符號表述,不包括這個函數的低階項首項係數。使用這種方式時,時間複雜度可被稱爲是漸近的,即考察輸入值大小趨近無窮時的狀況。spa

只說概念可能會比較困惑,下面看幾個例子就能理解了。設計

  1. 這個算法中,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);
    }
}
複製代碼
  1. 這個算法中,由於n是一個常量100,所以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);
    }
}
複製代碼
  1. 這個算法中,m和n都是變量 並且在內層for循環 中 執行了兩句打印,所以printf語句執行的總次數爲m*n*2次。回到上面的概念中看,時間複雜度不包括函數的首項係數。在這裏,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");
        }
    }
}
複製代碼
  1. 這個算法中,咱們一眼可能看不出x *= 2;一共執行幾回,因而咱們只好來進行一些簡單的計算了。
    首先假設一共執行了 i 次終於不知足x < n這個條件了,那麼咱們就能夠獲得,當循環結束時,有2*2^i>=n,取臨界條件可得2^{i+1}=n,進而可得i = log_2^n - 1。即這個算法的時間複雜度是O(log_2^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)
}
複製代碼
  1. 這個算法中,printf("xxxxxxx\n");語句一共執行了2n(n-1)次,即2n^2-2n次,忽略低階項獲得了2n^2,再忽略首項係數最終獲得n^2,因此最終這個算法的時間複雜度就表示爲O(n^2)
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個例子中2*2^i>=n 忽略大於直接去等了?

借鑑一張圖來總結一下常見的時間複雜度及其專業術語吧。

時間複雜度
至於這些階之間的大小關係,高中數學還沒忘的一眼就能看出來吧😂
下面的a表示常數。
O(1)<O(log_a^n)<O(n)<O(nlog_a^n)<O(n^a)<O(a^n)<O(n!)<O(n^n)

空間複雜度

空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度。

算法佔用的空間的計算方法

分析一個算法所佔用的存儲空間要從各方面綜合考慮。如對於遞歸算法來講,通常都比較簡短,算法自己所佔用的存儲空間較少,但運行時須要一個附加堆棧,從而佔用較多的臨時工做單元;若寫成非遞歸算法,通常可能比較長,算法自己佔用的存儲空間較多,但運行時將可能須要較少的存儲單元。

空間複雜度的計算方法

一個算法的空間複雜度只考慮在運行過程當中爲局部變量分配的存儲空間的大小,它包括爲參數表中形參變量分配的存儲空間和爲在函數體中定義的局部變量分配的存儲空間兩個部分。

從上面概念能夠看出,計算空間複雜度只考慮算法在運行過程當中分配的存儲空間的大小,而不考慮算法自己所佔的內存空間。

概念看的頭疼,仍是來用代碼解釋怎麼計算空間複雜度吧。

  1. 這個算法中,函數的參數有a b c,局部變量有a1 b1 c1,因此總共佔用了6塊內存空間。但無論幾塊,是個常數就行,空間複雜度O(1)。
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);
}
複製代碼
  1. 這個算法中,每次遞歸都會開闢一塊空間,而且在遞歸期間這一塊空間並不會釋放,所以這個算法的空間複雜度是O(n)。
void print2(int n) {
    if (n > 0) {
        int a = n - 1;
        print2(a);
        printf("%d\n", a);
    }
    else {
        printf("%d\n", 0);
    }
}
複製代碼

總結

本篇文章主要記錄了兩大部分

  1. 數據的結構。包括四大邏輯結構兩大物理結構
  2. 算法的複雜度。包括時間複雜度空間複雜度及對應的計算方法

本文地址

相關文章
相關標籤/搜索