LeetCod-KSum问题(巧妙的数组遍历问题、HashMap使用、递归)

2Sum

题意:
        给你一组个数组,array[] = {7,11,13,15,1,2,13,4,4}, target = 15,在array中找出两个数之和等于target。输出所有二元组(不能重复,[2, 13]和[13, 2]只能算一组),比如,本题的答案:[2,13],[1,14]。
解体思路:
        首先把数组进行排序,然后用behind指针指向排序后数组的头部,用ahead指针指向排序后的数组尾部。如果array[behind] + array[ahead] > target,说明两个数大了,则需要将ahead向前移动一位,那么两数之和就会减小向target靠近。如果array[behind] + array[ahead] < target,说明两个数小了,则需要把behind往后移动一位,那么两数之和就会减小向target靠近。

import java.util.*;
public class _2Sum {
    public static List<List<Integer>> _2Sum(int[] array, int target, List<List<Integer>> res) {
        boolean flag = false;
        int ahead = array.length - 1;
        int behind = 0;
        while(ahead > behind) {
            if(array[ahead] + array[behind] == target) {
                flag = true;
                res.add(Arrays.asList(array[ahead], array[behind]));
                //由于题目要求,不能有重复的元组,那么当ahead和behind指向的数字之和等于target时,需要把ahead之前并且等于array[ahead]的数跳过。
                while(ahead > behind) {
                    if(array[ahead] == array[ahead - 1]) {
                        ahead--;
                    }else break;
                }
                 //由于题目要求,不能有重复的元组,那么当ahead和behind指向的数字之和等于target时,需要把behind之后并且等于array[hehind]的数跳过。
                while(ahead > behind) {
                    if(array[behind] == array[behind + 1]){
                        behind++;
                    }else break;
                }
                //去掉重复元素之后,由于array[ahead-1] < array[ahead],array[behind] < array[behind + 1],那么需要同时ahead--和behind++,才可能找到下一个元组。
                ahead--;
                behind++;
            }else if(array[ahead] + array[behind] > target){
            //明两个数大了,则需要将ahead向前移动一位,那么两数之和就会减小向target靠近。
                ahead--;
            }else {
            //说明两个数小了,则需要把behind往后移动一位,那么两数之和就会减小向target靠近。
                behind++;
            }
        }
        return res;
    }
    public static void main(String[] args) {
        int[] array = new int[]{7,11,13,15,1,2,13,4,4};
        List<List<Integer>> res = new LinkedList<>();
        //给数组排序
        Arrays.sort(array);
        int target = 15;
        _2Sum(array, target, res);
        System.out.println(res);
    }
}

3Sum(LeetCode - 15)

题意:
        给你一组个数组,array[] = [-1, 0, 1, 2, -1, -4], target = 0,在array中找出3个数之和等于target。输出所有三元组(不能重复,[1, 0, -1]和[-1, 0, 1]算作一个),比如,本题的答案:[2,13],[1, 14]。
题目链接:https://leetcode.com/problems/3sum/description/
解体思路
        首先给数组排序,遍历数组中的每个可能作为三元组中首元素的nums[I],令sum = target - nums[I]。然后在nums[I+1,…,len-1]中寻找nums[ahead] + nums[behind] = sum的二元组。

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

