LeetCode454 四数相加II
题目描述:
给你四个整数数组 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
解释:
两个元组如下:
- (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
- (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
思路:
字典的key放nums1[i]和nums2[j]两数之和sum,value为两数之和出现的次数。遍历nums1和nums2数组,统计两个数组元素之和,和出现的次数,放到dic中,之后遍历nums3和nums4数组,找到其中是否有存在-sum的两数之和,有则数量加1,最后返回存在的数量。这里要知道dic.TryGetValue的用法,out var result,result得到的是存在该键的值的值,如果不用此方法,用ContainsKey方法也可以,不过最后的数量要加上就是该键的值。
代码:
public class Solution {
public int FourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Dictionary<int, int> dic = new Dictionary<int, int>();
foreach (var i in nums1)
{
foreach (var j in nums2)
{
int sum = i + j;
if (dic.ContainsKey(sum))
{
dic[sum]++;
}
else
{
dic.Add(sum, 1);
}
}
}
int res = 0;
foreach (var i in nums3)
{
foreach (var j in nums4)
{
int sum = i + j;
if (dic.TryGetValue(-sum,out var result))
{
res += result;
}
//此方法亦可
//if (dic.ContainsKey(-sum))
//{
// res += dic[-sum];
//}
}
}
return res;
}
}
LeetCode383 赎金信
题目描述:
给你两个字符串: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
思路:
哈希表,本题比较简单。
代码:
public class Solution {
public bool CanConstruct(string ransomNote, string magazine) {
Dictionary<char, int> dic = new Dictionary<char, int>();
foreach (var i in magazine)
{
if (!dic.ContainsKey(i))
{
dic.Add(i, 1);
}
else
{
dic[i]++;
}
}
int len = 0;
for (int i = 0; i < ransomNote.Length; i++)
{
if (!dic.ContainsKey(ransomNote[i]) || dic[ransomNote[i]] < 0)
{
return false;
}
if (dic.ContainsKey(ransomNote[i]) && dic[ransomNote[i]] > 0)
{
dic[ransomNote[i]]--;
len++;
}
}
return len == ransomNote.Length;
}
}
LeetCode15 三数之和
题目描述:
给你一个整数数组 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 。
思路:
本题应先将数组排序。
1.哈希表,解题时陷入了思维定式,拿哈希表硬解这道题,十分麻烦,细节很多,非常容易出错,去重还会增加耗时。
2.双指针,若一组解为 (x,y,z) , 则该方法是先确定x,然后使用双指针同时寻找y和z。首先对nums进行排序,然后取第一个值为x,并使用双指针指向剩余部分的头和尾,验证三数之和是否为0,若是,则将三数作为一组解 并保存下来,若否,则判断和大于0还是小于0,并根据情况移动指针。
代码:
哈希表:
public IList<IList<int>> ThreeSum(int[] nums)
{
int len = nums.Length;
var result = new List<IList<int>>();
if (len < 3) return result;
Array.Sort(nums);
//map<值,lastIndex>
var map = new Dictionary<int, int>();
for (int i = 0; i < len; i++)
{
if (map.ContainsKey(nums[i])) map[nums[i]] = i;
else map.Add(nums[i], i);
}
for (int i = 0; i < len - 2; i++)
{
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
for (int j = i + 1; j < len - 1; j++)
{
if (j != i + 1 && nums[j] == nums[j - 1]) continue;
int numsK = 0 - nums[i] - nums[j];
if (map.ContainsKey(numsK) && map[numsK] > j)
result.Add(new List<int>() { nums[i], nums[j], numsK });
}
}
return result;
}
双指针:
public class Solution {
public IList<IList<int>> ThreeSum(int[] nums)
{
IList<IList<int>> list = new List<IList<int>>();
int len = nums.Length;
if (len < 3)
{
return list;
}
Array.Sort(nums);
for (int i = 0; i < len - 2; i++)
{
if (nums[i] > 0) break;
if (i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = len - 1;
while (left < right)
{
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0)
{
list.Add(new List<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--;
}
else if (sum < 0)
{
left++;
}
else if (sum > 0)
{
right--;
}
}
}
return list;
}
}
LeetCode18 四数之和
题目描述:
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
思路:双指针
与上一题类似,注意细节和去重
代码:
public class Solution {
public IList<IList<int>> FourSum(int[] nums, int target) {
List<IList<int>> list = new List<IList<int>>();
Array.Sort(nums);
for (int i = 0; i < nums.Length - 3; i++)
{
int n1 = nums[i];
if (i > 0 && n1 == nums[i - 1])
{
continue;
}
for (int j = i + 1; j < nums.Length - 2; j++)
{
int n2 = nums[j];
if (j > i + 1 && n2 == nums[j - 1])
{
continue;
}
int left = j + 1;
int right = nums.Length - 1;
while (left < right)
{
int n3 = nums[left];
int n4 = nums[right];
//int 会溢出
long sum = (long)n1 + n2 + n3 + n4;
if (sum > target)
{
right--;
}
else if (sum < target)
{
left++;
}
else
{
list.Add(new List<int>() { n1, n2, n3, n4 });
while (left < right && nums[left] == n3)
{
left++; //去重
}
while (left < right && nums[right] == n4)
{
right--; //去重
}
}
}
}
}
return list;
}
}
总结:
做题不要陷入思维定式,多思考多尝试,找对解题方向,思路很重要,想不明白可以动手画图试试。
存在去重操作的时候尽量不要用哈希,十分不方便。