news 2026/8/30 22:04:22

腾讯2016研发工程师编程题复盘:五道经典算法题详解与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
腾讯2016研发工程师编程题复盘:五道经典算法题详解与避坑指南

每年秋招季,总会有同学翻出牛客网题库里“腾讯2016研发工程师编程题”这套经典卷子来刷。这套题在当年的校招笔试里算是比较有代表性的,题量不大、难度不低、坑位不少,考察的都是算法基本功和代码细节。九年过去,腾讯笔试的题型已经换了好几轮,但这套题依然被反复提及,原因很简单——它的出题逻辑和考察点,至今仍然是大厂笔试的主流。这篇文章我就以复盘的形式,把当年这套题里频次较高、也比较有代表性的几道题目拿出来完整拆一遍,从题目描述、解题思路到参考代码、易错点,一套流程走下来。无论你现在是在备战校招,还是单纯想看看大厂笔试到底考什么,这篇都值得收藏。

1. 2016年腾讯校招编程题全景回溯

1.1 当年的校招背景与笔试环境

2016年移动互联网还处在红利期,腾讯作为头部大厂,校招技术岗的简历投递量非常大。研发工程师岗位的笔试基本都走在线OJ系统,当年的主流平台是牛客网。笔试环境没有现在这么花哨,就是一套在线答题页面,客观题和编程题混在一起,编程题支持C/C++、Java等主流语言,每道题会给出输入输出样例,但不会有太大范围的测试用例提示。

这年研发岗笔试的编程题一般控制在3到5道,整体难度比现在的“ACM风格”要温和一些,但有一个特点非常明显:题意不长,坑不少。很多题看起来像是基础题,实际上一旦考虑不周,很容易出现部分样例通过、拿到0分或者一半分数的情况。也就是说,这套题的核心筛选逻辑不是看你懂多少高级算法,而是看你在有限时间内能不能写出健壮、正确、可运行的代码。

1.2 研发工程师岗位的考察逻辑:不挑方向,不挑语言,只挑基本功

要注意的是,2016年的“研发工程师”是统招岗位,不区分前端、后端、客户端等具体方向。所有人都用同一套笔试题,所以题目设置上有意避开特定业务场景,集中在算法与数据结构的通用能力上。按今天的话说,这套题就是一套“基本功体检”。

基本功体现在三方面:第一,基础算法是否扎实,比如排序、二分、动态规划、数论这些常见考点;第二,边界情况是否敏感,比如数组为空、输入相等、结果溢出这类情况是否处理到位;第三,代码能不能在无IDE提示的情况下一次写对。这三点对应到日常工作里,就是代码review时最常被挑出来的问题。所以这套题虽然叫“编程题”,实际上考察的是一个工程师最底层的代码素养。

1.3 题量分布与时间节奏

从当年参加过笔试同学的反馈来看,编程题普遍给60到90分钟,题量看起来不大,但每道题从读题、思考、编码到调试,平均下来一道题15到20分钟。这意味着你基本没有时间回头改前面的题,必须在第一次提交前尽可能保证正确性。

这也解释了为什么那几年笔试成绩出来以后,很多同学反映“都会做,但就是没AC”。问题不出在思路,而出在时间分配和代码细节上。比如组合计数忘取模、数组越界没检查、long long不够用、输入输出格式不对,任何一种情况都会让你与满分失之交臂。后面我会专门列一节实操避坑,这里先不展开。

2. 核心考点与出题逻辑拆解

2.1 基础数据结构与经典算法的权重

把这套题刷完一遍,你会发现它涉及的知识点集中在几个固定区域:排序、二分、字符串处理、动态规划、简单数论。没有线段树,没有网络流,没有复杂的平衡树,出题人刻意把题目控制在“科班学生大二大三就能掌握”的范围内。

为什么这么设计?我的理解是,校招研发岗进来以后要参与真实项目开发,考察的重点是你写业务代码、处理数据、排查问题的能力,而不是让你入职第一天就去做算法研究。基础算法代表的是逻辑思维能力和代码实现能力。排序能不能写对、二分会不会死循环、动态规划的状态转移方程能不能理清,这些直接反映一个人写代码的扎实程度。这也是为什么今天的大厂面试依然喜欢考基础题。

2.2 边界条件:大部分AC失败的原因

