今日任务:
题目链接
二分查找
简称二分法,很多人看二分法是一看就会一些就废,感觉二分法的代码很简单,但是每次写的时候总会出现各式各样的问题.
二分法其实就是在一个数组找一个目标值,找到了就返回这个数组的所对应的元素的下标,如果没找到就返回-1,我们在写这个代码的时候经常容易写错的地方,第一个就是我们在写这个循环的时候while(left<right)的时候是<还是<=,第二个就是在循环里面nums[mid]大于target要更新右区间,那么这个right是更新为mid还是mid-1.
首先,我们在区间里面搜索的时候,我们要对这个区间的定义一定要明确,就是这个区间到底是左闭右闭[left,right]还是左闭右开[left,right)的,对这个区间的定义是影响我们在写二分法的边界条件的处理,这样我们才能把握上面两点是while循环是<= 还是<,mid还是mid-1.
左闭右闭写法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size()-1;
while(left<=right)
{
//这里是防止right+left超过int最大值,防溢出.
int mid = ((right+left)/2);
if(target > nums[mid])
{
//如果target值大于mid,说明target值在区间的右半部分,所以要更新left
left = mid + 1;
}
else if (target < nums[mid])
{
//如果target值小于于mid,说明target值在区间的左半部分,所以要更新right
right = mid - 1;
}
else
{
return mid;
}
}
return -1;
}
};
左闭右开写法
左闭右闭的wihle(left<=right)是合法的,就比如[1,1],但是左闭右开的[1,1)是不合法的区间所以循环里面要变成<.
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
//左闭右开的右是不用的,所以不用-1
int right = nums.size();
//左闭右开的时候[1,1)是一个无效的区间所以不能等于
while(left<right)
{
int mid = ((left + right) / 2);
if(target < nums[mid])
{
//如果目标值小于mid,说明在区间的左边,但是因为是右开,所以是直接复制mid
right = mid;
}
else if(target > nums[mid])
{
如果目标值大于mid,说明在区间的右边,但是因为是左闭,还是要mid+1
left = mid+1;
}
else
{
return mid;
}
}
return -1;
}
};
以上就是两种写法,大家以后写二分法的时候就应该注意到边界处理要根据我们怎么定义区间来进行考虑.
移除元素
这道题目看起来是很简单,但是比较考验对数组的底层实现的理解,数组是一个连续的相近类型的元素的集合,如果要在数组中删除一个元素,其实并不是在这个地址上删除了,这个地址是删除不了的,只能依次覆盖,覆盖之后,把数组的size-1.比如C++里面Vector里面的erase函数的时间复杂度其实是O(n).
暴力解法
假如要删除元素3
那么后面的元素用个for循环依次往前覆盖
最后变成这样,size要-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--;
//每次删除数组总大小-1
size--;
}
}
return size;
}
};
双指针解法
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
- 慢指针:指向更新 新数组下标的位置
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//慢指针和快指针都从0开始
int slow = 0;
//快指针用for循环先走,寻找新数组所需要的元素
for(int fast = 0; fast < nums.size();fast++)
{
if(nums[fast] != val)
{
//找到新数组的所需元素,把他赋值给慢指针
nums[slow] = nums[fast];
slow++;
}
//不是新数组所需元素,慢指针不动,快指针继续向前寻找
}
return slow;
}
};
以上代码虽然比较简洁,但是很多人看到还是有点懵,主要是要弄清楚两个指针所指向的意义就能明白上述解法