赞
踩
目录
定义两个指针:left指向数组第一个元素,right指向数组的最后一个元素;通过某种条件的判断,让left与right向之间靠拢;最终left与right所指的元素来确定最后的答案
二分算法的实现有两个模板:朴素二分算法与非朴素二分算法
- while(left <= right)
- {
- int middle = left+(right-left)/2 //写成(right-left)/2有越界的风险
- if(...)
- {
- left =middle + 1;
- }
- else
- {
- right = middle - 1;
- }
-
- }
- //左端点
- while(left < right) //left <= right会越界
- {
- int middle = left + (right - left) / 2;//left +(right - left + 1) /2会死循环
- if(...)
- {
- left = middle + 1;
- }
- else
- {
- right = middle;
- }
-
- }
-
- //右端点
- while(left < right) //left <= right会越界
- {
- int middle = left + (right - left +1) / 2;
- if(...)
- {
- left = middle;
- }
- else
- {
- right = middle - 1;
- }
-
- }
大部分情况的二分算法题用的都是非朴素二分算法模板来实现的!
oj链接:二分查找
思路:懂了模板直接套用朴素二分算法解决:
当nums[middle] < target ; left=middle + 1;else right = middle - 1
- class Solution {
- public:
- int search(vector<int>& nums, int target) {
- int left=0,right=nums.size()-1;
- while(left<=right)
- {
- int middle=left+(right-left)/2;//防溢出
- if(nums[middle]<target)
- {
- left=middle+1;
- }
- else if(nums[middle]>target)
- {
- right=middle-1;
- }
- else
- {
- return middle;
- }
- }
- return -1;
- }
- };
朴素二分算法解决不了,用非朴素二分模板来解决:
题目要求第一个与最后一个位置,直接用两个非朴素模板求出与target相同的左右两个端点
- class Solution
- {
- public:
- vector<int> searchRange(vector<int>& nums, int target)
- {
- //越界判断
- if(nums.size() == 0) return {-1 , -1};
- //开始元素下标(求左端点)
- vector<int> ans(2);
- int left = 0 , right = nums.size() - 1;
- while(left < right)
- {
- int middle = left + (right - left) / 2;
- if(nums[middle] < target) left = middle + 1;
- else right = middle;
- }
- //有可能没有target
- if(nums[left] != target) return {-1 , -1};
- ans[0] = left;
- //最后元素下标(求右端点)
- //有的话在left的右边,left不用再次走到开始位置
- right = nums.size() -1;
- while(left < right)
- {
- int middle = left +(right - left +1) /2;
- if(nums[middle] <= target) left = middle;
- else right = middle -1;
- }
- ans[1] = right;
- return ans;
- }
- };
oj链接:点名
思路:此题的思路有很多:等差数列求和,哈希表,异或...
但这道题给出的条件适合来用二分算法解决:
判断数值与下标是否对应来进行left与right的移动
- class Solution {
- public:
- int takeAttendance(vector<int>& records)
- {
- int left=0,right=records.size()-1;
- while(left<right)
- {
- int middle=left+(right-left)/2;
- if(middle==records[middle]) left=middle+1;
- else right=middle;
- }
- //有可能数组里面与下标都对应上了返回后一个数字->[0,1]
- if(records[left]==left) return left+1;
- return left;
- }
- };
oj链接:x 的平方根
思路:从[1,x]中来找出符合某数的平方根<=x:如果在里面的某个值的平方根>x的,说明目标值是在这个值的左边区域,缩小范围更新right的值:是要等于它还是在它前一个位置就要自己来画图分析了;最后用非朴素模板套进去就解决本道题的求
oj链接:搜索插入位置
思路: 套用非朴素模板解决,注意最后结果的处理
- class Solution
- {
- public:
- int searchInsert(vector<int>& nums, int target)
- {
- int left = 0,right = nums.size() - 1;
- while(left < right)
- {
- int middle = left +(right -left)/2;
- if(nums[middle] < target) left = middle + 1;
- else right = middle;
- }
- //left与right走到数组最后的处理
- if(right == nums.size() - 1 && nums[right] < target) return right + 1;
- else return right;
- }
- };
oj链接:山脉数组的峰顶索引
思路:山脉数组将数组分为两个区间:
左半边是递增的:arr[i] > arr[i-1](包含峰值);右半边是递减的:arr[i] < arr[i-1](不包含)
有了这个二段性,我们就可以来利用二分算法来解决问题,使得最后left与right指向峰顶
- class Solution {
- public:
- int peakIndexInMountainArray(vector<int>& arr)
- {
- int left = 0,right = arr.size() - 1;
- while(left < right)
- {
- int middle = left + (right - left + 1) / 2;
- //middle在左半区间
- if(arr[middle] > arr[middle - 1]) left = middle;
- //middle在右半区间
- else if(arr[middle] < arr[middle -1]) right = middle -1;
- }
- return left;
- }
- };
oj链接:寻找峰值
思路:要找数组其中一个峰值转化为求数组的峰值,与上面思路是一样的!
- class Solution {
- public:
- int findPeakElement(vector<int>& nums)
- {
- int left = 0,right =nums.size() - 1;
- while(left < right)
- {
- int middle = left + (right - left) / 2;
- if(nums[middle] < nums[middle+1]) left = middle + 1;
- else if(nums[middle] > nums[middle + 1]) right = middle;
- }
- return left;
- }
- };
oj链接:寻找旋转排序数组中的最小值
思路: 将旋转数组分为两段;AB段前n(旋转的次数)个数值;CD是剩余的个数;我们来拿最后一个元素(nums[n - 1])作参照物,有这样的一个规律:
AB段的值nums[i]一定>nums[n - 1];CD段的值nums[j]<=nums[n - 1];
有了二段性,我们就可以用二分算法来解决问题,只需来判断left与right的移动就完成
- class Solution {
- public:
- int findMin(vector<int>& nums)
- {
-
- int left = 0,right = nums.size() - 1;
- while(left < right)
- {
- int middle = left + (right - left) / 2;
-
- if(nums[middle]>nums[nums.size()-1]) left=middle+1;
- else right=middle;
- }
- return nums[right];
- }
- };
那上面的参照物能用最左边的数来解决吗?好像也类似?
问题转换为能过测试用例[1,2]和[2,1]来思考!(坑帮你们填了)QAQ
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。