其他分享
首页 > 其他分享> > ♥每日理解一道题♥/ 457. 环形数组是否存在循环

♥每日理解一道题♥/ 457. 环形数组是否存在循环

作者:互联网

457. 环形数组是否存在循环

题意:

存在一个不含 0 的 环形 数组 nums ,每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数:

如果 nums[i] 是正数,向前 移动 nums[i] 步
如果 nums[i] 是负数,向后 移动 nums[i] 步
因为数组是 环形 的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。

数组中的 循环 由长度为 k 的下标序列 seq :

遵循上述移动规则将导致重复下标序列 seq[0] -> seq[1] -> … -> seq[k - 1] -> seq[0] -> …
所有 nums[seq[j]] 应当不是 全正 就是 全负
k > 1
如果 nums 中存在循环,返回 true ;否则,返回 false 。

示例 1:

输入:nums = [2,-1,1,2,2]
输出:true
解释:存在循环,按下标 0 -> 2 -> 3 -> 0 。循环长度为 3 。

示例 2:

输入:nums = [-1,2]
输出:false
解释:按下标 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。

示例 3:

输入:nums = [-2,1,-1,-2,-2]
输出:false
解释:按下标 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为 nums[1] 是正数,而 nums[2] 是负数。
所有 nums[seq[j]] 应当不是全正就是全负。

解题思路:

脑回路:

怎么判断是否循环了?有一个下标重复了,就说明有循环.那么就需要标记
怎么找到循环起点?记录经过的全部下标,如果有下标重复就在记录中寻找下标开始的位置,就是起点.找下标时先进先出,队列来记录路径
如何判断路径全为正数或者负数?取第一个数,然后逐个乘路径的值,如果相乘小于0.说明异号.
如何判断路径大于1?因为队列记录路径,所以队列长度就是路径长度.
提示1因为时循环数组,下标可能越界,所以需要特殊处理.(这里在代码详细说明)
提示2数组有可能不止一条循环路径,所以需要逐个遍历.
优化因为我们循环路径有经过一些节点,这些节点不需要继续遍历.因为肯定是错误的,所以如果遇到标记为true 时证明已经遍历过,直接下一个即可.
  • 优化这一步很重要,因为会减少很多运算,在leetcode可以直接到达0ms.

代码

class Solution {
public:

//寻找是否有循环路径
bool findTrue(vector<int>&nums,int start,vector<bool> &flag)
{
    //获得数组长度
	int n = nums.size();
	//用队列来记录路径
	queue<int> path;
    //从当前下标开始
	int index = start;
	while (1)
	{
        //当前下标标记为true,说明这个路径上有该下标
		flag[index] = true;
        //添加到路径中
		path.push(index);
        //index计算, index可能大于n也可能小于0.这里就需要特殊处理. 
        //所以需要先加上后余一个n让它的范围在[-n+1,n-1],在加上n后范围为[1,2n-1].再与n后范围为[1,n-1].
		index = ((index + nums[index]) % n + n) % n;
        //如果遇到标记为true证明有循环了,就退出循环
		if (flag[index] == true)
			break;
	}

    //找循环起点
	while (!path.empty())
	{
        //头节点的下标等于起点下标,就说明是路径起点
		if (path.front() == index)
			break;
		path.pop();
	}

    //如果路径小于1,就说明是重复的或者只有一个节点在循环.直接返回false
	if (path.size() <= 1)
		return false;


    //获得路径起点的值,用来判断是否异号
	int a = nums[path.front()];
	path.pop();
	while (!path.empty())
	{
        //如果相乘为异号,说明路径不是全为正或负
		if (a * nums[path.front()] < 0)
			return false;
		path.pop();
	}
    //如果都满足就说就是一个循环路径,返回true
	return true;
}
    bool circularArrayLoop(vector<int>& nums) {
    //标记数组
	vector<bool>flag(nums.size(), false);
    //需要每个节点都遍历,有多种可能路径.
	for (int i = 0; i < nums.size(); i++)
	{
        //如果等于true就说明已经遍历过,就不需要遍历这个下标了.
		if (flag[i] == true)
			continue;
        //如果为false证明还没有被遍历过,就让它进函数判断是否有循环路径,false为引用传递
		if (findTrue(nums, i, flag))
			return true;
	}
    //如果全部都没有说明没有循环路径
	return false;
    }
};

总结:

这道题用标记很容易做出来,第一次完成时候是每一个节点都遍历一次,那样运行时间太长,后面想到标记数组可以用引用,因为同一个路径上的循环路径终点是一样的,不需要继续遍历一次.这样可以省下很多时间.完成后可以达到0ms,满意!

标签:index,下标,nums,路径,环形,457,循环,数组,true
来源: https://blog.csdn.net/weixin_46282078/article/details/119491026