环形数组循环
环形数组循环
给定一个含有正整数和负整数的环形数组nums
,如果某个索引中的数k
为正数,则向前移动 k
个索引,相反如果是负数-k
,则向后移动k
个索引。因为数组是环形的,所以可以假设最后一个元素的下一个元素是第一个元素,而第一个元素的前一个元素是最后一个元素,确定nums
中是否存在循环或周期。循环必须在相同的索引处开始和结束并且循环长度>1
。此外,一个循环中的所有运动都必须沿着同一方向进行,换句话说,一个循环中不能同时包括向前的运动和向后的运动。
示例
输入:[2,-1,1,2,2]
输出:true
解释:存在循环,按索引 0 -> 2 -> 3 -> 0 。循环长度为 3 。
输入:[-1,2]
输出:false
解释:按索引 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。
输入:[-2,1,-1,-2,-2]
输出:false
解释:按索引 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为按索引 1 -> 2 的运动是向前的运动,而按索引 2 -> 1 的运动是向后的运动。一个循环中的所有运动都必须沿着同一方向进行。
题解
/**
* @param {number[]} nums
* @return {boolean}
*/
var circularArrayLoop = function(nums) {
var n = nums.length;
var getNext = x => {
var nextIndex = (x+nums[x])%n;
return nextIndex >= 0 ? nextIndex : nextIndex+n;
};
for(let i=0;i<n;++i) {
if(nums[i] === 0) continue;
let slow = i;
let fast = getNext(i);
while(nums[slow]*nums[fast] > 0 && nums[fast] * nums[getNext(fast)] > 0){
if(slow === fast){
if(slow === getNext(slow)) break;
else return true;
}
slow = getNext(slow);
fast = getNext(getNext(fast));
}
let tmp = i;
let val = nums[tmp];
while(val * nums[tmp] > 0){
let k = getNext(tmp);
nums[tmp] = 0;
tmp = k;
}
}
return false;
};
思路
首先需要解释一下题意,以示例1
中[2,-1,1,2,2]
为例,最开始是索引0
值为2
,那么索引向前走2
步到索引2
值为1
,继续向前走1
步到达索引3
值为2
,再向前走2
步循环索引回到0
,所以这完成了一次循环,这里的起始点并不一定是索引0
,起始点可以为任意索引位置,其次就是限制条件循环的长度必须大于1
以及一个循环中的所有运动都必须沿着同一方向进行。
本题使用快慢指针来做,快指针每次走两步,慢指针每次走一步,如果能够达成循环那么快慢指针必定会相遇,当然在此处一步与两步指的是移动一个nums[i]
的步长,不是移动index+1
,首先定义一个n
为数组长度以及getNext
方法作为取得该点的下一步的索引值,之后遍历数组,根据定义,数组中不能存在0
元素,所以以0
为标记值进行剪枝,以慢指针指向i
,快指针指向下一步的索引,while
循环中第一个判断是保证慢指针与快指针指向的数组值符号相同,第二个判断是保证快指针指向的数组值与下一个快指针指向的数组值同号,保证一个循环中的所有运动都必须沿着同一方向进行,之后如果快慢指针相遇,则判断是否循环的长度为1
,若循环的长度为1
则不符合条件,便继续查找,否则就可以说明该数组中存在循环,之后便是slow
指针走一步,fast
指针走两部,最后需要剪枝,因为已经遍历过的元素不可能出现在循环当中,所以将以i
为索引开始的每一步都置0
,用以实现剪枝。
每日一题
https://github.com/WindrunnerMax/EveryDay
参考
https://leetcode-cn.com/problems/circular-array-loop
- 孤立的SQL用户
- 如何卸载CDH(附一键卸载github源码)
- github & CSRF
- 如何使用Python读取大文件
- 介绍一种非常好用汇总数据的方式GROUPING SETS
- 史上最大的CPU Bug(幽灵和熔断的OS&SQLServer补丁)
- 数据库副本的自动种子设定(自增长)
- Git 项目推荐 | 基于go+protobuff 实现的分布式
- ReflectASM-invoke,高效率java反射机制原理
- Web应用渗透测试-本地文件包含
- shiro权限控制(二):分布式架构中shiro的实现
- Groovy实现原理分析——准备工作
- HBCTF第一场2个pwn题的简单分析
- ACM竞赛之输入输出(以C与C++为例)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android-ViewModel和LiveData使用详解
- 详解Android开发录音和播放音频的步骤(动态获取权限)
- Android自定义带圆角的ImageView
- 关于Kotlin写界面时诸多控件的点击事件
- Android webview注入JS代码 修改网页内容操作
- Kotlin 使用高阶函数实现回调方式
- Android WebView通过动态的修改js去拦截post请求参数实例
- Android使用Kotlin实现多节点进度条
- Android中webView加载H5绑定cookie实例
- 解决Android webview设置cookie和cookie丢失的问题
- Android实现清除单个域名的cookie
- Android实现触发html页面的Button控件点击事件方式
- webview添加参数与修改请求头的user-agent实例
- android webview获取html代码和根据id获取value实例
- pip 安装MySQL-python:EnvironmentError: mysql_config not found