LeetCode_Day7 | 三数之和、四数之和

LeetCode_哈希表

  • 15.三数之和
    • 1.题目描述
    • 2.双指针法
      • 2.1思路及注意点
      • 2.2随随便便的笔记
      • 2.3代码实现
    • 3.哈希法(有待修正)
      • 3.1 思路
      • 3.2 代码实现
  • 18.四数之和
    • 1. 题目描述
    • 2. 双指针法
      • 2.1 思路及注意点
      • 2.2 代码实现

15.三数之和

1.题目描述

在这里插入图片描述
在这里插入图片描述
详情leetcode链接

2.双指针法

2.1思路及注意点

  • 将数组排序,有一层for循环,i从下标0开始,同时定义一个下标left在i+1的位置上,定义下标right在数组末尾位置。
  • 依次在数组中找到abc,使得a + b +c =0,这里相当于 a =nums[i],b = nums[left],c = nums[right]。
  • 若nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
  • 若nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left向右移动,才能让三数之和大一些,直到left与right相遇为止。

去重注意:
因为本题要求不能重复的三元组,所以需要对每个数进行去重。

最外层for循环对a去重时,错误写法:if条件为nums[i]=nums[i+1],这样判断的是结果集中是否有重复元素,会误跳过结果集
正确写法:if条件为nums[i]=nums[i-1],举例{-1,-1,2}

b和c去重时,应该在找到一个三元组后进行去重

2.2随随便便的笔记

LeetCode_Day7 | 三数之和、四数之和_第1张图片

2.3代码实现

/**
 * 双指针法:(推荐)
 * 思路:
 *   将数组排序,有一层for循环,i从下标0开始,同时定义一个下标left在i+1的位置上,定
 * 义下标right在数组末尾位置,依次在数组中找到abc,使得a + b +c =0,这里相当于 a =
 * nums[i],b = nums[left],c = nums[right]。
 *   若nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是
 * 排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
 *   若nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left向右移
 *动,才能让三数之和大一些,直到left与right相遇为止。
 *    
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(1) 
 *  */
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,则必然不会组成规定的三元组
            if (nums[i]>0){
                return result;
            }
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            //正确去重方法
            if (i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            int left = i+1;
            int right = nums.length - 1;
            //边界条件考虑:若left = right,b和c相当于同一个数,不符合题意
            while (right > left){
                int sum = nums[i] + nums[left] + nums[right];
                if (sum > 0){
                    right --;
                }else if (sum < 0) {
                    left ++;
                }else {
                    result.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    //当找到一个三元组后,对b和c去重
                    while (right > left && nums[left]==nums[left+1]) left++;
                    while (right > left && nums[right]==nums[right-1]) right--;
                    left ++;
                    right --;
                }
            }
        }
        return result;
    }
}

3.哈希法(有待修正)

3.1 思路

  • 两层for循环就可以确定 a 和b 的数值了,然后使用哈希法来确定 0-(a+b) 是否在 数组里出现过
  • 因为结果数组中不包含重复的三元组,所以需要依次对三个数进行去重及对结果去重

3.2 代码实现

/**
 * 暴力法:双层for循环(不推荐)
 * 思路:两层for循环就可以确定 a 和b 的数值了,
 *       然后使用哈希法来确定 0-(a+b) 是否在 数组里出现过,
 *       因为结果数组中不包含重复的三元组,所以需要依次对三个数进行去重及对结果去重
 *       此法代码编写时间复杂度高且需要去重考虑的细节很多,复杂不推荐
 * 时间复杂度:O(n^2)
 * 空间复杂度:O(n) //额外的set开销
 *
 */
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        Set<Integer> set = new HashSet<>();
        Set<Integer> filter = new HashSet<>();
        for (int i = 0; i < nums.length; i++){
            if (nums[i] > 0){//若数组的第一个数大于0,直接返回
                return result;
            }
            if (i > 0 && nums[i]==nums[i-1]){//对第一个数去重
                continue;
            }
            for (int j = i + 1; j < nums.length; j++){
                if (j > i+1 && nums[j]==nums[j-1]){//对第二个数去重
                    continue;
                }
                int third = 0 - (nums[i] + nums[j]);
                if (set.contains(third) && !filter.contains(nums[j])){
                    List<Integer> singResult = new ArrayList<>();
                    singResult.add(nums[i]);
                    singResult.add(nums[j]);
                    singResult.add(third);
                    result.add(singResult);
                    set.remove(third);//对第三个数去重
                    filter.add(third);//防止有重复的集合
                }else {
                    set.add(nums[j]);
                }
            }
        }
        return result;
    }
}

18.四数之和

1. 题目描述

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
LeetCode_Day7 | 三数之和、四数之和_第2张图片
leetcode链接

2. 双指针法

2.1 思路及注意点

  • 四数之和,和15.三数之和 (opens new window)是一个思路,都是使用双指针法
  • 解法就是在15.三数之和 (opens new window)的基础上再套一层for循环。
  • 一些需要注意的细节:判断满足的条件跳出循环时,三数之和的targert为0是固定的,而四数之和不确定。

2.2 代码实现

/**
 * 思路:双指针法
 *   四数之和,和15.三数之和 (opens new window)是一个思路,都是使用双指针法,
 *   解法就是在15.三数之和 (opens new window)的基础上再套一层for循环。
 *   一些需要注意的细节:
 *   判断满足的条件跳出循环时,三数之和的targert为0是固定的,而四数之和不确定。
 * 时间复杂度:O(n^3)
 * 空间复杂度:O(1)
 */
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++) {
            //不能武断的判断nums[i]>target,例如数组是[-4, -3, -2, -1],
            //target是-10,不能因为-4 > -10而跳过
            if (nums[i] > target && (nums[i] > 0 || target > 0)) {
                //因为已对结果集进行了排序
                return result;
            }
            //对第一个数进行去重
            if (i > 0 && nums[i] == nums[i-1]) {
                continue;
            }
            for (int j = i+1; j < nums.length;j++) {
                //判断跳出操作
                if (nums[i] >0 && nums[i]+nums[j] > target){
                    continue;
                }
                //对第二个数去重
                if (j > i+1 && nums[j]==nums[j-1]){
                    continue;
                }
                int left = j +1;
                int right = nums.length-1;
                while (right > left){
            // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
                     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]));
                        // //当找到一个三元组时,对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; 
    }
}

你可能感兴趣的:(坚持学算法,leetcode,哈希算法,算法)