把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目描述了半天,其实就是让你找出数组中最小的数,有很多种方法,可以临时定义一个最小值,然后遍历,如果数值比当前最小值小,就更新最小值。遍历完成后返回即可。
class Solution {
public:
int minArray(vector<int>& numbers) {
if(numbers.empty()) return 0;
if(numbers.size() == 1) return numbers[0];
int length = numbers.size();
int temp = INT_MAX;
for(int i = 0;i<length;i++){
if (temp > numbers[i]) temp = numbers[i];
}
return temp;
}
};
也可以放到数组里,快排一下,返回数组第一个数,甚至可以用小顶堆。
class Solution {
public:
int minArray(vector<int>& numbers) {
if(numbers.empty()) return 0;
if(numbers.size() == 1) return numbers[0];
int length = numbers.size();
int array_temp[length];
for(int i = 0;i<length;i++)
array_temp[i] = numbers[i];
sort(&array_temp[0],&array_temp[length]);
return array_temp[0];
}
};
言归正传,题目费了半天口舌让你找最小数字,显然不是让你用以上的方法求解,题目中给的是【旋转数组】,旋转前是【递增排序】的,旋转后,以【最小值】为界,两边依然是【递增】的状态。
这样来看的话,我们只要找到那个所谓的【分界点】,也就是最小值了。如果说暴力法的复杂度是 O(n)的话,那么我们当然希望在O(log(n))来解决这一问题了,二分法呼之欲出。
找到中间的数值value_med、起始数值value_start、末尾数字value_end,如果value_med => value_start说明在【前半段数组】依然是【递增】的,那么,临界点一定处于数组的【后半段】,更新value_med、value_start,继续在【后半段】中查找。
如果value_med < value_start,说明这个【临界点】在数组的【前半段】了,继续在数组的前半段进行二分查找。
根据以上两种条件,分而治之,可以较快定位出【临界点】的位置
道理很简单,但是还是有很多特殊情况需要加以考虑才能完整的解决所有case
大概有以下几个问题需要思考清楚才能写
1:迭代终止条件是什么?
2:判断条件到底应该是 > 还是 >=?要不要用<=?
3:是否存在特殊情况?特殊的情况该如何处理
一个一个来分析
1:迭代终止条件是什么?换句话说,就是什么时候我们找到这个数了。
头指针一直在尾指针的前面,如果不断二分,最后头指针与尾指针是相邻的,此时便是终止条件
2:到底要不要用等于号,如果是这样一个数列【2,2,2, 0, 1】
med = 2 start=2,临界点是在后半段,所以我们需要加上“=”的。
从题目给的例子来看,这里的递增就是非递减,所以单纯用< >是不够的
3:特殊情况存在吗?
先说第一种特殊情况,数组本来就是递增的,没旋转,那么我们就不需要二分查找,同时返回头就行了,因此while的控制条件就是要求start>=end;我们把med初始化为头指针,这样return的结果也不需要变了
再说第二种情况,如果数组是【3,0,3 , 3 ,3】,我们会发现med=start的时候,经过第一次判断,会认为最小数位于后半段,其实不然。所以需要在循环中加一个判断,如果出现这种情况,我们就用暴力法去查找做小值
这样的程序一次写好是不可能的,我们需要先写一个简单的demo,然后逐步去完善demo,逐步考虑到这些问题,这也是刷题的目的之一,面试的时候没有太多时间,也比较紧张,或许不能考虑的如此周全,因而我们需要提前做做题,这样才能面试的时候写出好的代码
class Solution {
public:
int minfind(int* number,int start,int end){
int res = number[start];
for(int i = start + 1;i<=end;i++){
if(number[i] < res)
res = number[i];
}
return res;
}
int minArray(vector<int>& numbers) {
if(numbers.empty()) return 0;
int length = numbers.size();
if(length == 1) return numbers[0];
//if(numbers[0] == numbers[length-1] ) return numbers[0];
int start_pos = 0;
int end_pos = length-1;
int med_pos = start_pos;
while( numbers[start_pos] >= numbers[end_pos]){
if((end_pos - start_pos)==1) {
med_pos = end_pos;
break;
}
med_pos = (end_pos + start_pos)>>1;
if(numbers[start_pos] == numbers[end_pos] && numbers[med_pos] == numbers[end_pos])
return minfind(&numbers[0],start_pos,end_pos);
if(numbers[med_pos] >= numbers[start_pos]){
start_pos = med_pos;
}else if(numbers[med_pos] <= numbers[end_pos]){
end_pos = med_pos;
}
}
return numbers[med_pos];
}
};