LeetCode454:四数之和2
方法依据:因为本质还是寻找元素中是否存在某一个数–> 哈希。
**思路:**这一题和两数之和很相似,就是变成了四个数组。
首先,将nums1
和nums2
的和遍历出来,之后放到map中去,key
为和,value
为这个和出现的次数(因为最后返回的是次数。根据需要返回的结果做调整。)
随后,遍历nums3
和nums4
的和,如果这个和的相反数在map
中出现了,就可以将对应的次数取出来,在通过ans
累加得到最后的结果。
**关键点:**将最后结果的组成形式 拆成两部分,先求一半,在求一办。不要被四个数组给吓到,大不了多嵌几层循环。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
// 为什么选择使用哈希表:因为本质还是找到元素中是否存在某一个数。
// 和两数之和很类似。只不过这里变成了四个数组
// 只需要将nums1 和nums2看成一个,nums3和nums4看成另外一个即可。
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
int size = nums1.length;
int ans = 0;
int sum = 0;
// 遍历nums1和nums2,的到所有的和,放入map中和为key,和出现的次数为value(因为最后返回的是次数,如果返回别的东西那就根据情况,例如返回下标)
for(int i=0;i<size;i++){
for(int j=0;j<size;j++){
sum = nums1[i] + nums2[j];
if(map.containsKey(sum)){
map.put(sum, map.get(sum) + 1);
}
else{
map.put(sum, 1);
}
}
}
// 遍历nums3和nums4 看看他们俩的和的相反数 是否出现在map中,
// 出现了就可以根据value累加得到次数
for(int i=0;i<size;i++){
for(int j=0;j<size;j++){
sum = nums3[i] + nums4[j];
if(map.containsKey(-sum)){
ans += map.get(-sum);
}
}
}
return ans;
}
}
//cpp
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
unordered_map<int, int> umap; // key a+b的值,value:a+b数值出现的次数
for(int a: nums1){
for(int b:nums2){
umap[a+b]++;
}
}
int count = 0;
for(int c:nums3){
for(int d : nums4){
if(umap.find(0 - (c+d)) !=umap.end()){
count += umap[0-(c+d)];
}
}
}
return count;
}
};
LeetCode383:赎金信
方法依据:因为本质是找元素、组合是否出现。
思路:先遍历magazine
,得到每个字符及其个数放到map
中,key
为字符(这里用的是Integer
,所以也可以用数组来构建哈希,省时间和空间。),value
为对应字符的次数。接着遍历ransomNote
,对出现在map
中的元素减1,如果出现map
中没有出现的元素,返回false
;如果map
的value
出现负数则返回false
。
关键点:
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
// 先遍历magazine,中每个字符,并统计个数,存到map。key为字符;value为当前字符的个数。
// 遍历ransomNote,如果这个字符在map中存在就对应减1.最后判断map中的value是否有小于0的,如果有就返回false,否则返回true。
// 看到了都是小写英文字母,所以也可以用数组来构建哈希。
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
for(char ch : magazine.toCharArray()){
int delta = ch-'a';
if(map.containsKey(delta)){
map.put(delta, map.get(delta)+1);
}
else{
map.put(delta,1);
}
}
for(char ch: ransomNote.toCharArray()){
int delta = ch-'a';
if(map.containsKey(delta)){
map.put(delta, map.get(delta)-1);
}
else{
return false;
}
}
for(HashMap.Entry<Integer, Integer> entry:map.entrySet()){
if(entry.getValue() <0){
return false;
}
}
return true;
}
}
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if(ransomNote.size() > magazine.size())
{
return false;
}
unordered_map<char, int> umap;
for(char strTemp:magazine){
umap[strTemp]++;
}
for(char strTemp : ransomNote){
if(umap[strTemp] == 0){ // 这里是先判断然后再减,因此和0比较
return false;
}
umap[strTemp]--;
}
return true;
}
};
LeetCode15:三数之和(需要二刷,去重环节)
**思路:**双指针不难想到,去重环节的细节很多。理清思路很重要。
首先,为了去重,我要将重复的元素放在一起,所以需要先给数组排序Array.sort(nums)
,得到一个从下到大的nums。
按照代码随想录的解法构建三个指针,i,left, right(可以用不同的方法 left mid right)。i从头开始遍历到最后(其实是length-2一定会截止).left = i+1;right = nums.length -1。left 和right从两边向中间靠拢。寻找和为0的组合。
去重:如果
nums[i] = nums[i-1]
则调到下一个,因为i已经找过了,如果符合条件的话,那么后面的i就不需要了。
例如:[-1,-1,-1,2]
[-1,-1,2] --> [0,1,3]
已经记录过,后面的[-1]
就没有和left
和right
的组合就没有必要记录了(一定会重复)。所以调到下一个不同的nums[i]
。
还有就是left
和right
的去重
例如[-1,-1,-1,0,1,1,1]
。
[-1,0,1]-->[0,3,6]
此时left<right
所以还得继续进行,但是后面都是重复的所以需要去重。直到找到不同的left
和right
。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 双指针:使用哈希的方法不大好,因为说了答案中不可以包含重复的三元组。
// 重点是去重:去重的逻辑和细节很重要。
// 三个指针,left, mid,right ,left 和right从左到右遍历,(left<mid<right),mid在中间遍历
// 首先对数组里的元素进行排序,排完序之后才可以进行去重。
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for(int i=0;i<nums.length-2;i++){
if(nums[i]>0){
return ans;
}
if(i > 0 && nums[i] == nums[i-1]){ // 如果已经计算过了,那么后面再出现 就要去重 例如[-1, -1, -1, 2], 已经记录了一个[-1, -1, 2]-->[0, 1, 3]。要是i=1时在记录一次就会重复。
continue;
}
int left = i+1;
int right = nums.length-1;
while(right>left){
int sum = nums[i] + nums[left] + nums[right];
if(sum>0){
right--;
}
else if(sum<0){
left++;
}
else{
ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 记录一个之后就要开始去重了[-1,-1,-1,0,1,1,1]
// 因为while还没到终止条件 记录的时刻是[-1, 0, 1]-->[0,3,6] 3<6.但是后面的不能要了。
while(left<right && nums[right] == nums[right -1]){
right--;
}
while(left<right && nums[left] == nums[left + 1]){
left++;
}
// 找到一个后及时移到下一位寻找新的数。
left++;
right--;
}
}
}
return ans;
}
}
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
int length = nums.size();
int right = length-1;
int left = 1;
for(int i=0;i<length-2;i++){
if(nums[i] > 0){
return ans;
}
if(i>0 && nums[i] == nums[i-1]){
continue;
}
left = i+1;
right = length-1;
while(right > left){
if(nums[i] + nums[left] + nums[right] > 0){
right--;
}
else if(nums[i] + nums[left] + nums[right] < 0){
left++;
}
else{
ans.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑
while(right > left && nums[right] == nums[right - 1]) right--;
while(right > left && nums[left+1] == nums[left]) left++;
// 找到一个且完成去重操作之后
right--;
left++;
}
}
}
return ans;
}
};
LeetCode18:四数之和
思路:和三数之和类似,但是多了一层for循环,可以将前两个看成之前的i。
关键点:
if(nums[i] > 0 && nums[i] > target) {
// 为了避免sum求和超出int范围变成负数的情况。
// 加上这个判断可以把相加之后超过int范围,变成负数的情况
// 排除[1000000000,1000000000,1000000000,1000000000]
// target = -294967296。
return ans;
}
也可以直接将后面的换成long 就不会溢出了。
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
// 同三数之和,不能重复。
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for(int i=0;i<nums.length-3;i++){
if (nums[i] > 0 && nums[i] > target) { //
return ans;
}
if(i>0 && nums[i-1] == nums[i]){ // 这里要i>0 也是首先在已经记录过的基础上才会去重,(同时i-1在起始位置 肯定会异常)。
continue;
}
for(int j=i+1;j<nums.length-2;j++){
if(nums[j-1] == nums[j]&& j>i+1){ // 此时j>i+1 因为j是在已经记录之后再进行剪枝去重的没所以肯定要>i+1。
continue;
}
int left = j+1;
int right = nums.length-1;
while(right > left){
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if(sum<target){
left++;
}
else if(sum>target){
right--;
}
else{
ans.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while(right >left && nums[left] == nums[left+1]){
left++;
}
while(right > left && nums[right] == nums[right -1]){
right--;
}
left++;
right--;
}
}
}
}
return ans;
}
}