美文网首页
两数求和相等的所有可能

两数求和相等的所有可能

作者: ettingshausen | 来源:发表于2017-11-23 16:30 被阅读18次
2015年5月 海南大学

偶然发现一个算法题,不复杂,挺有意思的。

问题:给一个数组,里面的数字不重复,求任意两数相加的和为一个固定值的所有可能的下标组合。
使得时间复杂度最小
例:{3, 5, 1, 2, 4, 6, 7}问求和为7的所有可能性
输出结果:[1,3],[0,4],[2,5]

第一次尝试

不看答案,先尝试下自己的方案。
思路: 遍历2遍数组,首先将循环的元素与目标值做差,并将结果保存下来,接着第二次循环从后边的元素中找


int[] a = {3, 5, 1, 2, 4, 6, 7};
int target = 7;

List<Integer[]> result = new ArrayList<>();
List<Integer> tails = new ArrayList<>();
for (int i = 0; i < a.length; ++i) {
    if (tails.contains(a[i])) {
         continue;
    }
    int res = target - a[i];
    for (int j = i + 1; j < a.length; ++j) {
        if (a[j] == res) {
            Integer[] pair = new Integer[2];
            pair[0] = i;
            pair[1] = j;
            tails.add(a[j]);
            result.add(pair);
            break;
        }
    }
}

问题解决,只是几个数字根本看不出来耗时的差异。生成一个大的整数数组比较一下。

Set<Integer> source = new HashSet<>();
int size = 50000;
for (int i = 0; i < size; i++) {
    Random random = new Random();
    source.add(random.nextInt(size) + 1);
}
int[] array = source.stream()
                    .mapToInt(i -> i)
                    .toArray();

通过 System.currentTimeMillis() 查看耗时,消耗了885毫秒,使用答案的方法耗时33毫秒。差异巨大!
过一眼答案的做法,只用了一次循环,并且用到了 Map。 分析下时间复杂度,如果一次循环,那么时间复杂度为 $$O(N)$$, 如果循环嵌套复杂度为 $$O(N^2)$$ 与实际耗时基本上一致。

第二次尝试

根据刚才答案的提示,用一次循环搞定。


List<Integer[]> getSum(int[] a, int target) {

    long begin = System.currentTimeMillis();
    List<Integer[]> result = new ArrayList<>();
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < a.length; i++) {
        if (map.get(a[i]) != null) {
            Integer[] pair = new Integer[2];
            pair[0] = map.get(a[i]);
            pair[1] = i;
            result.add(pair);
            continue;
        }
        int sub = target - a[i];
        map.put(sub, i);
    }
    long end = System.currentTimeMillis();
    System.out.println(end - begin);

    return result;
}

效果非常惊艳,仅用了14毫秒,竟然比答案都要快。

对比分析

参考答案的代码如下:

List<Integer[]> twoSum(int[] a, int target) {
    long begin = System.currentTimeMillis();
    // key为当前下标对应的值和target的差值 value为当前下标
    Map<Integer, Integer> map = new HashMap<>();    
    List<Integer[]> result = new ArrayList<>(a.length);
    for (int i = 0; i < a.length; ++i) {
        int current = a[i]; // 当前下标对应的值
        Integer val = map.get(current);
        // 不存在差值则放入map
        if (val == null) {
            map.put(target - a[i], i);
        } else {
            // 存在差值,取出差值对应的下标和当前下标放入集合
            Integer[] each = new Integer[2];
            each[0] = map.get(current);
            each[1] = i;
            result.add(each);
        }
    }
    long end = System.currentTimeMillis();
    System.out.println(end - begin);
    return result;
}

通过代码来看,两个方法差不多,为什么会差一半的时间呢?经过反复的修改验证,发现 getSum 方法在 twoSum 方法之后调用,时间缩短了。交换顺序后,twoSum 消耗的时间也缩短了一半。看来,对同一个数组进行操作,系统自动做了优化。

总结

减少时间复杂度,通常的做法就是通过牺牲点空间复杂度来换取。这算是个典型的例子,通过充分利用哈希表良好的时间复杂度,提升程序的性能。

参考文献

[1] 算法练习02-两数求和相等的所有可能
[2] 浅谈时间复杂度- 算法衡量标准Big O

相关文章

  • 两数求和相等的所有可能

    偶然发现一个算法题,不复杂,挺有意思的。 问题:给一个数组,里面的数字不重复,求任意两数相加的和为一个固定值的所有...

  • 两数求和

    总结: 1、<<(左移运算符)的优先级大于 &(与运算)的优先级2、此题通过if(b==0)来控制迭代的终止3、如...

  • 2018-01-22 学习

    //一个数平方之后,逆序与原来相等 #include int reverse(int x){//倒序求和 int...

  • 两数求和算法

    题目 假设有一个整数数组 nums ,写一个方法 twoSum() ,返回数组中两个元素之和等于入参的下标数组。 ...

  • 算法:两数求和

    两数之和 题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 tar...

  • aabb,3n+1问题,阶乘之和

    例题2-1:aabb 输出所有形如aabb的四位完全平方数(即前两位数字相等,后两位数字也相等)。【分析】我们枚举...

  • AI编程范式 第3章 Lisp概览(下)

    3.4 说说相等和内部表示 在Lisp中主要有5种相等断言,因为不是所有的对象被创建的时候都是相等意义上的相等。数...

  • python笔记day6

    补充: == 和 is == --- 判断两个数据的值是否相等 is --- 判断地址是否相等 python数...

  • 微软面试leetcode top 500题

    按出题热度排序: 题号题目通过率难度#972相等的有理数40.90%困难#631设计Excel求和公式26.30%...

  • 2、两链表数求和

    1、问题 给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一...

网友评论

      本文标题:两数求和相等的所有可能

      本文链接:https://www.haomeiwen.com/subject/ocgzvxtx.html