算法系列博客
154. Find Minimum in Rotated Sorted Array II
Suppose an array sorted in ascending order 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.
The array may contain duplicates.
注:要求算法以函数形式实现,检测所用vector作为函数参数,求出结果作为函数返回值。
——————————————————————————————–
分析:在环形已排序数组中找出最小值,即是找出比前一元素小的元素的值,其中第一个元素的前一元素定义为最后一个元素。
因而算法的目标即是找出比前一元素小的元素。
解法一:简单遍历
遍历找出比前一元素小的元素,如果找不出这样的元素,即说明所有元素相等,直接返回任意元素即可。 容易看出时间复杂度O(n)
int findMin(vector<int>& nums) {
for(int i = 0; i < nums.size() - 1; i++)
if(nums[i] > nums[i+1]) return nums[i+1];
return nums[0];
}
解法二:二分递归
定义包含结果的数组段nums下标起点为begin,终点为end,中点mid=(begin+end)/2;
- 结束条件:nums[begin] < nums[end]或begin == end
此时最小值一定是nums[begin]; - 从中点将数组nums分为两个新的数组段[begin,mid]和[mid+1,end],
如果某段中起点的值比终点的值大,那么最小值一定是在这一段之中,此时便可对这个数组段进行递归; - 在前面两条都不成立的情况下,有如下表达式成立:
nums[begin] >= nums[end]
nums[begin] <= nums[mid]
nums[mid+1] <= nums[end]
如果有nums[begin] < nums[mid], 则[begin,mid]段段内必然升序,
并且有nums[mid+1] <= nums[end] <= nums[begin] < nums[mid], 因而nums[mid]即是比前一元素小的元素,也即最小值;
此时需要对两段进行递归,分别找出两段中各自的最小值,然后两者比较选其小; - 剩下的情况下,最小值一定在[mid+1,end]段之中
int _findMin(vector<int>& nums, int begin, int end) {
if(nums[begin] < nums[end] || begin == end)
return nums[begin];
int mid = (begin + end) / 2;
if(nums[mid+1] > nums[end])
return _findMin(nums, mid+1, end);
if(nums[begin] > nums[mid])
return _findMin(nums, begin, mid);
if(nums[begin] < nums[mid])
return nums[mid+1];
if(nums[begin] == nums[end] && nums[begin] == nums[mid] && nums[mid+1] == nums[end]) {
int front = _findMin(nums, begin, mid);
int back = _findMin(nums, mid+1, end);
return front < back ? front : back;
}
return _findMin(nums, mid+1, end);
}
int findMin(vector<int>& nums) {
return _findMin(nums, 0, nums.size()-1);
}
解法三:二分递推
设置两个下标low,high初始值分别为数组的下边界和上边界,调整low和high的值,使它们的距离越来越小,并且始终保持最小值在数组low位置和high位置之间,直到low和high相等,则此时low位置的元素记为最小元素
借助辅助量mid=(low+high)/2来对low和high进行调整
与解法二一样,精巧的设计可以免去一些不必要的迭代过程
1.nums[low] < nums[high]时,nums[low]必然是最小值
2.nums[low] > nums[mid]时,最小值必然在[low,mid], 故将mid赋值给high
3.nums[mid] > nums[high]时,最小值必然在[mid+1,high]
4.前面都不满足的条件下
如果nums[low] < nums[mid],同法二,nums[mid+1]必然是最小值
如果nums[mid] < nums[high],则nums[mid]必然是最小值
5.其余条件下,不能判断最小值在前半部分还是后半部分,但有nums[low] >= nums[high], 因而nums[low]一定不是最小值,
并且去掉后不会影响数组的环形有序性质,因而将low自增1以缩短low与high的距离
int findMin(vector<int>& nums) {
int low = 0, mid;
int high = nums.size() - 1;
while(low < high) {
if(nums[low] < nums[high])
return nums[low];
mid = (low + high) / 2;
if(nums[low] > nums[mid])
high = mid;
else if(nums[mid] > nums[high])
low = mid + 1;
else if(nums[low] < nums[mid])
return nums[mid+1];
else if(nums[mid] < nums[high])
return nums[mid];
else low++;
}
return nums[low];
}
同众多其他二分法一样,这两种二分的时间复杂度的级别均为O(log(n))
区别在于解法二属于递归,解法三属于递推,且此递推较此递推额外开销小