72

我在各种情况下多次遇到这个问题。尽管我对 C 或 Java 很熟悉,但它对所有编程语言都是通用的。

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

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};

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

我想避免一个数组在另一个数组中的重复迭代,这将增加执行时间(A 的长度乘 B 的长度),这在大型数组的情况下太多了。

有什么方法可以在每个数组中进行一次传递以获取公共元素?

4

22 回答 22

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

foreach element e in array B
    if H contains e 
        print e

这个算法是O(N)在时间和O(N)空间上的。

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

于 2012-11-07T13:15:23.347 回答
33

效率的下限是 O(n) - 您至少需要读取所有元素。然后有几种方法:

最简单的方法

在数组 2 中搜索数组 1 中的每个元素。时间复杂度 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(n) 时间,只要大多数添加花费 O(1) 时间。

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

于 2012-11-07T14:20:58.053 回答
20

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

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

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
于 2012-11-07T13:32:25.870 回答
12

因为这在我看来像一个字符串算法,所以我暂时假设不可能对这个序列(因此是字符串)进行排序,然后你可以使用最长公共序列算法(LCS)

假设输入大小是恒定的,那么问题的复杂度为 O(nxm),(两个输入的长度)

于 2012-11-07T13:32:47.557 回答
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;
    }
于 2012-11-07T13:42:41.910 回答
4
int s[256] // for considering all ascii values, serves as a hash function

for(int i=0;i<256;i++)
s[i]=0;

char a[]={'a','b','c','d'};
char b[]={'c','d','e','f'};

for(int i=0;i<sizeof(a);i++)
{
   s[a[i]]++;
 }

 for(int i=0;i<sizeof(b);i++)//checker function
 {
     if(s[b[i]]>0)
       cout<<b[i]; 
  }


  complexity O(m+n);
  m- length of array a
  n- length of array b
于 2012-11-07T23:22:09.937 回答
3

谷歌番石榴

对此已经有很多很好的答案,但是如果您想要使用库进行延迟编码的单线方法,我会使用Google Guava (for Java) 及其Sets.intersection方法。

(手头没有编译器,请耐心等待)

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};

Set<Character> intersection = Sets.intersection(
    Sets.newHashSet<Character>(Chars.asList(a)),
    Sets.newHashSet<Character>(Chars.asList(b))
);

显然,这是假设两个数组都没有重复项,在这种情况下,使用集合数据结构会更有意义,并且允许更有效地进行这种操作,特别是如果您从一开始就没有从基元数组开始.

可能适合也可能不适合您的用例,但对于一般情况来说,这是一种不费吹灰之力的方法。

于 2012-11-07T14:35:51.237 回答
2
  1. 对两个数组进行排序。
  2. 然后循环,直到它们具有共同的元素或数组之一到达其末尾。

渐近地,这需要排序的复杂性。即 O(NlogN) 其中 N 是较长输入数组的长度。

于 2012-11-07T13:15:52.097 回答
2

如果您关心重复,请使用哈希映射来索引列表 A,其中键是元素,值是该元素已被看到的次数。

您遍历 A 中的第一个元素和每个元素,如果它不存在于地图中,则将其放入其中,值为 1,如果它已存在于地图中,则将其添加到该值。

接下来,遍历 B,如果值存在,则减 1。如果不存在,则将 -1 放入表中该元素的值中。

最后,遍历地图,对于任何值为 != 0 的元素,打印出差异。

private static <T> List<T> intersectArrays(List<T> a, List<T> b) {
    Map<T, Long> intersectionCountMap = new HashMap<T, Long>((((Math.max(a.size(), b.size()))*4)/3)+1);
    List<T> returnList = new LinkedList<T>();
    for(T element : a) {
        Long count = intersectionCountMap.get(element);
        if (count != null) {
            intersectionCountMap.put(element, count+1);
        } else {
            intersectionCountMap.put(element, 1L);
        }
    }
    for (T element : b) {
        Long count = intersectionCountMap.get(element);
        if (count != null) {
            intersectionCountMap.put(element, count-1);
        } else {
            intersectionCountMap.put(element, -1L);
        }            
    }
    for(T key : intersectionCountMap.keySet()) {
        Long count = intersectionCountMap.get(key);
        if (count != null && count != 0) {
            for(long i = 0; i < count; i++) {
                returnList.add(key);
            }
        }
    }
    return returnList;
}

这应该在 中运行O(n),因为我们只对 List 进行一次迭代,对 Map 进行一次。Java 中使用的数据结构应该是高效的,因为其HashMap构造的容量可以处理最大大小的列表。

我使用 aLinkedList作为返回值,因为它为我们提供了一种为未知大小的交集添加和迭代列表的方法。

于 2012-11-07T16:39:50.007 回答
1

最好的方法是根本不从数组开始。数组最适合随机访问元素,但不是最适合搜索(这就是找到交集的全部意义所在)。当您谈论交集时,您必须将数组视为集合。所以使用更合适的数据结构(在Java中,a Set)。然后任务效率更高。

于 2012-11-07T13:18:56.337 回答
1

