♥每日理解一道题♥/ 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