您好,登錄后才能下訂單哦!
本篇內容主要講解“分析鏈表和遞歸問題”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“分析鏈表和遞歸問題”吧!
鏈表具有天然的遞歸性,一個鏈表可以看出頭節點后面掛接一個更短的鏈表,這個更短的鏈表是以原鏈表的頭節點的下一節點為頭節點,依次內推,直到最后的更短的鏈表為空,空本身也是一個鏈表(最基礎的)。
以單鏈表 1->2->3->null 為例子,如下圖示:
原鏈表
將原鏈表看出頭節點 1 后掛接一個更短的鏈表
頭節點+更短鏈表
繼續拆解,直到無法拆解
更更短鏈表
更更更短鏈表
有了這樣的思考,很多「鏈表」相關問題,都可以采用「遞歸」的思路來解答。
定義一個函數,輸入一個鏈表的頭節點,反轉該鏈表并輸出反轉后鏈表的頭節點。 示例: 輸入: 1->2->3->4->5->NULL 輸出: 5->4->3->2->1->NULL 限制: 0 <= 節點個數 <= 5000
要反轉鏈表,即將原鏈表的頭節點變為新鏈表的尾節點,原鏈表的尾節點變為新鏈表的頭節點。如下圖示:
反轉之前:
原鏈表
反轉之后:
新鏈表
主要策略主要有:1、直接修改鏈表的值,如上圖中的原鏈表圖所示,將原鏈表值 1 的頭節點改為原鏈表尾節點的值,依次類推;2、讓遍歷整個鏈表,讓鏈表的尾節點指向其前一個節點,依次類推。
雖然這兩個策略都可行,不過在面試中通常要求采用「策略2」。
由上面的「遞歸與鏈表」可知,本題也可以采用「遞歸法」去求解。
具體如何通過「遞歸」去解答呢?見下面例子。
「舉例」
鏈表 1->2->3->null 為例子,如下圖示。
示例
不斷遍歷找到原鏈表為尾節點,即新鏈表的頭節點。
原鏈表尾節點
然后讓尾節點指向其前驅節點,依次類推。
遞歸反轉
詳細步驟,如下動圖示:
遞歸反轉單鏈表
「C」
struct ListNode* reverseList(struct ListNode* head){ /* 遞歸終止條件 */ if (head == NULL || head->next == NULL) { return head; } /* 反轉當前所在的鏈表節點 */ struct ListNode* newHead = reverseList(head->next); /* 由原鏈表的尾節點挨個指向其前驅節點 */ head->next->next = head; /* 防止成環 */ head->next = NULL; /* 返回新的鏈表頭節點 */ return newHead; }
「java」
ListNode reverseList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode node = reverseList(head.next); head.next.next = head; head.next = null; return node; }
當然本題也可以采用「迭代」的方法去做,其代碼(python 版)也很優雅,具體如下:
「python」
def reverseList(self, head: ListNode) -> ListNode: cur, pre = head, None while cur: cur.next, pre, cur = pre, cur, cur.next return pre
「復雜度分析」
時間復雜度:「O(n)」,n 是鏈表的長度。對鏈表的每個節點都進行了反轉操作。
空間復雜度:「O(n)」,n 是鏈表的長度。遞歸調用的棧空間,最多為 n 層。
給你一個鏈表的頭節點 head 和一個整數 val ,請你刪除鏈表中所有滿足 Node.val == val 的節點,并返回 新的頭節點 。 示例 1: 輸入:head = [1,2,6,3,4,5,6], val = 6 輸出:[1,2,3,4,5] 示例 2: 輸入:head = [], val = 1 輸出:[] 示例 3: 輸入:head = [7,7,7,7], val = 7 輸出:[]
要移除鏈表中給定值的節點,一般策略是「找到給點值的節點的前驅節點,然后讓其指向給定值的節點的后繼節點」。
例如要刪除鏈表 1->2->3->null 中,節點值為 3 的節點,就得先找到其前驅節點(值為 2 的節點),如下圖示:
刪除給定值的節點
由上面的「遞歸與鏈表」可知,本題同樣也可以采用「遞歸法」去求解,不斷刪除更短鏈表中給定值的節點,然后再將處理后的更短的鏈表,掛接在其前驅節點后。
「注意」最后要判斷原鏈表的頭節點是否是待移除的節點。
「舉例」
以鏈表 1->2->3->null 為例子,移除鏈表中給定值的節點的過程,如下動圖示。
示例動圖
「C++」
ListNode* removeElements(ListNode* head, int val) { /* 遞歸終止條件 */ if(head == NULL) { return NULL; } /* 刪除鏈表中頭節點后值為 val 的元素的節點 */ head->next=removeElements(head->next,val); /* 判斷頭節點是否為值為 val 的節點,再返回*/ return head->val==val ? head->next : head; }
「Golang」
func removeElements(head *ListNode, val int) *ListNode { if head == nil { return head } head.Next = removeElements(head.Next, val) if head.Val == val { return head.Next } return head }
「復雜度分析」
時間復雜度:「O(n)」,n 是鏈表的長度。遞歸需要遍歷鏈表一次。
空間復雜度:「O(n)」,n 是鏈表的長度。遞歸調用棧,最多不會超過 n 層。
到此,相信大家對“分析鏈表和遞歸問題”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。