给定一个含有正整数和负整数的环形数组 nums。 如果某个索引中的数 k 为正数,则向前移动 k 个索引。相反,如果是负数 (-k),则向后移动 k 个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素。
确定 nums 中是否存在循环(或周期)。循环必须在相同的索引处开始和结束并且循环长度 > 1。此外,一个循环中的所有运动都必须沿着同一方向进行。换句话说,一个循环中不能同时包括向前的运动和向后的运动。
示例 1:
输入:[2,-1,1,2,2]
输出:true
解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。
示例 2:
输入:[-1,2]
输出:false
解释:按索引 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。
示例 3:
输入:[-2,1,-1,-2,-2]
输出:false
解释:按索引 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为按索引 1 -> 2 的运动是向前的运动,而按索引 2 -> 1 的运动是向后的运动。一个循环中的所有运动都必须沿着同一方向进行。
提示:
-1000 ≤ nums[i] ≤ 1000
nums[i] ≠ 0
1 ≤ nums.length ≤ 5000
进阶:
你能写出时间时间复杂度为 O(n) 和额外空间复杂度为 O(1) 的算法吗?
方法一:蛮力法。使用数组paths[index]记录nums[index]移动到的下一个点,然后穷举每一个点作为起始点。
class Solution {
public:
bool circularArrayLoop(vector<int>& nums) {
int numsSize = nums.size();
if (numsSize < 2){
return false;
}
vector<int> paths(numsSize, -1);//paths[index]记录nums[index]移动到的下一个点
//求解每一个点移动的下一个点的位置
for (int index = 0; index < numsSize; ++index){
//由于只能往一个方向移动,所以nums[index]必须和其到达的下一个点同号
if (nums[index] > 0 && nums[(index + nums[index]) % numsSize] > 0){
paths[index] = (index + nums[index]) % numsSize;
}
else if (nums[index] < 0 && nums[(index + numsSize - (abs(nums[index]) % numsSize)) % numsSize] < 0){
paths[index] = (index + numsSize - (abs(nums[index]) % numsSize)) % numsSize;
}
}
//穷举每一个点作为起始点
for (int index = 0; index < numsSize; ++index){
unordered_map<int, bool> flag;//标记是否走过
int tempI = index;
while (paths[tempI] != -1){//出现-1则说明出现了两种移动方向
if (flag.count(paths[tempI])){//走到了重复点
if (paths[tempI] != tempI){//不能是自己到自己的圈
return true;
}
else{
break;
}
}
flag[tempI] = true;//标记使用
tempI = paths[tempI];//移动
}
}
return false;
}
};
下面是评论区大佬的代码,我是添加上了注释
class Solution {
public:
int N;
bool circularArrayLoop(vector<int>& nums) {
N = nums.size();
if(N <= 1)
return false;
int visit[N] = {0};//标记是否访问
for(int i = 0; i < N; i++){
if(!visit[i]){
//根据nums[i]的正负性,选择不同的方向
if(nums[i] > 0 && dfs(i, visit, nums,1)) {
return true;
}
if(nums[i] < 0 && dfs(i, visit, nums, -1)) {
return true;
}
}
}
return false;
}
bool dfs(int i, int visit[], vector<int> nums, int dir){
visit[i] = -1;//初始化标记该点能够形成环
//确定i的下一个位置
int next = i + nums[i];
while(next < 0) {
next = next + N;
}
next = next % N;
//出现自己到自己或者与dir起始方向相反的方向
if(next == i || nums[next] * dir < 0){
visit[i] = 1;//修改标记为不能构成环
return false;
}
if(visit[next] == -1) {
return true;
}
//当一个点没有访问过,且下一个点能够形成环
if(!visit[next] && dfs(next, visit, nums, dir)) {
return true;
}
visit[i] = 1;//修改标记为不能构成环
return false;
}
};
我们得明白一个点的下一个位置是确定的,并且如果下一个位置不能构成环,则当前的点必定也不能构成环。而不能构成环只有出现逆方向、自己到自己两种情况。