离职电脑没清干净,赔到倾家荡产!
刚看到个贴子,说上海一姑娘离职时电脑只点了个“格式化”,结果公司技术员用恢复软件把她和新东家的聊天、项目资料全挖了出来。公司拿着证据索赔,姑娘当场崩溃,赔到倾家荡产。
我觉得这事吧,核心就是信息安全意识太薄弱。很多人以为清空、格式化就万事大吉,其实那只是把目录擦掉,数据还在,随便一款恢复软件就能搞出来。网友们有人说公司做得太狠,但说到底,她确实把商业秘密带走了,这是职场的大忌。
从我的角度看,这事其实像是“锁门但没拔钥匙”,表面安全,实则漏洞百出。离职交接不仅是交工作,也是保护自己。别想着侥幸,尤其涉及商业机密的东西,处理不好就是法律风险。
职场里赚钱不易,但防人之心不可无,数据清理和边界意识,真的是保护自己的最后一道防线。【备注:文末可领最新资料】
算法题:物块放置查询
先把题意说清楚:给你一串“物块”的放置请求,每个请求像 [x, len],表示在一维数轴上把一个边长为 len 的正方形物块放到区间 [x, x+len) 上。物块会叠起来,新的物块落下时,会先看它覆盖的区间目前最高有多高,然后“坐”在那个高度上,最终这个物块的顶端高度就是 区间当前最高高度 + len。我们要在每次放置后,输出当前全局最高高度。这个题在很多平台也叫“掉落的方块”。
直觉解法与瓶颈
最直接的办法是“模拟”:每放一个物块,就把它的区间与前面所有物块的区间两两相交,找出覆盖部分的最高高度。复杂度大约是 O(n^2),数据稍微大一点就慢得很明显。慢的根源其实是两个:一是区间是连续坐标,难以直接用数组表示;二是我们在做大量重复的区间最大值查询与区间赋值。
两个关键:坐标压缩 + 线段树
坐标压缩解决“连续坐标不好下手”的问题。把所有可能出现的关键点收集起来:每个物块带来两个端点 x、x+len,把这些端点去重排序后映射成离散下标。这样原来的大区间就变成了“小格子”的索引区间。
有了离散索引,就可以上“线段树(带懒标记)”。我们需要支持两种操作:
区间最大值查询:放置前查 [l, r)的当前最高高度;区间赋值更新:放置后把 [l, r)的高度整体更新为查询结果 + len(注意是“覆盖赋值”,不是加法叠加,因为顶面被新物块“抹平”成同一高度)。
线段树用 max 保存区间最高,懒标记保存“这个区间被整体赋成某高度”的信息,向下传递时要小心“赋值”的覆盖语义(赋值懒标记会直接覆盖子节点的最大值与其懒标记)。
压缩时要记住我们用的是半开区间 [x, x+len),所以坐标集里放两个端点即可。线段树的区间索引一般是 [0, m-1] 或半开写法,保持一致就不容易错。每次放置步骤是:压缩得到 l, r-1,先 query(l, r-1) 拿到基底高度 base,之后 update(l, r-1, base+len),并用一个变量维护全局最高值,打印或记录即可。
复杂度与适用范围
坐标压缩是 O(n log n),每次放置做一次查询一次更新,总体是 O(n log n),相比朴素的 O(n^2) 提升很明显。这个套路凡是遇到“一维区间上的重复查询+覆盖更新”几乎都能用。
import java.util.*;
publicclassFallingBlocks{
// 主函数:返回每次放置后的全局最高
public List<Integer> fallingSquares(int[][] positions){
// 1) 收集端点做坐标压缩
TreeSet<Integer> set = new TreeSet<>();
for (int[] p : positions) {
int x = p[0], len = p[1];
set.add(x);
set.add(x + len);
}
List<Integer> xs = new ArrayList<>(set);
Map<Integer, Integer> id = new HashMap<>();
for (int i = 0; i < xs.size(); i++) id.put(xs.get(i), i);
int m = xs.size();
SegmentTree seg = new SegmentTree(m);
// 2) 依次处理
List<Integer> ans = new ArrayList<>();
int global = 0;
for (int[] p : positions) {
int x = p[0], len = p[1];
int l = id.get(x), r = id.get(x + len) - 1; // 半开转成闭区间
if (r < l) { // 长度为0的保护,理论不会出现
ans.add(global);
continue;
}
int base = seg.query(1, 0, m - 2, l, r); // m 点形成 m-1 个小段
int top = base + len;
seg.update(1, 0, m - 2, l, r, top);
global = Math.max(global, top);
ans.add(global);
}
return ans;
}
// 线段树:覆盖赋值 + 区间最大
staticclassSegmentTree{
int[] max, lazy; // lazy 为覆盖高度,-1 表示无懒标记
SegmentTree(int n) {
max = newint[4 * n];
lazy = newint[4 * n];
Arrays.fill(lazy, -1);
}
voidpushDown(int idx){
if (lazy[idx] != -1) {
int v = lazy[idx];
int l = idx << 1, r = l | 1;
max[l] = v; max[r] = v;
lazy[l] = v; lazy[r] = v;
lazy[idx] = -1;
}
}
voidupdate(int idx, int L, int R, int ql, int qr, int val){
if (ql <= L && R <= qr) {
max[idx] = val;
lazy[idx] = val;
return;
}
pushDown(idx);
int mid = (L + R) >>> 1;
if (ql <= mid) update(idx << 1, L, mid, ql, qr, val);
if (qr > mid) update(idx << 1 | 1, mid + 1, R, ql, qr, val);
max[idx] = Math.max(max[idx << 1], max[idx << 1 | 1]);
}
intquery(int idx, int L, int R, int ql, int qr){
if (ql <= L && R <= qr) return max[idx];
pushDown(idx);
int mid = (L + R) >>> 1, res = 0;
if (ql <= mid) res = Math.max(res, query(idx << 1, L, mid, ql, qr));
if (qr > mid) res = Math.max(res, query(idx << 1 | 1, mid + 1, R, ql, qr));
return res;
}
}
// 小测试
publicstaticvoidmain(String[] args){
FallingBlocks fb = new FallingBlocks();
int[][] pos = {{1,2},{2,3},{6,1}};
System.out.println(fb.fallingSquares(pos)); // 示例输出:[2,5,5]
}
}
如果你更倾向“简一点”的写法,也可以把线段树换成“离散后用数组 + 区间扫描”的做法,但那样从 O(log n) 退回到 O(n),数据大了就吃力。这个版本在性能和代码长度之间还算平衡。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html