前言

本博文部分图片, 思路来自于剑指offer 或者编程珠玑

问题描述

05 旋转数组中的最小数字_java

05 旋转数组中的最小数字_java_02

思路

书中给出了两种思路
思路一 : 遍历一次列表找出最小的元素

思路二 : 如果 head < tail 则说明arr本身就是一个有序的数列
    否则如果 head==tail==(head+tail/2) 这个是不能用下面的方式判定, 所以采用顺序比较,
    否则 计算head, tail的中间索引
        如果 idx大于head元素 即说明其在第一个递增子序列中, 这时最小的元素在idx后面, 更新head为idx,
        否则如果 idx小于tail元素 即说明其在第二个递增子序列中, 这时最小的元素在idx前面, 更新tail为idx
        最终head会指向第一个递增子序列的最大元素, tail会执行第二个递增子序列的最小元素, 并且二者相邻[循环判断条件]

05 旋转数组中的最小数字_java_03

参考代码

/**
 * file name : Test26FindMinInSubOrderedSeq.java
 * created at : 9:28:22 AM Jun 5, 2015
 * created by 
 */

package com.hx.test04;

public class Test26FindMinInSubOrderedSeq {

    // 找出半排序的数组中最小的元素   [序列由一个有序序列左移动得到]
    public static void main(String []args) {

        int[] arr = {18, 19, 20, 22, 27, 28, 32, 34, 37, 38, 38, 48, 48, 4, 6, 11, 17 };

        int minIdx = findMinInSubOrderedSeq01(arr);
        Log.log(minIdx);

        minIdx = findMinInSubOrderedSeq02(arr);
        Log.log(minIdx);


//      String str = "4 6 11 17 18 19 20 22 27 28 32 34 37 38 38 48 48";
//      Log.log(str.replaceAll(" ", ", ") );

    }

    // 思路 : 遍历arr数组找到最小元素的索引
    public static int findMinInSubOrderedSeq01(int[] arr) {
        int min = Integer.MAX_VALUE, idx = -1;
        for(int i=0; i<arr.length; i++) {
            if(arr[i] < min) {
                min = arr[i];
                idx = i;
            }
        }

        return idx;
    }

    // 思路 : 令head, tail分别为arr的第一个元素和最后一个元素索引
    // 如果head < tail  则说明arr本身就是一个有序的数列
    // 否则如果 head==tail==(head+tail/2)   这个是不能用下面的方式判定, 所以采用顺序比较   
        // 例如 : {0, 1, 1, 1, 1 } -> {1, 0, 1, 1, 1 }/ {1, 1, 1, 0, 1 }   这两种变形都是无法判断的, head, tail指向的均是1
    // 否则  计算head, tail的中间索引
        // 如果idx大于head元素  即说明其在第一个递增子序列中, 这时最小的元素在idx后面, 更新head为idx
        // 如果idx小于tail元素  即说明其在第二个递增子序列中, 这时最小的元素在idx前面, 更新tail为idx
        // 最终head会指向第一个递增子序列的最大元素, tail会执行第二个递增子序列的最小元素, 并且二者相邻[循环判断条件]
    public static int findMinInSubOrderedSeq02(int[] arr) {
        int head = 0, tail = arr.length - 1;
        if(arr[head] < arr[tail]) {
            return head;
        } else if(arr[head] == arr[tail] && arr[head] == arr[((head + tail) >> 1 )]) {
            return findMinInSubOrderedSeq01(arr);
        }

        // arr[head] > arr[tail]
        int idx = -1;
        while (head != (tail - 1) ) {
            idx = (head + tail) >> 1;
            if(arr[idx] > arr[head]) {
                head = idx;
            } else {
                tail = idx;
            }
        }

        return tail;
    }

}

效果截图

05 旋转数组中的最小数字_java_04

总结

对于 思路二, 看着是否是很像二分查找呢?, 利用了有序旋转数组的性质, 得出了上面精巧的算法

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!