击败了90%的解法?Two Sum 从 O(n²) 到 O(n) 的优化之路

同样是 Two Sum,暴力解法耗时 56ms,而哈希解法仅需 2ms,差距在哪?

话不多说,先上图

下图为暴力解法

图1-1

下图为暴力解法的代码

js 复制代码
public class TwoSum {
    public int[] twoSum(int[] nums, int target) {
        // 外层循环:遍历每个元素作为第一个数
        for (int i = 0; i < nums.length; i++) {
            // 内层循环:遍历当前元素之后的所有元素(避免重复计算)
            for (int j = i + 1; j < nums.length; j++) {
                // 判断两数之和是否等于目标值
                if (nums[i] + nums[j] == target) {
                    // 找到则返回两个元素的下标
                    return new int[]{i, j};
                }
            }
        }
        // 题目假设一定有解,此处可根据实际需求返回null或抛出异常
        return null;
    }
}

下图为借助HashMap实现的

图1-3
下图为借助**HashMap**实现的代码

class 复制代码
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer>sumMap=new HashMap<>();
        //这里第一个Integer是nums[i]
        //第二个Integer是i(数组的索引值)
        for(int i=0;i< nums.length;i++){
            //这里的complement指的是目标值和当前遍历的值的差值
            int complement=target-nums[i];
            if(sumMap.containsKey(complement)){
                return new int[]{sumMap.get(complement),i};
            }
            //如果在sumMap中没有找到与nums[i]和为target的值
            // 那就把当前的值和对应的索引放在sumMap里,
            //为了给后面还没有遍历到的元素做匹配存储
            sumMap.put(nums[i],i);
        }
        return null;
    }
}

作为"小趴菜"的我第一眼看到这个题目,想到的就是暴力解法

这种方法容易想到而且好理解,但总觉得不对劲------如果数组很长,这代码岂不是要跑半天?比如说当数组长度 n=10⁵ 时,暴力解法需要计算 10¹⁰ 次,直接超时!而且效率极低!所以此方法绝不是最佳。其实这题藏着哈希表最核心的用法:用空间换时间,把"找元素"的效率从O(n)压到O(1)。

暴力解法 和利用HashMap解法的主要区别:

想找和为target的另一个数,其实就是找target - nums[i]。暴力法是一个个问(遍历),而哈希表像个"通讯录"------把见过的数和它的下标记下来,下次需要时直接查(不用再遍历)。比如 nums = [2,7,11,15],target=9:

  • 当i=0(nums[i]=2),需要找9-2=7,查通讯录(目前为空),就把2和下标0存进去;

  • 当i=1(nums[i]=7),需要找9-7=2,查通讯录------哎,2在里面!直接返回[0,1]。

  • 图1-4中 map.containsKey(complement) :"这一步就是'查通讯录',时间复杂度O(1)";

下表展示两种解法复杂度区别以及使用场景

方法 时间复杂度 空间复杂度 适用场景
暴力循环 O(n²) O(1) 数组极短的情况(n<1k)
哈希表 O(n) O(n) 大部分场景(n>1w)

除了这两种解法,是否还有其他解法。

答案是肯定的

它就是"双指针解法"

但是使用这种解法是有前提的。已排序数组 是双指针优化的关键前提!如果题目给定的数组是升序排列 (如 [2, 7, 11, 15]),可以直接用双指针将时间复杂度优化到 O(n) ,且空间复杂度为 O(1)(无需额外哈希表)。

上代码:

`public int[] twoSum(int[] nums, int target) {

sql 复制代码
int left = 0, right = nums.length - 1;
while (left < right) {
    int sum = nums[left] + nums[right];
    if (sum == target) {
        return new int[]{left, right}; // 返回索引
    } else if (sum < target) {
        left++; // 和太小,左指针右移
    } else {
        right--; // 和太大,右指针左移
    }
}
return null; // 无解

}`

  • 时间复杂度 :O(n)
    • 双指针最多遍历整个数组一次(如最坏情况下 leftright 相遇)。
  • 空间复杂度 :O(1)
    • 仅用两个指针变量,无额外空间。

以下是三种解法复杂度表

解法 时间复杂度 空间复杂度 适用场景
暴力双重循环 O(n²) O(1) 无序数组,数据量小(n<1k)
哈希表 O(n) O(n) 无序数组,数据量大 (n>1w)
双指针 O(n) O(1) 已排序数组

一定要注意!!!使用双指针的前提是已排序的数组

最后关于三数之和的复杂度,想和掘友们聊聊:暴力是O(n³),排序+双指针是O(n²) ,理论上哈希表也能做(存两数和),但实际为啥很少用哈希法?是空间换时间不划算,还是去重逻辑太复杂?大家在项目里遇到类似三数问题,优先选哪种思路?

有任何疑问,或者有其他优化思路,欢迎在评论区讨论~

欢迎大佬们不吝赐教~

感谢观看!

相关推荐
success1 小时前
【爆刷力扣-链表】画图自见
算法
今晚一定早睡2 小时前
代码随想录-数组-移除元素
前端·javascript·算法
前端拿破轮2 小时前
面试官:二叉树的前中后序遍历,用递归和迭代分别实现🤓🤓🤓
数据结构·算法·leetcode
Gyoku Mint2 小时前
深度学习×第10卷:她用一块小滤镜,在图像中找到你
人工智能·python·深度学习·神经网络·opencv·算法·cnn
智者知已应修善业2 小时前
2021-07-21 VB窗体求范围质数(Excel复制工作簿)
经验分享·笔记·算法
C++chaofan3 小时前
45. 跳跃游戏 II
java·开发语言·数据结构·算法·leetcode·游戏·职场和发展
拾光拾趣录3 小时前
举一反三:合并 K 个有序链表的最小堆实现
前端·算法
thginWalker3 小时前
差分数组算法
算法
拾光拾趣录3 小时前
合并K个有序链表
前端·算法
mochensage3 小时前
枚举算法入门
数据结构·算法