关于两个总和问题的两种不同实现的表现

时间:2017-11-22 21:29:17

标签: java hashmap

我有两种不同的经典二和问题解决方案,一种是使用hashmap遍历列表一次,另一种是使用两个索引和一个排序数组来找到解决方案。使用hashmap的时间复杂度是O(n),而另一种方法使用O(nlog(n)),但运行时报告显示使用排序数组比使用map更快。为什么呢?

方法1:使用hashmap

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

方法2:使用两个索引和一个排序数组

class Solution {
    public int[] twoSum(int[] nums, int target) {
        int[] sorted_nums = new int[nums.length];
        for(int i = 0; i < nums.length; i++) {
            sorted_nums[i] = nums[i];
        }
        Arrays.sort(sorted_nums);

        int begin = 0;
        int end = sorted_nums.length - 1;
        while(begin < end) {
            if(sorted_nums[begin] + sorted_nums[end] == target)
                break;
            else if(sorted_nums[begin] + sorted_nums[end] > target)
                end--;
            else
                begin++;
        }

        int[] result = new int[2];
        int idx = 0;
        for(int i = 0; i < nums.length; i++) {
            if(nums[i] == sorted_nums[begin] || nums[i] == sorted_nums[end])
                result[idx++] = i;
        }
        return result;
    }
}

enter image description here

1 个答案:

答案 0 :(得分:1)

这取决于输入数组大小 - 开销ob装箱/拆箱和额外的内存开销可能太大而无法“擦除渐近的运行时复杂性”

我想测试这里发生了什么,当你在较小的输入上运行很多时,似乎慢速部分是GC。然后GC开销大于更好的渐近复杂度的好处。

我要测试的代码

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class TwoSum {

    //run with -Xmx1g
    public static void main(String... args) {

        for (int sampleSize = 1; sampleSize < 1000; sampleSize*=5) {
            for (int size = 10; size <= 40_000_000; size *= 10) {
                System.gc();
                int[] numbers = new int[size];

                int a = 0;
                int aa = 1;
                for (int i = 0; i < size; i++) {
                    numbers[i] = a;
                    a += aa++;
                }

                Random r = new Random(42);
                int[] t = new int[sampleSize];
                for (int i = 0; i < sampleSize; i++) {
                    t[i++] = numbers[r.nextInt(size)] + numbers[r.nextInt(size)];
                }
                long b = System.currentTimeMillis();
                for (int i = 0; i < sampleSize; i++) {
                    twoSumMap(numbers, t[i]);
                }
                long c = System.currentTimeMillis();
                for (int i = 0; i < sampleSize; i++) {
                    twoSumArray(numbers, t[i]);
                }
                long d = System.currentTimeMillis();

                System.out.println(
                    "On size: " + size + " MAP takes: " + (c - b) + " ARRAY takes: " + (d - c) + " with sample size: " + sampleSize);
            }
        }
    }

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

    public static int[] twoSumArray(int[] nums, int target) {
        int[] sorted_nums = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            sorted_nums[i] = nums[i];
        }
        Arrays.sort(sorted_nums);

        int begin = 0;
        int end = sorted_nums.length - 1;
        while (begin < end) {
            if (sorted_nums[begin] + sorted_nums[end] == target)
                break;
            else if (sorted_nums[begin] + sorted_nums[end] > target)
                end--;
            else
                begin++;
        }

        int[] result = new int[2];
        int idx = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == sorted_nums[begin] || nums[i] == sorted_nums[end])
                result[idx++] = i;
        }
        return result;
    }
}

输出:

On size: 10 MAP takes: 0 ARRAY takes: 1 with sample size: 1
On size: 100 MAP takes: 0 ARRAY takes: 0 with sample size: 1
On size: 1000 MAP takes: 1 ARRAY takes: 0 with sample size: 1
On size: 10000 MAP takes: 2 ARRAY takes: 1 with sample size: 1
On size: 100000 MAP takes: 24 ARRAY takes: 4 with sample size: 1
On size: 1000000 MAP takes: 75 ARRAY takes: 90 with sample size: 1
On size: 10000000 MAP takes: 15 ARRAY takes: 862 with sample size: 1
On size: 10 MAP takes: 0 ARRAY takes: 0 with sample size: 5
On size: 100 MAP takes: 0 ARRAY takes: 0 with sample size: 5
On size: 1000 MAP takes: 0 ARRAY takes: 0 with sample size: 5
On size: 10000 MAP takes: 3 ARRAY takes: 0 with sample size: 5
On size: 100000 MAP takes: 107 ARRAY takes: 10 with sample size: 5
On size: 1000000 MAP takes: 144 ARRAY takes: 309 with sample size: 5
On size: 10000000 MAP takes: 45 ARRAY takes: 4167 with sample size: 5
On size: 10 MAP takes: 0 ARRAY takes: 0 with sample size: 25
On size: 100 MAP takes: 0 ARRAY takes: 0 with sample size: 25
On size: 1000 MAP takes: 1 ARRAY takes: 0 with sample size: 25
On size: 10000 MAP takes: 19 ARRAY takes: 2 with sample size: 25
**On size: 100000 MAP takes: 643 ARRAY takes: 36 with sample size: 25**
On size: 1000000 MAP takes: 902 ARRAY takes: 1516 with sample size: 25
On size: 10000000 MAP takes: 434 ARRAY takes: 20626 with sample size: 25
On size: 10 MAP takes: 0 ARRAY takes: 0 with sample size: 125
On size: 100 MAP takes: 1 ARRAY takes: 0 with sample size: 125
On size: 1000 MAP takes: 7 ARRAY takes: 1 with sample size: 125
On size: 10000 MAP takes: 104 ARRAY takes: 9 with sample size: 125
**On size: 100000 MAP takes: 2499 ARRAY takes: 148 with sample size: 125**
On size: 1000000 MAP takes: 3282 ARRAY takes: 7485 with sample size: 125
On size: 10000000 MAP takes: 1966 ARRAY takes: 102934 with sample size: 125
On size: 10 MAP takes: 0 ARRAY takes: 0 with sample size: 625
On size: 100 MAP takes: 3 ARRAY takes: 1 with sample size: 625
On size: 1000 MAP takes: 34 ARRAY takes: 4 with sample size: 625
**On size: 10000 MAP takes: 485 ARRAY takes: 42 with sample size: 625**
**On size: 100000 MAP takes: 12807 ARRAY takes: 734 with sample size: 625**
On size: 1000000 MAP takes: 16113 ARRAY takes: 37191 with sample size: 625