旋转数组的最小数字

139 阅读1分钟

---

主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy, hydrogen, condensed-night-purple, greenwillow, v-green, vue-pro, healer-readable, mk-cute, jzman, geek-black

贡献主题:github.com/xitu/juejin…

theme: juejin highlight:

题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。

示例 1:

输入:[3,4,5,1,2] 输出:1 示例 2:

输入:[2,2,2,0,1] 输出:0

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/xu…

思路

  • 暴力破解:从头遍历一遍数组,比较当前元素 curr 和下一个元素next的大小,当 curr > next 时 next 元素就是数组最小值。

    • 边界条件:原数组所有元素相同,这时遍历到最后一个元素也找不到比 curr 小的next元素,但此时数组任意一个元素都可以作为最小值。
  • 二分查找:原数组是递增排序的数组,旋转后的数组可以看做两个递增排序的子数组。而要求解的数组最小值就是两子数组的交界点。定义 left = 0, right = len - 1, mid = left + (right-left) / 2;

    • 当 mid = (left + (right-left)/2 ) > right 时表示 mid 在第一段数组,此时mid左侧元素整体升序,交界点在 mid 右侧。所以 left = mid + 1。

      4 5 6 1 2
      mid = 6 right = 2
      
    • 当 mid = (left + (right-left)/2) < right 时表示 mid 在第二段数组,此时mid右侧元素整体升序,交界点在 mid 左侧或 mid 就是交界点。right = mid

      6 1 2 4 5
      mid = 2 right = 5
      
    • 当 mid = right 时不能判定交界点是在 mid 左侧还是右侧,暴力移动右指针 right--。

      3 1 3 3 3
      mid = 3 left = right = 3
      3 3 3 1 3
      mid = 3 left = right = 3
      

代码实现

public int minArray(int[] numbers) {
        if (numbers.length == 0) return 0;
        int len = numbers.length;
        int left = 0, right = len-1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (numbers[mid] > numbers[right]) {
                left = mid + 1;
            } else if (numbers[mid] < numbers[right]) {
                right = mid;
            } else {
                right--;
            }
        }
        return numbers[left];
    }