数组
二分查找
二分查找的操作对象是区间,理清二分查找的思路需要时刻关注区间的变化
需要注意的是如果进行l = mid
操作,需要格外注意死循环的问题(因为mid = l + r >> 1)
(1)左闭右闭区间
//右闭,r要往前一位
int l = 0, r = nums.size() - 1;
//左闭右闭区间中l == r合法
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;
( 2)左闭右开区间
//右开,r不需要前移
int l = 0, r = nums.size();
//左闭右开区间中l == r不合法
while(l < r){
int mid = l + (r - l) / 2; //防止溢出
//右开,r不包含mid,不需要-1
if(nums[mid] > target) r = mid;
else if(nums[mid] < target) l = mid + 1;
else return mid;
}
return -1;
题目
力扣704.二分查找
模板题
力扣35.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
示例:
输入: nums = [1,3,5,6], target = 2
输出: 1
对于目标值不存在的处理:
/*执行r = mid - 1后跳出循环
即l == r处的值大于target
则target应该插入在l == r处
而r的值已经被更新,则返回l*/
if(r < l) return l;
else return r;
完整代码:
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0, r = nums.size() - 1;
while(l <= r){
int mid = (l + r) / 2;
if(nums[mid] > target) r = mid - 1;
else if(nums[mid] < target) l = mid + 1;
else return mid;
}
if(r < l) return l;
else return r;
}
};
力扣34.在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
示例:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res = {-1, -1};
int l = 0, r = nums.size() - 1;
while(l <= r){
int mid = (l + r) / 2;
if(nums[mid] > target) r = mid - 1;
else if(nums[mid] < target) l = mid + 1;
//找到了中间值的下标,然后利用中间值的下标分别寻找左右端点
else{
int l1 = l, r1 = r;
while(nums[l1] != target){
mid = (l1 + r1) / 2;
if(nums[mid] > target) r1 = mid - 1;
else if(nums[mid] == target){
r1 = mid;
l1 += 1;
}
else l1 = mid + 1;
}
res[0] = l1;
int l2 = l, r2 = r;
while(nums[r2] != target){
mid = (l2 + r2) / 2;
if(nums[mid] > target) r2 = mid - 1;
//因为是把mid赋值给左端点,所以要防止死循环
else if(nums[mid] == target){
l2 = mid;
r2 -= 1;
}
else l2 = mid + 1;
}
res[1] = r2;
return res;
}
}
return res;
}
};
双指针算法
题目
力扣27.移除元素
暴力解法:
class Solution {
public:
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];
}
i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
size--; // 此时数组的大小-1
}
}
return size;
}
};
暴力优化解法:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int num = 0;
for(int i = 0; i < nums.size(); i ++ )
if(nums[i] != val) nums[i - num] = nums[i];
else num ++;
return nums.size() - num;
}
};
双指针算法(快慢指针法):
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
//fastIndex不指向val则一起前进(相当于用第二个数组)
//fastIndex指向val时只有fastIndex前进
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
双指针算法(相向指针法):
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int leftIndex = 0;
int rightIndex = nums.size() - 1;
while (leftIndex <= rightIndex) {
// 找左边等于val的元素
while (leftIndex <= rightIndex && nums[leftIndex] != val){
++leftIndex;
}
// 找右边不等于val的元素
while (leftIndex <= rightIndex && nums[rightIndex] == val) {
-- rightIndex;
}
// 将右边不等于val的元素覆盖左边等于val的元素
if (leftIndex < rightIndex) {
nums[leftIndex++] = nums[rightIndex--];
}
}
return leftIndex; // leftIndex一定指向了最终数组末尾的下一个元素
}
};