在單鏈表中有了next指標,這就使得我們要查詢的下一結點的時間複雜度為o(1)。可是如果我們要查詢的是上一結點的話,那麼最壞的時間複雜度就是o(n),因此我們每次都要從頭遍歷開始查詢。
為了克服單向性這一缺點,設計出了雙向鍊錶(double linked list)是在單鏈表的每個結點中,在設定乙個指向其前驅結點的指標域。所以在雙鏈表中的結點都有兩個指標域,乙個指向直接後繼,另乙個指向直接前驅。
//線性表的雙向鍊錶的儲存結構
typedef
struct dulnode
dulnode,
*dulinklist;
既然單鏈表也可以有迴圈鍊錶,那麼雙鏈表也可以是迴圈鍊錶。
雙向鍊錶的迴圈帶頭結點的空鍊錶如下圖。
非空的迴圈的帶頭結點的雙向鍊錶如下圖
由於這是雙向鍊錶,那麼對於鍊錶中每乙個結點p,它的後繼的前驅是誰?當然還是他自己,它的前驅的後繼自然也自己。
p->next->prior = p = p=
->prior->next
雙向鍊錶是單鏈表中擴充套件出來的結構,所以它的很多操作是和單鏈表相同的,比如求長度,查詢元素,獲得元素的位置等。這些操作都只要涉及乙個方向的指標即可。
插入的操作,其實並不複雜,不過順序很重要。
假設儲存元素e的結點為s,要實現將結點s插入到結點p和p->next之間需要下面幾步:
//把p賦值給s的前驅
s->next = p->next;
//把p->next賦值非s的後繼
p->next->prior =s;
//把s賦值給p->next的前驅
p->next =s;
//把s賦值非p的後繼
關鍵在於他們的順序,由於第二步和第三步都用到了p->next。如果第四步先執行,則會使p->next提前變成了s,使得插入的工作完不成。所以我們不妨把上面的圖的基礎上理解,順序是先搞定s的前驅和後繼,在搞定後結點的前驅,最後解決前結點的後繼。如果插入操作理解了,那麼刪除操作,就很簡單。
若要刪除結點p,只需要下面的兩個步驟:
p->prior->next=p->next;
//把p->next賦值給p->prior的後繼
p->next->prior=p->prior;
//把p->prior賦值給p-》next的後繼
free
(p);
//釋放結點
雙向鍊錶相對於單鏈表來說,要更複雜一些,畢竟它多了乙個prior指標,對於插入和刪除時,需要注意。另外他由於每個結點都需要記錄兩份指標,所以在空間上占用多一點,不過,由於它良好的對稱性。使得對某個結點的前後結點的操作,帶來了方便,可以有效提高演算法的時間效能,說簡單點,就是用空間來換取時間。 C 語言 資料結構之雙向鍊錶
雙向鍊錶的空間結構如下圖所示 int initdlist dlinklist head 初始化雙向迴圈鍊錶 int createdlist dlinklist head,int n 建立雙向迴圈鍊錶 return 1 void printdlist dlinklist head 輸出雙向迴圈鍊錶中的...
C語言 資料結構之雙向鍊錶
本文將實現雙向鍊錶的基礎介面功能 1.初始化 銷毀 2.增刪改查 標頭檔案dlist.h pragma once 資料型別 typedef int dldatatype 結點型別 typedef struct dlistnode dlistnode 雙向鍊錶型別 typedef struct dli...
C語言資料結構 雙向鍊錶的實現
雙向鍊錶對比單向鍊錶增加了乙個指向前乙個節點的指標。雙向鍊錶和單向鍊錶節點的對比 1 單向鍊錶的節點 節點 typedef struct nodenode t 鍊錶的結構體 typedef struct link t 2 雙向鍊錶的節點 typedef struct nodenode t 鍊錶的結構...