21. Merge Two Sorted Lists
1. 原始题目
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
2. 题目理解
合并两个排序的链表,使得结果仍然是排序的。
坑:输入为空链表单独处理。
3. 解题之前:Python实现链表基础功能
在解题之前,我想先实现一下python中的链表,这样方便在本地调试。实现链表的基础功能,例如:添加,插入,删除,查找,打印。
思路:首先定义每个结点类型。在leetcode中,大都是如下的类型:(python中没有指针。所以实现节点时,用一个简单类就好。该节点包含数据和“指针next”。)
1) 结点定义
class ListNode: def __init__(self, x): self.val = x # 存放数据 self.next = None # 存放该结点的下一个结点的地址(其实就是当前结点.next = 下一个结点)
2)链表实现
1 class ListNode_handle: # 链表操作类型 2 def __init__(self, node): 3 self.head = node # 该类需要初始化头节点和当前节点。即用一个链表的头结点来实例化,可以用ListNode_handle(None)来实例化一个空链表 4 self.cur_node = node 5 6 7 def add(self, data): # 添加结点操作,该结点的数值为data 8 node = ListNode(None) # 新建ListNode类型 9 node.val = data 10 node.next = None 11 if self.head == None: # 若当前链表为空,则直接将其作为头结点 12 self.head = node 13 self.cur_node = node 14 else: # 否则将该结点连到后面,将当前结点更新 15 self.cur_node.next = node 16 self.cur_node = self.cur_node.next 17 return self.head 18 19 20 def insert_i(self, i, data): # 在第i个位置插入一个数值为data的结点 21 node = ListNode(None) 22 node.val = data 23 node.next = None 24 25 curr_node = self.head 26 for j in range(i-1): # 索引到位置i 27 curr_node = curr_node.next 28 node.next = curr_node.next 29 curr_node.next = node 30 31 32 def delete_i(self,i): # 删除位置为i的结点 33 34 curr_node = self.head 35 for j in range(i-1): # 索引到位置为i的结点 36 curr_node = curr_node.next 37 curr_node.next = curr_node.next.next 38 39 40 def print_node(self,node): # 从头开始打印所有结点的数值 41 while node: 42 print(node.val, end=' ') 43 node = node.next 44 print(end='\n') 45 46 47 def find_node(self, value): # 寻找链表中是否存在数值value 48 if self.head == None: 49 return False 50 else: 51 node = self.head 52 while(node): 53 if node.val == value: 54 return True 55 else: 56 node = node.next 57 return False
好了,以上就是链表操作类型。如果要新建一个结点对象,就实例化类 ListNode。 如果要新建一个链表对象,就实例化类 ListNode_handle。
3)为了演示方便,我们将题目中的两个链表建立起来:
# 新建链表1 listnode1 = ListNode_handle(None) s1 = [1,2,4] for i in s1: listnode1.add(i) listnode1.print_node(listnode1.head)
打印:1 2 4
# 新建链表2 listnode2 = ListNode_handle(None) s2 = [1,3,4] for i in s2: listnode2.add(i) listnode2.print_node(listnode2.head)
打印:1 3 4
好了,下面正式进入正题,实现两个有序链表的合并。
4. 解法
1) 传统解法
思路:先定义一个头结点p,然后两个指针同时指向l1和l2,比较谁更小,更小者赋给p,然后指针后移。到最后如果有哪个链表不为空,则直接将其连在已排序好的链表末尾。注意,无需新建空链表,只在原有基础上链接。注意对输入为空链表情况的处理。
1 class Solution: 2 def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: 3 if l1 == None: 4 return l2 5 if l2 == None: 6 return l1 7 p = ListNode(0) 8 q = p 9 while l1 and l2: 10 if (l1.val<l2.val): 11 p.next = l1 12 l1 = l1.next 13 else: # 小坑: 注意这里不可将 else 替换为 if(l1.val>=l2.val),if else是非a即b,而两个if语句则可能同时满足条件。。。 14 p.next = l2 15 l2 = l2.next 16 p = p.next
# 如果合并之后有链表还不为空,则直接将其连在排好序的链表之后 17 if l1 : # 小技巧:这一下几句可以合并为 p.next = l1 or l2 18 p.next = l1 19 if l2: 20 p.next = l2 21 22 return q.next # 因为第一个结点是最开始定义的一个无效结点(头结点) 而这里我们一般不需要头结点存储额外信息,链表的第一个结点就是有意义的第一个结点
2)Leetcode和剑指offer里给出了递归的解法:
因为有重复的步骤,是典型的递归过程。
1 class Solution: 2 3 def mergeTwoListsRecursion(self, l1: ListNode, l2: ListNode) -> ListNode: 4 if l1 == None: 5 return l2 6 if l2 == None: 7 return l1 8 p = ListNode(0) 9 10 if(l1.val<l2.val): 11 p = l1 12 p.next = self.mergeTwoListsRecursion(l1.next, l2) 13 else: 14 p = l2 15 p.next = self.mergeTwoListsRecursion(l1, l2.next) 16 17 return p
5. 验证合并结果
我们将合并后的链表打印出来,看看是否满足我们的预期:
1 # 合并两个有序链表 2 s = Solution() 3 newlist_head = s.mergeTwoLists(listnode1.head, listnode2.head) # 非递归 4 #newlist_head = s.mergeTwoListsRecursion(listnode1.head, listnode2.head) # 递归 5 newlist = ListNode_handle(newlist_head) # 递归或者非递归返回的都是表头,所以可以直接用第一个结点来实例化类 ListNode_handle
6 newlist.print_node(newlist.head) # 打印该链表
结果: 1 1 2 3 4 4
6. 验证其他功能
1 # 查找值为3的结点是否存在 2 print(newlist.find_node(3)) 3 4 # 在链表第三个位置插入666 5 newlist.insert_i(3,666) 6 newlist.print_node(newlist.head) 7 8 # 删除链表第三个位置的元素 9 newlist.delete_i(3) 10 newlist.print_node(newlist.head)
True
1 1 2 666 3 4 4
1 1 2 3 4 4