如何将两个数组之间的交集作为新数组?

Ran*_*rma 69 c c++ java algorithm

在各种情况下我多次遇到这个问题.虽然我对C或Java感到满意,但它对所有编程语言都是通用的.

让我们考虑两个数组(或集合):

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
Run Code Online (Sandbox Code Playgroud)

如何将两个数组之间的公共元素作为新数组?在这种情况下,阵列A和B的交点是char[] c = {'c', 'd'}.

我想避免在另一个数组内重复迭代一个数组,这将增加执行时间(A长度为B的长度),这对于大型数组来说太多了.

有没有什么办法可以在每个数组中单独传递以获得公共元素?

cod*_*ict 108

foreach element e in array A
    insert e into hash table H

foreach element e in array B
    if H contains e 
        print e
Run Code Online (Sandbox Code Playgroud)

该算法O(N)在时间和O(N)空间上.

为了避免额外的空间,您可以使用基于排序的方法.

  • @Yola我们可以整天打蜡.但你说"不是最快的方法",这在实践中是错误的(在预期的情况下它*是*最快的解决方案)*和理论上的*(如果推动推动你可以实现一个非线性的模式性能*极不可能*甚至保证有限域的线性性能).在典型的使用案例中,这些理论上完美的解决方案都不一定容易确保,但是散列已经是典型用例的最快解决方案. (6认同)
  • @Yola没有比O(n)更快的解决方案; 不可能. (5认同)
  • 请注意,重复项仍然存在问题(如何处理它没有在问题中指定),但通常要打印每个元素`min {#ocrance(A),#occurrence(B)}`或只打印一次,而这个解决方案打印它们`#occurances(B)`次 (5认同)
  • @Yola否,在哈希表中搜索需要O(1).计算哈希值需要多长时间取决于密钥类型,但它通常不比比较两个密钥(在树和排序中需要)慢得多.特别是,对于简单的`char`键,它非常快. (3认同)
  • 如果限制数据集的大小,一切都需要O(1)时间.散列查找在数据集大小上的O(1)达到某个限制,但是因此分解为素数.哈希是快速的,因为一个小的常数不大O符号. (3认同)
  • @Yola No.您可以确保有限域名为O(N).对于非限制域,您仍然可以证明*几乎肯定*是O(N),您甚至可以量化这种确定性. (2认同)
  • @Konrad搜索花费O(n)时间.想象一下所有元素都具有相同哈希值的情况,因此您需要将它们存储在列表中. (2认同)

Jak*_*rka 33

效率的下限是O(n) - 你至少需要读取所有元素.然后有几个apporaches:

愚蠢最简单的方法

从数组2中的第一个数组中搜索每个元素.时间复杂度O(n ^ 2).

排序方法

您只需要对数组1进行排序,然后使用二进制搜索从数组2中搜索元素.时间复杂度:排序O(nlogn),搜索O(n*logn)= O(nlogn),总O(nlogn).

哈希方法

从数组一个元素创建一个哈希表.从哈希表中的第二个表中搜索元素.时间复杂度取决于散列函数.您可以在最佳情况下为搜索实现O(1)(所有元素将具有不同的哈希值),但在最坏的情况下为O(n)(所有元素将具有相同的哈希值).总时间复杂度:O(n ^ x),其中x是散列函数效率的因子(在1和2之间).

一些散列函数可以保证构建一个没有冲突的表.但是建筑物不再需要每个元素严格的O(1)时间.在大多数情况下它将是O(1),但如果表已满或遇到碰撞,则表需要重新进行 - 花费O(n)时间.这种情况不常发生,比清洁添加频繁得多.因此,AMORTIZED时间复杂度为O(1).只要大多数添加花费O(1)时间,我们就不关心花费O(n)时间的一些添加.

但即便如此,在极端情况下,每次插入时都必须重新进行表格,因此严格的时间复杂度为O(n ^ 2)

  • @Konrad我不否认.我只是在严格的时间复杂度,最糟糕的情况下操作. (2认同)

Mik*_*ike 21

我知道某些语言中有一些方法可以完全满足您的需求,您是否考虑过查看其中一些实现?

PHP - array_intersect()

$array1 = array("a" => "green", "red", "blue");
$array2 = array("b" => "green", "yellow", "red");
$result = array_intersect($array1, $array2);
print_r($result);

>> green
   red
Run Code Online (Sandbox Code Playgroud)

Java - List.retainAll

Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));

listOne.retainAll( listTwo );
System.out.println( listOne );

>> dingo, hafil, iga
Run Code Online (Sandbox Code Playgroud)


Moa*_*sry 12

因为这对我来说就像一个字符串算法,我会假设它不可能排序这个序列(因此字符串)然后你可以使用最长公共序列算法(LCS)

假设输入大小是常数,则问题的复杂度为O(nxm),(两个输入的长度)

  • 好吧,OP要求两个阵列之间的交集.交集是集合论的概念,并且(根据定义)设置不依赖于其元素的顺序.我不是说LCS由于某种原因是坏的.我只是声称它没有给你交集.它是一种解决问题的算法,但不是(集)交集问题.如果数组不被视为集合,那么确定,但是你可能不会要求真正的交集.但我理解你的假设:如果它是一个数组,它更多的是一个字符串而不是一个集合. (4认同)
  • LCS没有给出这个问题的正确答案,算法适用于秩序重要的字符串,在这种情况下,交集总是在集合上,顺序无关紧要.@Gabriel的简单示例就在现场,我们需要对输入数据进行更多假设才能实现.给定排序约束,哈希答案可能是最好的. (2认同)

Mik*_*378 5

    public static void main(String[] args) {
        char[] a = {'a', 'b', 'c', 'd'};
        char[] b = {'c', 'd', 'e', 'f'};
        System.out.println(intersect(a, b));
    }

    private static Set<Character> intersect(char[] a, char[] b) {
        Set<Character> aSet = new HashSet<Character>();
        Set<Character> intersection = new HashSet<Character>();
        for (char c : a) {
            aSet.add(c);
        }
        for (char c : b) {
            if (aSet.contains(c)) {
                intersection.add(c);
            }
        }
        return intersection;
    }
Run Code Online (Sandbox Code Playgroud)