算法-秒杀旋转数组问题


搜索旋转数组是一个非常经典的题目了,在Leetcode和剑指Offer上店铺有相应的问题,可以总结为:一个经过排序的数组,在某点发生了旋转,寻找里面是不是有对应的值
例如,原数组为12345,而经过旋转后的数组为34512,我们搜索目标值为4.一种简单暴力的做法就是遍历整个字符串,时间复杂度为O(N),看上去已经很完美了,然而,由于数组是部分有序的,我们还可以使用二分法搜索数组,使时间复杂度达到 log(N)。

由于二分法只能在有序数组中使用,因此,我们首先需要做的就是找到旋转点。

1、找到旋转点
2、将数组划分为两部分,分别用二分法搜索

其中寻找到旋转点的操作也必须是log(N)。

怎么判断他是不是旋转点呢? 其实也不难理解,旋转点的前面的值大于后面的值(除了初始位置外)

1、声明一个low和一个high指针,指向数组两侧。
2、计算中点位置,如果中点位置的元素大于high位置元素,说明旋转点在右侧。
3、如果小于high位置的元素,说明旋转点在左侧
4、如果等于high位置的元素,我们是无法确定旋转点在哪里的,这时候让high向左移动一步。这时候其实是有可能退化成O(N)的,例如111121这种情况。

1、搜索旋转数组II

知道了上述说明的点,看一个经典的题目:81. 搜索旋转排序数组 II

问题描述:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

经过上面的描述,答案已经呼之欲出了

    public boolean search(int[] nums, int target) {
        if(nums.length==0){
            return false;
        }
        int low=0,high=nums.length-1;
        //寻找旋转点
        while(low<high){
            int mid=(low+high)>>1;
            if(nums[mid]<nums[high]){
                high=mid;
            }else if(nums[mid]>nums[high]){
                low=mid+1;
            }else{
                high--;
            }
        }
        return findTarget(nums,0,low-1,target)||findTarget(nums,low,nums.length-1,target);

    }

    public boolean findTarget(int nums[],int low,int high,int target){
        if(low<0||high>nums.length-1){
            return false;
        }
        while(low<=high){
            int mid=(low+high)>>1;
            if(nums[mid]>target){
                high=mid-1;
            }else if(nums[mid]<target){
                low=mid+1;
            }else {
                return true;
            }
        }
        return false;
    }

2、搜索旋转数组I

33.搜索旋转数组I更简单,因为没有重复元素的情况,不会退化成O(N)

题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -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
    public int search(int[] nums, int target) {
        if(nums.length==0){
            return -1;
        }
        int low=0,high=nums.length-1;
        //寻找旋转点
        while(low<high){
            int mid=(low+high)>>1;
            if(nums[mid]<nums[high]){
                high=mid;
            }else if(nums[mid]>nums[high]){
                low=mid+1;
            }else{//这里其实不需要判断了,因为在本题中没有重复元素
                high--;
            }
        }
        if(nums[nums.length-1]<target){
            return findTarget(nums,0,low-1,target);
        }else{
            return findTarget(nums,low,nums.length-1,target);
        }
    }

    public int findTarget(int nums[],int low,int high,int target){
        if(low<0||high>nums.length-1){
            return -1;
        }
        while(low<=high){
            int mid=(low+high)>>1;
            if(nums[mid]>target){
                high=mid-1;
            }else if(nums[mid]<target){
                low=mid+1;
            }else{
                return mid;
            }
        }
        return -1;
    }

3、旋转数组的最小数字

搜索旋转数组的最小数字出自剑指Offer,其实本质上就是个求旋转点的问题,属于上面两个题的第一步。

    public int minNumberInRotateArray(int [] array) {
        if(array.length==0){
            return 0;
        }
        int low=0,high=array.length-1;
        while(low<high){
            int mid=(low+high)>>1;
            if(array[mid]>array[high]){//在最小值在右边
                low=mid+1;
            }else if(array[mid]<array[high]){//旋转点在左边
                high=mid;
            }else {
                high--;
            }
        }
        return array[low];
    }
发布了28 篇原创文章 · 获赞 9 · 访问量 2414

猜你喜欢

转载自blog.csdn.net/qq_23594799/article/details/105094850