补day1,本篇代码为C语言版本。
一、二分查找
二分查找时数组都是有序数组,从小到大排列
1、力扣题目
题目:给定一个
n
个元素有序的(升序)整型数组nums
和一个目标值target
,写一个函数搜索nums
中的target
,如果目标值存在返回下标,否则返回-1
。示例:
输入:
nums
= [-1,0,3,5,9,12],target
= 9输出: 4
解释: 9 出现在
nums
中并且下标为 4
2、初次解题
思路:二分法相当于找到一个边界,每次先对半划分,然后根据条件,移动左右的边界值相互靠近,直到最后左右的划分的边界想邻,也就是当到达边界即left+1=right的时候,所以不满足该条件的时候应该一直循环。
(通俗点说就是一块空地,两边开始抢地盘,划分方法是每次剩下的空地对半抢,题目条件就是告诉你最后两边各自拥有地盘是多大。)
代码解释:
最终跳出循环的条件left+1=right
初始值是因为刚开始时土地都是空地,所以不是能下标为0和n-1之间的数(空地大小假设为n)并且根据最终判断条件left+1=right,要使得第一块下标为零的空地能够被抢到left+1=0才可以,所以left初始为-1,同理根据left=-1和判断条件要取到n-1,right应为n。
在数组为一个数时需注意数组是否会越界的问题
int search(int* nums, int numsSize, int target) {
int left=-1,right=numsSize;
int m=0;
while(left+1!=right){
m=(left+right)/2;//对半抢剩余的空地
if(nums[m]<=target)
left=m;
else
right=m;
}
if(left<0){
if(nums[right]==target)
return right;
else
return-1;
}//防止出现左边越界即数组种就一个数且该数大于target
else{
if(nums[left]==target)
return left;
else
return-1;
}
}
3、学习视频
视频链接:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili
总结:按取值区间的不同分为[left,right],[left,right)和(left,right],不过一般是前两个,所以掌握前两种情况即可。同时无论在何种情况判断条件至少应满足left<right。
(1)左闭右闭区间
①初始化:根据取值区间,此时left和right的初始值就是数组的最小和最大的下标
②循环条件:条件应该是left<=righ(根据是否合法就可以判断,假设数组中就一个元素[0],因为左闭右闭,可知[0,0]是合理的,所以此时等于是可以的)
③更新区间:已经判判断过的数,无须再次判断,所以左边更新边界时需+1,右边更新边界时需-1。
int search(int* nums, int numsSize, int target) {
int left=0,right=numsSize-1;
int m=0;
while(left<=right){
m=(left+right)/2;
if(nums[m]>target){right=m-1;}
if(nums[m]<target){left=m+1;}
if(nums[m]==target){return m;}
}
return -1;
}
(2)左闭右开区间
①初始化:根据取值区间,此时left和right的初始值就是数组的最小下标和最大下标+1。
②循环条件:条件应该是left<righ(假设数组中就一个元素[0],因为左闭右闭,可知[0,0)是非法的,所以此时等于是不可以的)
③更新区间:已经判判断过的数,无须再次判断,所以左边更新边界时为m+1,右边更新边界时为m。
int search(int* nums, int numsSize, int target) {
int left=0,right=numsSize;
int m=0;
while(left<right){
m=(left+right)/2;
if(nums[m]>target){right=m;}
if(nums[m]<target){left=m+1;}
if(nums[m]==target){return m;}
}
return -1;
}
4、总结
初次解题的思路能够解决一些划分问题,也就是分成左右两个范围,有些题目进行修改也可转化成类似的问题进行解决。
视频所学习到的方法,一般解题有四注意:注意初始化,注意判断条件,注意更新区间,注意返回值。
二、移除数组
1、力扣题目
题目:给你一个数组
nums
和一个值val
,你需要原地移除所有数值等于val
的元素,并返回移除后数组的新长度。不要使用额外的数组空间,你必须仅使用
O(1)
额外空间并原地修改输入数组。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例:
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
2、初次解题
思想是覆盖,先寻找数组非val的个数(即最后数组个数),再分成前后两个部分循环来进行覆盖,前面为val的被后面非val的覆盖。
int removeElement(int* nums, int numsSize, int val) {
int a=numsSize;
for(int i=0;i<numsSize;i++){
if(nums[i]==val)a--;
}//寻找数组非val个数
for(int i=a;i<numsSize;i++){//遍历后排
if(nums[i]!=val){//非val时
for(int j=0;j<a;j++){//循环前排
if(nums[j]==val){//前排第一个为val
nums[j]=nums[i];
break;//val值被非val代替,并跳出循环
}
}
}
}
return a;
}
3、学习视频
视频链接:数组中移除元素并不容易! | LeetCode:27. 移除元素_哔哩哔哩_bilibili
有两种方法:(1)使用库函数 (erase函数c++) (2)使用双指针进行覆盖
此处主要掌握双指针方法:fast来进行遍历原先数组,slow来得到新数组。如果不是val则fast赋值给slow。
int removeElement(int* nums, int numsSize, int val) {
int slow=0;
for(int i=0;i<numsSize;i++){
if(nums[i]!=val){
nums[slow]=nums[i];
slow++;
}
}//此处i就是fast
return slow;
}
4、总结
当进行同一个数组更新时,优先考虑双指针来进行更新。