【算法】k-sum 问题

本文探讨了k-sum问题,从2-sum到4-sum,分析了不同问题的解题思路和复杂度。针对2-sum问题,介绍了其简单解决方法,并提供了相关链接。3-sum问题的解决方案包括排序数组后使用双指针寻找满足条件的三元组,时间复杂度为O(n^2)。4-sum问题提出了两种解法,包括O(n^3)的迭代改进和利用HashMap实现的O(n^2)算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 2sum问题
    这道题在剑指 offer 上面有,具体的链接为https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b?tpId=13&tqId=11195&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
    不会太难,所以此处不再赘述

  2. 3-sum问题
    这道题在 Leetcode 上面有,链接为:https://leetcode.com/problems/3sum/description/
    题目描述:
    Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.

Note: The solution set must not contain duplicate triplets.

Example: Given array nums = [-1, 0, 1, 2, -1, -4],

A solution set is: [ [-1, 0, 1], [-1, -1, 2]]
解题思路:
1)先将数组排序
2)从最后一个数字开始,把他的指针设为i, j = 0, k = i -1;
然后若 j+k = -i,则表示找到了一个三元组
若 j + k < -i, 则 j 往右移动
若 j + k > -i, 则k往左移动
复杂度分析:由于 j 和 k 是相向运动的,所以第二层的循环会在n次之内结束,时间复杂度为O(n^2)
代码实现:

public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> a = new ArrayList<List<Integer>>();
        HashSet<String> hs = new HashSet<String>();

        // 排序
        Arrays.sort(nums);
        // 排除异常情况
        if ( nums == null || nums.length < 3 )
            return a;
        // 只要考虑所有非负数的情况就好了
        for ( int i = nums.length - 1; nums[i] >= 0 && i >= 2; i-- ) { 
            int j = 0;
            int k = i - 1;
            while ( j < k && nums[k] >= -nums[i] && nums[j] <= nums[i] ) { 
            // 此处可以提前跳出循环
                if ( nums[j] + nums[k] > -nums[i] ) {
                    k--;
                }
                else if ( nums[j] + nums[k] < -nums[i] ) {
                    j++;
                }
                else {
                    ArrayList<Integer> t = new ArrayList<Integer>();
                    t.add(nums[j]);
                    t.add(nums[k]);
                    t.add(nums[i]);
                    if ( !hs.contains(t.toString()) ) {
                     // 用集合来避免重复的三元组
                        a.add(t);
                        hs.add(t.toString());
                    }
                    j++;
                    k--;
                }

            }

            if ( nums[i-1] == nums[i] ) { // 避免多个一样的i
                i--;
            }
        }

        return a;
    }
  1. 4-sum问题
    链接:https://leetcode.com/problems/4sum/description/
    思路:
    第一种想法是改造3-sum问题的解法,把外层遍历改成两层 i1 和 i2 ,然后内层是遍历[0, i2)找到所有的解,这样的算法时间复杂度是O(n^3)
    第二种想法是O(n^2)的算法,借助了 HashMap 来实现
    1)先将数组排序
    2)枚举所有的(i,j)对的和sum( i < j),以 sum 为 key,(i,j) 为 value,放到 HashMap 中,此时 sum 的值会有重复的,所以 value 设计成 一个 ArrayList。这个步骤的时间复杂度是O(n^2)
    3) 枚举所有的 (v1, v2)对,看 target - v1 - v2 是否在 HashMap 里面,此时要过滤
    v1, v2 和 HashMap 里面的对重复的情况,这个步骤的时间复杂度是O(n^2)
    代码实现
public static List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        HashSet<String> hs = new HashSet<String>();
        HashMap<Integer, ArrayList<Integer>> hm 
            = new HashMap<Integer, ArrayList<Integer>>();

        // 排序
        Arrays.sort(nums);
        // 排除异常情况
        if ( nums == null || nums.length < 4 )
            return ans;

        // 建立 Hash 表
        for ( int i = 0; i < nums.length -1; i++ ) {
            for ( int j = i+1; j < nums.length; j++ ) {
                int sum = nums[i] + nums[j];
                if ( hm.containsKey(sum) ) {
                    hm.get(sum).add(i);
                    hm.get(sum).add(j);
                }
                else {
                    ArrayList<Integer> a = new ArrayList<>();
                    a.add(i);
                    a.add(j);
                    hm.put(sum, a);
                }
            }
        }

        for ( int i = 0; i < nums.length -1; i++ ) {
            for ( int j = i+1; j < nums.length; j++ ) {

                if ( hm.containsKey(target - nums[i] - nums[j]) ) {
                    ArrayList<Integer> a = hm.get(target - nums[i] - nums[j]);

                    for ( int k = 0; k < a.size(); k += 2 ) {

                        if ( a.get(k) == i || a.get(k+1) == i 
                            || a.get(k) == j || a.get(k+1) == j ) {
                            continue;
                        }
                        else {
                            ArrayList<Integer> t = new ArrayList<Integer>();
                            t.add(nums[i]);
                            t.add(nums[j]);
                            t.add(nums[a.get(k)]);
                            t.add(nums[a.get(k+1)]);
                            Collections.sort(t, new Comparator<Integer>() {
                                @Override
                                public int compare(Integer o1, Integer o2) {
                                    return ( o1 > o2 )?1: -1;
                                }
                            });
                            if ( !hs.contains(t.toString()) ) { // 用集合来避免重复
                                ans.add(t);
                                hs.add(t.toString());
                            }
                        }

                    }
                }

            }
        }

        return ans;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值