算法细节系列(29):any sum

算法细节系列(29):any sum

详细代码可以fork下Github上leetcode项目,不定期更新。

题目摘自leetcode:

Leetcode 001. Two Sum

头号种子啊,刷leetcode第一题就是它,带我入门带我飞。说说当初的思路吧,好幼稚。

顺序遍历所有pair对,把符合的target的sum挑出来。代码如下:

public int[] twoSum(int[] nums, int target) {
        int[] array = new int[2];
        for(int i =0;i<nums.length;i++){
            array[0]=i;
            for (int j =i+1;j<nums.length;j++){
                array[1]=j;
                if(nums[i]+nums[j]==target){
                    return array;
                }
            }
        }
        return array;
    }

TWO SUM的题目可以在 O(n) 的时间复杂度内解决战斗。思路:

find:
nums[i] + nums[j] = target

也就是说find:
nums[i] = target - nums[j]

查找一个元素最快的方法可以用map来解决。

代码如下:

public int[] twoSum(int[] nums, int target) {
        int[] result = new int[2];
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int i = 0; i < nums.length; i++) {
            if (map.containsKey(target - nums[i])) {
                result[1] = i;
                result[0] = map.get(target - nums[i]);
                return result;
            }
            map.put(nums[i], i);
        }
        return result;
    }

貌似这种假想一个可能存在可能不存在的元素很常见(target - nums[j])。

Leetcode 015. 3Sum

刚开始的想法也是用一个MAP去存放所有的nums[i] + nums[j]的组合,接着遍历一遍数组,去找和为key = 0 - nums[k]的组合,时间复杂度能控制在 O(n2) ,但很可惜这种方法没有解决duplicate问题。

思路:

  • 对数组进行排序,很关键,为了在搜索时,能把 O(n2) 复杂度降低到 O(n)
  • 把3sum问题归简为查找2sum问题。

先看代码:

public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++){
            int target = -nums[i];
            int lf = i + 1;
            int rt = nums.length - 1;
            while (lf < rt){
                int sum = nums[lf] + nums[rt];
                if (sum < target) lf++;
                if (sum > target) rt--;
                if (sum == target){
                    Integer[] triple = new Integer[3];
                    triple[0] = nums[i];
                    triple[1] = nums[lf];
                    triple[2] = nums[rt];
                    ans.add(Arrays.asList(triple));
                    while (lf < rt && nums[lf] == triple[1]) lf++;
                    while (lf < rt && nums[rt] == triple[2]) rt--;
                }
            }

            while (i + 1 < nums.length && nums[i+1] == nums[i]) i++;
        }
        return ans;
    }

起初困扰我的两点:

  • 为何for循环遍历过的元素,在找TWO SUM的解时,不在考虑了?(lf = i + 1,只考虑i后面的元素)
  • 这种while循环结构怎么就能确保找到了所有TWO SUM的解。

第一个问题,可以想象,假设遍历过的k元素,是在遍历i元素时(i > k),TWO SUM的一个解,由于代码查找的结构,一定在遍历k时,找到了TWO SUM中的某个i元素,而不会等到遍历i元素才找到k元素,解决了我之前重复的大问题。

第二个问题,它的查找复杂度为 O(n) ,原因在于数组有序,所以当:

sum = nums[lf] + nums[rt];
如果 sum < target
表明:nums[lf] + nums[rt'] < nums[lf] + nums[rt] < target
其中 rt' 在 (lf + 1, rt -1)范围内,这些组合都不必再考虑
同理:
如果 sum > target
target < nums[lf'] + nums[rt],lf后续的下标也不必考虑

所以每次我们只要比较边界即可
if sum < target lf++;
if sum > target rt--;

Leetcode 018. 4Sum

思路和3SUM一样,没什么特别的地方。代码如下:

public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; ++i){
            for (int j = i + 1; j < nums.length; ++j){
                int key = target - nums[i] - nums[j];
                int lf = j + 1;
                int rt = nums.length - 1;
                while (lf < rt){
                    int sum = nums[lf] + nums[rt];
                    if (sum < key) lf++;
                    if (sum > key) rt--;
                    if (sum == key){
                        ans.add(Arrays.asList(nums[i],nums[j],nums[lf],nums[rt]));
                        while (lf < rt && nums[lf + 1] == nums[lf]) lf++;
                        while (lf < rt && nums[rt - 1] == nums[rt]) rt--;
                        lf++;
                        rt--;
                    }
                }
                while (j + 1 < nums.length && nums[j+1] == nums[j]) j++;
            }
            while (i + 1 < nums.length && nums[i+1] == nums[i]) i++;
        }

        return ans;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值