这个是基础版
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个升序的数组的一个旋转,输出旋转数组的最小元素。
例如数组 {3,4,5,1,2} 为 {1,2,3,4,5} 的一个旋转,该数组的最小值为 1。
数组可能包含重复项。
注意:数组内所含元素非负,若数组大小为 0,请返回 −1。
样例
输入:nums = [2, 2, 2, 0, 1]
输出:0
看到这种题后,可以通过画图进行分析寻找规律。数组是递增的,旋转一般情况是从数组中间断开进行旋转,特殊情况是从数组末尾进行断开,并且断开位置前后的数字相同,此时没有后半段。
看到这后,发现前一段全部大于等于a[0],后一段全部小于等于a[0]。是时候想起来二分法了。
二分法的本质:存在性质的分界点,可以通过二分去寻找分界点,缩小范围,最后找到分界点。
但是此时还应该考虑刚才的特殊情况,如果二分的mid点落在与a[0]相等的那一段,此时无法区分是在前半段或者后半段。这样的话,我们可以二分之前把后半段与a[0】相等的那一段去掉,顺便判断从数组末尾进行断开,并且断开位置前后的数字相同,此时没有后半段的特殊情况。
这时直接开始代码
class Solution {
public:
int findMin(vector<int>& nums) {
if (nums.empty()) return -1;//数组为空时直接返回-1
int n = nums.size()-1;
while (n>0&&nums[n]==nums[0]) n--;//消去后半段与a[0]相等的点
if (nums[n]>nums[0]) return nums[0];//此时没有后半段,a[0] 最小
int l = 0,r = n;
while (l<r){
int mid = l+r>>1;
if (nums[mid]<nums[0]) r = mid;
else
l = mid+1;
}
return nums[r];
}
};
发现有时候用二分算法的时候,会莫名奇妙的让数组陷入死循环,后来一想明白了,因为l,r最后都会变成mid, 既l,r相等,如果l!=mid+1,最后l,r之间剩余两个数的话就会陷入死循环。另外刚开始初始双指针对应左右区间的开闭情况也会有区别。
可以看看这个链接
力扣上有一道题搜索旋转排序数组 II
题目和上面这道题差不多,唯一的不同是要搜索这个旋转一次的数组里是否有某个值。
那这该怎样做呢?
上一道题我们可以找到数组中的最小值,即两端递增区间的分界点。 那想要查找某个值,得看看这个值在哪个区间里,那在这个区间里用二分查找不就OK了。顿时感觉这题,已经是成成的了。
上代码
class Solution {
public:
bool check(vector<int>& nums, int l, int r, int target) {
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
return nums[r] == target;
}
bool search(vector<int>& nums, int target) {
if (nums.empty()) return false;
int n = nums.size() - 1;
while (n > 0 && nums[n] == nums[0]) n -- ;
if (nums[n] >= nums[0]) return check(nums, 0, n, target);//这是只有前一段,二分查找一段就行了
int l = 0, r = n;
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] < nums[0]) r = mid;
else l = mid + 1;
}
if (target >= nums[0]) return check(nums, 0, r - 1, target);
return check(nums, r, n, target);
}
};
写完后一想不对劲,二分法在最坏的情况下还是O(n)的时间复杂度,那么我直接遍历一遍查找不也行!!!
class Solution {
public:
bool search(vector<int>& nums, int target) {
for (auto &e :nums){
if (e==target)
return true;
}
return false;
}
};
。。。。。。。。。。。。。。麻了。。。。。
在4月8号,力扣又出了一道类似题
不过这道是可以进行多次旋转,并且,数组中的值并不允许重复,找出数组的最小值
链接在这
通过上面题的练习,通过分析,一般情况是,左右两段,最小值在右边那一段,直接二分。特殊情况是没有右边那一段,翻转多次正好使数组翻转回去,这时最小值为a[0],此时a[0]小于a[a.size()]。
class Solution {
public:
int findMin(vector<int>& nums) {
if (nums[0]<=nums.back()) return nums[0];
int l = 0,r = nums.size()-1;
while (l<r){
int mid = l+r>>1;
if (nums[mid]<nums[0])
r = mid;
else
l = mid+1;
}
return nums[r];
}
};
4月9号力扣每日一题还是关于旋转数组的,哈哈哈哈哈一看题,发现跟上面讲的第一题思路一样,直接把代码复制粘贴打卡成功。
4月9日每日一题
二分法例题:在D天内送达包裹的能力
剪绳子
旋转数组先介绍到这,后面慢慢更新