【问题标题】:Common elements among multiple lists多个列表之间的共同元素
【发布时间】:2015-12-12 08:31:58
【问题描述】:

我需要在多个预先排序的列表中找到所有常见元素(如果所有列表中都存在重复项,则它们必须列出多次)。列表的数量将由用户确定。我正在尝试找到一种具有 O(n) 效率的算法。

我一直在玩弄下面的代码,但如果我还不知道有多少个列表,我不知道如何使它工作:

Integer[] a = {2, 2, 4, 6, 7, 11};
Integer[] b = {2, 2, 3, 5, 7, 14}
int i = 0;
int j = 0;
while(i < a.length && j < b.length) {
   if(a[i] == b[j]) {
      System.out.print(a[i] + " ");
      i++;
      j++;
   } else if(a[i] > b[j]) {
        j++;
   } else {
        i++;
   }
}

期望的输出:2 2 7

【问题讨论】:

  • 使用Sets 代替这种方法,效率为O(N)。
  • @LuiggiMendoza 这不包括重复的情况。除非你有一些图书馆的Multiset
  • @immibis 好吧,使用Map&lt;Integer, Integer&gt;,其中键是元素,值是该键有多少元素的计数器。 Set 只是 Map&lt;WhateverKeyYouWantOrNeed, Boolean&gt;
  • @LuiggiMendoza 是否使用 Set 处理未知数量的列表,直到运行时?
  • 在运行前未知数量的列表是什么意思?

标签: java arrays sorting search collections


【解决方案1】:

以下是List而不是数组,因为处理动态数量的列表稍微复杂一些,但它支持所有ListCollectionIterable类型。

它适用于任何类型的Comparable 值列表,例如Integer, Long, Double, String, Date, ...

可以根据需要修改代码以处理 Integer[]int[] 或任何其他数字基元类型,但您必须为每个数组类型克隆代码。

没有进行验证。列表必须预先排序,不允许为空。

@SafeVarargs
@SuppressWarnings("unchecked")
private static <E extends Comparable<E>> List<E> getCommonValues(Iterable<? extends E> ... lists) {
    // For each list: Get iterator and first value
    Iterator<? extends E>[] iter  = new Iterator[lists.length];
    E[] value = (E[])new Comparable[lists.length];
    for (int i = 0; i < lists.length; i++) {
        iter[i] = lists[i].iterator();
        value[i] = (iter[i].hasNext() ? iter[i].next() : null);
    }

    List<E> commonValues = new ArrayList<>();
    while (true) {

        // Find value count, lowest value, and count of lowest value
        int valueCount = 0, lowestCount = 0;
        E lowestValue = null;
        for (int i = 0; i < lists.length; i++)
            if (value[i] != null) {
                valueCount++;
                int cmp = (lowestValue == null ? -1 : value[i].compareTo(lowestValue));
                if (cmp < 0) {
                    lowestCount = 1;
                    lowestValue = value[i];
                } else if (cmp == 0) {
                    lowestCount++;
                }
            }

        // Exit loop if no more values
        if (valueCount == 0)
            break;

        // Save common value if all values were lowest value
        if (lowestCount == lists.length)
            commonValues.add(lowestValue);

        // For each list: Get next value if value was lowest value
        for (int i = 0; i < lists.length; i++)
            if (value[i] != null && value[i].compareTo(lowestValue) == 0)
                value[i] = (iter[i].hasNext() ? iter[i].next() : null);
    }

    return commonValues;
}

测试

List<Integer> common = getCommonValues(Arrays.asList(2, 2, 4, 6, 7, 11),
                                       Arrays.asList(2, 2, 3, 5, 7, 14),
                                       Arrays.asList(2, 2, 2, 3, 3, 4, 5, 6, 7, 11, 14));
System.out.println(common);

输出

[2, 2, 7]

【讨论】:

  • tyvm...这很好用。知道这个算法的运行时效率是多少吗?是 O(n) 吗?
  • 正确。如果将最长列表的长度加倍,则 while 循环的迭代次数将加倍。如果将列表数量加倍,则三个for 循环的迭代次数将加倍。运行时间与列表的大小/数量成线性关系,即O(n)
【解决方案2】:

我认为这可行,但我只检查了少数情况。

public static void main(String[] args) {
    int[][] arrays = {{1, 1, 2, 2, 2, 3, 7, 7, 10, 10, 10}, 
                      {1, 1, 1, 2, 2, 3, 3, 7, 7, 7, 10}, 
                      {1, 1, 2, 2, 3, 7, 7, 10}};
    int n = arrays.length;
    int[] indices = new int[n];
    while (checkBounds(arrays, indices)) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++)
            max = Math.max(max, arrays[i][indices[i]]);
        boolean allEqualMax = true;
        for (int i = 0; i < n; i++) {
            if (arrays[i][indices[i]] < max) {
                indices[i]++;
                allEqualMax = false;
            }
        }
        if (allEqualMax) {
            System.out.print(max + " ");
            for (int i = 0; i < n; i++)
                indices[i]++;
        }
    }
}

private static boolean checkBounds(int[][] arrays, int[] indices) {
    for (int i = 0, n = arrays.length; i < n; i++)
        if (indices[i] >= arrays[i].length)
            return false;
    return true;
}

【讨论】:

  • 这似乎运作良好,无论我传递的数组数量如何。我只需要修改它以适用于任何类型的列表...一组字符串列表、整数列表、浮点列表等。
  • 您只需要通过合并Comparator 来修改算法。我还不太清楚该方法的时间复杂度。
  • 我不熟悉...你能帮我通过合并一个比较器来修改这个算法吗?
  • 实际上,你提到的所有类型(StringIntegerFloat)都是Comparable,所以你不需要Comparator。 Andreas 的回答展示了如何使用Comparable
  • 我想我不太确定如果用户传递字符串,我将如何使用 Math.max 语句。
猜你喜欢
  • 2011-02-21
  • 2021-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-10
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 2018-03-15
相关资源
最近更新 更多