zoukankan      html  css  js  c++  java
  • 剑指06.旋转数组的最小数字

    题目描述

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
    输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
    例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
    NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

    案例

    {3 4 5 1 2} (一般情况)
    
    {1 2 3 4 5} (特例,前面0个元素搬到后面,即已经排好序的情况)
    
    {1 0 1 1 1} / {1 1 1 0 1}(特例,都可以看成递增排序数组{0 1 1 1 1 }的旋转)

    分析

        遍历整个数组找出其中最小的数,复杂度为O(n),这是最容易想到的方案,但这样没用到旋转数组的特性,肯定不行!!
        本题考查二分查找。旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的元素都大于后面子数组中的元素,而且最小的元素刚好是这两个子数组的分界线。
    1. 和二分查找一样,用两个指针分别指向数组的第一个元素和最后一个元素。
    2. 找到数组的中间元素,如果中间元素大于第一个指针指向的元素,则中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面,我们让第一个指针left指向该中间元素;如果中间元素小于第二个指针指向的元素,则中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面,我们让第二个指针right指向中间元素。
    3. 这样查找范围会缩小到原来的一半,第一个指针总是指向前面递增数组的元素,第二个指针总是指向后面的递增数组的元素。最终,第一个指针将指向前面子数组的最后一个元素,而第二个指针会指向后面子数组的第一个元素(也就是最小的元素),即它们最终指向两个相邻的元素,循环结束。

    解法1

    import java.util.ArrayList;
    public class Solution {
        public int minNumberInRotateArray(int [] array) {
            if (array.length == 0)
                return 0;
            int indexLeft = 0;
            int indexRight = array.length - 1;
            int indexMid = indexLeft; //如果旋转数组是排序数组本身,直接返回第一个数字
            while (array[indexLeft] >= array[indexRight]){
                if (indexRight - indexLeft == 1){
                    indexMid = indexRight;
                    break;
                }
                indexMid = indexLeft + (indexRight - indexLeft) / 2;
                //对于特例[1,0,1,1,1]和[1,1,1,0,1]
                //当两个指针指向的数字及它们中间的数字三者相同的时候;
                //无法判断中间的数字是位于前面的子数组还是后面的子数组,只能采用顺序查找的方法
                if (array[indexMid] == array[indexLeft] && array[indexMid] == array[indexRight]){
                    return minInOrder(array, indexLeft, indexRight);
                }
                if (array[indexMid] >= array[indexLeft]){
                    indexLeft = indexMid;
                }
                if (array[indexMid] <= array[indexRight]){
                    indexRight = indexMid;
                }
            }
            return array[indexMid];
        }
        public int minInOrder(int[] array, int indexLeft, int indexRight){
            int result = array[indexLeft];
            for (int i = indexLeft + 1; i <= indexRight; i++){
                if (array[i] < result)
                    result = array[i];
            }
            return result;
        }
    }

     解法2

    import java.util.ArrayList;
    public class Solution {
        public int minNumberInRotateArray(int [] array) {
            if (array.length == 0)
                return 0;
            int indexLeft = 0;
            int indexRight = array.length - 1;
            while (indexLeft < indexRight){
                int indexMid = indexLeft + (indexRight - indexLeft) / 2;
                if (array[indexMid] > array[indexRight]){ //最小数字一定在indexMid的右边
                    indexLeft = indexMid + 1;
                }else if (array[indexMid] < array[indexRight]){//最小数字一定就是array[indexMid]或者左边,因为右边是递增的。
                    indexRight = indexMid;   //如果是indexMid-1,当只剩两个数时,就会出错。
                }else{ //类似于[1,0,1,1,1],此时不能判断是在左边还是右边,只能一个个试
                    indexRight = indexRight - 1;
                }
            }
            return array[indexLeft];
        }
    }
     
    Note:
    关于取中间值为什么是left+(right-left)/2,而不是直接(right+left)/2?
    答:第一种方式更稳定!!如果两个数都很大,相加将导致溢出。
    int x = 1999999998;
    int y = 1999999998;
    int mid = (x+y) / 2;
    int mid2 = x + (y-x) / 2;
    System.out.println(mid); //-147483650
    System.out.println(mid2); //1999999998
  • 相关阅读:
    mysql 基础整合大全
    django 完整日志配置
    rest_framework视图基类与五个扩展类
    Nginx + uWSGI 配置django---终极版
    小程序 textarea ios兼容解决
    小程序调取数字键盘,没有小数点解决办法
    消息框-提示框
    h5页面长按保存图片
    解决浏览器自动填充input
    angularJS(2)
  • 原文地址:https://www.cnblogs.com/HuangYJ/p/13438658.html
Copyright © 2011-2022 走看看