web-dev-qa-db-ja.com

整数のリストのリストで重複を見つける

整数のリストのリストで重複を見つけるための最良の方法は何ですか(どの位置にいても)。この問題に対処するための最良の方法であるコードは必要ありません。

例えば:

List<List<int>> TestData = new List<List<int>>
{
     new List<int> { 1, 2, 3 },
     new List<int> { 2, 1, 3 },
     new List<int> { 6, 8, 3 },
     new List<int> { 9, 2, 4 },
};

これは戻ってくるという考えです

2x) 1,2,3
1x) 6,8,3
1x) 9,2,4

私はこの一見非常に単純な質問に頭を悩ませてきましたが、何らかの理由でそれを理解することができません。誰かが助けてくれるといいのですが、私が言ったように、コードは必要ありませんが、とても感謝しています。

2
John
  • 1回ずつ並べ替え
  • 最初の長さを比較
  • 次に、要素ごとに比較します
    要素が一致しないとすぐに、リストは一致しません

このサイトはコードに関するものではありませんが、これは機能します

IEqualityComparer<List<int>> listComparer = new ListComparer();
testData.ForEach(l => l.Sort());
var distinctLists = testData
    .GroupBy(j => j, listComparer)
    .Select(group => new { List = group.Key, Count = group.Count() });

public class ListComparer : IEqualityComparer<List<int>>
{
    public bool Equals(List<int> x, List<int> y)
    {
        if (x.Count != y.Count)
            return false;
        for (int i = 0; i < x.Count; i++)
            if (x[i] != y[i]) return false;
        return true;
    }
    public int GetHashCode(List<int> x) => x.Count;
}
2
paparazzo

ブルートフォースアプローチ:

  • まず、TestDataのすべてのリストを並べ替えます
  • 次に 辞書式 並べ替えTestData(重複するリストが必然的に互いに続くように)
  • 最後にTestDataを繰り返します。すべてのリストについて、以下のリストがいくつあるかを数えて、重複を見つけます(重複が見つかった場合は、メインの繰り返しでスキップします)。

スマートなアプローチ:

  • 各リストのチェックサムをTestDataにインデックスと長さとともに保存するリストを作成し、チェックサムと長さで並べ替えます。
  • このリストを繰り返します。同じチェックサムと長さを持つ連続するアイテムのすべてのグループについて、ブルートフォースアプローチのように進めますが、対応する(潜在的に等しい)リストに対してのみです。

このアプローチは、同じチェックサムと長さを持つリストのみが等しい場合があるため、ソートおよび比較するリストの数を減らすという事実に基づいています。

この2番目の方法は実装がより複雑ですが、より高価なソートを実行し、本当に必要な場合にのみ比較するという利点があります。アルゴリズムを適応させて、ソートをスマートな比較に置き換えることができます


リストのエレガントな辞書式ソート を含む、最初のアプローチの小さな実装を次に示します。

    // Sort every list in the list 
    for (int i = 0; i < TestData.Count; i++) 
         TestData[i].Sort();

    // Order the list of lists using a lexicographic sort
    TestData.Sort((x, y) => {
        var result = x.Zip(y, Tuple.Create)
                       .Select(z => z.Item1.CompareTo(z.Item2))
                       .FirstOrDefault(k => k != 0);
        return result == 0 && !x.Any() ? -1 : result; 
    });
    var sorted = TestData; 

    // Iterating through the ordered list of list to spot the duplicates
    List<int> t=null;
    int cpt = 1;  
    foreach (var l in sorted)
    {
         if (t != null)  // do nothing for the very first list
         {
             // in all other cases, compare list with the previous one
             var a = t.SequenceEqual(l);
             if (a) // if it's the same, increment occurrence counter
                 cpt++;
             else   // if not, show the duplicates and restart counting
             {
                 Console.Write("{0} x ", cpt);
                 WriteList(t);
                 cpt = 1;
             }
         }
         t = l; 
     }
     if (t!=null)  // process the last element outside the loop
     {
         Console.Write("{0} x ", cpt);
         WriteList(t);
     }

次のテスト入力を使用します。

    List<List<int>> TestData
        = new List<List<int>>  { new List<int> { 1, 2, 3 },
                                 new List<int> { 1, 8, 2 },
                                 new List<int> { 2, 1, 3 },
                                 new List<int> { 9, 2, 4 } };

期待される出力を生成します:

    2 x  1 2 3
    1 x  1 2 8
    1 x  2 4 9
2
Christophe

各リストのソートされたバージョンでグループ化し、各グループの長さを取得する場合、これは実装が非常に簡単です。 Scalaでは、これは:

val groups = testData groupBy {_.sorted} mapValues {_.length}
groups foreach println
/* output:
   (List(3, 6, 8),1)
   (List(2, 4, 9),1)
   (List(1, 2, 3),2)
*/

当然、注文や出力フォーマットの要件がある場合は、さらに複雑になります。 C#はわかりませんが、LINQには GroupBy があり、非常によく似ています。

1
Karl Bielefeldt
  1. リストが複製されるということを定義する必要があります。注文は気にしないようです。{1、2、3}は{2、1、3}の重複ですが、重複した数字も無視するため、{1、2、3}は{2の重複です、1、1、3}?

  2. 決定した同等性の定義を使用してIEqualityComparerを記述します。 (例:セットまたは順序付きリストとして比較)

  3. 等値比較子でGroupByを使用して類似のリストをグループ化すると、各グループの重複のCountを取得できます。

0
JacquesB