【算法】旋转数组的最小数字

35 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

题目

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

解题思路

全局遍历法

最简单解题方法是从头到尾遍历数组找到最小元素,该方法最为简单粗暴。

int max = numbers[0];
for(int i = 1; i < numbers.length; i++) {
    if(numbers[i] > max){
      max = numbers[i];
    }        
}
return max;

子数组分割法

虽然遍历全部的方法简单粗暴但没有利用到题目所说旋转数组题意。旋转数组实际上可以划分为两个排序子数组,而前面子数组元素都大于或等于后面子数组元素。因此可以根据该规则特点发现最小元素就是两个子数组的分界线,只需要在数组中找到比前一个元素小的元素即可。

for(int i = 1; i < numbers.length; i++) {
    if(numbers[i] < numbers[i - 1]) {   // 可以使用已知条件获取最小值
        return numbers[i];
    }
}
return numbers[0];

二分法

同样根据最小元素是两个子数组分界线,在排序数组中使用二分法思路来找到最小元素。利用双指针分别指向数组第一个元素和最后一个元素。

  1. 可知第一个元素是大于或者等于最后一个元素的。
  2. 采用二分法找到双指针中间元素是否大于右边的值。
  3. 大于右边值则缩小左边范围,左指针指向中间值下标。
  4. 小于右边值则缩小右边范围,右指针指向中间值下标。
  5. 直到双指针越界。
 int left = 0;
 int right = nums.length - 1;
 while (left < right) { // 指针左小于指针 则继续找最小值
     int mid = left + (right - left) / 2; // 双指针中间值
     if (nums[mid] > nums[right]) {  // 中间值是否大于最右的值
         left = mid + 1; //大于则缩小左边范围
     } else {
         right = mid; // 小于则缩小右边范围
     }
 }
 return nums[left];