【leetcode&C语言】457. Circular Array Loop

目录

 

问题描述

举例说明

注意

后续

问题补充

思路

实现(MU:75%,RT:50%)​

优化1(MU:100%, RT:37.5%)


 


 

问题描述

You are given a circular array nums of positive and negative integers. If a number k at an index is positive, then move forward k steps. Conversely, if it's negative (-k), move backward k steps. Since the array is circular, you may assume that the last element's next element is the first element, and the first element's previous element is the last element.

Determine if there is a loop (or a cycle) in nums. A cycle must start and end at the same index and the cycle's length > 1. Furthermore, movements in a cycle must all follow a single direction. In other words, a cycle must not consist of both forward and backward movements.

给你循环数组nums,其中有正数和负数。如果在一个索引上有一个正数k,从该索引移动k个元素。相反地,如果是负数(-k),向后移动k个元素。因为数组是循环的,你可以假设最后一个元素的下一个元素是第一个元素,第一个元素的前一个元素是最后一个元素。

在nums中证实是否存在循环。一个循环必须在同一索引开始和结束,并且循环长度大于1。另外,一个循环的移动必须朝一个方向。换句话说,一个循环必不能都包括向前和向后移动。

举例说明

Example 1:

Input: [2,-1,1,2,2]
Output: true
Explanation: There is a cycle, from index 0 -> 2 -> 3 -> 0. The cycle's length is 3.

Example 2:

Input: [-1,2]
Output: false
Explanation: The movement from index 1 -> 1 -> 1 ... is not a cycle, because the cycle's length is 1. By definition the cycle's length must be greater than 1.

Example 3:

Input: [-2,1,-1,-2,-2]
Output: false
Explanation: The movement from index 1 -> 2 -> 1 -> ... is not a cycle, because movement from index 1 -> 2 is a forward movement, but movement from index 2 -> 1 is a backward movement. All movements in a cycle must follow a single direction.

例子1

输入:[2,-1,1,2,2]

输出:true

说明:有一个周期,索引顺序是0 -> 2 -> 3 -> 0。周期长度是3。

例子2

输入:[-1,2]

输出: false

说明:索引顺序为1 -> 1 -> 1 ...的移动不是一个周期,因为周期长度是1。根据定义,周期长度必须大于1。

例子3

输入:[-2,1,-1,-2,-2]

输出:false

说明:索引顺序为1 -> 2 -> 1 -> ... 的移动不是一个周期,因为索引1->2的移动是向前移动,但是索引2->1的移动是向后移动。循环的移动方向是单一的。

注意

  1. -1000 ≤ nums[i] ≤ 1000
  2. nums[i] ≠ 0
  3. 1 ≤ nums.length ≤ 5000

后续

Could you solve it in O(n) time complexity and O(1) extra space complexity?

你能在时间复杂度O(n),空间复杂度O(1)的条件下解决该问题么?(小声)不能,下次优化解决方法

问题补充

       把上面的文字敲完等于重新再一次审题。结合我走过的弯路,提一下作者没有着重提到的内容。

       千万不要以为循环都是从第一个元素开始!!!可以从第二个,第三个……第n个开始,只要有一个循环就可以返回true。比如数组[3,1,2],如果你只考虑从第一个元素开始循环的情况,索引顺序是0->0->->0……。根据题干,它不是周期长度大于1,所以返回false。然而答案是true!!!因为如果从第二个和第三个元素开始,索引顺序分别是1->2->1和2->1->2,符合要求,返回true。

 

