二分查找
思路
首先,二分查找的前提是有序的数组,如果不是有序数组,则不适用二分查找。其次,确定要查找的区间,这个很重要。一般来说,通常有左闭右闭和左闭右开这两个区间,不同的区间在写法上也会有不同,这是很多人会出错的地方。
左闭右闭
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;//左闭右闭区间
while(l <= r){
int mid = l + (r - l) / 2;
if(nums[mid] > target) r = mid - 1;//查找的数比中间的数小则更新右区间
else if(nums[mid] < target) l = mid + 1;//查找的数比中间的额数大则更新左区间
else return mid;//返回的是下标不是位序
}
return -1;//没找到则返回-1;
}
在左闭右闭区间中,因为是包含最左边和最右边的数,所以l = 0, r = nums.size() - 1;(如果是左闭右开区间的话,则r = nums.size()就可以了)。关于while循环的判断条件中是 < 还是 <= 取决于你使用的区间,在左闭右闭区间中,左区间和右区间相等是符合条件的,所以使用的是 <= ,如果你使用的是左闭右开区间,那么就左区间和右区间就不能相等,因为 [3,3) 这个区间显然是不合法的。if(nums[mid] > target) r = mid - 1;如果中间的数比要查找的数还要大的话,那么查找的数必然落在左区间,所以更新 r = mid - 1。下面的else同理,如果中间的数比查找的数还要小,那么查找的数必然落在右区间,所以更新l = mid + 1。如果这两种情况都不存在说明已经查到target了,直接返回mid即可。
左闭右开
int search(vector<int>& nums, int target) {
int l = 0, r = nums.size();//左闭右开区间
while(l < r){
int mid = l + (r - l) / 2;
if(nums[mid] > target) r = mid;
else if(nums[mid] < target) l = mid + 1;
else return mid;//返回的是下标不是位序
}
return -1;//没找到则返回-1;
}
和左闭右闭不同的是r = nums.size(),因为是左闭右开区间,所以不包含最右边的数,如果 r = nums.size() - 1 的话那么就会漏掉最右边的一个数。其次是while循环的判断条件,左闭右开区间不存在左区间 == 右区间的情况,举个例子就是[3,3) 这个区间显然是不合法的,所以用 < 而不用 <= 。再就是在区间更新时,因为是左闭右开区间,所以在更新 r 的时候就不能在减1了,因为if(nums[mid] > target) 已经判断了中间的数不是target,再减1的话就会漏掉一个数(因为左闭右开区间是不包含最右边的数),所以 r 更新为 r = mid。其余和左闭右闭区间是一样的。
删除元素
思路
首先,我们要知道数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖,所以由此衍生出的暴力解法就很容易想到了,也就是直接套两个for,外层循环负责遍历数组,内层循环负责将值为 val 后的所有元素依次前移。优化可以采用双指针的方法,定义一快一慢两个指针,快指针 fast 遇到目标元素直接跳过,也就是寻找不包含目标元素的新数组,慢指针碰到目标元素就停下,为快指针提供定位,方便快指针直接覆盖慢指针指向的位置(删除元素)。
暴力解法
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];
}
size--;
i--;
}
}
return size;
}
没什么好说的,就是套两层for,需要注意的点就是在找到指定元素使其之后的元素依次前移的时候不要忘记 i-- ,因为其之后的元素都前移了一位,所以 i 也前移一位。
双指针法
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.size(); fast++){
if(nums[fast] != val) nums[slow++] = nums[fast];
}
return slow;
}
因为看起来比较简单,所以很多人第一次看会很懵,最重要的就是要明白两个指针的含义和作用,然后手动模拟一遍这个过程,就懂了。快指针 fast 遇到目标元素直接跳过,也就是寻找不包含目标元素的新数组,慢指针碰到目标元素就停下,为快指针提供定位,方便快指针直接覆盖慢指针指向的位置(删除元素)