leetcode刷题之二分查找

第一个错误的版本

在这里插入图片描述

参考代码
//使用查到第一个满足某条件的模板
// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        int left=1,right=n;
        while(left<right){
            long long mid=left+(right-left)/2;
            if(isBadVersion(mid))
                right=mid;
            else
                left=mid+1;
        }
        return left;
    }
};
两个数组的交集II

在这里插入图片描述

参考代码
常规解法(哈希表)
class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        if(nums1.size()==0||nums2.size()==0) return ret;
        map<int ,int > mmap;
        for(int x:nums1){
            if(mmap.find(x)==mmap.end()){
                mmap.insert(make_pair(x,1));
            }
            else{
                mmap[x]++;
            }
        }
        for(int x:nums2){
            if(mmap.find(x)!=mmap.end()){
                if(mmap[x]!=0){
                    ret.push_back(x);
                    mmap[x]--;
                }
            }
        }
        return ret;
    }
};
进阶解法

1.两个数组排序
2.设定两个指向这两个数组头部的指针,比较两个指针的元素是否相等
3.如果相等,元素push到返回值里,两个指针同时往前
4.如果不相等,元素小的指针往前(因为大元素可能在小元素数组里存在,但是小元素在大元素所在数组肯定不存在。因为已经排过序了。)

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        if(nums1.size()==0||nums2.size()==0) return ret;
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int p=0;
        int q=0;
        while(p<nums1.size()&&q<nums2.size()){
            if(nums1[p]==nums2[q]){
                ret.push_back(nums1[p]);
                p++;
                q++;
            }else if(nums1[p]<nums2[q]){
                p++;
            }else{
                q++;
            }
        }
        return ret;
    }
};

常规解法的时间和空间复杂度都是O(n)
进阶解法的时间复杂度是O(nlogn),空间复杂度是O(1)
进阶问题:
1.若给定的数组排好序了,则使用进阶解法
2.若nums1的大小比nums小很多,先排序,再查找
3.先将nums1的元素存在map中,再遍历nums2进行查找

扩充知识

在这里插入图片描述

判断子序列

在这里插入图片描述

参考代码
class Solution {
public:
    bool isSubsequence(string s, string t) {
        if(s.size()==0) return true;
        int a=0;
        for(int i=0;i<t.size();i++){
            if(s[a]==t[i])
                a++;
            if(a==s.size()) return true;
        }
        return false;
    }
};
后继挑战

后续挑战,输入量大,小写字母创建25的二维数组,存储t的坐标,这样就可以把s的判断直接转为坐标的判断,
dp[0]代表了存储了a出现在t的所有的位置,逐个字符判断s的字符顺序是否在t内,直接返回结果。
时间复杂度O(t.size()+2000):分别为创建数组需要O(t.size()),
索引是递增的使用二分查找s的单个字符20次之内就可找到需要O(100* 20)。
适用大量的输入判断子序列。

bool isSubsequence(string s, string t) {        
    vector<vector<int>>dp(26);
    int tag=-1;//tag记录s中上一个字符在t中的位置
    for(int i=0;i<t.size();i++)
        dp[t[i]-'a'].push_back(i);
    for(int i=0;i<s.size();i++){
        int now=s[i]-'a';
        int left=0,right=dp[now].size()-1;
        //在t中找字符s[i],这个字符是第一个位置大于tag的
        while(left<right){
            int mid=(left+right)/2;
            if(dp[now][mid]>tag)
                right=mid;
            else
                left=mid+1;
        }
        //如果right<left说明dp[now].size()==0
        //dp[now][left]<tag已经遍历到了dp[now].size()-1那个元素
        if(right<left || dp[now][left]<tag)return false;
        tag=dp[now][left];
    
    }
    return true;
}
供暖器

在这里插入图片描述

参考代码
//解题思路:
//遍历所有House,记录当前house距离最近heater的距离,最后所有House的距离中的最大值即为所求
class Solution {
public:
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(houses.begin(),houses.end());
        sort(heaters.begin(),heaters.end());
        int maxDis=0;
        for(int c:houses){
            //找到第一个大于等于c的heater
            int left=0,right=heaters.size();
            while(left<right){
                long long mid=left+(right-left)/2;
                if(heaters[mid]>=c)
                    right=mid;
                else
                    left=mid+1;
            }
            //说明没有比C位置更大的heater
            if(left==heaters.size()) maxDis=max(maxDis,(c-heaters[left-1]));
            else if(heaters[left]==c) continue;
            else if(left==0) maxDis=max(maxDis,heaters[left]-c);
            else maxDis=max(maxDis,min(heaters[left]-c,c-heaters[left-1]));
        }
        return maxDis;
    }
};
山脉数组的峰顶索引

