两数之和
题目描述
给定一个整数数组 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]
题目分析
使用哈希表(通过unordered_map
实现)来解决“两数之和”问题,这种方法之所以有效且能保证不重不漏地找到解,主要基于以下几点:
1. 哈希表的快速查找能力
哈希表支持平均时间复杂度为O(1)的查找操作。这意味着你可以非常快速地检查数组中是否存在一个特定的数字,即是否存在一个数字等于target - nums[i]
(我们称之为补数)。
2. 一遍哈希过程
- 单次遍历:你在遍历数组的同时,将元素逐一添加到哈希表中。这个过程中,对于每个元素
nums[i]
,你都计算其补数complement = target - nums[i]
。 - 即时检查:在添加元素之前,先检查其补数是否已经存在于哈希表中。如果存在,说明之前遍历过的某个元素与当前元素之和正好等于
target
。由于补数在当前元素之前就已经被添加到哈希表了,这保证了不会有遗漏。 - 避免使用同一个元素两次:因为你是在将
nums[i]
添加到哈希表之前检查其补数,所以每个元素在检查时都是“新”出现的,从而避免了重复使用相同元素。
3. 索引的有序存储
通过将每个元素的值与其在数组中的索引关联起来存储在哈希表中,当找到满足条件的一对数字时,你能够直接返回这对数字的索引。这种方法保证了返回的解是正确的,并且在找到解后可以立即终止遍历,提高效率。
4. 无需额外空间处理重复值
即使数组中有重复值,由于哈希表是在检查完补数后才将当前数字及其索引加入,这保证了即使重复值可以作为解的一部分,也只有在它们可以组成有效对的情况下才被考虑。
综合解释
这种方法的有效性在于它结合了哈希表的快速查找与单次数组遍历的高效性,能够确保每个元素及其补数只被检查一次,从而保证了解的完整性和唯一性。这样的处理既快速又精确,适用于解决需要快速查找和配对的问题。
代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> numMap; // 哈希表存储数组元素与其索引的映射
for (int i = 0; i < nums.size(); i++) {
int complement = target - nums[i];
if (numMap.find(complement) != numMap.end()) {
return {numMap[complement], i}; // 找到则直接返回结果
}
numMap[nums[i]] = i; // 存储当前元素的索引
}
return {}; // 如果没有找到合适的结果,则返回空向量
}
};
字母异位词分组
题目描述
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
示例 1:
输入: strs = [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出: [[“bat”],[“nat”,“tan”],[“ate”,“eat”,“tea”]]
示例 2:
输入: strs = [“”]
输出: [[“”]]
示例 3:
输入: strs = [“a”]
输出: [[“a”]]
提示:
1 <= strs.length <= 104
0 <= strs[i].length <= 100
strs[i] 仅包含小写字母
思路
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。字母异位词分组的目标是将相同的字母异位词放在同一组。
本题的思路是:
主要是借助unordered_map<string, vector<string>>
容器;
- 分别遍历每一个单词;
- 对此单词排序,当作map的key值;
- 将此单词作为一个vector中的一个成员;
- 建立完map后,遍历取出每一组;
代码
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> anagramMap;
for (auto& str : strs) {
string key = str; // Copy the original string to use as a key
sort(key.begin(), key.end()); // Sort the copy to form the key
anagramMap[key].push_back(str); // Group by the sorted key
}
vector<vector<string>> result;
for (auto& mapi : anagramMap) {
result.push_back(std::move(mapi.second)); // Use std::move to avoid copying the vector
}
return result;
}
};
3. 最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
解题思路
本题的思路为遍历每一个元素num1,对于当前元素num1,查找num1+1是否存在;并维护一个全局的最大num_length;
对于每个元素的遍历这时间复杂度为O(n)
,对遍历到某一个元素后,检查从此元素开始的最大连续序列长度,可以使用哈希表;这里的关键点是,如果当前元素val的前一个元素val-1在map中存在,说明当前元素为开始的子序列长度肯定不是最大的,可以跳过;
代码
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> numSet(nums.begin(), nums.end()); // 更符合实际用途的命名
int max_length = 0;
for (auto val : numSet) { // 直接在集合上遍历,避免重复检查
if (numSet.find(val - 1) == numSet.end()) {
int current_length = 1;
int current_val = val;
while (numSet.find(current_val + 1) != numSet.end()) {
current_length++;
current_val++;
}
max_length = max(max_length, current_length);
}
}
return max_length;
}
};