您可以使用树,但时间将是 O(n(log n)) 并且元素必须具有可比性

于 2012-11-07T13:27:05.330 回答
1

首先,使用最佳排序算法对两个数组进行排序。
然后,通过线性搜索,您可以获得公共元素。

如果提供了额外的空间,那么我们可以使用哈希表来做到这一点。

于 2012-11-14T12:28:31.587 回答
1

在红宝石中你可以说

a = ['a', 'b', 'c', 'd']
b = ['c', 'd', 'e', 'f']
c = a & b

c 包含 ['c','d']

于 2013-03-12T01:25:18.640 回答
1

首先对两个数组进行排序,然后迭代它们,如果它们是相同的元素,则添加到要返回的数组中。

代码在这里:

public static void printArr(int[] arr){
    for (int a:arr){
        System.out.print(a + ", ");
    }
    System.out.println();
}

public static int[] intersectionOf(int[] arr1, int[] arr2){
    Arrays.sort(arr1);
    Arrays.sort(arr2);

    printArr(arr1);
    printArr(arr2);

    int i=0, j=0, k=0;
    int[] arr = new int[Math.min(arr1.length, arr2.length)];

    while( i < arr1.length && j < arr2.length){
        if(arr1[i] < arr2[j]){
            i++;
        } else if(arr1[i] > arr2[j]){
            j++;
        } else {
            arr[k++] = arr1[i++];
            j++;
        }
    }
    return Arrays.copyOf(arr, k);
}

public static void main(String[] args) {
    int[] arr1 = {1, 2, 6};
    int[] arr2 = {10, 2, 5, 1};
    printArr(intersectionOf(arr1,arr2));
}

输出:

arr1: 1, 2, 6, 
arr2: 1, 2, 5, 10, 
arr: 1, 2, 
于 2014-10-01T14:29:24.113 回答
0

假设您正在处理 ANSI 字符。该方法应该与 Unicode 类似,只需更改范围即可。

char[] A = {'a', 'b', 'c', 'd'};
char[] B = {'c', 'd', 'e', 'f'};
int[] charset = new int[256]

for(int i=0; i<A.length; i++) {
  charset[A[i]]++;
}

现在遍历 B,您可以检查被迭代字符的相应字符集值是否大于 0。您可以将它们存储在列表或任何其他集合中。

这种方法需要 O(n) 时间复杂度和用于检查的恒定空间,而不考虑用于保存公共元素的新数组/列表。

这在空间复杂度方面优于 HashSet/Hashtable 方法。

于 2012-11-07T18:33:06.687 回答
0

您可以在 .NET 3.5 或更高版本中使用 HashSet。示例 C# 代码:

HashSet<int> set1 = new HashSet<int>(new int[]{8, 12, 13, 15});

HashSet<int> set2 = new HashSet<int>(new int[] { 15, 16, 7, 8, 9 });

set1.IntersectWith(set2);

foreach (int i in set1)

   Console.Write(i+ " ");

//输出:8 15

于 2013-08-08T08:00:57.533 回答
0

现在对其中一个数组 (m Log(m) ) 排序

总时间复杂度:- (n+m)Log(m)

于 2013-10-15T13:55:10.773 回答
0

我希望以下内容会有所帮助。这是两种不同的方法:

  • 简单的交集,您可以将一个数组中的所有元素与另一个数组进行比较。

  • 基于排序和搜索的方法,该方法对一个数组进行排序并使用二进制搜索在第一个数组中搜索第二个数组元素。

//

public class IntersectionOfUnsortedArrays {
    public static void main(String[] args) {
        int[] arr1 = { 12, 4, 17 };
        int[] arr2 = { 1, 12, 7, 17 };
        System.out.println("Intersection Using Simple Comparision");
        printArray(simpleIntersection(arr1, arr2));
        System.out.println("Intersection Using Sort and Binary Search");
        printArray(sortingBasedIntersection(arr1, arr2));
    }

    /*
     * Simple intersection based on the comparison without any sorting.
     * Complexity O(n^2)
     */
    public static int[] simpleIntersection(int[] a, int[] b) {
        int minlen = a.length > b.length ? b.length : a.length;
        int c[] = new int[minlen];
        int k=0;
        for(int i=0;i<a.length;i++){
            for(int j=0;j<b.length;j++){
                if(a[i]==b[j]){
                    c[k++]=a[i];
                }
            }
        }
        int arr[] = new int[k];
        // copy the final array to remove unwanted 0's from the array c
        System.arraycopy(c, 0, arr, 0, k);
        return arr;
    }

    /*
     * Sorting and Searching based intersection.
     * Complexity Sorting O(n^2) + Searching O(log n)
     */

    public static int[] sortingBasedIntersection(int[] a, int[] b){
        insertionSort(a);
        int minlen = a.length > b.length ? b.length : a.length;
        int c[] = new int[minlen];
        int k=0;
        for(int i=0;i<b.length;i++){
            int result = binarySearch(a,0,a.length,b[i]);
            if(result > -1){
                c[k++] = a[result];
            }
        }
        int arr[] = new int[k];
        // copy the final array to remove unwanted 0's from the array c
        System.arraycopy(c, 0, arr, 0, k);
        return arr;
    }

