数据结构:链表
常用方法
-
迭代法
-
递归法
-
双指针法
-
快慢指针法
-
头插法
-
堆栈法
例题
1. 找出两个链表的交点
class Solution(object):
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
"""
:type head1, head1: ListNode
:rtype: ListNode
"""
# method-1: 双指针法
# a + b + c = b + a + c
# 移动至尾部后重新将指针指向另一链表开始移动,到公共结点时pA = pB,就获取到了公共结点
pA = headA
pB = headB
while pA != pB:
pA = pA.next if pA else headB
pB = pB.next if pB else headA
return pA
2. 链表反转
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# method-1: 迭代法
prev, curr = None, head
# 遍历
while curr:
"""
# 保存下个节点
tmp = curr.next
# 将当前结点指向prev
curr.next = prev
# prev和curr都前进一位
prev = curr
curr = tmp
"""
# 或直接同时复制
curr.next, prev, curr = prev, curr, curr.next
return prev
# method-2: 递归法
# 另nk.next.next = nk
# 但要注意n1必须指向NULL
if head == None or head.next == None:
return head
p = self.reverseList(head.next)
head.next.next = head
head.next = None
return p
3. 归并两个有序的链表
21. Merge Two Sorted Lists (Easy)](https://leetcode-cn.com/problems/merge-two-sorted-lists/description/)
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
# method-1: 递归法
# 头部较小的节点与剩下的元素merge合并
if l1 is None:
return l2
elif l2 is None:
return l1
elif l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
# method-2: 迭代
# l1和l2非空时,判断两者哪一更小,则将较小的添加到结果,对应链表的节点后移一位
# 哨兵节点
prevhead = ListNode(-1)
# prev指针用于调整其位置
prev = prevhead
while l1 and l2:
if l1.val <= l2.val:
prev.next = l1
l1 = l1.next
else:
prev.next = l2
l2 = l2.next
prev = prev.next
# 合并后l1和l2两者最多只有一个没有合并完,只需要将链表的末尾指向其即可
prev.next = l1 if l1 is not None else l2
return prevhead.next
4. 从有序链表中删除重复节点
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
# method-1:递归法
if head is None or head.next is None:
return head
head.next = self.deleteDuplicates(head.next)
head.next = head.next.next if head.val == head.next.val else head.next
return head
# method-2:直接法
curr = head
while curr is not None and curr.next is not None:
if curr.next.val == curr.val:
curr.next = curr.next.next
else:
curr = curr.next
return head
5. 删除链表的倒数第 n 个节点
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
# method-1: 两次遍历算法
# 删除从列表开头数起的第 (L−n+1) 个结点
# 第一次遍历,找出L
# 第二次遍历移动哑结点到 (L-n) 位置,把 L-n 指向 L-n+2
# 哑结点,用来简化极端情况,如只有一个结点,或需要删除列表头部
dummy = ListNode(-1)
dummy.next = head
length = 0
first = head
while first:
length += 1
first = first.next
length -= n # length = L-n
first = dummy
while length > 0:
length -= 1
first = first.next
# L-n位置
first.next = first.next.next
return dummy.next
# method-2:一次遍历算法(头插法)
# 使用双指针方法
# 第一个指针从开头移动n+1,第二个指针从头出发(两个指针被n个结点分开)
# 同时移动两个指针保持间隔,直到第一个指针到达最后一个结点,此时第二指针指向要删除的第n个结点
# 哑结点
dummy = ListNode(-1)
dummy.next = head
# q为前指针,p为后指针
p = dummy
q = dummy
if n == 0:
return dummy.next
else:
# 将q移动n步
for i in range(n):
q = q.next
while q.next:
p, q = p.next, q.next
p.next = p.next.next # p所指位置为第n-1个
return dummy.next
6. 交换链表中的相邻结点
class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
# method-1:递归法
# 从head开始,设置firstNode和secondNone两个结点,最后返回secondNode(新头)
# 交换后的头是原始链表的第二个节点
# 若无节点或只有一个节点
if not head or not head.next:
return head
first_node = head
second_node = head.next
first_node.next = self.swapPairs(second_node.next)
second_node.next = first_node
return second_node
# method-2:迭代法
# 链表分奇数和偶数节点
# prev_node为前节点的前驱节点
dummy = ListNode(-1)
dummy.next = head
prev_node = dummy
while head and head.next:
first_node = head
second_node = head.next
# 交换的前驱节点
prev_node.next = second_node
# 执行交换
first_node.next = second_node.next
second_node.next = first_node
# 移动prev_node和head
prev_node = first_node
head = first_node.next
return dummy.next
# method-2.1:迭代法优化
dummy = ListNode(-1)
dummy.next = head
if not head or not head.next:
return head
prev_node = head # 奇数
aft_node = head.next # 偶数
while aft_node and aft_node.next:
first_node = aft_node
second_node = aft_node.next
prev_node.next = second_node
# 交换
first_node.next = second_node.next
second_node.next = first_node
prev_node = first_node
aft_node = first_node.next
return dummy.next
7. 链表求和
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
# method-1:栈
# 把所有数字压入栈中,再依次取出相加,注意进位问题
s1, s2 = [], []
while l1:
s1.append(l1.val)
l1 = l1.next
while l2:
s2.append(l2.val)
l2 = l2.next
ans = None
carry = 0
while s1 or s2 or carry != 0:
a = 0 if not s1 else s1.pop()
b = 0 if not s2 else s2.pop()
curr = a + b + carry
carry = curr // 10 # 进位
curr %= 10 # 个位
curr_node = ListNode(curr)
curr_node.next = ans
ans = curr_node
return ans
8. 回文链表
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
"""
# method-1:将值复制到数组中后用双指针法
# 时间复杂度:O(n)
# 空间复杂度:O(n)
vals = []
curr_node = head
while curr_node is not None:
vals.append(curr_node)
curr_node = curr_node.next
return vals == vals[::-1]
"""
# method-2:快慢指针法
# 将后半部分链表反转
# 时间复杂度:O(n)
# 空间复杂度:O(1)
if head is None:
return True
first_half_end = self.end_of_first_half(head)
second_half_start = self.reverse_list(first_half_end.next)
res = True
first_position = head
second_position = second_half_start
while res and second_position is not None:
if first_position.val != second_position.val:
return False
first_position = first_position.next
second_position = second_position.next
first_half_end.next = self.reverse_list(second_half_start)
return res
# 快慢指针
# 找到指针前半部分的尾部
def end_of_first_half(self, head):
fast = head
slow = head
while fast.next is not None and fast.next.next is not None:
fast = fast.next.next
slow = slow.next
return slow
# 反转链表,具体可见206
def reverse_list(self, head):
prev, curr = None, head
while curr:
curr.next, prev, curr = prev, curr, curr.next
return prev
9. 分隔链表
class Solution:
def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
""""""
# method-1:创建新list2
cur = root
for N in range(1001):
if not cur: break
cur = cur.next
width, remainder = divmod(N, k)
ans = []
cur = root
for i in range(k):
head = write = ListNode(None)
for j in range(width + (i < remainder)):
write.next = write = ListNode(cur.val)
if cur:
cur = cur.next
ans.append(head.next)
return ans
# method-2:拆分链表
cur = root
for N in xrange(1001):
if not cur: break
cur = cur.next
width, remainder = divmod(N, k)
ans = []
cur = root
for i in range(k):
head = cur
for j in rang(width + (i < remainder) - 1):
if cur: cur = cur.next
if cur:
cur.next, cur = None, cur.next
ans.append(head)
return ans
10. 链表元素按奇偶聚集
class Solution:
def oddEvenList(self, head: ListNode) -> ListNode:
# method-1:双指针
if head is None:
return None
odd, even = head, head.next
evenHead = even
while even is not None and even.next is not None:
odd.next = even.next
odd = odd.next
even.next = odd.next
even = even.next
odd.next = evenHead
return head
参考
https://github.com/CyC2018/CS-Notes