刚看到个贴子,说男生一年140万,女朋友一年15万,差了快10倍,他就在那纠结要不要“换一个”,还说越看越不顺眼,才谈两年。

网友回帖我也瞄了下,有人劝他赶紧分,别耽误姑娘的青春;也有人说挣钱多是你能力,但拿收入当显微镜去挑伴侣,那就有点寒碜了。我比较认同后面这种。
怎么说呢,两个人收入差距大不稀奇,关键是你把对象当“人生伙伴”,还是当“理财产品”。婚姻又不是并购案,不是看谁报表好就收谁。今天你赚140万,万一哪天跌到40万呢?如果此刻你因为她挣得少就“越看越不顺眼”,说明问题已经不是钱,而是你根本不够尊重她。
今天下班路上在地铁上刷题,看到一个老生常谈的题,手机都差点没抓稳:最接近的三数之和。想着平时群里一堆小伙伴问这个题咋写,不如就用一篇文章讲清楚,顺手用 Ruby 写一版,比较少人用 Ruby 刷题,你们正好抄个作业。
意思特别简单:给你一个整数数组 nums,再给你一个目标值 target,让你从 nums 里随便挑三个数出来,相加以后,结果要尽量接近 target,最后把这个“最接近的和”返回。
比如:nums = [-1, 2, 1, -4], target = 1所有三数和里:
离 1 最近的是 2,所以答案就是 2。
看着是不是很“无脑”?但真要你写个高效的,很多人一上来就翻车。
最直观的思路就是: 三重循环,枚举所有三元组 (i, j, k),把和算出来,跟 target 比一比谁更近,一路更新答案。
伪代码脑补一下就是:
时间复杂度是啥?O(n^3)。 数组一长,直接原地爆炸。面试官可能会点点头:“会是会,但不能用”。
所以我们得动点脑子,把三重循环打薄一点。
你想啊,如果数组是有序的,事情就好办很多了。
整体思路是这样的:
nums 排个序,这个是 O(n log n)。i,相当于先选好第一个数。i+1 和 nums.length-1 开始,用左右指针往中间夹。为啥能这么干?因为数组有序了:
sumsum 比 target 小,那你想让它更接近 target,是不是应该把和变大一点? 有序数组里,变大最简单的方式,就是把左指针往右挪一格。sum 比 target 大,那就把右指针往左挪,让和变小。在这个过程中,我们一直维护一个“目前最接近的和”,每次算出新的 sum,就拿 |sum - target| 跟之前的最小差值比一比,小就更新。
如果有一次刚好 sum == target,那就不用算了,直接可以返回 target,再怎么折腾都不可能比它更近了。
时间复杂度变成 O(n^2): 外层固定 i 是 O(n),内层双指针跑一遍又是 O(n)。
直接上代码,你可以本地随便跑跑:
defthree_sum_closest(nums, target)# 至少要有三个数returnnilif nums.length < 3# 排序,方便用双指针 nums.sort!# 先随便取一个合法的三数和当初始值 best_sum = nums[0] + nums[1] + nums[2] (0..(nums.length - 3)).each do|i| left = i + 1 right = nums.length - 1while left < right current_sum = nums[i] + nums[left] + nums[right]# 如果更接近目标,就更新if (current_sum - target).abs < (best_sum - target).abs best_sum = current_sumend# 刚好等于 target,直接返回,没比这更近的了if current_sum == targetreturn current_sumelsif current_sum < target# 和太小了,让它大一点 left += 1else# 和太大了,让它小一点 right -= 1endendend best_sumend# 小测试nums = [-1, 2, 1, -4]target = 1puts three_sum_closest(nums, target) # 输出 2这个版本算是比较“面试友好”的写法:
稍微带你脑补一轮运行过程
就以 [-1, 2, 1, -4] 为例:
排序之后是 [-4, -1, 1, 2]
第一次 i = 0,选的是 -4
left = 1(-1),right = 3(2)sum = -4 + (-1) + 2 = -3,比 target = 1 小 想变大一点,left++ → 指向 1sum = -4 + 1 + 2 = -1,还是小于 1,再 left++left 跑到 right 了,这一轮结束第二次 i = 1,选的是 -1
left = 2(1),right = 3(2)sum = -1 + 1 + 2 = 2,这个跟 1 的距离是 1后面再怎么跑,也不会出现比 2 更接近 1 的结果了,所以最终答案就是 2。
随手提醒几个坑,免得你写题的时候被卡住:
Float::INFINITY 之类的,直接取前三个数的和更自然,也能保证是一个真的三数和。nil,实际项目里你可以抛异常或者自己约定个行为。这个“三数最接近”算是那种:一眼看上去简单、但想写得又快又优雅,还得理解一点“排序 + 双指针”套路的题。 你要是把这个思路吃透了,3Sum、四数之和、甚至更一般的 k-sum,套路都差不多,就是多一层递归而已。
有空你可以把上面这段 Ruby 代码自己手敲一遍,改几个测试数据看看输出对不对,比只看讲解强太多。哪一步不太顺的,直接拿具体例子在纸上跑一遍,脑子就通了。
🔥编程资料合集🔥