基础链表题目汇总:
1.什么是链表:
链表不同于数组,数组的存储需要一整块连续的内存空间
链表是通过“指针”(或是索引)将内存块串联起来,内存空间不连续
2.链表的五个常规操作:
1. 单链表反转:
示例:
方法一:三指针循环反转:
思路:三指针分别代表首节点为p,次结点为q,未遍历的结点值为r,遍历单链表,三指针相互反转
便于理解版:
def reverseLinkTree(head):
if head == None:
return None
# 定义两个指针一前一后:
p = head
q = head.next
# p指针反转指向空结点
p.next = None
while q:
# r代表剩下的未反转的结点值
r = q.next
# q指针反转指向p
q.next = p
p = q
# p、q位置后移
q = r
return p
代码简化版:
def Let_reverseLinkTree(head):
if not head:
return None
p, q, p.next = head, head.next, None
while q:
q.next, p, q = p, q, q.next
return p
方法二:双指针反转法:
思路:双指针分别代表首节点为cur,空结点为pre,遍历单链表,两指针前后反转
def leet_reverseLink(head):
if not head:
return None
cur = head
pre = None
# cur, pre = head, None
while cur:
cur.next = pre
pre = cur
cur = cur.next
# cur.next, pre, cur = pre, cur, cur.next
return pre
方法三:尾指针插入法:
思路:保持头指针不动,遍历链表。从后向前插入到次指针位置,当指针遍历结束后,将首指针放到末尾处
便于理解版:
def reverseLinkTail(head):
if head == None or head.next == None:
return head
# 从次指针开始
cur = head.next
while cur.next:
pre = cur.next
cur.next = pre.next
pre.next = head.next
head.next = pre
# 将头结点移到尾部
cur.next = head
head = head.next
cur.next.next = None
return head
代码简化版:
def Let_reverseLinkTail(head):
if not head or not head.next:
return head
cur = head.next
while cur.next:
pre = cur.next
cur.next, pre.next, head.next = pre.next, head.next, pre
p.next, head, p.next.next = head, head.next, None
return head
方法四:递归法:
思路:将单链表的反转,看作head和head.next之间的反转,循环往复
def rec_reverseLink(head):
if not head or not head.next:
return head
new_head = rec_reverseLink(head.next)
head.next.next = head
head.next = None
return new_head
2. 链表中的环检测
示例:
方法一:字典查重法
思路:创建空字典,遍历单链表,加入字典,如果结点值已出现在字典中,就证明有环
def dist_checkLink(head):
if not head:
return False
cur = head
res = {}
while cur.next:
if cur in res:
return True
res[cur] = cur.val
cur = cur.next
return False
方法二:快慢双指针法
思路:
1.快慢指针同处于首节点上,让快指针走两步,慢指针走一步。
2.比较两个指针,当指针相等时,证明有环
def double_checkLink(head):
if not head or head.next:
return False
fast = slow = head
while fast.next and fast.next.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
return True
return False
3. 两个有序的链表合并
示例:
方法一:迭代法
思路:创建一个新链表,遍历两个链表,一一比较大小排序,放入新链表中
def mergeLink(l1: ListNode, l2: ListNode):
head = ListNode(0)
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
if l1:
cur.next = l1
elif l2:
cur.next = l2
return head.next
方法二:递归法
思路:
1.比较两个链表中最小的头结点,让他和剩下的节点的递归结果,进行合并。
2.设置递归条件,也就是最小问题的返回结果。
def recursiveLink(l1: ListNode, l2: ListNode):
if not l1:
return l2
if not l2:
return l1
if l1.val < l2.val:
l1.next = recursiveLink(l1.next, l2)
return l1
else:
l2.next = recursiveLink(l2.next, l1)
return l2
4. 删除链表倒数第n个结点
示例:
方法:快慢双指针法
思路:
1.快慢指针同处于首节点上,先让快指针走n步。
2.再让慢指针走,等到快指针走到末尾,慢指针正好走到第K个结点上,删去该结点
def delete_link(head, n):
cur = ListNode(0)
cur.next = head
# 快指针先走n步
slow = fast = cur
for _ in range(n):
fast = fast.next
# 快慢指针同时走,fast指针到达尾部节点,slow到达倒数第N个节点的前一个节点
while fast and fast.next:
slow, fast = slow.next, fast.next
# 删除节点
slow.next = slow.next.next
return cur.next
5. 求链表的中间结点
示例:
方法:列表查找法
思路:
1.新建空列表,遍历链表,将结点值放入列表
2.最后使用列表的len()//2方法找到中间值
def midLink(head):
if not head:
return [head.val]
cur = head
new_list = []
while cur:
new_list.append(cur.val)
cur = cur.next
return new_list[len(new_list) // 2:]