我当年帮学弟学妹们复盘这套题时发现,思路完全想错的场景并不多,反而是边界条件处理不到位导致AC失败的占了大多数。常见的边界条件有这些:输入的最小值、最大值、重复值、数组长度为1、目标值等于区间端点、结果超出int范围、模运算的中间结果可能为负,等等。

比如“有趣的数字”这道题,如果数组里所有数字都相等,最大差值对数不是0而是C(n,2),这个边界不处理好,样例过得了,一提交就挂。“素数对”里如果输入的数很小,素数表可能为空,也要专门处理。这些边界条件的核心是:你不能假设输入是“合理”的,而是要假设输入是“刁钻”的。一个工程师在写代码时有没有这种意识,从笔试就能看出一大半。

2.3 从笔试题看腾讯工程师的通用能力要求

回过头来看这套题,它其实在传递一个信号:腾讯需要的是能独立解决问题、代码够稳的工程师。笔试题目不会故意刁难人,但会把工程中“细节决定成败”的理念映射到算法题里。

举个很典型的例子,“geohash编码”这道题,看起来就是一个二分模拟,很多同学觉得简单,但真正写起来,区间更新、中点判断、二进制位拼接,每一步都有出错的可能。这就像真实项目里对接一个外部接口,流程看起来很简单,但参数边界、异常处理、返回值格式,任何一个细节没对齐都会出线上事故。所以这套题本质上是用算法题的方式,考察候选人能不能像写生产代码一样去处理细节。

3. 五道经典真题的完整复盘

3.1 小Q的歌单:组合计数与背包思想

题目描述(回忆版):

小Q有X首长度为A的歌曲和Y首长度为B的歌曲,现在想用这些歌曲组成一个总长度恰好为K的歌单。每首歌最多被使用一次,问一共有多少种组合方式。结果对1000000007取模。

输入格式:

第一行输入K,第二行输入A、X,第三行输入B、Y。

样例

输入: 5 2 3 3 3 输出: 9

思路分析

这道题表面上是问组合数,实际上可以转化为一个0-1背包问题。歌单的总长度K相当于背包容量,每首歌相当于一个物品,物品的“重量”就是歌曲长度,每个物品只能用一次,问恰好装满背包的方案数。

定义dp[i]表示组成总长度为i的歌单的方案数,初始dp[0]=1。先处理第一组歌曲,对于X首长度为A的歌,每首歌都有“选”和“不选”两种选择,所以从K到A倒序遍历,执行dp[j] = (dp[j] + dp[j-A]) % MOD。倒序的原因是保证每首歌只被使用一次。处理完第一组再处理第二组长度为B的Y首歌。

参考代码

#include <bits/stdc++.h> using namespace std; const int MOD = 1000000007; int main() { int K; cin >> K; int A, X; cin >> A >> X; int B, Y; cin >> B >> Y; vector<long long> dp(K + 1, 0); dp[0] = 1; for (int i = 0; i < X; i++) { for (int j = K; j >= A; j--) { dp[j] = (dp[j] + dp[j - A]) % MOD; } } for (int i = 0; i < Y; i++) { for (int j = K; j >= B; j--) { dp[j] = (dp[j] + dp[j - B]) % MOD; } } cout << dp[K] << endl; return 0; }

复杂度:时间复杂度O(K*(X+Y)),空间复杂度O(K)。当年这个数据范围完全够用。

易错点

  • 忘记取模,或者只在最后一步取模,导致中间结果溢出。
  • 同一个歌曲组内部,每首歌都要单独处理一次背包遍历,不能直接把X首歌曲一次性做“完全背包”处理,否则同一首歌会被重复使用。
  • dp数组要用long long或者在中途取模,因为组合数可能会非常大。

这道题还有另一种做法,就是直接组合数学计算C(X,i) * C(Y,j),枚举i和j满足iA + jB = K。两种做法都行,但背包思路更容易扩展到类似场景。

3.2 构造回文:最长公共子序列的巧妙变形

题目描述(回忆版):

给定一个字符串s,你可以删除其中任意数量的字符,使得剩下的字符组成一个回文串。求最少需要删除多少个字符。

样例

输入:abcda 输出:2

解释:删除b和c,得到“ada”,为回文串;或者删除c和d,得到“aba”,也是回文串。

思路分析

