This is the seventh day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Leetcode -457- Whether circular arrays have loops

[Blog link]

The way to study 🐔

The nuggets home page

[Topic description]

There is a circular array nums without zeros, and each NUMs [I] represents the number of subscripts that the role at subscript I should move forward or backward:

  • ifnums[i]Be positive,forwardmobilenums[i] 步
  • ifnums[i]Is negative,backwardmobilenums[i] 步

Since the array is circular, you can assume that a step forward from the last element will lead to the first element, and a step backward from the first element will lead to the last element.

The loop in an array consists of a sequence seq of length k with subscript:

  • Following the above movement rules results in repeated subscriptsseq[0] -> seq[1] -> ... -> seq[k - 1] -> seq[0] -> ...
  • allnums[seq[j]]Shall notAll areisAll negative
  • k > 1

Return true if nums has a loop; Otherwise, return false **.

Example 1:

Input: nums = [2,-1,1,2,2] Output: true Description: a loop exists, press the subscript 0 -> 2 -> 3 -> 0. The length of the cycle is 3.Copy the code

Example 2:

Input: nums = [-1,2] Output: false Description: Press the subscript 1 -> 1 -> 1... The movement of PI cannot form a cycle because the cycle has length one. By definition, the length of the loop must be greater than 1.Copy the code

Example 3:

Input: nums = [2, 1, 1, 2, 2] output: false interpretation: according to the subscript 1 - > 2 - > 1 - >... The movement of cannot form a loop because nums[1] is positive and nums[2] is negative. All nums[seq[J]] should be either positive or negative.Copy the code

Tip:

  • 1 <= nums.length <= 5000
  • -1000 <= nums[i] <= 1000
  • nums[i] ! = 0

 

Advanced: Can you design an algorithm of O(n) time complexity and O(1) extra space complexity?

Related Topics

  • An array of

  • Hash table

  • Double pointer

  • 👍 👎 0 121

[Topic link]

Leetcode title link

[making address]

Code link

[思路介绍]

Idea 1: Hash + class depth-first traversal

  • Iterating from the current node to repeating the node element at the starting index position represents a loop
  • Nodes traversed are stored through a set, which does not exceed the size of the array
  • If it is repeated and not satisfied, you can return false directly
  • At the same time, judge whether the elements in the ring are all positive/all negative
public boolean circularArrayLoop(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        if (check(nums, i)) {
            return true; }}return false;
}

public boolean check(int[] nums, int i) {
    int n = nums.length;
    // Return false if the starting point can be directly to the end
    if (nums[i] % n == 0) {
        return false;
    }
    int cur = nums[i] > 0 ? 1 : -1;
    Set<Integer> set = new HashSet<>();
    set.add(i);
    while (set.size() <= n) {
        // Exception
        if (nums[i] % n == 0 || cur * nums[i] < 0) {
            return false;
        }
        if (i + nums[i] < 0) {
            i = Math.abs((n + i + nums[i]) % n);
        } else if (i + nums[i] >= n) {
            i = (i + nums[i] - n) % n;
        } else {
            i = i + nums[i];
        }
        if (set.contains(i)) {
            return true;
        }
        set.add(i);
    }
    return false;
}
Copy the code
  • Time complexity O(n^{2})
  • Space complexity O(n)

Idea 2: Optimization of idea 1

  • The next index in the coordinate shift can be optimized for judgment
  • Math.abs((n + i + nums[i]) % n)
public boolean circularArrayLoop(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        if (check(nums, i)) {
            return true; }}return false;
}

public boolean check(int[] nums, int i) {
    int n = nums.length;
    // Return false if the starting point can be directly to the end
    if (nums[i] % n == 0) {
        return false;
    }
    int cur = nums[i] > 0 ? 1 : -1;
    Set<Integer> set = new HashSet<>();
    set.add(i);
    while (set.size() <= n) {
        // Exception
        if (nums[i] % n == 0 || cur * nums[i] < 0) {
            return false;
        }
        i = Math.abs((n + i + nums[i]) % n);
        if (set.contains(i)) {
            return true;
        }
        set.add(i);
    }
    return false;
}
Copy the code
  • Time complexity O(n^{2})
  • Space complexity O(n)

Idea 3: Further optimization of idea 1

  • You can do that without a set
  • Because a circular array goes through a point at the end, we iterate through the state of each node as the starting node
  • So when judging, we just need to judge whether we can return to the state of the repeated node
  • The size can also be determined by using the CNT number pointer
public boolean circularArrayLoop(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        if (check(nums, i)) {
            return true; }}return false;
}

