代码随想录算法训练营Day 1 关于二分/双指针
关于二分模板
二分的模板看看学学,不同的板子都用过了,各种模板都没办法避免检验答案合法性的情况。
那就写一个最漂亮的开区间写法吧,起码是对称的。
先给出核心的模板代码,再进行解释。
可行区域在左侧
int lo = 0, hi = 1E9; // 这里的lo和hi的初始值分别是理论值的上下界(开区间的上下界)
while (lo + 1 < hi) { // 开区间
int mid = lo + (hi - lo) / 2; //防溢出
if (check(mid)) lo = mid; // 可行区域在左侧的写法
else hi = mid;
}
return lo;
可行区域在右侧
int lo = 0, hi = 1E9; // 这里的lo和hi的初始值分别是理论值的上下界(开区间的上下界)
while (lo + 1 < hi) { // 开区间
int mid = lo + (hi - lo) / 2; //防溢出
if (check(mid)) hi = mid; // 可行区域在右侧的写法
else lo = mid;
}
return hi;
Q:while循环里面为什么这么写?
A:因为我需要保证每一次进入循环的是一个需要检测的合法区间,因为是闭区间,那么当\(lo + 1 < hi\)成立的时候,这是一个合法的区间,我们是需要对他进行检测的。
Q:为什么lo,hi指针的移动是对的?
A:要理解这个问题,首先需要明白“循环不变量”。我们使用二分的前提是,我们可以找到一个check函数,使得所有待检测的区间呈\([F, F, F, ..., T, T]\),或者\([T, T, T, ..., T, F, F]\),我们可以找到的是第一个使check函数为真的位置或者最后一个使check函数为真的位置。
所谓的循环不变量就是这样,以可行区域在左侧为例,我们\([start, lo]\)的所有位值都是\(F\),而\([hi, end]\)的所有位置都是\(T\)。因而当我们检测最小的区间——开区间只包含一个元素的区间——如果这个值为\(T\),那么\(hi\)左移,循环结束,此时\(hi\)占据的位置就是答案;反之,\(lo\)右移,循环也结束,\(hi\)占据的位置是第一个\(T\),同样是正确的。
题外话
这个地方写成开区间是最漂亮的,因为他的所有部分都是对称的,可行区域在哪侧,哪个指针进行移动,最后的答案也就是那个指针。最后判断答案的时候是需要通过越界来判断合法性的,这一点千万不能忘记。
LeetCode 704. 二分查找
题目链接:https://leetcode.cn/problems/binary-search/
class Solution {
public:
int search(vector<int>& nums, int target) {
// 分析单调性 对于 nums[i] >= target 呈现[F, F, F, ..., F, T, T]
// 那么我们可以认为 可行区域在右侧 进行查找
int lo = -1, hi = nums.size();
while (lo + 1 < hi) {
int mid = (lo + hi) >> 1;
if (nums[mid] >= target) hi = mid;
else lo = mid;
}
return hi >= 0 && hi < nums.size() && nums[hi] == target ? hi : -1;
}
};
活学活用,\(check\)函数也可以写成\(nums[mid] < target\),然后认为可行区域在左侧,查找最后一个可行解。
LeetCode 27. 移除元素
题目链接:https://leetcode.cn/problems/remove-element/
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int fast = 0, slow = 0;
while (fast < nums.size()) {
if (nums[fast] != val) {
nums[slow ++] = nums[fast];
}
fast ++;
}
return slow;
}
};
这道题的难点是不能用额外的空间,我们通过快慢双指针来解决这个问题。第一次做的时候没有太好的想法,挖一个双指针总结的坑。
LeetCode 977.有序数组的平方
题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int n = nums.size();
vector<int> ans(n);
int l = 0, r = n - 1, idx = n - 1;
while (l <= r) {
int candidate1 = nums[l] * nums[l];
int candidate2 = nums[r] * nums[r];
if (candidate1 >= candidate2) {
ans[idx --] = candidate1;
l ++;
} else {
ans[idx --] = candidate2;
r --;
}
}
return ans;
}
};
这个双指针的想法一开始想不到,可能总结了双指针之后会好一点点。

浙公网安备 33010602011771号