連結串列演算法題之中等級別,debug除錯更簡單

奮進的小樣發表於2021-03-04

文章簡述

大家好,本篇是個人的第 5 篇文章

從本篇文章開始,分享關於連結串列的題目為中等難度,本次共有 3 道題目。

連結串列演算法題之中等級別,debug除錯更簡單

一,兩數相加

連結串列演算法題之中等級別,debug除錯更簡單
1.1 題目分析

題中寫到數字是按照逆序的方式儲存,從進位的角度看,兩兩節點相加我們是可以直接將進位傳遞到下一組兩兩節點相加。

比如題中第二組節點【4】和節點【6】相加結果為 10,而 10 就需要進位,也就是說該節點只能儲存數字【0】,而進位【1】就要傳遞到下一組節點相加。

那再整理下思路。

如果兩個連結串列的節點數是相等的,那隻需要依次將兩兩節點進行相加。如果節點數是不相等的,比如。

連結串列演算法題之中等級別,debug除錯更簡單

L2 連結串列少了一個節點,像這種情況我們就需要在高位用【0】進行補位。

連結串列演算法題之中等級別,debug除錯更簡單

我們再回到題中的案例,而上面說的位數不夠也是需要考慮的一種情況。再一步步分析下如何進行兩兩節點相加。

連結串列演算法題之中等級別,debug除錯更簡單

第一組節點相加為2+5=7,不滿足進位。建立一個新的連結串列儲存相加後的數,那此時連結串列第一個節點數為【7】。

接著是4+6=10,此時滿足進位要求,按照題目要求和我們上面的分析,需要將低位【0】儲存到節點,高位【1】傳遞到下一組節點。

連結串列演算法題之中等級別,debug除錯更簡單

那現在進行最後一組相加3+4=7,但是還有重要一步不能丟,即上一組節點相加時,還有高位進 1,那最後的結果是 3+4+1=8。

最後將上面相加的結果用連結串列進行儲存,那麼結果為。

連結串列演算法題之中等級別,debug除錯更簡單

同理,如果位數不足時用【0】進行補位也是一樣的方式。

1.2 程式碼分析

老方式,先建立單連結串列

				// 建立連結串列-L1
        ListNode l1 = new ListNode(2);
        ListNode l2 = new ListNode(4);
        ListNode l3 = new ListNode(3);

        ListNodeFun listNodeFun = new ListNodeFun();
        listNodeFun.add(l1);
        listNodeFun.add(l2);
        ListNode listNode1 = listNodeFun.add(l3);

        ListNodeFun listNodeFun2 = new ListNodeFun();
        // 建立連結串列-L2
        ListNode l11 = new ListNode(5);
        ListNode l22 = new ListNode(6);
        ListNode l33 = new ListNode(4);
        listNodeFun2.add(l11);
        listNodeFun2.add(l22);
        ListNode listNode2 = listNodeFun2.add(l33);

兩數相加程式碼

    if(null == l1 || null == l2){
            return null;
        }
        // 初始化頭指標
        ListNode head = new ListNode();
        ListNode cur = head;
        // 定義變數儲存進位值
        int temp = 0;
        while(null != l1 || null != l2){
            // 獲取每個節點的值
            int x = l1 == null ? 0 : l1.val;
            int y = l2 == null ? 0 : l2.val;
            // 兩數相加
            int sum = x + y + temp;
            // 獲取相加結果
            temp = sum  / 10;
            // 獲取低位(個位)
            sum = sum % 10;
            // 建立新的節點
            cur.next = new ListNode(sum);
            // 移動指標
            cur = cur.next;
            // 移動連結串列指標,要判斷為空,否則會空針
            if(null != l1){
                l1 = l1.next;
            }
            if(null != l2){
                 l2 = l2.next;
            }
        }
        if(1 == temp){
            cur.next = new ListNode(1);
        }
        return head.next;
    }
1.3 debug 除錯
第一步,2+5

兩個連結串列的首節點相加,結果為 7。

【7】不需要進位,建立新的連結串列進行儲存。

連結串列演算法題之中等級別,debug除錯更簡單
第二步,4+6
連結串列演算法題之中等級別,debug除錯更簡單

結果為 10 就需要進位,將個位上的【0】儲存到節點中,十位上【1】需要進行進位。

