二分查找(Binary Search)
二分查找是一种在有序数组中快速查找目标值的算法,时间复杂度为 O(log n),空间复杂度为 O(1)。
典型题目: 704. 二分查找
题目要点:
- 数组是有序的(一定单调性)
- 使用双指针(left和right)定义搜索范围
- 每次比较中间元素(mid)与目标值,缩小搜索范围
实现
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) { // [left, right]左闭右闭区间
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
};
注意点:
- 循环条件left <= right对应的是左闭右闭区间
- 计算mid时使用left + (right - left)/2而非(left+right)/2,防止整数溢出
- 更新指针时要±1,确保范围能不断缩小
双指针技巧(Two Pointers)
双指针技巧通常用于原地修改数组或在单次遍历中解决问题,时间复杂度通常为O(n),无需构建额外空间所以空间复杂度一般O(1)。
典型题目
题目要点
原地修改数组(避免额外空间)则可以考虑使用双指针:
-
fast指针遍历原数组
-
slow指针构建新数组,直接在原数组上覆盖,无需额外空间。
实现
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//fast遍历数组,slow指向新数组
int fast=0, slow=0;
for(; fast<nums.size(); fast++){
if(val != nums[fast]){
nums[slow++] = nums[fast];
}
//相等的话fast快指针继续遍历,slow则不动
}
return slow;
}
};
时间复杂度
- 时间复杂度:O(n),仅需一次遍历。
- 空间复杂度:O(1)
要点
-
单调性(包含负数)且平方后的极值分布:平方后的最大值肯定是在数组的俩端(要么最左的负数,要么最右边的正数)所以可以考虑使用双指针由俩边向中间遍历。
-
要求新数组,所以可以定义一个vector
实现
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
vector<int> result;
result.resize(n);
//left,right分别指向原数组的俩端,双指针从俩端向中间遍历
//newIndex指向新数组的末尾,由后往前填充数组
int left=0, right=n-1, newIndex=n-1;
while(left<=right){
int rSquares = nums[right]*nums[right];
int lSquares = nums[left]*nums[left];
if(rSquares>=lSquares){
result[newIndex--] = rSquares;
right--;
}else{
result[newIndex--] = lSquares;
left++;
}
}
return result;
}
};
复杂度
- 时间复杂度:O(n),仅需一次遍历。
- 空间复杂度:O(n),存储结果数组(题目允许返回新数组)。