leetcode 剑指offer 11 旋转数组的最小数字
问题描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
解题思路:
如下图所示,寻找旋转数组的最小元素即为寻找 右排序数组 的首个元素 nums[x],称x为 旋转点 。
PS:
- 主要是要利用旋转数组的性质 左排序数组任意元素 >= 右排序数组任意元素
- 由于是排序数组,二分法解决问题,可将 遍历法 的 线性级别 时间复杂度降低至 对数级别 。
所以采用以下算法步骤:
- 初始化: 声明i, j双指针分别指向nums数组左右两端;
- 循环二分: 设
m=(i+j)/2为每次二分的中点( "/" 代表向下取整除法,因此恒有i≤m<j),可分为以下三种情况:- 当
nums[m]>nums[j]时:m一定在左排序数组中,即旋转点x一定在[m+1,j]闭区间内,因此执行i=m+1; - 当
nums[m]<nums[j]时:m一定在右排序数组中,即旋转点x一定在[i,m]闭区间内,因此执行j=m; - 当
nums[m]=nums[j]时:无法判断m在哪个排序数组中,即无法判断旋转点x在[i,m]还是[m+1,j]区间中。解决方案: 执行j=j-1缩小判断范围,分析见下文。
- 当
- 返回值: 当
i=j时跳出二分循环,并返回 旋转点的值nums[i]即可。
实际上,当出现nums[m] = nums[j]时,一定有区间 [i,m] 内所有元素相等 或 区间[m,j]内所有元素相等(或两者皆满足)。对于寻找此类数组的最小值问题,可直接放弃二分查找,而使用线性查找替代。
解法:
class Solution {
public:
int minArray(vector<int>& numbers) {
int i = 0, j = numbers.size() - 1;
while(i < j)
{
int m = (i + j)/2;
if(numbers[j] < numbers[m])
{
i = m + 1;
}
else if(numbers[m] < numbers[j])
{
j = m;
}
else
{
for (auto i = numbers.cbegin();(i + 1)!= numbers.cend();)
{
if (*i > *(++i))
{
return *i;
}
}
return *(numbers.cbegin());
}
}
return numbers[i];
}
};

浙公网安备 33010602011771号