问题:
给定数组,nums[i]=k,k!=0
若k>0, 则下一个数的index是向右+|k|
若k<0,则下一个数的index是向左-|k|
(数组首尾相连)
这样的移动规则,问是否存在形成一个循环圈的访问环。(该环长度>1,且单向移动)
解法:
快慢指针法
慢指针一次移动一个,快指针一次移动两个,不断移动,若出现慢指针==快指针的时候,说明形成环
这里需要注意的:
1.应对nums[i]为负数的情况。
2.应对nums[i]为负数过大,mod之后还为负数的情况。
3.应对只对应同向移动
4.应对出现循环长度为1的情况
5.对不满足条件的置零操作。
参考代码:
1 class Solution { 2 public: 3 int N; 4 int getnext(vector<int>& nums, int i){ 5 int nextindex=0; 6 nextindex=(i+nums[i]+N)%N;//1. +N:应对nums[i]为负数的情况。 7 return nextindex; 8 } 9 bool circularArrayLoop(vector<int>& nums) { 10 int fast, slow; 11 N=nums.size(); 12 int sign = 1; 13 for (int& n : nums) { 14 n %= N; // 2. 比如[-2,-3,-9]这样的情况,-9的下一个+N之后再mod N会得到一个负数,防止负数的情况,首先将数组的每个数,都限定在整个数组长度内。 15 } 16 for(int i=0; i<N; i++){ 17 sign=nums[i]>0?1:-1; 18 slow=i; 19 fast=i; 20 while(true){ 21 if(nums[getnext(nums,fast)]*sign<=0 || nums[getnext(nums,getnext(nums,fast))]*sign<=0) break; 22 //3. 要使得每次遍历的数字符号与第一个数nums[i]相同,则为同方向移动 23 //这里,除了第一个nums[i],已经得到参照对象符号sign 24 //从本次循环的,两个元素分别判断: 25 //第二个元素getnext(nums,fast),第三个元素getnext(nums,getnext(nums,fast)) 26 slow=getnext(nums,slow); 27 fast=getnext(nums,getnext(nums,fast)); 28 if(slow==fast){ 29 if(slow==getnext(nums,slow)) break; 30 //4. 若出现循环,且其长度为1,则不满足条件,break 31 return true; 32 } 33 } 34 //5. 将不满足的【第一个元素i~最后一个<3或4的退出条件:开始反向移动 or 出现长度为1的循环>】 35 //将所有遍历过的数字置零,防止判断下一个数字重复判断同一个移动路径 36 //while(nums[k]*sign>0){ 可以满足同向移动,且没有被置零的路径,都进行置零 37 int k=i; 38 while(nums[k]*sign>0){ 39 int nexx=getnext(nums,k); 40 nums[k]=0; 41 k=nexx; 42 } 43 } 44 return false; 45 } 46 };