【Leetcode-018】四数之和(Java && C++实现)


1 数组排序+双指针+递归(Java实现)

/*
    方法:数组排序 + 双指针 + 递归调用
    难度:中等
    思路:从二数之和扩展到三数之和,再扩展到四数之和
          对排序后的数组进行遍历时(第1个数),第2、3、4个数调用已有的三数之和API
          遍历过程需保证第1个数不能出现重复(第2、3、4个数已控制不会出现重复结果)
    时间复杂度:O(n^3)
           数组排序O(n logn)
           计算三数之和的双指针操作O(n * n) → 循环调用n次
           总时间复杂度:O(n logn + O(n* n) * n) = O(n^3)
    空间复杂度:O(n)
    类似题目:t001、t015
*/

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        //对数组进行排序
        Arrays.sort(nums);

        List<List<Integer>> res = new ArrayList<>();
        //for循环遍历数组
        for(int i = 0; i < nums.length; i++){
    
    
            //调用三元组的函数threeSumTarget → 计算目标值为target - nums[i]的三元组
            List<List<Integer>> temps = threeSumTarget(nums, i + 1,target - nums[i]);

            if(!temps.isEmpty()){
    
    
                for(List<Integer> temp : temps){
    
    
                    temp.add(nums[i]);
                    res.add(temp);
                }
                //跳过左侧遍历过程中的重复元素
                while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
            }
        }
        return res;
    }

    //返回数组nums中和为target的三元组(循环的起始位置为start)
    public List<List<Integer>> threeSumTarget(int[] nums, int start , int target){
    
    
        //对数组进行排序
        Arrays.sort(nums);

        List<List<Integer>> res = new ArrayList<>();
        //for循环遍历数组
        for(int i = start; i < nums.length; i++){
    
       //第2个数的起始索引为start
            //调用二元组的函数twoSumTarget → 计算目标值为target - nums[i]的二元组
            List<List<Integer>> temps = twoSumTarget(nums, i + 1,target - nums[i]);

            if(!temps.isEmpty()){
    
    
                for(List<Integer> temp : temps){
    
    
                    temp.add(nums[i]);
                    res.add(temp);
                }
                //跳过左侧遍历过程中的重复元素
                while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
            }
        }
        return res;
    }

    //返回数组nums中和为target的二元组(循环的起始位置为start)
    public List<List<Integer>> twoSumTarget(int[] nums, int start ,int target){
    
    
        //对数组进行排序
        Arrays.sort(nums);
        int low = start;    //第2个元素的起始索引位置start
        int high = nums.length - 1;

        List<List<Integer>> res = new ArrayList<>();
        //while循环遍历数组
        while(low < high){
    
    
            //记录当前的左值和右值
            int left = nums[low];
            int right = nums[high];
            int sum = left + right;
            if(sum < target){
    
    
                while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
            }else if(sum > target){
    
    
                while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
            }else if(sum == target){
    
    
                //向结果集res中添加符合条件的结果
                res.add(new ArrayList<Integer>(Arrays.asList(left, right)));
                while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
                while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
            }
        }
        return res;
    }
}

2 拓展:N数之和

/*
    框架:求解数组nums中N个数之和为目标值,结果不能重复
    方法:数组排序 + 双指针 + 递归调用
    思路:从两数之和、三数之和及四数之和中找出规律
           (1)两数之和:while循环,左右指针
           (2)三数之和:for循环,控制两数之和while循环的起始索引位置,调用两数之和结果
           (3)N数之和:for循环,控制for循环的起始索引,避免第1个数出现重复;递归调用 N - 1数之和的结果
    注意:调用前必须对数组进行排序,避免每次递归调用过程中进行没必要的排序
    类似题目:t001、t015、t018
*/

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    
    
    public List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        //对数组nums进行排序
        Arrays.sort(nums);
        // n 为 4,从 nums[0] 开始计算和为 target 的四元组
        return nSumTarget(nums, 4 , 0, target);
    }

    //调用前必须对数组进行排序,避免每次递归调用过程中进行没必要的排序
    public List<List<Integer>> nSumTarget(int[] nums, int n, int start ,int target) {
    
    
        List<List<Integer>> res = new ArrayList<>();

        //边界排除:至少是2个数求和,且不能超过数组元素个数
        if(n < 2 || n > nums.length) return res;

        /* 两数之和 */
        if(n == 2){
    
    
            int low = start;    //起始索引位置start
            int high = nums.length - 1;

            //while循环遍历数组
            while(low < high){
    
    
                //记录当前的左值和右值
                int left = nums[low];
                int right = nums[high];
                int sum = left + right;
                if(sum < target){
    
    
                    while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
                }else if(sum > target){
    
    
                    while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
                }else if(sum == target){
    
    
                    //向结果集res中添加符合条件的结果
                    res.add(new ArrayList<Integer>(Arrays.asList(left, right)));
                    while(low < high && nums[low] == left) low++; //跳过左侧所有重复的元素
                    while(low < high && nums[high] == right) high--; //跳过右侧所有重复的元素
                }
            }
        }else{
    
      /* 多数之和 */
            //递归计算 (n-1)Sum 的结果
            //for循环遍历数组
            for(int i = start; i < nums.length; i++){
    
    
                //调用 (n-1)元组 的函数threeSumTarget → 计算目标值为target - nums[i]的(n-1)元组
                //递归调用 N - 1数之和的结果
                List<List<Integer>> temps = nSumTarget(nums, n - 1,i + 1,target - nums[i]);

                if(!temps.isEmpty()){
    
    
                    for(List<Integer> temp : temps){
    
    
                        temp.add(nums[i]);
                        res.add(temp);
                    }
                    //跳过左侧遍历过程中的重复元素
                    while(i < nums.length - 1 && nums[i] == nums[i+1]) i++;
                }
            }
        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/newson92/article/details/113811123
今日推荐