算法训练-Day6|454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

454.四数相加II

        在使用map时,要学会函数的使用。在本题中,用到了getOrdefault(key,defaultValue)函数。其中,key表示Map中的键,defaultValue表示当键key不存在时返回的默认值。

        函数的由来:在Java 8中,Map接口中引入了一个新的方法getOrDefault(key, defaultValue),用于获取指定key对应的value,若该key不存在,则返回defaultValue。此方法使用方便,代码简洁,常被用于避免NullPointerException异常。

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int res = 0;  //统计四数之和为0的组数
        Map<Integer,Integer> map = new HashMap<Integer,Integer>(); //创建unordered_map,key为两数之和,value存放次数

        //计算nums1和nums2的两数之和,并记录出现的次数
        for(int i:nums1)    
            for(int j:nums2){
                int sum = i+j;
                map.put(sum,map.getOrDefault(sum,0)+1);
            }
        //计算nums3和nums4的两数之和,判断其负数是否在map中,若存在,说明四数之和为0
        for(int i:nums3)
            for(int j:nums4)
                res += map.getOrDefault(0-i-j,0);

        return res;  
    }

383. 赎金信

        这道题目和242.有效的字母异位词 (opens new window)很像。根据题意,很明显用数组哈希法。

        在这道题中,要记住的函数是toCharArray()函数,"toCharArray()"是String类的一个方法,用于将字符串转换为一个字符数组。它返回一个新的字符数组,该数组包含字符串中的所有字符,且顺序与原字符串保持一致。

    public boolean canConstruct(String ransomNote, String magazine) {
        int[] count = new int[26];  //存放字母出现的次数

        //记录ransomNote字母出现次数
        for(char i:ransomNote.toCharArray())
            count[i-'a']++;

        //与ransomNote中出现的字母次数抵消(可以理解为删除了ransomNote中的字母)
        for(char j:magazine.toCharArray())
            count[j-'a']--;

        //ransomNote中的字母是否有未被抵消的
        for(int k = 0;k < 26;k++)
            if(count[k] > 0)
                return false;   //若有,则返回false
        
        return true;   //若没有,则返回true
    }

15. 三数之和

       本题与 1. 两数之和 类似,是非常经典的面试题,但是做法不尽相同。

        因为本题是不能重复的,所以存在去重的问题。但是本题如果用哈希法的话去重会比较复杂,因此使用双指针法。

        而且要注意,要先对数组进行排序后,才能进行双指针法。因为本题只要知道元素相加等于0,返回的不是下标,因此可以先进行排序。而且双指针法的前提是数组有序。

        本题中,注意下Arrays.sort()函数,是对数组进行排序的。还要注意下List的创建。

 public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        int len = nums.length;
        if(nums == null || len < 3) //考虑特殊情况
            return result;

        //选择排序
       for(int i = 0;i < len;i++){
           int min = i; //这里要注意,是每轮循环时min都要先为i
            for(int j = i+1;j < len;j++)
                if(nums[min] > nums[j])
                    min = j;
            int temp = nums[min];
            nums[min] = nums[i];
            nums[i] = temp;
       }

        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for(int i = 0;i < len;i++){
            if(nums[i] > 0)     
                break;

            //对a去重,我们要做的是不能有重复的三元组,但三元组内的元素是可以重复的!,因此不能为nums[i] == nums[i+1]
            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){
                    result.add(Arrays.asList(nums[i],nums[left],nums[right]));

                    //给b和c去重,三元组间去重
                    while(left < right && nums[left] == nums[left+1])  //去重b
                        left++;
                    while(left < right && nums[right] == nums[right-1]) //去重c
                        right--;

                    //注意left和right的更新
                    left++;
                    right--;
                }else if(sum < 0)
                    left++;
                else
                    right--;

             
            }
        }
        return result;
    }

 18. 四数之和 

        四数之和与三数之和的方法一样,也是排序+双指针法。

        四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。

        那么一样的道理,五数之和、六数之和等等都采用这种解法。

public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        int len = nums.length;
        
        //判断特殊情况,比如为空或者数组长度小于4
        if(nums == null || len < 4)
            return result;

        //选择排序
        for(int i = 0;i < len;i++){
            int min = i;
            for(int j = i+1;j < len;j++)
                if(nums[min] > nums[j])
                    min = j;
            int temp = nums[i];
            nums[i] = nums[min];
            nums[min] = temp;
        }

        for(int i = 0;i < len;i++){
            //比如:数组是[-4, -3, -2, -1],target是-10,不能因为-4 > -10而跳过,所以要价格nums[i]>0
            if(nums[i] > 0 && nums[i] > target)   
                return result;

            if(i > 0 && nums[i] == nums[i-1])  //对nums[i]去重
                continue;

            for(int j = i+1;j < len;j++){
                if(j > i+1 && nums[j] == nums[j-1]) //对nums[j]
                    continue;

                int left = j+1;
                int right = len-1;

                while(left < right){
                    long sum = (long)nums[i] + nums[j] + nums[left] + nums[right]; 
                    if(sum == target){
                        result.add(Arrays.asList(nums[i],nums[j],nums[left],nums[right]));
                        //对left去重
                        while(left < right && nums[left] == nums[left+1])
                            left++;
                        //对right去重
                        while(left < right && nums[right] == nums[right-1])
                            right--;
                        //更新right和left的值
                        right--;
                        left++;
                    }else if(sum > target)
                        right--;
                    else    
                        left++;
                }
            }
        }
        return result;
    }

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值