public class _3Sum {
    public static List<List<Integer>> _3Sum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> res = new LinkedList<>();
        int len = nums.length;
        for(int i = 0; i < len - 2; i++) {
            if(i == 0 || (i > 0 && nums[i] != nums[i-1])) { //寻找可能作为三元组首元素的nums[i]。
            //后面的元素与前面相同则跳过,因为,nums[i-1] 已经和nums[i...len-1]组合过了(其中还包括nums[i-1]与nums[i]的组合),所以直接跳过array[i]对匹配结果无影响,因为再次使用nums[i]去寻找结果,结果只能是无解(不能使用和其值相等的nums[i-1]了,可能找不到结果)或者找到重复结果。
                // 转化为2Sum问题,即在behind和ahead之间寻找等于Sum的问题了。
                int behind = i + 1, ahead = len - 1, sum = target - nums[i];
                while(behind < ahead) {// 2Sum(参考上面)
                    if(nums[behind] + nums[ahead] == sum) {
                        res.add(Arrays.asList(nums[i], nums[behind], nums[ahead]));

                        while(ahead > behind && nums[ahead] == nums[ahead - 1]) {
                             ahead--;
                        }
                        while(ahead > behind && nums[behind] == nums[behind + 1]) {
                            behind++;
                        }
                        ahead--;
                        behind++;
                    }else if(nums[behind] + nums[ahead] > sum) {
                        ahead--;
                    }else {
                        behind++;
                    }
                }
            }
        }
        return res;
    }
    public static void main(String[] args) {
        int[] array = new int[]{-1, 0, 1, 2, -1, -4};
        List<List<Integer>> res = new LinkedList<>();
        int target = 0;
        res = _3Sum(array, target);
//        res = threeSum(array);
        System.out.println(res);
    }}

TwoSum(LeetCode - 1)借助HashMap实现

题目链接:https://leetcode.com/problems/two-sum/description/
题意:
        给你一个数组(元素各不相同)和一个target数,找出数组中两个数之和等于target的数,返回他们的下标。保证只有一个组数据满足答案要求。

解体思路:

import java.util.Arrays;
import java.util.HashMap;

public class LeetCode_1_TwoSum {
    public static int[] TwoSum(int[] nums, int target) {
        int[] index = new int[] {-1,-1};
        HashMap<Integer,Integer> hashMap = new HashMap<>(); //因为数据值不会相同,所以可以使用HashMap,如果有重复的数据,使用HashMap就会有数据丢失。
        int len = nums.length;
        //首先把nums[i]和对应的下标存储在HashMap中,为后面寻找ans是否存在做铺垫。
        for(int i = 0; i < len; i++) {
            hashMap.put(nums[i], i);
        }
        for(int i = 0; i < len; i++) {
            int ans = target - nums[i];
            //利用HashMap的containsKey()方法,可以找出答案,ans + nums[i] == target,遍历所有的nums[i],如果hashMap中有ans,那么就找到答案了,O(n)的时间复杂度就可以解决。
            if(hashMap.containsKey(ans) && hashMap.get(ans) != i) {
                index[0] = i;
                index[1] = hashMap.get(ans);
            }
        }
        return index;
    }
    public static void main(String[] args) {
        int[] nums = new int[]{2, 7, 11, 15};
        int target = 9;
        int[] ans = new int[2];
        ans = TwoSum(nums, target);
        System.out.println(Arrays.toString(ans));
    }
}

KSum:(递归)

解体思路:
递归出口:2sum Problem
递归:Reduce K sum problem to K – 1 sum Problem

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
public class KSum {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        return kSum(nums, 0, 4, target);
    }
    private List<List<Integer>> kSum (int[] nums, int start, int k, int target) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(k == 2) { //twoSum: two pointers from left and right
            int left = start, right = len - 1;
            while(left < right) {
                int sum = nums[left] + nums[right];
                if(sum == target) {
                    List<Integer> path = new ArrayList<Integer>();
                    path.add(nums[left]);
                    path.add(nums[right]);
                    res.add(path);
                    while(left < right && nums[left] == nums[left + 1]) left++;
                    while(left < right && nums[right] == nums[right - 1]) right--;
                    left++;
                    right--;
                } else if (sum < target) { //move left
                    left++;
                } else { //move right
                    right--;
                }
            }
        } else {
            for(int i = start; i < len - (k - 1); i++) {
                if(i > start && nums[i] == nums[i - 1]) continue;
                List<List<Integer>> temp = kSum(nums, i + 1, k - 1, target - nums[i]);
                for(List<Integer> t : temp) {
                    t.add(0, nums[i]);
                }
                res.addAll(temp);
            }
        }
        return res;
    }
    public static void main(String[] args) {
        int[] nums = new int[]{1, 0, -1, 0, -2, 2};
        int target = 0;
        List<List<Integer>> res = new LinkedList<>();
        KSum kSum = new KSum();
        res = kSum.fourSum(nums, target);
        System.out.println(res);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值