第三步,3+4+1
連結串列演算法題之中等級別,debug除錯更簡單

最後看下執行結果

連結串列演算法題之中等級別,debug除錯更簡單

簡單總結下,這道題並不算難,但需要考慮清楚當節點相加時是否需要進行補位的情況。

二,刪除連結串列的倒數第 N 個結點

連結串列演算法題之中等級別,debug除錯更簡單
2.1 題目分析

這道題,是不是似曾相識?

連結串列演算法題之中等級別,debug除錯更簡單

沒錯,在上一篇文章中《連結串列演算法題二,還原題目,用 debug 除錯搞懂每一道題》有一道題是【連結串列中倒數第 k 個節點】。但是這兩道題之間略有不同,上一篇文章中的題目是返回倒數第 K 個節點,本道題中是移除第 K 個節點,返回其他完整連結串列。

那麼這兩道題相似度很高,是不是套路也是一樣。

上一道題我們使用了雙指標的方式,那本道題也是一樣的。所以上一道題如果搞懂了,那這道所謂中等級別的題也就成簡單級別的了。雖然本人目前題量不多,但是如果善於總結的話,套路確實很接近,反正這個題我是直接寫出來了,哈哈(開玩笑)。

連結串列演算法題之中等級別,debug除錯更簡單

話又說回來,分析題中的含義,假設移除節點【4】,按照雙指標的方式,那就是一個慢指標指向節點【3】,快指標指向節點【5】。將節點【3】的下下個 next 指向節點【5】,即可移除節點【4】。

連結串列演算法題之中等級別,debug除錯更簡單

參考上一道題的方式,需要將【fast】快指標先移動 K 個節點,初始化指標位置。

注意:移除節點後,是需要反回其它完整的連結串列節點。但是有一種情況有坑,先看下圖

連結串列演算法題之中等級別,debug除錯更簡單

連結串列只有一個節點並移除,正確結果應該是返回空。像這種情況是不能直接返回 head 連結串列,因此是需要建立頭指標來指引原始的連結串列,如下圖。

連結串列演算法題之中等級別,debug除錯更簡單

所以定義雙指標的起始節點位置就是 head 節點。

按照套路先將快指標移動 K 個節點

連結串列演算法題之中等級別,debug除錯更簡單

剩下的操作即移動快慢指標,直到 fast 指標移動到最後一個節點。
(1)
連結串列演算法題之中等級別,debug除錯更簡單
(2)
連結串列演算法題之中等級別,debug除錯更簡單
(3)
連結串列演算法題之中等級別,debug除錯更簡單

最後更改 slow 指標直接指向 fast 指標指向的節點即可。

2.2 程式碼分析

建立連結串列的程式碼同上題一樣,本道題只需要建立一個 1-5 節點的連結串列。

直接貼上刪除節點的程式碼

public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode pre = new ListNode();
        pre.next = head;
        // 定義雙指標
        ListNode slow = pre;
        ListNode fast = head;
        // 先將快指標移動n個節點
        while(--n>0){
            fast = fast.next;
        }
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return pre.next;
    }
2.3 debug 除錯

我們先 debug 除錯看下初始化節點位置後,快慢指標的位置。

連結串列演算法題之中等級別,debug除錯更簡單

接著進入第 2 個 while 迴圈,將剩餘的節點遍歷完。

連結串列演算法題之中等級別,debug除錯更簡單

這一步 slow 指標到節點【1】,fast 指標到節點【3】

下一步 slow 指標到節點【2】,fast 指標到節點【4】,直接看最後一步 slow 指標到節點【3】,fast 指標到節點【5】

連結串列演算法題之中等級別,debug除錯更簡單

節點【5】即最後一個節點,此時退出迴圈,最後將 slow 指標 next 指向 fast 指標指向的節點。

連結串列演算法題之中等級別,debug除錯更簡單

執行結果

連結串列演算法題之中等級別,debug除錯更簡單

三,兩兩互動連結串列中的節點

連結串列演算法題之中等級別,debug除錯更簡單
3.1 題目分析

說來慚愧,這道題當時寫的時候基本沒有什麼思路,結果還是看了題解才寫出來的。