最少删除字符数等于字符串长度减去最长回文子序列的长度。而最长回文子序列有一个经典解法:把原串反转得到逆序串,求原串和逆序串的最长公共子序列(LCS)长度。

为什么可以这样转化?因为一个回文串从前往后读和从后往前读是一样的,那么原串中能构成回文子序列的那些字符,在逆序串中必然以相同顺序出现,所以它们就是原串和逆序串的公共子序列。反过来,任何一个原串和逆序串的公共子序列,在形态上都对应一个回文子序列。因此最长回文子序列长度就是LCS的长度。

参考代码

#include <bits/stdc++.h> using namespace std; int main() { string s; while (cin >> s) { string t = s; reverse(t.begin(), t.end()); int n = s.size(); vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0)); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (s[i - 1] == t[j - 1]) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); } } } cout << n - dp[n][n] << endl; } return 0; }

复杂度:时间复杂度O(n^2),空间复杂度O(n^2),n是字符串长度。当年这道题的数据范围n通常在1000以内,O(n^2)完全够用。也可以用滚动数组优化空间到O(n),但不是必须。

易错点

  • dp数组的边界要初始化为0,否则第一行第一列会出问题。
  • 输入可能有多个测试用例,要用while循环读取,否则只处理一次就结束了。
  • 字符串可能为空,这种情况下输出0。

这道题的价值在于它展示了“回文”和“公共子序列”之间的等价关系。以后再遇到类似“删除最少字符构造回文串”的题目,都可以直接套LCS的模板。

3.3 有趣的数字:排序与双指针统计

题目描述(回忆版):

小Q有n个数字,从这n个数字中任选两个组成二元组,问差值的绝对值最小有多少对,差值的绝对值最大有多少对。

输入第一行是n,第二行是n个数字。

样例

输入: 6 1 2 3 4 5 6 输出: 1 1

解释:最小差值绝对值为1,只有相邻的(1,2)等5对?等一下,这里需要仔细说明统计规则。实际输出取决于题目定义:差值为1的对数一共有5对,为什么样例输出是1呢?这说明我记忆中的样例可能有偏差。

所以我们直接按正确算法来推导:最小差值对数需要统计所有差值等于最小值的二元组。排序后相邻两个数的差一定是候选最小差值。如果数组是1 2 3 4 5 6,排序后相邻差都是1,最小差值为1,那么能组成差值1的相邻对是(1,2)、(2,3)、(3,4)、(4,5)、(5,6),一共5对。因此输出应该是5而不是1。所以这个样例并不是标准样例,我在这里只是说明思路,不要照搬样例。

正确的题目考察点就比较清晰了:

思路分析

第一步排序,这是处理差值统计的常规操作。排序后,最大差值一定出现在最大值和最小值之间,但要注意:差值最大的二元组有多少对,取决于最小值的个数和最大值的个数,也就是count(min) * count(max)。如果所有数字都相等,那么最大值等于最小值,此时最大差值对数就是C(n,2)。

第二步求最小差值对数。排序后,相邻两数的差值最小。先遍历一遍找到最小差值minDiff。如果minDiff等于0,说明存在重复数字,此时所有差值等于0的组合都来自重复数字内部,统计每个重复数字分组的大小size,累加C(size,2)。如果minDiff大于0,直接遍历相邻元素,统计差值等于minDiff的相邻对数即可,因为此时每个元素最多同时属于两个相邻对,而最小差值大于0保证不会出现跨多个元素的重复组合。

参考代码

#include <bits/stdc++.h> using namespace std; int main() { int n; while (cin >> n) { vector<int> a(n); for (int i = 0; i < n; i++) cin >> a[i]; sort(a.begin(), a.end()); long long minCount = 0, maxCount = 0; // 最大差值对数 if (a[0] == a[n - 1]) { maxCount = 1LL * n * (n - 1) / 2; } else { int minValCnt = 1, maxValCnt = 1; for (int i = 1; i < n; i++) { if (a[i] == a[0]) minValCnt++; else break; } for (int i = n - 2; i >= 0; i--) { if (a[i] == a[n - 1]) maxValCnt++; else break; } maxCount = 1LL * minValCnt * maxValCnt; } // 最小差值对数 int minDiff = INT_MAX; for (int i = 1; i < n; i++) { minDiff = min(minDiff, a[i] - a[i - 1]); } if (minDiff == 0) { int i = 0; while (i < n) { int j = i; while (j < n && a[j] == a[i]) j++; int cnt = j - i; minCount += 1LL * cnt * (cnt - 1) / 2; i = j; } } else { for (int i = 1; i < n; i++) { if (a[i] - a[i - 1] == minDiff) { minCount++; } } } cout << minCount << " " << maxCount << endl; } return 0; }

