72

さまざまな状況で何度もこの問題に直面しました。私は C や Java に慣れていますが、すべてのプログラミング言語に共通です。

2 つの配列 (またはコレクション) を考えてみましょう。

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

2 つの配列間の共通要素を新しい配列として取得するにはどうすればよいですか? この場合、配列 A と B の交点は ですchar[] c = {'c', 'd'}

巨大な配列の場合は長すぎる (A の長さ B の長さ) だけ実行時間が長くなる、別の配列内で 1 つの配列を繰り返し反復することを避けたいと考えています。

共通の要素を取得するために、各配列で単一のパスを実行する方法はありますか?

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)。

ハッシュアプ​​ローチ

配列 1 の要素からハッシュ テーブルを作成します。ハッシュ テーブルの 2 番目のテーブルから要素を検索します。時間計算量はハッシュ関数によって異なります。最適な場合 (すべての要素のハッシュ値が異なる) の検索で 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) になります。

于 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

これは文字列アルゴリズムのように見えるので、このシーケンス (したがって文字列) をソートすることは不可能であると仮定し、Longest Common Sequence アルゴリズム (LCS)を使用できます。

入力サイズが一定であると仮定すると、問題の複雑さは O(nxm) (2 つの入力の長さ) になります。

于 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 (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

重複が気になる場合は、ハッシュマップを使用してリストAにインデックスを付けます。キーは要素であり、値はその要素が表示された回数です。

Aの最初のすべての要素を繰り返し処理し、マップに存在しない場合は1の値でそこに配置し、マップにすでに存在する場合はその値に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)リストを1回、マップを1回だけ繰り返すため、これはで実行する必要があります。ここでJavaで使用されるデータ構造HashMapは、リストの最大サイズを処理できる容量で構築されているため、効率的である必要があります。

LinkedListサイズが不明な交差点のリストを追加して反復する方法を提供するため、リターンにaを使用しています。

于 2012-11-07T16:39:50.007 に答える
2
  1. 両方の配列を並べ替えます。
  2. 次に、共通の要素が含まれるまで、または配列の1つが最後に到達するまでループを実行します。

漸近的に、これはソートの複雑さを取ります。つまり、O(NlogN)です。ここで、Nは長い入力配列の長さです。

于 2012-11-07T13:15:52.097 に答える
1

ルビーでは、あなたはただ言うことができます

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

c には ['c','d'] が含まれています

于 2013-03-12T01:25:18.640 に答える
1

ツリーを使用できますが、時間は O(n(log n)) になり、要素は比較可能でなければなりません

于 2012-11-07T13:27:05.330 に答える
1

最良の方法は、配列から始めないことです。配列は、要素へのランダム アクセスには最適ですが、検索には最適ではありません (交差を見つけることがすべてです)。交差点について話しているので、配列をセットと見なす必要があります。したがって、より適切なデータ構造を使用してください (Java では a Set)。その後、タスクははるかに効率的です。

于 2012-11-07T13:18:56.337 に答える
1

まず、最適な並べ替えアルゴリズムを使用して 2 つの配列を並べ替えます。
次に、線形検索を使用して、共通要素を取得できます。

余分なスペースが提供されている場合は、ハッシュ テーブルを使用してそれを行うことができます。

于 2012-11-14T12:28:31.587 に答える
0

Java 8 機能を使用して、リストをセットに変換する代わりに、リスト内の重複を尊重するアルゴリズムを次に示します。並べ替えがないので、いいえn log n

  1. リストの 1 つをマップに変換します。値は出現回数 (コスト: O(n)) です。
  2. 他のリストの各アイテムについて、そのアイテムがマップに存在する場合、発生を 1 つ減らします (コスト: 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

以下が役立つことを願っています。これらは 2 つの異なるアプローチです。

  • ある配列のすべての要素を別の配列と比較する単純交差。

  • 1 つの配列をソートし、バイナリ検索を使用して最初の配列内の 2 番目の配列要素を検索する、ソートおよび検索ベースのアプローチ。

//

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

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

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

質問に示されているように、コレクションが既にソートされている場合、最善の解決策 (まだ言及されていません) は、O(n+m) で実行されるマージソートのようなアルゴリズムです。

各コレクションの最初の要素を比較します。それらが同じである場合は、要素を交差セットに追加し、コレクションから両方の要素をポップします。要素が異なる場合は、他の要素と比較して大きい要素をポップします。1 つのコレクションが空になるまで繰り返します。

于 2015-11-03T02:25:08.637 に答える
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 に答える