一、链表中环的定义
单链表中存在环的概念,如下所示
head = [ 3 2 0 -4]
环的入口 1 (环索引值,索引值从0开始)
二、经典问题
问题1:判断是否有环
LeetCode141
思路分析:
方法1:hash/集合
遍历环,将元素放到map/集合中,如果有环,一定会发生碰撞/重复
方法2:双指针(快慢指针)
定义两个指针,快指针(每次走两步),慢指针(每次走一步)
如果没有环,快指针能够到达环的末尾
如果有环,快指针会与环相遇(套圈)
理解:为什么快慢指针一定会相遇呢?
存在以下两种情况,评估下来都会相遇
代码实现(python)
方法1实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
s = set()
node = head
while node:
if node in s:
return True
s.add(node)
node = node.next
return False
# python列表也能实现
方法2实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, 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:寻找环的入口
LeetCode142
思路分析:
方法1:双指针法
实现原理:
先按照双指针(快慢指针)寻找到相遇的位置Z,然后将两指针分别放在链表头X和相遇位置Z,改为相同的速度(速度为1)推进,则两指针会在环入口Y相遇
原理分析:
存在两种相遇情况:1圈后相遇,多(n)圈后相遇
情况1:1圈后相遇
根据快慢指针速度和行走距离建立公式:a+b+c+b = 2(a+b) => a=c
情况2:多(n)圈后相遇
根据快慢指针速度和行走距离建立公式:
a+b+n(c+b) = 2(a+b)
=> n(b+c) = a+b
=> c+(n-1)(b+c) = a
其中b+c为环的周长,据此可以推断
1. 快慢指针相遇时快指针已经在环中转了n-1圈
2. 如果指针1从起点触发,指针2从相遇点出发,2者能够恰好在环入口处相遇,且相遇前,指针2已经在环中转了n-1圈
方法2:三指针法
实现原理:
找到环的大小(长度)K和环的末尾节点(即入口节点),将问题转为:寻找倒数第K个节点
Q1:如何找到环的大小?
利用双指针(快慢指针)判断是否有环,找到环境的相遇点Z;找到后固定一个指针在Z,另一个指针从Z出继续遍历,当两个指针再次相遇就可得到环的大小
Q2:如何确定末尾节点?
遍历列表,如果p.next = 入口节点Y,P就是链表的终点
实现步骤:
第1步使用快慢指针判断是否存在环
第2步使用双指针判断环的大小
第3步使用倒数第K个节点的方法寻找入口
方法3:利用Python列表特性实现
注意:面试不建议这么干,被认为在逃避问题;工程里需要这么写,简单易维护
代码实现
方法1实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast, slow = head, head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow:
break
if not (fast and fast.next) :
return None
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return fast
# 代码优化
class Solution2(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast, slow = head, head
while True:
if not (fast and fast.next) :
return None
fast = fast.next.next
slow = slow.next
if fast == slow:
break
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return fast
方法2实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
fast, slow = head, head
while True:
if not (fast and fast.next) :
return None
fast = fast.next.next
slow = slow.next
if fast == slow:
break
# 注意环的长度,从1开始
cycleLen = 1
while fast.next != slow:
fast = fast.next
cycleLen += 1
# 寻找倒数第K个元素
fast, slow = head, head
for _ in range(cycleLen):
fast = fast.next
while fast != slow:
fast = fast.next
slow = slow.next
return fast
方法3实现
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
node_list = []
node = head
while node:
if node in node_list:
return node
node_list.append(node)
node = node.next
return None