思路

      由题干可知,符合循环数组的条件是

  1. 循环的索引路径不能只有一个索引,必须是在一个以上
  2. 至少某次索引目的地需要掉头,这样才能达成循环
  3. 有且仅有一个方向 
  4. 开始和结束是在同一个元素 

      我的思路很浅显,就是遍历每一个元素i,以其作为循环起点,再开一个for循环考察其他方面是否符合。

      首先应该考虑第一个条件,因为其他三个条件需要for循环模拟移动的情况在做出判断,而第一个条件不用。判断索引i的元素nums[i]是否是数组长度numsSize的倍数。如果等于是,那就证明循环路径上只有一个索引(如例子2),continue;如果不不是,那就进入for循环考察以下三个方面。(一开始从这个条件上只想到要判断索引i的目的地是不是i,其实隐藏的条件是nums[i]是否等于数组长度。)

     对于移动到下一个索引的算式,是下一个索引j=(i+nums[i])%numsSize,如果k<0,还需要j=numsSize+((i+nums[i])%numsSize)。因为要判断第二个条件,所以取余之前先判断 (i+nums[i])/ numsSize != 0,如果不等于证明还不会走出数组长度,如果等于则需要取余得到目的地索引(掉头)。for循环之前设置一个布尔变量isCycle为false,当(i+nums[i])/ numsSize != 0时,证明其走完了数组需要掉头,即符合第二个条件,isCycle为true。但是,这个判断只适用于正数。假设数组[-1,-2,-4],i=0,i+nums[i]=-1,k=3-1=2。虽然(i+nums[i])/ numsSize=0,但是它也实现了“掉头”。所以如果(i+nums[i])<0 && |i+nums[i]|

     得到了j以后判断nums[j]*nums[i]<0,小于证明其有两个方向,即不符合第三个条件。

     第四个条件很好判断,如果起点索引i 等于索引j ,就说明有可能开始和结束是在同一个元素 ,此时还要将isCycle纳入考虑,即开始和结束是同一个元素,且确实循环了一周(或n周)。

实现(MU:75%,RT:50%)

bool circularArrayLoop(int* nums, int numsSize) {
	int i, j = 0, steps;
	bool isCycle;
	for (i = 0; i < numsSize; i++) {
		steps = 0;
		isCycle = false;
		if (nums[i] % numsSize == 0) continue;
		for (j = i + nums[i]; steps <= numsSize; j = j + nums[j], steps++) {
			if (j / numsSize != 0) 
            {
              isCycle = true;
              j = j % numsSize;//碍眼
            }			
			if (j < 0)
			{
				j = numsSize + j;
				isCycle = true;//碍眼
			}
			if (nums[j] * nums[i] < 0) break;
			if (j == i && isCycle) return true;
		}
	}
	return false;
}

【leetcode&C语言】457. Circular Array Loop_第1张图片

 

优化1(MU:100%, RT:37.5%)

       接上次的思路。其实排除了起点步长k是数组长度numsSize的倍数(即例子2的情况)之后,就不用考虑是否是循环这种情况。这个结论是从我原先代码得出,两个if语句都有isCycle=true,说明它还可以更简洁。我们可以考虑一下循环时所遇到的情况:

  1. 所有经过的元素和起点元素相乘不小于0,即都在同一个方向;所有经过的元素都不是数组长度的倍数
  2. 有元素nums[j]与起点元素相乘小于0×
  3. 有元素是数组长度的倍数×
  4. 第二种和第三种情况并存×

    按照这个思路,得到下面的代码:

bool circularArrayLoop(int* nums, int numsSize) {
	int i, j = 0, steps;
	for (i = 0; i < numsSize; i++) {
		steps = 0;
		if (nums[i] % numsSize == 0) continue;
		for (j = (i + nums[i])%numsSize; steps <= numsSize; j = (j + nums[j]) % numsSize, steps++) {
			if (j < 0)j = numsSize + j;
			if (nums[j] * nums[i] < 0) break;
			if (nums[j] % numsSize == 0) break;
			if (j == i) return true;
		}
	}
	return false;
}

     然而这个代码时间复杂度反而增加了

 

【leetcode&C语言】457. Circular Array Loop_第2张图片

 

你可能感兴趣的:(C,数据结构)