《資料結構與演算法之美》筆記 鍊錶

2021-09-11 23:27:29 字數 1897 閱讀 7837

typedef struct node node;
陣列和鍊錶都是線性表。陣列必須是連續空間,而鍊錶無所謂。

鍊錶:單鏈表、迴圈鍊錶、雙向鍊錶

陣列:插入、刪除的時間複雜度是o(n),隨機訪問的時間複雜度是o(1)。

鍊錶:插入、刪除的時間複雜度是o(1),隨機訪問的時間複雜端是o(n)。

快取淘汰策略:先進先出策略 fifo(first in,first out),最少使用策略 lfu(least frequently used)、最近最少使用策略 lru(least recently used)。

lru:lru(least recently used,最近最少使用)演算法根據資料的歷史訪問記錄來進行淘汰資料,其核心思想是「如果資料最近被訪問過,那麼將來被訪問的機率也更高」。

一、理解指標或引用的含義

示例:p—>next = q; 表示p節點的後繼指標儲存了q節點的記憶體位址。

p—>next = p—>next—>next; 表示p節點的後繼指標儲存了p節點的下下個節點的記憶體位址。

二、警惕指標丟失和記憶體洩漏(單鏈表)

插入節點

在節點a和節點b之間插入節點x,b是a的下一節點,,p指標指向節點a,則造成指標丟失和記憶體洩漏的**:p—>next = x;x—>next = p—>next; 顯然這會導致x節點的後繼指標指向自身。

正確的寫法是2句**交換順序,即:x—>next = p—>next; p—>next = x;

三、利用「哨兵」簡化實現難度

什麼是「哨兵」?

鍊錶中的「哨兵」節點是解決邊界問題的,不參與業務邏輯。如果我們引入「哨兵」節點,則不管鍊錶是否為空,head指標都會指向這個「哨兵」節點。我們把這種有「哨兵」節點的鍊錶稱為帶頭鍊錶,相反,沒有「哨兵」節點的鍊錶就稱為不帶頭鍊錶。

未引入「哨兵」的情況

如果在p節點後插入乙個節點,只需2行**即可搞定:

new_node—>next = p—>next;

p—>next = new_node;

但,若向空煉表中插入乙個節點,則**如下:

if(head == null)
如果要刪除節點p的後繼節點,只需1行**即可搞定:

p—>next = p—>next—>next;
但,若是刪除鍊錶的最有乙個節點(鍊錶中只剩下這個節點),則**如下:

if(head—>next == null)
從上面的情況可以看出,針對鍊錶的插入、刪除操作,需要對插入第乙個節點和刪除最後乙個節點的情況進行特殊處理。這樣**就會顯得很繁瑣,所以引入「哨兵」節點來解決這個問題。

引入「哨兵」的情況

「哨兵」節點不儲存資料,無論鍊錶是否為空,head指標都會指向它,作為鍊錶的頭結點始終存在。這樣,插入第乙個節點和插入其他節點,刪除最後乙個節點和刪除其他節點都可以統一為相同的**實現邏輯了。

「哨兵」還有哪些應用場景?

這個知識有限,暫時想不出來呀!但總結起來,哨兵最大的作用就是簡化邊界條件的處理。

四、重點留意邊界條件處理

經常用來檢查鍊錶是否正確的邊界4個邊界條件:

如果鍊錶為空時,**是否能正常工作?

如果鍊錶只包含乙個節點時,**是否能正常工作?

如果鍊錶只包含兩個節點時,**是否能正常工作?

**邏輯在處理頭尾節點時是否能正常工作?

5個常見的鍊錶操作:

單鏈表反轉;鍊錶中環的檢測;兩個有序鍊錶合併;刪除鍊錶倒數第n個節點;求鍊錶的中間節點。

參考文獻

資料結構與演算法之美 鍊錶

如何優雅的寫出鍊錶 6大學習技巧 一 理解指標或引用的含義1.含義 將某個變數 物件 賦值給指標 引用 實際上就是就是將這個變數 物件 的位址賦值給指標 引用 2.示例 p next q 表示p節點的後繼指標儲存了q節點的記憶體位址。p next p next next 表示p節點的後繼指標儲存了p...

資料結構與演算法之美 鍊錶

1.如何實現lru快取淘汰演算法 答 回答這個問題之前,我們首先了解一下什麼是快取 2.鍊錶的三種形式 單鏈表 雙向鍊錶 迴圈鍊錶 簡介一下單鏈表 頭節點用於記錄基位址,有了它我們就可以遍歷整條鍊錶,而尾節點特殊地方不是指向下乙個節點,而是指向乙個空位址。鍊錶因為不是乙個連續的位址,所以不需要考慮連...

資料結構與演算法之美 03鍊錶

陣列 缺點 大小固定 優點 簡單實用,訪問效率更高 可借助cpu快取機制 鍊錶 缺點 記憶體不連續,訪問效率不高。對記憶體使用苛刻,當鍊表發生頻繁插入 刪除操作時,會導致頻繁的記憶體申請和釋放。優點 鍊錶本身沒有大小的限制,天然地支援動態擴容 1 在刪除給定指標指向的結點時,我們已經找到了要刪除的結...