数组理论基础
⚠️C++er 要注意:
vector 和 array 的区别,vector的底层实现是array,严格来讲vector是容器,不是数组。
Day1. 数组:704.二分查找
题目链接:https://leetcode.cn/problems/binary-search/
文章讲解:https://programmercarl.com/0704.二分查找.html
视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
使用二分法的前提条件:
- 数组是有序数组
- 数组中无重复元素
二分法的2种常见写法:左闭右闭、左闭右开
“二分查找涉及的很多的边界条件,逻辑比较简单,但(很多同学)就是写不好。
例如到底是
while(left < right)
还是while(left <= right)
?到底是
right = middle
呢,还是要right = middle - 1
呢?”
对区间的不一样的定义,会影响到我们在写二分法时这个边界条件的处理。
左闭右闭写法:
[left, right]
eg: [1, 1]
边界的处理规则都是根据对这个区间的定义来写的。
定义target是在上面这个左闭右闭的区间里,当target出现在middle
的左边,我们需要思考接下来是middle
还是middle - 1
?
回到这个区间的定义,这是一个左闭右闭的区间,我们已经判断middle所在的值已经大于target了,说明nums[middle]一定不是我们要搜索的值。
那么接下来的区间就一定不会包含这个数值,
既然不包含,就应该是在[left, middle - 1]
这个区间进行搜索。
AC代码: (核心代码模式)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right) {
int middle = left + ((right - left) / 2); //防止溢出的写法,相当于:(left + right) / 2
if (nums[middle] > target) { //目标值在middle左边
right = middle - 1; //更新右区间
}
else if (nums[middle] < target) { //目标值在middle右边
left = middle + 1; //更新左区间
}
else
return middle;
}
return -1;
}
};
左闭右开写法:
[left, right)
eg:[1, 1) 即包含1,又不包含1。这是一个不合法的区间。
left和right相等的情况在这个区间里没有意义,因此考虑while (left < right)
nums[middle]已经大于target了,说明下一个搜索的左区间是不包含这个middle所在的数值的,
那么这个right直接等于middle就可以了。
⚠️注意:right = nums.size();
,
因为我们这个区间里面不包含右边界了。
AC代码: (核心代码模式)
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size();
while (left < right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle;
}
else if (nums[middle] < target) {
left = middle + 1;
}
else
return middle;
}
return -1;
}
};
Day1. 数组:27.移除元素
题目链接:https://leetcode.cn/problems/remove-element/
文章讲解:https://programmercarl.com/0027.移除元素.html
视频讲解:<htt ps://www.bilibili.com/video/BV12A4y1Z7LP>
暴力解法:
时间复杂度:O(n^2)
空间复杂度:O(1)
写2层for循环:1️⃣第一层循环遍历数组元素。 2️⃣第二层循环更新数组
AC代码: (核心代码模式)
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;
}
};
双指针解法:(重点)
时间复杂度:O(n)
空间复杂度:O(1)
快指针是获取新数组中的元素,慢指针是获取新数组中需要更新的位置,都是在同一个数组上进行操作。
AC代码: (核心代码模式)
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];
slowIndex++;
}
}
return slowIndex;
}
};