-
-
Notifications
You must be signed in to change notification settings - Fork 9.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
robot
committed
Sep 4, 2021
1 parent
ecdd15d
commit 16dbd7b
Showing
3 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
## 题目地址(457. 环形数组是否存在循环) | ||
|
||
https://leetcode-cn.com/problems/circular-array-loop/ | ||
|
||
## 题目描述 | ||
|
||
``` | ||
存在一个不含 0 的 环形 数组 nums ,每个 nums[i] 都表示位于下标 i 的角色应该向前或向后移动的下标个数: | ||
如果 nums[i] 是正数,向前(下标递增方向)移动 |nums[i]| 步 | ||
如果 nums[i] 是负数,向后(下标递减方向)移动 |nums[i]| 步 | ||
因为数组是 环形 的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。 | ||
数组中的 循环 由长度为 k 的下标序列 seq 标识: | ||
遵循上述移动规则将导致一组重复下标序列 seq[0] -> seq[1] -> ... -> seq[k - 1] -> seq[0] -> ... | ||
所有 nums[seq[j]] 应当不是 全正 就是 全负 | ||
k > 1 | ||
如果 nums 中存在循环,返回 true ;否则,返回 false 。 | ||
示例 1: | ||
输入:nums = [2,-1,1,2,2] | ||
输出:true | ||
解释:存在循环,按下标 0 -> 2 -> 3 -> 0 。循环长度为 3 。 | ||
示例 2: | ||
输入:nums = [-1,2] | ||
输出:false | ||
解释:按下标 1 -> 1 -> 1 ... 的运动无法构成循环,因为循环的长度为 1 。根据定义,循环的长度必须大于 1 。 | ||
示例 3: | ||
输入:nums = [-2,1,-1,-2,-2] | ||
输出:false | ||
解释:按下标 1 -> 2 -> 1 -> ... 的运动无法构成循环,因为 nums[1] 是正数,而 nums[2] 是负数。 | ||
所有 nums[seq[j]] 应当不是全正就是全负。 | ||
提示: | ||
1 <= nums.length <= 5000 | ||
-1000 <= nums[i] <= 1000 | ||
nums[i] != 0 | ||
进阶:你能设计一个时间复杂度为 O(n) 且额外空间复杂度为 O(1) 的算法吗? | ||
``` | ||
|
||
## 前置知识 | ||
|
||
- 图 | ||
|
||
## 公司 | ||
|
||
- 暂无 | ||
|
||
## 解法一 - 暴力解 | ||
|
||
### 思路 | ||
|
||
根据题意,我们可以检查所有情况。即分别检查从索引 0,1,2。。。 n-1 开始的情况, 判断其是否能**构成长度至少为 2 的环**。 | ||
|
||
不难理出算法框架为: | ||
|
||
```py | ||
for i in range(n): | ||
if can(i): return True | ||
return False | ||
``` | ||
|
||
can(i) 功能是检查是否从 i 开始可以有一条长度至少为 2 的循环。 | ||
|
||
那么剩下的问题就是如何实现 can(i)。 检查是否有环明显就是一个标准的图的搜索问题,套用模板即可。 | ||
|
||
这里有几点需要注意: | ||
|
||
1. 由于我们必须同正同负,那么我们可以记录一下其实的正负。如果遍历到的值不同为正负则可以提前退出。 | ||
2. 如果环大小小于 2 则返回 False,这提示我们记录一下环的大小。如下代码 steps 就是环的大小。 | ||
3. 由于题目是限定了**数组是 环形 的,所以可以假设从最后一个元素向前移动一步会到达第一个元素,而第一个元素向后移动一步会到达最后一个元素。** 因此我们需要对两种越界分开讨论。不过为了代码一致,我用了统一的写法 (cur + nums[cur]) % n + n ) % n 获取到下一个索引。 | ||
|
||
### 代码 | ||
|
||
- 语言支持:Python3 | ||
|
||
Python3 Code: | ||
|
||
```python | ||
|
||
class Solution: | ||
def circularArrayLoop(self, nums: List[int]) -> bool: | ||
def can(cur, start, steps): | ||
if nums[cur] ^ nums[start] < 0: return False | ||
if cur == start and steps != 0: return steps > 1 | ||
if cur in visited: return False | ||
visited.add(cur) | ||
return can(((cur + nums[cur]) % n + n ) % n, start, steps + 1) | ||
n = len(nums) | ||
visited = None | ||
for i in range(n): | ||
visited = set() | ||
if can(i, i, 0): return True | ||
return False | ||
|
||
``` | ||
|
||
**复杂度分析** | ||
|
||
令 n 为数组长度。 | ||
|
||
- 时间复杂度:$O(n^2)$ | ||
- 空间复杂度:$O(n)$ | ||
|
||
## 解法二 - 空间优化 | ||
|
||
### 思路 | ||
|
||
和解法一类似。不过由于**如果 steps 大于 n 则一定不存在解,可直接返回 False**,因此我们可以根据 steps 判断是否无解,而不必使用 visited 数组。这样做可以减少空间,不过时间复杂度上常数项上要比解法一差,因此不推荐,仅作为参考。 | ||
|
||
### 代码 | ||
|
||
- 语言支持:Python3 | ||
|
||
Python3 Code: | ||
|
||
```python | ||
|
||
class Solution: | ||
def circularArrayLoop(self, nums: List[int]) -> bool: | ||
def can(cur, start, steps): | ||
if nums[cur] ^ nums[start] < 0: return False | ||
if cur == start and steps != 0: return steps > 1 | ||
if steps > n: return False | ||
return can(((cur + nums[cur]) % n + n ) % n, start, steps + 1) | ||
n = len(nums) | ||
for i in range(n): | ||
if can(i, i, 0): return True | ||
return False | ||
|
||
|
||
``` | ||
|
||
**复杂度分析** | ||
|
||
令 n 为数组长度。 | ||
|
||
- 时间复杂度:$O(n^2)$ | ||
- 空间复杂度:$O(1)$ | ||
|
||
## 解法三 - 原地标记 | ||
|
||
### 思路 | ||
|
||
我们可以对前面两种解法进行优化。 | ||
|
||
我们可以使用哈希表 visited 记录访问情况,其中 key 为索引,value 为起始点。如果当前节点在 visited 中,就只有两种情况: | ||
|
||
- visited[cur] == start 为真,也就是说 cur 是在当前轮被标记访问的,直接返回 true | ||
- 否则不是在当前轮标记的,那么一定是之前标记的,那直接返回 false 就好了。因此之前轮已经检查过了**经过 cur 不存在环** | ||
|
||
我们使用了 visited 后每个点最多被处理一次,因此可以将时间复杂度降低到 $O(n)$。 | ||
|
||
进一步,我们可以使用原地标记的算法而不开辟 visited ,从而将空间复杂度降低到 $O(1)$。实现也很简单,只需要将数组值映射到题目值域外的数即可。 以这道题来说,加 5000 就可以映射到题目数据范围外。 | ||
|
||
### 代码 | ||
|
||
- 语言支持:Python3 | ||
|
||
Python3 Code: | ||
|
||
```python | ||
|
||
class Solution: | ||
def circularArrayLoop(self, nums: List[int]) -> bool: | ||
def can(cur, start, start_v): | ||
if nums[cur] >= 5000: return nums[cur] - 5000 == start | ||
if nums[cur] ^ start_v < 0: return False | ||
nxt = ((cur + nums[cur]) % n + n ) % n | ||
if nxt == cur: return False | ||
nums[cur] = start + 5000 | ||
return can(nxt, start, start_v) | ||
n = len(nums) | ||
for i in range(n): | ||
if can(i, i, nums[i]): return True | ||
return False | ||
|
||
``` | ||
|
||
**复杂度分析** | ||
|
||
令 n 为数组长度。 | ||
|
||
- 时间复杂度:$O(n)$ | ||
- 空间复杂度:不考虑递归产生的调用栈开销的话是 $O(1)$ | ||
|
||
> 读者可以轻易地将上面的代码改为迭代,感兴趣的读者不妨试试看。 | ||
## 关键点 | ||
|
||
- 使用哈希表 visited 记录访问情况,其中 key 为索引,value 为起始点。这样可以将时间复杂度降低到 $O(n)$ | ||
|
||
> 此题解由 [力扣刷题插件](https://leetcode-pp.github.io/leetcode-cheat/?tab=solution-template) 自动生成。 | ||
力扣的小伙伴可以[关注我](https://leetcode-cn.com/u/fe-lucifer/),这样就会第一时间收到我的动态啦~ | ||
|
||
以上就是本文的全部内容了。大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 40K star 啦。大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 | ||
|
||
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。 | ||
|
||
![](https://tva1.sinaimg.cn/large/007S8ZIlly1gfcuzagjalj30p00dwabs.jpg) |