學會這幾道鏈表算法題,面試不再怕手寫鏈表了

學會這幾道鏈表算法題,面試不再怕手寫鏈表了

筆者文筆功力尚淺,若有不妥,請慷慨指出,一定感激涕零node

在面試的時候常常被問到讓手寫關於鏈表的代碼,下面幾個都是我在面試中被問到過的問題。固然我寫的不必定是最優解,若是有更好的解決辦法歡迎你們指出。git

便於你們觀看,我先將題目列出github

  • 刪除鏈表中倒數第N個節點
  • 鏈表反轉
  • 合併兩個有序鏈表
  • 求鏈表的中間節點

你們必定要本身在電腦手敲一遍,最好是在紙上本身手寫一遍面試

刪除鏈表中倒數第N個節點

題目:給定一個鏈表,刪除鏈表的倒數第 n 個節點,而且返回鏈表的頭結點。算法

給定一個鏈表: 1->2->3->4->5, 和 n = 2.

當刪除了倒數第二個節點後,鏈表變爲 1->2->3->5.

複製代碼

在鏈表的題目中,有時候一個指針解決不了的問題那麼咱們就再加一個指針。通常來講兩個指針就能解決大部分的問題bash

上面是咱們定義的鏈表,例如咱們想要刪除倒數第N個節點,那麼就定義兩個指針,一個快指針,一個慢指針。快指針比慢指針快N步,而後快慢指針一塊兒向前移動,那麼正好快指針走到Null的時候慢指針所指向的就是咱們要刪除的節點。ui

舉個例子例如咱們想要刪除倒數第二個節點,那麼初始化的指針指針指向以下。編碼

遍歷完之後的指針以下,咱們就能夠看到左邊的指針指向的就是咱們想要刪除的節點spa

部分代碼以下3d

public void deleteNodeForN(Node head, int n){

    Node left = head;
    Node right = head;
    int i = 0;
    while (right!=null && i < n){
        right = right.getNext();
        i++;
    }

    while (right!=null){
        left = left.getNext();
        right = right.getNext();
    }
	// 遍歷完後刪除左節點
    deleteNode(left);

}

複製代碼

鏈表反轉

題目:反轉一個單鏈表。

輸入: 0->1->2->3->4->5->NULL
輸出: 5->4->3->2->1->0->NULL

複製代碼

鏈表中沒有什麼問題是經過加指針解決不了的,若是有,那麼就再加一個指針。

解法一:加指針

在上面鏈表刪除第N個節點中咱們加了兩個指針解決了問題,那麼接下來若是要反轉一個鏈表該怎麼作呢?兩個指針已經不夠用了,咱們須要三個指針用來定義當前節點、當前節點的前節點、當前節點的後節點。固然這種方式是既不佔用空間,時間也快的一種解法。

仍是咱們定義的一個鏈表,那麼咱們想要的指針效果是什麼樣呢?接下來咱們用圖示一步一步演示怎麼用三個指針將鏈表翻轉過來,你們不用看我最後給出的解法答案,能夠本身試着看着個人圖本身寫一遍代碼,看能不能寫出來。

部分代碼展現

public Node reversalNodeThree(Node head) {
    if (head == null || head.getNext() == null){
        return head;
    }

    Node preNode = null;
    Node nextNode = null;
    while (head != null){
        nextNode = head.getNext();
        head.setNext(preNode);
        preNode = head;
        head = nextNode;
    }
    return preNode;
}

複製代碼

解法二:遞歸

遞歸代碼的關鍵是如何將大問題分解爲小問題的規律,而且基於此寫出遞歸公式,而後再推敲終止條件。

在寫遞歸代碼的時候咱們的思路千萬不要一步一步往裏套,套着套着本身就會容易蒙了。其實遞歸的本質就是分解小任務執行,而咱們正確的思惟方式屏蔽掉遞歸的細節,假設後面的已經咱們想要的結果,而後只想第一步便可。

咱們就以反轉鏈表爲例子,怎麼用遞歸的思想來思考,又怎樣把咱們的思考變成代碼。

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

仍是上面的鏈表爲例,咱們要反轉,假設第一個節點隨後的全部節點已經反轉成功了,那麼接下來咱們怎麼作呢?相信這裏你們都會了吧,至關於兩個節點的轉換。

Node(0).getNext().setNext(Node(0));
Node(0).setNext(null);

複製代碼
  • 終止條件:當所傳Node是null或者所傳的Node.next是null。代表傳進來的是空節點或者就一個節點,無需反轉

咱們利用上面的終止條件以及分析出來的代碼就能夠寫出以下的遞歸反轉一個鏈條的代碼了。

public Node reversalNodeTwo(Node head){

    if (head == null || head.getNext() == null){
        return head;
    }

    Node reHead = reversalNodeTwo(head.getNext());

    head.getNext().setNext(head);

    head.setNext(null);

    return reHead;

}

複製代碼

合併兩個有序鏈表

題目:將兩個有序鏈表合併爲一個新的有序鏈表並返回。新鏈表是經過拼接給定的兩個鏈表的全部節點組成的。

輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4

複製代碼

迭代

迭代的方式將兩個鏈表合併是經過指針的方式來解決問題的。加入咱們如今有下面兩個鏈表。咱們會定義兩個指針分別指向兩個鏈表的表頭,來開始進行一一比較,較小的一方將節點移出來,並將指針向後移動。直至爲null。接下來咱們用圖片分解每一步。你們能夠根據圖片的提示本身先編碼練習一下,後面附有答案。

部分代碼展現

public Node mergeTwoListTwo(Node nodeOne, Node nodeTwo){

    AboutLinkedList mergeTwoList = new AboutLinkedList();
    Node headNodeOne = nodeOne;
    Node headNodeTwo = nodeTwo;

    while (headNodeOne!=null || headNodeTwo!=null){

        if (headNodeOne == null || headNodeOne.getNum() > headNodeTwo.getNum()){
            mergeTwoList.addNode(headNodeTwo);
            Node pre = headNodeTwo;
            headNodeTwo = headNodeTwo.getNext();
            pre.setNext(null);
        }else {
            mergeTwoList.addNode(headNodeOne);
            Node pre = headNodeOne;
            headNodeOne = headNodeOne.getNext();
            pre.setNext(null);
        }
    }
    return mergeTwoList.head.getNext();
}

複製代碼

求鏈表的中間節點

題目:求鏈表的中間節點

輸入:0->1->2->3->4
輸出:2

複製代碼

指針

通常來講鏈表的題咱們能夠用指針的話不管是時間仍是空間都是最優的解決辦法,其實這裏有個小技巧,就是定義兩個指針(快慢指針),快指針每次走兩個節點,慢指針每次走一個節點。這樣當快指針走到最後的時候慢指針正好走到了中間位置。接下來咱們用圖更直觀的感覺一下指針是如何走的。你們能夠按照圖中的演示本身寫一下代碼,而後再看我最後給出的代碼。這樣會記憶更深入

部分代碼展現

public Node getNodeForCenter(Node head){
    if (head == null){
        return null;
    }else if (head.getNext() == null){
        return head;
    }
    Node slow = head;
    Node fast = head;
    while (fast!=null && fast.getNext()!=null){
        slow = slow.getNext();
        fast = fast.getNext().getNext();
    }
    return slow;
}

複製代碼

最後提醒,你們必定要本身在電腦手敲一遍,最好是在紙上本身手寫一遍

代碼地址

相關文章
相關標籤/搜索