LeetCode 梦开始的地方 ------ 两数之和
文章目录
- [LeetCode 梦开始的地方 ------ 两数之和](#LeetCode 梦开始的地方 —— 两数之和)
1. 前言
对于很多程序员来说,LeetCode 是学习算法和数据结构的重要起点。而"两数之和"这道题目,作为 LeetCode 上的经典入门题目之一,对于理解哈希表的应用以及如何高效地解决问题有着重要的意义。本文将带你从零开始,逐步理解并实现这道题目的解决方案。
2. 题目描述
题目来源: LeetCode - 两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会有一个有效答案
3. 解题思路
方法一:暴力法
最直接的方法是使用双重循环遍历数组中的所有元素组合,找到满足条件的一对数值。这种方法的时间复杂度为 O(n^2),效率较低。
方法二:哈希表
更高效的解决方案是使用哈希表(字典)。我们可以通过遍历数组一次来查找目标值。对于每个元素 nums[i]
,我们检查 target - nums[i]
是否已经在哈希表中。如果存在,则找到了一对解;如果不存在,则将 nums[i]
存入哈希表中。
这种方法的时间复杂度为 O(n),空间复杂度也为 O(n)。
4. 代码实现
方法一:暴力法
cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size(); // 获取 nums 数组的长度
for (int i = 0; i < n; ++i) { // 第一层循环,遍历 nums 数组
for (int j = i + 1; j < n; ++j) { // 第二层循环,从 i+1 开始遍历,避免重复检查
if (nums[i] + nums[j] == target) { // 检查当前两个数之和是否等于目标值
return {i, j}; // 如果等于目标值,返回这两个数的索引
}
}
}
return {}; // 如果没有找到符合条件的两个数,返回空的 vector
}
};
-
定义类 Solution:
cppclass Solution {
这行代码定义了一个名为
Solution
的类。这是 LeetCode 中常见的做法,用于组织解题代码。 -
声明成员函数 twoSum:
cpppublic: vector<int> twoSum(vector<int>& nums, int target) {
这是一个公开的成员函数
twoSum
,它接受一个整数向量nums
的引用和一个整数target
作为参数,并返回一个包含两个整数的向量。这里的nums
是问题描述中的整数数组,target
是目标值。 -
获取数组长度:
cppint n = nums.size();
这行代码获取了
nums
向量的大小,并将其存储在整数变量n
中。 -
第一层循环:
cppfor (int i = 0; i < n; ++i) {
这是第一层循环,用于遍历整个
nums
向量。变量i
作为索引从 0 开始递增。 -
第二层循环:
cppfor (int j = i + 1; j < n; ++j) {
这是第二层循环,它从
i + 1
开始,直到n
。这样可以确保不会重复检查相同的元素对。 -
检查两数之和:
cppif (nums[i] + nums[j] == target) {
这行代码检查当前的
nums[i]
和nums[j]
之和是否等于target
。 -
返回索引:
cppreturn {i, j};
如果找到匹配的元素对,就立即返回包含这两个索引的向量
{i, j}
。 -
结束第二层循环:
cpp} }
-
结束第一层循环:
cpp}
-
如果没有找到匹配的元素对:
cppreturn {}; //如果遍历完所有的可能组合都没有找到匹配的元素对,那么返回一个空的向量。
这段代码实现了暴力法来解决"两数之和"问题,其时间复杂度为 O(n^2)。虽然这种方法不是最优的,但它简单直观,易于理解。
方法二:哈希表
cpp
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable; // 创建一个无序哈希表来存储数值及其索引
for (int i = 0; i < nums.size(); ++i) { // 遍历 nums 向量
auto it = hashtable.find(target - nums[i]); // 查找哈希表中是否存在 target - nums[i]
if (it != hashtable.end()) { // 如果找到了对应的键
return {it->second, i}; // 返回对应的索引对
}
hashtable[nums[i]] = i; // 将当前元素 nums[i] 和它的索引 i 存入哈希表
}
return {}; // 如果没有找到解,返回空向量
}
};
-
定义类 Solution:
cppclass Solution {
这行代码定义了一个名为
Solution
的类。这是 LeetCode 中常见的做法,用于组织解题代码。 -
声明成员函数 twoSum:
cpppublic: vector<int> twoSum(vector<int>& nums, int target) {
这是一个公开的成员函数
twoSum
,它接受一个整数向量nums
的引用和一个整数target
作为参数,并返回一个包含两个整数的向量。这里的nums
是问题描述中的整数数组,target
是目标值。 -
创建哈希表:
cppunordered_map<int, int> hashtable;
这行代码创建了一个
unordered_map
,用来存储每个元素的值及其在数组中的索引。unordered_map
是 C++ 标准库中的容器,提供了类似哈希表的数据结构。 -
遍历数组:
cppfor (int i = 0; i < nums.size(); ++i) {
这是循环,用于遍历整个
nums
向量。变量i
作为索引从 0 开始递增。 -
查找补数:
cppauto it = hashtable.find(target - nums[i]);
这行代码尝试在哈希表中查找
target - nums[i]
。find
函数返回一个迭代器,指向哈希表中对应的键,如果键不存在则返回end()
。 -
检查是否找到补数:
cppif (it != hashtable.end()) {
这行代码检查
it
是否不等于hashtable.end()
,即判断target - nums[i]
是否存在于哈希表中。 -
返回索引对:
cppreturn {it->second, i};
如果找到了匹配的元素,就立即返回包含两个索引的向量
{it->second, i}
。这里it->second
是target - nums[i]
对应的索引。 -
更新哈希表:
cpphashtable[nums[i]] = i;
如果没有找到匹配的元素,则将当前元素
nums[i]
和它的索引i
存入哈希表中,以便后续的查找。 -
结束循环:
cpp}
-
如果没有找到匹配的元素对:
cppreturn {};
如果遍历完所有的元素都没有找到匹配的元素对,那么返回一个空的向量。
这段代码实现了使用哈希表来解决"两数之和"问题,其时间复杂度为 O(n),空间复杂度同样为 O(n)。这种方法比暴力法更高效,因为它只需要遍历一次数组。
5. 测试结果
运行上述代码后,我们可以得到如下输出:
Indices: [0, 1]
这与题目要求的结果一致。
6. 总结
通过解决"两数之和"这个问题,我们不仅学会了如何利用哈希表来提高算法的效率,还掌握了处理数组问题的基本技巧。这对于后续更复杂的算法问题非常有帮助。希望这篇博客能帮助你更好地理解和掌握这道经典的 LeetCode 题目。