📍 题目背景
今天咱们来聊聊无数程序员刷题之路的起点,传说中的 LeetCode 第 1 题:两数之和 。 别看它是一道简单题,很多人一开始只会用两个 for 循环暴力破解(时间复杂度 O(N\^2))。而在大厂面试中,面试官真正想考察的是你能不能用空间换时间的思维,把它优化到极致的 O(N)!
【题目描述】 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
【示例】
输入: nums =
[2,7,11,15], target =9输出:[0,1]解释: 因为 nums0 + nums1 == 9 ,返回 0, 1 。
✨ 解法一:哈希表"备忘录" (终极满分解法 O(N))
【核心逻辑】 如果用暴力法,我们拿到一个数字 2,就得跑去后面挨个问:"你们谁是 7 呀?"这种做法极其浪费时间。 更聪明的做法是:我们准备一个"超级雷达备忘录"(哈希表 unordered_map)。
-
遍历到一个数字(比如
2),我们心里立刻算出它需要的另一半need = target - 2 = 7。 -
我们翻开备忘录查一下:刚才有人登记过
7吗? -
如果查到了,直接配对成功,返回它俩的座位号(下标)。
-
如果没查到,就把当前的数字
2和它的座位号登记在备忘录上,等待后面有缘人的到来。
💻 C++ 代码实现
#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 核心备忘录:记录格式为 <数字本身, 它的原始下标>
unordered_map<int, int> mp;
// 只需要从左到右看一遍数组!
for (int i = 0; i < nums.size(); i++) {
int need = target - nums[i]; // 算出命中注定的另一半
// 去备忘录里查,这个另一半刚才出现过吗?
if (mp.count(need)) {
// 找到了!返回它俩的下标
return {mp[need], i};
}
// 没找到另一半,就把自己和自己的下标存进备忘录,等待有缘人
mp[nums[i]] = i;
}
return {}; // 兜底,没找到就返回空
}
};
🚀 解法二:双指针法 + "贴名牌" (O(N \\log N) 进阶思路)
有些同学非常喜欢用"排序 + 左右指针向中间夹击"的方法来求两数之和。 但是,这道题有一个巨大的坑: 题目要求返回的是 "原始下标"!一旦你对数组使用了 sort() 排序,数字的座位就全乱了,原来的下标就彻底丢失了!
【补救绝招:贴名牌】 既然怕乱,我们在排序前,就用 C++ 的 pair 把每个数字和它的原始下标绑在一起(贴上名牌)。排完队后,我们看着名牌就能知道它原来坐在哪里!
💻 C++ 代码实现
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
// 1. 给每个数字贴上"原始座位号"的名牌
vector<pair<int, int>> numsWithIndex;
for (int i = 0; i < nums.size(); i++) {
numsWithIndex.push_back({nums[i], i});
}
// 2. 带着名牌一起排序 (默认根据 pair 的 first 数字大小排序)
sort(numsWithIndex.begin(), numsWithIndex.end());
// 3. 派出左右小弟夹击
int left = 0;
int right = numsWithIndex.size() - 1;
while (left < right) {
int sum = numsWithIndex[left].first + numsWithIndex[right].first;
if (sum == target) {
// 找到啦!提取它们名牌上的"原始座位号"
return {numsWithIndex[left].second, numsWithIndex[right].second};
}
else if (sum < target) {
left++; // 太小了,左指针右移找大数
}
else {
right--; // 太大了,右指针左移找小数
}
}
return {};
}
};
🔍 总结与复杂度分析
-
哈希表法(推荐解法):
-
时间复杂度: O(N)。只需遍历数组一次,哈希表的查询时间是 O(1)。
-
空间复杂度: O(N)。需要一个哈希表来存储遍历过的数字。
-
点评: 典型的"空间换时间",是实际工程开发和算法面试中最完美的标准答案。
-
-
贴名牌双指针法:
-
时间复杂度: O(N \\log N)。虽然寻找答案只需 O(N),但前期的
sort排序拖慢了整体速度。 -
空间复杂度: O(N)。需要额外开辟一个存
pair的数组。 -
点评: 虽然不是最优解,但这种"利用 Pair 绑定原始信息"的技巧非常巧妙,在很多更复杂的算法题(如寻找第K大的元素等)中经常用到,是非常棒的思维拓展!
-