题目描述:旋转数组求最小元素(II 假设有重复元素)
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
Find the minimum element.
I You may assume no duplicate exists in the array.
Follow up for "Find Minimum in Rotated Sorted Array":
What if duplicates are allowed?
Would this affect the run-time complexity? How and why?
题目分析:我们可以从I开始分析,注意到旋转后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于(或者等于)后面子数组的元素。我们还可以注意到最小的元素刚好是这两个子数组的分界线。在排序的数组中我们可以利用二分法实现O(logn)的查找。
和二分查找法一样,我们用两个指针分别指向数组的第一个元素和最后一个元素。按照题目中的旋转规则,第一个元素应该是大于或者等于最后一个元素的。(但是还有一种特例就是这个数组根本没有旋转,还是从小到大有序排列的。)
接着我们可以找到数组中间的元素。如果中间元素位于前面递增子数组,那么它应该大于或者等于第一个指针指向的元素。此时数组中最小的元素应该位于该中间元素的后面。我们可以把第一个指针指向该中间元素,这样可以缩小寻找的范围。移动后的第一个指针仍然位于前面的递增子数组中。
同样,如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的元素。此时该数组中最小的元素应该位于该中间元素的前面。我们可以把第二个指针指向该中间元素,这样也可以缩小寻找的范围。移动之后的第二个指针仍然位于后面的递增子数组中。
不管是移动第一个指针还是第二个指针,查找范围都会缩小到原来的一半,接下来我们再用更新之后的两个指针,重复做新一轮的查找。
按照上述思路,第一个指针总是指向前面递增数组的元素,而第二个指针总是指向后面递增数组的元素。最终第一个指针将指向前面子数组的最后一个元素,而第二个指针会指向后面子数组的第一个元素。也就是它们最终会指向两个相邻的元素,而第二个指针指向的刚好是最小的元素。这就是循环结束的条件。
所以,Find Minimum in Rotated Sorted Array I 代码如下,因为I中没有重复元素,所以不存在类似于 1111011这种情况。
class Solution {
public:
int findMin(vector<int>& nums) {
int N=nums.size();
if(N==1) return nums[0]; //若数组只有一个元素
int l=0, r=N-1, m;
if(nums[l]<nums[r]) return nums[l];//若没有发生旋转
while(l<=r)
{
if(r-l ==1) return nums[r];
m = (l+r)/2;
if( nums[m] > nums[l] ) l=m;
else if( nums[m] < nums[r] ) r=m;
}
}
};
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
元素 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
第一个和第二个指针都是指向1并且中间指针也是指向1,这三个数字相同,这个时候我们无法判断中间的数字是位于前面的子数组中还是后面的子数组中,也就无法移动两个指针来缩小查找范围。此时我们要采用顺序查找的方法。
但是,当并不是三个数字相同时,如下:a[l]=1, a[r]=1, a[m]=0,并不存在上述问题。l=0,r=3, m=1,这时移动指针判断条件的等号就要加上了。
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
元素 | 1 | 1 | 1 | 0 | 1 | 1 | 1 |
综上,Find Minimum in Rotated Sorted Array II 代码如下。
class Solution {
public:
int findMin(vector<int>& nums) {
int N = nums.size();
if(N==1) return nums[0];//single element
int l=0, r=N-1, m;
if( nums[l]<nums[r] ) return nums[l]; //sorted array without duplicates and rotated
while(l<=r)
{
if(r-l == 1) return nums[r];
m=(l+r)/2;
if(nums[l]==nums[m] && nums[m]==nums[r]) return search_min_inorder(nums,l,r);
if( nums[l]<=nums[m] ) l=m; //加等号
if( nums[m]<=nums[r] ) r=m; //加等号
}
}
int search_min_inorder(vector<int>& nums,int l,int r)
{
int mini=nums[l];
for(int i=l+1; i<=r; i++)
{
if(nums[i] < mini) mini=nums[i];
}
return mini;
}
};