zoukankan      html  css  js  c++  java
  • leetcode中的旋转排序数组问题:

    关于旋转排序数组leetcode中共有4道题目,思路都是基于二分查找。

    什么是旋转排序数组?

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

    具体问题

    找最小值和普通搜索两种

    找最小值问题

    1.假设数组中不存在相同元素(153题 中等)
    示例:
    输入: [3,4,5,1,2]
    输出: 1
    
    思路:

    每次将mid和hi位置的数比较即可判断最小值所在的区间

    代码:
     public int findMin(int[] nums) {
            int l=0,h=nums.length-1;
            while (l<h){
                int mid=l+(h-l)/2;
                if (nums[mid]>nums[h]){
                    l=mid+1;
                }else {
                    h=mid;
                }
            }
            return nums[l];
        }
    
    2.假设数组中存在相同元素(154题 困难)
    示例:
    输入: [1,1,1,0,1]
    输出: 0
    
    思路:

    如果数组元素允许重复的话,那么就会出现一个特殊的情况:nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,需要切换到顺序查找。例如对于数组 {1,1,1,0,1},l、m 和 h 指向的数都为 1,此时无法知道最小数字 0 在哪个区间。

    代码:
    public int findMin(int[] nums) {
            int l=0,h=nums.length-1;
            while (l<h){
                int mid=l+(h-l)/2;
                if (nums[l]==nums[mid]&&nums[mid]==nums[h]){
                    return find(nums,l,h);//切换到顺序查找
                }
                else if (nums[mid]<=nums[h]){
                    h=mid;
                }else {
                    l=mid+1;
                }
            }
            return nums[l];
        }
        public int find(int[] nums,int l,int h){
            for (int i = l; i <h ; i++) {
                if (nums[i]>nums[i+1]){
                    return nums[i+1];
                }
            }
            return nums[l];
        }
    

    搜索问题

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1

    1.假设数组中不存在相同元素(33题 中等)
    实例:
    输入: nums = [4,5,6,7,0,1,2], target = 0
    输出: 4
    
    思路:

    可以基于153题,第一次二分查找找到最小值的位置(翻转点的位置)。有了最小值的索引便有个一个映射关系,mid和realmid。第二遍的二分查找就是一个常规的二分查找,但要考虑映射关系,即每次拿realmid的值比较而不是mid位置的值。

    代码:
    public int search(int[] nums, int target) {
            int l=0,h=nums.length-1;
            //用二分查找找到最小值的索引
            while (l<h){
                int mid=l+(h-l)/2;
                if (nums[mid]<=nums[h]){
                    h=mid;
                }else {
                    l=mid+1;
                }
            }//l=h 为最小值的索引 也就是翻转的位置
    
            int rot=l;
            l=0;
            h=nums.length-1;
            //第二遍二分查找 就是正常的二分查找但要考虑翻转点
            while (l<=h){
                int mid=l+(h-l)/2;
                int realmid=(mid+rot)%nums.length; //通过映射关系找到真实的中间点
                if (nums[realmid]==target){
                    return realmid;
                }else if (nums[realmid]>target){
                    h=mid-1;
                }else {
                    l=mid+1;
                }
            }
            return -1;
        }
    
    2.假设数组中存在相同元素(81题 中等)
    示例:
    Input: nums = [2,5,6,0,0,1,2], target = 0
    Output: true
    
    思路:

    这题相当于在154题的基础上,
    第一步,用154题的函数找到存在重复元素数组中最小值(翻转点)的位置;
    第二步,参考1的做法,通过映射关系完成一次常规二分查找。

    代码:
     public boolean search(int[] nums, int target) {
            
            //调用函数找到最小值的索引
            int rot=findMinIndex(nums);
    
            int l=0,h=nums.length-1;
            
            //二分查找 就是正常的二分查找但要考虑翻转点
            while (l<=h){
                int mid=l+(h-l)/2;
                int realmid=(mid+rot)%nums.length; //通过映射关系找到真实的中间点
                if (nums[realmid]==target){
                    return true;
                }else if (nums[realmid]>target){
                    h=mid-1;
                }else {
                    l=mid+1;
                }
            }
            return false;
        }
        public int find(int[] nums,int l,int h){ //顺序查找
            for (int i = l; i < h; i++) {
                if (nums[i]>nums[i+1]){
                    return i+1;
                }
            }
            return l;
    
        }
        public int findMinIndex(int[] nums) {
            int l=0,h=nums.length-1;
            while (l<h){
                int mid=l+(h-l)/2;
                if (nums[l]==nums[mid]&&nums[mid]==nums[h]){
                    return find(nums,l,h);//切换到顺序查找
                }
                else if (nums[mid]<=nums[h]){
                    h=mid;
                }else {
                    l=mid+1;
                }
            }
            return l;
        }
    

    总结:

    旋转排序数组中的问题无非就是查找问题,而排序数组中的查找问题采用二分查找效率最高,所以要尽量利用其中的顺序关系转化为二分查找来解决。

    对于找最小值(旋转点)问题,又可以分为无重复元素和有重复元素;对于无重复元素找最小值问题,每次将mid位置元素和h位置元素比较;对于有重复,注意特例类似[1,1,1,0,1,1],出现特例时就要局部利用顺序查找来继续查找了,所以复杂度会由O(logN)退化到O(N)。

    对于搜索问题,第一步都是解决找旋转点的问题,根据有无重复元素采用不同函数。第二步,在找到旋转点后,便有了“mid”和“realmid”的映射关系,就可以转化为普通二分查找搜索问题。

  • 相关阅读:
    8天学通MongoDB——第五天 主从复制
    5天不再惧怕多线程——第五天 线程池
    8天玩转并行开发——第四天 同步机制(上)
    8天学通MongoDB——第八天 驱动实践
    8天玩转并行开发——第三天 plinq的使用
    8天玩转并行开发——第一天 Parallel的使用
    8天玩转并行开发——第五天 同步机制(下)
    5天不再惧怕多线程——第一天 尝试Thread
    虚函数、纯虚函数详解
    libevent 笔记
  • 原文地址:https://www.cnblogs.com/10zhang/p/9904992.html
Copyright © 2011-2022 走看看