题一:141. 环形链表
链接
编程思路
Me:
- 快慢指针若快指针越界则False,若快慢相遇则True,是环形链表找起点那一题的阉割版
力扣实战
思路一:快慢指针
class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
if not head:
return False
cur1 = head
cur2 = head
#快慢指针,只需考虑快指针是否越界
while cur1 and cur1.next and cur1.next.next:
cur1 = cur1.next.next
cur2 = cur2.next
if cur1==cur2:
return True
return False
# 反思1:
思路二 哈希表
class Solution:
def hasCycle(self, head: ListNode) -> bool:
seen = set()
while head:
if head in seen:
return True
seen.add(head)
head = head.next
return False
题二:148. 排序链表
链接
关键点
编程思路
Me:
力扣实战
思路一:归并排序(递归法)
cut 环节本质上是通过二分法得到链表最小节点单元,再通过多轮合并得到排序结果。
class Solution:
def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
#归并排序,二分法
if not head or not head.next:
return head # 终止
# cut the LinkedList at the mid index.
slow, fast = head, head.next
#快慢指针,快跑2慢跑1,快到终点慢在中间
while fast and fast.next:
fast, slow = fast.next.next, slow.next
mid, slow.next = slow.next, None # save and cut.
# recursive for cutting.
left, right = self.sortList(head), self.sortList(mid)
# merge `left` and `right` linked list and return it.
h = res = ListNode(0) #虚拟头结点
#合并链表,有一个为空时停止,把不空的放最后,返回到上一层进行合并
while left and right:
if left.val < right.val: h.next, left = left, left.next
else: h.next, right = right, right.next
h = h.next #找到下一个连的地方
h.next = left if left else right #不空的放最后
return res.next
# 反思1:
思路二:自底到顶 trick感浓郁,感觉不好推广至其他题
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def sortList(self, head: ListNode) -> ListNode:
h, length, intv = head, 0, 1
while h: #找到链表长度
h, length = h.next, length + 1
res = ListNode(0) #虚拟头结点
res.next = head
# merge the list in different intv.
while intv < length:
pre, h = res, res.next
while h:
# 获取当前最小长度intv下的h1的尾部
h1, i = h, intv
while i and h:
h, i = h.next, i - 1
if i: #i还不为0说明h1的长度都不满了,h2为空
break # 无需合并,到了最后,intv可倍乘了
h2, i = h, intv #此时h为h1尾部的下一个,也就是h2的头
while i and h: #找h2的尾部,也是下轮h1的头
h, i = h.next, i - 1
# 如果h2有元素则h1长度一定拉满,h2不一定
c1, c2 = intv, intv - i
# merge the `h1` and `h2`.
while c1 and c2:
if h1.val < h2.val:
pre.next, h1, c1 = h1, h1.next, c1 - 1
else:
pre.next, h2, c2 = h2, h2.next, c2 - 1
pre = pre.next
#若h1 h2中有一个较短,则把长的那部分剩余的放最后
pre.next = h1 if c1 else h2
while c1 > 0 or c2 > 0: #找到当前段的结尾
pre, c1, c2 = pre.next, c1 - 1, c2 - 1
pre.next = h #找到下一段翻转的开头
intv *= 2
return res.next