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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

递归反转链表的一部分

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

遞歸反轉(zhuǎn)鏈表的一部分

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

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

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

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

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

但是我們的遞歸解法不??個 for 循環(huán), 純遞歸實現(xiàn)反轉(zhuǎn)

?、 遞歸反轉(zhuǎn)整個鏈表

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

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

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

明?了函數(shù)的定義, 在來看這個問題。 ?如說我們想反轉(zhuǎn)這個鏈表:

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

ListNode last = reverse(head.next);

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

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

并且根據(jù)函數(shù)定義, reverse 函數(shù)會返回反轉(zhuǎn)之后的頭結(jié)點, 我們?變量last 接收了

現(xiàn)在再來看下?的代碼:

head.next.next = head;

接下來:

head.next = null; return last;

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

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

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

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

head.next = null;

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

?、 反轉(zhuǎn)鏈表前 N 個節(jié)點

這次我們實現(xiàn)?個這樣的函數(shù):

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

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

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

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

具體的區(qū)別:

1、 base case 變?yōu)?n == 1 , 反轉(zhuǎn)?個元素, 就是它本?, 同時要記錄后驅(qū)
節(jié)點。

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

三、 反轉(zhuǎn)鏈表的?部分

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

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

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

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

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

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

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

四、 最后總結(jié)

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

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。