版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luke2834/article/details/79393145
前言
- 这类题,做法很简单,但是我每次证明正确性的时候总是卡壳,所以这次整理一版我个人觉得比较清晰的证明,希望能以后能记住。。
思路
- 题型特点:给你一个链表,或者只知道递推关系的数据(形如初始是
,递推关系是
),让你在
O(1)
空间集线性时间里,判断数据是否有环,进一步会问环起点是哪个数据 - 判断是否有环算法:两个指针,慢指针每次走一步,快指针每次走两步,能相遇则有环,否则无
- 找环起点算法:在上一步基础上,从相遇点各出发一个指针,每次走一步,再相遇点就是环起点
- 证明判断有环算法,若有环则一定会相遇:
- 设在
t
时刻相遇,无环段长 , 有环段长 ,则有
- 两式相减得到: ,
- 则能相遇的条件是,满足对任意
,存在
- 显然可以令 , 足够大就可以满足上述条件
- 设在
- 证明第二个算法中相遇点是环头:
- 把上一个证明中的两个等式消元掉
t
,并换元简化一下式子,得到
- 我们的目的,是得到 ,根据等式,显然分别从头和 的位置出发,最后会在 相遇
- 把上一个证明中的两个等式消元掉
leetcode相关题目
141. Linked List Cycle
- 判断链表是否有环
- 实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if (!head)
return false;
auto slow = head, fast = head;
do {
if (fast->next == NULL || fast->next->next == NULL)
return false;
fast = fast->next->next;
slow = slow->next;
}while (slow != fast);
return true;
}
};
142. Linked List Cycle II
- 找环头
- 实现:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (!head)
return NULL;
auto slow = head, fast = head;
do {
if (fast->next == NULL || fast->next->next == NULL)
{
fast = NULL;
break;
}
fast = fast->next->next;
slow = slow->next;
}while (slow != fast);
if (!fast)
return NULL;
slow = head;
while (slow != fast){
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
202. Happy Number
- 给定next函数和初始值
n
,next为n
的各十进制位的平方和 - 求最后能否变为
1
- 思路:我们会发现,不管初始
n
多大,由于是int值,经过一次变换后,最大不会超过1000
- 完全可以通过判断是否提前有环来解
- 实现:
class Solution {
public:
int next(int x){
int ret = 0;
while (x > 0){
ret += (x % 10) * (x % 10);
x /= 10;
}
return ret;
}
bool isHappy(int n) {
int slow = n, fast = n;
do{
slow = next(slow);
fast = next(fast);
fast = next(fast);
if (fast == 1)
return true;
}while(slow != fast);
return false;
}
};