代码随想录算法训练营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;
    }
};

这个双指针的想法一开始想不到,可能总结了双指针之后会好一点点。

posted @ 2025-08-20 21:34  Tangzy0121  阅读(866)  评论(0)    收藏  举报