1. 程式人生 > 實用技巧 >資料結構與演算法———簡介

資料結構與演算法———簡介

什麼叫資料結構 ?

直白來說,就是研究資料的儲存方式。資料儲存的目的是為了方便後期對資料的再利用。因此資料在計算機儲存空間的存放,絕不是胡亂的。需要我們選擇一種好的方式來儲存資料,這也是資料結構的核心內容。
一般我們可以認為資料結構指的是一組資料的儲存結構。

演算法 ?

演算法就是操作資料的方法,即如何操作資料效率更高,更節省資源。
舉一個例子,你有一批貨物需要運走,你是找小轎車來運還是找卡車來運?這就是資料結構的範疇,選取什麼樣的結構來儲存;
至於你貨物裝車的時候是把貨物堆放在一起還是分開放這就是演算法放到範疇了,如何放置貨物更有效率更節省空間。

舉例說明:

類似儲存 1、{a,b,c} 這種型別的資料,無疑是用變數或者陣列對資料進行儲存,即:
int a=1;
char str[3]={'a','b','c'};

如果要儲存這樣一組資料:{張亮,張平,張華,張群,張晶,張磊},資料之間的關係為:張亮是張平、張華和張群的父親,同時張平還是張晶和張磊的父親。
如果使用變數或陣列儲存資料沒有問題,但是無法體現資料之間的邏輯關係,後期根本無法使用,顯然不明智。針對此類資料,資料結構中提供“樹”結構來儲存這類資料。

導航軟體,其導航功能的實現都需要大量地圖資料的支援,針對此類資料,資料結構提供了圖儲存結構,專門用於儲存這類資料。

總結:
因此,資料結構是什麼? 我認為,資料結構是一門學科,它教會我們“如何儲存具有複雜關係的資料更有助於後期對資料的再利用”。

資料結構的儲存結構:

  • 線性結構,包括陣列,單鏈表,雙鏈表,棧,佇列
  • 樹結構,包括普通樹,二叉樹,哈夫曼樹,紅黑樹等
  • 堆結構,包括二叉堆,斐波那契堆等
  • 圖結構

下面對各種資料結構做介紹:

1. 線性結構

特點是:資料元素之間存在一對一的線性關係。就像小朋友手拉手。
線性結構擁有兩種不同的儲存結構:即順序儲存結構和鏈式儲存結構。
      (1)順序儲存的線性表稱為順序表,順序表中的儲存元素是連續的,如陣列;
      (2)鏈式儲存的線性表稱為連結串列,連結串列中的儲存元素不一定是連續的,元素節點中存放資料元素以及相鄰元素的地址資訊。

* 棧:棧的操作只能在線性表的一端進行,就是我們常說的先進後出(FILO)
* 佇列:佇列的插入操作線上性表的一端進行而其他操作線上性表的另一端進行,先進先出(FIFO)
       由於線性結構存在兩種儲存結構,因此佇列和棧各存在兩個實現方式。

2. 樹結構
樹儲存結構適合儲存具有“一對多”關係的資料。

3. 堆結構
堆是一個數組,可以被看成一個近似的完全二叉樹,樹上的每一個節點對應陣列的每個元素。除了最底層外,該樹是完全充滿的,而且是從左向右填充。

4. 圖結構
圖儲存結構適合儲存具有“多對多”關係的資料。

時間複雜度和空間複雜度

演算法:解決問題的方法。同一個問題,使用不同的演算法。雖然得到的結果相同,但是耗費的時間和資源卻是不同的。
好演算法的標準: 首先必須能解決問題(稱為準確性);其次使用這個演算法的程式在任何情況下都不能崩潰(稱為健壯性)。在滿足前兩種條件下,必須考慮執行的效率。

執行效率體現在兩方面:

  • 演算法的執行時間。(稱為“時間複雜度”)
  • 執行演算法所需的記憶體空間大小。(稱為“空間複雜度”)

1. 時間複雜度的計算:

   程式由三種結構構成:順序結構、分支結構和迴圈結構。順序結構和分支結構中的每段程式碼只執行一次;迴圈結構中的程式碼的執行時間要看迴圈的次數。相比而言,迴圈結構對演算法的執行時間影響更大。所以,演算法的時間複雜度,主要看演算法中使用到的迴圈結構中程式碼迴圈的次數(稱為“頻度”)。次數越少,演算法的時間複雜度越低。

舉例說明:

a) ++x; s=0;
b) for (int i=1; i<=n; i++) { ++x; s+=x; }
c) for (int i=1; i<=n; i++) { for (int j=1; i<=n; j++) { ++x; s+=x; } }
// a 程式碼的運行了 1 次,b 程式碼的運行了 n 次,c 程式碼運行了 n*n 次。

演算法的時間複雜度的表示方式為:O(頻度),稱為大“O”記法。
   對於上邊的例子而言,a 的時間複雜度為O(1),b 的時間複雜度為O(n),c 的時間複雜度為為O(n2)。

   如果a、b、c組成一段程式,那麼演算法的時間複雜度為O(n2+n+1)。但這麼表示是不對的,還需要對n2+n+1進行簡化。
簡化的過程總結為3步:

  • 去掉執行時間中的所有加法常數。(例如 n2+n+1,直接變為 n2+n)
  • 只保留最高項。(n2+n 變成 n2)
  • 如果最高項存在但是係數不是1,去掉係數。(n2 係數為 1)
    所以,最終a、b和c合併而成的程式碼的時間複雜度為O(n2)。

常用的時間複雜度的排序:

O(1)常數階 < O(logn)對數階 < O(n)線性階 < O(n2)平方階 < O(n3)(立方階) < O(2n) (指數階)

舉例說明:

/**** 1. O(1)常數階 ****/
int i = 1;

/**** 2. O(logn)對數階 ****/
int i = 1;
while(i<n)
{
    i = i * 2;
}
// 假設迴圈x次之後,i 就大於 2 了,也就是說 2 的 x 次方等於 n,那麼 x = logn

/**** 3. O(n)線性階****/
for(i=1; i<=n; ++i)
{
   j = i;
   j++;
}

/**** 4. O(n2)平方階/O(n3)(立方階)****/
二次/三次巢狀迴圈

/**** 5. O(nlogN)線性對數階****/
for(m=1; m<n; m++)
{
    i = 1;
    while(i<n)
    {
        i = i * 2;
    }
}

2. 空間複雜度:

演算法的時間複雜度和空間複雜度是可以相互轉化的。
谷歌瀏覽器相比於其他的瀏覽器,執行速度要快。是因為它佔用了更多的記憶體空間,以空間換取了時間。

舉例說明:

int[] m = new int[n]
for(i=1; i<=n; ++i)
{
   j = i;
   j++;
}
// 第一行new了一個數組出來,這個資料佔用的大小為n,後面的程式碼,雖然有迴圈,但沒有再分配新的空間,因此,這段程式碼的空間複雜度主要看第一行即可,即 S(n) = O(n)

參考網站
http://data.biancheng.net/view/153.html
https://baijiahao.baidu.com/s?id=1669543657044278427&wfr=spider&for=pc