二分搜索——找出有序循环数组中的最小值

题目链接: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];
	}

}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值