剑指offer--旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素,例如数组{3, 4, 5, 1, 2}为数组{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1.

我们采用二分法,就是设计两个指针,一个指向数组头部,一个指向数组尾部,因为这样的旋转数组在最小元素前面和最小元素的后面都是递增的.而且因为是旋转,所以最小元素前面的元素一定是大于包括最小元素在内的后面的元素的.当然,如果元素没有经过旋转,那么另当别论.
在这里插入图片描述

根据二分法的思想,我们每次得到一个中间元素middle = (phead + ptail)>>1.通过判断middle与尾指针ptail的大小来缩小范围.如果middle > ptail, 那么phead = middle, ptail = ptail, 否则phead = phead, ptail = middle.这样每次将范围缩小一半,.设置循环条件为phead > ptail.因为数组的长度有限,最后总能缩小到phead+1 = ptail所以最后phead > ptail,而这个范围又一定包含这个最小的元素,所以ptail所指元素一定就是最小的元素.返回ptail所指的元素即可.
因为还要考虑到没有旋转的情况,而没有旋转的时候第一个元素就是最小的元素,所以做一个判断,如果phead < ptail,那么说明元素是排好序的,直接返回第一个元素即可.
但是我们上面只是考虑了数组中的元素都是严格单调递增的,如果条件放宽些,就未必是这样,书中给出的一个比较特殊的例子是{1, 0, 1, 1, 1}和{1, 1, 1, 0, 1}.就是一个反例.
根据我们前面的条件,phead > ptail或者phead<ptail在这个例子中均不满足.所以为了普适性情况,必须要在某一个地方包括上"="这个情况,我们不妨就加在前面一个条件上,现在循环的条件放款为phead >= ptail.此时这个数组就进入了循环.因为p[0] >= p[4],所以寻找中间元素p[2],也就是1.这个时候因为p[middle]>=p[tail],事实上我们无法确定最小的元素是在[0,2]还是在[2, 4],如果我们让这种情况下从[2, 4]找,那么对于数组{1, 0, 1, 1, 1}就找不到最小元素0.但如果我们在这种情况下从[0, 2]找,那么对于数组{1, 1, 1, 0, 1}就找不到最小元素0.这就让我们无从判断在出现p[middle] = p[tail]的情况下该如何缩小范围.
所以书中无奈表示,当出现p[middle] == p[tail]时,只能通过顺序查找最小的元素了.
事实上条件也可以写成p[head] == p[middle] == p[middle].因为如果p[head] > p[tail]且p[middle] = p[tail]的话,也就是p[head] > p[middle]了.这个时候可以判断最小的元素在[head, middle]范围内,因为如果它在[middle, tail]范围内的话,就存在两个元素大于它们后面一个元素了.第一个在middle之前,另外一个在middle之后,这跟数组只进行了一次旋转的事实矛盾.所以当p[head]>p[tail]且p[tail]==p[middle]这种情况实际上是可以缩小范围的.即使时考虑到这种情况{2, 1, 1, 1, 1},那也是可以确定最小元素的.但更多的是这种情况{2, 0, 1, 1, 1},绝不可能出现这种情况{2, 1, 1, 0, 1}(因为这绝对不是一次旋转导致的).

总结

所以我们为了能够同时解决元素非严格递增的数组(就是有很多相同的元素的数组),需要将循环的条件(二分的条件)设置为phead >= ptail.但如果出现了phead == pmiddle == ptail的情况时,我们只能够进行顺序查找了,只要不是这种情况,我们都可以缩小范围,知道最后数组中只剩下两个元素,那个较小的元素就是我们要找的最小的元素.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值