目录
嵌入式学习分享个人主页:Orion嵌入式随想录 - 小红书 (xiaohongshu.com)
704. 二分查找
二分法
二分法是一种在有序数组中查找特定元素的算法。它通过将数组分成两半,然后根据目标值与中间元素的比较结果来决定是继续搜索左半部分还是右半部分,从而逐步缩小搜索范围。二分法查找的效率很高,其时间复杂度为 O(log n)。
使用条件:
有序数组(整体有序[1,2,3,4,5],或局部有序[4,5,1,2,3])
上下界确定(需要确定边界从而设置判断条件)
特定元素(查找值满足某种特定的条件)
使用步骤:确定一个区间[L,R](也可以不是全闭合,改变边界条件)并找到一个性质(由题目条件决定),该性质满足两点:二段性 、答案是二段性的分界点
-
解题思路:
-
整体有序数组,无重复元素考虑用二分法
-
注意区间界限定义和循环比较条件
-
-
解题步骤:
-
设置左右区间
-
设置循环比较条件
-
循环内计算中值
-
循环内进行目标值比较
-
循环内更新区间
-
继续循环找值
-
-
代码注意:
-
nums.size() - 1:注意数组角标计算
-
int middle = left + ((right - left) / 2):防止溢出,等同于(left + right)/2
-
target < nums[middle]:比较的是数组值
-
return -1:找不到要在循环外返回
-
-
代码一:左闭右闭
-
区间设置为[left, right] ,改变边界处理方法
-
while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
-
if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 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;
}
};
-
代码二:左闭右开
-
区间设置为[left, right) ,改变边界处理方法
-
while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
-
if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
-
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;
}
};
27. 移除元素
-
文章讲解:代码随想录
暴力解法
数组是存放在连续内存空间上的相同类型数据的集合。
数组下标都是从0开始的。数组内存空间的地址是连续的正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
数组的元素是不能删的,只能覆盖。
C++中二维数组在地址空间上是连续的。
-
解题思路:
-
由于数组是连续内存,不可以单个删除,只能一个个覆盖。
-
考虑这两层for循环,一个for循环遍历数组元素 ,第二个for循环更新数组。
-
-
解题步骤:
-
一层循环遍历,次数为元素总个数
-
一层循环内查找值
-
查找到值则二层循环遍历,次数为剩下元素个数
-
二层循环内更新值
-
更新数组下标与大小
-
-
代码注意:
-
值判断后在二层循环内最后更新i与size
-
-
代码一:嵌套循环
// 时间复杂度: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;
}
};
双指针法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
慢指针:指向更新,新数组下标的位置
-
解题思路:
-
利用指针指向不同在一个数组上实现双循环
-
快指针找到需要值则赋值,找到移除值跳过
-
慢指针指到移除值的下标则从此处开始令快指针赋值
-
-
解题步骤:
-
定义双指针
-
每次循环快指针加
-
循环内判断为需要新值,则慢指针加,同时赋新值
-
否则不执行,仅快指针加
-
-
代码注意
-
在判断内,先nums[slowIndex] = nums[fastIndex];最后更新slowIndex++;
-
nums[slowIndex++] = nums[fastIndex];等同于上流程,先运行等式再执行++
-
-
代码二:快慢指针
// 时间复杂度: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;
}
};
(说明:基于代码随想录课程学习,部分内容引用自代码随想录文章)