1. 题目描述
2. 算法思路
本题的详细解来自于下,建议一看,由于二分法的细节实在是太多了,此处无法完全列举,只抛砖引玉,给我自己的思考和总结,详细可以去下文。
作者:jyd
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/solution/mian-shi-ti-11-xuan-zhuan-shu-zu-de-zui-xiao-shu-3/
来源:力扣(LeetCode)
本渣的思考和总结:
- 看到题目,就觉得肯定是二分,但是由于题目的特殊性,这个二分拥有很多让人头疼的细节,不过能够将时间复杂度从线性降到 log级,还是很棒滴。
首先先来分析,二分的使用:
- i 指向numbers左端, j 指向numbers右端, m = ( i + j ) / 2
- 我们比较numbers[m] 和 numbers[j];
- 此处解释一下,为啥选择numbers[j],而不是和numbers[i]进行比较。
- 因为,当示例只含有右排序数组,即numbers = [1,2,3,4,5]时,numbers[m] > numbers[i] 就无法进行判断,按道理来说,当numbers[m] > numbers[i] ,就应该向m的右边进行收缩,但是面对这种情况,向右收缩就会出错。
- 当 numbers[m] > numbers[j] ,就说明,target一定在右边,因此 i = m + 1;
- 当 numbers[m] < numbers[j] ,就说明,target一定在左边,因此 j = m ;
- 当 numbers[m] = numbers[j] ,无法说明是在左边,还是在右边, j = j - 1
- 解释为啥判断不了,例如下面两个,得出的结果就不一致
- 例 [1, 0, 1, 1, 1]:旋转点 x = 1 ,因此 m = 2在 右排序数组 中。
- 例 [1, 1, 1, 0, 1]:旋转点 x = 3,因此 m = 2 在 左排序数组 中
接下来对numbers[m] = numbers[j]的情况进行专门的分析:此处不做严格的算法证明,为了理解,我将讨论区里大佬讨论得到的结果粘出来,非常精辟。如下图,
3. 代码
class Solution {
public int minArray(int[] numbers) {
int i = 0;
int j = numbers.length - 1;
while(i < j){
int m = (i + j) / 2;
if(numbers[m] > numbers[j]){
//说明在右边
i = m + 1; //比numbers[j]大,所以一定不包含m
}else if(numbers[m] < numbers[j]){
//说明在左边,但是无法证明不包含m,因此j = m
j = m;
}else{
j--;
}
}
return numbers[j]; //此时一定j = i
}
}
4. 测试结果