复杂度:时间复杂度O(n log n),主要是排序开销。空间复杂度O(n)。

易错点

  • 最大差值对数在“所有数字相等”时不是count(min)*count(max)而是C(n,2)。
  • 统计最小差值对数时,如果minDiff为0,不能直接遍历相邻对,否则会漏掉“同一个数字出现多次,任选两个都能构成差值0”的情况。
  • 计数可能超过int范围,要用long long。

这道题是典型的“排序+分类讨论”题型,看起来简单,但要把各种情况分清楚并不容易。面试或者笔试中遇到这种题,建议先在草稿纸上列出输入的所有可能情况再写代码。

3.4 素数对:筛选法在数论题中的应用

题目描述(回忆版):

给定一个正整数n,求有多少对素数之和等于n。例如n=10,满足条件的素数对有3+7和5+5,共2对。输入值范围不超过1000。

思路分析

这道题第一步用埃氏筛生成[2, n]范围内的素数表。然后枚举第一个素数i从2到n/2,判断i和n-i是否都是素数,如果是则计数加一。因为i从2到n/2枚举,所以不会出现重复计数,而且能正确统计5+5这类两个素数相同的组合。

埃氏筛的原理是:从2开始,把所有2的倍数标记为合数,然后找下一个未被标记的数,再把它的倍数全部标记。筛完以后剩下的就是素数。这个算法是数论题的常客,笔试中出现的频率很高。

参考代码

#include <bits/stdc++.h> using namespace std; int main() { int n; while (cin >> n) { vector<bool> isPrime(n + 1, true); isPrime[0] = isPrime[1] = false; for (int i = 2; i * i <= n; i++) { if (isPrime[i]) { for (int j = i * i; j <= n; j += i) { isPrime[j] = false; } } } int count = 0; for (int i = 2; i <= n / 2; i++) { if (isPrime[i] && isPrime[n - i]) { count++; } } cout << count << endl; } return 0; }

复杂度:时间复杂度O(n log log n),空间复杂度O(n)。

易错点

  • 筛法初始化时,isPrime[0]和isPrime[1]必须手动置为false。
  • 内层循环从i*i开始标记,避免重复标记,这是埃氏筛的常见优化。
  • n可能小于4,这样连一对素数都组不出来,直接输出0。
  • 输入可能有多个测试用例,需要循环读取。

这道题本身不难,属于“送分题”,但如果素数表生成错误或者边界没处理好,就很容易变成“送命题”。

3.5 geohash编码:二分思想的工程化落地

题目描述(回忆版):

geohash编码常用于将经纬度转换为字符串,其中一步是对纬度进行二进制编码。给定一个纬度值x(-90到90之间),采用如下规则编码:

  1. 初始区间为[-90, 90],取区间中点mid。
  2. 如果x >= mid,则当前位编码为1,同时将区间左端点更新为mid;否则当前位编码为0,同时将区间右端点更新为mid。
  3. 重复上述步骤,直到生成6位二进制编码。

输出这6位二进制编码。

思路分析

这道题是对二分查找的变形考察。每次循环确定一个二进制位,同时把区间缩小一半。循环6次,每次根据x与mid的大小关系确定当前位,并更新区间。

这里有一个值得注意的点:题目中的边界条件是“x >= mid编码为1”,还是“x > mid编码为1”,不同题目可能有不同定义。在写代码时要仔细读题,不能想当然。

参考代码

#include <bits/stdc++.h> using namespace std; int main() { double x; cin >> x; double left = -90.0, right = 90.0; int res = 0; for (int i = 0; i < 6; i++) { double mid = (left + right) / 2.0; if (x >= mid) { res = res * 2 + 1; left = mid; } else { res = res * 2; right = mid; } } for (int i = 5; i >= 0; i--) { cout << ((res >> i) & 1); } cout << endl; return 0; }

