leetcode 35
参考 weiwei 哥的leetcode题解 总结一下 二分题目类型
关键思想 (排除法):把待搜索的目标值留在最后判断,在循环体内不断地把不符合题目要求的子区间排除掉,在退出循环以后,因为只剩下 1 个数没有看到,它要么是目标元素,要么不是目标元素,单独判断即可
在判断是选择 > 或者< 号时 一定确保 区间内 一定不存在 target 元素 才能 舍弃这部分区间
-
思路 : 将区间分为 两部分 一部分为一定不存在目标元素,另一部分可能存在目标元素,根据mid 分在 左右区间 分成两种情况。
-
一般步骤
1 终止条件 严格写成left< right (退出时 left=right)while(left<right)
2 根据边界收缩行为 ,修改取中间的行为
int mid= left+(right-left)/2 (下取整 无法取到有边界)
3 退出时 判断是否需要 进一步检查 left 是否满足题目的要求
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
const int N=nums.size();
if(N==0) return 0;
int l=0;
int r=N;
//if(target>nums[r]) return N;
//因为有可能数组的最后一个元素的位置的下一个是我们要找的,故右边界是 len
while(l<r)
{
int mid=(l+r)/2;
if(target>nums[mid]){
l=mid+1;
}else{
r=mid;
}
}
return l;
}
};
特别注意的是 right 边界的问题 的选择 取不到 right 元素 ,可以返回 right 下标
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
思考题目中关键点
首先确认左边界
[5,7,7,8,8,10] 当 mid 为 8 时 它之后的 8 一定不是 左边界(此处比较绕 需要思考一下),根据排除法则 确定不在此区间内的元素情况。
target 不在目标区间内
if(num[mid]<target) 下一次搜索区间为[mid+1,right]
left=mid+1
此时只需要考虑对立区间就行
else {
right=mid [left,mid]
}
并没有漏掉区间中的任何一个元素
确认有边界
[5,7,7,8,8,10] 当 mid 为 8 时 它之前的 8 一定不是 右边界
所以只需要向右收缩就好了 (==向左扩展)
区间没有 8 的时候
target 不在区间内
if(num[mid]>target) // 下次搜索区间 [left ,mid-1]
right=mid-1
else left=mid // 此时注意 超时问题
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res(2);
int left=0;
if(nums.empty())
{
res[0]=-1;
res[1]=-1;
return res;
}
int right=nums.size()-1;
// 不是返回要插入数组的下标不用对 r进行考虑
while(left<right){
int mid=left+(right-left)/2;
// 通过排除 没有元素的区间来确定元素 区间
// 给定目标值 在数组开始的位置
if(nums[mid]<target){
left=mid+1; //[mid+1, right]
}else {
right=mid; //[left, mid]
}
}
if(nums[left]==target)
res[0]=left;
else {
res[0]=-1;
res[1]=-1;
return res;
}
left=0,right=nums.size()-1;
while(left<right){
int mid=left+(right-left+1)/2;
// 若此区间 完全不含有 target
if(nums[mid]>target){
right=mid-1; // 下一次搜索区间 [left mid-1]
}else {
left=mid; // [mid, right] 特别注left=mid 发生死循环
}
cout<<left;
}
res[1]=left;
return res;
}
};
class Solution {
public:
// 在数组中找到一个数num[i ] 使得这个数 =num.len-i;
// 看nums[mid] 和区间[mid,len-1]得长度 既 len-1-mid+1
// 返回nums 中得值
int hIndex(vector<int>& citations) {
int len=citations.size();
int left=0;
int right=len-1;
// 此种情况因为是存在得
if(len==0||citations[len-1]==0) return 0;
while(left<right){
int mid=(left+right)/2;
// 比长度小,就得去掉这个值
if(citations[mid]<len-mid) {
left=mid+1;
}else{
// 比长度大是满足 区间得,我们应该继续rangmid 向左走 尝试看有没有跟小得 mid
// 类似求 含有重复元素得第一个值 区间需要向左收缩
right=mid;
}
}
// 返回区间长度
return len-left;
}
};
暴力算法有问题目前没想到那里有问题
class Solution {
public:
int hIndex(vector<int>& citations) {
// 采用暴力方法
// 有序得找第一个 num[i] 且len-i = num[i];
int res=0;
int N=citations.size();
// end() 指向最后一份元素得下一个元素
if(N==0||citations.back()==0) return 0;
for(int i=0;i<=N-1;i++)
{
if(citations[i]==N-i)
{
return citations[i];
}
}
return 1;
}
};
leetcode69 x 得平凡根
class Solution {
public:
int mySqrt(int x) {
if(x<2) return x;
long long i=0;
long long j=x/2;
while(i<=j){
long long mid =(i+j)/2;
long long res=mid*mid;
if(res==x) return mid;
if(res<x) {
i=mid+1;
}else{
j=mid-1;
}
}
return j; //返回 i 会报错 int8 8 通不过
//return j时说明while里没有找到符合的mid 此时i大于j 要取小的
}
};
/继续使用万能模板
判断去掉中位数的分支
如果midmid<x,不可以去掉当前mid,举例来说,8的平方根是2,而22=4,就小于8,但不能去掉当前的mid值
2) 所以如果midmid>x,就可以去掉当前mid,以8举例,假如mid=3,33=9,肯定不是平方根,可以去掉当前的mid值
(3)
所以这样在不去掉中位数的分支就会是left=mid,这样当只剩下两个数字的时候,当mid为左边界时,且会进入不去掉中位数得分支时,这样left是不会变的,就会进入死循环,所以我们取得mid要用右中位数,让她只有两个数字的时候中位数取右边。
这样左边界就取到右边得数,此时left=right,就会跳出循环了。