旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [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
解题思路
首先看到题目后直接用暴力排序的方法来进行解题。利用sort算法将vector容器中的元素从小到大排序,然后取出排序后容器里的第一个元素就是数组的最小值。不过运行后这种解法结果很不理想。
代码如下:
class Solution {
public:
int minArray(vector<int>& numbers) {
sort(numbers.begin(),numbers.end());
return numbers.front();
}
};
执行用时:28 ms, 在所有 C++ 提交中击败了8.07%的用户
内存消耗:12.3 MB, 在所有 C++ 提交中击败了7.68%的用户
解法二
对于排序数组的查找问题我们也比较容易想到二分法来解决。
数组旋转以后其实就是将原先有序递增的数组分为左右两个递增子数组,左边区间的任意一个元素>=右区间的任意一个元素。
利用二分法我们可以定义i指向数组第一个元素,j指向数组最后一个元素。当i<j时我们进入循环进行比对。定义mid=(i+j)/2,此时比对应该分为三种情况:
- 当num[mid]<num[j]时,我们可以判断出最小值一定在[i,mid]区间内;
- 当num[mid]>num[j]时,我们可以判断出最小值一定在[mid,j]区间内;
- 当num[mid]=num[j]时,此时我们无法判断出最小值在哪个区间内
原因如下:
设以下两个旋转点值为0的示例数组,则当 i = 0, j = 4时 m = 2,两示例结果不同。
示例一 [1, 0, 1, 1, 1]:旋转点 x = 1,因此 m = 2在右排序数组中。
示例二 [1, 1, 1, 0, 1]:旋转点 x = 3,因此 m = 2在左排序数组中。
但我们可以判断出此时左区间[i,mid]所有元素相等,或者右区间[mid,j]所有元素相等,或者是整个区间[i,j]所有元素相等。所以我们可以跳出二分法,转而使用线性遍历的方法来进行查找,直到查找到最小值后返回其值。
代码如下:
class Solution {
public:
int minArray(vector<int>& numbers) {
int i=0,j=numbers.size()-1;
while(i<j)
{
int mid=(i+j)/2;
if(numbers[mid]<numbers[j])
j=mid;
else if(numbers[mid]>numbers[j])
i=mid+1;
else
{
int tem=i;
for(int a=tem+1;a<j;a++)
{
if(numbers[a]<numbers[tem])
tem=a;
}
return numbers[tem];
}
}
return numbers[i];
}
};
执行用时:8 ms, 在所有 C++ 提交中击败了93.98%的用户
内存消耗:12.2 MB, 在所有 C++ 提交中击败了31.08%的用户