本文是对旋转数组类型的题目的总结,一共6道,基本都是二分法的套路。
189. 旋转数组
给定一个数组,将数组中的元素向右移动 k
个位置,其中 k
是非负数。
进阶:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例 1:
1 | 输入: nums = [1,2,3,4,5,6,7], k = 3 |
示例 2:
1 | 输入:nums = [-1,-100,3,99], k = 2 |
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
解题思路:
方法一:使用额外的数组
可以使用额外的数组来将每个元素放至正确的位置。用 n
表示数组的长度,我们遍历原数组,将原数组下标为 i
的元素放至新数组下标为 (i+k)%n
的位置,最后将新数组拷贝至原数组即可。
1 | func rotate(nums []int, k int) { |
方法二:环状替换
从另一个角度,我们可以将被替换的元素保存在变量 temp
中,从而避免了额外数组的开销。我们从位置 0 开始,最初令 temp=nums[0]
。根据规则,位置 0 的元素会放至 (0+k)%n
的位置,令 x=(0+k)%n
,此时交换 temp
和 nums[x]
,完成位置 x 的更新。然后,我们考察位置 x,并交换 temp
和 nums[(x+k)%n]
,从而完成下一个位置的更新。不断进行上述过程,直至回到初始位置 0。
容易发现,当回到初始位置 0 时,有些数字可能还没有遍历到,此时我们应该从下一个数字开始重复的过程,可是这个时候怎么才算遍历结束呢?我们不妨先考虑这样一个问题:从 0 开始不断遍历,最终回到起点 0 的过程中,我们遍历了多少个元素?
1 | func rotate(nums []int, k int) { |
方法三:数组翻转
1 | nums = "----->-->"; k =3 |
1 | func rotate(nums []int, k int) { |
在旋转数组里寻找最小值
可以看作寻找最小值左边界问题(直接解决又重复元素的情况)。
153. 寻找旋转排序数组中的最小值
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
- 若旋转
4
次,则可以得到[4,5,6,7,0,1,2]
- 若旋转
7
次,则可以得到[0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
给你一个元素值 互不相同 的数组 nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
示例 1:
1 | 输入:nums = [3,4,5,1,2] |
示例 2:
1 | 输入:nums = [4,5,6,7,0,1,2] |
示例 3:
1 | 输入:nums = [11,13,15,17] |
提示:
- n == nums.length
- 1 <= n <= 5000
- -5000 <= nums[i] <= 5000
- nums 中的所有整数 互不相同
- nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
解题思路:
本题可以看作是找数组中的最小数。如果更改一下题目条件,数组中的元素是可以重复的,那么可以看作找最小数的左边界即可。
1 | func findMin(nums []int) int { |
154. 寻找旋转排序数组中的最小值 II
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,4,4,5,6,7] 在变化后可能得到:
- 若旋转
4
次,则可以得到[4,5,6,7,0,1,4]
- 若旋转
7
次,则可以得到[0,1,4,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
给你一个可能存在 重复 元素值的数组 nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
示例 1:
1 | 输入:nums = [1,3,5] |
示例 2:
1 | 输入:nums = [2,2,2,0,1] |
提示:
- n == nums.length
- 1 <= n <= 5000
- -5000 <= nums[i] <= 5000
- nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
解题思路:
同上题。
1 | func findMin(nums []int) int { |
在旋转数组里寻找指定的值
上面两道题是在旋转数组里寻找最小值,下面两道题是在旋转数组里寻找指定的值,这两道题的区别也是存不存在重复值。
33. 搜索旋转排序数组
整数数组 nums
按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums
在预先未知的某个下标 k(0 <= k < nums.length)
上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7]
在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2]
。
给你 旋转后 的数组 nums
和一个整数 target
,如果 nums
中存在这个目标值 target
,则返回它的下标,否则返回 -1
。
示例 1:
1 | 输入:nums = [4,5,6,7,0,1,2], target = 0 |
示例 2:
1 | 输入:nums = [4,5,6,7,0,1,2], target = 3 |
示例 3:
1 | 输入:nums = [1], target = 0 |
提示:
- 1 <= nums.length <= 5000
- -10^4 <= nums[i] <= 10^4
- nums 中的每个值都 独一无二
- 题目数据保证 nums 在预先未知的某个下标上进行了旋转
- -10^4 <= target <= 10^4
解题思路:
其实方法还是类似于上题,不过对于寻找特定的数,需要判断该数可能存在于什么区间内。
- 如果
[l, mid - 1]
是有序数组,且target
的大小满足[nums[l],nums[mid])
,则我们应该将搜索范围缩小至[l, mid - 1]
,否则在[mid + 1, r]
中寻找。 - 如果
[mid, r]
是有序数组,且target
的大小满足(nums[mid+1],nums[r]]
,则我们应该将搜索范围缩小至[mid + 1, r]
,否则在[l, mid - 1]
中寻找。
1 | func search(nums []int, target int) int { |
81. 搜索旋转排序数组 II
已知存在一个按非降序排列的整数数组 nums
,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length
)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7]
在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4]
。
给你 旋转后 的数组 nums
和一个整数 target
,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums
中存在这个目标值 target
,则返回 true
,否则返回 false
。
示例 1:
1 | 输入:nums = [2,5,6,0,0,1,2], target = 0 |
示例 2:
1 | 输入:nums = [2,5,6,0,0,1,2], target = 3 |
提示:
- 1 <= nums.length <= 5000
- -104 <= nums[i] <= 104
- 题目数据保证 nums 在预先未知的某个下标上进行了旋转
- -104 <= target <= 104
解题思路:
大体上同上题思路是一致的,只需要在遇到nums[mid]==nums[right]
时缩小查找范围即可。
1 | func search(nums []int, target int) bool { |
面试题 10.03. 搜索旋转数组
搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。
示例1:
1 | 输入: arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 5 |
示例2:
1 | 输入:arr = [15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14], target = 11 |
提示:
- arr 长度范围在[1, 1000000]之间
解题思路:
本题是上一题的一个小进阶,不仅要确认是否有target
值,还要输出其左边界,在上一题的基础上套用二分法查找左边界的方法即可。
1 | func search(arr []int, target int) int { |
- Post title:循转数组相关题目
- Post author:洪笳淏
- Create time:2021-09-12 18:30:00
- Post link:https://jiahaohong1997.github.io/2021/09/12/循转数组相关题目/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.