题目要求:
快慢指针法
1、问题的转化
如果题目中给的是单向链表上寻找一个环,那么很简单,我们只需要从起点出发, 看是否出现回头路即可。但是这道题并不是链表结构,而是数组结构,每一个数组索引都存有一个值(非零),它代表他向左或者向右可以走的步数,那么我们这么想,某一个点可以向左向右走到的位置,和该点不正是一个单向边的关系吗,所以所有单向边构成了一个大型链表,我们只需要从每个节点都出发,寻找环路即可,综上:该问题就被转化成了,数组转化为链表,链表上寻找环路的问题。
2、解决方案
寻找环路最基础的想法就是快慢指针法:基础方法讲解见我的另一篇博客单向链表上寻找环。
具体代码
1、当从一个点出发,发现没有环路或者不合法的时候,就可以不用再寻找,刚才找过的点已经无意义,这来源于本体的特点,每个数组索引都只能有一个值,即每个节点都只能有一个出边,再根据每个节点的值都是非零的,所以走过的无用节点置零。
2、根据题意,我们能走的是同方向的,非零节点,但是这里注意,快节点一次走两下,必须保证这两下都和慢节点同方向,才符合题意。
3、判断出有环,必须在看是否是长度为1的环,是的话必须干掉。
4、只有找到合法环的时候才能返回真,其余情况将走过的点置零
5、置零的时候,谁被走过是个问题,这么想就行,就是开始的时候快慢指针同一起点出发(题目中的初始值是为了让while循环进行下去,这两种初始值都行),快指针要在getIndex(add)基础上再走一次,所以就必须要求add点和getIndex(add)点二者同方向,快慢指针才会继续走下去,所以条件出来了。
class Solution {
public:
vector<int> item;
int n;
int getIndex(int cur) {//将数组转化成链表结构,获得该节点的下一个节点位置
return ((cur + item[cur]) % n + n) % n;
}
bool circularArrayLoop(vector<int>& nums) {
n = nums.size();
item = nums;
for (int i = 0; i < n; i++) {//遍历每一个节点
if (!nums[i]) {//访问过的节点
continue;
}
int slow = i, fast = getIndex(i);
while (nums[slow] * nums[fast] > 0 && nums[slow] * nums[getIndex(fast)] > 0) {//保证快指针两次走的节点方向和慢指针一致
if (slow == fast) {//当判断出循环了,需要排除长度为一
if (slow != getIndex(slow)) {
return true;
}
else {//特殊情况,循环长度为一
break;
}
}
slow = getIndex(slow);
fast = getIndex(getIndex(fast));
}
//将刚才走过的都置零
int add = i;
while (nums[add] * nums[getIndex(add)] > 0) {
int tmp = add;//这里必须这样写,因为getIndex函数的原因
add = getIndex(add);
nums[tmp] = 0;
}
}
return false;
}
};
(所有代码均已在力扣上运行无误)
经测试,该代码运行情况是(经过多次测试所得最短时间):