代码随想录Day1-数组-二分查找:力扣704、35、34题

704. 二分查找

题目链接
代码随想录文章讲解链接

方法一:左闭右闭

思路

二分查找,设置左右边界,根据中间位置元素与目标元素的大小关系改变查找范围,右边界有意义

  • 时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度 O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;

        while (left <= right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
};

方法二:左闭右开

思路

二分查找,右边界无意义

  • 时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度 O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();

        while (left < right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid;
            } else {
                return mid;
            }
        }
        return -1;
    }
};

看完讲解的思考

一开始只想到左闭右闭的写法,看完讲解后学习到左闭右开的方法。
关键在于右区间是否有意义:
左闭右闭的右边界是位于搜索范围内的,while的判断条件是<=,且右边界的更新是等于中间索引;
左闭右开的右边界是位于搜索范围外的,while的判断条件是<,且右边界的更新是等于中间索引减一

代码实现遇到的问题


35. 搜索插入位置

题目链接
代码随想录文章讲解链接

方法一:左闭右闭

思路

二分查找

  • 时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度 O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;

        while (left <= right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                return mid;
            }
        }
        return left;
    }
};

方法二:左闭右开

思路

二分查找

  • 时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度 O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();

        while (left < right) {
            int mid = (right - left) / 2 + left;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid;
            } else {
                return mid;
            }
        }
        return left;
    }
};

看完讲解的思考

代码实现遇到的问题


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

题目链接
代码随想录文章讲解链接

方法一:二分查找结合暴力解法(自己一开始想到的)

思路

二分查找结合暴力解法,非标准解法,是一开始自己想到的做法。
使用二分查找找到某一个目标值的位置,然后从该位置向两侧分别寻找第一个目标元素的位置和最后一个目标元素的位置。
但是当数组全为同一元素时,这种方法的时间复杂度就会退化成 O ( n ) O(n) O(n),因为每个数字都要遍历一遍。

  • 时间复杂度:最好为 O ( log ⁡ n ) O(\log n) O(logn),最差情况会退化成 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int size = nums.size();
        int left = 0;
        int right = size - 1;

        if (right != -1) {
            while (left <= right) {
                int mid = (right - left) / 2 + left;
                if (nums[mid] < target) {
                    left = mid + 1;
                } else if (nums[mid] > target) {
                    right = mid - 1;
                } else {
                    right = mid;
                    left = mid;
                    while (right + 1 < size && nums[right + 1] == nums[mid]) {
                        ++right;
                    }
                    while (left - 1 > -1 && nums[left - 1] == nums[mid]) {
                        --left;
                    }
                    return {left, right};
                }
            }
        }
        return {-1, -1};
    }
};

方法二:两次二分查找

思路
  • 定义左边界为小于目标值的元素最右边的位置,若超出范围数组范围则为-2。
    例如数组[1,3,5,7]对于目标0和8的左边界为-2(-2超出1~7的范围),对于1的左边界为-1,对于5的左边界为1。

  • 定义右边界为大于目标值的元素最左边的位置,若超出范围数组范围则为-2。
    例如数组[1,3,5,7]对于目标0和8的右边界为-2(-2超出1~7的范围),对于5的右边界为3,对于7的右边界为4。

  • 此处定义的左右边界都不包括目标数字本身。

  • 使用两次二分查找,第一次二分查找寻找左边界的位置,若结果为-2,则说明目标值不在数组范围之中,直接返回[-1,-1];否则,进行第二次二分查找,寻找右边界的位置。
    如果目标值是一个位于数组范围内的数,但是是数组中不存在的数,查找到的左右边界会是相邻的值,例如[1,3,5,7]查找目标4的左右边界,结果是1和2,这种情况也是返回[-1,-1]。
    否则,左边界+1的位置即为目标数字的第一个位置,右边界-1的位置即为目标数字的最后一个位置。

C++代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = leftBorder(nums, target);
        if (left == -2) {
            return {-1, -1};
        } else {
            int right = rightBorder(nums, target);
            if (right - left > 1) {
                return {left + 1, right - 1};
            }
            return {-1, -1};
        }
    }

private:
    int leftBorder(vector<int>& nums, int target) {
        // 寻找左边界(不包含目标值)
        int left = 0;
        int right = nums.size() - 1;
        int boarder = -2;

        while (left <= right) {
            int mid = (right - left) / 2 + left;
            //若中间的值比目标值大或者相等,那么左边界一定在中间的值的左边部分
            if (nums[mid] >= target) {
                right = mid - 1;
                boarder = right;
            } else {
                left = mid + 1;
            }
        }
        return boarder;
    }

    int rightBorder(vector<int>& nums, int target) {
        // 寻找右边界(不包含目标值)
        int left = 0;
        int right = nums.size() - 1;
        int boarder = -2;

        while (left <= right) {
            int mid = (right - left) / 2 + left;
            //若中间的值比目标值小或者相等,那么右边界一定在中间的值的右边部分
            if (nums[mid] <= target) {
                left = mid + 1;
                boarder = left;
            } else {
                right = mid - 1;
            }
        }
        return boarder;
    }
};

看完讲解的思考

  • 遇到难题注意问题的拆分,本题就可以把问题拆分成找左边界和右边界。
  • 因为数组不是严格递增的,使用二分查找代码逻辑与一般的二分查找不同,只有两种条件的判断,注意想清楚逻辑。

代码实现遇到的问题

  • 使用二分查找寻找边界的代码实现时不正确,一开始没有搞清楚边界值的含义

最后的碎碎念

25届毕业生对这严峻的就业环境开始焦虑,今天是开始刷代码随想录的第一天,也是小白刚开始尝试写博客,感觉写博客真的能提升对问题的理解,把思路理清楚。希望能早日一刷完且能坚持写博客,猪扒加油加油~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值