《資料結構與演算法分析》筆記

weixin_34208283發表於2017-09-03

一:資料結構概論

  • 在資料結構中資料分為兩種關係,一種時線性,一種是非線性
  • 線性關係,比如一張學生登記表。
  • 非線性關係,比如資料夾是樹型關係,比如計算機網路是圖關係。

資料結構包括:

  • 資料的儲存
    • 物理結構:資料在計算機內的儲存表示
  • 資料之間的關係
    • 邏輯結構:資料之間的邏輯關係。分為兩種一種是順序儲存結構,一種是非順序儲存結構。順序結構一般用一維陣列體現資料之間的關係。非順序儲存結構一般採用指標實現資料之間的關係,包括鏈式儲存結構和雜湊結構,索引儲存結構等。鏈式儲存利用指標直接表示資料元素之間的關係。雜湊結構是根據節點的關鍵字,利用雜湊函式直接計算出該節點的儲存地址。索引儲存結構是在儲存節點資訊的同時,還建立附加的索引表。索引結構分為稠密索引和稀疏索引。
  • 資料的操作。在各種結構上的演算法。

資料型別:一個值的集合以及在這些值上定義的一組操作的總稱。

演算法特徵:

  • 正確性
  • 確定性
  • 有窮性
  • 輸入
  • 輸出
    效率和低儲存量兩者通常情況下是矛盾的。

二:線性表

線性表定義:每個資料元素最多隻有一個直接前趨,每個資料元素最多隻有一個直接後繼,只有第一個資料元素沒有直接前趨,最後一個資料元素沒有直接後繼。

線性表的儲存分為順序儲存和非順序儲存。

  • 順序儲存也稱為向量儲存或一維陣列儲存。特點是邏輯關係上相鄰的兩個元素在物理位置上也相鄰,隨機存取元素簡單,插入刪除會造成大量資料移動。線性表的順序儲存的情況下插入和刪除演算法的時間複雜度為o(n);求表長以及取第i個元素的時間複雜度為o(1);

  • 線性表的鏈式儲存,不要求邏輯上相鄰的資料元素在物理位置上也相鄰。由於不要求物理位置上也相鄰,那麼每個節點物件包含兩個元素,一個是當前值,一個是指向下一個節點的地址。如果插入某個值,那麼將插入的值的節點指向之前的後繼,之前的指向下一個節點指向這個插入的節點即可。

    • 尾插入法。需要一個head指標指向頭部,一個tail指標指向尾部,其他的地址都儲存在前趨的next中。
    • 頭插入法。不需要tail指標指向尾部,只是需要不斷修改head頭指標。

鏈式儲存的查詢比較麻煩,要按照順序一個一個查,求表長也如此。單向連結串列,頭節點至關重要。迴圈鏈式儲存,是指最後一個元素的next指向頭部。這樣以來,就可以查詢每個元素的前趨。

雙向連結串列是在每個節點的值和next兩個元素的基礎上,再加一個prior,用來儲存指向前趨的指標。當然還有雙向迴圈連結串列。

三:棧和佇列

兩種特殊的線性表。

棧:先進後出。限制只有棧頂才可以操作。每次pop刪除的總是最新元素,每次push壓入的元素也總是最新元素。

實現棧的方式:順序棧和鏈式棧

順序棧:入棧是線上性表的頭部插入元素,出棧是線上性表的頭部刪除一個元素。這樣效率不高,時間代價為o(n)。如果是線上性表的尾部作為棧頂,插入刪除元素,那麼時間代價為o(1),效率高。

鏈式棧:單向連結串列儲存棧。操作一般在頭部。

順序棧和鏈式棧比較:當需要堆疊共享時,順序棧可以使用一個陣列儲存兩個棧, 陣列的兩端作為兩個棧各自的棧低,中間部分為共享區域。這樣的情況適合兩個棧有相反的需求時,此消彼長的情況。鏈式棧是每個節點多了一個指標域的開銷。

佇列:先進先出。只需要操作線性表的兩端。一端只能進入,另一端只能出。隊尾進,隊首出。有順序儲存和鏈式儲存兩種。佇列假溢位是因為隊首指標確定導致的,就是被刪除的元素空間無法被重複利用。可以讓隊首和隊尾的指標迴圈起來就可以,就是將元素儲存在迴圈向量中。

基本運算:
判斷隊空

佇列初始化

判斷隊滿

入隊元素

出隊元素

取隊首元素

順序佇列:必須用一個向量空間來存放元素。設定front和rear分別來指示隊首和隊尾的位置。

迴圈佇列:就是將元素儲存在迴圈向量中。

鏈式佇列:

四:陣列和廣義表

矩陣儲存分為行優先和列優先兩種。