在这里插入图片描述

参考代码
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& A) {
        int left=0,right=A.size()-1;
        if(A.size()<=2) return -1;
        while(left<=right){
            long long mid=left+(right-left)/2;
            if(A[mid]>A[mid-1]&&A[mid]>A[mid+1]) return mid;
            else if(A[mid]>A[mid-1])
                left=mid+1;
            else
                right=mid-1;
        }
        return -1;
    }
};
搜索旋转排序数组

在这里插入图片描述

参考代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left<=right){
            long long mid=left+(right-left)/2;
            if(nums[mid]==target) return mid;
            else if(nums[mid]>=nums[left]){
                //如果前半部分有序,注意是大于等于,当等于时mid==left,此时是一个元素,也认为有序,否则会出错
                if(nums[left]<=target&&target<nums[mid])
                    right=mid-1;
                else
                    left=mid+1;
            }else{
            //后半部分有序
                if(nums[mid]<target&&target<=nums[right])
                    left=mid+1;
                else
                    right=mid-1;
            }
        }
        return -1;
    }
};
搜索旋转排序数组 II

在这里插入图片描述

参考代码
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(nums.size()==0) return false;
        int left=0,right=nums.size()-1;
        while(left<=right){
            long long mid=left+(right-left)/2;
            if(nums[mid]==target) return true;
            else if(nums[mid]==nums[left]&&nums[mid]==nums[right]){//中间与两边的值相等时,分不清前面有序还是后面有序
                left=left+1;
                right=right-1;
            }
            else if(nums[mid]>=nums[left]){
                if(nums[mid]>target&&target>=nums[left])
                    right=mid-1;
                else
                    left=mid+1;
            }else if(nums[mid]<nums[left]){
                if(nums[mid]<target&&target<=nums[right])
                    left=mid+1;
                else
                    right=mid-1;
            }
        }
        return false;
    }
};
pow(x,n)

在这里插入图片描述

参考代码
class Solution {
public:
    double myPow(double x, int n) {
        if(n==0) return 1;
        long long N=n;
        if(n<0){
            x=1/x;
            N=-N;//不能写成N=-n,因为若n=-x^31,此时会越界
        }
        double ret=1,base=x;
        while(N){
            if(N&1){//若N为奇数
                ret*=base;
            }
            base*=base;
            N>>=1;//N右移一位
        }
        return ret;
    }
};
搜索二维矩阵

在这里插入图片描述

参考代码
//将二维矩阵展开进行二分查找
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0) return false;
        int n=matrix.size();
        int m=matrix[0].size();
        int left=0,right=n*m-1;
        while(left<=right){
            long long mid=left+(right-left)/2;
            int row=mid/m,col=mid%m;
            if(matrix[row][col]==target) return true;
            else if(matrix[row][col]>target)
                right=mid-1;
            else
                left=mid+1;
        }
        return false;
    }
};
寻找旋转排序数组中的最小值

在这里插入图片描述

参考代码
class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size()==1) return nums[0];
        int left=0,right=nums.size()-1;
        if(nums[left]<nums[right]) return nums[left];
        while(left<=right){
            long long mid=left+(right-left)/2;
            if(nums[mid]>nums[mid+1])   //因为mid取得是左中位数,所以mid+1一定不会越界
                return nums[mid+1];
            if(nums[mid]<nums[mid-1])   //放在第二个判断,一定不能放在第一个判断,因为不能保证mid-1>=0,而当只有两个元素是会出现mid-1=-1的情况,此时第一个判断条件可以直接得到结果,不会进入该判断
                return nums[mid];
            if(nums[mid]>nums[0])
                left=mid+1;
            else
                right=mid-1;
        }
        return nums[left];
    }
};
寻找峰值(?)

在这里插入图片描述

参考代码
//解题关键:只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值
class Solution {
public:
    int findPeakElement(vector<int>& nums) {
        int left=0,right=nums.size()-1;
        while(left<right){
            long long mid=left+(right-left)/2;
            if(nums[mid]>nums[mid+1])
                right=mid;
            else
                left=mid+1;
        }
        return left;
    }
};
长度最小的子数组

在这里插入图片描述

