代码随想录算法训练营第一天 | 704. 二分查找、27. 移除元素
备考期末微积分中途刷题记录
二分查找
文章讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
思路:
二分查找的思想和高中所学的二分法找零点是一样的。(必须是有序无重复的数组)
分为两种思路:
- 左闭右闭区间 [left, right]
初始状态:left = 0; right = len - 1;
注意这里右端点应该刚好处于数组的末尾,因为右端点是可以取到的
循环查找:
-
找到中点
mid = (right + left)/2
; 但是为了防止left + right 的数值超过了int的最大值,所以改为mid = left + (right - left)/2
。并将中点处的数值与目标值比较 -
若大于目标值,则目标值应该位于左边的区间 ,更新右端点位置
right = mid - 1
-
同理,若小于,则更新左端点
left = mid + 1
注:右端点都是可以取到的,所以更新端点时,right = mid - 1 即可,而不是right = mid(因为mid不等于目标值,不需要在下一个查找区间中)
结束条件:
-
当左右端点重合时,说明没有查找到目标值(可以假想一个长度为1的数组,而且存储的数值不是目标值,那此时直接结束循环,条件就是left = right)
-
当mid =val(目标值)时,return出来即可
-
-
左闭右开区间 [left, right)
初始状态:
left = 0; right = len
此时右端点在数组末尾的下一位,因为右端点是取不到的
循环查找:
-
与上面一样,只需要修改右端点的更新位置
right = mid
(mid是不进入下一查找区间的,而刚好right是取不到的)结束条件:
-
当
left = right -1
时,说明没有查找到目标值(可以假想一个长度为1的数组,而且存储的数值不是目标值,那此时直接结束循环,条件就是left = right - 1) -
当mid =val(目标值)时,return出来即可
-
附上代码:
int search(vector<int>& nums, int target) {
//二分查找,左闭右闭
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
//如果中间值等于目标值,返回中间值的下标
if (nums[mid] == target) {
return mid;
//如果中间值小于目标值,将左边界设置为中间值加1
} else if (nums[mid] < target) {
left = mid + 1;
//如果中间值大于目标值,将右边界设置为中间值减1
} else {
right = mid - 1;
}
}
//如果没有找到目标值,返回-1
return -1;
//--------------------------------------
//二分查找,左闭右开
int l = 0;
int r = nums.size();
while(l < r)
{
int mid = l + (r-l)/2;
if(nums[mid] == target) return mid;
else if(nums[mid] < target) l = mid + 1;
else if(nums[mid] > target) r = mid ;
}
return -1;
}
移除元素
文章讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP
思路
对于这道题目,我一开始的想法就是新建一个数组,把非目标删除元素(val)的放到新数组中就行。其实类似于双指针的过程,只不过双指针法只需要在同一个数组上操作就行,不过最直接的还是暴力解法了。
- 暴力解法:
直接双层for循环,找到一个目标元素就将后面的所有元素往前移动一位覆盖掉目标元素,就相当于移除元素了
int removeElement(vector<int>& nums, int val) {
// 1.暴力解法
int len = nums.size();
int i;
for (i = 0; i < len; i++) {
if (nums[i] == val) {
for (int j = i + 1; j < len; j++) {
//往前移位覆盖
nums[j - 1] = nums[j];
}
// 注意:后面的元素都向前移了一位,此时原来位于i+1的元素现在处于i的位置上,如果i不向前移动一位则这个元素没有被检测
i--;
len--;
}
}
return len;
}
- 双指针法:
首先需要明确两个指针的意义:
-
慢指针:假想自己创建了一个新的空数组(但是直接在原数组上操作就行),这个慢指针就是新数组的需要更新(加入元素)的位置下标
-
快指针:往前移动寻找数组中非目标删除元素的值(即寻找新数组需要加入的元素)
int removeElement(vector<int>& nums, int val) {
//i为慢指针,j为快指针
int i = 0;
for (int j = 0; j < nums.size(); j++)
{
if (nums[j] != val) //快指针向前查找新数组的元素
{
//当确定是需要加入的元素时直接将该值赋值给新数组慢指针对应的位置
nums[i] = nums[j];
i++;
//新数组加入一个元素后,慢指针就往后移动,更新到下一位需要加入元素的地方
//当然,这里没有必须创建新数组,直接覆盖原数组元素就可以
}
//这里整体有点像vector的push_back
}
return i;
}