代码随想录刷题DAY1|二分查找(附代码)

 今天主要刷了Leetcode二分查找相关的题目704、35、34、69、367,还有双指针的27。

Leetcode:704 二分查找

数组:存放在连续内存空间上的相同类型数据的集合。

二分查找的局限性

适用于数组有序的情况,当数组中的数组无序的时候,二分查找失效。

二分查找复杂度

时间复杂度O(logn),每次查找区间折半。

空间复杂度O(1)

注意点

左闭右闭、左闭右开、右开左闭区间定义下,判断条件的改变。

基本思路

  • 先确定区间类型
  • 定义相关数据,left、right、middle
  • 写while的循环条件
  • 写二分讨论的条件
  • 输出

相关代码

C++

左闭右闭

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;//注意right在不同开闭区间-1和不减有区别
        int middle;
        while(left <= right){
            middle = (left + right ) / 2;
            if(nums[middle] > target){
                right = middle - 1;//开闭区间的判别
            }
            else if(nums[middle] < target){
                left = middle + 1;//开闭区间的判别
            }
            else{
                return middle;
            }
        } 
        return -1;//用于判断如果没有数字在上面的情况
    }
};

左闭右开

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

代码优化

 middle = (left + right) / 2;
 //可以写成下述,来防止数组溢出
 middle = left + ((right - left) / 2)

附加题 Leetcode:35 搜索插入位置

这道题在上述题目的基础上进行,要求O(logn)复杂度,因此需要采用双指针的手段。虽然说题目要求插入,但是不需要真的插入操作,只需要返回要插入位置的下标就可以了。因此只需要在上题的基础上,将不属于数组的情况输出为-1改成需要插入的地址就可以了。

相关代码

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;//注意right在不同开闭区间-1和不减有区别
        int middle;
        while(left <= right){
            middle = left + (right - left) / 2;
            if(nums[middle] > target){
                right = middle - 1;//开闭区间的判别
            }
            else if(nums[middle] < target){
                left = middle + 1;//开闭区间的判别
            }
            else{
                return middle;
            }
        }
        return left;//虽然说题目要求插入,但是不需要真的插入操作,只需要返回要插入位置的下标就可以了
    }
};

附加题 Leetcode:34 在排序数组中查找选组第一个和最后一个位置

代码随想录

中等难度,没法写出来。解题思路:先寻找左编解,然后寻找右边界。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // 情况二
        return {-1, -1};
    }
private:
     int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
};

附加题 Leetcode:69 x的平方根

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

当然这道题有时间和空间复杂度更好的解法,但是在这里确实可以尝试二分法解决问题。

时间复杂度为O(logn),空间复杂度为O(1)。只需要比较中间值的平方与目标值的大小关系,就可以通过比较的结果调整上下界。

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

附加题 Leetcode:367 有效的完全平方数

和上题类似,但是比上一题简单很多,主要在于最后输出的处理上,找到中间的数字,就输出true。

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

Leetcode:27 删除元素

因为数组在存储空间上是连续的,因此删除元素的操作在实际实现上是通过元素覆盖的方式实现。

在C++中有erase函数,其时间复杂度为O(n)

C++中string类型的erase()函数详解_c++ string erase-CSDN博客

该题的主要实现erase函数的操作。

方法一:暴力求解

复杂度O(n^2)

使用两个for循环,来暴力求解。

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++) {//用j = i样例就不过了,循环太多
                    nums[j - 1] = nums[j];
                }
                i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 此时数组的大小-1
            }
        }
        return size;

    }
};

方法二:双指针

复杂度O(n)

使用快慢指针,fast指针用于搜索比较,slow指针用于赋值覆盖。从数组第一位开始,fast指针开始比较,如果fast指针指向的数值不等于我们要删除的数值,那么我们就可以将该值赋给slow指针。直到最后循环结束,返回slow指针。

如果是前后指针,在最后输出slow的时候,要记得反向输出。

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];
                slow++;
            }
            fast++;
        }
        return slow;
    }
};

注意题干最后的输出,是向量,但是输出一个数字就会返回对应长度的向量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值