题目链接:http://www.lintcode.com/zh-cn/problem/find-minimum-in-rotated-sorted-array-ii/
假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。
你需要找到其中最小的元素。
数组中可能存在重复的元素。
注意事项
The array may contain duplicates.
给出[4,4,5,6,7,0,1,2] 返回 0
思路:
1. 如果arr[L]<arr[R],说明该数组是有序的,自然最小值在最左边
2. 如果arr[L]>=arr[R],说明L到R范围内包含循环部分,比如2 2 3 1 2,此时我们考察第一个数和中间的那个数的大小
(1) 如果arr[L]>arr[M],此时说明最小的那个数只能在L到M范围内,比如 7 8 9 1 2 3 4 5 6。因为只有当arr[M]是循环过的部分时,才有arr[L]>arr[M]出现。
(2) 如果arr[M] >arr[R],此时说明最小的那个数只能在M到R范围内,因为只有当arr[M]不是循环部分的时候,才会有arr[M] >arr[R],比如4 5 6 7 8 9 1 2 3
(3) 上述两种情况不满足时,说明arr[L]<=arr[M]并且arr[M] <=arr[R],此时又有条件arr[L]>=arr[R],说明arr[L]=arr[M] =arr[R],其实这种情况,无法再继续用二分查找,比如数组2 2 …2 1 2… …2 2(只有一个1其实都是2),无论1出现在哪个位置都满足有序循环数组的条件,此时找到1只能用遍历的方式。当然,我们可以将左指针右移一位,略过一个相同数字,这对结果不会产生影响,因为我们只是去掉了一个相同的,然后对剩余的部分继续用二分查找法,在最坏的情况下,比如数组所有元素都相同,时间复杂度会升到O(n),也就是遍历。
public class CircularArrayMinimumNum {
//遍历,复杂度为O(n)
public static int getMinNum_1(int[] num) {
int min = num[0];
for (int i = 1; i < num.length; i++) {
if (num[i] < min)
min = num[i];
}
return min;
}
//二分搜索,复杂度O(logN),最坏情况O(n)
public static int getMinNum(int[] num) {
if (num == null || num.length == 0)
return -1;
int left = 0;
int right = num.length - 1;
if (num[left] >= num[right]) {
while (left<right-1) {
int mid = left + (right - left) / 2;
if (num[left] > num[mid])
right = mid; // 不能用mid-1,因为中间的数也有可能是最小的
else if(num[mid] > num[right]) //该条件可替换为 num[left] < num[mid]
left = mid;
else //num[left]==num[right]==num[mid]
left++; //这种情况,把左指针右移,略过相同数字
}
return Math.min(num[left],num[right]);
}
return num[0];
}
}
上面是数组中出现重复数字的情况,那么当数组没有重复数字时,那么就更简单了,把上面的代码删掉相等的情况就可以,如下:
public class CircularArrayMinimumNum_2 {
public static int getMinNum(int[] nums) {
if (nums == null || nums.length == 0)
return -1;
int left = 0;
int right = nums.length - 1;
if (nums[left] > nums[right]) {
while (left<right-1) {
int mid = left + (right - left) / 2;
if (nums[left] > nums[mid])
right = mid; // 不能用mid-1,因为中间的数也有可能是最小的
else //if(num[mid] > num[right]) //该条件可替换为 num[left] < num[mid]
left = mid;
// else //num[left]==num[right]==num[mid]
// left++; //这种情况,把左指针右移,略过相同数字
}
return Math.min(nums[left],nums[right]);
}
return nums[0];
}
}