复杂度:时间复杂度O(1),因为编码长度固定为6位。空间复杂度O(1)。

易错点

  • 如果直接按“res的二进制位”输出,需要注意res的位数,不满6位时要在前面补0。上面代码从5到0逐位输出,可以保证输出6位。
  • mid的类型要用double,不能用int,否则精度丢失会导致区间更新错误。
  • 区间端点的初始值要写对,一个是-90,一个是90,写反了整套编码就完全错。

这道题后面还可以继续扩展成完整的geohash编码,包括经度二分和base32转码。如果在面试中遇到,能顺带把这两步讲清楚,会是不错的加分项。

4. 笔试实操心得与避坑指南

4.1 时间分配:先拿基本分,再攻难题

我那会儿参加笔试最大的教训,就是在第一道题上死磕太久,结果后面几道基础题都没时间写。现在回想起来,笔试编程题很忌讳“完美主义”。正确的时间分配策略是:先把所有题目都读一遍,判断难度,从最简单的开始做,保证会做的题全部拿到分,再回头思考难题。

这套题里,“素数对”和“geohash编码”属于读完题就能写的类型,“有趣的数字”属于中等难度,“构造回文”和“小Q的歌单”需要一点思考时间。如果按题目顺序死磕,很可能卡在某一题上。从易到难推进,至少能保证3道题AC,这已经能超过很多候选人了。

4.2 输入输出与代码提交的易错点

在线笔试的输入输出经常坑人,常见情况包括:输入包含多组数据、每组数据格式不同、输出结果之间需要换行、输出结果有额外空格会不会判错等。我的习惯是:

第一,能用while(cin >> n)循环读取的,一定用循环,因为你不确定测试数据到底有几组。第二,输出时严格按题目要求,不要多输出任何提示文字,比如“请输入”这类提示在OJ里会被当作错误输出。第三,如果结果需要取模,每一步计算都取模,不要只在最后取一次。

另外特别提醒一点:C++的运算符优先级很容易踩坑,比如 (res >> i) & 1 这里,位移的优先级比按位与高,所以可以不加括号,但为了可读性最好还是加上。这种细节在笔试中不扣分,但在代码review里一定会被同事提出来。

4.3 设计测试用例:把边界当作一等公民

我在刷题复盘时发现,很多同学写完代码只跑题目给的样例,跑通了就提交,结果一提交就挂。这是因为题目给的样例通常只覆盖最普通的情况,不会帮你覆盖边界。

我的建议是养成“边界测试”的习惯。每写完一道题,至少测试这几种输入:最小规模(n=1)、最大规模(n取题面上限)、全相等、全不同、递增、递减、随机大数、结果溢出场景。用“构造回文”这道题举例,至少要测空串、单字符、全相同字符、已经回文的串、完全逆序的串。用“有趣的数字”举例,至少要测所有数字相同、有重复数字且最小差值为0、没有重复数字且最小差值大于0。把这些测试用例跑一遍,代码的正确性基本就有保障了。

很多同学觉得这是浪费时间,但实际上,花5分钟测边界,比提交后挂掉再花10分钟找原因要划算得多。

5. 从2016到今天的题目演进与备考建议

5.1 校招笔试的出题趋势:从纯算法到场景抽象

最近的腾讯校招笔试,编程题已经很少直接考“给你一个数组,求如何如何”这种纯数学题了,更多是把算法包装在业务场景里。比如让你设计一个推荐系统的排序逻辑,或者模拟一个消息队列的消费顺序。场景化出题的本质没有变,考察的还是同样的算法和数据结构,但需要先读懂题意,从场景中抽象出数学模型。

这不是说2016年的题目就没有价值。恰恰相反,基础算法是所有场景题的底座。你现在能把LCS、背包、二分、筛选法吃透,到了场景题里照样能看懂题目背后的算法本质。

5.2 九年后回头看:这套题还能刷吗?

我的答案是,当然能刷,而且很适合作为校招笔试的入门训练。原因有三个:第一,题量适中,五道题覆盖了五个常见考点,刷一遍不会花太多时间;第二,难度曲线合理,从简单到中等都有,适合用来定位自己的薄弱环节;第三,网上题解和相关讨论非常多,刷完以后很容易找到对应的复盘文章,形成“做题-对答案-总结”的学习闭环。

