1.题目

假设按照升序排序的数组在预先未知的某个点上进行了旋转。例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] 。

请找出其中最小的元素。

示例 1:

输入:nums = [3,4,5,1,2]

输出:1

示例 2:

输入:nums = [4,5,6,7,0,1,2]

输出:0

示例 3:

输入:nums = [1]

输出:1

来源:力扣(LeetCode)


2.时间复杂度O(n)的解法

2.1 思路

本思路是概括了题目和修改后的题目,暴力破解都是这个方法。

原数组是非递减的,旋转后,有两种情况,数组仍是非递减的,例如将全部元素放到数组的末尾,它相当于还是原数组,或者有重复项,例如 {2,2,2,2},旋转后仍是非递减的,那么我们返回第一个元素即可。另一种情况就会出现前一个元素大于后一个元素,那么后一个元素一定是最小的,也就是我们要的解。

2.2 代码

int findMin(vector<int>& nums) {
if(!nums.size()) return -1;
for(int i = 0; i < nums.size() - 1; i++) {
if(nums[i] > nums[i + 1])
return nums[i + 1];
}
return nums[0];
}


3.时间复杂度O(logn)的解法

3.1 思路

此思路是假设没有重复的数。

我们知道,当数组是已经排序好的,我们可以用二分法来查找,其实本题也可以利用二分法来做。

我们找一个中间值,如果这个中间值小于此时的left,那么就意味着我们要找的数就在left 到 mid 中间,如果中间值大于left,那么我们要找的值就在mid到right中间,直到缩减到两个数的时候,此时左指针指向的数一定大于右指针指向的数,mid此时就会在左指针的位置上,我们返回右指针即可

3.2 代码

class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
int mid = (left + right) / 2;
if (nums[left] < nums[right])
{
return nums[left];
}
while (left != mid)
{
if (nums[mid] > nums[left])
{
left = mid;
}
else if (nums[mid] < nums[right])
{
right = mid;
}
mid = (left + right) / 2;
}
return nums[right];
}
};


4.修改后的题(允许元素重复)

4.1题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

输入一个升序的数组的一个旋转,输出旋转数组的最小元素。

例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

数组可能包含重复项。

注意:数组内所含元素非负,若数组大小为0,请返回-1。

样例

输入:nums=[2,2,2,0,1]

输出:0


​题目地址​

4.2解法

题目中允许出现重复的数字,这就意味着在原来的理解上我们要多一层可能,那就是中间的值等于左侧值或者右侧的值,举一个例子[2,2,2,1,1,2],此时mid指向第三个2,最小值在mid的右侧;[2,2,2,1,1,2,2,2,2,2,2],此时最小值在mid左侧。这样我们就需要来判断此时我们是要在左侧寻找还是在右侧寻找了。但不管怎么样,如果相同,我们可以省略掉相同的数的那个指针。

所以分三种情况,当中间的比最右端的小,此时我们让右指针移动过来,当中间的比右边打,我们让左指针移到中间指针的后方,如果相等,就让右指针移动。

4.3代码

class Solution {
public:
int findMin(vector<int>& nums) {
int low = 0;
int high = nums.size() - 1;
while (low < high) {
int pivot = low + (high - low) / 2;
if (nums[pivot] < nums[high]) {
high = pivot;
}
else if (nums[pivot] > nums[high]) {
low = pivot + 1;
}
else {
high -= 1;
}
}
return nums[low];
}
};