题目
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例 1:
输入: [3,4,5,1,2]
输出: 1
示例 2:
输入: [4,5,6,7,0,1,2]
输出: 0
分析
需要注意的是这个数组最开始是有序的,如{1,2,3,4,5,6}。
数组旋转的是什么意思?就是数组中的元素向后移动,末尾的元素移到头就到数组头然后继续后移。
数组后移1位,即{6,1,2,3,4,5}
数组后移2位,即{5,6,1,2,3,4}
数组后移3位,即{4,5,6,1,2,3}
现在数组旋转的概念介绍清楚了,那么我在来看一下旋转后的数组的规律
- 一个升序序列变成了两个升序序列
- 最小值小于其左边的值
方案一:数组重排序
将数组重新按升序排序,然后取第一个元素即是最小值
代码实现
/**
* 求升序数组旋转后的最小值 排序求解
*
* @param nums
* @return
*/
public int findMin(int[] nums) {
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length - i; j++) {
if (nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
}
return nums[0];
}
时间复杂度O(n2),空间复杂度O(n)。
方案二:逐个比较
将数组第一个元素置为最小值,逐个比较,如果其值更小则替换,取所有小值的最小值即为结果
代码实现
/**
*
* @param nums
* @return
*/
public int findMinByDP(int[] nums) {
int iMin = nums[0];
int min = nums[0];
for (int i = 0; i < nums.length; i++) {
iMin = Math.min(iMin, nums[i]);
min = Math.min(iMin, min);
}
return min;
}
时间复杂度O(n),空间复杂度O(n)。
方案三:二分查找
如果arr[length - 1] > arr[0],说明数组没有旋转,最小值是arr[0]。
否则说明数组旋转了,
我们去数组的中间值arr[mid],
如果arr[mid]>arr[0]说明最小值在数组的右半部分。
最小值出现在左遍的值>右边的值,右边值即是最小值(数组升序)
代码实现
/**
* 二分查找
* @param nums
* @return
*/
public int findMinByBinary(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
if (nums[nums.length -1] > nums[0]){
return nums[0];
}
int left = 0;
int right = nums.length -1;
int mid;
while (left <= right) {
mid = (left + right) / 2;
if (nums[mid] > nums[mid + 1]) {
return nums[mid + 1];
}
if (nums[mid] < nums[mid - 1]) {
return nums[mid];
}
if (nums[mid] > nums[0]) {
left = mid + 1;
}else {
right = mid - 1;
}
}
return nums[left];
}
时间复杂度O(logn),空间复杂度O(n)。