【基础算法题】旋转数组中的最小数字

旋转数组的最小数字_牛客题霸_牛客网 (nowcoder.com)

题目要求:

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

示例: 

输入:[3,4,5,1,2]

返回:1

分析:

这道题我们可以遍历数组挨个查找最小元素,也可以调用JDK中Arrays的sort方法将数组排序,输出第一个元素,但是这两个方法显然不够高效,在此,我们使用二分查找的方法来解题。

但是题意告诉我们这是非降序数组,也就意味着可能出现大量的重复元素,这是我们有两种方法:

  1. 第一种是比较左右两端和中间值这三者的关系,若三者相等,就放弃二分法,遍历数组
  2. 第二种是只比较中间值和最右端二者之间的大小关系,若出现相等的情况,只排除最右端一个,因为此时最右端的数肯定不是我们要找的数

这里第二种方法相对来说简单一点,但是第一种方法会比较高效,读者可以根据喜好选择。

代码实现:

1.方法一:

     public int minArray(int[] numbers) {
        if(numbers.length == 1){
            return numbers[0];
        }
      int left = 0;
      int right = numbers.length - 1;
      int mid = 0;
      while (numbers[left] >= numbers[right]){
          if(right - left == 1){
              //left 和 right 相邻,此时right就是指向最小值
              mid = right;
              break;
          }
          mid = left + ((right - left) >> 1);
          //如果有大量重复元素,只能使用循环遍历
          if(numbers[left] == numbers[mid] && numbers[right] == numbers[mid]){
              int min = numbers[left];
              for (int i = left+1; i < right; i++) {
                  if(numbers[i] < min){
                      min = numbers[i];
                  }
              }
              return min;
          }
          if(numbers[mid] >= numbers[left]){
              //此时mid处在未交换的位置,应该去mid后面找
              left = mid;
          }else {
              //numbers[mid] < numbers[right]
              //mid在交换的位置,要往mid前面找
              right = mid;
          }
      }
   return numbers[mid];
    }

 2.方法二:

    public int minArray(int[] numbers) {
        if (numbers.length == 0) {
            return 0;
        }
        int left = 0;
        int right = numbers.length - 1;
        int mid = 0;
        while (left < right) {
            mid = left + ((right - left) >> 1);
            if (numbers[mid] > numbers[right]) {
                //mid一定在原数组的部分,mid 以及 mid 的左边一定不是最小数字
                // 下一轮搜索区间是 [mid + 1, right]
                left = mid + 1;
            } else if (numbers[mid] < numbers[right]) {
                // mid一定在旋转过的部分, 它的右边一定不是最小数字,mid 有可能是
                // 下一轮搜索区间是 [left, mid]
                right = mid;
            } else {
                //此时numbers[mid] = numbers[right]
                // 只能把 right 排除掉,下一轮搜索区间是 [left, right - 1]
                right--;
            }
        }
        return numbers[left];
    }

这两种方法的限制语句和返回值都是不同的,这些细微的差距都可能时结果出错,在做题时,一定要多画图来理解。

你可能感兴趣的:(算法专栏,算法,java)