存在一个不含0的环形数组nums,每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数:
1)如果 nums[i] 是正数,向前移动 nums[i] 步
2)如果 nums[i] 是负数,向后移动 nums[i] 步
因为数组是环形的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。
数组中的循环由长度为k的下标序列seq:
1)遵循上述移动规则将导致重复下标序列 seq[0] -> seq[1] -> ... -> seq[k-1] -> seq[0] -> ...
2)所有 nums[seq[j]] 应当不是 全正 就是 全负
3)k > 1
如果 nums 中存在循环,返回 true;否则,返回 faslse
示例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]]应当不是全正就是全负。
第1种解题方法(算是自己想出来的一种暴力解法):
按下标循环遍历整个数组,对于每个下标,创建两个指针,分别记作快指针、慢指针,慢指针不动,快指针不断向前遍历,判断其是否会遇到慢指针,如果遇到慢指针(快指针遍历的元素个数需大于1,排除由1个元素构成的闭环),则返回“有环”的结果,如果遇到下述3种情况之一,则终止当前快指针的遍历循环,继续向前遍历数组
1)快指针遍历到的元素与当前下标指定的方向不同,则从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中),并且将数组还可以遍历的有效元素个数 减去 最新被标记为0的元素的个数
2)对于每个下标,如果快指针遍历的元素个数大于数组中还可以遍历的有效元素时,快指针仍未与慢指针相遇,则暂停当前循环,只将慢指针指向的元素置0。在此种情况中,可以判定慢指针指向的元素肯定不在环中,但无法判断是否有环,这里需暂停程序,否则,程序可能会陷入死循环中。
3)如果快指针遍历到“已判断出肯定不在环中的元素”时,则从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中),并且将数组还可以遍历的有效元素个数 减去 最新被标记为0的元素的个数
代码如下所示:
#include <iostream>
#include <vector>
using namespace std;
bool circularArrayLoop(vector<int>& nums) {
int fastIndex;
int len;
int direction;
int removeSum = 0;
int index;
int step;
for(int slowIndex = 0; slowIndex < nums.size(); slowIndex++) {
len = 0;
if(nums[slowIndex] > 0) {
direction = 1;
}
else {
direction = -1;
}
fastIndex = (slowIndex + nums[slowIndex] + nums.size()) % nums.size();
while(true) {
if(fastIndex == slowIndex) {
if(len == 0) {
break;
}
else {
return true;
}
}
if(nums[fastIndex] * direction <= 0) {
index = (slowIndex + nums[slowIndex] + nums.size()) % nums.size();
while(index != fastIndex) {
step = nums[index];
nums[index] = 0;
index = (index + step + nums.size()) % nums.size();
removeSum++;
}
break;
}
else {
if(len < nums.size() - removeSum) {
fastIndex = (fastIndex + nums[fastIndex] + nums.size()) % nums.size();
len++;
}
else {
nums[slowIndex] = 0;
break;
}
}
}
}
return false;
}
int main() {
vector<int> arr;
string line;
string temp;
while (getline(cin, line)) {
int i;
for (i = 0; i < line.size(); i++) {
if(line[i] == '[') {
continue;
}
if(line[i] == ',') {
arr.emplace_back(stoi(temp));
temp.clear();
continue;
}
if(line[i] == ']') {
arr.emplace_back(stoi(temp));
temp.clear();
break;
}
temp += line[i];
}
bool flag = circularArrayLoop(arr);
cout << flag << endl;
arr.clear();
}
return 0;
}
第2种解题方法
第2种方法是对第1种方法的优化,这里仅写出两种方法的区别之处
在遍历数组中的每个元素时,对于每个下标,创建两个指针,分别记作快指针、慢指针,慢指针不动,快指针遇到4种情况时会终止当前遍历,第二种方法与第一种方法的区别之处就在第2种情况的优化处理,添加了第4种情况
1)快指针遍历到的元素与当前下标指定的方向不同,则从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中),并且将数组还可以遍历的有效元素个数 减去 最新被标记为0的元素的个数
2)对于每个下标,如果快指针遍历的元素个数大于等于数组中还可以遍历的有效元素时,则判定这种情况必然存在环(使用第4种情况排除由一个结点构成的闭环),返回“有环”的结果
3)如果快指针遍历到“已判断出肯定不在环中的元素(该元素已标记为0)”时,则从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中),并且将数组还可以遍历的有效元素个数 减去 最新被标记为0的元素的个数
4)如果快指针遍历到元素和其遍历到的上一个元素相同,则从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中),并且将数组还可以遍历的有效元素个数 减去 最新被标记为0的元素的个数
代码如下所示(处理输入输出的代码与第一种情况相同,不再给出):
bool circularArrayLoop(vector<int>& nums) {
int fastIndex;
int len;
int direction;
int removeSum = 0;
int index;
int step;
int lastIndex;
for(int slowIndex = 0; slowIndex < nums.size(); slowIndex++) {
len = 0;
if(nums[slowIndex] > 0) {
direction = 1;
}
else {
direction = -1;
}
fastIndex = (slowIndex + nums[slowIndex] + nums.size()) % nums.size();
lastIndex = slowIndex;
while(true) {
if(fastIndex == slowIndex) {
if(len == 0) {
break;
}
else {
return true;
}
}
if((nums[fastIndex] * direction <= 0) || (fastIndex == lastIndex)) {
index = (slowIndex + nums[slowIndex] + nums.size()) % nums.size();
while(index != fastIndex) {
step = nums[index];
nums[index] = 0;
index = (index + step + nums.size()) % nums.size();
removeSum++;
}
break;
}
else {
if(len < nums.size() - removeSum) {
lastIndex = fastIndex;
fastIndex = (fastIndex + nums[fastIndex] + nums.size()) % nums.size();
len++;
}
else {
return true;
}
}
}
}
return false;
}
第3种解题方法(快慢指针法):
遍历数组中的每个下标,对于1个下标,分别创建快指针、慢指针,快指针每次移动2步,慢指针每次移动1步,如果快指针等于慢指针,返回“有环”的结果,遇到以下3种情况,则终止当前快慢指针的遍历循环
1)快指针遍历到的元素与当前下标指定的方向不同。从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中)
2)快指针遍历到元素和其遍历到的上一个元素相同。从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中)
3)快指针遍历到“已判断出肯定不在环中的元素(该元素已标记为0)”时。从当前下标开始到快指针遍历到的元素(不含)均设置为0(标记该元素已遍历过,不可能在环中)
此处使用快慢指针法判断是否有环,可进行进一步的拓展,计算环入口的下标、环的周长,可与LeetCode141.环形链表类比,代码如下所示:
#include <iostream>
#include <vector>
using namespace std;
int getPerimter(vector<int>& nums, int fastIndex) {
int index = fastIndex;
int len = 0;
do {
index = (index + nums[index] + nums.size()) % nums.size();
len++;
} while(index != fastIndex);
return len;
}
int getPosition(vector<int>& nums, int start, int fastIndex) {
int index1 = start;
int index2 = fastIndex;
while(index1 != index2) {
index1 = (index1 + nums[index1] + nums.size()) % nums.size();
index2 = (index2 + nums[index2] + nums.size()) % nums.size();
}
return index1;
}
bool circularArrayLoop(vector<int>& nums) {
int direction;
int slowIndex;
int fastIndex;
int lastIndex;
int index;
int step;
for(int i = 0; i < nums.size(); i++) {
slowIndex = i;
fastIndex = i;
if(nums[slowIndex] == 0) {
continue;
}
else if(nums[slowIndex] > 0) {
direction = 1;
}
else {
direction = -1;
}
while(true) {
lastIndex = fastIndex;
fastIndex = (fastIndex + nums[fastIndex] + nums.size()) % nums.size();
if(fastIndex == lastIndex) {
index = i;
step = nums[index];
nums[index] = 0;
while(index != fastIndex) {
index = (index + step + nums.size()) % nums.size();
step = nums[index];
nums[index] = 0;
}
break;
}
if(nums[fastIndex] * direction <= 0) {
index = i;
do {
step = nums[index];
nums[index] = 0;
index = (index + step + nums.size()) % nums.size();
} while(index != fastIndex);
break;
}
lastIndex = fastIndex;
fastIndex = (fastIndex + nums[fastIndex] + nums.size()) % nums.size();
if(fastIndex == lastIndex) {
index = i;
step = nums[index];
nums[index] = 0;
while(index != fastIndex) {
index = (index + step + nums.size()) % nums.size();
step = nums[index];
nums[index] = 0;
}
break;
}
if(nums[fastIndex] * direction <= 0) {
index = i;
do {
step = nums[index];
nums[index] = 0;
index = (index + step + nums.size()) % nums.size();
} while(index != fastIndex);
break;
}
slowIndex = (slowIndex + nums[slowIndex] + nums.size()) % nums.size();
if(fastIndex == slowIndex) {
int len = getPerimter(nums, fastIndex);
cout << "the perimter of ring: " << len << endl;
int position = getPosition(nums, i, fastIndex);
cout << "entrance index: " << position << endl;
return true;
}
}
}
return false;
}
int main() {
vector<int> arr;
string line;
string temp;
while (getline(cin, line)) {
int i;
for (i = 0; i < line.size(); i++) {
if(line[i] == '[') {
continue;
}
if(line[i] == ',') {
arr.emplace_back(stoi(temp));
temp.clear();
continue;
}
if(line[i] == ']') {
arr.emplace_back(stoi(temp));
temp.clear();
break;
}
temp += line[i];
}
bool flag = circularArrayLoop(arr);
cout << flag << endl;
arr.clear();
}
return 0;
}