class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; //定义target的区间左闭右闭,[left,right]
while (left <= right){
int middle = left + ((right - left) / 2); //防止溢出
if (nums[middle] > target){
right = middle - 1;
}
else if (nums[middle] < target){
left = middle + 1;
}
else{
return middle;
}
}
//未找到目标值
return -1;
}
};
二分查找主要是要注意自己的代码是左闭右闭还是左闭右开,也就是第一点:left = right是否合法,第二点:nums[middle]不是搜索值,这时候再看是右开还是右闭,闭区间的right值能搜索到所以不能等于right值,反之右开区间可以。
还有点小细节就是可以用位运算:>>1 去替换 / 2,这样运算速度会更快。
以704为母题,34、35和704也是一类题。特点是先判断target值在数组中的位置情况,其中35要求时间复杂度为O(log n), 其实遇到这类暗示一般都会想到用二分法查找, 具体情况如下:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0;
int right = n - 1;
while (left <= right) { //当区间为[left, right]时仍然有效
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
}
else if (nums[middle] < target) {
left = middle + 1;
}
else { //nums[middle] == target
return middle;
}
}
// 处理四种情况
// 目标值在数组所有元素之前 [0 , -1]
// 目标值等于数组中的某个元素, return middle;
// 目标值插入数组中的位置 [left , right], return right + 1;
// 目标值在数组所有元素后, return right + 1;
return right + 1;
}
};
而34题更复杂一点,由于数组中有重复的目标值,需要去找目标值的左边界和右边界,通过给左右边界赋值去判断目标值是否在数组范围中:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// target不在数组范围中
if(leftBorder == -2 || rightBorder == -2) return {-1, -1};
// target在数组范围中且数组存在target
if(rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// target在数组范围中但是数组不存在target
return {-1, -1};
}
private:
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while(left <= right){
int middle = left + ((right - left) / 2); //防止溢出
if (nums[middle] > target){
right = middle - 1;
}
else{
// 当nums[middle] == target时,才得到target的右边界
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间内,[left,right]
int leftBorder = -2; // 记录一下rightBorder没有被赋值的情况
while(left <= right){
int middle = left + ((right - left) / 2); //防止溢出
if (nums[middle] >= target){// nums[middle] = target时更新right
right = middle - 1;
leftBorder = right;
}
else{
left = middle + 1;
}
}
return leftBorder;
}
};
注:关于二分查找最好画个数轴,直观一些。
lc27.移除元素 给一个数组,一个目标值,在数组中删除等于这个目标值的元素,返回新数组的大小。
这个题最关键的底层逻辑是数组中的元素不能删除,只能覆盖。如果用暴力法就是先遍历数组,再赋下标值。通过双指针的思路,把快指针记录的新数组中所需的元素值赋给慢指针,而慢指针的作用是获取新数组中需要更新的位置,遇到相同值的元素时不更新慢指针的位置进入下个循环。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++){
if (val != nums[fastIndex]){
nums [slowIndex++] = nums [fastIndex];
}
}
return slowIndex;
}
};
与27题相关的题还没做,明天一并更新。
文章详细介绍了二分查找算法在解决不同问题中的应用,如LC704、LC34和LC35。强调了二分查找时区间的选择,以及处理边界条件和优化运算速度的方法。此外,还提到了LC27题,讨论了在数组中移除元素的双指针策略。
804

被折叠的 条评论
为什么被折叠?



