前言
- 这类题,做法很简单,但是我每次证明正确性的时候总是卡壳,所以这次整理一版我个人觉得比较清晰的证明,希望能以后能记住。。
思路
- 题型特点:给你一个链表,或者只知道递推关系的数据(形如初始是
a0
a
0
,递推关系是
at=f(at−1)
a
t
=
f
(
a
t
−
1
)
),让你在
O(1)
空间集线性时间里,判断数据是否有环,进一步会问环起点是哪个数据 - 判断是否有环算法:两个指针,慢指针每次走一步,快指针每次走两步,能相遇则有环,否则无
- 找环起点算法:在上一步基础上,从相遇点各出发一个指针,每次走一步,再相遇点就是环起点
- 证明判断有环算法,若有环则一定会相遇:
- 设在
t
时刻相遇,无环段长
x1
x
1
, 有环段长
x2
x
2
,则有
t=x1+c+k1x22t=x1+c+k2x2
t
=
x
1
+
c
+
k
1
x
2
2
t
=
x
1
+
c
+
k
2
x
2
- 两式相减得到:
t=(k2−k1)x2
t
=
(
k
2
−
k
1
)
x
2
,
- 则能相遇的条件是,满足对任意
x1,x2∈N
x
1
,
x
2
∈
N
,存在
k1,k2,t∈Ns.t.t=(k2−k1)x2,t≥x1
k
1
,
k
2
,
t
∈
N
s
.
t
.
t
=
(
k
2
−
k
1
)
x
2
,
t
≥
x
1
- 显然可以令
k1=0
k
1
=
0
,
k2
k
2
足够大就可以满足上述条件
- 证明第二个算法中相遇点是环头:
- 把上一个证明中的两个等式消元掉
t
,并换元简化一下式子,得到
x1=kx2−c
x
1
=
k
x
2
−
c
- 我们的目的,是得到
x1
x
1
,根据等式,显然分别从头和
x1+c
x
1
+
c
的位置出发,最后会在
x1
x
1
相遇
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;
}
};