454.四数相加Ⅱ
给定四个包含整数的数组列表A,B,C和D,计算有多少个四元组(i,j,k,l),使得A[i] + B[j] + C[k] + D[l] = 0。A,B,C和D具有相同的长度N,且0 =< N =< 500。所有的整数范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1。
A,B,C和D之间为独立的数组,因此本题不需要考虑重复元素的情况,只要遍历数组找到符合要求的组合,记录出现次数即可。可以借助哈希表首先存储A和B数组中所有元素的和,并记录出现的次数,将key作为元素和,将次数存储为value。然后,遍历C和D数组,如果 c + d = 0 - (a + b),则记录count。
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int count = 0;
unordered_map<int, int> map;
for (int a : nums1) {
for (int b :nums2) {
map[a + b]++;
}
}
for (int c : nums3) {
for (int d : nums4) {
if (map.find(0 - (c + d)) != map.end()) {
count += map[0 - (c + d)];
}
}
}
return count;
}
};
383.赎金信
给定一个赎金信 (ransom) 字符串和一个杂志 (magazine) 字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。)
根据题目要求,字母不能重用,所以杂志字符串的长度要大于等于赎金字符串,反之则直接返回false。本题的解题思路,和有效的字母异位词相似,可以通过数组映射的哈希表实现。首先,初始化一个赎金字符串长的数组,通过数组的值记录杂志字符串中字符的出现次数;然后,根据赎金字符串中字母的出现次数修改数组值;最后,判断数组中是否存在负值,如果有则说明杂志字符串不符合赎金字符串的要求,故返回false,返回返回true。
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
for (int m : magazine) {
record[m - 'a']++;
}
for (int r : ransomNote) {
record[r - 'a']--;
}
for (int i : record) {
if (i < 0) {
return false;
}
}
return true;
}
};
15.三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。注意:答案中不可以包含重复的三元组。
本题要求在一个整数数组中,寻找三元组,该三元需要满足相加为 0,且三元素不能重复。可以通过哈希表实现,但是去重操作过于复杂。我们可以通过双指针的方式实现,首先对数组以升序排序,如果第一个元素值大于 0,则后续不存在可能的三元组满足题意。选取第一个元素作为标志元素,设定下一个元素作为left指针起始点,数组尾元素作为right指针起始点,因为经过了排序,所以right > left,所以当标志 + left + right > 0 时,说明right太大了,让right左移一位,当标志 + left + right < 0 时,说明left太小了,让left右移一位,在移动双指针的过程中,如果存在满足题意的三元组,则记录该三元组。当 right < left 时,结束当前循环,将标志位加一。需要注意的一点是,在选取标志位,移动双指针的过程中,都要进行去重,以确保不存在重复的三元组。
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++) {
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while(left < right) {
if (nums[i] + nums[left] + nums[right] > 0) {
right--;
}else if (nums[i] + nums[left] + nums[right] < 0) {
left++;
}else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
return result;
}
};
18.四数之和
和三数之和思路类似,只不过多加一个判定循环和去重。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
if (nums[i] > target && nums[i] >= 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < nums.size(); j++) {
if (nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) {
break;
}
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = nums.size() - 1;
while(left < right) {
if ((long) nums[i] + nums[j] + nums[left] + nums[right] > target) {
right--;
}else if ((long) nums[i] + nums[j] + nums[left] + nums[right] < target) {
left++;
}else {
result.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) left++;
while (left < right && nums[right] == nums[right - 1]) right--;
left++;
right--;
}
}
}
}
return result;
}
};