剑指Offer-旋转数组中的最小数
题目如下
仓库管理员以数组 stock
形式记录商品库存表。stock[i]
表示商品 id
,可能存在重复。原库存表按商品 id
升序排列。现因突发情况需要进行商品紧急调拨,管理员将这批商品 id
提前依次整理至库存表最后。请你找到并返回库存表中编号的 最小的元素 以便及时记录本次调拨。
示例 1:
输入:stock = [4,5,8,3,4]
输出:3
示例 2:
输入:stock = [5,7,9,1,2]
输出:1
提示:
- 1 <= stock.length <= 5000
- -5000 <= stock[i] <= 5000
思路
这道题在力扣上需要通过不难,因为数据集并不是很大,甚至可以直接暴力解法,这里要分享的是用二分法搜索。
二分查找
通过题意我们可以知道,原数组原本是一个递增数组,后面由于一些原因旋转了。
如下面一个例子:
[5,7,9,1,2]
[5…9]是递增的
[1…2]也是递增的
但是两个部分并不连续递增,我们目的就是要找出中间开始变化的那个点。
定义left指针和right指针
left:指向区间的最左边
right:指向区间的最右边
每一次循环用mid = (left+right)/2
与right指向的值比较(下面解释
-
若stock[mid] > stock[right],则left = mid+1
-
若stock[mid] < stock[right],则right = mid
-
若stock[mid] = stock[right], 则right–
循环终止条件为:left=right
可行性说明
- 为什么不和left比较而是和right比较呢?
举个例子:
[1,2,3,4,5]
[3,4,5,1,2]
假设left=0,right=4,那么mid=2,即指向中间值,此时mid指向的值均大于left指向的值,但是两种数组的情况是不一样的。
这就证明了为什么不能与left比较,原因是有二义性。
- 为什么当stock[mid] > stock[right],left = mid+1,而stock[mid] < stock[right],却不是right = mid - 1呢?
首先解释为什么要mid+1
也是一样的例子
[3,4,5,1,2]
如果stock[mid] > stock[right],则说明:此时数组在继续往下的话就会遇到断点,在此之前数组是正常递增
因此区间[left,mid]都是正常的,这里是闭区间
所以下一个区间应该收缩到[mid+1,right]
而如果stock[mid] < stock[right],则说明:mid此时的位置已经在异常区间里头,不能保证mid-1是正常的递增区间
因此区间[mid,right]是不正常的,且不能保证[mid-1,right]也正常。
[3,4,5,6,1,2,3]
此时mid=5,指向val = 2,触发第二个条件,而如果right = mid-1就把前一个的值(1)给略过了。
- 为什么等于时无法判断,而且要right–?
若mid 指向的值等于最右边,由于数组可重,因此不能说明在哪个区间。
至于为什么要right–?不知道,我也没搞懂(双手摊开)
代码如下
class Solution {
public:
int stockManagement(vector<int>& stock) {
int left = 0, right = stock.size()-1;
int mid;
while(left<right){
mid = (left+right)/2;
if(stock[mid] > stock[right]){ // 位于右边的序列
left = mid + 1;
}else if(stock[mid] < stock[right]){ // 位于左边的序列
right = mid ;
}else{
right--;
}
}
return stock[left];
}
};