什么是用于唯一识别节点的最佳集合?

时间:2011-11-01 17:55:24

标签: c# .net data-structures collections

目前我使用Dictionary<int,node>来存储大约10,000个节点。密钥用作ID号以便以后查找,“node”是包含一些数据的类。程序中的其他类使用ID号作为指向节点的指针。 (这可能听起来效率低下。但是,解释我为此使用字典的原因超出了我的问题的范围。)

但是,20%的节点是重复的。 我想要做的是当我添加节点检查以查看是否所有准备就绪。如果是,则使用该ID号。如果没有创建一个新的。

这是我目前解决问题的方法:

public class nodeDictionary 
{

    Dictionary<int, node> dict = new Dictionary<int, node>( );
    public int addNewNode( latLng ll )
    {
        node n = new node( ll );
        if ( dict.ContainsValue( n ) )
        {
            foreach ( KeyValuePair<int, node> kv in dict )
            {
                if ( kv.Value == n )
                {
                    return kv.Key;
                }
            }
        }
        else
        {
            if ( dict.Count != 0 )
            {
                dict.Add( dict.Last( ).Key + 1, n );
                return dict.Last( ).Key + 1;
            }
            else
            {
                dict.Add( 0, n );
                return 0;
            }
        }
        throw new Exception( );
    }//end add new node
}

这个问题是当尝试将新节点添加到100,000个节点的列表时,添加节点需要78毫秒。这是不可接受的,因为我可以在任何给定时间添加额外的1,000个节点。

那么,有没有更好的方法呢?我不是在找人为我编写代码,我只是在寻找指导。

6 个答案:

答案 0 :(得分:3)

听起来你想要

  • 确保LatLng重写Equals / GetHashCode(最好实现IEquatable<LatLng>接口)
  • 将所有项目直接填入HashSet<LatLng>

有关实施GetHashCode的信息,请参阅此处:Why is it important to override GetHashCode when Equals method is overridden?

如果您需要以某种方式生成“人工”唯一ID,我建议您再次使用字典方法,但“反向”:

// uses the same hash function for speedy lookup/insertion
IDictionary<LatLng, int> idMap = new Dictionary<LatLng, int>(); 

foreach (LatLng latLng in LatLngCoords)
{
    if (!idMap.ContainsKey(latLng))
        idMap.Add(latLng, idMap.Count+1); // to start with 1
}

您可以让idMap替换HashSet<>;实现(和性能特征)基本相同,但作为关联容器。

这是一个从LatLng到Id的查找函数:

int IdLookup(LatLng latLng)
{
     int id;
     if (idMap.TryGetValue(latLng, id))
         return id;
     throw new InvalidArgumentException("Coordinate not in idMap");
}

你可以及时添加它:

int IdFor(LatLng latLng)
{
     int id;
     if (idMap.TryGetValue(latLng, id))
         return id;

     id = idMap.Count+1;
     idMap.Add(latLng, id);
     return id;
}

答案 1 :(得分:1)

此代码的目的究竟是什么?

if ( dict.ContainsValue( n ) )
{
    foreach ( KeyValuePair kv in dict )
    {
        if ( kv.Value == n )
        {
            return kv.Key;
        }
    }
}

ContainsValue搜索(而不是键)并且效率非常低(O(n))。同上foreach。更不用说只有一个是必要的时候才能同时执行这两项操作(您可以通过重新排列ContainsValue s来完全删除if

你应该维护一个与原始字典“反向”的附加字典(即旧字典中的值是新字典中的键,反之亦然),以“覆盖”您的搜索模式(类似于数据库如何维护多个索引par表覆盖多种方式表可以查询。)

答案 2 :(得分:1)

我会为反方向添加第二个字典。即Dictionary<Node,int>

然后你要么

  • 满足于引用相等并且什么都不做。
  • 创建IEqualityComparer<Node>并将其提供给词典
  • 覆盖Equals
  • 上的GetHashCodeNode

在这两种情况下,哈希码的良好实现对于获得良好的性能至关重要。

答案 3 :(得分:1)

您的解决方案不仅缓慢,而且错误。 Dictionary中的项目顺序未定义,因此无法保证dict.Last()返回最后添加的项目。 (虽然它通常看起来像那样。)

使用id来识别应用程序中的对象似乎也是错误的。您应该考虑直接使用对象的引用。

但是如果您想使用当前的设计并假设您根据latLng比较节点,则可以创建两个词典:您已经拥有的词典和第二个词典Dictionary<latLng, int>,可用于有效地了解某个节点是否已存在。如果确实如此,它会给你它的身份。

答案 4 :(得分:0)

您可以尝试使用HashSet<T>

答案 5 :(得分:0)

您可能需要考虑将其重组为仅使用List(其中'key'只是List的索引)而不是Dictionary。一些优点:

  1. 按整数键查找元素现在是O(1)(并且非常快O(1),因为它只是内部的数组解引用。)

  2. 插入新元素时,执行O(n)搜索以查看它是否已存在于列表中。如果没有,您还已经遍历了列表,并且可以记录您是否遇到了具有空记录的条目。如果有,则该索引是新密钥。如果不是,则新密钥是当前列表Count。您只是枚举集合一次而不是多次,枚举本身比枚举字典要快得多。