[LeetCode] Circular Array Loop 環形陣列迴圈
You are given an array of positive and negative integers. If a number n at an index is positive, then move forward n steps. Conversely, if it's negative (-n), move backward n steps. Assume the first element of the array is forward next to the last element, and the last element is backward next to the first element. Determine if there is a loop in this array. A loop starts and ends at a particular index with more than 1 element along the loop. The loop must be "forward" or "backward'.
Example 1: Given the array [2, -1, 1, 2, 2], there is a loop, from index 0 -> 2 -> 3 -> 0.
Example 2: Given the array [-1, 2], there is no loop.
Note: The given array is guaranteed to contain no element "0".
Can you do it in O(n) time complexity and O(1) space complexity?
說實話,這道題描述的並不是很清楚,比如題目中有句話說迴圈必須是forward或是backward的,如果不給例子說明,不太容易能get到point。所謂的迴圈必須是一個方向的就是說不能跳到一個數,再反方向跳回來,這不算一個loop。比如[1, -1]就不是一個loop,而[1, 1]是一個正確的loop。看到論壇中一半的帖子都是各種需要clarify和不理解test case就感覺很好笑~博主也成功踩坑了。弄清楚了題意後來考慮如何做,由於從一個位置只能跳到一個別的位置,而不是像圖那樣一個點可以到多個位置,所以這裡我們就可以根據座標建立一對一的對映,一旦某個達到的座標已經有映射了,說明環存在,當然我們還需要進行一系列條件判斷。首先我們需要一個visited陣列,來記錄訪問過的數字,然後我們遍歷原陣列,如果當前數字已經訪問過了,直接跳過,否則就以當前位置座標為起始點開始查詢,進行while迴圈,將當前位置在visited陣列中標記true,然後計算下一個位置,計算方法是當前位置座標加上對應的數字,由於是迴圈陣列,所以結果可能會超出陣列的長度,所以我們要對陣列長度取餘。當然上面的數字也可能是負數,加完以後可能也是負數,所以光取餘還不夠,還得再補上一個n,使其變為正數。此時我們判斷,如果next和cur相等,說明此時是一個數字的迴圈,不符合題意,再有就是檢查二者的方向,數字是正數表示forward,若是負數表示backward,在一個loop中必須同正或同負,我們只要讓二者相乘,如果結果是負數的話,說明方向不同,直接break掉。此時如果next已經有映射了,說明我們找到了合法的loop,返回true,否則建立一個這樣的對映,繼續迴圈,參見程式碼如下:
class Solution { public: bool circularArrayLoop(vector<int>& nums) { unordered_map<int, int> m; int n = nums.size(); vector<bool> visited(n, false); for (int i = 0; i < n; ++i) { if (visited[i]) continue;int cur = i; while (true) { visited[cur] = true; int next = (cur + nums[cur]) % n; if (next < 0) next += n; if (next == cur || nums[next] * nums[cur] < 0) break; if (m.count(next)) return true; m[cur] = next; cur = next; } } return false; } };