資料結構 雙向鍊錶

2021-08-14 02:58:45 字數 3404 閱讀 3938

單鏈表的單向性:只能從頭結點開始高效訪問鍊錶中的資料元素。

單鏈表還存在另乙個缺陷:逆序訪問時候的效率極低。

如下:

linklistlist;

for(int i = 0; i < 5; i++)

for(int i = list.length() - 1; i >= 0; i--)

根據大o推算法可以得出乙個for迴圈的時間複雜度為o(n),get(i)的時間複雜度也是o(n),所以乙個逆序訪問資料的時間複雜度為o(n^2),所以效率是極低的。

所以新的需求來了,我們需要一種線性表能夠高效逆序訪問資料。在單鏈表的結點基礎上增加乙個指標域pre,它指向上乙個結點,即前驅結點。

我們稱之為雙向鍊錶。

那麼雙向鍊錶的繼承層次結構是什麼?由於它和單鏈表的資料結點的結構不同,所以它的繼承層次為繼承自list類。

通過類模板實現雙向鍊錶。

模板實現宣告如下:

template class duallinklist : public list;

//mutable node m_header;//分析下面的匿名結構的作用:本質上就是防止呼叫建立t value物件時呼叫建構函式,下面的匿名結構在記憶體布局上和node m_header布局相同

mutable struct : public object//如果未繼承自object可能導致記憶體布局和node m_header記憶體布局不同。

m_header;

int m_length;

int m_step;//儲存游標移動的次數

node* m_current;//游標

node* position(int i)const//用於定位 ,優化insert、remove、get、set函式用,但是本檔案未優化,便於複習使用,這裡只是說明可以優化

return current;

}virtual node* create()

virtual void destroy(node* pn)

public:

duallinklist();

bool insert(int i,const t& e);

bool insert(const t& e);

bool remove(int i);

bool set(int i,const t& e);

bool get(int i,t& e )const;

virtual t get(int i)const;

int find(const t& e )const;//返回的是查詢到的結點的位置

int length()const;

void clear();

virtual bool move(int i, int step = 1);

virtual bool end();

virtual t current();

virtual bool next();

virtual bool pre();

~duallinklist();

};

在模板類中只需要實現一些關鍵操作,如insert、remove、clear、pre操作,其餘的都和linklist的實現完全相同。

一、插入

插入操作的原理本質上和單鏈表是一樣的,只是多了連線前驅指標的步驟,具體步驟如下圖:

實現**如下:

bool insert(int i,const t& e)

node* next = current->next;

node->value = e;

//連線next域

//第一二步

node->next = next;

current->next = node;

//連線pre域

//第三四步

if(current != reinterpret_cast(&m_header))

else

if(next != null)

m_length++;

}else

}return ret;

}bool insert(const t& e)

實現步驟就是如圖步驟所示,需要注意的是插入的位置為首結點時pre域應該為null,當next結點不指向null時pre域才有效。

最後實現了插入過載函式,每次插入末尾位置。

二、刪除

刪除操作也需要對pre域進行連線,先將鍊錶連好,最後銷毀需要刪除的結點。

如圖:

實現如下:

bool remove(int i)

node* todel = current->next;

node* next = todel->next;

if(m_current == todel)//作用:在遍歷中執行remove操作時刪除結點後會導致m_current指向不變,從而使m_current->value為隨機值,所以當需要刪除將m_current指向下乙個結點

//第一步

current->next = next;

//第二步

if(next != null)

m_length--;//保證異常安全,因為當銷毀資料時丟擲異常(結點是類型別,並且在析構函式中丟擲異常)先長度減一再銷毀結點

destroy( todel );

}return ret;

}

具體實現在圖中已有說明,程式是按照圖中步驟進行的。同樣需要注意的是next不為null時pre域才有效。

三、清空

實現原理是每次刪除首結點,直到鍊錶長度為0。

四、前移

單鏈表中實現了向後移動的操作,在雙向鍊錶中也新增相似的功能函式,實現如下:

virtual bool pre()

return (i == m_step);

}

基本操作就這些,在宣告中還有一些函式並沒有實現,因為它們和單鏈表中的實現完全一樣,具體實現請參考前面的文章「單鏈表實現」。且建構函式和析構函式的函式體幾乎一樣,只不過建構函式中需要初始化pre指標。

小結:雙向鍊錶是為了彌補單鏈表缺陷而設計的。

在概念上,雙向鍊錶不是單鏈表,所以沒有直接繼承關係。

雙向鍊錶中的游標能夠直接訪問當前節點的前驅和後繼。

雙向鍊錶是線性表概念的最終實現(更貼近理論上的線性表)。

資料結構 鍊錶 雙向鍊錶

注意typedef的定義結構,以及dinklist的資料型別 typedef struct dnode dnode,dinklist 注意插入第乙個結點時,prior指標的空指向問題 if l next null 若l後繼結點為空 則省略該步驟 l next prior p 基本 頭插法建立雙向鍊錶...

資料結構 雙向鍊錶

前幾天寫了乙個單向鍊錶,今天參考自己單向鍊錶改寫了乙個雙向非迴圈鍊錶,下面只討論雙向非迴圈鍊錶。雙向非迴圈鍊錶有如下特點 一 雙向鍊錶每個結點都有乙個前驅指標和後驅指標 當然頭結點和尾結點除外 二 雙向鍊錶中的任意乙個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。三 頭結點只有後驅指標沒有前驅...

資料結構 雙向鍊錶

帶頭節點的雙向鍊錶 dlinklist.h pragma once include include include typedef int dlinktype typedef struct dlinknode dlinknode void dlinklistinit dlinknode head 初...