剑指offer_旋转数组的最小数字

题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为属于组的旋转,输入一个递增排序的数组的一个旋转。输出数组的最小元素,例如{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
题目解析,遍历数组找出最小的数字,好像是最不用思考的一个方法,以至于我一开始觉得这个题目出错了, 但是肯定不能用这种方式了
解析: 我们可以发现旋转以后的数组实际上可以分成两个有序的子数组,而前面的子数组的元素都比后面子数组的元素要大,而且我们发现最小的元素刚好是这两个子数组的分界线,我们可以使用二分查找的方法实现0(logn)的查找,本题给出的数组在一定程度上是排序的,因此可以考虑这种方式
和二分查找一样,
1 我们用两个指针分别指向数组的第一个元素和最后一个元素,按照题目中旋转的规则,第一个元素应该大于或者等于最后一个元素的,如果第一个元素小于最后一个元素,那么只能说明,这个数组已经排序排好了。比如{1,2,3,4,5},这样的话第一个元素是最小的。
2 找到数组中间的元素,如果中间元素位于前面的递增子数组,那么它应该大于等于第一个指针指向的元素。此时最小元素应该在它的后面,我们可以把第一个指针指向中间元素,这样可以缩小范围,移动之后第一个指针任然位于前面的递增子数组之中。
3 与2相反,如果中间元素位于后面的递增子数组,那么它一定小于第一个指针指向的元素。此时最小元素应该在这个指针的前面,我们就把第二个指针指向中间元素,这样可以缩小范围。

代码实现:

public static int findMin(int[] arr) {
		// 定义两个索引
		int i = 0;//i 表示的是左边的索引
		int j = arr.length - 1;//j 表示的是右边的索引
		// 找出数组的中间元素,arr[(i+j)/2]
		while (i < j) {  
			if (arr[i] < arr[j])
				return arr[i];
			int mid = (i + j) / 2;
			if (arr[mid] > arr[i]) {
				i=mid+1;
			} else if (arr[mid] == arr[j]) {
				i++;
			} else {
				j = mid;
			}
		}
		return arr[i];

多考虑一步
前面我们提到在旋转数组中,由于是把递增排序数组前面若干数字搬到数组的后面,因此第一个数字总平时大于等于最后一个数字,但是又特例: 如果把排序数组的0 个元素搬到最后面,即排序数组本身,这任然是数组的一个旋转,我们需要支持这种情况。
上述代码似乎已经考虑的比较完整了,但是还有情况需要分析:

下标为index1 和index2 分别和途中P1 和P2 相对应,两个数相同的情况,在前面的代码中,当这两个数相同,并且他们中间的数字,也即 indexMid 指向的数字也相同的时候,我们把indeXMid赋值给了index1 ,也就是认为此时最小的数字位于中间数字的后面,但是不一定是这样的

例如数组{1,0,1,1,1} 和数组{1,1,1,0,1}都可以看成是递增排序数组{0,1,1,1,1}的旋转
在这里插入图片描述
这两个数组中,第一个数字,最后一个数字和中间的数字都是1, 我们无法确定中间的数字1 属于第一个递增子数组还是属于第二个递增子数组

在两种情况下,第一个指针和第二个指针指向的数字都是1 ,并且两个指针中间的数字也是1 ,这三个数字相同。在第一种情况中, 中间数字(下标为2 )位于后面的子数组;在第二中情况中,中间数字(下标为2 )位于前面的子数组中,因此当两个指针指向的数字以及它们中间的数字三者相同的时候,我们无法判断中间的数字是位于前面的还是位于后面的数组中,也就无法移动两个指针来缩小查找的范围此时,我们不得不采用顺序查找的办法

这样的话就是在逻辑里面加入一个判断,循环查找

public static int findMin(int[] arr) {
		int min = 0;
		// 定义两个索引
		int i = 0;
		int j = arr.length - 1;
		// 找出数组的中间元素,arr[(i+j)/2]
		while (i < j) {
			if (arr[i] < arr[j])
				return arr[i];
			int mid = (i + j) / 2;
			if (arr[mid] == arr[i] && arr[i] == arr[j]) { //如果
				for (int k = 0; k < arr.length; k++) {
					if (arr[k] < min) {
						min = arr[k];
					}
				}
				return min;
			} else if (arr[mid] > arr[i]) {
				i = mid + 1;
			} else if (arr[mid] == arr[j]) {
				i++;
			} else {
				j = mid;
			}
		}
		return arr[i];
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值