【问题标题】:What data structure(s) are appropriate for my situation?什么数据结构适合我的情况?
【发布时间】:2012-12-18 15:53:45
【问题描述】:

首先,这可能是一个 XY 问题,对此感到抱歉。

我正在从文件加载文件表并将其放入内存文件树中。树中的节点代表树中的目录/文件。目前,我每个节点使用两个数据结构,这会导致明显的加载时间,因为插入到集合中,并且由于复制字符串数据和引用每个节点两次而导致更高的内存使用。树只加载一次,之后不会发生变异。

每个节点都有一个用于访问已排序子节点的列表和一个用于按名称访问子节点的字典。出于性能原因,该列表被延迟排序。 SortedDictionary 不符合我的使用要求,因为我需要将有子节点的节点排在没有子节点的节点之上,因此传递 IComparer 是不够的。当两个节点都有/没有子节点时,它们按字典顺序排序(OrdinalIgnoreCase)。

.net 中是否有可以满足我需求的数据结构?

另外,有没有办法在插入字典时为键提供哈希,然后从字典中获取存储桶的一部分(即:GetValuesByHash(int hashValue) 产生其对应键具有给定哈希的所有值)?我正在读取的文件表已经包含整个文件路径的哈希值(适用于我正在做的另一件事),目前,字典只是无缘无故地重新计算它们。

我想我可以通过定义我自己的自定义键来组合一个解决方案,其中包含 { Hash, Node } 以及自定义比较器,但这看起来真的很难看,而且你将无法获得共享相同的节点桶哈希。如果有的话,那仍然感觉像是使用了错误的数据结构。

我已经用谷歌搜索了“c# dictionary get hash”以及其他一些查询,但目前我还没有看到任何类似的问题。

总的来说,寻找具有以下属性的数据结构(可能与字典有关):

  • ContainsKeyOfHash()、Get(hash):文件名哈希 -> 文件条目描述符
  • ContainsKey()、Get(key):文件名 -> 文件条目描述符
  • Add(string fileName, Entry entry, int hash = gethash(fileName))
  • 条目排序如下:

        m_children.Sort(
           (a, b) => {
              bool aHasChildren = a.HasChildren;
              bool bHasChildren = b.HasChildren;
              if (aHasChildren && !bHasChildren)
                 return 1;
              if (!aHasChildren && bHasChildren)
                 return -1;
              else
                 return -String.Compare(a.m_resourceName, b.m_resourceName, StringComparison.OrdinalIgnoreCase);
           }
        );
    
  • 可以按上述排序顺序检索所有子节点。目前,我有一个 ChildrenSorted 和 ChildrenUnsorted 属性。 ChildrenSorted 属性可能会因排序而导致性能下降,而 ChildrenUnsorted 属性则不会。

我认为更糟糕的是,我的解决方案是编写我自己的类字典类。我不必从字典中删除键,所以应该不难。不过,我有点想避免这样做。

我的节点实现可以查看:http://pastie.org/5547925

谢谢!

【问题讨论】:

  • 要实现“子排序”行为,您可以使用SortedList<string, Node>,其中string 不仅是节点的名称,而且是节点名称的前缀,例如0 有孩子,1 没有孩子。那么排序顺序就会正确,您可以先尝试0TheName,然后再尝试1TheName,按名称查找项目。

标签: c# optimization data-structures dictionary


【解决方案1】:

我认为你的解决方案已经很不错了。以下是一些想法:

  1. 对于一个既可以排序又可以按键快速访问的集合,我只能想到树形数据结构。您可能不想要为每个项目分配一个对象的数据结构。可能所有项目都放在一个数组中的一种堆为您提供最好的服务。我认为您可以通过首先对所有子项进行排序然后填充它们来非常有效地构建该结构(就像您现在正在做的那样)。
  2. 您可以考虑将所有数据填充到单个这样的树中。这将为您节省大部分每个节点的开销(例如本身具有子对象的集合)。关键是节点的“路径”,以某种有效的格式存储。它可以是"d1\d2\filename"string[] 之类的路径。

第 (2) 点将是 RDBMS 将如何做到这一点。

【讨论】:

  • 您能详细说明一下#1 吗?我无法真正理解实现,因此无法理解#2,这取决于#1。很抱歉 - 我以前没有真正接触过这样的东西。
  • 您可以使用树来实现既是字典又是排序列表的集合。这使您可以按名称查找孩子并以排序方式枚举所有孩子。查看“C5 Powercollections”以获得这样的数据结构。我不记得它的名字,但它在那里(也许是 IntervalHeap?我认为它在 C5 中并且是一个很好的基于数组的树)。
【解决方案2】:

您可以使用SortedDictionary,只需将Sort() lambda 放入`IComparer:

public class MyComparer : IComparer, IComparer<MyNode>
{
    public int Compare(object x, object y)
    {
        return Compare(x as MyNode, y as MyNode);
    }

    public int Compare(MyNode x, MyNode y)
    {
        if (ReferenceEquals(x, y))
        {
            return 0;
        }

        if (ReferenceEquals(x, null))
        {
            return -1;
        }

        if (ReferenceEquals(y, null))
        {
            return 1;
        }

        bool xHasChildren = x.HasChildren;
        bool yHasChildren = y.HasChildren;
        if (xHasChildren && !yHasChildren)
            return 1;
        if (!xHasChildren && yHasChildren)
            return -1;
        else
            return String.Compare(y.m_resourceName, x.m_resourceName, StringComparison.OrdinalIgnoreCase);
    }
}

【讨论】:

  • 这不允许按名称查找节点,对吗? (我可能错了,但要使用 IComparer&lt;Node&gt; Node 必须是 key 类型,对吧?)
  • 很确定罗林是对的,在这里。我认为我无法通过此实现获得给定名称的直系后代。
猜你喜欢
  • 2010-11-21
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-01
  • 1970-01-01
  • 2017-03-10
  • 1970-01-01
相关资源
最近更新 更多