不少已经上岸的工程师在写面经时,都会提到自己当年刷过这套题。它不一定是你拿到offer的决定性因素,但一定是你建立笔试信心的重要起点。

5.3 系统化备考路线与复盘方法

如果你现在才开始准备校招笔试,我的建议是不要只盯着某一年的题,而是按知识点系统刷。比较稳妥的路线是:先刷剑指Offer,把高频面试题过一遍;然后刷LeetCode Hot 100,按数组、链表、树、动态规划、字符串、二分、贪心、数论这些专题逐个攻破;最后再用“腾讯2016研发工程师编程题”这类真题卷做模拟,检验整体水平。

刷题过程中,一定要做错题记录,写清楚自己当时的错误思路和正确解法。不要追求刷题数量,要追求每道题都能讲清楚两个问题:为什么这样解是对的,还有没有更好的解法。能把一道中等题说到让一个完全不懂的人听明白,才说明你是真的掌握了。

我自己的体会是,面试官其实不指望你把每道题都写出最优解,更看重的是你面对一个陌生问题时的分析过程和代码风格。这套2016年的题,恰好就能帮你练出这种分析能力。当你把一道题从题意、思路、实现到测试完整走一遍,你会发现,大厂笔试并没有传说中那么可怕。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 22:01:18

无需换浏览器:用OpenAI API把AI能力接入现有工作流

过去一年&#xff0c;OpenAI 的产品迭代速度很快&#xff0c;因此“AI 浏览器”成了一个不断被讨论的话题。很多人看到 ChatGPT 能读网页、总结文档、生成代码&#xff0c;就以为需要换一个内置 AI 的专用浏览器&#xff0c;才能把这些能力融入日常操作。实际上&#xff0c;Ope…

作者头像 李华
网站建设 2026/8/30 22:00:44

天正CAD免费下载安装教程:正版渠道与AutoCAD版本匹配指南

很多刚接触建筑设计、土木工程或室内装饰的朋友&#xff0c;都会先听到“天正CAD”这个名字&#xff0c;然后在网上一搜&#xff0c;看到大量“天正cad下载安装教程”“天正cad免费下载”的帖子。这些帖子来源繁杂&#xff0c;有的版本过期&#xff0c;有的下载链接指向第三方站…

作者头像 李华
网站建设 2026/8/30 22:00:22

Claude记忆功能升级:跨聊天记忆与Cowork多会话协作实战

前一阵在持续维护一个中大型项目时&#xff0c;我发现一个很“分裂”的现状&#xff1a;上午和 Claude 聊好的接口规范&#xff0c;下午开一个新会话它又像“失忆”一样重新问一遍&#xff1b;团队里另一位同事想复用我的项目背景&#xff0c;也要从头描述大半天。最麻烦的是&a…

作者头像 李华
网站建设 2026/8/30 22:00:16

从“生成快”到“可维护”:AI Skills如何让辅助编程告别屎山代码

最近我让 AI 帮我写一个批量文件重命名脚本&#xff0c;它一次成功&#xff1b;半个月后当我想加一个排除目录的功能&#xff0c;打开代码却开始怀疑人生。变量名是 a、b、c&#xff0c;逻辑全堆在 main 里&#xff0c;错误处理为零&#xff0c;日志也没留。改一行&#xff0c;…

作者头像 李华
网站建设 2026/8/30 22:00:08

字节跳动前端实习面经:从准备到三面全流程复盘

2020前端实习面经:字节跳动写这篇面经的时候&#xff0c;我刚从字节的实习面试流程里走出来不久。2020年这个时间点&#xff0c;前端岗位的竞争已经相当激烈&#xff0c;尤其是字节这种体量的公司&#xff0c;一个实习岗放出来&#xff0c;简历池里各种背景的人都有。我自己的背…

作者头像 李华
网站建设 2026/8/30 22:00:00

Rust CLI 工具 Presse:本地批量 PDF 压缩与合并实战

PDF 这个格式在日常办公和开发场景里太常见了&#xff0c;但真到了要批量压缩一批扫描件、合并十几个章节文档的时候&#xff0c;你会发现那些在线工具上传慢、有文件大小限制&#xff0c;还总担心隐私泄漏。今天要看的这个项目&#xff0c;就是专门解决这个痛点的&#xff1a;…

作者头像 李华