[LeetCode] Circular Array Loop 环形数组循环
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.
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.
Note:
- -1000 ≤ nums[i] ≤ 1000
- nums[i] ≠ 0
- 1 ≤ nums.length ≤ 5000
Follow up:
Could you solve it in O(n) time complexity and O(1) extra space complexity?
说实话,这道题描述的并不是很清楚,比如题目中有句话说循环必须是 forward 或是 backward 的,如果不给例子说明,不太容易能 get 到 point。所谓的循环必须是一个方向的就是说不能跳到一个数,再反方向跳回来,这不算一个 loop。比如 [1, -1] 就不是一个 loop,而 [1, 1] 是一个正确的 loop。看到论坛中一半的帖子都是各种需要 clarify 和不理解 test case 就感觉很好笑~ 当然博主也成功踩坑了。弄清楚了题意后来考虑如何做,由于从一个位置只能跳到一个别的位置,而不是像图那样一个点可以到多个位置,所以这里我们就可以用个 HashMap 根据坐标建立一对一的映射,一旦某个达到的坐标已经有映射了,说明环存在,当然我们还需要进行一系列条件判断。首先我们需要一个 visited 数组,来记录访问过的数字,然后我们遍历原数组,如果当前数字已经访问过了,直接跳过,否则就以当前位置坐标为起始点开始查找,进行 while 循环,计算下一个位置,计算方法是当前位置坐标加上对应的数字,由于是循环数组,所以结果可能会超出数组的长度,所以我们要对数组长度取余。当然上面的数字也可能是负数,加完以后可能也是负数,所以在取余之前还得再补上一个n,使其变为正数,但是 若这个负数远大于n的话,取余之前只加上一个n,可能是不够的,所以正确的方法是应该先对n取余,再加上n。为了同时把正数的情况也包含进来,最终我们的处理方法是先对n取余,再加上n,再对n取余,这样不管正数还是负数,大小如何,都可以成功的旋转跳跃了。此时我们判断,如果 next 和 cur 相等,说明此时是一个数字的循环,不符合题意,再有就是检查二者的方向,数字是正数表示 forward,若是负数表示 backward,在一个 loop 中必须同正或同负,我们只要让二者相乘,如果结果是负数的话,说明方向不同,直接 break 掉。此时如果 next 已经有映射了,说明我们找到了合法的 loop,返回 true,否则建立一个这样的映射,将 next 位置在 visited 数组中标记 true,继续循环,参见代码如下:
解法一:
class Solution {
public:
bool circularArrayLoop(vector<int>& nums) {
int n = nums.size();
vector<bool> visited(n);
for (int i = ; i < n; ++i) {
if (visited[i]) continue;
visited[i] = true;
unordered_map<int, int> m;
int cur = i;
while (true) {
int next = ((cur + nums[cur]) % n + n) % n;
if (next == cur || nums[next] * nums[cur] < ) break;
if (m.count(next)) return true;
m[cur] = next;
cur = next;
visited[next] = true;
}
}
return false;
}
};
我们也可以不用 visited 数组,直接在 nums 中标记,由于题目中说了 nums 中不会有0,所以可以把访问过的位置标记为0。然后计算 next 位置,对于会超出数组的长度的正数,我们可以通过对n取余,但是对于负数,若这个负数远大于n的话,取余之前只加上一个n,可能是不够的,所以正确的方法是应该先对n取余,再加上n。为了同时把正数的情况也包含进来,最终我们的处理方法是先对n取余,再加上n,再对n取余,这样不管正数还是负数,大小如何,都可以成功的旋转跳跃了。接下来看,如果 next 和i相等,直接跳过,因为这表示循环只有一个数字,不合题意。然后开始循环,当 cur 和 nums[next] 的乘积为正时,说明此时是一个方向的,我们将 cur 赋值为 nums[next],将 nums[next] 赋值为0,表示已经访问过,然后再计算新的 next 位置。直到循环被打破,若此时 next 和i相等,说明有大于1个数字的环存在,返回 true。最终 for 循环退出后,返回 false 即可,代码参见评论区11楼。想法倒是不错,但是存在一个逻辑上的错误。
对于 [1, 1, 2] 这个例子,当 i=0 时,最后跳出 while 循环时 next 是等于1的,和 i 不相等,所以没法返回 true。但这个例子其实是有正确的循环的,是后两个数字1和2可以循环,那为什么我们没 catch 到呢?因为我们前面的思路默认为循环开始的位置就是i,但明显不一定成立的,就像在链表中找环一样,环的起点可以是任意位置啊,不一定是表头结点啊。那为啥上面的解法就可以呢?这是因为上面使用的是 HashMap,而且对于每个i,都使用了一个新的 HashMap,跟之前的并没有联系,而且把以i位置为起点,经过的位置都存到了 HashMap 中,这样是可以找出环的。而这里我们直接跟i相比肯定是不对的。那么既然说到了链表中找环,刷题老司机们大声告诉我该用什么?对了,就是快慢指针了。那么对于每个i位置,慢指针指向i,快指针指向下一个位置,这里调用子函数来计算下一个位置。此时慢指针指向的数要和快指针指向的数正负相同,这个不难理解。并且慢指针指向的数还要跟快指针的下一个位置上的数符号相同,这个原因后面再讲。上面这两个就是 while 循环的条件,我们直到当快慢指针相遇的时候,就是环出现的时候,但是这里有个坑,即便快慢指针相遇了,也不同立马返回 true,因为题目中说了了环的长度必须大于1,所以我们要用慢指针指向的数和慢指针下一个位置上的数比较,若相同,则说明环的长度为1,此时并不返回 false,而且 break 掉 while 循环。因为这只能说以i位置开始的链表无符合要求的环而已,后面可能还会出现符合要求的环。但是若二者不相同的话,则已经找到了符合要求的环,直接返回 true。若快慢指针还不相同的,则分别更新,慢指针走一步,快指针走两步。当 while 循环退出后,我们需要标记已经走过的结点,从而提高运算效率,方法就是将慢指针重置为i,再用一个 while 循环,条件是 nums[i] 和 慢指针指的数正负相同,然后计算下一个位置,并且 nums[slow] 标记为0,并且慢指针移动到 next 位置。最终 for 循环退出后,返回 false 即可,参见代码如下:
解法二:
class Solution {
public:
bool circularArrayLoop(vector<int>& nums) {
int n = nums.size();
for (int i = ; i < n; ++i) {
if (nums[i] == ) continue;
int slow = i, fast = getNext(nums, i), val = nums[i];
while (val * nums[fast] > && val * nums[getNext(nums, fast)] > ) {
if (slow == fast) {
if (slow == getNext(nums, slow)) break;
return true;
}
slow = getNext(nums, slow);
fast = getNext(nums, getNext(nums, fast));
}
slow = i;
while (val * nums[slow] > ) {
int next = getNext(nums, slow);
nums[slow] = ;
slow = next;
}
}
return false;
}
int getNext(vector<int>& nums, int i) {
int n = nums.size();
return (((nums[i] + i) % n) + n) % n;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/457
参考资料:
https://leetcode.com/problems/circular-array-loop/
https://leetcode.com/problems/circular-array-loop/discuss/94148/Java-SlowFast-Pointer-Solution
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] Circular Array Loop 环形数组循环的更多相关文章
- [LeetCode] 457. Circular Array Loop 环形数组循环
You are given a circular array nums of positive and negative integers. If a number k at an index is ...
- 【LeetCode】457. Circular Array Loop 环形数组是否存在循环 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题思路 快慢指针 代码 日期 题目地址:https://le ...
- Leetcode: Circular Array Loop
You are given an array of positive and negative integers. If a number n at an index is positive, the ...
- Leetcode 457.环形数组循环
环形数组循环 给定一组含有正整数和负整数的数组.如果某个索引中的 n 是正数的,则向前移动 n 个索引.相反,如果是负数(-n),则向后移动 n 个索引. 假设数组首尾相接.判断数组中是否有环.环中至 ...
- Java实现 LeetCode 457 环形数组循环
457. 环形数组循环 给定一个含有正整数和负整数的环形数组 nums. 如果某个索引中的数 k 为正数,则向前移动 k 个索引.相反,如果是负数 (-k),则向后移动 k 个索引.因为数组是环形的, ...
- LeetCode 457. Circular Array Loop
原题链接在这里:https://leetcode.com/problems/circular-array-loop/ 题目: You are given a circular array nums o ...
- [Swift]LeetCode457. 环形数组循环 | Circular Array Loop
You are given an array of positive and negative integers. If a number n at an index is positive, the ...
- [LeetCode] Design Circular Deque 设计环形双向队列
Design your implementation of the circular double-ended queue (deque). Your implementation should su ...
- [LeetCode] Design Circular Queue 设计环形队列
Design your implementation of the circular queue. The circular queue is a linear data structure in w ...
随机推荐
- CSS美化网页元素
<span>标签 </span>属性名 含义 举例font-family 设置字体类型 font-family:"隶书"font-size 设置字体大小 f ...
- Mybatis-no getter for property named 'col_name' in 'class com.xxx.onebean'
Mybatis中出现该异常 There is no getter for property named 'col_name' in 'class com.xxx.onebean 意思是onebean这 ...
- Linux下的硬链接与软链接
本文总结自: https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/index.html#fig2 一个文件可以用下图表 ...
- HTTP协议----URI,URL,持久连接,管道与Cookie
URI与URL有什么不同呢? URI:Universal Resource Identifier统一资源标志符 URL:Universal Resource Locator统一资源定位器 URI是用来 ...
- 极光征文 | 写写文章就能赢 Filco,岂不美滋滋
由极光社区举办的第二届征文大赛 --「我和极光的那些事儿」又来啦! 在简书平台发布文章并投稿至「我和极光的那些事」专题,只要参与就能 100% 获得京东购物卡,更有机会赢取象征信仰的 Filco 机械 ...
- Java读取文件存储到mysql
写了一批Lua脚本,要放到数据库里面,调用的时候进行计算,由于有太多lua脚本,就写了个程序来录入. 下面主要分三个部分: public static String readToString(Stri ...
- Angular.js 1++快速上手
AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Goole所收购.是一款优秀的前端JS框架.AngularJS有着诸多特性,最为核心的是:MVC,撗块化,自动化双向数据绑 ...
- ebtables和iptables与linux bridge的交互
本文为翻译文,不一定是逐字逐句的翻译,而且中间会加上自己的一点见解,如有理解错误的地方,还请大家指出,我定虚心学习.原文见链接 其中斜体字是自己的理解,建议和ebtables手册和iptables手册 ...
- java JDK源码解析
Hashmap 使用java语言进行系统开发时,使用得比较多得数据结构hashmap,它以[key,value],进行数据存储,通过key可以快速找到到对应的value值,但是key,value不能是 ...
- js判断操作系统windows,ios,android(笔记)
使用JS判断用户使用的系统是利用浏览器的userAgent. navigator.userAgent:userAgent 获取了浏览器用于 HTTP 请求的用户代理头的值. navigator.pla ...