代码随想录算法训练营第一天|704.二分查找、27.移除元素

lc704. 二分查找

class Solution {
public:
    int search(vector& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; //定义target的区间左闭右闭,[left,right]
        while (left <= right){
            int middle = left + ((right - left) / 2); //防止溢出
            if (nums[middle] > target){
                right = middle - 1;
            }
            else if (nums[middle] < target){
                left = middle + 1;
            }
            else{
                return middle;
            }
        }
        //未找到目标值
         return -1;
    }
};

二分查找主要是要注意自己的代码是左闭右闭还是左闭右开,也就是第一点:left = right是否合法,第二点:nums[middle]不是搜索值,这时候再看是右开还是右闭,闭区间的right值能搜索到所以不能等于right值,反之右开区间可以。

还有点小细节就是可以用位运算:>>1 去替换 / 2,这样运算速度会更快。

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

lc35.搜索插入位置

以704为母题,34、35和704也是一类题。特点是先判断target值在数组中的位置情况,其中35要求时间复杂度为O(log n), 其实遇到这类暗示一般都会想到用二分法查找, 具体情况如下:

class Solution {
public:
    int searchInsert(vector& nums, int target) {
        int n = nums.size();
        int left = 0;
        int right = n - 1;
        while (left <= right) { //当区间为[left, right]时仍然有效
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            }
            else if (nums[middle] < target) {
                left = middle + 1;
            }
            else { //nums[middle] == target
                return middle;
            }     
        }
        // 处理四种情况
        // 目标值在数组所有元素之前 [0 , -1]
        // 目标值等于数组中的某个元素, return middle;
        // 目标值插入数组中的位置 [left , right], return right + 1;
        // 目标值在数组所有元素后, return right + 1;
        return right + 1;
    }
};

而34题更复杂一点,由于数组中有重复的目标值,需要去找目标值的左边界和右边界,通过给左右边界赋值去判断目标值是否在数组范围中: 

class Solution {
public:
    vector searchRange(vector& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // target不在数组范围中
        if(leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // target在数组范围中且数组存在target
        if(rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // target在数组范围中但是数组不存在target
        return {-1, -1};
    }
private:
    int getRightBorder(vector& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while(left <= right){
            int middle = left + ((right - left) / 2); //防止溢出
            if (nums[middle] > target){
                right = middle - 1;
            }
            else{   
                // 当nums[middle] == target时,才得到target的右边界
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(vector& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
        int leftBorder = -2; // 记录一下rightBorder没有被赋值的情况
        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;
    }
};

注:关于二分查找最好画个数轴,直观一些。

lc27.移除元素 给一个数组,一个目标值,在数组中删除等于这个目标值的元素,返回新数组的大小。

这个题最关键的底层逻辑是数组中的元素不能删除,只能覆盖。如果用暴力法就是先遍历数组,再赋下标值。通过双指针的思路,把快指针记录的新数组中所需的元素值赋给慢指针,而慢指针的作用是获取新数组中需要更新的位置,遇到相同值的元素时不更新慢指针的位置进入下个循环。

class Solution {
public:
    int removeElement(vector& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++){
            if (val != nums[fastIndex]){
                nums [slowIndex++] = nums [fastIndex];
            }
        }
        return slowIndex;
    }
};

与27题相关的题还没做,明天一并更新。

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,数据结构)