代码随想录第一天 leetcode 704、35、34、27

Leetcode 704.二分查找

  1. 左闭右闭
    不同的区间就是要对while循环中的判断条件以及right边界做出改变。
    因为是左闭右闭,所以right = nums.size() - 1,即nums[right]可以取到,且循环条件为while(left <= right),因为当right == left时进入循环时有意义的。
  2. 左闭右开
    因为是左闭右开,所以right = nums.size(),即取不到边界值,那么同理当right == left时进入循环时没有意义。

下面给出左闭右开的代码:

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

Leetcode 35.搜索插入位置

本题与704唯一不同的地方就是当target在递增数组范围内却找不到时需要插入。
需要注意的点有两个:

  1. 当元素在数组中间位置插入时,这里返回最后一次的mid值即可
  2. 当元素在数组边界位置插入时,尤其是右边界,这种情况需要增加数组的容量才能插入新的元素,所以采用左闭右开的二分法。当循环结束时返回值为原数组长度,相当于新插入的索引。

下面给出代码:

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

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

在这里插入图片描述

  1. 本题如果采用暴力解法的思路就是,先正序遍历数组找到第一个target出现的位置,再倒序遍历数组找到最后一个target出现的位置。
    下面给出代码:
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = -1;
        int right = -1;
        for(int i = 0; i < nums.size(); i++){
            if(nums[i] > target) break;
            if(nums[i] == target){
                left = i;
                break;
            }
            else continue;
        }
        for(int i = nums.size() - 1; i >= 0; i--){
            if(nums[i] < target) break;
            if(nums[i] == target){
                right = i;
                break;
            }
            else continue;
        }
        return {left,right};
    }
};
  1. 但是题目要求时间复杂度为O(log n),所以想到二分法找边界,本题使用左闭右闭的二分法。二分法思路和暴力差不多,但是在算法上做了优化,并且根据卡哥的提示,分开写左右边界的寻找,本题在细节上还是有很多需要注意的地方。
  • 首先就是确定在二分的时候,何时更新左右边界,以右边界为例。
    寻找右边界,需要用左边界不断逼近使循环结束时,left所处位置为target的下一个不等于target的元素。
    所以当target < nums[mid]时是不需要更新右边界的,因为此时不用更新二分中的左边界,同时要找到target元素的边界,在target == nums[mid]时也是要更新的。下面给出代码:
		int left = 0;
        int right = nums.size() - 1;
        int rightboarder = -2;
        while(left <= right){
            int mid = left + (right - left)/2;
            if(nums[mid] > target){
                right = mid - 1;
            }else{
                left = mid + 1;
                rightboarder = left;
            }
        }
        return rightboarder;

左边界同理,使用二分的右边界来不断逼近,使结束循环时nums[right]位于target元素的左边一个位置。最后综合两者来完成主函数。

  • 卡哥在寻找左右边界前先讨论了三种情况,这三种情况下的判断条件也是细节之一。
		int leftboarder = searchleft(nums, target);
        int rightboarder = searchright(nums, target);
        //这里是或不是与
        if(leftboarder == -2 || rightboarder == -2) return {-1,-1};
        if(rightboarder - leftboarder > 1) return {leftboarder + 1 , rightboarder - 1};
        return {-1 , -1};

结合寻找左右边界时的思路,就可以写出当target存在于数组时的边界,因为找出的边界是target两侧前一个或后一个元素,所以输出的时候需要对应的+1和-1。另外一个点就是只要左右边界有任意一个不满足条件,那么两者都不满足target在数组中的条件。

Leetcode 27.移除元素

双指针法是本题的精髓。本题相当于是对vector中erase方法的实现,其中慢指针是指向新数组中需要更新的位置,快指针指向新数组的元素。所以当遇到需要删除(覆盖)的元素时,慢指针不动,快指针继续移动;当慢指针当前指向的元素不是需要删除的元素时,慢指针正常移动。
代码如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int left = 0;
        for(int right = 0; right < nums.size(); right++){
            if(nums[right] == val) continue;
            nums[left] = nums[right];
            left++;
        }
        return left;
    }
};

额外的知识点

在二分法计算mid时,会有一个操作:mid = left + (right - left)/2,这样是防止mid值的溢出,向群里大佬学习也可以用移位操作来实现。
这里搬运记录一下:

  • 当n为负数时,>> 1/2的结果是一样的
  • 当n为负数且为偶数时,>> 1/2的结果是一样的
  • 当n为负数且为奇数时,>> 1/2的结果是不一样的
    因为奇数除以二会发生截断现象,而此时>> 1/2截断的方向不一样,导致结果不同。
    如:-5 / 2 = -(int)2.5 = -2-5 >> 1 = (1011) >> 1 = (1101) = -3
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值