Day01: 704.二分查找、 27.移除元素

文章目录


前言

关于二分法以及一维数组元素移除的一些知识点总结。


704.二分法

使用前提

  • 有序数组
  • 数组元素不重复
  • 注意区间

力扣题目链接

二分法区间分类

1.左闭右闭区间
  1. 循环条件left <= right允许成立
  2. 当left>mid,left == mid+1;当right<mid,right == mid-1
  3. right==num.size()-1
2.左闭右开区间
  1. 循环条件left < right
  2. 当left>mid,left == mid+1;当eight<mid,right == mid
  3. right==num.size()

注:最好用mid == left+(right-left)/2,防止整型溢出。

本题代码实现

1. 左闭右闭法

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        //左闭右闭
        while(left<=right){
            int mid=(right-left)/2+left;
            if(nums[mid]>target)right=mid-1;
            else if(nums[mid]<target)left=mid+1;
            else return mid;
        }
        return -1;
    }
};

2. 左闭右开        

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size();
        //左闭右开
        while(left<right){
            int mid=(right-left)/2+left;
            if(nums[mid]>target)right=mid;
            else if(nums[mid]<target)left=mid+1;
            else return mid;
        }
        return -1;
    }
};

其他题目

1. 值查找

35.搜索插入位置

下面是我的第一思路,就是直接把插入元素情况分成:1.小于最小 、2.大于最大、3.可以插入在数组内

情况1和情况2很简单,就直接插入在头或尾;

情况3我又细分成了下面三种情况:

  • 当nums[left]>nums[mid],在这种情况下,有可能nums[mid-1]<target,这时直接就找到了插入位置,否则left=mid+1;
  • 当nums[left]<nums[mid],有可能nums[mid+1]>target,这时也直接就找到了插入位置,right=mid-1;
  • 当nums[left]=nums[mid],那么就不需要操作,直接返回mid就好了。

而最后要写一个return 0是函数的硬性要求,其实插入情况已经包含在前面了,这个return不管返回的是什么就满足题意

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        if(nums[0]>target)return 0;
        if(nums[nums.size()-1]<target)return nums.size();
        //左闭右闭
        while(left<=right){
            int mid=(right-left)/2+left;
            if(nums[mid]>target){
                if(nums[mid-1]<target){
                    return mid;
                }
                right=mid-1;
            }
            else if(nums[mid]<target){
                if(nums[mid+1]>target)return mid+1;
                left=mid+1;
            }
            else return mid;
        }
        return 0;
    }
};

不过还有一种更简单的方法就是直接查找,如果真的有nums[mid]==target,就返回下标;

否则就是没有找到和target相同的元素,这时跳出while循环后,right肯定刚好小于left,right的下标则为刚好小于target的最大值,返回right+1即可。

367.有效的完全平方数

题目是查找一个整数值,用二分模板即可解决

class Solution {
public:
    bool isPerfectSquare(int x) {
        int left=0,right=x,ans;
        while(left<=right){
            int mid=(right-left)/2+left;
            if((long long)mid*mid<x){
                ans=mid;
                left=mid+1;
            }
            else if((long long)mid*mid>x){
                right=mid-1;
            }
            else return true;
        }
        return false;
    }
};

2. 边界查找 

34.在排序数组中查找元素的第一个位置和最后一个位置

查找左边界时,当nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界

查找右边界时,当nums[mid] == target 时不要立即返回,而要收紧左侧边界以锁定右侧边界

下面的函数都是以查找左边界为准,所以在查找target右边界时选择传入target+1,这样只需要找到target+1的左边界,其值减1则是target的右边界。

class Solution { 
public:
    int binary_search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] >= target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return right + 1;
    }

    vector<int> searchRange(vector<int>& nums, int target) {
        int left_idx = binary_search(nums, target);              // 找到左边界
        int right_idx = binary_search(nums, target + 1) - 1;     // 找到下一个数的左边界后减1
        if (left_idx <= right_idx) {                             // 只要left <= right 就合法
            return vector<int>{left_idx, right_idx};
        } 
        return vector<int>{-1, -1};
    }
};

69.x的平方根

因为题目要求省略小数点位数,题目也就相当于找左边界,和上一题一样

class Solution {
public:
    int mySqrt(int x) {
        int left=0,right=x,ans;
        while(left<=right){
            int mid=(right-left)/2+left;
            if((long long)mid*mid<=x){
                ans=mid;
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }
        return ans;
    }
};

27.移除元素

力扣题目链接

注意:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。

覆盖方法分类

1.暴力法

两个for循环,一个查找,一个覆盖

2.双指针法

同向指针:一个快指针查找,一个慢指针覆盖

双向指针 (不支持原地覆盖):因为原地覆盖之后让元素减少或不变,不需要删除的元素整体是往前移动的,所以双向移动,从左往右走的指针用来找不需要删除原地留下的元素,当读到需要删除的地方就可以停下,再让从右往左走的指针查找不需要删除的元素,读取成功后让右指针查询带的元素覆盖左指针需要删除的元素,依次类推。

本题代码实现

1.暴力法

// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位
                for (int j = i + 1; j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 此时数组的大小-1
            }
        }
        return size;

    }
};

2. 单向指针 

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int fast=0,slow=0;
        while(fast!=nums.size()){
            if(nums[fast]!=val)nums[slow++]=nums[fast];
            fast++;
        }
        return slow;
    }
};

3. 双向指针(不支持本题,注意理解思路)

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left= 0;
        int right=nums.size()-1;
        while (left<=right) {
            // 找左边等于val的元素
            while (left<=right&& nums[left]!=val){
                ++left;
            }
            // 找右边不等于val的元素
            while (left<=right&& nums[right]==val) {
                --right;
            }
            // 将右边不等于val的元素覆盖左边等于val的元素
            if (left<right) {
                nums[left++] = nums[right--];
            }
        }
        return left;   // left一定指向了最终数组末尾的下一个元素
    }
};

其他题目

26.删除有序数组中的重复项

class Solution {

public:

    int removeDuplicates(vector<int>& nums) {

        int slow=0;

        for(int i=0;i<nums.size();i++){

            if(i>0&&nums[i]==nums[i-1])continue;

            else nums[slow++]=nums[i];

        }

        return slow;

    }

};

283.移动零

class Solution {

public:

    void moveZeroes(vector<int>& nums) {

        int slow=0,flag=0,fast=0;

        for(;fast<nums.size();fast++){

            if(nums[fast]==0)continue;

            else nums[slow++]=nums[fast];

        }

        for(int i=slow;i<fast;i++){

            nums[i]=0;

        }

    }

};

844.比较含退格的字符串

class Solution {

public:

    bool backspaceCompare(string s, string t) {

        int slows=0,slowy=0;

        for(int fast=0;fast<s.size();fast++){

            if(s[fast]=='#'){

                if(slows>0)slows--;

                else continue;

            }

            else s[slows++]=s[fast];

        }

        for(int fast=0;fast<t.size();fast++){

            if(t[fast]=='#'){

                if(slowy>0)slowy--;

                else continue;

            }

            else t[slowy++]=t[fast];

        }

        if(slows!=slowy)return false;

        while(slows){

            slows--;slowy--;

            if(s[slows]!=t[slowy])return false;

        }

        return true;

    }

};


总结

一刷:因为昨天已经刷过这两道题了,没有记录时间。不仅是第一次写博客,更是第一次下定决心要努力改变自己,坚持坚持坚持

二刷:修改完善了一下博客,复习了之前学的东西,整体感觉还不错,继续加油。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值