LeetCode -- Search in Rotated Sorted Array 搜索旋转的排序数组

96 阅读3分钟

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm's runtime complexity must be in the order of O(log n).

假设某个递增排序的数组在某个点进行了旋转,比如[0,1,2,4,5,6,7] 可能变成了[4,5,6,7,0,1,2]

给你一个目标值进行查找。如果没找到,返回-1.假设没有重复的值。 你的算法复杂度应该为O(log n).

解题思路:在没有旋转的时候,排序数组查找应该使用二分法。现在被旋转了一次,要求还是O(log n),那么就应该还是二分法,只不过每次查找时多增加了一些判断方法而已。代码和注解如下所示:

public int search(int[] nums, int target) {
        int n = nums.length;
        if(n == 0){
            return -1;
        }
        int low = 0;
        int high = n-1;
        int first_value = nums[0];
        // 判断目标值,是否大于数组的第一个值
        boolean target_big = target >= first_value;
        while(low <= high){
            int middle = (low + high)/2;
            int middle_value = nums[middle];
            if(target == middle_value){
                return middle;
            }
            // 判断此次中间值,是否大于数组的第一个值
            boolean middle_big = middle_value >= first_value;
            
            if(middle_big == target_big){
                if(middle_value < target){
                    low = middle + 1; 
                }else{
                    high = middle -1;
                }
            }else{
                if(middle_big){
                    low = middle + 1;
                }else{
                    high = middle -1;
                }
            }
        }
        return -1;
    }

可以分成四种情况讨论: 初始情况 max_order + min_order: 也就是大的递增序列 + 小的递增序列

    1. 目标值 target_big = true,表示目标值在左边较大的递增序列中。本次循环中间值 middle_big = true,表示中间值大于数组第一个值,这表示此次循环中,左边还是单调递增序列。
    //表示了左边还是单调递增序列
    if(middle_big && target_big){
        if(middle_value < target){
            //如果中间值小于目标值,则目标值如果存在的话,则在右半边中
            low = middle + 1;
        }else{
            // 中间值大于目标值,则在左半边中
            high = middle -1;
        }
    }
    1. 目标值 target_big = false,表示目标值如果存在的话,在初始数组的右半边较小的递增序列中。middle_big = false,表示此次循环中间值小于数组的第一个值,所以 左边-中间 和 中间-右边 还是初始队列一样的排序。
    // 左半边 和 右半边都是两个 递增--递增 序列
    if( !middle_big && !target_big){
        if(middle_value < target){
            //中间值小于目标值
            low = middle +1;
        }else{
            high = middle - 1;
        }
    }

总结这两种情况,就是当middle_big == target_big 时,如果中间值小于目标值,就再次在右半边查找,否则在左半边查找。

    if(middle_big && target_big){
        if(middle_value < target){
            low = middle + 1; 
        }else{
            high = middle -1;
        }
    }
    1. 当 middle_big = true, target_big = false时,表示此次循环左边是单调递增序列,但是目标值在右边较小的递增序列min_order中,所以继续在右边查找:
    if(middle_big){
        low = middle + 1;
    }
    1. 当middle_big = false, target_big = true时,表示此次循环右边是单调递增序列,但是因为目标值在左边较大的初始递增序列中,所以继续在左边查找:
    high = middle - 1;

所以最后代码如第一个代码框所示。