代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
数组理论基础
数组要点
- 数组是存放在连续内存空间的相同类型数据的集合。
- 数组的下标都是从0开始的。
- 数组内存空间的地址是连续的。
- 数组中的元素不能删除,只能进行覆盖。
所以增删元素的时候需要对其他元素的地址做相关操作。
C++相关
- 在C++中,vector和array存在区别,vector的底层实现是array,严格来说vector是容器,不是数组。
- 在C++中,二维数组在内存空间上是连续分布的。
704.二分查找
题目链接:704. 二分查找
文档讲解:704. 二分查找
视频讲解:二分查找法
目标:熟悉根据 左闭右开,左闭右闭 两种区间规则 学出来的二分法
状态:以前的方法全都忘记了,边界条件含糊不清,还是要多总结
学习前的想法
因为平常也了解过一些简单的算法,加上学校上课提到的,二分查找的基本思想我是了解的,只需不断的将区间的中位数与目标值作比较,选择左右区间,直到找到目标值或数组越界。
尝试写出代码如下(错误):
1 | class Solution { |
代码超时!
反思之后,在写题时对边界条件没有很好的理解,导致浪费了大量时间还没写对。
2024/2/21
个人并没有看出代码是在哪里出错,可能是循环终止条件有问题。🥲
学习后的想法
二分法的关键是处理好边界问题,包括:循环边界,区间边界
一般来说,我们采用的有两种边界取法,左闭右闭,左闭右开,以left和right分别表示数组的边界,即[left, right]和[left, right)。
在二分法中,有两处需要判断边界,一处为while循环的终止条件,另一处为选择边界时left或者right与middle的取值。
我们在解决这类问题时,需要选择上述所说的一种区间,然后一路走到底。对于每处边界判断,都要以我们所选定的区间来取舍,来判断进行选择后取值是否符合区间范围。
卡哥讲的很清楚,确实是让我理解了这个边界选择的方法,以前看acwing的视频没有理解为什么,只背了模板,结果时间长了模板忘了,题也做不对了。
实现
左闭右闭
1 | // 区间左闭右闭,[left, right] |
左闭右开
1 | // 区间左闭右闭,[left, right) |
27.移除元素
题目链接:27.移除元素
文档讲解:27. 移除元素
视频讲解: LeetCode:27. 移除元素
状态:暴力法能够解决,不过还是不熟练,双指针法确实开拓了思路
学习前的想法
题目给出了一个数组中nums和一个值val,要求原地移除所有数值等于val的元素,并返回移除后数组的新长度。
原地算法,即空间复杂度为O(1)
暴力法(双层for循环):我的想法是遍历数组,遇到与val相等的值,则将后续所有元素向前移动一位,接着从当前位置继续遍历,并将数组长度减一。
代码实现:
1 | // 时间复杂度:O(n^2) |
2024/2/21
和卡哥的代码对比,for循环前对length==0的判断似乎没有必要
学习后的想法
题外话:有关库函数的使用问题
卡哥的建议:如果一道题目使用库函数可以直接解决,不建议使用;如果使用库函数知识解决这个问题的其中一小步,并且我们直到这个库函数内部的实现过程,以及它的时间复杂度,可以用。
1 | vector.erase() |
使用双指针法可以达到O(n)的时间复杂度,仅使用一层for循环:
快指针:声明为fast,初始值为0,用于遍历数组,找到所有不等于目标元素,应该保留的值。
慢指针:声明为slow,初始值为0,相当于指向一个空的、新的数组,不断的填入快指针找到的值。
流程:
当快指针指向的值符合条件时,即不等于val时,将该值赋给慢指针指向的位置,fast++; slow++;
当快指针指向的值不符合条件,即等于val时,跳过该元素不赋值,fast++; slow不变;
最终,当快指针遍历完数组时,慢指针指向其所表示新数组的最后一个元素的后一位,由于其值从0开始,因此慢指针的位置便是新数组的长度。
实现过程记录
1 | // 时间复杂度:O(n) |
收获与学习时长
学习时长:3h45min
收获
熟悉了 根据 左闭右开,左闭右闭 两种区间规则 写出来的二分法。
掌握双指针法解决移除元素。(相向双指针暂时没有手敲,思路时使用左右指针进行遍历)