题意: 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。 你可以按任意顺序返回答案。 题目链接:https://leetcode.cn/problems/two-sum/
视频链接:https://www.bilibili.com/video/BV1vkNGehEun/
一、看到题目的第一想法
-
暴力解法的本能反应 :最直接的思路是双重循环遍历 :用外层循环固定第一个数,内层循环遍历后续所有数,判断两数之和是否等于
target。如果找到符合条件的两个数,就返回它们的下标。这种方法逻辑简单,和第二张图的代码完全对应,很容易写出来,但时间复杂度是 O (n²),在数据量大的时候会超时。 -
哈希表优化的进阶思路:为了把时间复杂度降到 O (n),我立刻想到用 哈希表(散列表) 的 "空间换时间" 思想:
- 遍历数组时,先计算当前数的 "补数"(
target - nums[i]),如果补数已经在哈希表中,就说明找到了一对答案,直接返回补数的下标和当前下标。 - 如果补数不在哈希表中,就把当前数和它的下标存入哈希表,继续遍历。这种方法每个元素只需要访问一次,效率大幅提升,对应第一张图的 C 语言实现。
- 遍历数组时,先计算当前数的 "补数"(
二、实现过程中遇到的困难(以 C 语言哈希表实现为例)
-
C 语言没有内置哈希表,需要手动实现 :这是最大的难点。Python/Java 里直接有
dict/HashMap,但 C 语言需要自己处理哈希表的结构、插入、查找逻辑,或者用第三方的uthash库(第一张图用的就是uthash)。- 不熟悉
uthash的 API:比如HASH_ADD_INT、HASH_FIND_INT的用法,一开始经常因为参数传错(比如 key 的地址、哈希表头指针)导致程序崩溃。 - 内存管理问题:每次插入元素都要
malloc新的hashTable节点,一开始忘记处理malloc失败的情况,也没考虑重复 key 的覆盖逻辑(题目保证只有一个解,但还是要处理重复值的情况,比如nums = [3,3], target = 6)。
- 不熟悉
-
边界情况处理容易出错:
- 比如数组中存在重复元素(如
[3,3]),暴力解法没问题,但哈希表实现时,如果在遍历当前元素前就插入哈希表,会错误地把自己和自己匹配,导致下标相同的错误结果。 - 一开始写的哈希表逻辑是 "先插入当前元素,再找补数",结果会返回错误答案,后来调整为 "先找补数,再插入当前元素",才解决了这个问题。
- 比如数组中存在重复元素(如
-
LeetCode 的 C 语言返回值规范 :题目要求返回的数组必须是
malloc分配的,并且通过returnSize参数返回数组长度。一开始忘记给returnSize赋值,或者malloc的大小算错(比如写成sizeof(int)而不是sizeof(int)*2),导致测试用例不通过。 -
暴力解法的优化误区:一开始想通过提前退出循环来优化暴力解法,但 LeetCode 的测试用例里有很大的数组,双重循环的时间复杂度始终是 O (n²),还是会超时,这才意识到暴力解法只能作为入门思路,实际场景必须用哈希表。
三、今日收获心得
-
时间复杂度优化的核心是 "减少重复计算":暴力解法的问题在于,每个元素都要和后面所有元素比较,大量重复计算。而哈希表通过 O (1) 的查找,把 "找补数" 的操作从 O (n) 降到了 O (1),整体时间复杂度从 O (n²) 降到 O (n),这是算法优化的典型思路 ------ 用额外的空间开销,避免重复的遍历。
-
C 语言实现数据结构,细节决定成败 :用
uthash实现哈希表时,不仅要理解哈希表的逻辑,还要注意:- 结构体的定义必须包含
UT_hash_handle成员,否则库函数无法正常工作。 HASH_ADD_INT的参数顺序是 "哈希表头指针、key 字段名、要插入的节点",很容易搞混。- 内存分配和释放:虽然题目里没要求释放,但实际工程中必须在函数结束后释放哈希表的节点,避免内存泄漏。
- 结构体的定义必须包含
-
算法题的 "暴力解法" 是基础,优化思路才是核心:很多题目的暴力解法很容易写,但面试和工程中,面试官更看重你对时间复杂度的理解和优化能力。这道题让我深刻体会到,"先写出能跑的代码,再想办法优化" 是很实用的解题步骤,但也要知道暴力解法的局限性。
-
边界测试用例的重要性 :比如
nums = [3,2,4], target = 6这种不能用自身加自身的情况,还有nums = [3,3], target = 6这种重复元素的情况,只有提前想到这些边界,才能写出健壮的代码,而不是只通过几个简单用例就觉得没问题了。

