题目链接
https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
输入:[3,4,5,1,2] 输出:1 输入:[2,2,2,0,1] 输出:0
解题思路
方式一:暴力枚举
初始化变量ans为INT_MAX,for循环扫描一遍输入的数组并迭代更行ans的值(当数组中元素的值小于ans,则更新ans的值),时间复杂度O(n)
方式二:双指针
该数组有个特性,可以看成是由一个有序序列拆分成俩个部分的有序数列,所以比较连续两个元素的大小即可。
方式三:二分查找
普通的二分查找算法都是在有序数组中寻找是否出现每个值,在本题中,数组不在完全有序且题目并没有给我们一个需要寻找的值。
题目提供的数组部分有序,且我们只需要比较numbers[mid]、numbers[l]、numbers[r]这三个元素的大小即可,所以我们依旧可以使用二分查找算法,把时间复杂度从O(n)将为O(logn)
伪代码如下:
为什么更新r值的时候不需要+1,而l值需要呢?
题目要找的是最小值,所以当numbers[mid] < numbers[l]的时候,numbers[mid]很可能是我们所要寻找的答案,而当numbers[mid] > numbers[r],因为numbers[r]的值已经小于numbers[mid],所以numbers[mid]不可能是我们要找的答案,所以需要更新l的值为mid + 1。
如果出现numbers[mid] == numbers[l] == numbers[r] == value的时候该怎么办?例如numbers = [3,3,1,3]、numbers = [1, 1, 1, 2, 3, 1]
无论value值是否是我们所需要的最小值,我们都可以把它删除,因为一共有3个value值,删除最右边的一个value元素不会造成影响。
AC代码
暴力枚举
class Solution { public int minArray(int[] numbers) { // 暴力枚举 int ans = 123456789; for(int i = 0; i < numbers.length; i++) { if(numbers[i] < ans) ans = numbers[i]; } return ans; } }
双指针
class Solution { public int minArray(int[] numbers) { //双指针 int l = 0; int r = numbers.length - 1; while(l < r){ if(numbers[l] > numbers[l+1]) return numbers[l+1]; if(numbers[r] < numbers[r-1]) return numbers[r]; l++; r--; } return numbers[0]; } }
二分查找
class Solution { public int minArray(int[] numbers) { //二分 int l = 0; int r = numbers.length - 1; while(l <= r) { int mid = (l + r) / 2; if(numbers[mid] < numbers[l]){ r = mid; }else if(numbers[mid] > numbers[r]){ l = mid + 1; }else if(numbers[mid] == numbers[r]){ r--; }else{ //代表数组已经是一个完全有序的数组 break; } } return numbers[l]; } }