主题

<
,
,
,
,
,
,
,
,
,
>
答疑
问:是什么原因导致了这两种算法的快慢?
答:我用「获取了多少信息」来解释。
暴力做法每次拿两个数出来相加,和
而哈希表做法,每次查询都能知道
这就是为什么我们可以把暴力的
问:力扣是如何测试题目的?为什么没有 main 函数?
答:简单来说,力扣评测机内部有 main 函数,里面会调用你写的 twoSum 函数(方法),传入相应的测试数据,并对比 twoSum 的返回值和正确答案是否一致。如果对于所有测试数据,返回值都与正确答案一致,则判定通过。
所以,我们只需编写核心逻辑,保证返回结果计算正确即可。
问:如何在本地测试代码?
答:见此。
暴力写法
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i, x in enumerate(nums): # x=nums[i]
for j in range(i + 1, len(nums)): # 枚举 i 右边的 j
if x + nums[j] == target: # 满足要求
return [i, j] # 返回两个数的下标
# 这里无需 return,因为题目保证有解cpp
// C++ 版待补充cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for (int i = 0; ; i++) { // 枚举 i
for (int j = i + 1; j < nums.size(); j++) { // 枚举 i 右边的 j
if (nums[i] + nums[j] == target) { // 满足要求
return {i, j}; // 返回两个数的下标
}
}
}
// 题目保证有解,循环中一定会 return
// 所以这里无需 return,毕竟代码不会执行到这里
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。 - 空间复杂度:
。仅用到若干额外变量。
哈希表写法
问:为什么下面的代码,要先查询
答:反过来写是错误的。例如
原因在于,题目要求「不能使用两次相同的元素」,也就是两个数的下标必须不同。我们的做法是枚举右边的数的下标
python
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
idx = {} # 创建一个空哈希表(字典)
for j, x in enumerate(nums): # x=nums[j]
if target - x in idx: # 在左边找 nums[i],满足 nums[i]+x=target
return [idx[target - x], j] # 返回两个数的下标
idx[x] = j # 保存 nums[j] 和 jcpp
// C++ 版待补充cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> idx; // 创建一个空哈希表
for (int j = 0; ; j++) { // 枚举 j
// 在左边找 nums[i],满足 nums[i]+nums[j]=target
auto it = idx.find(target - nums[j]);
if (it != idx.end()) { // 找到了
return {it->second, j}; // 返回两个数的下标
}
idx[nums[j]] = j; // 保存 nums[j] 和 j
}
}
};复杂度分析
- 时间复杂度:
,其中 为 的长度。 - 空间复杂度:
。哈希表需要 的空间。
相比暴力做法,哈希表多消耗了内存空间,但减少了运行时间,这就是「空间换时间」。
总结 · 练习
很多涉及到「两个变量」的题目,都可以枚举其中一个变量,把它当成常量看待,从而转化成「一个变量」的问题。
代码实现时,通常来说「枚举右,寻找左」是更加好写的。
思考题
- 如果
是有序的,是否还需要哈希表?换句话说,能否做到 额外空间? - 如果要求寻找三个数,它们的和等于
呢?
解答请看【基础算法精讲】。
分类题单
- 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
- 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
- 单调栈(基础/矩形面积/贡献法/最小字典序)
- 网格图(DFS/BFS/综合应用)
- 位运算(基础/性质/拆位/试填/恒等式/思维)
- 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
- 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
- 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
- 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
- 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
- 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
- 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)
欢迎关注 B站@灵茶山艾府