1. 程式人生 > 程式設計 >學會這幾道連結串列演演算法題,面試再也不怕手寫連結串列了

學會這幾道連結串列演演算法題,面試再也不怕手寫連結串列了

學會這幾道連結串列演演算法題,面試再也不怕手寫連結串列了

筆者文筆功力尚淺,如有不妥,請慷慨指出,必定感激不盡

在面試的時候經常被問到讓手寫關於連結串列的程式碼,下面幾個都是我在面試中被問到過的問題。當然我寫的不一定是最優解,如果有更好的解決辦法歡迎大家指出。

便於大家觀看,我先將題目列出

  • 刪除連結串列中倒數第N個節點
  • 連結串列反轉
  • 合併兩個有序連結串列
  • 求連結串列的中間節點

大家一定要自己在電腦手敲一遍,最好是在紙上自己手寫一遍

刪除連結串列中倒數第N個節點

題目:給定一個連結串列,刪除連結串列的倒數第 n 個節點,並且返回連結串列的頭結點。

1給定一個連結串列: 1->2->3->4->5, 和 n = 2.

2
3當刪除了倒數第二個節點後,連結串列變為 1->2->3->5.
複製程式碼

在連結串列的題目中,有時候一個指標解決不了的問題那麼我們就再加一個指標。一般來說兩個指標就能解決大部分的問題

上面是我們定義的連結串列,例如我們想要刪除倒數第N個節點,那麼就定義兩個指標,一個快指標,一個慢指標。快指標比慢指標快N步,然後快慢指標一起向前移動,那麼正好快指標走到Null的時候慢指標所指向的就是我們要刪除的節點。

舉個例子例如我們想要刪除倒數第二個節點,那麼初始化的指標指標指向如下。

遍歷完以後的指標如下,我們就可以看到左邊的指標指向的就是我們想要刪除的節點

部分程式碼如下

 1public
 void deleteNodeForN(Node head, int n)
{
2
3    Node left = head;
4    Node right = head;
5    int i = 0;
6    while (right!=null && i < n){
7        right = right.getNext();
8        i++;
9    }
10
11    null){
12        left = left.getNext();
13        right = right.getNext();
14    }
15    // 遍歷完後刪除左節點

16    deleteNode(left);
17
18}
複製程式碼

連結串列反轉

題目:反轉一個單連結串列。

1輸入: 0->1->2->3->4->5->NULL
2輸出: NULL
複製程式碼

連結串列中沒有什麼問題是通過加指標解決不了的,如果有,那麼就再加一個指標。

解法一:加指標

在上面連結串列刪除第N個節點中我們加了兩個指標解決了問題,那麼接下來如果要反轉一個連結串列該怎麼做呢?兩個指標已經不夠用了,我們需要三個指標用來定義當前節點、當前節點的前節點、當前節點的後節點。當然這種方式是既不佔用空間,時間也快的一種解法。

還是我們定義的一個連結串列,那麼我們想要的指標效果是什麼樣呢?接下來我們用圖示一步一步演示怎麼用三個指標將連結串列翻轉過來,大家不用看我最後給出的解法答案,可以自己試著看著我的圖自己寫一遍程式碼,看能不能寫出來。

部分程式碼展示

 1public Node reversalNodeThree(Node head) {
2    if (head == null || head.getNext() ==  3        return head;
4    }
5
6    Node preNode = null;
7    Node nextNode =  8    while (head !=  9        nextNode = head.getNext();
10        head.setNext(preNode);
11        preNode = head;
12        head = nextNode;
13    }
14    return preNode;
15}
複製程式碼

解法二:遞迴

遞迴程式碼的關鍵是如何將大問題分解為小問題的規律,並且基於此寫出遞迴公式,然後再推敲終止條件。

在寫遞迴程式碼的時候我們的思路千萬不要一步一步往裡套,套著套著自己就會容易蒙了。其實遞迴的本質就是分解小任務執行,而我們正確的思維方式遮蔽掉遞迴的細節,假設後面的已經我們想要的結果,然後只想第一步即可。

我們就以反轉連結串列為例子,怎麼用遞迴的思想來思考,又怎樣把我們的思考變成程式碼。

這裡0號節點的下一節點不是5號節點,而是我們灰色背景下的大節點

還是上面的連結串列為例,我們要反轉,假設第一個節點隨後的所有節點已經反轉成功了,那麼接下來我們怎麼做呢?相信這裡大家都會了吧,相當於兩個節點的轉換。

1Node(0).getNext().setNext(Node(0));
2null);
複製程式碼
  • 終止條件:當所傳Node是null或者所傳的Node.next是null。表明傳進來的是空節點或者就一個節點,無需反轉

我們利用上面的終止條件以及分析出來的程式碼就可以寫出如下的遞迴反轉一個鏈條的程式碼了。

public Node reversalNodeTwo(Node head){
3     4         5    }
6
7    Node reHead = reversalNodeTwo(head.getNext());
8
9    head.getNext().setNext(head);
11    head.setNext(null);
12
13    return reHead;
14
15}
複製程式碼

合併兩個有序連結串列

題目:將兩個有序連結串列合併為一個新的有序連結串列並返回。新連結串列是通過拼接給定的兩個連結串列的所有節點組成的。

1輸入:1->2->4, 1->3->4
2輸出:1->1->2->3->4->4
複製程式碼

迭代

迭代的方式將兩個連結串列合併是通過指標的方式來解決問題的。加入我們現在有下面兩個連結串列。我們會定義兩個指標分別指向兩個連結串列的表頭,來開始進行一一比較,較小的一方將節點移出來,並將指標向後移動。直至為null。接下來我們用圖片分解每一步。大家可以根據圖片的提示自己先編碼練習一下,後面附有答案。

public Node mergeTwoListTwo(Node nodeOne, Node nodeTwo){
3    AboutLinkedList mergeTwoList = new AboutLinkedList();
4    Node headNodeOne = nodeOne;
5    Node headNodeTwo = nodeTwo;
7    while (headNodeOne!=null || headNodeTwo!= 9        if (headNodeOne == null || headNodeOne.getNum() > headNodeTwo.getNum()){
10            mergeTwoList.addNode(headNodeTwo);
11            Node pre = headNodeTwo;
12            headNodeTwo = headNodeTwo.getNext();
13            pre.setNext(14        }else {
15            mergeTwoList.addNode(headNodeOne);
16            Node pre = headNodeOne;
17            headNodeOne = headNodeOne.getNext();
18            pre.setNext(19        }
20    }
21    return mergeTwoList.head.getNext();
22}
複製程式碼

求連結串列的中間節點

題目:求連結串列的中間節點

1輸入:0->1->2->3->4
2輸出:2
複製程式碼

指標

一般來說連結串列的題我們可以用指標的話無論是時間還是空間都是最優的解決辦法,其實這裡有個小技巧,就是定義兩個指標(快慢指標),快指標每次走兩個節點,慢指標每次走一個節點。這樣當快指標走到最後的時候慢指標正好走到了中間位置。接下來我們用圖更直觀的感受一下指標是如何走的。大家可以按照圖中的演示自己寫一下程式碼,然後再看我最後給出的程式碼。這樣會記憶更深刻

public Node getNodeForCenter(Node head){
return  4    }else if (head.getNext() ==  5         6    }
7    Node slow = head;
8    Node fast = head;
9    while (fast!=null && fast.getNext()!=10        slow = slow.getNext();
11        fast = fast.getNext().getNext();
12    }
return slow;
14}
複製程式碼

最後提醒,大家一定要自己在電腦手敲一遍,最好是在紙上自己手寫一遍

程式碼地址