力扣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 。
思路
我們先解析一下題目的意思是什么:一個數組中存儲的值的絕對值就是移動的步數,正負則是移動的方向。想要判斷一個數組有環必須滿足兩個條件:一是環狀的也就是會重復進行,二是這個環的每個位置的移動方向都是相同的。也就是要么全部為正形成一個環要么全部為負形成一個環。有正有負的不算是一個環。
在了解了題目真正的意思后我們來思考一下要怎么做出這道題目,有環比較滿足兩個條件那么我們解法的重點也是這兩個條件,判斷是環狀的所以我們需要計算出下一個位置在哪,移動方向要相同的所以我們需要設一個標志位來進行判斷。
標志位好說,我們可以設置一個bool類型當作標志位flag,值大于0就為true小于0就為負,每次得到下一個位置時我們都需要進行判斷flag和nums[next]的值是否是相反的如果相反就說明環不成立。接下來就是如何計算下一個位置了如果全為正值那么就好計算了我們只需要讓當前下標加上當前下標所代表的值再取模整個數組的長度即可,但是這道題中是存在負數的如果全為負數我們要怎么計算下一個位置呢?我們要在正數的基礎上加上數組的長度如何再取模一次數組的長度也就是(x+n)%n,這是因為在原本的計算方式下在第一次取模完數組的長度值的大小就被控制在了(-n,n)這個區間里所以我們再加上一個數組的長度這樣范圍就變成了(0,n)這個區間里。這樣無論是正數還是負數都可以得到下一個位置。
在有了這兩個條件后大體的框架就出來了但是我們還需要注意一個點,有沒有可能一個數組沒有環但是它的移動方向全部也是相同的呢?完全有可能,所以我們還需要一個判斷條件就是我們移動了幾次,如果移動次數大于數組的長度了判斷出有環就說明這個數組是沒有環的并且移動方向還都是相同的。
代碼
class Solution {
public:bool check(int start,vector<int> & nums){int cur = start;int n = nums.size();//開頭位置是向后的還是向前的bool flag = nums[start] > 0;//處理的數字數量int k = 1;while(true){//如果數量超過長度//說明有數字被處理兩次即沒有環if(k > n){return false;}//下一個位置int next = ((cur + nums[cur]) % n + n) % n;//如果下一個位置和當前位置前進方向是相反的說明沒有環if(flag && nums[next] < 0 ){return false;}if(!flag && nums[next] > 0){return false;}//如果下一個位置和開始位置相同說明有環if(next == start){//同時還需要判斷位置是否移動了return k >1;}cur = next;k++;}}bool circularArrayLoop(vector<int>& nums) {int n = nums.size();for(int i = 0;i < n ;i++){//把每個數字都當做起點來進行判斷是否有環if(check(i,nums)){return true;}}return false;}
};