leetcode-33 Search in Rotated Sorted Array

问题描述:

Supposea sorted array 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 arrayreturn its index, otherwise return -1.

You may assume no duplicate exists in the array.


问题分析:

问题表示有一个原来有序,后来在某一位置反转的数组,查找某个值;

该题很容易想到《剑指offer》中的求旋转数组的最小值的问题,这里要更简单一点,因为不用考虑重复值问题;

原题可以转化为先求取旋转数组的旋转位置,然后将旋转后的数组想象成一个循环右移一定位数的有序数组,进行二分查找即可;


代码:

public class Solution {
   // 其实可以看做两个问题的组合,先找到反转位置,再进行二分查找
   public intsearch(int[] nums, int target) {
        if (nums.length == 0)
            return -1;
        return binarySearch(nums, 0, nums.length - 1, target,getRotateIndex(nums, 0, nums.length - 1));
   }
   
   // 找到反转的位置,即整个反转数组中的最小值
   private int getRotateIndex(int[] nums, int start, int end) {
        int mid = start;
        while (start < end) {
            mid = start + (end - start) / 2;
            // 由于每个值都不同,中间值大于末尾值,则表示中间值位于前面的递增序列,则最小值一定在右半段中
            if (nums[mid] > nums[end])
               start = mid + 1;
            else // 注意如果[mid]值小于[end],可能[mid]就是最小值,故end = mid;而不是mid - 1
                end = mid;
        }
        return start;
   }
   
   // 二分查找
   private intbinarySearch(int[] nums, int start, int end, int target, int index) {
        int mid, read_mid;
        while (start <= end) {
            mid = start + (end -start) / 2;
            // 这里涉及到一个技巧,将反转的数组想象成一个循环数组,则相当于把0-n位循环左移了index位(0移动到了index);
            read_mid = (mid + index) % nums.length;
            if (target == nums[read_mid])
                return read_mid;
            if (nums[read_mid] < target)
                start = mid + 1;
            else
                end = mid - 1;
        }
        return -1;
   }
}


运行结果:

leetcode-33 Search in Rotated Sorted Array_第1张图片 

 

拓展:

剑指Offer》 旋转数组的最小数字:

题目描述:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}{1,2,3,4,5}的一个旋转,该数组的最小值为1

 

解题思路:

遍历的时间复杂度为O(n),性能较差;由于旋转数组是由两个递增数组(当旋转元素个数为0时,只有一个递增数组,要特殊处理此特殊情况)组成。

采用二分查找(双指针法)可以有效地优化时间复杂度;

情况一:

对于{3,4,5,1,2},采用双指针法,中间元素[mid]的值如果在前面的递增序列,则[mid]一定不小于[start];故可以缩小查找的范围:将start挪至mid处,因为最小值即为两个递增数组的分界点;

情况二:

对于{4,5,1,2,3},[mid]处于后面的递增序列,则[mid]一定不大于[end];此时可以缩小查找范围:将end挪至mid处

情况三:

此为特殊情况需要考虑:{2,2,2,2,1,2},此时[mid],[start],[end]值相同,无法判断是向左还是向右缩小窗口;故只能采取遍历的方法找到最小值;

情况四:

特殊情况:即0个元素反转时,此时应当满足[start]<[end](等于的情况可以认为已经做了反转),直接输出首元素即可;

 

代码

public class Solution {
    public int minNumberInRotateArray(int [] array){       
        if (array.length == 0)
            return 0;
        int result = 0;
       
        int start = 0, end = array.length - 1;
        int mid = start;
        while (array[start] >= array[end]) {
           // 处理start = 2,end = 3,这种特殊情况

            if (end - start <= 1) {
                mid= end;
                break;
            }
           
            mid= start + (end - start) / 2;
            // 如果出现头指针和尾指针,以及中间指针三个数字相同的情况,则需要顺序遍历
            if ((array[start] ==array[mid])&&(array[end] == array[mid])){
                result= array[start];
                for (int i = start + 1; i <= end; i++) {
                    if (array[i] < result) {
                        result= array[i];
                    }  
                }
                return result;
            }
           
            // 中间元素在左边递增序列中的情况
            if (array[mid] >= array[start])
                start= mid;
            if (array[mid] <= array[end])
                end= mid;           
        }
        return array[mid];
    }
}
 


你可能感兴趣的:(leetcode-33 Search in Rotated Sorted Array)