public boolean check(int[] nums, int i) {
    int n = nums.length;
    int cur =i, k = 1;
    while (k <= n) {
        // Exception: single point cycle | | non-unidirectional movement
        if (nums[i] % n == 0 || nums[cur] * nums[i] < 0) {
            return false;
        }
        i = Math.abs((n + i + nums[i]) % n);
        if (i == cur ){
            return true;
        }
        k++;
    }
    return false;
}
Copy the code
  • Time complexity O(n^{2})
  • Space complexity O(1)

Train of thought four: fast and slow pointer

  • It’s easy to think of the loop finding method for a two-way linked list
    • How fast a pointer
  • The proof principle might be understood as
  • If there is a ring, the fast and slow Pointers must meet, and the meeting node is the starting node of the ring
  • The proof process can refer to Leetcode -141- Circular linked list
  • It also needs to determine whether it is one-way
int slow = 0, fast = 0, n = 0;
 int[] _nums;

 public boolean circularArrayLoop(int[] nums) {
     _nums = nums;
     n = nums.length;
     // There may be more than one ring
     for (int i = 0; i < nums.length; i++) {
         slow = i;
         fast = i;
         move();
         while(slow ! = fast) { move(); }// Loop the current point
         if (nums[slow] % n == 0) {
             continue;
         }
         / / FeiChanXiang
         int temp = slow;
         move();
         while(temp ! = slow) {if (nums[temp] * nums[slow] < 0) {
                 break;
             }
             move();
         }
         if (temp == slow){
             return true; }}return false;
 }

 public void move(a) {
     slow = Math.abs((n + slow + _nums[slow]) % n);
     fast = Math.abs((n + fast + _nums[fast]) % n);
     fast = Math.abs((n + fast + _nums[fast]) % n);
 }
Copy the code
  • Time complexity O(n^{2})
  • Space complexity O(1)

Idea 5: Optimization of idea 4

  • The fast and slow pointer can be directly assigned a value of 0 for special loop cases, and no longer traversal
  • In this case, the speed pointer formed by traversing all nodes can be guaranteed to be O(n) time complexity
 int n = 0;
   int[] _nums;

  public boolean circularArrayLoop(int[] nums) {
       _nums = nums;
       n = nums.length;
       // There may be more than one ring
       for (int i = 0; i < nums.length; i++) {
           // The already scanned points can be traversed without repeating
           if (nums[i] == 0) {
               continue;
           }
           int slow = i, fast = move(i);
           // Fast and slow Pointers move in the same direction
           while (nums[slow] * nums[fast] > 0 && nums[slow] * nums[move(fast)] > 0) {
               if (slow == fast) {
                   // Non-self ring
                   if(slow ! = move(slow)) {return true;
                   }
                   // Self loop
                   else {
                       break;
                   }
               }
               slow = move(slow);
               fast = move(move(fast));
           }
           // If a loop is not formed after a one-way scan, the value 0 can be assigned directly without repeated scanning
           int temp = i;
           while (nums[temp] * nums[move(temp)] > 0) {
               int tmp = temp;
               temp = move(temp);
               nums[tmp] = 0; }}return false;
   }

   public int move(int i) {
       return Math.abs((n + (i + _nums[i]) % n) % n);
       //return ((i + _nums[i]) % n + n) % n;
   }
Copy the code
  • Time complexity O(n)
  • Space complexity O(1)

Idea 6: Figure

  • This train of thought is the train of thought of three leaf big guy, somewhat difficult to understand
  • Define a sufficiently large number, OFFSET, as the OFFSET
  • Every time you get to one, you do the +1 operation
  • Returns true if a non-loop is detected
public boolean circularArrayLoop(int[] nums) {
    int OFFSET = 100010;
    int n = nums.length;
    for (int i = 0; i < n; i++) {
        // We have already traversed the nodes that cannot be looped, so we can not repeat the traversal
        if (nums[i] >= OFFSET) continue;
        // Represents the current node, the value of the node, and the length of the previous node
        int cur = i, tag = OFFSET + i, last = -1;
        // Represents the direction in which the node moves
        int flag = nums[cur] ;
        while (true) {
            // Next node
            int next = ((cur + nums[cur]) % n + n ) % n;
            // The length of the move
            last = nums[cur];
            // The current node has been traversed
            nums[cur] = tag;
            // The node moves to the next bit
            cur = next;
            // Repeat a node -- jump out of the loop
            if (cur == i) break;
            // Already traversed
            if (nums[cur] >= OFFSET) break;
            // Determine whether one direction is correct
            if (flag * nums[cur] < 0) break;
        }
        // If the loop is traversed only once, the loop is satisfied
        if(last % n ! =0 && nums[cur] == tag) return true;
    }
    return false;
}
Copy the code
  • Time complexity O(n)
  • Space complexity O(1)