鏈表知識點總結(二)雙鏈表、循環鏈表的總結以及一些LeetCode的鏈表題目推薦

接上一篇 鏈表知識點總結(一) 這次講講單鏈表的擴展——雙鏈表、循環鏈表,以及推薦一些我個人認爲比較好的鏈表練習題LeetCode。

 

雙鏈表的簡單總結

雙鏈表其實就是節點的定義裏多了一個prev指針指向上一個節點,對於帶頭節點的雙鏈表來說,頭節點的prev指針是指向NULL的(與此對應的是尾結點的next指針指向NULL),對於雙鏈表來說,增刪改查的操作與單鏈表沒什麼不同,只是多一個prev指針需要修改,這裏就不再一一列出各種實現了。

值得一提的是,對雙鏈表來說,可以定義一個函數link(Node* l,Node * r),功能是連接節點l和節點r,代碼實現:

inline void link(Node *l, Node *r)
{
	if (l)
		l->next = r;
	if (r)
		r->prev = l;
}

假如我們有這樣一個鏈表  a->b->c ,我要在b的位置(也就是a的後面)插入元素newNode,如果我們使用link函數的話,我們就需要非常簡潔地調用兩次link函數:

link(newNode,a->next);//連接新結點和b
link(a,newNode);//連接a和新節點

對於上面兩行代碼,如果a是尾結點的話link也是不會出錯的,因爲link的實現裏對l和r有判空的邏輯,所以不管l和r是否爲NULL都不會出錯。使用link函數分離邏輯,可以讓代碼的邏輯更加清晰,特別是在後面寫循環雙鏈表的時候,只需要按邏輯調用link函數就可以完成繁雜的連接操作。

 

循環鏈表的簡單總結

循環鏈表就是尾結點不指向NULL,而是指向頭節點的鏈表,循環鏈表可以用於解決一些帶有“循環”概念的問題,如約瑟夫問題等。循環鏈表的各種操作的實現和單鏈表的實現差不多,只是要注意的是,對於帶頭節點的循環鏈表,遍歷時應從第一個數據節點開始,以指針掃一圈後指向頭節點爲終止條件,以循環鏈表的遍歷輸出爲例,其實現代碼如下:

void print(Node* header)
    {
        Node *cur = header->next;//開始時指向第一個數據節點
        while (cur != header)//cur重新回到header時結束
        {
            cout << cur->data << " ";
            cur = cur->next;
        }
        cout << endl;
    }

循環雙鏈表相比起簡單的單鏈表來說,它可以以O(1)的時間複雜度得到某個節點的前一個節點(利用prev指針),而簡單單鏈表的話需要O(n)的時間複雜度(重新從頭遍歷一次得到),除此之外,如果要在鏈表中尋找某個位置的節點的話,循環雙鏈表可以根據索引離兩端的遠近來選擇從頭向尾遍歷或者是從尾向頭遍歷,這樣實際查找元素的時間複雜度就變成了O(n/2),比單鏈表只能從頭遍歷要好一些,但總歸是O(n)。實際開發中要用鏈表的話,一般都會選擇循環雙鏈表來代替簡單的單鏈表以提高效率。

 

 LeetCode上鍊表相關的題目推薦

我所推薦的題目都是中文LeetCode網站上的題目,網址:力扣,並且推薦的都是難度 簡單-中等的練手題目,可以認爲我推薦的都是有一定的思考量且需要一定的編碼能力才能解決的題目,但不會太難,初學者也能做,我個人認爲這類題目更多地是要去學習如何寫出高效又簡潔的代碼解決問題,也就是略微偏重於編碼能力的題目,畢竟是側重鏈表知識點的題目。

 

2. 兩數相加

這道是模擬加法計算的題,仔細閱讀題目所給的鏈表順序,先試着用自己的代碼實現,然後着重練習如何將代碼寫到最簡潔的形式。

 

147. 對鏈表進行插入排序

鏈表的插入排序問題。

 

237. 刪除鏈表中的節點

簡單題,但是需要拐點彎,需要拋棄定勢思維才能想到解決辦法。

 

141. 環形鏈表

判斷鏈表有無環的模板題,比較經典。

 

21. 合併兩個有序鏈表

鏈表的二路歸併,較爲簡單,需要一定的編碼能力。

 

206. 反轉鏈表

反轉鏈表的模板題,題目本身很簡單,推薦嘗試用遞歸來解決。

 

24. 兩兩交換鏈表中的節點

中等題,需要一定的編碼能力。

 

876. 鏈表的中間結點

面試中的常見問題,快慢指針的經典問題。

 

以上題目就是我做過的比較經典的、同時又不會太難的題目,適合初學者練習,如果還想繼續做題的話可以自行上LeetCode刷題,多刷題多總結,才能更快地提升自己的能力。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章