【问题标题】:How to solve this using hashmaps?如何使用哈希图解决这个问题?
【发布时间】:2015-10-18 17:32:10
【问题描述】:

假设给你一个包含以下值的列表:

[1,4,5,7,8,9]

你得到k=4 其中k 是两个数组元素之间的差异。你怎么知道k 出现了多少次?比如在这个列表中k出现了3次[(5-1),(8-4),(9-5)]

我可以使用两个 for 循环来解决这个问题,但这需要 O(n^2) 时间。我听说这可以使用 hashmaps 解决,但不确定如何实现它?

任何想法将不胜感激!

【问题讨论】:

  • 你确定应该是HashMap而不是HashSet吗?
  • 请注意,Óscar López 的解决方案(目前已接受的答案)无法处理 [4], k = 0[4, 4, 4], k = 0 的琐碎案例
  • ^ 收回;现已修复

标签: java arrays algorithm data-structures hashmap


【解决方案1】:

这个想法是将 k 和每个值之间的所有可能差异存储在输入数组 (numbers) 中。 然后计算输入数组中适合差异的值的数量。

这将起作用:

public class Solution {
    public int twoSum(int[] numbers, int k) {
        if (numbers == null) {
            return null;
        }
        int count = 0;
        HashMap<Integer, Integer> difference = new HashMap<Integer, Integer>();
        for (int i = 0; i < numbers.length; i++) {
            difference.put(k - numbers[i], i);
        }
        for (int i = 0; i < numbers.length; i++) {
            int cur = -numbers[i];
            if (difference.containsKey(cur) && difference.get(cur) != i) {
                count++;
            }
        }
        return count;
    }
}

关键是要有difference.get(cur) != i 条件(其中i 是索引cur),以避免出现k = 0 的情况,并且每个值都会与其自身形成一对。

【讨论】:

  • 这不是矫枉过正。它可以正确处理具有重复数字的数组和具有相同 O(n) 运行时的不同数字的数组。这很重要,因为它没有在问题中明确指定。即它正确处理 [4, 4, 4], k = 0 的情况。
  • 此外,SetHash 解决方案因琐碎情况而中断:[4],k = 0
【解决方案2】:

使用HashSet(内部使用HashMap)我们可以假设它的contains方法接近O(1)所以你可以

  1. 用所有元素填充这样的集合
  2. 遍历元素并计算 +4-4 差异的值
  3. 检查这些值是否存在于集合中。
  4. 将结果除以 2,因为您将得到 1,55,1 对,这实际上是一对。

作为shown by Óscar López,您可以通过仅计算+4-4 之一来改进它并跳过最后一步

【讨论】:

    【解决方案3】:

    事实上,它可以只使用Set 来解决:我们必须找出集合中是否包含另一个元素,其与当前结果的差异导致k。尝试以下解决方案,它假设输入中至少有两个元素没有重复,并且不会像我们为这个问题使用HashMap 那样为不需要的值浪费空间:

    int k = 4;
    int howMany = 0;
    Set<Integer> set = new HashSet<>(Arrays.asList(1, 4, 5, 7, 8, 9));
    
    System.out.printf("k = %d%n", k);   
    
    for (Integer n : set) {
        if (set.contains(n - k)) {
            howMany++;
            System.out.printf("(%d - %d) = k%n", n, n - k);
        }
    }
    
    System.out.printf("k appears %d times%n", howMany);
    

    以上结果如下:

    k = 4
    (5 - 1) = k
    (8 - 4) = k
    (9 - 5) = k
    k appears 3 times
    

    【讨论】:

    • contains() 方法正在使用迭代。所以实际上这是O(n^2)
    • @Ramesh-X 不,这就是我们使用集合的原因!它保证了O(1) 操作的contains() 复杂性。
    • @Ramesh-X 但它只迭代一个桶中的元素,该桶包含具有相同哈希码的元素(桶中这样的元素并不多)。这就是它如此之快的原因。
    • @Pshemo 啊..我忘了它在运行equals方法之前使用的是哈希码..
    • 原始问题未指定不同的数组。此解决方案因 case: [4, 4, 4], k = 0 而中断
    【解决方案4】:

    即使数组包含重复项,以下方法也有效。它实现了 Pshemo 的最终算法(没有重复工作,也没有除以 2)。

    首先,它将原始数组的内容存储在哈希图中。原始值是key,原始值在数组中出现的次数就是hashmap的值。此阶段的运行时间为 O(原始列表中的项目数),并且使用存储 O(原始列表中的不同项目数)。

    其次,它是loops through the hashmap,它会找到任何大于 (delta = 4) 的项目。它做了一些数学运算来增加结果计数。它有一个特例来处理(delta == 0)场景,来处理凌忠提到的问题。此阶段的运行时间为 O(原始列表中不同项目的数量)。

    public class Solution {
        public int countDifferences(int delta, int[] array) {
            if (array == null) {
                return 0;
            }
            // Load the contents of the array into a hashmap.
            // This process loses the sorting.
            HashMap<Integer, Integer> choices = new HashMap<>();
            for (int arrayItem : array) {
                if (choices.containsKey(arrayItem)) {
                    choices.put(arrayItem, 1 + choices.get(arrayItem));
                } else {
                    choices.put(arrayItem, 1);
                }
            }
    
            // Count the result.
            int result = 0;
            for(Map.Entry<Integer, Integer> e : choices.entrySet()) {
                Integer key   = e.getKey();
                Integer value = e.getValue();
                if (delta == 0) {
                    result += value * (value - 1) / 2; // add summorial(value - 1)
                } else {
                    if (choices.containsKey(key + delta)) {
                        result += value * choices.get(key + delta);
                    }
                }
            }
            return result;
        }
    }
    

    【讨论】:

      【解决方案5】:

      您可以在单次迭代中实现这一目标。 应用这样的逻辑,当开始迭代时假设你有一个带有 k,v 对的哈希图 前任。 1、虚拟值 4、虚拟值 5、虚拟值

      假设你的 k=4 因此,当开始遍历 map 时,你会说第一个 1,所以这样做就像 1+k 即 1+4 即 5 所以回顾你的数据结构或 map for key=5 map.get(5) 如果 map 返回值更新另一个地图或另一个数据结构保存你的 count ok k 假设我们有一个 k=4 的地图,所以如果我们得到这里是 4 的键,它的值将类似于这个 count = count +1。

      【讨论】:

        猜你喜欢
        • 2021-08-30
        • 2021-08-13
        • 2021-05-26
        • 2019-08-13
        • 1970-01-01
        • 2020-06-13
        • 1970-01-01
        • 2019-08-18
        • 2020-04-06
        相关资源
        最近更新 更多