    public static void insertionSort(int array[]) {
        for (int i = 1; i < array.length; i++) {
            int j = i;
            int b = array[i];
            while ((j > 0) && (array[j - 1] > b)) {
                array[j] = array[j - 1];
                j--;
            }
            array[j] = b;
        }
    }

    static int binarySearch(int arr[], int low, int high, int num) {
        if (high < low)
            return -1;
        int mid = (low + high) / 2;
        if (num == arr[mid])
            return mid;
        if (num > arr[mid])
            return binarySearch(arr, (mid + 1), high, num);
        else
            return binarySearch(arr, low, (mid - 1), num);
    }

    public static void printArray(int[] array) {
        for (int value : array) {
            System.out.print(" "+value);
        }
        System.out.println("\n");
    }
}

于 2014-12-14T05:30:09.363 回答
0

如果集合已经排序,如问题所示,那么最好的解决方案(尚未提及)是在 O(n+m) 中运行的类似合并排序的算法。

比较每个集合的第一个元素。如果它们相同,则将元素添加到交集并从它们的集合中弹出两个元素。如果元素不同,则弹出与另一个元素相比更大的元素。重复直到一个集合为空。

于 2015-11-03T02:25:08.637 回答
0

使用 Java 8 特性,这里有一个算法,它尊重列表中的重复项,而不是将列表转换为集合。没有排序,所以没有n log n

  1. 将其中一个列表转换为地图,其值为出现次数(成本:O(n))。
  2. 对于另一个列表中的每个项目,如果该项目存在于地图中,则将出现次数减少一(成本:O(n))。

因此,总成本为 O(n)。代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Dup {
  public static void main(String[] args) {
    List<Integer> listA = Arrays.asList(3, 1, 4, 1, 9, 5, 9);
    List<Integer> listB = Arrays.asList(2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3);
    findCommons(listA, listB);
  }

  static void findCommons(List<Integer> listA, List<Integer> listB) {
    Map<Integer, Long> mapA = 
        listA.stream().collect(
            Collectors.groupingBy(Integer::intValue, Collectors.counting()));

    List<Integer> commons = new ArrayList<>();
    listB.stream()
        .filter(e -> mapA.get(e) != null)
        .filter(e -> mapA.get(e) > 0)
        .forEach(e -> {
            mapA.put(e, mapA.get(e) - 1);
            commons.add(e);
        });

    System.out.println(commons);
  }
}

上面的代码将给出这个输出:[5, 3, 9, 9].

于 2015-12-16T03:07:03.510 回答
0

导入 java.util.Scanner;

公共类数组公共{

public static void main(String[] args) {
    Scanner sc=new Scanner(System.in);
    // display common element in two diffrent array
    int sizea,sizeb,i=0,j=0,k=0;
    int count=0;
    System.out.println("enter the size array A:"+'\n');
    sizea=sc.nextInt();
    System.out.println("enter the size array B"+'\n');
    sizeb=sc.nextInt();
    int a[]=new int[sizea];
    int b[]=new int[sizeb];
    int c[]=new int[sizea];


    System.out.println("enter the element in array A:"+'\n');
    for (i = 0; i < sizea; i++) {

        a[i]=sc.nextInt();
    }
    System.out.println("enter the element in array B:"+'\n');
    for (i = 0; i < sizeb; i++) {

        b[i]=sc.nextInt();
    }
    System.out.println("the element in array A:"+'\n');
    for (i = 0; i < sizea; i++) {

        System.out.print(a[i]+" ");

    }
    System.out.println('\n');
    System.out.println("the element in array B:"+'\n');
    for (i = 0; i < sizeb; i++) 
    {

        System.out.print(b[i]+" ");
    }

    for (i = 0; i <sizea; i++) 
    {
        for (j = 0; j < sizeb; j++) 
        {
           if(a[i]==b[j])
           {
               count++;
               c[k]=a[i];
               k=k+1;
           }
        }
    }
    System.out.println('\n');
    System.out.println("element common in array is");

    if(count==0)
    {
        System.out.println("sorry no common elements");
    }
    else
    {
        for (i = 0; i <count; i++) 
        {

        System.out.print(c[i]+" ");
        }
    }

}

}

于 2016-09-28T03:36:48.487 回答
0
    simply search each element of first array with each element of second array and stored matched result in third array
class Union
{
  public static void main(String[] args) {
  char a[] ={'f','g','d','v','a'};
  char b[] ={'a','b','c','d','e'};
  char temp[] = new char[5];
  int p=0;
  for(int i=0;i<a.length;i++)
  {
    for(int j=0;j<b.length;j++)
    {
      if(a[i]==b[j])     //searches if both array has common element
      {

        temp[p] = a[i];   //if match found store it in a new array
        p++;
      }

    }

  }
  for(int k=0;k<temp.length;k++)
  {
      System.out.println(temp[k]);
  }

  }
}
于 2017-03-17T12:22:16.323 回答