leetcode数和问题:两数之和、三数之和、四数之和

  • 两数之和:HashMap或者双重循环

  • 有序数组的两数之和:双指针或者二分查找

  • 三数之和:排序+双指针,单重循环嵌套双指针

  • 四数之和:排序+双指针,双重循环嵌套双指针

  • 四个数组内的四数之和:分组+HashMap

1. 两数之和

1.1 题目描述

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]

提示:

  • 2 <= nums.length <= 103
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109
  • 只会存在一个有效答案

1.2 解题思路一

双重循环,时间复杂度 $O(n^2) $,空间复杂度$O(1) $。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        i:for(int i=0;i<nums.length-1;i++) {
            for(int j=i+1;j<nums.length;j++) {
                if(nums[i] + nums[j] == target){
                    result[0] = i;
                    result[1] = j;
                    break i;
                }
            }
        }
        return result;
    }
}

1.3 解题思路二

利用HashMap,空间换时间,时间复杂度$O(n)$ ,空间复杂度$O(n)$ 。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        Map<Integer, Integer> hashMap = new LinkedHashMap<Integer, Integer>();
        for(int i=0;i<nums.length;i++) {
            //target - nums[i'] == num[i]
            //说明这两个位置上的数字之和等于target
            if(hashMap.containsKey(nums[i])){
                //通过键找值,找到target - nums[i']对应的下标
                result[0] = hashMap.get(nums[i]);
                result[1] = i;
                break;
            }
            //键:target-nums[i],值:下标
            hashMap.put(target-nums[i], i);
        }
        return result;
    }
}

167. 两数之和 II - 输入有序数组

167.1 题目描述

给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例 1:

输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

示例 2:

输入:numbers = [2,3,4], target = 6
输出:[1,3]

示例 3:

输入:numbers = [-1,0], target = -1
输出:[1,2]

提示:

  • 2 <= numbers.length <= 3 * 104
  • -1000 <= numbers[i] <= 1000
  • numbers递增顺序 排列
  • -1000 <= target <= 1000
  • 仅存在一个有效答案

167.2 解题思路

思路一:双指针,时间复杂度$O(n)$,空间复杂度$O(1)$

思路二:二分查找,时间复杂度$O(nlogn)$,空间复杂度$O(1)$

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int[] result = new int[2];
        for(int i=0,j=numbers.length-1;i<j;) {
            if(numbers[i] + numbers[j] == target) {
                result[0] = i+1;
                result[1] = j+1;
                break;
            }else if(numbers[i] + numbers[j] > target) {
                j--;
            }else {
                i++;
            }
        }
        return result;
    }
}

15. 三数之和

15.1 题目描述

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:

输入:nums = []
输出:[]

示例 3:

输入:nums = [0]
输出:[]

提示:

  • 0 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

15.2 解题思路

三重循环使用双指针方式转化成双重循环

具体思路:

  • 将数组按从大到小的顺序进行排序:Arrays.sort(nums);,时间复杂度$O(nlogn)$。

  • 判断特例:

    • 数组大小小于3,直接返回空链表
    • 排序后数组的首位元素(循环中 i 指向的元素)大于0,直接返回结果
  • 使用三个指针,分别为i、j、k,双重循环时间复杂度$O(n^2)$

    • 固定 i 不变,j 和 k 分别指向下标 i 之后的数组的头和尾
    • 如果三指针指向的数组元素和大于0,则 k--
    • 如果三指针指向的数组元素和小于0,则 j++
    • 如果三指针指向的数组元素和等于0,则在返回链表中添加对应的数组元素
  • 题目中的难点:返回的三元组结果不能重复

    • 为了避免重复,我们首先要保证最外层循环中,i 指向的元素是不重复的,所以每次循环前要检查目前 i 指向的数组元素是否等于 i 上次指向的数组元素,如果重复就 i++
    • 在内层循环中,找到和等于0的三元素之后,继续内层循环,此时要确保 j 和 k 指向的元素和上次指向的元素不同

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> linkedList = new LinkedList<>();
        Arrays.sort(nums);
        if(nums.length<3) {
            return linkedList;
        }
        for(int i=0;i<nums.length-2;i++) {
            if(nums[i]>0)
                break;
            while(i>0 && i<nums.length-2 && nums[i]==nums[i-1]) {
                i++;
            }
            int j=i+1,k=nums.length-1; 
            while(j<k) {
                if(nums[i]+nums[j]+nums[k]>0) {
                    k--;
                }else if(nums[i]+nums[j]+nums[k]<0){
                    j++;
                }else {
                    //双重链表添加元素的方式
                    linkedList.add(new LinkedList<>(Arrays.asList(nums[i],nums[j],nums[k])));
                    k--;
                    j++;
                    while(j<k && nums[j]==nums[j-1]){
                        j++;
                    }
                    while(j<k && nums[k]==nums[k+1]){
                        k--;
                    }
                }
            }
        }
        return linkedList;
    }
}

