剑指offer_旋转数组的最小数字

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

首先暴力法,没有丝毫意义

1 import java.util.Arrays;
2 public class Solution {
3     public int minNumberInRotateArray(int [] array) {
4     Arrays.sort(array);
5     return array[0];
6     }
7 }

 解题思路:

将旋转数据对半分可以得到一个包含最小元素的新旋转数组,以及一个非递减排序的数组。新的旋转数组的数组元素是原数组的一半,从而将问题的规模减少了一半,这种折半性质的算法的时间复杂度为O(logN) 。

此时问题的关键在于确定对半分的两个数组哪个是旋转数组,哪个是非递减数组。我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素。

通过修改二分查找算法进行求解(l代表low,m代表mid,h代表high):

当 nums[m] <= nums[h] 时,表示 [m, h] 区间内的数组是非递减数组,[l, m] 区间内的数组是旋转数组,此时 令 h = m;

否则 [m + 1, h] 区间内的数组是旋转数组,令 l = m + 1。

 1 public int minNumberInRotateArray(int[] nums) {
 2 if (nums.length == 0)
 3 return 0;
 4 int l = 0, h = nums.length - 1;
 5 while (l < h) {
 6 int m = l + (h - l) / 2;
 7 if (nums[m] <= nums[h])
 8 h = m;
 9 else
10 l = m + 1;
11 }
12 return nums[l];
13 }

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

 1 public int minNumberInRotateArray(int[] nums) {
 2 if (nums.length == 0)
 3 return 0;
 4 int l = 0, h = nums.length - 1;
 5 while (l < h) {
 6 int m = l + (h - l) / 2;
 7 if (nums[l] == nums[m] && nums[m] == nums[h])
 8 return minNumber(nums, l, h);
 9 else if (nums[m] <= nums[h])
10 h = m;
11 else
12 l = m + 1;
13 }
14 return nums[l];
15 }
16 private int minNumber(int[] nums, int l, int h) {
17 for (int i = l; i < h; i++)
18 if (nums[i] > nums[i + 1])
19 return nums[i + 1];
20 return nums[l];
21 }

 

posted @ 2019-08-13 10:56  chyblogs  阅读(103)  评论(0)    收藏  举报