代码随想录 day1 704二分查找法,27移除元素

yi    二分查找法   LeetCode704

通过卡哥的讲解将二分法进行梳理:有时候没有注意到那么多问题

常用的情况分为两种区间为左闭右闭和左闭右开情况。 

(1)左闭右闭

// 版本一
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

(2)左闭右开 

// 版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

问题点:

1.right值的确定: 容易出现问题,核心在元素个数和可以访问的元素

在左闭右闭的情况,设置right=size-1就可以访问到所有的元素

但是在左闭右开的情况,因为right不能取到,要求所有的元素都能被访问,则一开始right等于size,也就是元素最后一个值之后的一个元素

2.while()中left和right中<与<=的确定:通过特例容易理解

【1,1】在1中可以成立,但是在2中不能够成立,这也就是=的区别

3.分为三种情况对下标进行返回:==单列

4.两种情况中nums[mid]<right的情况不同:容易理解,容易忽略。

er 移除元素   LeetCode27 

比较简单经典的问题

(1)双重for循环

// 时间复杂度:O(n^2)
// 空间复杂度: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 + 1; j < size; j++) {
                    nums[j - 1] = nums[j];
                }
                i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
                size--; // 此时数组的大小-1
            }
        }
        return size;

    }
};

问题点:

1.对于j的书写方式,感觉上应该是for(int j = i; j<size; ++j)也是可以的,但是因为nums

[size]处的元素并没有意义,在LeetCode中不能通过。

2.--i容易被忘记,因为整体向前移动所以需要在移动完成以后进行--i操作,使得从未判断的位置继续进行判断。

3.开始使用size代替nums.size()是为了便于书写

(2)双指针的方式

双指针的方式思路比较难想

// 时间复杂度: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;
    }
};

核心思想:快慢指针也许可以帮助消除一层for循环

问题:

1.搞清if的条件,理解原理:fast指针是为了遍历所有的元素,slow指针是为了保存不含val的元素。因此fast一直向前走,slow遇到当fast与val不相同的情况才进行保存即向前移动。

2.最终的返回时slow,可以带入例子验证,slow是落在最终数组的后面的一个位置。

补充基础知识:

1.vector的底层是数组,实际上也就是三个指针。

 2.常常用到的vector.erase() 的时间复杂度是O(n),所以当数据量比较大的时候不要忘记这个问题。

  • 15
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值