Linklist 基礎

#clippings

關於鏈表,你該瞭解這些!

什麼是鏈表,鏈表是一種通過指針串聯在一起的線性結構,每一個節點由兩部分組成,一個是數據域一個是指針域(存放指向下一個節點的指針),最後一個節點的指針域指向null(空指針的意思)。

鏈表的入口節點稱為鏈表的頭結點也就是head。

如圖所示: 鏈表1

接下來說一下鏈表的幾種類型:

單鏈表

剛剛說的就是單鏈表。

雙鏈表

單鏈表中的指針域只能指向節點的下一個節點。

雙鏈表:每一個節點有兩個指針域,一個指向下一個節點,一個指向上一個節點。

雙鏈表 既可以向前查詢也可以向後查詢。

如圖所示: 鏈表2

循環鏈表

循環鏈表,顧名思義,就是鏈表首尾相連。

循環鏈表可以用來解決約瑟夫環問題。

鏈表4

瞭解完鏈表的類型,再來說一說鏈表在內存中的存儲方式。

數組是在內存中是連續分佈的,但是鏈表在內存中可不是連續分佈的。

鏈表是通過指針域的指針鏈接在內存中各個節點。

所以鏈表中的節點在內存中不是連續分佈的 ,而是散亂分佈在內存中的某地址上,分配機製取決於作業系統的內存管理。

如圖所示:

鏈表3

這個鏈表起始節點為2, 終止節點為7, 各個節點分佈在內存的不同地址空間上,通過指針串聯在一起。

接下來說一說鏈表的定義。

鏈表節點的定義,很多同學在面試的時候都寫不好。

這是因為平時在刷leetcode的時候,鏈表的節點都默認定義好了,直接用就行了,所以同學們都沒有注意到鏈表的節點是如何定義的。

而在面試的時候,一旦要自己手寫鏈表,就寫的錯漏百出。

這裡我給出C/C++的定義鏈表節點方式,如下所示:

struct ListNode {
    int val;  
    ListNode *next;  
    ListNode(int x) : val(x), next(NULL) {}  
};

有同學說了,我不定義構造函數行不行,答案是可以的,C++默認生成一個構造函數。

但是這個構造函數不會初始化任何成員變量,下面我來舉兩個例子:

通過自己定義構造函數初始化節點:

ListNode* head = new ListNode(5);

使用默認構造函數初始化節點:

ListNode* head = new ListNode();
head->val = 5;

所以如果不定義構造函數使用默認構造函數的話,在初始化的時候就不能直接給變量賦值!

刪除節點

刪除D節點,如圖所示:

鏈表-刪除節點

只要將C節點的next指針 指向E節點就可以了。

那有同學說了,D節點不是依然存留在內存裡麼?只不過是沒有在這個鏈表裡而已。

是這樣的,所以在C++裡最好是再手動釋放這個D節點,釋放這塊內存。

其他語言例如Java、Python,就有自己的內存回收機製,就不用自己手動釋放了。

添加節點

如圖所示:

鏈表-添加節點

可以看出鏈表的增添和刪除都是O(1)操作,也不會影響到其他節點。

但是要注意,要是刪除第五個節點,需要從頭節點查找到第四個節點通過next指針進行刪除操作,查找的時間複雜度是O(n)。

再把鏈表的特性和數組的特性進行一個對比,如圖所示:

鏈表-鏈表與數據性能對比

數組在定義的時候,長度就是固定的,如果想改動數組的長度,就需要重新定義一個新的數組。

鏈表的長度可以是不固定的,並且可以動態增刪, 適合數據量不固定,頻繁增刪,較少查詢的場景。