ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Coding面试高频题全解析:从快排到Redis分布式锁

AI Coding面试高频题全解析:从快排到Redis分布式锁 这两年我做技术面试官陆续面过不少投AI Coding方向的候选人。简历上清一色写着“熟练使用AI编程助手”结果一到笔试和代码面水平落差比想象中大。这篇内容是我整理的真实AI Coding面试题合集每道题都带实现和配套解法包括快速排序的Java手写、LRU缓存的链表实现、JWT实现Token续签、Redis分布式锁这些高频考题也会聊聊AI Coding时代笔试环节到底是怎么考的。适合正在准备AI Coding笔试和面试的同学也适合团队里做技术评审、带新人写代码的工程师参考。标题说是“面试题整理”实际上更像一份能直接照着练的备考手册看完之后至少心里有数哪些题值得反复刷哪些解法是面试官真正想听的。1. AI Coding面试到底在考什么先搞懂出题人意图1.1 面试官视角AI Coding候选人最常暴露的3个薄弱点AI Coding岗位和其他后端开发岗位不一样笔试环节通常不会只考“会不会写代码”还会同时考察“会不会借助AI工具高质量地写代码”。但我面试下来发现大多数候选人的问题不是不会用工具而是不会判断工具输出的好坏。最典型的三个薄弱点第一能读懂AI生成的代码但说不清为什么这样写边界条件一塌糊涂第二遇到报错只能反复把错误堆栈丢给AI自己没有定位问题的思路第三代码风格和命名一塌糊涂甚至出现同一份代码里混着两种完全不同的变量风格。这三个问题的本质是同一个把AI当成了“代写器”而不是“结对搭子”。面试官并不介意你用AI真正一票否决的是你连基础能力都没有只是拿AI生成的答案往上贴。所以后面所有题目我都按“如果是我自己写会怎么写”的视角来拆解AI只是加速工具不是替身。1.2 为什么说“只会让AI写代码”的候选人会被一票否决有段时间网上都在讨论“AI Coding的到来会不会让代码质量下降”我的答案是工具本身不会让质量下降使用方式会。面试官心里都有一杆秤一个候选人如果能在30分钟里用AI完成一道题并讲清楚每一步为什么这么做这反而是加分项说明他有工程判断力。反过来如果只贴一段AI生成的结果题目稍微改一个条件就崩那结论很明显——这岗位招进来以后线上出问题也只能靠AI兜底兜不住的那天就是事故日。所以我会在题目里刻意设计“小改动”来验证候选人比如你写完快排我忽然说数组里有一半重复元素你的写法会不会退化写完JWT续签我追问refresh token被重放怎么办。这些问题AI都知道答案但你得自己接得住。这是一个很现实的信号AI Coding时代核心竞争力的门槛从“能写出代码”变成了“能判断代码对不对、好在哪、怎么改”。2. 真实高频真题拆解算法与数据结构篇附实现2.1 快速排序Java实现手写细节决定你能不能过这一关快速排序几乎出现在每一次面试里但能干净利落手写出来的人真的不多。先贴一份我认为面试时最容易讲清楚的写法轴点用随机选择避免固定选第一个元素时被有序数组打爆。public class QuickSort { public void quickSort(int[] a, int l, int r) { if (l r) { return; } int p partition(a, l, r); quickSort(a, l, p - 1); quickSort(a, p 1, r); } private int partition(int[] a, int l, int r) { // 随机选一个轴点交换到末尾避免有序数组导致 O(n^2) int idx l (int) (Math.random() * (r - l 1)); swap(a, idx, r); int pivot a[r]; int i l; for (int j l; j r; j) { if (a[j] pivot) { swap(a, i, j); i; } } swap(a, i, r); return i; } private void swap(int[] a, int i, int j) { int t a[i]; a[i] a[j]; a[j] t; } }这段代码里真正的考点有三个。第一个是边界l r时递归必须结束很多候选人会写成l r虽然多数情况能跑但一旦传入空数组或区间为空就崩。第二个是轴点选择固定取a[r]遇到已经有序的数组就是灾难时间复杂度退化到 O(n^2)随机化之后期望复杂度回到 O(n log n)。第三个是循环不变量整个for循环跑完之后i左边全是小于 pivot 的元素i右边到r-1全是大于等于 pivot 的元素这一步讲不清楚面试官基本可以判断你只是背下的代码。接着我一般会追问“如果数组里重复元素很多你的实现会不会退化”标准答案是双路快排或三路 partition。三路 partition 把数组分成“小于、等于、大于”三个区间等于区间直接跳过重复元素很多时反而更快。候选人如果能把这点主动讲出来这一题基本拿满分。2.2 LRU缓存手写实现链表加哈希表的黄金搭档LRU 是另一道高频题考察点很聚焦数据结构怎么设计。思路一句话说透——哈希表负责 O(1) 查找双向链表负责 O(1) 删除和移动。为什么是双向链表因为当你找到某个节点要把它移到头部时必须知道它的前驱节点才能断开单向链表做不到 O(1)。import java.util.HashMap; import java.util.Map; class LRUCache { static class DLinkedNode { int key; int value; DLinkedNode prev; DLinkedNode next; DLinkedNode() {} DLinkedNode(int key, int value) { this.key key; this.value value; } } private final int capacity; private int size; private final MapInteger, DLinkedNode cache new HashMap(); private final DLinkedNode head new DLinkedNode(); private final DLinkedNode tail new DLinkedNode(); public LRUCache(int capacity) { this.capacity capacity; head.next tail; tail.prev head; } public int get(int key) { DLinkedNode node cache.get(key); if (node null) { return -1; } moveToHead(node); return node.value; } public void put(int key, int value) { DLinkedNode node cache.get(key); if (node null) { DLinkedNode newNode new DLinkedNode(key, value); cache.put(key, newNode); addToHead(newNode); size; if (size capacity) { DLinkedNode removed removeTail(); cache.remove(removed.key); size--; } } else { node.value value; moveToHead(node); } } private void addToHead(DLinkedNode node) { node.prev head; node.next head.next; head.next.prev node; head.next node; } private void removeNode(DLinkedNode node) { node.prev.next node.next; node.next.prev node.prev; } private void moveToHead(DLinkedNode node) { removeNode(node); addToHead(node); } private DLinkedNode removeTail() { DLinkedNode res tail.prev; removeNode(res); return res; } }很多候选人会直接用LinkedHashMap一行交差我完全不反对但面试官一定会追问LinkedHashMap的accessOrder是什么为什么它底层能实现 LRU如果不会答那还不如老老实实手写双链表。另外这道题还值得往工程方向延伸Redis 的过期淘汰是近似 LRUMySQL 的 InnoDB buffer pool 把 LRU 链表分成 young 区和 old 区防止全表扫描把热点数据冲掉。你哪怕只熟悉其中一个面试观感都会完全不同。2.3 一道容易翻车的组合求和题AI生成代码的“边界黑洞”除了经典题AI Coding笔试里特别爱考“看着简单、边界极多”的题。比如这种给定不重复整数数组candidates和整数target返回所有和为target的组合同一个数字可以重复使用。回溯模板谁都能写但候选人用 AI 生成之后经常出现两个问题一是不排序就去重导致结果里有重复组合二是递归终止条件只判断target 0不判断target 0一旦选择某个大数超出目标就直接索引越界。public ListListInteger combinationSum(int[] candidates, int target) { ListListInteger res new ArrayList(); Arrays.sort(candidates); dfs(candidates, target, 0, new ArrayList(), res); return res; } private void dfs(int[] candidates, int target, int start, ListInteger path, ListListInteger res) { if (target 0) { res.add(new ArrayList(path)); return; } for (int i start; i candidates.length; i) { if (candidates[i] target) { break; // 剪枝排序后才能这样写 } path.add(candidates[i]); dfs(candidates, target - candidates[i], i, path, res); // 可以重复选同一个数所以传 i 而不是 i1 path.remove(path.size() - 1); } }这道题如果让 AI 生成十次里有七八次会写错dfs参数里的start。它可能写成i 1那结果就从“可重复使用”变成“每个数只能用一次”题目直接跑偏。这就是典型的“AI 没有读题能力”场景它只会按统计概率出下一段代码。我个人建议这类回溯题不要从头到尾让 AI 写而是自己先把递归语义定好再让 AI 补模板最后自己逐行过一遍终止条件。3. 工程实现题别让“背过的八股”坑了你3.1 JWT实现Token续签滑动续期还是刷新令牌附代码JWT 本身无状态但无状态也意味着登录态失效不及时。所以面试题里经常出现“JWT 过期了怎么办”或者“JWT 实现 Token 续签怎么做”常见方案有两种滑动续期和双 Token。滑动续期是每次请求时检查剩余有效期如果少于某个阈值就用旧 Token 重签一个新 Token 返回给前端双 Token 则是短期 access token 加长期 refresh tokenaccess token 过期后用 refresh token 换新的。两种方案必须结合业务场景选不是越复杂越好。我直接给一个双 Token 的简化版实现重点看续签那段逻辑。// 生成 access token过期时间 15 分钟 String accessToken Jwts.builder() .setSubject(userId) .setExpiration(new Date(System.currentTimeMillis() 15 * 60 * 1000)) .signWith(secretKey) .compact(); // 生成 refresh token存储 Redis过期时间 7 天 String refreshToken UUID.randomUUID().toString(); redisTemplate.opsForValue().set( refresh: userId, refreshToken, Duration.ofDays(7) );续签接口的核心步骤是前端把 refresh token 传到后端 - 后端检查 Redis 里存的是不是同一个 - 如果是重新签发 access token并且可以顺势把 refresh token 的过期时间整个滑动一下保证活跃用户不用每 7 天重新登录一次 - 如果不是说明 refresh token 可能被盗用直接把该用户所有 token 作废。这里有几个面试官惯用的追问点。第一个为什么 refresh token 要设计成不透明的 UUID而不是再签一个 JWT因为 refresh token 本来就不需要承载业务数据UUID 更短且万一泄露没法被离线解密。第二个refresh token 被重放怎么办常见做法是“重用检测”每次换新的 refresh token 后立即让旧的失效如果发现有人拿旧 token 再换一次就判定异常。第三个要不要把 access token 也存 Redis存了就失去了 JWT 无状态的意义但换来了实时吊销能力属于典型的取舍题。面试时能把这个取舍讲清楚比背十个 JWT 知识点有用得多。3.2 分布式锁面试题Redis锁的正确姿势与常见翻车点分布式锁是 AI Coding 面试里“八股浓度”最高的一道题但大部分候选人只记得SETNX。我一般会直接问“用 Redis 实现分布式锁要注意什么”标准答案是加锁必须保证原子性值必须是随机字符串释放锁必须用 Lua 脚本保证“判断是自己”和“删除”两步原子执行。// 加锁SET key value NX EX Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, requestId, Duration.ofSeconds(30)); if (Boolean.TRUE.equals(locked)) { // 获取锁成功执行业务逻辑 } // 释放锁Lua 脚本保证原子性 String luaScript if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; redisTemplate.execute(new DefaultRedisScript(luaScript, Long.class), List.of(lockKey), requestId);每次面试我都会问一个送命题为什么释放锁时要判断 value直接del不就行了真正场景是A 线程持有锁后业务执行超过过期时间锁自动释放了B 线程拿到锁开始干活A 终于干完了执行del把 B 的锁删了。加上随机 value 后只能删掉自己创建的那把锁删不掉 B 的。再往深一点就是可重入和“看门狗”了。可重入锁的本质是给同一个线程同一个 key 计数Redis 本身不提供这能力得在客户端用ThreadLocal做重入计数。看门狗是定时续期的意思类似 Redisson 里默认给锁每 10 秒续期一次直到业务结束。这两点只要主动提出来就已经超过大多数只会背 SETNX 的候选人一大截。另外我想提醒一句主从切换时 Redis 分布式锁会丢极端情况下锁会失效所以才会有 Redlock 这类争论不休的方案。这个争议点到为止就行能说出来会让面试官觉得你对方案边界有认知但没必要站队。4. AI Coding笔试现场实录我最常看到的三类翻车4.1 笔试实录AI生成的代码为什么一眼就能被认出现在的 AI Coding 笔试平台一般都会开“AI 辅助模式”允许你用工具但要求提交后讲清楚思路。我见过最典型的翻车现场有三个大家提前避雷。第一个候选人全程只粘贴 AI 输出完全不读代码。有一道题要求实现一个二分查找的变体AI 生成的代码跑样例全绿但题目要求的是“返回第一个大于 target 的位置”AI 写成了“返回第一个等于 target 的位置”。候选人完全没有发现被追问后愣在原地。这类候选人笔试基本垫底。第二个代码风格突变。前面两道题还是驼峰命名、标准缩进第三道题突然变成变量全叫a1、b2缩进乱成一团函数行数还特别长。面试官很容易判断“这道题不是本人写的”。所以哪怕你用了 AI也要花一分钟把代码通读一遍改掉 AI 风格再交上去。第三个不会 debug。AI 生成的代码编译不过去候选人把报错信息原样发给 AIAI 修改后另一处又报错来回折腾十分钟。正确的做法是自己先读报错堆栈定位到具体行理解问题后再决定自己修还是让 AI 修。面试官要看的是排查能力不是你和 AI 的对话轮数。4.2 AI辅助编码的正确姿势从提示词到代码评审的闭环很多候选人不知道什么叫“正确地用 AI 写代码”。我建议按这个闭环走先自己分析问题画出关键逻辑再让 AI 生成脚手架然后自己实现核心函数最后让 AI 做 review。这样一来AI 解决的是“机械劳动”核心判断留在你手里。举个例子我写提示词时会刻意带上工程约束请帮我实现以下需求 1. 输入整数数组 nums可能包含重复元素 2. 输出所有和为 target 的三元组要求不重复 3. 语言Java 17无第三方依赖 4. 方法签名已给定请按签名实现 5. 不要解释基础语法不要写 main 方法 6. 边界情况请单独注释。这个提示词的价值在于把需求、约束、输入输出边界都定义清楚了AI 生成的代码可参考度明显更高。代码生成之后我还会再加一句“请 review 这段代码重点检查数组越界、重复组合、空指针三个问题。”这一步看着简单但能把 AI 从“生成器”切换成“审查者”相当于多了一个结对伙伴。我在实际面试里特别愿意看到候选人用这类方式展示“人机协作”能力。因为生产环境里没有人只让 AI 写一遍就直接上线总是要有人负责 review、补测试、加日志。你把这个过程搬到笔试本身就是专业素养的体现。5. 代码质量与规范AI Coding时代被低估的加分项5.1 命名与结构AI生成的代码为什么看起来就是“不太对”“AI Coding 会不会让代码质量下降”这个问题落到实操层面最直观的就是命名和结构。AI 默认生成的代码喜欢用很短的变量名或者反过来用特别长的描述性命名一口气写一个 300 行的函数中间塞满data、temp、result。这些代码功能没问题但可维护性极差。面试官一眼就能看出哪些是 AI 直接生成的因为你让一个有经验的人写代码他会下意识思考“这个变量的生命周期到哪结束”“这段逻辑能不能抽一个方法出来”。我见过一段 AI 生成的订单状态流转代码一个方法里嵌套四层 if变量名全是s1、s2。候选人自己都说不清s2是什么。这就是 AI 时代的新八股懂原理但写不出整洁代码。这里给一个改善模板循环里的临时变量尽量语义化比如pendingOrder而不是temp超过 20 行的核心逻辑就考虑拆方法public 方法必须对外表达“做什么”private 方法可以更多表达“怎么做”。这三点做到了AI 生成的代码立刻有“人味”。5.2 边界与测试守住代码质量的最后一公里面试过程中我还会让人现场补测试用例。不是说非要用 JUnit而是看候选人有没有“边界意识”。比如上面的组合求和题我会问如果candidates为空数组怎么办target为负数怎么办数组里有超大数会不会性能崩能想到这些问题的人才是生产环境敢放代码的人。大家可以准备一个通用的边界测试清单空输入、单个元素、全相等、正序、倒序、极大极小值、null、并发场景。每一类都对应一类典型的线上 bug。我见过很多候选人面试时把功能代码写得飞快一到边界测试就卡住很可惜。因为你写出的代码如果只能在“标准输入”下工作那 AI 也能写为什么要招你呢6. 常见问题速查现场追问最容易答不上的点6.1 高频追问汇总速查表问题错误示范推荐回答方向快排最坏情况是什么不知道或只回答“O(n^2)”有序数组 固定轴点用随机化或三数取中避免LRU 为什么用双向链表“为了遍历”需要 O(1) 删除节点必须知道前驱JWT 续签怎么做“重新签一个就行了”区分滑动续期和双 Token结合吊销和重用检测Redis 分布式锁为什么可能失效“Redis 很稳定不会失效”主从切换、过期时间过短、业务执行超时你用 AI 写代码会不会降低质量“不会AI 很可靠”取决于 review 和边界测试人负责判断代码里出现了 AI 风格怎么办直接忽略通读、改命名、补注释、加测试这张表是我个人常用的速查框架核心原则就一条面试官要的不是唯一的“正确答案”而是你面对开放问题时的判断框架。只要你能沿着“方案对比 - 取舍分析 - 落地验证”这条线讲哪怕最后结论不是面试官心里的那个也不会得低分。6.2 不会写也要给出思路现场应对的通用框架笔试遇到完全没思路的题最忌讳的是干坐着不说话。哪怕是在笔试平台我也会让候选人把思考过程写成注释。一个通用的思路框架是先定义输入输出再考虑暴力解最后优化复杂度。比如看到“单链表排序”没写过归并排序可以先评论一句“暴力解法是把所有值取出来排好序再放回去时间复杂度 O(n log n)但额外空间 O(n)如果追求原地归并需要找中点、合并两个有序链表”。这一段写在注释里面试官再追问时你已经证明自己有结构化思维能力了。真到面试对话环节哪怕写不全代码你也可以说“我目前没有完整解法但我知道大概会用到快慢指针找中点”这远比沉默或硬编强。AI Coding 岗位尤其看重这个能力因为以后你遇到 AI 生成的陌生代码也需要这种“不完全懂但能给出分析路径”的能力。7. 最后想对准备面试的人说几句这篇文章整理了快排、LRU、组合求和、JWT 续签、Redis 分布式锁以及 AI Coding 笔试的实战经验。我个人在实际带教和面试里体会到的是AI Coding 面试题的难度其实不在“题本身”而在“你会不会像一个成熟的工程师那样使用 AI”。基础算法是你判断 AI 输出质量的底线工程细节是你和面试官建立信任的桥梁代码规范是你区别于“只会贴答案的人”的核心证据。如果只让我给一条最实用的建议那就是从现在开始每次用 AI 写完代码都坚持做两件事自己把核心逻辑讲一遍再让 AI 帮你 review 一批边界条件。坚持两个星期你的代码能力反而会比不用 AI 时提升更快。最后再分享一个小技巧面试前一天把自己写过的代码按“题目、思路、关键代码、可能追问”整理成一张表快速过一遍。这个习惯帮我压中了不止一次面试题希望也能帮到你。
返回列表