Day75 | 灵神 | 二分查找 寻找旋转排序数组中的最小值 搜索旋转排序数组
153.寻找旋转排序数组中的最小值
153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
思路:
思路就是如何判断nums[mid]是在最小值的左侧还是右侧
这道题第一次做还是没啥思路,看到灵神说和最后一个数a进行比较就懂了
不管旋转没有旋转,只要nums[mid]<=a,那说明最小值肯定在[l,mid]之中
只要nums[mid]>a,那说明最小值肯定在[mid,r)之中
具体看灵神怎么说:
我们需要判断 x 和数组最小值的位置关系,谁在左边,谁在右边?
把 x 与最后一个数 nums[n−1] 比大小:
如果 x>nums[n−1]
,那么可以推出以下结论:
1.nums 一定被分成左右两个递增段;
2.第一段的所有元素均大于第二段的所有元素;
3.x 在第一段。
4.最小值在第二段。
所以 x 一定在最小值的左边。
如果 x≤nums[n−1]
,那么 x 一定在第二段。(或者 nums 就是递增数组,此时只有一段。)
x 要么是最小值,要么在最小值右边。
所以,只需要比较 x 和 nums[n−1]
的大小关系,就间接地知道了 x 和数组最小值的位置关系,从而不断地缩小数组最小值所在位置的范围,二分找到数组最小值。
完整代码:
笔者写的左闭右开区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public: int findMin(vector<int>& nums) { int l=0,r=nums.size(); int num=nums[r-1]; while(l<r) { int mid=l+(r-l)/2; if(nums[mid]<=num) r=mid; else l=mid+1; } return nums[l]; } };
|
33.搜索旋转排序数组
33. 搜索旋转排序数组 - 力扣(LeetCode)
思路:
如果笔者直接做这个题那可能就蒙圈了,但是笔者是做了153才来做的
所以一下就想到找最小值然后分成两段再进行两次二分这个思路
直接开干得到如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class Solution { public: int findMin(vector<int>& nums) { int l=0,r=nums.size(); int num=nums[r-1]; while(l<r) { int mid=l+(r-l)/2; if(nums[mid]<=num) r=mid; else l=mid+1; } return r; } int b_search(vector<int>& nums, int l,int r,int target) { while(l<r) { int mid=l+(r-l)/2; if(target<=nums[mid]) r=mid; else l=mid+1; } return (l<nums.size()&&nums[l]==target)?l:-1; } int search(vector<int>& nums, int target) { int l=findMin(nums); if(l!=0) { int a=b_search(nums,l,nums.size(),target); int b=b_search(nums,0,l,target); return a!=-1?a:b; } else return b_search(nums,0,nums.size(),target); } };
|
需要注意我在二分查找target中写的l和nums.size()的关系判断,因为我用的是左闭右开区间,所以如果最后没找到target的话,并且数组大小为1,那最后l==r==nums.size()
了,就会越界
而灵神的判断比我更加精巧,只用了两次二分,一次查找最小,一次找target
1 2 3
| if(target>nums.back()) return b_search(nums,0,index,target); return b_search(nums,index,nums.size(),target);
|
target>nums.back()
,如果数组只有一段,那说明数组中没有target,传入的其实是l=0,r=0
如果数组有两段,第一段的所有数字都大于第二段,那target肯定就是在第一段或者不存在,那就是[0,index)
target<=nums.back()
,如果数组只有一段,那肯定数组中有target,传入的其实实际上是l=0,r=nums.size()
,即搜索整个数组,如果数组有两段,第一段的所有数字都大于第二段,所以target肯定在第二段,传入的就是l=index,r=nums.size()
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Solution { public: int b_search(vector<int>& nums, int l,int r,int target) { while(l<r) { int mid=l+(r-l)/2; if(target<=nums[mid]) r=mid; else l=mid+1; } return nums[l]==target?l:-1; } int findMin(vector<int>& nums) { int l=0,r=nums.size(); int num=nums[r-1]; while(l<r) { int mid=l+(r-l)/2; if(nums[mid]<=num) r=mid; else l=mid+1; } return r; } int search(vector<int>& nums, int target) { int index=findMin(nums); if(target>nums.back()) return b_search(nums,0,index,target); return b_search(nums,index,nums.size(),target); } };
|