day7算法训练|哈希表part02

第454题.四数相加II

思路:比较迷茫,就想着用backtrack

总结:四个数来自四个独立的数组,不用考虑结果重复的问题。所以可以用哈希办法处理。

先用O(n2)的循环把两个数组的结果存入hashmap (空间换时间)

再遍历后面两个数组,找hashmap中是否有对应值 O(n2) 

整个算法时间复杂度为O(n2)

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int res = 0;
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        //统计两个数组中的元素之和,同时统计出现的次数,放入map
        for (int i : nums1) {
            for (int j : nums2) {
                int sum = i + j;
                map.put(sum, map.getOrDefault(sum, 0) + 1);
            }
        }
        //统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                res += map.getOrDefault(0 - i - j, 0);
            }
        }
        return res;
    }
}

常用:getOrDefault(key, defaultValue);

 383. 赎金信  

思路:用hash (数组)来储存第一个字符串的信息,再来检查第二个字符串是否匹配

总结:因为字符串中只有小写字母,只有26种情况,还能用其与字符a的ascii码的差值来代表各个字母,所以可以直接用一个数组来充当哈希

可以在函数开始的时候判断长度大小直接得到false结论

第15题. 三数之和

排序的原因:1. 三数和大于/小于0时候,知道如何操作left&right指针

2. 对与最外层循环的每个i,找到一个答案后,只需要将左右指针同时去重往内部锁一位(去重的情况下),其他答案肯定在内部,不需要重制左右指针

e.g -2,-2,-2,-1,-1,-1,0,0,0,0,0,0,1,1,1,2,2,2,3,3

先发现-2,0,2;再是-1,0,1;最后0,0,0。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new LinkedList<>();
        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){
                if(nums[i]+nums[right]+nums[left]>0){
                    right--;
                }else if(nums[i]+nums[right]+nums[left]<0){
                    left++;
                }else{
                    LinkedList<Integer> temp = new LinkedList<>();
                    temp.add(nums[i]);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    res.add(temp);
                    while(left<right&&nums[left]==nums[left+1]){
                        left++;
                    }
                    while(left<right&&nums[right]==nums[right-1]){
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }
        return res;
    }
}

a(nums[i])的去重:

nums[i]==nums[i+1] 在进入bc选择前就把所有的重复i给遍历了,去除了b,c与a重复的可能性

应该是i>0&&nums[i]==nums[i-1]来去重,避免这次nums[i]跟之前用的同一个数

b, c的去重 (nums[left], nums[right])

写代码遇到的问题:1.我之前把去重部分放在了if else 中left right更新那里。2.忘记了在找到正确答案后更新left和right并去重,再去寻找下一个答案

1. 只是找到答案,if else的条件就足够让left++, right--了去重写在这里,只是提前去重,没有意义,重要的是在得到答案后,避免答案有重复,所以要对b,c去重

2. 找到答案后,并去重后,记得把left和right都向里移一位,寻找下一个答案,否则会陷入死循环(每个if else的可能性都需要有left,rigth的更新,来达到循环结束的条件)

nums[i]>0的剪枝:在排序的情况下第一个数字大于0,说明sum>0可以直接剪掉

第18题. 四数之和

剪枝:由于target不一定是正数,所以剪枝的条件如下

nums[i]>target && nums[i]>0

首先target或者nums[i]大于零 才能保证nums[i]后面都是正数,所以接着加肯定比target大,可以直接剪枝

去重:j的initial value是i+1,但是去重从i+1之后开始考虑,因为要考虑nums[i]==nums[j]的情况

if (j > i + 1 && nums[j - 1] == nums[j]) {  // 对nums[j]去重
     continue;
}

nSum:  用递归的方法到2sum 记得去重

    public class Solution {
        int len = 0;
        public List<List<Integer>> fourSum(int[] nums, int target) {
            len = nums.length;
            Arrays.sort(nums);
            return kSum(nums, target, 4, 0);
        }
       private ArrayList<List<Integer>> kSum(int[] nums, int target, int k, int index) {
            ArrayList<List<Integer>> res = new ArrayList<List<Integer>>();
            if(index >= len) {
                return res;
            }
            if(k == 2) {
            	int i = index, j = len - 1;
            	while(i < j) {
                    //find a pair
            	    if(target - nums[i] == nums[j]) {
            	    	List<Integer> temp = new ArrayList<>();
                    	temp.add(nums[i]);
                    	temp.add(target-nums[i]);
                        res.add(temp);
                        //skip duplication
                        while(i<j && nums[i]==nums[i+1]) i++;
                        while(i<j && nums[j-1]==nums[j]) j--;
                        i++;
                        j--;
                    //move left bound
            	    } else if (target - nums[i] > nums[j]) {
            	        i++;
                    //move right bound
            	    } else {
            	        j--;
            	    }
            	}
            } else{
                for (int i = index; i < len - k + 1; i++) {
                    //use current number to reduce ksum into k-1sum
                    ArrayList<List<Integer>> temp = kSum(nums, target - nums[i], k-1, i+1);
                    if(temp != null){
                        //add previous results
                        for (List<Integer> t : temp) {
                            t.add(0, nums[i]);
                        }
                        res.addAll(temp);
                    }
                    while (i < len-1 && nums[i] == nums[i+1]) {
                        //skip duplicated numbers
                        i++;
                    }
                }
            }
            return res;
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值