
简介针对 lintcode 平台的算法与数据结构题目这份压缩包提供 Java 与 Python 两种语言的完整实现方案既能覆盖初学者学习算法所需也能满足有基础开发者提升解题能力的需求尤其适合作为毕业设计的参考资料。包内共 36 个文件包含 11 个 Java 源码、11 个 Python 源码、12 个 Markdown 说明文档及少量 Git 配置文件压缩后仅 17KB轻量方便。目前已有 46 人学习浏览。资源按题目单独归档每个题目均附有 Java 和 Python 两种语言的解答代码配套 Markdown 文档详细说明算法基本思想、时间与空间复杂度评估、数据结构的选择与优化策略所涉及内容覆盖打劫房屋、超级丑数、中位数、爬楼梯、最小路径和、二分查找等 lintcode 经典题型。通过对比同一算法在不同语言中的写法与性能差异读者可以更直观理解不同语言下如何设计合理的数据结构与算法提升实际编码和问题解决能力对算法类毕业设计或技术面试准备都具有较高的参考价值。1. 为什么我把 LintCode 刷题源码同时写成 Java 和 Python 两个版本刷过 LintCode 的人都知道题目拿下来最容易卡住你的不是算法本身而是对语言特性和底层数据结构的熟悉度。我拿到这份「基于Java和Python的分析、实现lintcode的算法、数据结构」时第一反应不是把它当代码包而是当一份双语言对照的解题笔记。它把同一道题分别用 Java 和 Python 实现正好覆盖了面试和工程两类场景Java 用来扣边界和工程感Python 用来快速验证思路。适合准备大厂算法面试、正在刷题但总在换语言之间纠结的人。你在 LeetCode 或 LintCode 上提交时通常只写一种语言时间一长就形成路径依赖。Java 的强类型让人不敢乱写Python 的简洁又容易让人忽略类型。双语言实现逼着你同时关注接口设计和运行效率这正是这份资料最有价值的地方。我后面会按分析题、写 Java、写 Python、排错、整理素材的顺序把整个流程展开。2. 分析 LintCode 题目从题意到测试用例先于代码的解题步骤2.1 读题后先列输入输出边界LintCode 的隐藏用例和边界LintCode 的题目描述通常很简短像“假设一个旋转排序数组在某点上旋转找到最小值”这种话如果不逐字抠边界第一版代码大概率过不了隐藏用例。我个人的习惯是拿到题先不写代码先在一张表格里列三样东西输入的数据类型、数据范围、异常情况。比如找旋转排序数组最小值我会写下输入期望输出说明[4,5,6,7,0,1,2]0常规旋转[1]1单元素[]-1空输入按题目约定返回[2,2,2,2]2全重复必须返回那个值你可能会说这有什么好列的但实际刷题时很多 WA 不是算法错而是边界没考虑。比如题目说“数组元素可重复”那[2,2,2,2]和[1,1,2,1]这类用例必须自己补上。用 Python 写个测试函数把表格里的用例全部塞进去跑一次就知道边界假设对不对。这种“先列用例再写算法”的流程能省下至少一半的 debug 时间。对于第一版代码我一般会把输入参数名起得非常直白比如nums、target这样在测试脚本里也容易对应。LintCode 方法签名中param注释会写清楚类型但隐藏用例里常常出现空列表、null、超大整数。Java 里null和空数组是两回事Python 里None和[]也是两回事这些差异如果在分析阶段不写下来写代码时就会模棱两可。一个明确的边界表就是这段时间的“后悔药”后面所有实现都拿它当验收标准。我尤其推荐用 pytest 的 parametrize 把边界用例批量挂上去例如import pytest pytest.mark.parametrize(nums,expected, [ ([4,5,6,7,0,1,2], 0), ([1], 1), ([2,2,2,2], 2), ([], -1), ]) def test_find_min(nums, expected): assert find_min(nums) expected这个写法的好处是哪个用例挂了直接显示参数值和期望值而不是停在一个中途断言上。等你把边界表里的每个条件都转成一个 parametrize 用例分析阶段才算真正完事。2.2 用 Java 写主方法签名用 Python 写单元测试一种可复现的分析流程我习惯把“分析”这一步分成两个半场先用 Java 写出符合 LintCode 判题器期望的方法签名再用 Python 写一个 pytest 测试文件把所有边界用例挂在上面。为什么要这样组合因为 Java 的强类型能逼你确认参数和返回值比如两数之和的签名是public int[] twoSum(int[] nums, int target)看到int[]你就知道不能返回ListInteger而 Python 的 pytest 写起来快改起来也快适合在思路还不稳定时反复试。例如两数之和我先在 Java 文件里只写方法签名和 TODOpublic int[] twoSum(int[] nums, int target) { // TODO: 用哈希表记录 num - index return null; }然后在 Python 测试文件里写预期行为from solution import two_sum def test_two_sum(): case_one ([2, 7, 11, 15], 9, [0, 1]) case_two ([3, 2, 4], 6, [1, 2]) case_three ([3, 3], 6, [0, 1]) for nums, target, expected in [case_one, case_two, case_three]: assert two_sum(nums, target) expected, f{nums}, {target} - {expected}这里的逻辑是Java 端只定义“长什么样”Python 端验证“做什么”。等 Python 版本通过全部用例后再回 Java 端正式实现。这样分析流程是可复现的边界表 Java 签名 Python 测试三样东西缺一不可。你可能会问为什么不直接在 LintCode 网页里试因为本地有 IDE 的补全和调试跑用例更快而且把测试文件留在项目里下次回看时还能知道当时是怎么思考的。参数说明pytest 的assert一条条执行失败会直接定位到具体实例。如果你用的是 Java 的 JUnit也可以做到同样效果但 Python 不需要编译改一下测试数据立刻就能跑。这个流程对新手来说尤其值——你能把“猜题”变成“验题”。这里有一个细节Python 测试里我用了f{nums}, {target} - {expected}这样断言失败时能看到具体输入不用回头去猜是哪个用例挂了。2.3 复杂度估算先算再写避免 O(n^2) 翻车LintCode 很多题目在题目描述里会写“时间复杂度 O(log n)”或“不使用额外空间”但有些不会。我的底线是开工前先估算最坏情况下的复杂度。常见做法是画一张表把常用数据结构的基本操作复杂度写出来贴在屏幕旁边数据结构查找插入删除数组无序O(n)O(n)O(n)哈希表O(1) 平均O(1) 平均O(1) 平均二叉搜索树O(log n) 平均O(log n) 平均O(log n) 平均堆O(n) 最坏O(log n)O(log n)然后对照题目给的数据范围比如 n 到 10^5那就不能用 O(n^2) 的朴素做法。我见过有人在 Merge Intervals 这道题里用两层循环本地跑小样本全过一提交就 TLE这就是没有先算复杂度的血泪经验。还有一个典型是 KMP 算法字符串匹配如果不用 KMP直接朴素的 O(n*m) 在长字符串上会慢到怀疑人生但如果你先分析出模式串长度和主串长度就能判断是否值得用 O(nm) 的 KMP。另外像归并排序算法这种经典排序如果题目要求稳定排序你就要意识到 Java 的Arrays.sort对基础类型用的是双轴快排不稳定对象类型用的是归并排序稳定。这个细节在排序题里直接决定结果。复杂度估算不是玄学它决定了你要不要引入额外结构。比如两数之和如果不看限制暴力 O(n^2) 很简单但看到数据范围后就会想到用哈希表做空间换时间。所以在写代码前我会在边界表旁边加一列“复杂度目标”比如“最多 O(n log n)”或“用 O(n) 空间换 O(n) 时间”。这一步做扎实了后面 Java 和 Python 的实现才不会跑偏。对于数据结构相关题目复杂度表还能帮你判断是不是需要用线段树、树状数组这类高级结构如果只是区间查询且不需要更新前缀和就够用了。另外递归时间也很容易高估或低估。斐波那契数列用朴素递归是 O(2^n)但如果你画出递归树会发现大量重复计算这时候动态规划把中间结果存下来复杂度直接降到 O(n)。在 LintCode 上这种“先画调用树再定状态”的分析方法比直接背模板可靠得多。每次拿到题我都先在自己的边界表里写下“最坏复杂度”和“期望复杂度”两列然后才开始选数据结构。这样即使写代码中途改了思路测试用例和复杂度目标还能拉你回到正确的解题轨道上。3. 用 Java 实现高频数据结构链表、二叉树与排序算法3.1 链表题快慢指针和哑结点的 Java 写法链表题在 LintCode 里的出现率很高因为 Java 的ListNode类是面试高频对象。我见过不少新手在while (fast ! null fast.next ! null)这个条件上翻车少写一个判断就 NPE。这里我先给一个最常用的快慢指针模板用来找链表中点public ListNode middleNode(ListNode head) { ListNode slow head, fast head; while (fast ! null fast.next ! null) { slow slow.next; fast fast.next.next; } return slow; }逻辑说明fast每次走两步slow每次走一步当fast到末尾时slow正好在中点。参数说明两个空判断缺一不可如果链表长度是偶数这个返回的是后一半的第一个节点比如[1,2,3,4]返回3。如果你要返回前一个中点条件要改成while (fast.next ! null fast.next.next ! null)别小看这个差别在回文链表这类题里会直接影响答案。同样的快慢指针可以判断环形链表public boolean hasCycle(ListNode head) { ListNode slow head, fast head; while (fast ! null fast.next ! null) { slow slow.next; fast fast.next.next; if (slow fast) return true; } return false; }逻辑说明如果有环快指针迟早会追上慢指针如果无环快指针先到末尾。这里不需要额外空间是面试官最爱听的答案。注意指针比较用因为比较的是引用地址而不是节点值。哑结点是另一个高频技巧。删除倒数第 n 个节点时如果你直接操作头节点必须特判“删除的是头节点”这种情况用哑结点则统一public ListNode removeNthFromEnd(ListNode head, int n) { ListNode dummy new ListNode(0); dummy.next head; ListNode left dummy, right dummy; for (int i 0; i n 1; i) { right right.next; } while (right ! null) { left left.next; right right.next; } left.next left.next.next; return dummy.next; }参数说明dummy的值任意left和right之间相距 n1 个节点这样当right走到末尾时left正好指向要删除节点的前一个节点。最后返回dummy.next而不是head因为原头节点可能被删掉。这个模板解决了很多“链表头节点不确定”的边界问题可以说是 Java 写链表题最值得背的操作之一。3.2 二叉树递归与迭代前序/中序/后序统一模板二叉树的递归写法很简洁但面试官经常追加一句“不用递归实现一下”这时候统一的迭代模板就很香。我常用一个带标记状态的栈模板可以在前序、中序、后序之间一键切换public ListInteger inorderTraversal(TreeNode root) { ListInteger res new ArrayList(); DequeObject stack new ArrayDeque(); if (root ! null) stack.push(root); while (!stack.isEmpty()) { Object obj stack.pop(); if (obj instanceof TreeNode) { TreeNode node (TreeNode) obj; stack.push(node.right); stack.push(node.val); // 用 Integer 标记“该输出了” stack.push(node.left); } else { res.add((Integer) obj); } } return res; }逻辑说明压栈顺序是“右、标记值、左”弹出时遇到TreeNode就继续破解遇到Integer就直接加入结果。如果你想要前序把压栈顺序改成“右、左、标记值”想要后序改成“标记值、右、左”。这里有一个参数细节Deque用push/pop如果你用Stack类也可以但Stack是同步的在 LintCode 这种单线程环境性能略差ArrayDeque更快。递归版也不是没用它的优势是更贴近题目语义比如求二叉树最大深度public int maxDepth(TreeNode root) { if (root null) return 0; return 1 Math.max(maxDepth(root.left), maxDepth(root.right)); }这个版本没有任何额外空间除了调用栈理解起来也容易。但要注意Java 和 Python 对递归深度有不同容忍度后面第 4 章会讲到。这里我的建议是递归想思路迭代写实现两个都要会。单独背递归版遇到超深树会爆栈单独背迭代版面试讲思路时反而绕。层次遍历也是二叉树题的基础Java 用队列实现public ListListInteger levelOrder(TreeNode root) { ListListInteger res new ArrayList(); if (root null) return res; DequeTreeNode queue new ArrayDeque(); queue.offer(root); while (!queue.isEmpty()) { int size queue.size(); ListInteger level new ArrayList(); for (int i 0; i size; i) { TreeNode node queue.poll(); level.add(node.val); if (node.left ! null) queue.offer(node.left); if (node.right ! null) queue.offer(node.right); } res.add(level); } return res; }逻辑说明每次处理一层通过board.size()固定当前层节点数避免把下一层混进来。这里用offer/poll而不是add/remove因为队列为空时remove会抛异常poll返回 null更安全。3.3 归并排序与快速排序Java 的边界参数调优排序算法在 LintCode 里不会单独考但会作为工具出现在“有序数组合并”“求逆序对”这类题里。归并排序是这类题目最常被要求手写的算法因为它保证了稳定性和 O(n log n) 复杂度。我这里给出一个面试安全的 Java 版本public void mergeSort(int[] arr, int left, int right, int[] temp) { if (left right) return; int mid left (right - left) / 2; mergeSort(arr, left, mid, temp); mergeSort(arr, mid 1, right, temp); merge(arr, left, mid, right, temp); } private void merge(int[] arr, int left, int mid, int right, int[] temp) { int i left, j mid 1, k left; while (i mid j right) { if (arr[i] arr[j]) temp[k] arr[i]; else temp[k] arr[j]; } while (i mid) temp[k] arr[i]; while (j right) temp[k] arr[j]; for (int p left; p right; p) arr[p] temp[p]; }参数说明mid用left (right - left)/2避免(leftright)溢出temp数组在入口创建一次不要在递归里new否则频繁分配内存会让时间翻倍。归并排序的边界最容易错的是while (i mid j right)里的和建议每次写完都对三个输入跑一遍单元素、两个元素、交替乱序。快速排序的边界调优更琐碎尤其是partition里i和j的移动条件。如果 pivot 选arr[left]那么右侧指针先动否则左侧先动这个顺序错了结果就是错的。LintCode 的排序题不会到这种粒度但面试官会问。我个人认为刷题阶段优先掌握归并排序因为它的合并过程天然适配“逆序对”这类统计题快速排序只要能默写出来即可不用过度优化三路快排。排序最好最坏平均稳定性归并排序O(n log n)O(n log n)O(n log n)稳定快速排序O(n log n)O(n^2)O(n log n)不稳定堆排序O(n log n)O(n log n)O(n log n)不稳定这张表在写“排序数组”那类题时能提示你选哪个。如果题目要求空间复杂度 O(1)归并排序的 O(n) 空间就不满足只能选堆排序或快速排序。如果要求稳定快速排序就要加下标比较。这种选择在 LintCode 的进阶题里很常见提前背好能少走不少弯路。4. 用 Python 实现同一套题代码量优势与性能陷阱4.1 Python 的切片与列表推导让归并排序写得更短Python 写归并排序可以短到让人怀疑是不是同一道题。下面这个版本是我在入门时常用的def merge_sort(nums): if len(nums) 1: return nums mid len(nums) // 2 left merge_sort(nums[:mid]) right merge_sort(nums[mid:]) return merge(left, right) def merge(left, right): i, j 0, 0 res [] while i len(left) and j len(right): if left[i] right[j]: res.append(left[i]); i 1 else: res.append(right[j]); j 1 res.extend(left[i:]) res.extend(right[j:]) return res逻辑说明切片nums[:mid]复制了子数组让递归天然独立。参数说明注意mid len(nums) // 2当长度为 2 时左边 1 个右边 1 个不会死循环。这里的隐藏问题在性能每次递归都创建新列表总内存是 O(n log n) 而不是 O(n)LintCode 数据量大时会 MLE。所以如果你要提交到 LintCode我更建议用索引版本的归并排序避免切片带来的额外空间。索引版和 Java 版结构一致只是把数组换成列表def merge_sort(nums, left, right): if left right: return mid (left right) // 2 merge_sort(nums, left, mid) merge_sort(nums, mid 1, right) temp [] i, j, k left, mid 1, 0 while i mid and j right: if nums[i] nums[j]: temp.append(nums[i]); i 1 else: temp.append(nums[j]); j 1 while i mid: temp.append(nums[i]); i 1 while j right: temp.append(nums[j]); j 1 nums[left:right1] temp注意最后一行用nums[left:right1] temp回写原列表这里不能用nums temp因为那样只是换了局部变量引用原列表没变。这个坑 Python 入门书里很少提但刷题时非常常见。列表推导是另一个 Python 特色比如快速排序可以用一行搞定分区def quick_sort(nums): if len(nums) 1: return nums pivot nums[0] left [x for x in nums[1:] if x pivot] right [x for x in nums[1:] if x pivot] return quick_sort(left) [pivot] quick_sort(right)这个写法非常 Pythonic但它不稳定而且每次分区都要遍历两次nums[1:]。我的建议是用列表推导快速验证思路可以但别把它当成工程方案。在 LintCode 上这种写法遇到 10^5 规模的输入会因为 list 拼接和重复遍历明显变慢。4.2 递归深度限制LintCode 二叉树题目和 sys.setrecursionlimitPython 的默认递归深度是 1000这意味着如果你用递归写一棵退化成链表的二叉树遍历输入长度一超过 1000 就会RecursionError。LintCode 的判题环境里有的题目数据深度可能超过这个值所以本地必须做好准备import sys sys.setrecursionlimit(10000)参数说明setrecursionlimit把递归深度上限调大到 10000 通常够用但不要无脑调 10 万因为 Python 的调用栈本身也有内存限制过大反而会导致程序启动失败。常见做法是放在solution.py的顶部只设置一次。除了直接调大还可以用sys.setrecursionlimit之外的办法把递归改成迭代。比如二叉树中序遍历Python 用栈写迭代几乎和 Java 一样def inorder(root): res [] stack [] cur root while cur or stack: while cur: stack.append(cur) cur cur.left cur stack.pop() res.append(cur.val) cur cur.right return res逻辑说明外层循环负责“有左子树就一直往左压栈”弹出后再转向右子树。这个版本的性能比递归稳定且不受递归深度限制。我遇到很多 JSON 解析、树遍历的题目用迭代以后性能问题一下解决。所以 4.2 的教训是递归深度是 Python 刷题的第一道坎尤其当你从 Java 切到 Python 时Java 的栈深度和 Python 不是一个量级必须主动处理。4.3 用 Python 字典模拟哈希表与 Java HashMap 的差异两数之和的 Python 版可以写得很优雅def two_sum(nums, target): seen {} for i, num in enumerate(nums): need target - num if need in seen: return [seen[need], i] seen[num] i return []逻辑说明seen字典保存num - index一次遍历完成。参数说明need in seen是哈希查找平均 O(1)Python 的 dict 对 key 要求是可哈希的所以不能拿list当 key。这里和 JavaHashMap的差异有两点第一Python dict 不用声明容量和负载因子但内部分桶超过 2/3 时会自动扩容扩容时所有条目重新哈希偶尔会卡一下第二Java HashMap 的get/put需要处理 nullPython 的dict.get(key, default)更简洁但要注意default只用于 key 不存在时不能用于 key 存在但值为 None 的情况。这个差异在 LintCode 的“字符串映射”“数组区间”题里很常见。我习惯在 Python 版里用defaultdict或setdefault来简化初始化但要注意引入collections是否影响判题环境的导入限制。LintCode 一般允许标准库所以用defaultdict是安全的。还有一个值得记的小技巧用setdefault做分组groups {} for word in words: groups.setdefault(len(word), []).append(word)参数说明setdefault(key, default)在 key 不存在时插入default然后返回这个列表存在时直接返回原值。这样省掉了“先判断 key 是否存在”的三行代码。在统计字符频率、建立邻接表这类场景里这个写法比 Java 的putIfAbsent更顺手。当然刷题归刷题面试讲算法时还是要把哈希表的冲突处理讲清楚Python 的 dict 用的是开放寻址Java 的 HashMap 用的是链表法这也算数据结构常识。5. 避坑与常见问题Java 和 Python 双语言实现时最容易出错的 5 个地方5.1 现象Java 里 int 溢出导致答案错误现象两数之和或求中位数时本地测试小数据全过提交后部分用例答案错误。比如(left right) / 2在leftright超过Integer.MAX_VALUE时变成负数。原因Java 的 int 是 32 位有符号溢出后不会报错直接回绕。解决用left (right - left) / 2代替(left right) / 2或者把中间量声明为long。这个坑在二分查找、归并排序的 mid 计算里最常见。我见过有人用Arrays.sort没事但手写快排时 pivot 选(leftright)/2就翻车所以现在 linter 都会提醒。5.2 现象Python 默认参数被复用链表节点定义踩坑现象定义一个函数def add_node(node, seen[])第二次调用时seen里还保留着第一次调用的数据导致结果异常。原因Python 的默认参数在函数定义时只创建一次可变对象会被后续调用共享。解决默认参数一律用None在函数体内再初始化def add_node(node, seenNone): if seen is None: seen [] ...这个坑在刷题时很容易踩尤其是你为了省代码把辅助函数写成带默认列表的时候。包括ListNode的定义也要注意不能把next默认设置成[]而是None。如果你的本地测试挂了优先检查所有参数默认值。5.3 现象LintCode 的 ListNode 和 TreeNode 构造与本地测试不一致现象你在本地用ListNode(1)创建节点然后提交到 LintCode 报错说找不到ListNode类。原因LintCode 判题器会自动提供ListNode和TreeNode的定义但本地需要你自己写一个一模一样的类。解决在项目里建一个lintcode_classes.py把 LintCode 的类定义复制下来统一导入。常见做法是class ListNode: def __init__(self, val0, nextNone): self.val val self.next next注意属性名val和next必须与 LintCode 一致这里拼错一个字母本地跑得再好提交也是编译错误。同样地Java 版需要在Solution类里使用ListNode类但 LintCode 的 Java 环境自带本地你需要自己建一个.java文件。这个坑只要你用“本地测试 粘贴提交”的流程几乎必然会遇到。这也是 java 面试题里经常用来考候选人的地方因为很多人只记得刷题不记得类定义的边界。5.4 现象使用相同算法但 Java AC、Python TLE现象同一道题Java 版 O(n log n) 通过Python 版同样思路却超时。原因Python 是解释型语言常数大同样的循环在 Java 里可能 200msPython 可能 2000ms。LintCode 对 Python 的时间限制一般放宽 35 倍但某些题仍会卡。解决先从算法本身优化剪枝、提前返回、用迭代代替递归再考虑语言层面把高频函数提取为局部变量避免len()每次调用。如果还是 TLE检查是否使用了 O(n^2) 的嵌套写法比如在循环里调用list.index或in操作这些在 Python 里都是 O(n)。最后用PyPy本地模拟测试也不一定能完全复现因为 LintCode 环境是 CPython。这个坑很玄学但多做几题就能摸到 LintCode 的脾气。5.5 现象测试用例没覆盖空输入提交后 WA现象你在本地只测了非空输入提交后一个隐藏用例是空数组结果代码抛异常或返回错误值。原因读题时忽略了“如果数组为空返回 -1”这类提示或者测试脚本里没写空用例。解决在边界表里把空输入单独一行并且用pytest.mark.parametrize把所有边界用例挂在测试脚本里每次提交前跑一遍。同时注意 Java 和 Python 的空表示不同Java 数组可能为null也可能长度为 0代码里需要两个判断Python 里None和[]都要检查。写代码时用“先判空再取值”的顺序不要假设输入非空。6. 把刷题代码变成面试素材一个能复用的双语言模板6.1 给每个题目标注核心知识点和复杂度当你刷了 50 道题后会发现回头翻代码比写代码还难。我的习惯是每个题目下放一个README片段用表格记录题号题目语言核心数据结构时间复杂度56两数之和Java/PythonHashMapO(n)57旋转数组最小值Java/Python数组二分O(log n)这样面试前扫一眼就能回忆起来。表格放在notes/目录下和源码分开。6.2 用 Python 脚本生成 Markdown 刷题笔记我写了一个简单脚本扫描源码目录把每个文件的题号和类名提取出来自动生成 Markdownimport os, re pattern re.compile(r# (\d)) for fname in sorted(os.listdir(.)): if fname.endswith(.java) or fname.endswith(.py): head open(fname, encodingutf-8).read(200) title pattern.search(head) if title: print(f- {fname} {title.group(1)})参数说明这个脚本只读取文件前 200 个字符提取# 56这样的注释标签所以你在每个源码文件头部写一行# 56 两数之和就能被索引。这看起来不起眼但面试前 48 小时这个脚本能帮你把 100 道题压成一张清单。6.3 面试前 48 小时的高频题自测法我的自测法是拿一张纸随意打开笔记里的一行只看题号不看代码在纸上写出算法步骤和复杂度。如果写不出来就重新看一遍实现。这个方法是把“浏览代码”变成“主动回忆”效果比再刷一遍题好。还有一个技巧每一题都用 Java 和 Python 各跑一遍测试不只是验证正确性还能对比两种语言的写感。我在面试 Java 岗时讲的是 Java 版但脑子里同时有 Python 版的简洁思路讲边界时反而讲得更清楚。给新手的建议是不要贪多每天吃透 2 道题把代码、测试、笔记都放进这个模板三个月后你就有自己的“Java Python 双语言 LintCode 笔记”。面试前我会重点看那些标了“常考”和“边界复杂”的题目其他题只扫一眼复杂度。这个习惯陪了我很久希望帮到你。本文还有配套的精品资源点击获取