问题描述
整数数组 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:
输入: nums = [4, 5, 6, 7, 0, 1, 2], target = 0
输出: 4
示例 2:
输入: nums = [4, 5, 6, 7, 0, 1, 2], target = 3
输出: -1
示例 3:
输入: nums = [1], target = 0
输出: -1
约束条件:
- 1 <= nums.length <= 5000
- -10^4 <= nums[i] <= 10^4
- nums 中的每个值都独一无二
- 题目数据保证 nums 在预先未知的某个下标上进行了旋转
- -10^4 <= target <= 10^4
解题思路
对于有序数组总查找某个值,常用的方法就是二分法。但是在本题中,有序数组经过“旋转”后,变成了两个有序序列,不能仅通过较中间元素和目标值的大小去决定下次二分的范围。下面就研究下,在本题的条件下,如何确定二分法后应在中间元素的哪侧进行继续查找。
假设 nums 是要查询的数组,target 为查询的目标值,令指针low 指向第一个元素,指针 high 指向最后一个元素,指针 mid 指向数组的中间元素,即 mid = (low + high) / 2。
- 若 target == nums[mid],则找到,直接结束查找。
- 若 nums[low] <= nums[mid],则 mid 左侧为有序数组(=符号是要考虑,最后只剩下两个元素或数组只有两个元素的时候),旋转点应在 mid 右侧。此时,若目标值在 low 和 mid 之间,即 num[low] <= target < nums[mid],那么下次应在 mid 左侧查找,即查找范围变为:[low, mid -1]。否则,应在 mid 右侧查找,查找范围变为:[mid + 1, high]。
- 若 nums[low] > nums[mid],则旋转点在 mid 左侧,mid 右侧为有序数组,且 mid 右侧的值都比 nums[low] 小。此时,
- 若 target > nums[low] > nums[mid],由于 mid 右侧的数值都比 nums[low] 小,那下次查找范围应在 mid 左侧,即查找范围变为:[low, mid -1];
- 若 nums[low] > target > nums[mid],此时 target 应在 mid 右侧,即查找范围变为:[mid + 1, high];
- 若 nums[low] > nums[mid] > target,此时 target 应在旋转点和 mid 间,查找范围变为:[low, mid -1];
- 重复以上步骤,直到找到 target 或指针 low 和指针 high 相遇。
代码
1 | public int search(int[] nums, int target) { |