21.两数之和
(力扣1题)
给定一个整数数组 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]
- 解题思路
- 初始化一个空的哈希表。
- 遍历数组中的每个元素,在每一步中,计算目标值与当前元素的差值。
- 检查哈希表中是否存在这个差值,如果存在,立即返回对应的索引和当前元素的索引。
- 如果不存在,将当前元素及其索引存入哈希表,继续遍历。 这种方法确保每个元素只遍历一次,时间复杂度为O(n),空间复杂度为O(n),通过哈希表的快速查找特性,大大提高了效率。
代码
c++
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
std::unordered_map<int, int> map;
for(int i = 0 ; i < nums.size(); i++)
{
// 遍历当前元素,并在map中寻找是否有匹配的key
auto iter = map.find(target - nums[i]);
if(iter != map.end())
{
// 找到了返回找到数组下标和当前数组下标
return {iter->second, i};
}
// 如果没找到匹配对,就把访问过的元素和下标加入到map中
map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
22. 快乐数
(力扣202题)
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
- 如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n
是 快乐数 就返回 true
;不是,则返回 false
。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
- 解题思路
- 计算平方和 :定义
getSum
函数,用于计算一个数各位数字的平方和。 - 检测循环 :在
isHappy
函数中,使用哈希集合unordered_set
存储出现过的平方和。 - 循环判断 :不断计算当前数的平方和,如果平方和为 1,返回
true
表示是快乐数;如果平方和重复出现(已在集合中),返回false
表示陷入无限循环。 - 更新数值:将当前数更新为计算得到的平方和,继续下一轮判断。
c++
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap;
for (int a : nums1)
{
for (int b : nums2)
{
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : nums3)
{
for (int d : nums4) {
if (umap.find(0 - (c + d)) != umap.end())
{
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
23.四数相加II
(力扣454题)
给你四个整数数组 nums1
、nums2
、nums3
和 nums4
,数组长度都是 n
,请你计算有多少个元组 (i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
示例 1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2
解释:
两个元组如下:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例 2:
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
- 解题思路
- 构建哈希表 :遍历数组
nums1
和nums2
,计算每对元素之和a + b
,并将这些和及其出现的次数存储在哈希表umap
中。 - 查找补数 :遍历数组
nums3
和nums4
,计算每对元素之和c + d
,然后查找哈希表中是否存在0 - (c + d)
这个键。如果存在,将对应的出现次数累加到结果count
中。
这种方法通过将问题分解为两部分(计算 a + b
和查找 -(c + d)
),将四数之和的问题转化为两次两数之和的问题,从而降低了时间复杂度。通过哈希表的高效查找特性,确保了算法的高效性
代码
c++
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap;
for (int a : nums1)
{
for (int b : nums2)
{
umap[a + b]++;
}
}
int count = 0; // 统计a+b+c+d = 0 出现的次数
// 再遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
for (int c : nums3)
{
for (int d : nums4) {
if (umap.find(0 - (c + d)) != umap.end())
{
count += umap[0 - (c + d)];
}
}
}
return count;
}
};
24.赎金信
(力扣383题)
给你两个字符串:ransomNote
和 magazine
,判断 ransomNote
能不能由 magazine
里面的字符构成。
如果可以,返回 true
;否则返回 false
。
magazine
中的每个字符只能在 ransomNote
中使用一次。
示例 1:
输入:ransomNote = "a", magazine = "b"
输出:false
示例 2:
输入:ransomNote = "aa", magazine = "ab"
输出:false
示例 3:
输入:ransomNote = "aa", magazine = "aab"
输出:true
- 解题思路
- 初始化哈希表 :使用一个大小为26的整型数组
record
来记录每个字母的出现次数,初始值为0。 - 统计杂志字符 :遍历
magazine
中的每个字符,将其对应的位置在record
中的计数加1。 - 验证赎金信字符 :遍历
ransomNote
中的每个字符,将其对应的位置在record
中的计数减1。如果某个字符的计数变为负数,说明杂志中没有足够的该字符,无法构造赎金信,返回false
。 - 大小检查 :如果
magazine
的长度小于ransomNote
的长度,直接返回false
,因为不可能有足够的字符来构造赎金信。 - 返回结果 :如果所有字符都验证通过,则返回
true
,表示可以构造赎金信
代码
c++
#include <iostream>
using namespace std;
class Solution
{
public:
bool canConstruct(std::string ransomNote, string magazine)
{
// 哈希表
int record[26] = {0};
if(magazine.size() < ransomNote.size())
{
return false;
}
// 遍历ransomNote元素存入哈希表
for (int i = 0; i < magazine.size(); i++)
{
record[magazine[i] - 'a']++;
}
for (int i = 0; i < ransomNote.size(); i++)
{
record[ransomNote[i] - 'a']--;
if(record[ransomNote[i] - 'a'] < 0)
{
return false;
}
}
return true;
}
};
25.三数之和
(力扣15题)
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
- 解题思路
这段代码实现了寻找数组中所有和为零的三元组,核心在于利用排序和双指针技术来高效查找和去重。
首先对数组进行排序,这是为了方便后续的去重操作和双指针的应用。排序后,数组中的元素按非递减顺序排列,相同的元素都相邻。
接下来,外层循环遍历数组,固定第一个元素作为三元组的第一个元素。这里有一个优化条件,如果当前元素大于零则直接返回结果,因为后面的元素都大于零,不可能再形成和为零的三元组。
然后,对固定元素进行去重,避免重复处理相同的第一个元素。内层循环使用双指针法,左指针初始化为当前固定元素的下一个位置,右指针初始化为数组末尾。通过计算三数之和来调整指针的位置,小于零则左指针右移,大于零则右指针左移,直到找到和为零的三元组。
在找到一个有效的三元组后,对左右指针进行去重,跳过所有重复的元素,确保每个三元组只被记录一次。最后,左右指针同时向中间移动,继续寻找下一个可能的三元组。
这种方法的时间复杂度为O(n²),空间复杂度为O(1)
代码
// 使用双指针法
class Solution
{
public:
vector<vector<int>> threeSum(vector<int> &nums)
{
// 存储结果集
vector<vector<int>> result;
// 数组排序
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
// 如果a是正数,a<b<c,不可能形成和为0的三元组
if (nums[i] > 0)
{
return result;
}
// 错误去重a方法,将会漏掉-1,-1,2 这种情况
/*
if (nums[i] == nums[i + 1]) {
continue;
}
*/
// [a, a, ...] 如果本轮a和上轮a相同,那么找到的b,c也是相同的,所以去重a
if (i > 0 && nums[i] == nums[i - 1])
{
continue;
}
// 定义双指针
int left = i + 1;
int right = nums.size() - 1;
while (left < right) // 等于的话就是双元组了
{
// 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
/*
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
*/
if (nums[i] + nums[left] + nums[right] < 0)
left++; // 向右(大)移动
else if (nums[i] + nums[left] + nums[right] > 0)
right--; // 向左(小)移动
else
{
// 找到目标
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (left < right && nums[left] == nums[left + 1])
left++;
while (left < right && nums[right] == nums[right - 1])
right--;
// 找到目标,双指针同时收缩
left++;
right--;
}
}
}
return result;
}
};
wchart.js/