本篇文章根据labuladong的算法小抄汇总链表的常见算法,采用python3实现
文章目录
单链表基本技巧
合并两个有序链表
class ListNode:
def __init__(self,val=0,next=None):
self.val = val
self.next = next
def mergeTwoLists(l1,l2):
#虚拟头结点
dummy = ListNode(-1)
p = dummy
p1 = l1
p2 = l2
while (p1 is not None) and (p2 is not None):
if p1.val > p2.val:
p.next = p2
p2 = p2.next
else:
p.next = p1
p1 = p1.next
p = p.next
if p1 is not None:
p.next = p1
if p2 is not None:
p.next = p2
return dummy.next
合并k个有序链表
class ListNode:
def __init__(self,val=0,next=None):
self.val = val
self.next = next
import heapq
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
n = len(lists)
if n == 0:
return None
dummy = ListNode(-1)
p = dummy
pq = []
#遍历所有结点,每个值都插入最小堆
for head in lists:
while head is not None:
heapq.heappush(pq,head.val)
head = head.next
#二叉堆中元素构建新链表
while len(pq) != 0:
p.next = ListNode(heapq.heappop(pq))
p = p.next
return dummy.next
单链表的倒数第k个结点
def findFromEnd(head,k):
p1 = head
for i in range(k):
p1 = p1.next
p2 = head
while p1 is not None:
p2 = p2.next
p1 = p1.next
return p2
删除链表的倒数第N个结点
双指针技巧:p1先走n步,p2指向头结点,那么p1走完时,p2刚好走到倒数第n个结点
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
#注意如果不添加虚拟头结点,要删除原头结点就很麻烦
dummy = ListNode(-1)
dummy.next = head
p1 = dummy
for i in range(n):
p1 = p1.next
p2 = dummy
while p1.next:
p1 = p1.next
p2 = p2.next
p2.next = p2.next.next
return dummy.next
单链表的中点
快慢指针:每当慢指针slow前进一步,快指针fast就前进两步,那么当fast走到末尾时,slow就指向中点
def middleNode(head):
slow = head
fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
return slow
判断链表是否包含环
快慢指针:每当慢指针slow前进一步,快指针fast就前进两步,那么如果fast最终遇到空指针,说明没有环;如果fast最终和slow相遇,那肯定是fast超过slow一圈,说明有环
class Solution:
def hasCycle(self, head: ListNode) -> bool:
fast = head
slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
return True
return False
返回链表入环的第一个结点
快慢指针:若链表含有环,当快慢指针相遇时,将一个指向头指针,以同样速度遍历,会在环起点相遇
def detectCycle(head):
slow = head
fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
break
if (not fast) or (not fast.next):
return None
slow = head
while slow != head:
slow = slow.head
fast = fast.head
return slow
两个链表是否相交

让p1走掉a1,a2,c1,c2,b1,b2,b3→c1
让p2走掉b1,b2,b3,c1,c2,a1,a2→c1
其实就是走完自己的链表走另一个链表
def getIntersectionNode(headA,headB):
p1 = headA
p2 = headB
while p1 != p2:
if not p1:
p1 = headB
else:
p1 = p1.next
if not p2:
p2 = headA
else:
p2 = p2.next
return p1
反转链表
迭代反转整个链表
def reverse(head):
if (not head) or (not head.next):
return head
prev = None
curr = head
while curr:
next_ = curr.next
curr.next = prev
prev = curr
curr = next_
return prev
递归反转整个链表
reverse函数的定义:输入一个结点head,将以head为起点的链表反转,并返回反转之后的头结点
def reverse(head):
if (not head) or (not head.next):
return head
last = reverse(head.next)
head.next.next = head
head.next = None
return last
递归反转链表前N个结点
def reverseN(head,n):
if n == 1:
successor = head.next
return head
last = reverseN(head.next,n-1)
head.next.next = head
head.next = successor
return last
递归反转一部分链表
索引从1开始,反转区间[m,n]中的链表元素
def reverseBetween(head,left,right):
def reverseN(head,n):
if n == 1:
self.successor = head.next
return head
last = reverseN(head.next,n-1)
head.next.next = head
head.next = self.successor
return last
if left == 1:
return reverseN(head,right)
head.next = self.reverseBetween(head.next,left-1,right-1)
return head
迭代反转一部分链表
class Solution:
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
def reverse(head):
pre = None
cur = head
while cur:
next_ = cur.next
cur.next = pre
pre = cur
cur = next_
dummy = ListNode(-1)
dummy.next = head
left_connect = dummy #不反转的左边最后一个结点
for i in range(left-1):
left_connect = left_connect.next
right_node = left_connect #反转的最后一个结点
for i in range(right-left+1):
right_node = right_node.next
right_connect = right_node.next #不反转的右边第一个结点
#切断连接
left_node = left_connect.next #反转的第一个结点
left_connect.next = None
right_node.next = None
#反转
reverse(left_node)
#连接
left_connect.next = right_node
left_node.next = right_connect
return dummy.next
K个一组反转链表
def reverseKGroup(head,k):
#反转区间[a,b)的元素
def reverse(a,b):
pre = None
cur = a
while cur != b:
next_ = cur.next
cur.next = pre
pre = cur
cur = next_
return pre
if not head:
return None
a = head
b = head
for i in range(k):
if not b:
return head
b = b.next
newHead = reverse(a,b)
a.next = reverseKGroup(b,k)
return newHead
判断单链表是否回文
寻找回文串是从中间向两端扩展,判断回文串是从两端向中间收缩
法1:把原始链表反转存入新链表,比较两条链表是否相同。
法2:借助二叉树的后序遍历,模仿双指针判断回文
def isPalindrome(head):
def traverse(right):
if not right:
return True
res = traverse(right.next)
res = res and (right.val == left.val)
left = left.next
return res
left = head
return traverse(head)
法1和法2的时间复杂度都为O(N),空间复杂度也都为O(N)
优化空间复杂度到O(1):
(1)快慢指针找到中点
(2)如果fast没有指向None,说明链表长度为奇数,slow需要再向前一步
(3)从slow开始反转后面的链表,比较回文串
def isPalindrome(head):
def reverse(head):
pre = None
cur = head
while cur:
next_ = cur.next
cur.next = pre
pre = cur
cur = next_
return pre
if not head.next:
return True
slow = head
fast = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast:
slow = slow.next
left = head
right = reverse(slow)
while right:
if left.val != right.val:
return False
right = right.next
left = left.next
return True