16. 最接近的三数之和

16.1 题目描述

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:

  • 3 <= nums.length <= 10^3
  • -10^3 <= nums[i] <= 10^3
  • -10^4 <= target <= 10^4

16.2 解题思路

排序+双指针

时间复杂度$O(n^2)$ ,空间复杂度$O(logn)$

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        if(nums.length<3) {
            return 0;
        }
        int result, mostClose = nums[0] + nums[1] + nums[2];
        for(int i=0;i<nums.length-2;i++) {
            for(int j=i+1,k=nums.length-1;j<k;) {
                result = nums[i] + nums[j] +nums[k];
                if(result>target) {
                    k--;
                }else if(result<target) {
                    j++;
                }else {
                    return result;
                }
                if(Math.abs(mostClose-target)>Math.abs(result-target)) {
                    mostClose = result;
                }
            }
        }
        return mostClose;
    }
}

18. 四数之和

18.1 题目描述

给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,**b,cd ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:答案中不可以包含重复的四元组。

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [], target = 0
输出:[]

提示:

  • 0 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

18.2 解题思路

排序+双指针

双重循环

时间复杂度:$O(n^3)$,其中 $n$ 是数组的长度。排序的时间复杂度是$O(nlogn)$,枚举四元组的时间复杂度是
$O(n^3)$ ),因此总时间复杂度为:$O(n^3)+O(nlogn)$

空间复杂度:$O(logn)$,其中 $n$ 是数组的长度。空间复杂度主要取决于排序额外使用的空间。此外排序修改了输入数组nums,实际情况中不一定允许,因此也可以看成使用了一个额外的数组存储了数组nums的副本并排序,空间复杂度为$O(n)$。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> list = new LinkedList<>();
        if(nums.length < 4){
            return list;
        }
        Arrays.sort(nums);
        int result=0;
        for(int i=0;i<nums.length-3;) {
            for(int l=i+1;l<nums.length-2;) {
                for(int j=l+1,k=nums.length-1;j<k;) {
                    result = nums[i] + nums[j] + nums[k] + nums[l];
                    if(result>target) {
                        k--;
                    }else if(result<target) {
                        j++;
                    }else {
                        list.add(new LinkedList<>(Arrays.asList(nums[i],nums[l],nums[j],nums[k])));
                        j++;
                        k--;
                        //检查重复元素
                        while(j<k && nums[j]==nums[j-1]) {
                            j++;
                        } 
                    }
                }
                l++;
                //检查重复元素
                while(nums[l]==nums[l-1] && l<nums.length-2) {
                    l++;
                }
            }
            i++;
            //检查重复元素
            while(nums[i]==nums[i-1] && i<nums.length-3) {
                i++;
            }
        }
        return list;
    }
}

454. 四数相加 II

454.1 题目描述

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:

输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]

输出:
2

解释:
两个元组如下:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

454.2 解题思路

分组+哈希表

时间复杂度$O(n2)$,空间复杂度$O(n2)$

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int n=nums1.length;
        if(n == 0) {
            return 0;
        }
        int count=0;
        Map<Integer,Integer> hashMap = new LinkedHashMap<>();
        for(int i=0;i<n;i++) {
            for(int j=0;j<n;j++) {
                //key:两数之和  value:该数出现的次数
                if(hashMap.containsKey(nums1[i]+nums2[j])) {
                    hashMap.put(nums1[i]+nums2[j], hashMap.get(nums1[i]+nums2[j])+1);
                }else {
                    hashMap.put(nums1[i]+nums2[j], 1);
                }
            }
        }
        for(int k=0;k<n;k++) {
            for(int l=0;l<n;l++) {
                if(hashMap.containsKey(-nums3[k]-nums4[l])) {
                    count += hashMap.get(-nums3[k]-nums4[l]);
                }
            }
        }
        return count;
    }
}
posted @ 2021-04-28 14:35  又又又8  阅读(157)  评论(0编辑  收藏  举报