应优先使用 std::lower_bound 查找合并起点,它基于严格弱序准确定位首个右端点≥新区间左端点的位置;比较器需按 a.right < b.left 定义,避免手写二分的边界错误;vector 比 set 更轻量缓存友好,合并时需显式处理相邻区间(+1 判断)以匹配业务需求。

用 std::lower_bound 找插入点,别手写二分
直接调 std::lower_bound 查找第一个右端点 ≥ 新区间左端点的区间位置,这是最稳的做法。手写二分容易漏边界、错判合并条件,尤其当区间本身可能重叠或相邻时。std::lower_bound 基于严格弱序,只要自定义比较逻辑正确,就能准确定位——它返回的是“不破坏有序性的最早插入位置”,正好对应合并起点。
关键不是比左端点,而是用 [new_interval.left, new_interval.right] 和现有区间 [it->left, it->right] 的关系来设计比较器:
auto it = std::lower_bound(intervals.begin(), intervals.end(),
new_interval,
[](const Interval& a, const Interval& b) {
return a.right < b.left; // 注意:a 是已有区间,b 是新区间
});
这个比较逻辑确保:所有满足 a.right 的区间排在前面,所以 <code>it 指向第一个可能与 b 重叠或相邻的区间。
合并时必须向两边扩展,不能只看 it 当前位置
找到 it 后,新区间可能和它前面一个、它自己、以及它后面多个区间都重叠。常见错误是只检查 it 和 it-1,结果漏掉后缀合并。
立即学习“C++免费学习笔记(深入)”;
- 从
it开始向前检查:it != begin()且(it-1)->right >= new_interval.left→ 合并,--it - 再从原
it(现在是合并起点)向后检查:it != end()且it->left → 合并,<code>++it - 最后用
erase删除被合并的连续段,再insert合并后的新区间
注意:合并过程中要动态更新新区间的 left 和 right,初始值就是它自己,每次合并就取 min(left, it->left) 和 max(right, it->right)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
用 vector 还是 set?性能和接口差异很实际
如果插入频繁但查询少,vector + std::lower_bound + erase/insert 更轻量;如果需要持续按左端点排序且增删密集,std::set 自带红黑树,但得自定义比较器保证唯一性(比如按 left 排序,left 相同时按 right),否则插入重复 left 会失败。
set 的问题更隐蔽:合并后删除多个节点再插入一个,中间状态不一致,容易迭代器失效;而 vector 虽然 erase 是 O(n),但现代 CPU 缓存友好,实测在几千区间内反而更快。
别用 map 存区间——键只能是左端点,无法表达重叠或覆盖关系,后续合并逻辑会绕弯子。
边界情况:相邻区间要不要合并?看业务需求,代码里得显式控制
数学上 [1,2] 和 [3,4] 不重叠,但有些场景(如内存页、时间槽)要求“无缝”合并,即 right+1 == next.left 就算相邻需合并。这个逻辑不能靠默认比较器,必须在合并循环里加判断:
// 向前合并时:
if (it != intervals.begin() &&
(prev_it->right + 1 >= new_interval.left)) { // +1 表示允许相邻
new_interval.left = min(new_interval.left, prev_it->left);
--it;
}
同理向后也加 +1。这个 +1 是开关,开或关直接影响最终区间数量,但很多人写完发现“明明没重叠却合并了”,就是这里没对齐需求。
真正麻烦的是混合场景:一部分要合并相邻,一部分不要。这时得给区间加标记字段,或拆成两个容器维护,而不是在同一个合并循环里塞条件分支——那会让逻辑迅速不可读。

















