【问题标题】:Removing duplicate string from List (.NET 2.0!)从列表中删除重复的字符串(.NET 2.0!)
【发布时间】:2009-08-26 15:30:39
【问题描述】:

我在寻找从字符串列表 (List) 中删除重复项的最有效方法时遇到了问题。

我当前的实现是一个双 foreach 循环,检查每个对象的实例计数是否只有 1,否则删除第二个。

我知道还有很多其他问题,但所有最好的解决方案都需要高于 .net 2.0,这是我目前正在使用的构建环境。(通用汽车和克莱斯勒非常抗拒改变......: ) )

这通过不允许任何 LINQ 或 HashSet 来限制可能的结果。

我使用的代码是 Visual C++,但 C# 解决方案也可以正常工作。

谢谢!

【问题讨论】:

    标签: c# .net c++ list


    【解决方案1】:

    这可能不是您想要的,但如果您对此有控制权,最有效的方法是一开始就不添加它们...

    您对此有控制权吗?如果是这样,您只需在添加项目之前调用myList.Contains(currentItem) 即可完成设置

    【讨论】:

    • 哈,我从没想过,我确实可以控制初始列表的生成!
    • 请注意,随着列表大小的增加,这种方法不能很好地扩展...
    • 如果大小是一个问题,我认为您可以使用上述相同的方法,但使用与标准列表相反的 SortedList
    • 这是 O(n^2),因为 List.Contains 是 O(n)。您需要借用 Jared 的字典来跟踪您添加的项目,检查时间为 O(1),总体为 O(n)。
    • 幸运的是,在这种特殊情况下,规模不是问题
    【解决方案2】:

    您可以执行以下操作。

    List<string> list = GetTheList();
    Dictionary<string,object> map = new Dictionary<string,object>();
    int i = 0;
    while ( i < list.Count ) {
      string current = list[i];
      if ( map.ContainsKey(current) ) {
        list.RemoveAt(i);
      } else {
        i++;
        map.Add(current,null);
      }
    }
    

    这会产生构建Dictionary&lt;TKey,TValue&gt; 对象的开销,该对象将复制列表中的唯一值列表。但它在速度方面相当有效。

    【讨论】:

    • +1 想到的第一件事是将每个值相互比较,同时删除发现的重复项,但其复杂性为 N^2。 Jared 的解决方案要好得多,因为使用字典数据结构将利用散列,因此查找速度非常快。复杂度 = N(log N) ?
    • 如果速度很重要,您最好创建一个新的唯一值列表,而不是从原始列表中删除重复项,因为 RemoveAt 是 O(n) 但 Add 是 O(1) 时你提前知道最大长度。
    【解决方案3】:

    我不是 Comp Sci 博士,但我想使用字典,将列表中的项目作为键会很快。

    由于字典不允许重复键,因此在迭代结束时您只有唯一的字符串。

    【讨论】:

      【解决方案4】:

      请记住,在提供自定义类来覆盖 Equals() 方法以使 Contains() 按需要运行时。

      例子

      List<CustomClass> clz = new List<CustomClass>()
      
      public class CustomClass{
      
          public bool Equals(Object param){
              //Put equal code here...
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果你要走“只是不添加重复项”的路线,那么在添加项目之前检查“List.Contains”有效,但它的 O(n^2) where n是您要添加的数字字符串。它与您当前使用两个嵌套循环的解决方案没有什么不同。

        使用哈希集存储已添加的项目会更好,但由于您使用的是 .NET 2.0,因此字典可以替代哈希集:

        static List<T> RemoveDuplicates<T>(List<T> input)
        {
            List<T> result = new List<T>(input.Count);
            Dictionary<T, object> hashSet = new Dictionary<T, object>();
            foreach (T s in input)
            {
                if (!hashSet.ContainsKey(s))
                {
                    result.Add(s);
                    hashSet.Add(s, null);
                }
            }
            return result;
        }
        

        这在 O(n) 中运行并使用 O(2n) 空间,它通常可以很好地处理多达 100K 的项目。实际性能取决于字符串的平均长度——如果你真的需要最大化性能,你可以利用一些更强大的数据结构,比如让插入更快的尝试。

        【讨论】:

        • HashSet 是 .net 3.5+,超出了这个问题的范围。
        • 我的代码不使用 HashSet,它使用字典代替 HashSet。
        • 我应该更彻底地阅读你的代码,我只是看到了HashSet这个词,并跳过了它。
        • NULL 值呢?字典将为键抛出 ArgumentNullException。
        猜你喜欢
        • 2014-08-02
        • 2011-12-17
        • 2019-05-05
        • 2016-10-24
        • 1970-01-01
        • 2013-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多