Golang從合併連結串列聊遞迴

那肉香腸發表於2020-09-19

從合併連結串列聊遞迴

遞迴是工程師最常見的一種解決問題的方式,但是有時候不容易真正掌握。有人說是看起來很簡單,自己寫起來會費點勁。

最著名的例子就是斐波那契數列(Fibonacci sequence),透過尋找遞推公式來計算出結果。
而最近刷到的一道合併連結串列的演算法題,也可以使用遞迴來實現。下面看看題目描述吧:

將兩個升序連結串列合併為一個新的 升序 連結串列並返回。新連結串列是透過拼接給定的兩個連結串列的所有節點組成的。
示例:
輸入:1->2->4, 1->3->4輸出:1->1->2->3->4->4來源:力扣(LeetCode)

先丟擲本人觀點,遞迴的關鍵是:找到 邊界條件遞迴公式

分析一下題目,可以發現用第一個連結串列l1的頭部節點來去和l2的節點對比,如果大於l2的當前節點,那麼偏移l1的next和l2繼續對比大小。反之如果l1的頭節點對比L2的當前節點更小,那麼就需要對l2做類似處理。
這種不斷對比和偏移的過程,可以總結出一種遞迴公式。
用虛擬碼寫法就是:

if l1.val < l2.val:
    l1.next = mergeTwoList(l1.next, l2)    
    return l1else:
   l2.next = mergeTwoList(l1, l2.next)   return l2

而邊界條件就是在不斷偏移的時候,走到某個連結串列的最後一個節點為止,虛擬碼就是:

if l1 === null:    return l2if l2 === null:    return l1

用golang來實現,程式碼也很清晰:

type ListNode struct {
	Val int
	Next *ListNode
}func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {	if l1 == nil {		return l2
	}	if l2 == nil {		return l1
	}	if l1.Val < l2.Val {
		l1.Next = mergeTwoLists(l1.Next, l2)		return l1
	} else {
		l2.Next = mergeTwoLists(l1, l2.Next)		return l2
	}
}

在LeetCode裡面提交,執行反饋如下:

執行結果:
透過
顯示詳情
執行用時:
0 ms, 在所有 Go 提交中擊敗了
100.00%
的使用者
記憶體消耗:
2.6 MB, 在所有 Go 提交中擊敗了
63.64%
的使用者

可以看到遞迴是非常消耗記憶體的,它迴圈呼叫,猶如爾羅斯套娃,一層一層返回內層的呼叫結果。

如果要最佳化的話可以使用迭代方式來實現,程式碼需要做一些調整:

func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {
    head := &ListNode{}
	result := head	for l1 != nil && l2 != nil {		if l1.Val < l2.Val {
			head.Next = l1
			head = head.Next
			l1 = l1.Next
		} else {
			head.Next = l2
			head = head.Next
			l2 = l2.Next
		}
	}	if l1 == nil {
		head.Next = l2
	}	if l2 == nil {
		head.Next = l1
	}	return result.Next
}

可以看出需要建立一個頭部指標來做偏移,而最終result作為一個合成結果連結串列來儲存結果。
最後提交執行,發現結果資料稍微好看了一丟丟:

執行用時:
4 ms, 在所有 Go 提交中擊敗了
62.28%
的使用者
記憶體消耗:
2.5 MB, 在所有 Go 提交中擊敗了
100.00%
的使用者

由於在資料量不大的情況下,其實效能差距也不大,所以使用遞迴也是沒有毛病的。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69959503/viewspace-2722467/,如需轉載,請註明出處,否則將追究法律責任。

相關文章