找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3045

积分

0

好友

431

主题
发表于 5 小时前 | 查看: 0| 回复: 0

刚在云栈社区看到有人分享了一个帖子,挺有感触的。说的是有位网友去面试,碰到的候选人比他大8岁,简历上写着是前大厂P7,曾经年薪百万。但现在失业快一年了,面试时只敢开口要20k的月薪,还一再表示不挑活、能加班。发帖的网友说,那一瞬间,仿佛看到了几年后的自己。

社交媒体截图:面试前大厂P7前辈的感慨

这件事最扎心的地方在哪?或许在于,我们总以为站得足够高就能一劳永逸,但现实会告诉你,职场如流水线,谁都有掉队的可能。

不过换个角度想,这位前辈的经历也给了我们两点提醒:第一,过去的功劳簿不能吃一辈子,千万别把“年薪百万”当成永远的护身符;第二,再辉煌的履历,在遭遇低谷时,也只能放下身段,咬牙向前。

所以,与其被这种故事吓到,陷入焦虑,不如早点行动起来,给自己多准备几条路:技能别太单一、手头存点钱、也别过分迷信所谓的大厂光环。

说到技能,面试求职过程中绕不开的,就是算法题。比如下面这个经典的“三数之和”。

“三数之和”到底难在哪?

题目就一句话:给你一个整数数组 nums,找出所有和为 0 的三元组,并且三元组不能重复。

很多人第一反应就是用三个 for 循环暴力枚举:

def three_sum_bruteforce(nums):
    n = len(nums)
    res = set()
    for i in range(n):
        for j in range(i + 1, n):
            for k in range(j + 1, n):
                if nums[i] + nums[j] + nums[k] == 0:
                    triplet = tuple(sorted([nums[i], nums[j], nums[k]]))
                    res.add(triplet)
    return [list(t) for t in res]

这个思路没问题,但效率太低,时间复杂度是 O(n³),数据量一大必然超时。当然,这个版本有两个点值得注意:

  • set 来去重,因为 [1, -1, 0][-1, 0, 1] 是同一个三元组。
  • 把找到的三元组排序后转成 tuple,才能存入 set

这种写法对付小样例可以,但面试中基本过不了关。

高效解法:排序 + 双指针

更常见的解法是“排序后使用双指针”,核心思路如下:

  1. 排序:先把数组从小到大排好序。这是后续所有操作的基础。
  2. 固定一个数:外层循环固定一个数 nums[i],问题就转化为在 i 右侧的区间里,寻找两个数,使它们的和等于 -nums[i]
  3. 双指针搜索:在 i 右侧的区间设置左指针 left 和右指针 right
    • 如果 nums[left] + nums[right] < -nums[i],说明和太小,left 右移。
    • 如果 nums[left] + nums[right] > -nums[i],说明和太大,right 左移。
    • 如果正好相等,就找到一组解,记录下来,然后左右指针同时向中间移动,继续寻找。

这样一来,外层循环 O(n),内层双指针总共 O(n),整体时间复杂度就降到了 O(n²),比 O(n³) 高效得多。

真正的难点:去重

这题最“恶心”的地方不在于算法思路,而在于“不重复”这个条件。重复主要来自三个方面:

  1. 外层固定的数 nums[i] 重复。
  2. 左指针 nums[left] 重复。
  3. 右指针 nums[right] 重复。

所以,我们需要在代码的三个地方加入“跳过重复值”的逻辑。

完整代码与细节

下面是考虑了所有细节的 Python 实现:

from typing import List

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        res: List[List[int]] = []

        for i in range(n):
            # 剪枝:因为数组已排序,如果最小的数都大于0,后面不可能再有三数之和为0
            if nums[i] > 0:
                break

            # 跳过相同的起点,避免重复三元组(去重点1)
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            left, right = i + 1, n - 1
            target = -nums[i]

            while left < right:
                s = nums[left] + nums[right]
                if s == target:
                    res.append([nums[i], nums[left], nums[right]])

                    # 左指针去重(去重点2)
                    left_val = nums[left]
                    while left < right and nums[left] == left_val:
                        left += 1

                    # 右指针去重(去重点3)
                    right_val = nums[right]
                    while left < right and nums[right] == right_val:
                        right -= 1

                elif s < target:
                    left += 1
                else:
                    right -= 1

        return res

可以测试一下:

print(Solution().threeSum([-1, 0, 1, 2, -1, -4]))
# 输出:[[-1, -1, 2], [-1, 0, 1]]

关键点梳理

  1. 排序是前提:不排序,双指针的逻辑就不成立。
  2. 剪枝优化if nums[i] > 0: break 是因为数组有序,nums[i] 作为三元组中最小的数如果已经大于0,和不可能为0。
  3. 去重逻辑:三处去重必须写对,且顺序很重要。找到答案后的去重,一定要先记录当前值,再用 while 循环跳过所有相同的值,否则容易出错。

题目考察的本质

表面上是求三数之和,实际上考察的是:

  • 优化意识:能否从 O(n³) 的暴力法,想到 O(n²) 的“固定一个数+双指针”套路。
  • 利用有序性:排序后,能否大胆使用剪枝条件。
  • 代码严谨性:处理边界条件和重复数据时是否细心。

刷题到一定阶段会发现,很多题目都可以归入“排序+双指针”这个模式,比如两数之和 II(有序数组)、最接近的三数之和、盛最多水的容器等,思路都是相通的。掌握这个模式,能帮你解决一大类问题。





上一篇:K8s 流量入口:为什么 Ingress-Nginx 依然是生产环境的“扛把子”?
下一篇:别再折腾Tkinter了,用PySimpleGUI快速给脚本加个图形界面
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-29 23:42 , Processed in 0.406586 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表