首页 > 其他分享 >力扣.1 两数之和 N 种解法 two-sum

力扣.1 两数之和 N 种解法 two-sum

时间:2024-11-13 23:43:25浏览次数:3  
标签:target nums int sum 力扣 new 两数 indexList

数组系列

力扣数据结构之数组-00-概览

力扣.53 最大子数组和 maximum-subarray

力扣.128 最长连续序列 longest-consecutive-sequence

力扣.1 两数之和 N 种解法 two-sum

力扣.167 两数之和 II two-sum-ii

力扣.170 两数之和 III two-sum-iii

力扣.653 两数之和 IV two-sum-IV

力扣.015 三数之和 three-sum

力扣.016 最接近的三数之和 three-sum-closest

力扣.259 较小的三数之和 three-sum-smaller

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 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 <= 10^4 -10^9 <= nums[i] <= 10^9 -10^9 <= target <= 10^9

只会存在一个有效答案

进阶:你可以想出一个时间复杂度小于 O(n^2) 的算法吗?

前言

这道题作为 leetcode 的第一道题,看起来非常简单。

不过今天回头看,解法还是很多的。

大概可以有下面几种:

  1. 暴力解法

  2. 数组排序+二分

  3. HashSet/HashMap 优化

v1-暴力解法

思路

直接两次循环,找到符合结果的数据返回。

这种最容易想到,一般工作中也是我们用到最多的。

实现

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] res = new int[2];

        final int n = nums.length;
        for(int i = 0; i < n; i++) {
            for(int j = i+1; j < n; j++) {
                if(nums[i] + nums[j] == target) {
                    res[0] = i;
                    res[1] = j;
                }
            }
        }

        return res;
    }
}

效果

49ms 33.92%

效果一般

小结

暴力算法虽然容易想到,不过如果遇到特别长的场景用例,会直接超时。

当然这一题明显看到了 leetcode 的怜悯,怕我们上来就放弃。

我们如何改进一下呢?

排序是这个场景另一种很有用的方式。

v2-排序+二分

思路

我们希望排序,然后通过二分法来提升性能。

代码

public int[] twoSum(int[] nums, int target) {
    int[] res = new int[2];
    Arrays.sort(nums);

    // 遍历+二分
    int n = nums.length;
    for(int i = 0; i < n; i++) {
        // 找另一部分
        int t = target - nums[i];
        // 找到了自己怎么办?
        int j = Arrays.binarySearch(nums, t);
        if(j > 0) {
            res[0] = i;
            res[1] = j;
            return res;
        }
    }

    return res;
}

效果

当然,你会发现这段代码无法通过测试。

因为排序导致顺序错乱了。

我们要如何保证顺序的同时,有进行排序呢?

顺序修正

整体思路解释:

  1. 我们用一个二维数组,记录原始值+原始的下标

  2. 排序后,target-nums[i]就是剩下要找的数,我们在数组中用二分法寻找。

这里为了限制 j > i,二分法我们直接自己实现了,顺便练习一下。

class Solution {

    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;

        // 存储值+下标 避免排序后找不到原始的索引
        List<int[]> indexList = new ArrayList<>();
        for(int i = 0; i < n; i++) {
            indexList.add(new int[]{nums[i], i});
        }
        Collections.sort(indexList, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });

        // 遍历+二分  这里直接手写二分比较简单,因为直接查询数字可能会重复
        for(int i = 0; i < n-1; i++) {
            int t = target - indexList.get(i)[0];
            //从当前 i 的后面开始寻找
            int j = binarySearch(indexList, t, i+1);
            if(j >= 0) {
                // 原始下标
                return new int[]{indexList.get(i)[1], j};
            }
        }

        //NOT FOUND
        return new int[]{-1, -1};
    }

    private int binarySearch(List<int[]> indexList,
                             final int target,
                             final int startIx) {
        int left = startIx;
        int right = indexList.size()-1;
        while (left <= right) {
            int mid = left + (right-left) / 2;
            int val = indexList.get(mid)[0];
            if(val == target) {
                // 原始下标
                return indexList.get(mid)[1];
            }

            // update
            if(val < target) {
                left = mid+1;
            } else {
                right = mid-1;
            }
        }

        return -1;
    }

}

效果

9ms 38.89%

只能说,比暴力要好不少。

不过依然后进步的空间。

v3-排序+双指针

在做完了第 T167 之后,收到了双指针的启发。

思路

我们定义两个指针

left=0
right=n-1
sum=num[left]+num[right-1]

因为数组有有序的,所以只有 3 种情况:

  1. sum == target 直接满足

  2. sum < target,left++

  3. sum > target, right--

实现

class Solution {


    public int[] twoSum(int[] nums, int target) {
        int n = nums.length;

        // 存储值+下标 避免排序后找不到原始的索引
        List<int[]> indexList = new ArrayList<>();
        for(int i = 0; i < n; i++) {
            indexList.add(new int[]{nums[i], i});
        }
        Collections.sort(indexList, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });

