程序员老鬼

刚升小组长,组员却频繁质疑决策,咋办。。

我有个朋友,刚升小组长没几天,就开始感叹:“这椅子还没坐热,怎么感觉像坐在火山口上?”

组员天天开会“提建议”,但说白了,就是在明里暗里质疑他的每一个决策。从排期到分工,甚至连用什么变量名都要辩论一番。讲真,要不是他脾气好,早就摔鼠标了。

Image

后来他找了个办法,特别骚:提拔了一个“常务副组长”。表面上是让人分担压力,实际上就是专门用来当黑脸的。每次有决定,他只负责传达方向,具体的强硬执行全甩给副组长。

Image

效果居然出奇地好。组员们一开始还试图抗议,但发现“副组长”比正组长还刚,立马就怂了。

你问我怎么看?讲真,打工人要想活得明白,最重要的不是让所有人满意,而是让机制代替情绪。你不想得罪人,那就找个能“得罪人”的人站你前面。唱白脸太累,就唱唱红脸,别忘了你才是导演。【备注:文末可领最新资料】

算法题:Range 模块

Range 模块这题,说难不难,说简单也容易踩坑,尤其是当你遇到那种“连续覆盖+频繁查询+删除区间”的组合操作,没点儿数据结构功底是真的容易翻车。先说下需求吧,这玩意儿就是你维护一组区间段,支持:

1️⃣ 添加一个新区间 2️⃣ 查询一个区间是否被完全覆盖 3️⃣ 删除一个区间

光看操作就知道这不是靠 List 傻愣愣地硬顶就能搞定的。那种暴力遍历每次 O(n) 查找合并的写法,面试能拿分,真项目你跑几千条数据就等着服务器飙内存吧🤯。

Java 做这题我第一时间想到的是 TreeMap,标准的“带顺序的 key-value 存储”,用区间的左端点当 key,右端点当 value,天然就能做“查上界/下界”操作。

简单给个结构图脑补一下:

TreeMap<Integer, Integer> intervals = new TreeMap<>();

每个 entry 代表一个区间 [start, end),注意是开区间。

先说添加 addRange(left, right) 的逻辑。这块儿最容易出错的点在于 —— 你不能简单把区间加进去,还得“合并”所有跟这个区间重叠的原有区间。不合并会出现一堆重叠碎片,到时候你查一个 [8, 10) 却发现你明明加了 [5, 15),结果查不到🤡

合并的逻辑我这样写:

publicvoidaddRange(int left, int right){
    Integer start = intervals.floorKey(left);
if (start != null && intervals.get(start) >= left) {
        left = start;
        right = Math.max(right, intervals.get(start));
        intervals.remove(start);
    }

while (true) {
        Integer next = intervals.ceilingKey(left);
if (next == null || next > right) break;
        right = Math.max(right, intervals.get(next));
        intervals.remove(next);
    }

    intervals.put(left, right);
}

有没有发现?这段代码就是一个循环清理所有跟 left, right 有交集的旧区间,然后重新插入新的合并结果。妙在 TreeMap 的 floorKey 和 ceilingKey,能高效地帮你找“左边最近的”和“右边最近的”区间。

再看查区间 queryRange(left, right),这玩意其实逻辑很简单:只要看有没有某个区间 [start, end) 包含了 [left, right)。

publicbooleanqueryRange(int left, int right){
    Integer start = intervals.floorKey(left);
if (start == null) returnfalse;
return intervals.get(start) >= right;
}

简洁有力💪 如果你脑子一抽查的是 TreeMap 的 get(left),那就完犊子了,真是“查错key毁终生”。

最后删区间 removeRange(left, right) 是最绕的一个。我一开始以为像添加那样简单处理一下就行,结果发现你得拆区间,拆成不包含 [left, right) 的部分。画个图就理解了:

假设你有区间 [2, 10),删 [4, 6),你得拆成两个新段 [2, 4) 和 [6, 10),这就是区间剪裁的经典套路✂️。

代码实现大概长这样:

publicvoidremoveRange(int left, int right){
    Integer start = intervals.floorKey(left);
if (start != null && intervals.get(start) > left) {
int end = intervals.get(start);
if (start < left) intervals.put(start, left);
if (end > right) intervals.put(right, end);
        intervals.remove(start);
    }

while (true) {
        Integer next = intervals.ceilingKey(left);
if (next == null || next >= right) break;
int end = intervals.get(next);
if (end > right) intervals.put(right, end);
        intervals.remove(next);
    }
}

这里的逻辑细节巨多,尤其是你得防止遗漏那些跨越删除段的区间。删完之后不要忘记——一定要手动重新插入剪下来的“保留段”。

讲真,这题最适合当那种面试官考你“数据结构应用”能力的送命题,你要是 TreeMap 不熟,这题真没法写利索。

顺便说一下我试过的一些错误姿势:

🚫 想当然地用 List 保存所有区间,每次 add 都线性合并 🚫 用 HashMap,结果顺序一乱彻底找不到哪个区间相邻 🚫 删除区间的时候不考虑跨区段合并,结果测试一跑全挂

所以建议大家刷到这题别跳,认真写一遍,一方面能锤炼你对 TreeMap 的使用,另一方面也逼你思考“数据结构设计”的细节。毕竟像这种“区间合并”类题,在工作里也是常见的,比如日志合并、内存回收、时间段处理等等。

话说回来,这类结构我其实一直觉得应该封装成库函数,Java 的 NavigableMap 太底层了,每次都得自己封装区间管理。不过话说回来,这不正是咱们写算法题提升代码抽象能力的机会么?

你怎么看,TreeMap 这种东西是不是 Java 的宝藏容器?

最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek

也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。

-END-

ok,今天先说到这,老规矩,给大家分享一份不错的副业资料,感兴趣的同学可以链接我,微信:hls404 找我领取。

以上,就是今天的分享了,看完文章记得右下角点赞,也欢迎在评论区写下你的留言。