0023_合并K個排序鏈表
題目描述
合并 k 個排序鏈表,返回合并後的排序鏈表。請分析和描述演算法的複雜度。
樣本:
輸入:[ 1->4->5, 1->3->4, 2->6]輸出: 1->1->2->3->4->4->5->6
演算法
type ListNode struct { Val int Next *ListNode}func mergeKLists(lists []*ListNode) *ListNode { n := len(lists) switch n { case 0: return nil case 1: return lists[0] case 2: //針對兩個鏈表進行歸併排序 return merge(lists[0], lists[1]) default: key := n / 2 //數組拆分,使下一次遞迴的lists的長度=2 //最佳化思路: mergeKLists(lists[:key]),使用Goroutine+channel進行並發合并(歸併排序的特點) return mergeKLists([]*ListNode{mergeKLists(lists[:key]), mergeKLists(lists[key:])}) }}//merge 對兩個有序鏈表進行歸併排序func merge(left *ListNode, right *ListNode) *ListNode { //head: 新的鏈表的head指標,保持不變 //tail: 新鏈表的尾指標 var head, tail *ListNode if left == nil { return right } if right == nil { return left } if left.Val < right.Val { head, tail, left = left, left, left.Next } else { head, tail, right = right, right, right.Next } //迴圈,直到某一個鏈表已遍曆完 for left != nil && right != nil { //找到下一個節點,添加到新鏈表的尾 if left.Val < right.Val { tail.Next, left = left, left.Next } else { tail.Next, right = right, right.Next } //更新tail tail = tail.Next } //剩下的節點位元組拼接到新鏈表尾部 if left != nil { tail.Next = left } if right != nil { tail.Next = right } return head}
個人思路
1. 對已經有序的多個鏈表進行合并,可以借鑒歸併排序,分治法的思想,層層遞迴2. 兩個鏈表可以進行遍曆比較節點大小,合并為一個新的鏈表
總結
- 分治法的特點:各層分治遞迴可以同時進行,最佳化思路可以採用Goroutine+channel,在此筆者就不進行最佳化了,不是本文重點
遞迴優缺點
優點:
1. 代碼簡潔
缺點:
1. 空間消耗大,每一次函數調用都需要在記憶體棧中分配空間儲存參數,返回地址以及臨時變數2. 棧裡面壓入和彈出都需要時間3. 遞迴有棧溢出的問題
迴圈
優點
1. 和遞迴相比,空間消耗小
缺點
1. 代碼可讀性不如遞迴
GitHub
- 項目源碼在這裡
- 筆者會一直維護該項目,對leetcode中的演算法題進行解決,並寫下自己的思路和見解,致力於人人都能看懂的演算法
個人公眾號
- 喜歡的朋友可以關注,謝謝支援
- 記錄90後碼農的學習與生活