排序数组的查找问题首先考虑使用 二分法 解决,其可将遍历法的 线性级别 时间复杂度降低至 对数级别 。
题目:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
思路:
!!!排序数组查找问题别一上来就是循环遍历,想想二分法!!!
观察旋转数组可以发现:[3,4,5,1,2]中称[3,4,5]为左排序数组,[1,2]为右排序数组。PS:定义左排序数组小于右排序数组,所以最小元素是右排序数组。
突破点:最小元素(称为旋转点)就在左右排序数组之间!
算法流程:
- 循环二分: 设置 i, j 指针分别指向
numbers数组左右两端,m=(i+j)/2 为每次二分的中点,可分为以下三种情况:
- 当
numbers[m] > numbers[j]时: m 一定在 左排序数组 中,即旋转点 x 一定在 [m+1,j] 闭区间内,因此执行 i = m + 1【记住我们目的是缩小范围后的的数组都是在左右排序数组之间】;- 当
numbers[m] < numbers[j]时: m 一定在 右排序数组 中,即旋转点 x一定在[i, m] 闭区间内,因此执行 j = m【记住是m,不是m-1,因为很有可能下标m对应的值就是X】;- 当
numbers[m] == numbers[j]时: 无法判断 m 在哪个排序数组中,即无法判断旋转点 x 在 [i, m]还是 [m + 1, j] 区间中。解决方案: 执行 j = j - 1缩小判断范围 。【不能通过i=i+1来缩小判断范围,因为只能通过比较numbers[j]而不是numbers[i],本质是因为 j初始值肯定在右排序数组中; i初始值无法确定在哪个排序数组中。想想数组[1,2,3]】
- 返回值: 当 i = j 时跳出二分循环,并返回
numbers[i]即可。
代码:
if (numbers.size() == 0)
{
return -1;
}
int i = 0, j = numbers.size() - 1, m;
while(i != j)
{
// 计算中间的下标
m = ( i + j ) / 2;
if (numbers[m] < numbers[j])
{
j = m;
}
else if (numbers[m] > numbers[j])
{
i = m + 1;
}
else
{
j--;
}
}
return numbers[i];
4679



被折叠的 条评论
为什么被折叠?



