1. 程式人生 > >連結串列演算法題之中等級別,debug除錯更簡單

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

#### 文章簡述 大家好,本篇是個人的第 5 篇文章 從本篇文章開始,分享關於連結串列的題目為中等難度,本次共有 3 道題目。 #### 一,兩數相加 ##### 1.1 題目分析 題中寫到數字是按照逆序的方式儲存,從進位的角度看,兩兩節點相加我們是可以直接將進位傳遞到下一組兩兩節點相加。 比如題中第二組節點【4】和節點【6】相加結果為 10,而 10 就需要進位,也就是說該節點只能儲存數字【0】,而進位【1】就要傳遞到下一組節點相加。 那再整理下思路。 如果兩個連結串列的節點數是相等的,那隻需要依次將兩兩節點進行相加。如果節點數是不相等的,比如。 L2 連結串列少了一個節點,像這種情況我們就需要在高位用【0】進行補位。 我們再回到題中的案例,而上面說的位數不夠也是需要考慮的一種情況。再一步步分析下如何進行兩兩節點相加。
第一組節點相加為**2+5=7**,不滿足進位。建立一個新的連結串列儲存相加後的數,那此時連結串列第一個節點數為【7】。 接著是**4+6=10**,此時滿足進位要求,按照題目要求和我們上面的分析,需要將低位【0】儲存到節點,高位【1】傳遞到下一組節點。 那現在進行最後一組相加**3+4=7**,但是還有重要一步不能丟,即上一組節點相加時,還有高位進 1,那最後的結果是 3+4+1=8。 最後將上面相加的結果用連結串列進行儲存,那麼結果為。 同理,如果位數不足時用【0】進行補位也是一樣的方式。 ##### 1.2 程式碼分析 老方式,先建立單鏈表 ```java // 建立連結串列-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); ``` 兩數相加程式碼 ```java 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】不需要進位,建立新的連結串列進行儲存。
###### 第二步,4+6 結果為 10 就需要進位,將個位上的【0】儲存到節點中,十位上【1】需要進行進位。 ###### 第三步,3+4+1 最後看下執行結果 **簡單總結下,這道題並不算難,但需要考慮清楚當節點相加時是否需要進行補位的情況。** #### 二,刪除連結串列的倒數第 N 個結點 ##### 2.1 題目分析 這道題,是不是似曾相識? 沒錯,在上一篇文章中《連結串列演算法題二,還原題目,用 debug 除錯搞懂每一道題》有一道題是【連結串列中倒數第 k 個節點】。但是這兩道題之間略有不同,上一篇文章中的題目是返回倒數第 K 個節點,本道題中是移除第 K 個節點,返回其他完整連結串列。 那麼這兩道題相似度很高,是不是套路也是一樣。 上一道題我們使用了雙指標的方式,那本道題也是一樣的。所以上一道題如果搞懂了,那這道所謂中等級別的題也就成簡單級別的了。雖然本人目前題量不多,但是如果善於總結的話,套路確實很接近,反正這個題我是直接寫出來了,哈哈(開玩笑)。
話又說回來,分析題中的含義,假設移除節點【4】,按照雙指標的方式,那就是一個慢指標指向節點【3】,快指標指向節點【5】。將節點【3】的下下個 next 指向節點【5】,即可移除節點【4】。 參考上一道題的方式,需要將【fast】快指標先移動 K 個節點,初始化指標位置。 > 注意:移除節點後,是需要反回其它完整的連結串列節點。但是有一種情況有坑,先看下圖 連結串列只有一個節點並移除,正確結果應該是返回空。像這種情況是不能直接返回 head 連結串列,因此是需要建立**頭指標**來指引原始的連結串列,如下圖。 所以定義雙指標的起始節點位置就是 head 節點。 按照套路先將快指標移動 K 個節點 剩下的操作即移動快慢指標,直到 fast 指標移動到最後一個節點。 (1) (2) (3) 最後更改 slow 指標直接指向 fast 指標指向的節點即可。 ##### 2.2 程式碼分析 建立連結串列的程式碼同上題一樣,本道題只需要建立一個 1-5 節點的連結串列。 直接貼上刪除節點的程式碼 ```java 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 除錯看下初始化節點位置後,快慢指標的位置。 接著進入第 2 個 while 迴圈,將剩餘的節點遍歷完。 **這一步 slow 指標到節點【1】,fast 指標到節點【3】** 下一步 slow 指標到節點【2】,fast 指標到節點【4】,直接看最後一步 slow 指標到節點【3】,fast 指標到節點【5】 節點【5】即最後一個節點,此時退出迴圈,最後將 slow 指標 next 指向 fast 指標指向的節點。 執行結果 #### 三,兩兩互動連結串列中的節點 ##### 3.1 題目分析 說來慚愧,這道題當時寫的時候基本沒有什麼思路,結果還是看了題解才寫出來的。 ![](https://yx-1305006541.cos.ap-guangzhou.myqcloud.com/img/generalpicture/clipboard_20210303_100536.png) 現在想想這真是道靈魂題啊,真是沒想到還能用遞迴去寫這道題,不得不說真是萬能的遞迴啊(主要本人太菜,哈哈)。 遞迴的方式在於如果是偶數連結串列,將兩兩節點相互交換;如果是奇數連結串列,那最後一個節點保持不動,下面用 debug 除錯會看的清楚些。 將在偶數位上的節點指向上一個奇數位的節點,使用遞迴依次類推來遍歷整個連結串列。 **大致的思路是這樣,使用遞迴將連結串列遍歷結束,然後返回最後節點【4】並指向上一個節點【3】;接著返回遞迴的結果【2】指向上一個節點【1】,而節點【1】也是指向節點【4】。** 接著下次遞迴 按照規則分析,節點【4】與節點【3】交換位置,那返回的就是節點【4】 現在回到第一步遞迴的結果,當時 head 指向的節點【1】,那麼 head.next 指向誰?現在遞迴結果返回節點【4】,因此 head.next 也就指向的是節點【4】 最後節點【1】與節點【2】交換位置,就成了最後的連結串列交換結果。 這樣分析還是很抽象,下面用 debug 除錯走一遍就清晰了。 ##### 3.2 程式碼分析 遞迴的程式碼還是比較簡單,先貼上來。 ```java 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】 開始進入遞迴迴圈,此時 nextNode 節點為【2】,那該節點的下一個節點為【3】 ###### 第二步,節點【3】和節點【4】 現在 nextNode 節點為【4】,再次進入遞迴迴圈時,節點【4】的 next 就為 null,因為節點【4】為最後一個節點,開始結束遞迴。 現在開始返回遞迴的結果,首先返回的就是節點【3】和節點【4】 再看第 43 行程式碼,將節點【4】下一個節點指向了節點【3】,並返回了節點【4】 接著返回節點【1】和節點【2】 > 注意:上一步遞迴中,我們返回的結果為節點【4】 **上圖中看到 head 節點為【1】,而 head.next 也就是節點【4】了** 最後返回交換後的節點【1】 ###### 3.4 小補充 還記得上面我們說的,如果連結串列為奇數,最後結果如何呢? 現在接著上面的 debug 看下最後奇數的節點怎麼返回。 假設現在新增一個節點【5】 按照 if 判斷,節點【5】為最後一個節點,進入 if 判斷後就將節點【5】返回。 還記得我們上面說的 head.next 指標嗎,它指向的是遞迴返回的結果,我們最後一次遞迴的時候,head 不就是節點【3】嗎! **當節點【3】和節點【4】交換後,節點【3】不就正好指向了返回的節點【5】** #### 四,總結 解決連結串列相關的題目,我們大多可以使用雙指標(快慢指標),陣列,遞迴,迭代這 4 種方式。 在做完簡單題目後,再加上本篇文章的 3 道中等題目,使用雙指標,遞迴就可解決大多數的題目。後面將中等題目刷完後,再來看看連結串列題目有多少是可以用上述幾種方式去解決。 #### 最後,求關注 原創不易,每一篇都是用心在寫。如果對您有幫助,就請一鍵三連(**關注**,點贊,再轉發) 我是楊小鑫,堅持寫作,分享更多有意義的文章。 感謝您的閱讀,期待與您相識! ![](https://img2020.cnblogs.com/blog/1655301/202103/1655301-20210304141323723-4383312