day07:四数相加II、赎金信、三数之和、四数之和

第454题.四数相加II

454. 四数相加 II - 力扣(LeetCode)

这个题目是找出有多少个元组能够满足其值之和为0 。 没有限定顺序,但是从数组中找到的都算。

可以考虑2数之和的逻辑。 key: 我们要找的数组和, value是对应的索引下标,

我们把数组拆成2个,ab为一组,cd为一组。这样的话就可以实现类同:两数之和的逻辑了。

我们首先完成ab数组的所有组合遍历。获得sum值,设计一个map,把不同的sum值都存进去。如果一个sum值多次出现,就对应的value上++,然后再对cd进行for循环  ,对cd进行for循环的过程中,我们要用0减去这次cd中i+j的值,看结果是否在之前的map中的key中,如果找到,我们就计数,最后做累加的过程。 

java代码如下:

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer,Integer> mapSum = new HashMap<Integer,Integer>();
        int res =0;
        for(int i=0;i<nums1.length;i++){
            for(int j=0;j<nums2.length;j++){
                int sum =nums1[i]+nums2[j];
                mapSum.put(sum,mapSum.getOrDefault(sum,0)+1);
            }
        }
        for(int i=0;i<nums3.length;i++){
            for(int j=0; j<nums4.length;j++){
                res += mapSum.getOrDefault(0-nums3[i]-nums4[j],0);
            }
        }
        return res;
    }
}

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 i: nums1){
            for(int j: nums2){
                umap[i+j]++;
            }
    }
      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;

    }
};

python代码如下:

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        hashMap =dict()
        for a in nums1:
            for b in nums2:
                hashMap[a+b]=hashMap.get(a+b,0)+1
        
        count=0
        for c in nums3:
            for d in nums4: 
                count +=hashMap.get(0-(c+d),0)
        return count

383.赎金信

383. 赎金信 - 力扣(LeetCode)

这个题目是考虑使用magzine中的元素去覆盖ransomNote的元素。 我们便利2个字符串,如果ransomNote的哪一个字符是出自于magzine中的话,我们就对ransomNote元素进行删减操作。 

当把magzine遍历完,并在ransomeNote中判断没有对应的元素后,for循环结束。我们再一次的判断ransomNote是不是有元素,如果有元素,说明之前的erase删除操作没有把ransonNote中的元素完整的删除掉。 那么表明ransomNote的元素不完全是使用magzine中的元素的。 

2层for循环,这个时间复杂度有点高了。 

c++代码如下:

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        
        for(int i=0; i < magazine.length(); i++){
            for(int j=0;j<ransomNote.length(); j++){
                if(ransomNote[j]==magazine[i]){
                    ransomNote.erase(ransomNote.begin()+j);
                    break;
                }
            }
        }
        if(ransomNote.length()==0){
            return true;
        }
        return false;
    }
};

通过数组模拟哈希的方式,将magazine的26个小写字母映射成ASCII码值,并且作为索引,对应的字母出现次数作为对应索引下的value值。 然后在ransomNote中进行同样的映射,并做减法,如果最后的这个数组record的各个元素值为小于0 ,说明ransomNote调用的字母多于magazine,那么返回false。如果大于等于0,说明ransomNote的每个字母都来自于magazine(大于0时候,ransomNote的字母是magazine的子集) 

代码如下:

bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};  // 假设只有小写字母a-z
        for(int i = 0; i < magazine.length(); i++) {
            record[magazine[i] - 'a']++;  // 计数 magazine 中每个字符的出现次数
        }
        for(int j = 0; j < ransomNote.length(); j++) {
            int idx =ransomNote[j]-'a';
            record[idx]--;
            if(record[idx]<0){
                return false;
            }
        }
        return true;
    }

或者更加清晰,代码冗长一点
 

//使用一个数组的方式模拟hash来进行处理
    bool canConstruct(string ransomNote, string magazine) {
        int record [26]= {0};
        for(int i=0; i<magazine.length();i++){
            record[magazine[i]-'a']++;
        }
        for(int j=0;j<ransomNote.length();j++){
            record[ransomNote[j]-'a'] --;
        }
        for(int s= 0; s<sizeof(record)/sizeof(record[0]);s++){
            if(record[s] <0){
                return false;
            }
        }
        return true ;
    }

java代码如下:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
         int [] record =new int[26];
         for(int i =0; i<magazine.length();i++){
             char  ch =magazine.charAt(i);
             record[ch-'a'] ++;
            
         }
         for(int i =0; i<ransomNote.length();i++){
            char ch =ransomNote.charAt(i);
            record[ch-'a']--;
         }
         for (int i=0; i<record.length;i++){
            if(record[i]<0){
                return false;
            }
         }
         return true ;
    }
}

或者再简单点
 

public boolean canConstruct(String ransomNote, String magazine) {
        int[] record = new int[26];
        for(int i=0; i<magazine.length();i++){
            record[magazine.charAt(i)-'a']++;
        }
        for(int i=0;i<ransomNote.length();i++){
            int idx = ransomNote.charAt(i) - 'a';
            int j = --record[idx];
            //int res=--j;
        
            if(j <0){
                return false;
            }
            
        }
        return true;
    }

python代码如下:

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        list =[0 ]*26
        for i in magazine:
            char = ord(i)-ord('a')
            list[char]+= 1
        for i in ransomNote :
            char =ord(i)-ord('a')
            list[char] -=1
        for i in list :
            if i<0:
                return False
        return True 
from collections  import defaultdict
def canConstruct(self,ransomNote:str ,magazine:str)-> bool :
        ransomNote_count =[0]*26
        magazine_count   =[0]*26 
        for c in magazine:
            magazine_count[ord(c)-ord('a')] += 1
        for c in ransomNote:
            ransomNote_count [ord(c)-ord('a')] +=1 
        return all(ransomNote_count[i]<= magazine_count[i] for i in range(26))
