第89期-基礎演算法:遞迴 合併兩個有序連結串列
1 問題描述
將兩個升序連結串列合併為一個新的 升序 連結串列並返回。新連結串列是通過拼接給定的兩個連結串列的所有節點組成的。
示例 1:
輸入:l1 = [1,2,4], l2 = [1,3,4]
輸出:[1,1,2,3,4,4]
示例 2:
輸入:l1 = [], l2 = []
輸出:[]
示例 3:
輸入:l1 = [], l2 = [0]
輸出:[0]
初始程式碼
# Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.nextView Code= None class LinkList: def __init__(self): self.head=None def initList(self, data): while len(data)==0:return None self.head = ListNode(data[0]) r=self.head p = self.head for i in data[1:]: node = ListNode(i) p.next = node p= p.next return r def printlist(self,head): a=[] if head == None: return [] node = head while node != None: a.append(node.val) node = node.next return a class Solution: def mergeTwoLists(self,l1: ListNode, l2: ListNode) -> ListNode:#在此填寫程式碼 if __name__ == '__main__': print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([1,2,4]),LinkList().initList([1,3,4])))) print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([]),LinkList().initList([])))) print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([]),LinkList().initList([0]))))
2 解題思路
- 標籤:遞迴
- 終止條件:當兩個連結串列都為空時,表示我們對連結串列已合併完成。
- 如何遞迴:我們判斷 l1 和 l2 頭結點哪個更小,然後較小結點的 next 指標指向其餘結點的合併結果。(呼叫遞迴)
- 則當前位的值為(l1.val + l2.val + i) % 10
- 則進位值為(l1.val + l2.val + i) / 10
- 建立新node,然後將進位傳入下一層。
#3 解題方法
# Definition for singly-linked list. class ListNode: def __init__(self, x): self.val = x self.next = None class LinkList: def __init__(self): self.head=None def initList(self, data): while len(data)==0:return None self.head = ListNode(data[0]) r=self.head p = self.head for i in data[1:]: node = ListNode(i) p.next = node p = p.next return r def printlist(self,head): a=[] if head == None: return [] node = head while node != None: a.append(node.val) node = node.next return a class Solution: def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: import math if not l1 and not l2:return if (l1.val if l1 else math.inf) <(l2.val if l2 else math.inf): node=ListNode(l1.val) node.next=self.mergeTwoLists(l1.next,l2) else: node=ListNode(l2.val) node.next=self.mergeTwoLists(l1,l2.next) return node if __name__ == '__main__': print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([1,2,4]),LinkList().initList([1,3,4])))) print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([]),LinkList().initList([])))) print(LinkList().printlist(Solution().mergeTwoLists(LinkList().initList([]),LinkList().initList([0]))))View Code
第1-30,41-44行:題目中已經給出的資訊,執行程式碼時要根據這些程式碼進行編輯
第31行:引入math模組(後面會用到)
第32行:當兩個連結串列都遍歷完時,結束函式遞迴
第33行:判斷l1與l2當前節點值的大小(若不存在則節點值大小為無窮大(math.inf為無窮大))
第34行:若l1節點值小於l2節點值,則用l1節點值新建一個連結串列node
第35行:連結串列node的下一個元素進行遞迴操作,內部變數分別為l1連結串列的後一段連結串列以及l2連結串列
第36-38行:若l1節點值大於或等於於l2節點值,則用l2節點值新建一個連結串列node,並進行遞迴操作
第37行:返回node連結串列
程式碼執行結果為:
#演算法講解
這裡用到了基礎演算法:遞迴,簡單講解下這個演算法:
什麼是遞迴
程式呼叫自身的程式設計技巧稱為遞迴
遞迴做為一種演算法在程式設計語言中廣泛應用。
遞迴演算法一般用於解決三類問題:
(1)資料的定義是按遞迴定義的。(Fibonacci函式)
(2)問題解法按遞迴演算法實現。
(3)資料的結構形式是按遞迴定義的。
遞迴函式特徵
必須有一個明確的結束條件;
每次進入更深一層遞迴時,問題規模相比上次遞迴都應有所減少
相鄰兩次重複之間有緊密的聯絡,前一次要為後一次做準備(通常前一次的輸出就作為後一次的輸入)。
遞迴效率不高,遞迴層次過多會導致棧溢位(在計算機中,函式呼叫是通過棧(stack)這種資料結構實現的,每當進入一個函式呼叫,棧就會加一層棧幀,每當函式返回,棧就會減一層棧幀。由於棧的大小不是無限的,所以,遞迴呼叫的次數過多,會導致棧溢位)
#結構講解
這裡用到了基礎結構:連結串列,簡單講解下這個連結串列:
連結串列
連結串列是一組資料項的集合,其中每個資料項都是一個節點的一部分,每個節點還包含指向下一個節點的連結
連結串列的結構:data為自定義的資料,next為下一個節點的地址。
基本元素
節點:每個節點有兩個部分,左邊稱為值域,存放使用者資料;右邊部分稱為指標域,用來存放指向下一個元素的指標。
head:head節點永遠指向第一個節點
tail: tail永遠指向最後一個節點
None:連結串列中最後一個節點的指標域為None值