文章目录
1.1 完全反转
- 迭代
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
p = head
pre = None
while p:
aft = p.next
p.next = pre
pre = p
p = aft
return pre
- 递归
递归法就记一张图 — 递归反转链表的一部分
1->2->3->^
1->2<-3
|->^
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return head
last = self.reverseList(head.next)
head.next.next = head
head.next = None
return last
1.2 部分反转
结合对1.1
递归法的学习,将后继None
替换成本场景的successor
,我们看看commits:
class Solution:
successor = None
def reverseN(self, head: ListNode, N=inf) -> ListNode:
if N == 1 or head is None or head.next is None:
self.successor = head.next
return head
last = self.reverseN(head.next, N - 1)
head.next.next = head
head.next = self.successor
return last
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
if left == 1:
return self.reverseN(head, right)
head.next = self.reverseBetween(head.next, left - 1, right - 1)
return head
1.3 K个一组反转
class Solution:
def reverse(self, a, b):
# 相当于左开右闭,例如k=2,
# 1->2->3
# a↑ b↑
# 最后返回是的 2->1->∅
pre = None
p = a
while p != b:
aft = p.next
p.next = pre
pre = p
p = aft
return pre
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
a = b = head
# 循环体写错 (老错误)
for _ in range(k):
if b is None:
return head
b = b.next
new_head = self.reverse(a, b)
# 把 a 写成了 new_head
a.next = self.reverseKGroup(b, k) # b 写错 (老错误)
return new_head
2. 链表相交 + 链表找环
本文还有大量没有考虑到的情况,可以看相关博客
数据结构及算法:链表相关(如何判断单链表是否存在环、找出入环点、链表相交等问题)
插图出自"求两个链表的相交点(含链表带环情况)",本文2.1 普通相交讨论case 0
,2.2 普通有环讨论case 1
,2.3 相交有环讨论case 2
, case 3
。2.3 相交有环对4种情况都做了总结,考虑了所有边界条件。
2.1 普通相交
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
a, b = headA, headB
while a != b:
a = a.next if a else headB
b = b.next if b else headA
return a
class Solution:
def lowestCommonAncestor(self, p: 'Node', q: 'Node') -> 'Node':
a, b = p, q
while a != b:
a = a.parent if a else q
b = b.parent if b else p
return a
2.2 普通有环
2.2.1 判断是否有环
class Solution:
def hasCycle(self, head: ListNode) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False
2.2.2 找出环的入口
如果快慢指针是在写不出来,可以用hash表来写,就是空间复杂度会变成 O ( N ) O(N) O(N)
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
vis = {}
p = head
while p:
id_ = id(p)
if id_ in vis:
return vis[id_]
vis[id_] = p
p = p.next
return None
快慢指针
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if fast and fast.next:
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
return None
2.3 相交有环
本题LeetCode没有,笔者结合博客插图的测试用例,设计了4个单元测试
- 核心代码
class ListNode:
'''链表结点定义'''
def __init__(self, val, next=None):
self.next = next
self.val = val
def getIntersectionNode(headA: ListNode, headB: ListNode) -> ListNode:
'''headA,headB均无环时,求两个链表的交点(如果没有交点,返回None)'''
a, b = headA, headB
while a != b:
a = a.next if a else headB
b = b.next if b else headA
return a
def detectCycle(head: ListNode) -> ListNode:
'''找环入口点函数'''
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if fast and fast.next:
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
return None
def detectIntersect(headA: ListNode, headB: ListNode) -> ListNode:
'''找到headA与headB的交点,考虑多种边界条件'''
if headA is None or headB is None:
return None
entry1 = detectCycle(headA)
entry2 = detectCycle(headB)
if entry1 is None and entry2 is None:
return getIntersectionNode(headA, headB)
if entry1 is None or entry2 is None:
return None
# 环外相交
if entry1 == entry2:
# 临时将环解开,然后用【普通链表相交】的方法做就行了
entry1_next = entry1.next
entry1.next = None
ret = getIntersectionNode(headA, headB)
entry1.next = entry1_next
return ret
# 环内相交
else:
# 返回任一相交结点即可
return entry1
- 测试代码
# =============================
# ==========测试代码============
# =============================
def get_nodes():
ret = {}
for i in list(range(1, 11)) + list(range(11, 56, 11)):
ret[i] = ListNode(i)
return ret
def test_0():
'''两个链表都不带环'''
nodes = get_nodes()
for i in range(1, 10):
nodes[i].next = nodes[i + 1]
for i in range(11, 45, 11):
nodes[i].next = nodes[i + 11]
nodes[55].next = nodes[4]
assert detectIntersect(nodes[1], nodes[11]).val == 4
def test_1():
'''两个链表其中一个带环'''
nodes = get_nodes()
for i in range(1, 10):
nodes[i].next = nodes[i + 1]
nodes[10].next = nodes[5]
for i in range(11, 34, 11):
nodes[i].next = nodes[i + 11]
assert detectIntersect(nodes[1], nodes[11]) is None
def test_2():
'''两个都带环,交点在环外'''
nodes = get_nodes()
for i in range(1, 10):
nodes[i].next = nodes[i + 1]
nodes[10].next = nodes[5]
for i in range(11, 34, 11):
nodes[i].next = nodes[i + 11]
nodes[33].next = nodes[4]
assert detectIntersect(nodes[1], nodes[11]).val == 4
def test_3():
'''两个都带环,交点在环内'''
nodes = get_nodes()
for i in range(1, 10):
nodes[i].next = nodes[i + 1]
nodes[10].next = nodes[3]
for i in range(11, 34, 11):
nodes[i].next = nodes[i + 11]
nodes[33].next = nodes[4]
ret = detectIntersect(nodes[1], nodes[11]).val
# print(ret)
assert ret in [3, 4]
if __name__ == '__main__':
test_0()
test_1()
test_2()
test_3()
3 删除排序链表中的重复元素
3.1 使每个元素只出现一次
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
slow = fast = head
while fast:
while fast and slow.val == fast.val:
fast = fast.next
slow.next = fast
slow = fast
return head
3.2 删除全部重复元素
在草稿纸上推一下测试用例,只要能想明白要用.next.val
做比较,就不难理解了。
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
# 结点数<=1直接返回
if not (head and head.next): return head
dummy = ListNode(-inf)
dummy.next = head
a = dummy
b = head
while b and b.next:
if a.next.val == b.next.val: # 忘了写对 .val 的判断
while b and b.next and a.next.val == b.next.val:
b = b.next
a.next = b.next
b = b.next
else:
a = a.next
b = b.next
return dummy.next
4 链表重排
4.1 两两交换链表中的节点
- 首先在草稿纸上画出上图
- 因为需要保证
p1
,p2
存在,故while
条件是dp.next and dp.next.next
- 做完之后,
p1
成为新的dp
- 因为需要保证
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummy = ListNode(0)
dummy.next = head
dp = dummy
while dp.next and dp.next.next:
p1 = dp.next
p2 = dp.next.next
dp.next = p2
p1.next = p2.next
p2.next = p1
dp = p1
return dummy.next
4.2 重排链表 — 中点 + 反转 + 交替
这道题反过来就是字节的一个题了
def reverse(head: ListNode):
p=head
pre=None
while p:
aft=p.next
p.next=pre
pre=p
p=aft
return pre
class Solution:
def reorderList(self, head: ListNode) -> None:
if head is None or head.next is None: # 边界
return head
slow=head
fast=head.next
while fast and fast.next:
fast=fast.next.next
slow=slow.next
p2=slow.next
if p2 is None: # 边界
return head
slow.next=None
p2=reverse(p2)
a=head
b=p2
while a and b:
an=a.next
bn=b.next
a.next=b
b.next=an
a=an
b=bn
return head
4.3 字节跳动高频题——排序奇升偶降链表
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def sortOddEvenList(self,head):
if not head or not head.next:
return head
oddList,evenList = self.partition(head)
evenList = self.reverse(evenList)
return self.merge(oddList,evenList)
def partition(self, head: ListNode) -> ListNode:
evenHead = head.next
odd, even = head, evenHead
while even and even.next:
odd.next = even.next
odd = odd.next
even.next = odd.next
even = even.next
odd.next = None
return [head,evenHead]
def reverse(self,head):
dumpy = ListNode(-1)
p = head
while p:
temp = p.next
p.next = dumpy.next
dumpy.next = p
p = temp
return dumpy.next
def merge(self,p,q):
head = ListNode(-1)
r = head
while p and q:
if p.val <= q.val:
r.next = p
p = p.next
else:
r.next = q
q = q.next
r = r.next
if p:
r.next = p
if q:
r.next = q
return head.next
感觉交替切分链表不太熟,练了下
def partition(head: ListNode):
if not head or not head.next:
return head
p1, p2 = head, head.next
head1, head2 = p1, p2
# p2 后面至少有1个结点
while p2 and p2.next:
p1.next = p2.next
p1 = p1.next
p2.next = p1.next
p2 = p2.next
p1.next = None
return head1, head2
5 链表递归
5.1 链表部分反转 + K个一组反转
class Solution:
successor=None
def reverseN(self,head, n):
if n==1:
self.successor=head.next
return head
last=self.reverseN(head.next,n-1)
head.next.next=head
head.next=self.successor
return last
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
if left==1:
return self.reverseN(head, right)
head.next=self.reverseBetween(head.next,left-1, right-1)
return head
class Solution:
def reverse(self, a, b):
# 相当于左开右闭,例如k=2,
# 1->2->3
# a↑ b↑
# 最后返回是的 2->1->∅
pre = None
p = a
while p != b:
aft = p.next
p.next = pre
pre = p
p = aft
return pre
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
a = b = head
# 循环体写错 (老错误)
for _ in range(k):
if b is None:
return head
b = b.next
new_head = self.reverse(a, b)
# 把 a 写成了 new_head
a.next = self.reverseKGroup(b, k) # b 写错 (老错误)
return new_head
5.2 回文链表
- 递归
class Solution:
def recur(self,left):
if left is None:
return True
ans=self.recur(left.next)
ans &= (left.val==self.right.val)
self.right=self.right.next
return ans
def isPalindrome(self, head: ListNode) -> bool:
self.right=head
return self.recur(head)
- 快慢+翻转
def reverse(head):
if head is None or head.next is None:
return head
last=reverse(head.next)
head.next.next=head
head.next=None
return last
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None or head.next is None:
return True
slow=head
fast=head.next
while fast and fast.next:
slow=slow.next
fast=fast.next.next
p2=slow.next
slow.next=None
p2=reverse(p2)
p1=head
while p1 and p2:
if p1.val !=p2.val:
return False
p1=p1.next
p2=p2.next
return True
6 链表排序
6.1 合并K个升序链表
方法 | 暴力 | 分治 | 堆 |
---|---|---|---|
时间复杂度 | O ( k 2 n ) O(k^2n) O(k2n) | O ( k log k n ) O(k\log kn) O(klogkn) | O ( k log k n ) O(k\log kn) O(klogkn) |
空间复杂度 | O ( 1 ) O(1) O(1) | O ( log k ) O(\log k) O(logk) | O ( n ) O(n) O(n) |
def merge(a: ListNode, b: ListNode):
dummy = ListNode(0)
dp = dummy
while a and b:
if a.val < b.val:
dp.next = a
a = a.next
else:
dp.next = b
b = b.next
dp = dp.next
dp.next = a if a else b
return dummy.next
def partition(lists):
n = len(lists)
if n == 0:
return None
elif n == 1:
return lists[0]
else:
mid = len(lists) // 2
return merge(partition(lists[:mid]), partition(lists[mid:]))
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
return partition(lists)
6.2 链表插入排序
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head: return head
# 有序区 头指针
dummy = ListNode(-inf)
dummy.next = head
# 有序区的尾指针,后继为None
tail = head
p = head.next # 无序区 指针
tail.next = None
while p: # 在无序区中遍历
aft = p.next # 后继
# 放置在有序区最后面
if p.val >= tail.val:
tail.next = p
tail = p
tail.next = None
# 插入进有序区
else:
dp = dummy
while dp.next and dp.next.val < p.val:
dp = dp.next
p.next = dp.next
dp.next = p
p = aft # 进入后继
return dummy.next
6.3 链表归并排序
merge函数参考 merge-two-sorted-lists
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return head
slow = head
fast = head.next
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
A = self.sortList(head)
B = self.sortList(mid)
dummy = ListNode(0)
dp = dummy
while A and B:
if A.val < B.val:
dp.next = A
A = A.next
else:
dp.next = B
B = B.next
dp = dp.next
dp.next = A if A else B
return dummy.next