def canConstruct(self ,ransomNote :str ,magazine :str)-> bool:
        hashmap =defaultdict(int)

        for c in magazine: 
            hashmap[c] +=1
        for x in ransomNote :
            value =hashmap.get(x)
            if   not value :
                return False
            else:
                hashmap[x] -=1

        return True
def conConstruct6 (self,ransomNote:str,magazine:str )-> bool:
        return all ( ransomNote(c)< magazine(c) for c in set(ransomNote))
        #觉得这个有问题,回头研究
 def conConstruct (self,ransomNote:str,magazine:str )-> bool:
    # 统计 ransomNote 和 magazine 中每个字符的出现次数
        ransomNote_count = Counter(ransomNote)
        magazine_count = Counter(magazine)
    
    # 检查 ransomNote 中每个字符的数量是否 <= magazine 中对应字符的数量
        for char, count in ransomNote_count.items():
            if magazine_count[char] < count:
                return False
        return True

15. 三数之和

这道题目使用双指针的方式进行处理。 从第一位置算起,i,left,right, 我们针对每个i(for循环nums的size() ),

首先进行兼枝,如果是第一位就大于0的情况,break, 有重复的i,我们要去重。

如果 nums[i] + nums[left] + nums[right] 三者的和大于零,我们right--,如果小于0,我们left++, 这样就能找到nums[i] + nums[left] + nums[right]==0的位置,

然后把这个存到二维动态列表vector中去。  拿到结果后,我们要进行判断,如果nums[left]和它下一位重复,我们要进行去重,同样的,右侧right一样要进行去重。 

之后我们还需要做一步工作,再一次内缩指针。

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) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum< 0) {
                    left++;
                } else {
                    result.push_back({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;
    }
};

java代码如下:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 定义一个二维list,用来存放不同的一维list,每个一维list中是3个元素,能够满足nums[i] + nums[j] + nums[k] == 0
        List<List<Integer>> result = new ArrayList<>();
        // 开始对这个nums数组排序进行遍历循环,并对其中的一些逻辑进行过滤,比如说第一个值就是大于0,比如第一个值和它之后的挨着的值是同一个值,这种情况都要进行过滤。
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    // 创建一个新的一维数组list
                    List<Integer> triplet = new ArrayList<>();
                    triplet.add(nums[i]);
                    triplet.add(nums[left]);
                    triplet.add(nums[right]);
                    result.add(triplet);
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }
        return result ;
    }
}

python :

def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()  # 排序,便于后续的双指针法
        for i in range(len(nums)):
            if nums[i] > 0:
                return result  # 如果当前数大于0,后续不可能有满足条件的三元组了
            if i > 0 and nums[i] == nums[i - 1]:
                continue  # 跳过重复的元素
            
            left = i + 1
            right = len(nums) - 1
            while left < right:
                sum = nums[i] + nums[left] + nums[right]
                if sum > 0:
                    right -= 1
                elif sum < 0:
                    left += 1
                else:
                    result.append([nums[i], nums[left], nums[right]])
                    # 跳过重复的数字
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1
                    # 更新指针以继续寻找新的三元组
                    left += 1
                    right -= 1

        return result

有人写出来很der的代码,我没有看懂,帖上来,有脑子的人帮我看看,这个der为何要这么写:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()
        # 找出a + b + c = 0
        # a = nums[i], b = nums[j], c = -(a + b)
        for i in range(len(nums)):
            # 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            if nums[i] > 0:
                break
            if i > 0 and nums[i] == nums[i - 1]: #三元组元素a去重
                continue
            d = {}
            for j in range(i + 1, len(nums)):
                if j > i + 2 and nums[j] == nums[j-1] == nums[j-2]: # 三元组元素b去重
                    continue
                c = 0 - (nums[i] + nums[j])
                if c in d:
                    result.append([nums[i], nums[j], c])
                    d.pop(c) # 三元组元素c去重
                else:
                    d[nums[j]] = j
        return result

18 四数之和

18. 四数之和 - 力扣(LeetCode)

这个题目和三数字之和的解题思路是差不多的,需要注意的是这个4数字之和,我们可以多出来一个固定的数k, 然后再遍历i=k+1, i之后的思路left ,right 和之前的三数字之和是一样的。

我们剪枝的时候, 第一层是对k的循环,这个过程中,我们过滤是考虑nums[k]>0&&target>0,如果target是负数,那就中招了。 

同样的道理,我们在对i进行遍历循环时候,首先要把nums[k] +nums[i] 看成是一个整体, 其次我们剪枝时候,也要把nums[k] +nums[i]>0 && target>0 考虑进去。 

其他的思路和之前的三数之和逻辑一样。 

代码如下:
 


class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        // 类似3个数和的操作,进行一个遍历,多了一个固定的k
        List<List<Integer>> result = new ArrayList<>();
        for (int k = 0; k < nums.length; k++) {
            // 进行剪枝策略,
            // nums[k] > 0 && nums[k] > target && target > 0
            // nums[k] > target && target > 0
            if (nums[k] > target && nums[k]  > 0) {
                return result;
            }
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            // 想一下i怎么进行固定呢?对i进行for循环,
            for (int i = k + 1; i < nums.length; i++) {
                // k和i 2个看成是一个整体
                if (nums[i] + nums[k] > target && (nums[i] + nums[k]) > 0 ) {
                    continue;
                }
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.length - 1;
                while (left < right) {
                    int sum = nums[k] + nums[i] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[k] ,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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值