面试高频算法题:搜索旋转排序数组

搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值 target,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。

示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

解答

为了方便讲解,这里我们把数组中的最小值称之为旋转点吧。接下来我们就以上面中的例子来进行讲解。

没有旋转之前的数组

旋转之后的数组

显然,这个旋转点是最特殊的点,因为旋转点既比左边的数小,同时也比右边的数小。

我们知道,在我们平时的二分查找中(也就是数组没有旋转之前),我们会把目标值 target 与 中间元素 mid 进行比较,每次比较都会有如下三种结果

(1)如果 target 比 mid 小,那么 mid 以及 mid 右边的所有元素一定比 target 大,所以 target 只能存在于 mid 的左边元素中。

(2)如果 target 比 mid 大,那么 mid 以及 mid 左边的所有元素一定比 target 小,所以 target 只能存在于 mid 的右边元素中。

(3)如果 target 与 mid 相等,则直接把 mid 对应的下标返回即可。

而在这道题中,情况要比这个复杂,因为它还有受旋转点的位置所影响,具体可以分为以下两种情况。

这里我在强调一下,二分查找的结果就是要把范围缩小一般,所以我们的目的就是,寻找出 target 究竟是位于 mid 的左边还是右边。

(一)情况1:中间元素在旋转点的左侧

(1)target 在 mid 的左边。

如果 target 在 mid 的左边,显然需要满足条件:最左边元素 >= target > mid

(2)taeget 在 mid 的右边

如果不满足(1)的条件,则意味着在右边

(二)情况2:中间元素在旋转点的右侧或者和旋转点重合

右侧

重合

(1)target 在 mid 的 右边

显然,需要满足的条件是 mid > target >= 最右侧元素

(1)target 在 mid 的左边

如果不满足 (1) 中的条件,则在左边。

当然,在上面的情况中,我们没有 mid 与 target 相等的情况,因为如果他们相等的话,就直接返回下边即可,无需讨论。

上面的各种情况我们都讨论好了,下面我们直接按照上面的逻辑来写代码即可,代码如下:

int rotatedBinarySearch(int[] arr, int target){
 // 最左侧元素下标
 int left = 0;
 // 最右侧元素下标
 int right = arr.length - 1;
 while(left >= right){
 // 中间元素下标
 int mid = left + (right - left) / 2;
 if(arr[mid] == target){
 return mid;
 }
 // 情况1:如果中间元素在旋转点左侧
 if(arr[mid] <= arr[left]){
 //target 如果位于中间元素的左侧
 if(arr[mid] < target && target <= arr[left]){
 right = mid - 1;
 }else{
 left = mid + 1;
 }
 }
 // 情况2:中间元素在旋转点的右侧
 else{
 // target 如果位于中间元素的右侧
 if(arr[mid] > target && target >= arr[right]){
 left = mid + 1;
 }else{
 right = mid - 1;
 }
 }
 }
 return -1;
}
THE END