        // 双指针
        int left = 0;
        int right = n-1;
        while (left < right) {
            int sum = indexList.get(left)[0] + indexList.get(right)[0];
            if(sum == target) {
                return new int[]{indexList.get(left)[1], indexList.get(right)[1]};
            }
            if(sum < target) {
                left++;
            }
            if(sum > target) {
                right--;
            }
        }

        //NOT FOUND
        return new int[]{-1, -1};
    }

}

效果

8ms 39.15%

v4-HashMap

思路

在我们写完上面的写法之后,有没有一种感觉?

既然是要找另一部分的值,那么直接 Hash,复杂度 O(1) 不是更快?

是的,你真是个小机灵鬼。

哈希在这种等于的场景是最快的,不过上面的二分适用性更广一些,比如查询大于或者小于的时候。

当然,这是其他类型的题目。

我们先来看一下哈希的解法。

代码

public int[] twoSum(int[] nums, int target) {
    int n = nums.length;
    HashMap<Integer, Integer> hashMap = new HashMap<>();
    for(int i = 0; i < n; i++) {
        int other = target - nums[i];
        if(hashMap.containsKey(other)) {
            int j = hashMap.get(other);
            return new int[]{i, j};
        }
        // 存储
        hashMap.put(nums[i], i);
    }
    return new int[]{-1, -1};
}

效果

2ms 99.68%

只能说效果拔群,Hash 确实是这类方法中最快的。

小结

这类题目的思路基本都是类似的。

我们后续将看一下 n 数之和的系列,感兴趣的小伙伴点点赞,关注不迷路。

标签:target,nums,int,sum,力扣,new,两数,indexList
From: https://blog.csdn.net/ryo1060732496/article/details/143669776

相关文章

  • 力扣—543.二叉树的直径
    543.二叉树的直径给你一棵二叉树的根节点,返回该树的 直径 。二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。两节点之间路径的 长度 由它们之间边数表示。示例1:输入:root=[1,2,3,4,5]输出:3解释:3,取......
  • 力扣刷题——3261. 统计满足 K 约束的子字符串数量 II
    看了题目的两个初始用例,感觉能用前缀和和滑动窗口来解决,前缀和设定为从下标0到当前位置所有符合条件的答案数量,于是先写了一个:vector<longlong>countKConstraintSubstrings(strings,intk,vector<vector<int>>&queries){intn=s.size();vector<longlong>pre......
  • 力扣题目解析--有效的括号
    题目给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。代码展示 classSolution{public:boolisValid(string......
  • 力扣21 打卡15 长度为 K 的子数组的能量值 II
    思路:该算法使用滑动窗口和计数器来判断每个长度为(k)的子数组是否满足连续递增的条件。遍历数组时,使用cnt记录当前连续递增的元素数。如果当前元素和前一个元素不连续递增,则将cnt重置为1,否则增加cnt。当cnt大于等于(k)时,表示找到了一个满足条件的子数组,将......
  • 力扣 653. 两数之和 IV 二叉树/binary-tree two-sum IV
    数组系列力扣数据结构之数组-00-概览力扣.53最大子数组和maximum-subarray力扣.128最长连续序列longest-consecutive-sequence力扣.1两数之和N种解法two-sum力扣.167两数之和IItwo-sum-ii力扣.170两数之和IIItwo-sum-iii力扣.653两数之和IVtwo-sum-IV力......
  • Solution - Codeforces 1217E Sum Queries?
    对于这个“好的”的判定条件看起来有点奇怪,不妨结合上题目要求的“最小\(sum\)”一起考虑。因为要最小化\(s_p\),所以一个比较直观的想法是先从选的数个数入手。考虑到如果选的只有\(1\)个数\(a_i\),那么\(sum=a_i\),一定是好的,排除。如果选的是\(2\)个数\(a_i,a_j\),......
  • ResumeSDK简历解析库编程案例
    目录1、软件概述2、编程案例2.1、官网案例(阿里云)2.2、优化案例3、解析结果1、软件概述ResumeSDK简历解析是北京无奇科技有限公司研发,业界领先的智能简历解析和人岗匹配算法厂商,提供专业的AI招聘技术服务,致力于人力资源行业智能化这一进程。并已经上线阿里云或腾讯云,......
  • leetcode 29. 两数相除
    29.两数相除一、使用long类型classSolution{public:longdivide2(longdividend,longdivisor){if(dividend<0&&divisor<0)returndivide2(-dividend,-divisor);elseif(dividend<0&&divisor>0)return-div......
  • 力扣 170. 两数之和 III - 数据结构设计 two-sum III
    数组系列力扣数据结构之数组-00-概览力扣.53最大子数组和maximum-subarray力扣.128最长连续序列longest-consecutive-sequence力扣.1两数之和N种解法two-sum力扣.167两数之和IItwo-sum-ii力扣.170两数之和IIItwo-sum-iii力扣.653两数之和IVtwo-sum-IV力......
  • 1. 两数之和
    题目链接解题思路:方法一:两个for循环,时间复杂度:O(n^2)方法二:先排序,然后双指针,时间复杂度:O(n*logn)方法三:使用一个set,从左往右遍历,每次遍历到一个数num,先查找set,是否存在target-num的数,如果存在,直接返回了。时间复杂度:O(n)。因为题目需要下标,所以要用map,value就是下......