leetcode算法练习——454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

leetcode算法练习

454.四数相加II

题目链接
思路:

  • 将四个数组分为A、B、C、D 遍历取前两个数组中的数为a+b 放入集合中 后两个数组中的数为c+d 判断能实现target=0时 c+d所需的匹配数 是否在集合中出现过 即考虑0-(c+d)是否在集合中
  • 考虑使用什么数据结构? 本题中数值可能会较大 利用数组下标做映射可能不够 故数组pass 同时 不但要考虑a+b在集合中是否出现过 还要考虑到a+b出现过多少次 这样才能和c+d作映射 故考虑使用map(key为a+b value为a+b出现的次数)
  • 最后统计有多少组符合条件的 在累加时 加的是value的值 而不是每次+1
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        // 设定存放值和次数的map
        Map<Integer, Integer> map = new HashMap<>();
        int temp;
        int res = 0;
        // 统计两个数组中的元素之和 同时统计出现的次数 放入map
        // 遍历第一个数组
        for (int i : nums1) {
            // 遍历第二个数组
            for (int j : nums2) {
                // 记录下元素之和
                temp = i + j;
                // 判断该元素之和是否已经存在于map中
                if (map.containsKey(temp)) {
                    // 如若已经存在 将该元素对应的value +1
                    map.put(temp, map.get(temp) + 1);
                } else {
                    // 若还不存在 将元素之和作为key存入 value为1 代表出现次数
                    map.put(temp, 1);
                }
            }
        }
        // 统计剩余的两个元素的和 在map中找是否存在相加为0的情况 同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                temp = i + j;
                // 查找目标值 即之前说明的0- (c + d)
                if (map.containsKey(0 - temp)) {
                    // 次数要加对应的value
                    res += map.get(0 - temp);
                }
            }
        }
        return res;
    }
}

383. 赎金信

思路:
判断元素是否出现过之间联想到哈希法 本题的数值范围为a~z 故可以考虑使用数组来解决——a对应到数组下标为0的位置 z对应到数组下标为25的位置 遍历第一个字符串出现的字母频率 存入定义好的record[26]数组中 遍历第二个字符串每个字母的出现频率 对应地到record数组中做减法

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 定义一个哈希映射数组
        int[] record = new int[26];
        // 遍历第一个字符 对应作加法
        for(char c : magazine.toCharArray()){
            record[c - 'a'] += 1;
        }
        // 遍历第二个字符 对应作减法
        for(char c : ransomNote.toCharArray()){
            record[c - 'a'] -= 1;
        }
        // 如果数组中存在负数,说明ransomNote字符串总存在magazine中没有的字符
        for(int i : record){
            if(i < 0){
                return false;
            }
        }
        return true;
    }
}

15. 三数之和

题目链接
注意:
三元组是要去重的——本题去重较复杂

思路:

  • 本题即找到a+b+c=0 若使用哈希法:两个for循环 第一个for确定a 第二个for确定b 然后在数组中查询0-(a+b)是否出现过 利用map来做映射 但因为a b c都要去重 使用哈希法比较麻烦 因为哈希法在寻找c时是没有什么顺序的

  • 选择使用双指针法较为合适 因为三元组要去重

  • 首先要先对数组进行排序 创建三个指针i left right(i表示a的下标 left表示b的下标 right表示c的下标) 第一个for循环来控制i i表示现在遍历的元素 从左至右 ——当nums[i]+nums[left]+nums[right] > 0时 意味着数据大了 应该缩小 故让right往左边移动一位;当nums[i]+nums[left]+nums[right] < 0时 意味着数据小了 应该扩大 故让left往右边移动一位;当nums[i]+nums[left]+nums[right] = 0时 获取结果

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        // 对数组进行排序
        Arrays.sort(nums);
        // 遍历数组
        for (int i = 0; i < nums.length; i++) {
            // 因为已经排过序了 如果第一个数就已经>0 那不可能后面再加=0
            if (nums[i] > 0) {
                return result;
            }
            // 注意判断是num[i] == num[i+1] 还是num[i] == num[i-1]
            // 例如:-1  -1       2
            //       i  left    right
            //如果num[i] == num[i+1] 那么前-1 == -1 变成判断集合内元素是否有2重复
            //实际上是要判断 集合是否有重复 故应该选择num[i] == num[i-1] 这样当后-1 == 前-1 才意味着 重复了——可以理解为:a现在是第一个数 如果第一个数确定了 那么其left right也确定了 所以a要和之前已经记录过的元素不一样
            // 对a去重
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 初始化left和right 各自向中心移动
            int left = i + 1;
            int right = nums.length - 1;
            // 应该是right >= left 还是right > left——现在是求三个数 若left=right 那么就是两个数了 故left != right
            while (right > left) {
                int sum = nums[i] + nums[left] + nums[right];
                // 若三数之和>0;<0;=0
                if (sum > 0) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    // 把三个数初始成一个数组 放入result数组中
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 对结果去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
}

18. 四数之和

题目链接
思路:

  • 在三数之和的基础上 再多加一个for循环 即i j left right
  • 主要难点在剪枝和去重的操作:
  • 首先不能像三数之和一样 刚开始就单纯判断nuns[i]>target剪枝 因为target是任意输入的 同时数组内的数可能<0 例如:target=-5 排序后数组为 -4 -1 0 0 那么nums[i] = -4 > target 但是明显是有符合条件的数组的——故利用if(nums[i] > 0 && nums[i] > target) break;
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {
            // 剪枝操作
            if (nums[i] > 0 && nums[i] > target) {
                return result;
            }
            // 和三数之和一样的去重逻辑
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < nums.length; j++) {
                // 2级剪枝处理
                if (nums[i] + nums[j] > target && nums[i] + nums[j] >= 0) {
                    break;
                }
                // 对j进行去重
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                int left = j + 1;
                int right = nums.length - 1;
                while (right > left) {
                    long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target) {
                        right--;
                    } else if (sum < target) {
                        left++;
                    } else {
                        result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值