704.二分查找
文档讲解:代码随想录;
视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili
状态:做出来了
//算法和程序设计技术的先驱高德纳曾说过:虽然二分查找的基本思想相对简单,但细节可能会非常棘手。
第一次接触二分查找的代码是b站上的一个教程视频:二分查找为什么总是写错?_哔哩哔哩_bilibili
这个视频将二分查找的初始边界定为了-1和N(并非N-1),做法是根据题目确定一个边界条件,将给定的数组分成红蓝两个边界,然后根据具体情况取红边或者蓝边的某个值。
循环条件:while(l+1!=r)
为何初始边界是-1和N:
该做法意为将未涂色的数组涂上红蓝两色来划分,l==-1代表l所在字符以及之前的字符都是蓝色,r==N表示r所在字符以及之后字符都为红色。如果初始边界为0或者N-1的话,r==0意为该字符为蓝色,但是很有可能整个数组都是红色的,便出现错误,r==N-1同理。
该做法的指针更新:
这种写法更新指针时,能不能写成l=m+1或r=m-1呢? 并不行:
假如m为蓝色边界的最后一个,令其+1便会使蓝色指针跳到红色区域,反之同理。
如图:
注意边界条件 :
l或者r可能一直在初始位置(不在数组内),即整个字符串为全红或者全蓝,这时候要考虑l和r不在数组内的边界条件
接下来是学习随想录的做法:
该做法与上述做法在mid的判断上有区别,上述做法进行两次判断,循环中只将数组分为两个部分,在循环外根据条件返回值。而随想录在循环中进行三次判断,如果直接找到target就返回,不然缩小范围,缩到最后还找不到就return-1;
个人认为上一种做法在类似题目上更具有普适性,如在寻找第一个不大于或者不小于的数字,可作为一种模板。
随想录做法分为两种,左闭右闭和左闭右开(左开右闭)
左闭右闭:
此时的初始条件:l=0,r=num.size()-1
即left和right都包含在区间里面,此时的循环条件应为while(left<=right),因为left==right的比较是有意义的。
在判断mid时,if (nums[mid] > target) ,将r取作mid-1,因为比较区域中是包括r的,所以要把不符合的mid排除掉。
循环结束条件:l的更新在mid前一位,r更新在mid后一位,多次更新后r会在l的左边此时循环结束(r==l时依然没找到target,r和l的左右次序就变了)
左闭右开:
此时初始条件:l=0,r=num.size()
right不被包含在区间里,所以循环条件为while(left<right),因为left不能跟不在区间里的right比较。
在判断mid时,if(nums[mid]>target),将r取作mid,将mid排除在区间外。
27.移除元素
文档讲解:代码随想录
视频讲解:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili
状态:做出来了
第一次看到这个题目的时候心里想的是一版数据结构的书上对于vector容器的删除操作
如图:
第一次看书上这个做法的时候觉得很巧妙,结果竟然是暴力做法(无语了)。
后来想到图上这种做法其实是要保持相对次序,本题题目要求为元素顺序可以改变,于是只交换应被删除元素和数组尾
代码如下(这个代码块怎么不能调节大小,看着好难受) :
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int l=0;
int r=nums.size()-1;
while(l<=r)
{
if(nums[l]==val)
{
nums[l]=nums[r];
r--;
}
else
l++;
}
return r+1;
}
};
边界条件:如果l==r时候仍要被删除,r=-1,即数组为空。此时返回r+1=0个元素 。
该代码思路和leetcode题解给出的思路基本一致,只遍历了一遍数组,复杂度自然为O(n)。
但是怎么在O(n)复杂度下保持元素顺序呢?
学习随想录的算法:
我以为上述做法已经够巧妙了,没想到还有更勇猛的,这是谁的部将。
快慢指针算法:
让快指针和慢指针都指向初始位置,如果快指针指向的位置不等于val,就将快指针的值赋给慢指针所在位置,如果等于val,快指针就向前走一位。
当快指针走到最后时,循环结束,此时快指针将整个数组遍历了一遍,并将所有不等于val的值依次赋给了原数组形成新数组,复杂度为O(n),且保持了元素顺序。
代码:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int l=0;
for(int r=0;r<nums.size();r++)
{
if(nums[r]!=val)
nums[l++]=nums[r];
}
return l;
}
};
总结:
这两道题之前都学习过 ,所以写起来比较快,今天的时间比较充裕,所以博客写的详细一些,长一些。之后如果忙起来会尽可能写个大纲找机会补上。
写博客感觉好处还是很多的,之前脑袋里有很多想法,知道是怎么回事,落到文字上才发现有些说不清道不明的地方,将其转化为文字有助于对我自己思考的整理。
就先这样。