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

文章讲述了在有序数组中使用二分查找算法解决LeetCode问题704,以及两种不同的边界条件处理方法。还介绍了移除数组元素的暴力解法和双指针优化,展示了如何在O(n)时间和O(1)空间复杂度下完成操作。
摘要由CSDN通过智能技术生成

1 基础知识:数组基础

文档讲解:代码随想录

数组的在内存空间上是连续的,同时每一个元素的数据类型相同

数组的下标从0开始

数组的地址空间连续

由于地址空间的连续性,在删除和添加元素是,会对后边的所有元素造成影响,需要重新覆盖其后的所有元素

数组的元素只能覆盖,不能删除

二维数组的地址空间也是线性连续的

2 LeetCode704 二分查找

题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

关键词:有序,不重复,查找

关于二分查找有两种边界条件。为什么要区分呢?因为每次更新边界变量时要遵循统一的边界条件。

左闭右开:[left, right)

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();  // 开区间[left, right)
        
        while(left < right){  // 开区间不能重合,用小于
            int mid = left + (right - left) / 2;  // 防止溢出  
            if(nums[mid] < target){  // mid已经确认了比较小,该往mid的右边建立[mid + 1, right)
                left = mid + 1;
            }else if(nums[mid] > target){  // mid已经确认比较大, 该往当前的mid的左边建立[letf, mid)
                right = mid;
            }else if(nums[mid] == target){  // mid正好,返回下标
                return mid;
            }
        }
        return -1;
    }
};

注意:一开始的右边界 int right = nums.size() 是超出数组长度的!

左闭右闭:[left, right]

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() -1;  // 左闭右闭区间[left, right]
        
        while(left <= right){  // 左闭右闭区间可以重合,用<=
            int mid = left + (right - left) / 2;  // 防止溢出
            if(nums[mid] < target){  // mid已经确认了比较小,该往mid的右边建立[mid + 1, right]
                left = mid + 1;
            }else if(nums[mid] > target){  // mid已经确认比较大, 该往当前的mid的左边建立[letf, mid - 1]
                right = mid - 1;
            }else if(nums[mid] == target){  // mid正好,返回下标
                return mid;
            }
        }
        return -1;
    }
};

总结:

二分查找针对有序的不重复的一组数据。

二分法思路:

以序列的边界作为区间

在区间成立的情况下

区间的中间点进行判断

如果满足条件则返回中间点下标

不满足条件则更新区间边界(统一的边界条件)

注意:

1 数组越界问题:右开区间直接用vector的.size();右闭区间采用.size() - 1。

2 两个数之和内存溢出问题:mid = left + (right - left) / 2。

3 LeetCode27.移除元素

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度;不要使用额外的数组空间,你必须仅使用 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; j < size - 1; j++){  // 全覆盖实现“删除”功能
                    nums[j] = nums[j+1];
                }
                size--;  // “删除”一个元素后,数组长度减一
                i--;  // “删除”一个元素后,新的元素一道当前位置,不需要向下走一步,自减以抵消最外层循环的自增
            }
        }
        return size;
    }
};

很有意思的暴力解法,其中的数组上限是动态变化的!

双指针法:

如何在暴力解法的规律之上再次分析?

快指针用来查找目标值

慢指针用来覆盖

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int fastIndex = 0;  // 定义慢指针
        int slowIndex = 0;  // 定义快指针
        int size = nums.size();  // 右开边界
        int length = size;  // 返回长度
        for (fastIndex; fastIndex < size; fastIndex++){
            if(nums[fastIndex] == val){  // 快指针索引到了目标值,需要让快慢指针异步
                length--;
                nums[slowIndex] = nums[fastIndex];
                continue;
            }
            nums[slowIndex] = nums[fastIndex];  // 1 最简单的情形,覆盖一下
            slowIndex++;  // 更新慢指针
        }
        return length;
    }
};

代码经处理后:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        int size = nums.size();  // 右开边界
        for (int fastIndex = 0; fastIndex < size; fastIndex++){
            if(nums[fastIndex] == val){
                continue;  // 检索到目标之后的操作
            }
            // 基础操作
            nums[slowIndex] = nums[fastIndex];
            slowIndex++;
        }
        return slowIndex;
    }
};

附上参考答案:

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

双指针可以用于无序序列中每次查找都有一次操作的情况!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值