LeetCode 环形数组循环

给定一个含有正整数和负整数的环形数组 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;
    }
};

在这里插入图片描述
我们得明白一个点的下一个位置是确定的,并且如果下一个位置不能构成环,则当前的点必定也不能构成环。而不能构成环只有出现逆方向、自己到自己两种情况。

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/88901925