参考代码(双指针)
//定义左右指针p,q
//若[p,q]区间内的元素和大于等于s,则p++
//若[p,q]区间内的元素和小于s,则q++
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if(nums.size()==0) return 0;
        int sum=nums[0];
        int len=INT_MAX;
        int p=0,q=0;
        while(true){
            if(sum>=s)
            {
                len=min(len,(q-p)+1);
                sum-=nums[p];
                p++;
                if(p>=nums.size()) break;
            }else{
                q++;
                if(q>=nums.size()) break;
                sum+=nums[q];
            }
        }
        return len==INT_MAX?0:len;
    }
};
搜索二维矩阵 II

在这里插入图片描述

参考代码
解法一(排除法)
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0||matrix[0].size()==0)
            return false;
        int row=0;
        int col=matrix[0].size()-1;
        while(row<matrix.size()&&col>=0){
            if(matrix[row][col]<target)
                row++;
            else if(matrix[row][col]>target)
                col--;
            else
                return true;
        }
        return false;
    }
};
解法二:递归二分法
//解题思路:我们可以将已排序的二维矩阵划分为四个子矩阵,其中两个可能包含目标,其中两个肯定不包含
//我们沿着索引行的矩阵中间列,则目标值要么在中间列上,要么在左上和右下举矩阵中

H指数II

在这里插入图片描述

参考代码
//套用模板查找第一个满足条件的位置,注意要考虑不存在的情况
class Solution {
public:
    int hIndex(vector<int>& citations) {
        if(citations.size()==0) return 0;
        if(citations.size()==1&&citations[0]) return 1;
        else if(citations.size()==1&&citations[0]==0) return 0;
        int left=0,right=citations.size()-1;
        while(left<right){
            long long mid=left+(right-left)/2;
            int num=citations.size()-mid;
            if(citations[mid]>=num)
                right=mid;
            else
                left=mid+1;
        }
        int ret=citations.size()-left;
        return (ret<=citations[left])?ret:0;
    }
};
寻找重复数

在这里插入图片描述

参考代码
哈希表法
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        unordered_map<int,int> mmap;
        for(int x:nums){
            if(mmap.find(x)==mmap.end()){
                mmap.insert(make_pair(x,1));
            }else{
                mmap[x]++;
                if(mmap[x]>1) return x;
            }
        }
        return -1;
    }
};
二分法

在这里插入图片描述

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        //这里的left,right并不是索引下标,而是nums的取值范围[left,right],初始时的取值范围是[1,n]
        int left=1,right=nums.size()-1;
        while(left<right){
            //这里的mid是指中位数,不是索引
            long long mid=left+(right-left)/2;
            int counter=0;
            //统计有多少个小于等于mid的数,若没有重复的数,则小于等于mid的数应当有mid个
            for(int num:nums)
                if(num<=mid)
                    counter++;
            if(counter>mid)
                right=mid;
            else
                left=mid+1;
        }
        return left;
    }
};
有序矩阵中第k小的元素

在这里插入图片描述

参考代码

解题思路:
首先考虑一维数组的情况:
[1,3,2,5,4,6,8,9] k=3
首先计算出中位数(1+9)/2=5,统计小于等于5的个数为5>=3,所以目标值在[1,5]范围内
再计算中位数(1+5)/2=3,同理统计个数有3>=3,所以目标值在[1,3]内
再计算中位数(1+3)/2=2,统计个数有2<3,所以目标值在[3,3],此时只有一个数,所以目标值就是3

二维数组同理,有序二维矩阵的最小值为最左上角的数,最大值为右下角的数

class Solution {
public:
    int kthSmallest(vector<vector<int>>& matrix, int k) {
        int len=matrix.size();
        int left=matrix[0][0],right=matrix[len-1][len-1];
        while(left<right){
            long long mid=left+(right-left)/2;
            //统计小于等于mid的个数
            int j=matrix[0].size()-1;
            int counter=0;
            for(int i=0;i<len;i++){
                while(j>=0&&matrix[i][j]>mid) j--;
                counter+=(j+1);
            }

            if(counter>=k)
                right=mid;
            else
                left=mid+1;
        }
        return left;
    }
};

疑问:如何保证每次迭代求得的mid一定在有序矩阵中

总结

1.使用二分模板查找满足某条件的元素时,若该元素不一定存在,一定要对边界情况进行讨论。
2.对于一些找目标值的问题,若给出的数组中数的取值范围确定,则可以考虑对数的值的二分查找,而不是对索引的二分查找,比如寻找重复数问题,有序矩阵中第k小的元素
3.二分法的使用需要数据是有序的,同时在编写的时候一定要避免出现死循环

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值