現在想想這真是道靈魂題啊,真是沒想到還能用遞迴去寫這道題,不得不說真是萬能的遞迴啊(主要本人太菜,哈哈)。

遞迴的方式在於如果是偶數連結串列,將兩兩節點相互交換;如果是奇數連結串列,那最後一個節點保持不動,下面用 debug 除錯會看的清楚些。

將在偶數位上的節點指向上一個奇數位的節點,使用遞迴依次類推來遍歷整個連結串列。

大致的思路是這樣,使用遞迴將連結串列遍歷結束,然後返回最後節點【4】並指向上一個節點【3】;接著返回遞迴的結果【2】指向上一個節點【1】,而節點【1】也是指向節點【4】。

連結串列演算法題之中等級別,debug除錯更簡單

接著下次遞迴

連結串列演算法題之中等級別,debug除錯更簡單

按照規則分析,節點【4】與節點【3】交換位置,那返回的就是節點【4】

連結串列演算法題之中等級別,debug除錯更簡單

現在回到第一步遞迴的結果,當時 head 指向的節點【1】,那麼 head.next 指向誰?現在遞迴結果返回節點【4】,因此 head.next 也就指向的是節點【4】

最後節點【1】與節點【2】交換位置,就成了最後的連結串列交換結果。

這樣分析還是很抽象,下面用 debug 除錯走一遍就清晰了。

3.2 程式碼分析

遞迴的程式碼還是比較簡單,先貼上來。

public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode nextNode = head.next;
        head.next = swapPairs(nextNode.next);
        nextNode.next = head;
        return nextNode;
    }
3.3 debug 除錯
第一步,節點【1】和節點【2】
連結串列演算法題之中等級別,debug除錯更簡單

開始進入遞迴迴圈,此時 nextNode 節點為【2】,那該節點的下一個節點為【3】

第二步,節點【3】和節點【4】
連結串列演算法題之中等級別,debug除錯更簡單

現在 nextNode 節點為【4】,再次進入遞迴迴圈時,節點【4】的 next 就為 null,因為節點【4】為最後一個節點,開始結束遞迴。

連結串列演算法題之中等級別,debug除錯更簡單

現在開始返回遞迴的結果,首先返回的就是節點【3】和節點【4】

連結串列演算法題之中等級別,debug除錯更簡單

再看第 43 行程式碼,將節點【4】下一個節點指向了節點【3】,並返回了節點【4】

連結串列演算法題之中等級別,debug除錯更簡單

接著返回節點【1】和節點【2】

連結串列演算法題之中等級別,debug除錯更簡單

注意:上一步遞迴中,我們返回的結果為節點【4】

上圖中看到 head 節點為【1】,而 head.next 也就是節點【4】了

連結串列演算法題之中等級別,debug除錯更簡單

最後返回交換後的節點【1】

連結串列演算法題之中等級別,debug除錯更簡單
3.4 小補充

還記得上面我們說的,如果連結串列為奇數,最後結果如何呢?

現在接著上面的 debug 看下最後奇數的節點怎麼返回。

假設現在新增一個節點【5】

連結串列演算法題之中等級別,debug除錯更簡單

按照 if 判斷,節點【5】為最後一個節點,進入 if 判斷後就將節點【5】返回。

還記得我們上面說的 head.next 指標嗎,它指向的是遞迴返回的結果,我們最後一次遞迴的時候,head 不就是節點【3】嗎!

連結串列演算法題之中等級別,debug除錯更簡單 連結串列演算法題之中等級別,debug除錯更簡單

當節點【3】和節點【4】交換後,節點【3】不就正好指向了返回的節點【5】

連結串列演算法題之中等級別,debug除錯更簡單

四,總結

解決連結串列相關的題目,我們大多可以使用雙指標(快慢指標),陣列,遞迴,迭代這 4 種方式。

在做完簡單題目後,再加上本篇文章的 3 道中等題目,使用雙指標,遞迴就可解決大多數的題目。後面將中等題目刷完後,再來看看連結串列題目有多少是可以用上述幾種方式去解決。

最後,求關注

原創不易,每一篇都是用心在寫。如果對您有幫助,就請一鍵三連(關注,點贊,再轉發)

我是楊小鑫,堅持寫作,分享更多有意義的文章。

感謝您的閱讀,期待與您相識!

相關文章