矩陣壓縮儲存,是針對特殊矩陣隊儲存,只儲存其元素一部分,另一部分通過相應的演算法計算出來。這樣的矩陣包括對稱矩陣,稀疏矩陣和三角矩陣。

  • 稀疏矩陣:矩陣中有多數為零的值。到底這個數佔了全部數的多少位稀疏矩陣呢?假設有m行n列矩陣,有t個非零元素,那麼滿足(t+1)*3<=m*n即可

廣義表是線性表的擴充套件。元素包括

  • 原子元素
  • 可以再分的元素

如果所有元素都是原子元素則是線性表。如果有可以再分的元素,也就是子表,則是廣義表。廣義表含有元素的個數稱為廣義表的長度,廣義表中含有括號對數稱為廣義表的深度,也就是層。


1679305-8ed0912df901e61e.jpg

五:樹

非線性結構。樹的遞迴定義:樹是由根節點和若干棵子樹構成的。

一個節點的子樹個數稱為該節點的度。

度為零的節點稱為葉子或終端節點。不為零的為分支節點。除根節點之外的稱為內部節點。

一棵樹中節點度最大的值稱為該樹的度。

二叉樹:或者為空,或者由一個根節點加上兩棵左右互不交叉的子樹構成。

1679305-72c3778831bde6f5.jpg

  • 滿二叉樹:每個父親都有兩個兒子。
  • 完全二叉樹

二叉樹的順序儲存:只儲存節點的值,不儲存節點之間的邏輯關係


1679305-4960e237bbd88790.jpg

二叉樹的連結儲存:每個節點由資料域和指標域兩部分組成。指標域有兩個,一個指向父親,一個指向兒子。


1679305-8c3da37c99e3245d.jpg

二叉樹遍歷,包括前中後三序遍歷,以及層次遍歷。

當我們遍歷完二叉樹,就形成一個線性序列,於是就有了唯一的前趨和後繼節點。

線索二叉樹,就是為了解決尋找前趨和後繼的。每個連結節點有五個變數,通常樹都是鏈式儲存。

1679305-d0462be16d71fb87.jpg

將一棵樹轉為二叉樹的方法:

  • 樹中所有相鄰兄弟之間加一條連線。
  • 對樹中每個節點,只保留它與第一個兒子節點之間的聯絡,刪除它與其他兒子的連線。
  • 以樹的根節點為軸旋轉。
  • 這樣的旋轉可以證明是唯一的。而且過程是可逆的。


    1679305-311044015394ef79.jpg

將二叉樹還原為普通樹的方法:


1679305-38302b69d810cacf.jpg

樹的遍歷分為先根遍歷和後根遍歷。

森林的遍歷分為前序遍歷和中序遍歷。

哈夫曼樹,也是二叉樹。這種二叉樹的帶權路徑長度最小。並且每個權值都是葉子節點。
帶權路徑長度為根節點到該節點之間的路徑長度與該節點的值的乘積。該節點的值,是我們人為指定的。
路徑長度是層數減1.根節點為第一層。


1679305-99c676f0123f0caa.jpg

六:圖

圖是非線性結構。圖中任何兩個頂點都可能有關聯,頂點間的關係是多對多點關係。圖的每個節點有任意多個前趨和後繼。圖分為有向圖和無向圖。帶權的圖稱為網。網分為有向網和無向網。

1:圖的儲存結構

圖的鄰接矩陣表示法和鄰接表表示法。
鄰接矩陣,行與列分別表示各個頂點。1表示有邊,0表示沒有邊。比如第一行第二列是1,則表示頂點1到頂點2有邊。第三行第四列是1,則表示頂點3到頂點4有邊。這是有向圖。如果是無向圖的話,那麼矩陣是對稱的,就是說如果第三行第四列是1,那麼第四列第三行也是1,因為頂點3和頂點4的邊沒有方向。

鄰接表:是圖的一種鏈式儲存結構。先建立一個連結串列儲存每個頂點。每個頂點有兩個域,鄰接點域和指標域。鄰接點域存序號,指標域指向邊的表節點。邊表是儲存頂點與鄰接點具有邊關係的表,每個節點也有兩個域,指標域指向下一個與鄰接表節點具有邊關係的頂點。比如頂點1與頂點2,3都有邊。那麼頂點1在鄰接表中的指標域指向邊表的頂點2的位置。邊表中頂點2的指標域指向與頂點1有邊的頂點3的位置。

2:圖的遍歷

圖的深度優先遍歷和廣度優先遍歷

  • 深度優先遍歷:遞迴訪問頂點,直到某個頂點沒有未被訪問的頂點為止,開始訪問它的前趨。如果前趨是最開始訪問的那個頂點,就結束遍歷。

  • 廣度優先遍歷:從最開始的訪問點出發,訪問與它鄰接的所有點,再從這些鄰接點出發訪問它們的鄰接點。直到所有頂點均被訪問為止。

