快慢指针专题
针对的题型
快慢指针主要针对以下两种类型的题目,其中第一种比较常见
- 判断链表或数组是否有环或与环相关的问题
- 寻找中点
与双指针的区别:
快慢指针:像单链表这样的数据结构,指针只能进行单向运动,通常使用快慢指针
双指针:像数组或双向链表这样的数据结构,可以考虑是否使用双指针。还有一个特点是,通常数组或双向链表是有序的
下面从LeetCode中选取四道题为例。
快慢指针例题
141.环形链表
题目描述:给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个(从0开始)节点。(pos=-1时表示无环)
思路:可以类比环形跑道赛跑,只要跑道是圆的,快的人总能超慢的人一圈。
题解:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head: return False
fast = slow = head
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if slow == fast:
return True
return False
142.环形链表2
题目描述:给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。(pos描述同上)
思路:首先,题目存在无环的情况,因此需要判断有没有环,如环形链表的做法,使用快慢指针。
当确定有环,可以思考这样一个问题,快指针比慢指针多走了几步?实际上,仔细思考不难得出这样一个结论,快指针在有环链表中追上慢指针多走的步数正好等于环中结点的个数(快指针比慢指针多走了一个环才能追上慢指针)。
得到这么一个结论后,找到环的开始结点实际上就比较容易了,假设环的长度为m,找到链表中倒数第m个结点就是环开始的结点。找到倒数第m个结点同样可以使用快慢指针,快指针先走m步,然后快慢指针一起一步一步走,当快指针走到链表最后一个结点之后再走一步会回到环开始的结点,与慢指针正好相遇,此时停止,就能找到环开始的结点了。
题解:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head):
if not head or not head.next: return None
length = 0
fast = slow = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
length += 1
if slow == fast:
break
if slow!=fast:
return None
slow = fast = head
for i in range(length):
fast = fast.next
while slow!=fast:
slow = slow.next
fast = fast.next
return slow
202.快乐数
题目描述:编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和,然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 True ;不是,则返回 False 。
示例:
输入:19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
思路:任意数n不停的求各位数上的平方之和,一直求解,总是会循环,这里只给结论,可以手算几个数总结出来,这里暂时不给出严谨的证明。有环,且快乐数的环必定只有一个结点,且该节点为1,那显然是可以使用快慢指针的,只要判断相遇时的结点是否为1即可,因为相遇总是在环中某一结点相遇,而快乐数的环只有一个结点。
题解:
class Solution:
def isHappy(self, n):
slow = n
fast = self.squareSum(n)
while slow!=fast:
slow = self.squareSum(slow)
fast = self.squareSum(self.squareSum(fast))
return slow == 1
def squareSum(self, n):
s = 0
while n>0:
digit = n%10
s += digit**2
n//=10
return s
876.链表的中间结点
题目描述:给定一个带有头结点 head
的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。
示例2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4
题解:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def middleNode(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow