题目:给定一组含有正整数和负整数的数组。如果某个索引中的 n 是正数的,则向前移动 n 个索引。相反,如果是负数(-n),则向后移动 n 个索引。
假设数组首尾相接。判断数组中是否有环。环中至少包含 2 个元素。环中的元素一律“向前”或者一律“向后”。
示例 1:给定数组 [2, -1, 1, 2, 2], 有一个循环,从索引 0 -> 2 -> 3 -> 0。
示例 2:给定数组[-1, 2], 没有循环。
注意:给定数组保证不包含元素"0"。
你能写出时间复杂度为 O(n) 且空间复杂度为 O(1) 的算法吗?
元旦休息两天回来热热手~~~
题意分析大致就是当遍历到nums数组某位置时,用当前位置下标再加上当前位置的数字得到下一个位置,如果能回到出发点则有循环,但是中间的数字必须都是全正数或者全负数,如果相加后得到的数字在数组之外则分正负情况取模得到循环的位置。
第一眼看想到的是再创建一个标志位vector,存储每个数字的访问情况,顺次往下进行循环,如果标志位表示数字已经访问过则用continue跳过,但是题目中要求空间复杂度为O(1)。
思考一下后想到题目中要求循环中的数字必须同号,那么可以利用这一特性按照上面题意分析中所说的进行遍历,并记录遍历开始时的头部位置,如果遍历回了头部则说明有循环,注意需要一个size变量记录当前循环长度,如果小于2也不是一个循环。
但如果当前遍历到的数字异号,则表示之前记录的循环已经不能进行下去了,需要进行更新,则将头部位置改为当前变号的位置,并将size重新置为1,标志位flag置为当前的正负号。
最后是循环停止条件,如果while大循环次数大于长度了,表示至少有数字已经遍历了两次,即有某一曾经试探过的循环路径走过两次(注意理解此句话,这句理解了也就明白为何选择这个条件了),如果存在循环则函数必定已经返回true,但是仍然未返回,说明数组中不存在循环,那么则跳出循环返回false。看了下其他的代码多用到双指针法,但是改变了原数组,而本方法则没有改变。下给代码:
class Solution {
public:
bool helpArrayLoop(int num) { //辅助判断正负号
return num > 0 ? true : false;
}
bool circularArrayLoop(vector<int>& nums) {
int len = nums.size();
if (len < 2) return false; //长度为0或1,必定没有循环
int head = 0, loc = 0, time = 0, size = 1; //分别记录当前试探的循环起始位,当前遍历位置,while大循环进行次数,试探的循环的长度
bool flag = helpArrayLoop(nums[loc]); //记录当前试探的循环的正负号
while (time != len) {
if (helpArrayLoop(nums[loc]) != flag) { //如果当前位置正负号与循环不同,则更新循环信息,从此处重新进行循环试探
flag = helpArrayLoop(nums[loc]);
head = loc;
size = 1;
}
int mov = (loc + nums[loc]) % len;
loc = mov >= 0 ? mov : len + mov; //注意正负号
if (loc == head && size > 1) return true;
++size;
++time;
}
return false;
}
};
执行用时: 0 ms, 在Circular Array Loop的C++提交中击败了100.00% 的用户