3:最小生成樹

克魯斯卡爾演算法。普里姆演算法

4:最短路徑和拓撲排序

最短路徑:迪卡斯特拉演算法

拓撲排序:從網中旋轉一個入度為0的頂點並輸出。也就是沒有其它頂點指向這個頂點,只有這個頂點指向其它頂點。從網中刪除此頂點及其所有出邊。如此迴圈。拓撲排序解決的是各個頂點的依賴關係的有序數列。

七:排序

排序的穩定性是根據需要排序的元素中的關鍵字如果有相等的情況,那麼這些相等關鍵字的元素如果排序前後的相對位置不變就是穩定的,如果這些相等關鍵字的元素相對位置發生了改變,就是不穩定的。
排序過程中是否涉及資料的內外存交換可以將排序分為:

  • 內部排序

    • 插入排序。將待排序的陣列中元素,一個一個地插入到有序陣列當中。
      • 直接插入排序。穩定。正序是o(n),反序和隨機是o(n*n);
      • 希爾排序。不穩定。在直接插入排序的基礎上進行分組插入。
    • 選擇排序。每一趟從待排序的記錄中選擇關鍵字最小的紀錄順序放在排好序的子檔案最後。
      • 直接選擇排序。不穩定。o(n*n)
      • 堆排序。
    • 交換排序。兩兩比較待排序記錄的關鍵字,發現兩個紀錄的次序相反時就進行交換。
      • 氣泡排序。正序是o(n)。最壞是o(n*n)。穩定。排序過程中交替改變掃描方向,改進不對稱性。
      • 快速排序。不穩定。平均時間複雜度o(nlgn)
    • 歸併排序
    • 分配排序
  • 外部排序

    • 合併排序
    • 直接合並排序法
1679305-b168c493c1c3d224.jpg

八:查詢

1:順序查詢。適用於線性表的順序結構,也適用於線性表的鏈式儲存結構。但是鏈式儲存結構需要從第一個節點開始掃描。
2:二分查詢。屬於靜態查詢,因為如果該表要是還需要修改,那麼就很費時。要求線性表是有序的,並且要用向量作為表的儲存結構。二分查詢不會超過樹的深度,但是由於需要有序,因此也費時。二分查詢只能用於順序結構。鏈式結構需要用順序查詢。因為二分查詢需要線性表可以隨機存取。因為二分查詢需要隨機讀取一半的位置,一半的一半的位置。。。
3:分塊查詢。比如給一個學號要在一個學校中查詢。我們需要維護一個陣列用來存放有序的的班級資訊。通過二分查詢找到班級所在的塊之後,再通過順序查詢來查詢班級中的學號。分塊查詢是順序查詢和二分查詢的結合。需要多維護一個有序索引陣列。
4:二叉排序樹。動態查詢表效率高。每個節點的左邊子元素的值小於該節點,右子元素的值大於該節點。二叉排序樹最壞的情況是形成一個單支樹,最好的情況是勻稱。
5:B樹,用來對磁碟等外部儲存進行查詢。B樹幾乎替代了除雜湊方法意外的所有大型檔案查詢。
6:雜湊表。建立關鍵字與地址之間的關係,通過對元素直接定址來查詢。兩個不同的關鍵字,由於雜湊函式值不同,而被對映到同一個低智商,稱為衝突。

九:動態儲存管理

動態記憶體分割槽常用演算法:

最先適配法(nrst-fit):按分割槽在記憶體的先後次序從頭查詢,找到符合要求的第一個分割槽進行分配。該演算法的分配和釋放的時間效能較好,較大的空閒分割槽可以被保留在記憶體高階。但隨著低端分割槽不斷劃分會產生較多小分割槽,每次分配時查詢時間開銷便會增大。

下次適配法(迴圈首次適應演算法 next fit):按分割槽在記憶體的先後次序,從上次分配的分割槽起查詢(到最後{區時再從頭開始},找到符合要求的第一個分割槽進行分配。該演算法的分配和釋放的時間效能較好,使空閒分割槽分佈得更均勻,但較大空閒分割槽不易保留。

最佳適配法(best-fit):按分割槽在記憶體的先後次序從頭查詢,找到其大小與要求相差最小的空閒分割槽進行分配。從個別來看,外碎片較小;但從整體來看,會形成較多外碎片優點是較大的空閒分割槽可以被保留。

最壞適配法(worst- fit):按分割槽在記憶體的先後次序從頭查詢,找到最大的空閒分割槽進行分配。基本不留下小空閒分割槽,不易形成外碎片。但由於較大的空閒分割槽不被保留,當對記憶體需求較大的程式需要執行時,其要求不易被滿足。

相關文章