链表数据结构
# 链表节点
class ListNode:
def __init__(self,x):
self.val=x
self.next=None
带环的链表
给定一个单链表,只给出头指针head:
1、如何判断是否存在环?
2、如何知道环的长度?
3、如何找出环的连接点在哪里?
4、带环链表的长度是多少?
1. 判断链表是否有环
判断给定的链表中是否有环。
题解:
设定两个指针slow、fast,从头指针开始,每次分别前进1步、2步。如存在环,则两者相遇;如不存在环,fast遇到None退出。
def hasCycle(head):
fast,slow=head,head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
if fast==slow:
return True
return False
2. 链表中环的长度
记录下问题1的碰撞点,fast从该点开始,再次碰撞所走过的操作数就是环的长度。
def cycleLen(head):
fast,slow=head,head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
if fast==slow:
break
if not fast or not fast.next:
return 0
ans=1
fast=fast.next
while slow!=fast:
ans+=1
fast=fast.next
return ans
3. 链表中环的入口节点

因此,分别从碰撞点、头指针开始走,相遇的那个点就是入口节点。
def detectCycle(head):
fast,slow=head,head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
if fast==slow:
break
if not fast or not fast.next:
return None
fast=head
while fast!=slow:
fast=fast.next # 从头指针开始移动
slow=slow.next # 从碰撞指针开始移动
return fast
4. 带环链表的长度
问题3中已经求出连接点距离头指针的长度,加上问题2中求出的环的长度,二者之和就是带环单链表的长度。
def listLen(head):
fast,slow=head,head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
if fast==slow:
break
# 环长
if not fast or not fast.next:
cycle=0
else:
cycle=1
fast=fast.next
while slow!=fast:
cycle+=1
fast=fast.next
# 非环部分长度
ans,fast=0,head
while slow!=fast:
fast=fast.next
slow=slow.next
ans+=1
return ans+cycle
反转链表
输入一个链表,反转链表后,输出新链表的表头。
题解:
- 定义两个指针: pre 和 cur ;pre 在前 cur 在后。
- 每次让 pre 的 next 指向 cur ,实现一次局部反转。
- 局部反转完成之后, pre 和 cur 同时往前移动一个位置。
- 循环上述过程,直至 pre 到达链表尾部。
def ReverseList(head):
cur,pre=None,head
while pre:
tmp=pre.next
pre.next=cur
cur,pre=pre,tmp
return cur
链表中倒数第 k 个结点
输入一个链表,输出该链表中倒数第k个结点。
题解:
- 初始化: 前指针 pre、后指针 cur ,双指针都指向头节点 head 。
- 构建双指针距离: 前指针 pre 先向前走 k 步(结束后,双指针 pre 和 cur 间相距 k 步),如果链表长度小于 k,则返回None。
- 双指针共同移动: 循环中,双指针 pre 和 cur 每轮都向前走一步,直至 pre 走过链表尾节点时跳出,跳出后, cur 与尾节点距离为 k-1,即 cur 指向倒数第 k 个节点。
def FindKthToTail(head, k):
cur,pre=head,head
for i in range(k):
if pre:
pre=pre.next
else:
return None
while pre:
pre,cur=pre.next,cur.next
return cur
合并有序链表
将两个有序的链表合并为一个新链表,要求新的链表是通过拼接两个链表的节点来生成的。
题解:
- 初始化:伪头节点 dum,节点 cur 指向 dum;
- 循环合并:使用双指针遍历两个链表,当 l1 或 l2 为空时跳出
- l1.val<l2.val:cur 的后继结点指定为 l1,l1 向前走一步;
- l1.val>=l2.val:cur 的后继结点指定为 l2,l2 向前走一步;
- 结点 cur 向前走一步。
- 合并剩余尾部:跳出时有两种情况,即 l1 为空或 l2 为空
- 若 l1=None,将 l1 添加至结点 cur 之后;
- 否则,将 l2 添加至结点 cur 之后.
- 返回值:合并链表在伪头结点 dum 之后,因此返回 dum.next 即可。
def mergeTwoLists(l1,l2):
cur=dum=ListNode(0)
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
return dum.next
两个链表的第一个公共结点
输入两个链表,找出它们的第一个公共结点。
题解:
使用两个指针 l1,l2 分别指向两个链表 headA,headB 的头结点,然后同时分别逐结点遍历,当 l1 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 l2 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。
def FindFirstCommonNode(headA,headB):
l1,l2=headA,headB
while l1!=l2:
l1=l1.next if l1 else headB
l2=l2.next if l2 else headA
return l1