日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

递归反转链表的一部分

發布時間:2024/4/11 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 递归反转链表的一部分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

遞歸反轉鏈表的一部分

反轉單鏈表的迭代實現不是?個困難的事情, 但是遞歸實現就有點難度了,如果再加?點難度, 讓你僅僅反轉單鏈表中的?部分, 你是否能夠遞歸實現呢?

本?就來由淺?深, 逐步地解決這個問題。 如果你還不會遞歸地反轉單鏈表也沒關系, 本?會從遞歸反轉整個單鏈表開始拓展, 只要你明?單鏈表的結構, 相信你能夠有所收獲

// 單鏈表節點的結構 public class ListNode {int val;ListNode next;ListNode(int x) { val = x; } }

什么叫反轉單鏈表的?部分呢, 就是給你?個索引區間, 讓你把單鏈表中這部分元素反轉, 其他部分不變

迭代的思路?概是: 先??個 for 循環找到第 m 個位置, 然后再??個 for 循環將 m 和 n 之間的元素反轉

但是我們的遞歸解法不??個 for 循環, 純遞歸實現反轉

?、 遞歸反轉整個鏈表

ListNode reverse(ListNode head) {if (head.next == null) return head;ListNode last = reverse(head.next);head.next.next = head;//相當于把head的next個節點的next指針指向自己//此時head節點和head的next節點之間相當于雙向鏈表//斷開head指向next個節點的指針,因為代碼走到這里說明//head的下一個next節點的next指針已經指向head節點了//完成反轉功能head.next = null;return last; }

我們下?來詳細解釋?下這段代碼

對于遞歸算法, 最重要的就是明確遞歸函數的定義。 具體來說, 我們的reverse 函數定義是這樣的:輸??個節點 head , 將「以 head 為起點」 的鏈表反轉, 并返回反轉之后的頭結點

明?了函數的定義, 在來看這個問題。 ?如說我們想反轉這個鏈表:

那么輸? reverse(head) 后, 會在這?進?遞歸:

ListNode last = reverse(head.next);

不要跳進遞歸(你的腦袋能壓?個棧呀? ) , ?是要根據剛才的函數定義,來弄清楚這段代碼會產?什么結果:

這個 reverse(head.next) 執?完成后, 整個鏈表就成了這樣:

并且根據函數定義, reverse 函數會返回反轉之后的頭結點, 我們?變量last 接收了

現在再來看下?的代碼:

head.next.next = head;

接下來:

head.next = null; return last;

神不神奇, 這樣整個鏈表就反轉過來了! 遞歸代碼就是這么簡潔優雅, 不過其中有兩個地?需要注意:

1、 遞歸函數要有 base case, 也就是這句:

if (head.next == null) return head; 意思是如果鏈表只有?個節點的時候反轉也是它??, 直接返回即可。

2、 當鏈表遞歸反轉之后, 新的頭結點是 last , ?之前的 head 變成了最后?個節點, 別忘了鏈表的末尾要指向 null:

head.next = null;

理解了這兩點后, 我們就可以進?步深?了, 接下來的問題其實都是在這個
算法上的擴展。

?、 反轉鏈表前 N 個節點

這次我們實現?個這樣的函數:

// 將鏈表的前 n 個節點反轉(n <= 鏈表?度) ListNode reverseN(ListNode head, int n)

?如說對于下圖鏈表, 執? reverseN(head, 3) :

解決思路和反轉整個鏈表差不多, 只要稍加修改即可:

ListNode successor = null; // 后驅節點// 反轉以 head 為起點的 n 個節點,返回新的頭結點 ListNode reverseN(ListNode head, int n) {if (n == 1) { // 記錄第 n + 1 個節點successor = head.next;return head;}// 以 head.next 為起點,需要反轉前 n - 1 個節點ListNode last = reverseN(head.next, n - 1);head.next.next = head;// 讓反轉之后的 head 節點和后面的節點連起來head.next = successor;return last; }

具體的區別:

1、 base case 變為 n == 1 , 反轉?個元素, 就是它本?, 同時要記錄后驅
節點。

2、 剛才我們直接把 head.next 設置為 null, 因為整個鏈表反轉后原來的head 變成了整個鏈表的最后?個節點。 但現在 head 節點在遞歸反轉之后不?定是最后?個節點了, 所以要記錄后驅 successor (第 n + 1 個節點) , 反轉之后將 head 連接上。

三、 反轉鏈表的?部分

現在解決我們最開始提出的問題, 給?個索引區間 [m,n] (索引從 1 開始) , 僅僅反轉區間中的鏈表元素。

ListNode reverseBetween(ListNode head, int m, int n)

?先, 如果 m == 1 , 就相當于反轉鏈表開頭的 n 個元素嘛, 也就是我們剛才實現的功能:

ListNode reverseBetween(ListNode head, int m, int n) {// base caseif (m == 1) {// 相當于反轉前 n 個元素return reverseN(head, n);}// ... }

如果 m != 1 怎么辦?如果我們把 head 的索引視為 1,那么我們是想從第 m 個元素開始反轉對吧;如果把 head.next 的索引視為 1 呢?那么相對于 head.next,反轉的區間應該是從第 m - 1 個元素開始的;那么對于 head.next.next 呢……

區別于迭代思想,這就是遞歸思想,所以我們可以完成代碼:

ListNode reverseBetween(ListNode head, int m, int n) {// base caseif (m == 1) {return reverseN(head, n);}// 前進到反轉的起點觸發 base casehead.next = reverseBetween(head.next, m - 1, n - 1);return head; }

四、 最后總結

遞歸的思想相對迭代思想, 稍微有點難以理解, 處理的技巧是: 不要跳進遞歸, ?是利?明確的定義來實現算法邏輯。

遞歸操作鏈表并不?效。 和迭代解法相?, 雖然時間復雜度都是 O(N), 但是迭代解法的空間復雜度是 O(1), ?遞歸解法需要堆棧, 空間復雜度是 O(N)。

總結

以上是生活随笔為你收集整理的递归反转链表的一部分的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。