链表篇
2. 两数相加
- 思路1:采取双指针,一一对应相加,过程中记录进位以及考虑双指针是否为空
class Solution(object):
def addTwoNumbers(self, l1, l2):
ret,cin = ListNode(0),0
pre = ret
while l1 or l2:
if l1 and l2:
val = (l1.val + l2.val + cin) % 10
cin = (l1.val + l2.val + cin) // 10
elif l1:
val = (l1.val + cin) % 10
cin = (l1.val + cin) // 10
elif l2:
val = (l2.val + cin) % 10
cin = (l2.val + cin) // 10
tmp = ListNode(val)
pre.next = tmp
pre = tmp
if l1: l1 = l1.next
if l2: l2 = l2.next
if cin:
pre.next = ListNode(cin) # 默认新建节点next 为None 符合链表尾节点定义
return ret.next
总结:头结点不确定时,可引入哨兵节点
445. 两数相加 II
- 思路1:将链表进行逆序,采取逆序的方式去保存每位数字,然后一一对应相加,过程中记录进位以及考虑双指针是否为空
- 思路2:在l1和l2中的短链表前面填充节点0,直到两个链表长度一致 一一对应相加即可,会多耗费空间
# 思路1
class Solution(object):
def addTwoNumbers(self, l1, l2):
l1 = self.reverseList(l1)
l2 = self.reverseList(l2)
ret,cin = ListNode(0),0
pre = ret
while l1 or l2:
if l1 and l2:
val = (l1.val + l2.val + cin) % 10
cin = (l1.val + l2.val + cin) // 10
elif l1:
val = (l1.val + cin) % 10
cin = (l1.val + cin) // 10
elif l2:
val = (l2.val + cin) % 10
cin = (l2.val + cin) // 10
tmp = ListNode(val)
pre.next = tmp
pre = tmp
if l1: l1 = l1.next
if l2: l2 = l2.next
if cin:
pre.next = ListNode(cin) # 默认新建节点next 为None 符合链表尾节点定义
ret = self.reverseList(ret.next)
return ret
def reverseList(self,head):
pre,cur = None,head
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
# 思路2
class Solution:
def __init__(self):
self.cin = 0
def addTwoNumbers(self,l1,l2):
ret = ListNode(0)
if not l1: return l2
if not l2: return l2
cur1,cur2 = l1,l2
while cur1 or cur2: # 在短的链表前面 填充节点为0的节点
if cur1 and not cur2:
tmp = ListNode(0)
tmp.next = l2
l2 = tmp
if cur2 and not cur1:
tmp = ListNode(0)
tmp.next = l1
l1 = tmp
if cur1: cur1 = cur1.next
if cur2: cur2 = cur2.next
self.dfs(l1,l2,ret)
if self.cin: # 存在进位 在最前面添加节点,值为1
cur = ListNode(1)
tmp = ret.next
ret.next = cur
cur.next = tmp
return ret.next
def dfs(self,l1,l2,ret):
if not l1 and not l2: return
self.dfs(l1.next,l2.next,ret) # 后序
cur = ListNode((l1.val+l2.val+self.cin)%10)
self.cin = (l1.val+l2.val+self.cin) // 10
tmp = ret.next
ret.next = cur
cur.next = tmp
总结:链表存储非负整数,每位数字一般采取 逆序方式!
143. 重排链表
- 思路1:链表涉及到前半部分、后半部分概念时,可采取快慢指针进行链表二分,对后半部分链表反转,然后一一比较
class Solution(object):
def reorderList(self, head):
# 链表分成两半 用快慢指针
pre,cur = head,head
while cur and cur.next:
pre = pre.next
cur = cur.next.next
tmp = pre.next
pre.next = None
pre,cur = None,tmp # 链表反转
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
cur = head
while pre: # 一一重排
tmp = cur.next
pre_next = pre.next
cur.next = pre
pre.next = tmp
cur = tmp
pre = pre_next
return head
总结:链表二分,用快慢指针
21. 合并两个有序链表(面试高频)
- 思路1:引入哨兵节点,一一比较两个有序链表元素,小的接到哨兵节点后面
class Solution(object):
def mergeTwoLists(self, l1, l2):
head = ListNode() #辅助头节点
cur = head
while(l1 and l2):
if(l1.val <= l2.val):
cur.next = l1
l1 = l1.next
else:
cur.next = l2
l2 = l2.next
cur = cur.next
cur.next = l1 if l1 else l2
return head.next
总结:头结点不确定,引入哨兵节点
148. 排序链表
- 思路1:链表排序,采取归并排序 无需额外数组;归并排序先 递归二分链表,然后对 两个有序子链表采取合并
- 思路2:借助数组,把链表节点放入数组中,对数组自定义排序,根据节点val排序,然后把数组中排序后的节点连接起来
# 链表归并排序
class Solution(object):
def sortList(self, head):
# 链表为了节约 空间,可采取归并排序
if not head or not head.next: return head
cur1,cur2 = head,self.mid(head)
cur1 = self.sortList(cur1)
cur2 = self.sortList(cur2)
return self.merge(cur1,cur2)
def mid(self,head): # 链表二分
if not head or not head.next: return head
tmp = ListNode(0,next = head) # 头结点可能变化 因此需要引入哨兵节点
pre,cur = tmp,tmp
while cur and cur.next:
pre = pre.next
cur = cur.next.next
p = pre.next
pre.next = None # 中间断掉链表
return p
def merge(self,cur1,cur2):
if not cur1: return cur2
if not cur2: return cur1
if cur1.val < cur2.val:
cur1.next = self.merge(cur1.next,cur2)
return cur1
else:
cur2.next = self.merge(cur1,cur2.next)
return cur2
# 借助数组
class Solution(object):
def sortList(self, head):
if not head: return None
nums = []
while head:
nums.append(head)
head = head.next
nums.sort(key=lambda x:x.val)
for i in range(len(nums)-1):
nums[i].next = nums[i+1]
nums[-1].next = None
return nums[0]
总结:链表排序采取归并排序,链表最后一个元素指向None
23. 合并k个升序链表(hard)
- 思路1:两两有序链表合并,合并k-1次,时间复杂度较高
- 思路2:采取归并排序的思想,递归完成两个有序子链表的排序
# 思路1
class Solution(object):
def mergeKLists(self, lists):
if not lists: return None
head = lists[0]
for i in range(1,len(lists)):
head = self.mergeTwoLists(head,lists[i])
return head
def mergeTwoLists(self,l1,l2):
auxi = ListNode(0)
pre = auxi
while l1 and l2:
if l1.val <= l2.val:
pre.next = l1
l1 = l1.next
else:
pre.next = l2
l2 = l2.next
pre = pre.next
if l1: pre.next = l1
if l2: pre.next = l2
return auxi.next
# 思路2
class Solution(object):
def mergeKLists(self, lists):
if not lists: return None
i,j = 0,len(lists)-1
return self.dfs(lists,i,j)
def dfs(self,lists,i,j):
if i>=j: return lists[i]
mid = (i+j) // 2
cur1 = self.dfs(lists,i,mid)
cur2 = self.dfs(lists,mid+1,j)
return self.mergeTwoLists(cur1,cur2)
def mergeTwoLists(self,l1,l2):
auxi = ListNode(0)
pre = auxi
while l1 and l2:
if l1.val <= l2.val:
pre.next = l1
l1 = l1.next
else:
pre.next = l2
l2 = l2.next
pre = pre.next
if l1: pre.next = l1
if l2: pre.next = l2
return auxi.next
总结:合并k个有序链表,可以采取 归并排序二分的思想,然后对有序子链表排序
25. k个一组翻转链表(hard)
- 思路1:引入哨兵节点 遍历链表,依次反转节点 ,记录每k个节点的起始节点 当计数为k时。更新链表连接指向,并更新起始节点,如果发现 最终起始节点不为空,说明最后 一组节点没有k个(但是还是在遍历过程中被反转了),因此需要再反转回来
class Solution(object):
def reverseKGroup(self, head, k):
if not head or not head.next: return head
cnt,ret = 0,ListNode(0,next=head)
pre,cur = ret,head
preNode,start,end = None,head,None
while cur:
cnt += 1
tmp = cur.next
if not tmp: end = cur
cur.next = preNode
preNode = cur
if cnt == k:
pre.next = cur
pre = start
start = tmp
cnt = 0
preNode = None
cur = tmp
preNode = None
if start: #
while end: # 最后 一组节点没有k个(但是还是在遍历过程中被反转了),因此需要再反转回来
tmp = end.next
end.next = preNode
preNode = end
end = tmp
pre.next = start
return ret.next
总结:头结点不确定,引入哨兵节点