704. 二分查找
要点
- 数组元素只能覆盖,不能删除
- 数组、链表这种线性结构的考虑双指针和滑动窗口
1. 左闭右闭写法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right){
int mid = left + ((right - left) >> 1);
if (nums[mid] == target) return mid;
if (nums[mid] > target) right = mid - 1;
if (nums[mid] < target) left = mid + 1;
}
return -1;
}
};
2. 左闭右开写法
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size();
while (left < right){
int mid = left + ((right - left) >> 1);
if (nums[mid] == target) return mid;
if (nums[mid] > target) right = mid;
if (nums[mid] < target) left = mid + 1;
}
return -1;
}
};
难点
- 两种区间范围二选一,左闭右闭和左闭右开,决定了边界的处理方式和每轮待处理的子数组
- size()方法返回vector容器的元素个数
- 右移 >> 1位运算来代替除2操作
总结
二分查找的搜索过程可简化为从二叉树的根节点找一条到叶节点的路径,其时间复杂度为O(logn),两种思路使得写算法的时候,边界的判断更加清晰。
相关题:35.搜索插入位置 , 34. 在排序数组中查找元素
27. 移除元素
1. 暴力法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int len = nums.size();
for (int i = 0; i < len; i++){
if (nums[i] == val){
for (int j = i+1; j < len; j++){
nums[j-1] = nums[j];
}
len--;
i--;
}//if
}//for
return len;
}
};
-
如果元素等于val,进入for循环覆盖元素,覆盖后实际数组长度
len - 1 -
为了继续比对下一个元素
nums[i]、不漏掉原删除位置的新元素,需要事先i--
2. 双指针法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.size(); fast++){
if (val != nums[fast]){
nums[slow++] = nums[fast];
}
}
return slow;
}
};
要点
- fast指针寻找不要丢弃的元素
- slow指针指向最前方的需要更新的位置
fast-slow为移除的元素个数
总结
双指针法巧妙的是两个指针的移动写法,将fast指针的移动写在for循环里,slow指针写成赋值语句中的slow++,包含了一开始两个指针一起移动的情况。
若fast指向需要移除的元素,循环体内为空,只继续移动fast指针。
最后返回slow,因为slow指向新数组最后一个元素的下标,slow++后正好是新数组的元素个数。