【发布时间】:2011-07-11 19:33:23
【问题描述】:
我正在研究c# jquery implementation,并试图找出一种有效的算法来定位整个 DOM 的子集(例如子选择器)中的元素。目前我正在创建一个常用选择器的索引:构建 DOM 时的类、id 和标记。
基本数据结构正如人们所期望的那样,一棵Elements 的树,其中包含IEnumerable<Element> Children 和一个Parent。当使用Dictonary<string,HashSet<Element>> 来存储索引搜索整个域时,这很简单。
我一直无法理解使用索引搜索元素子集的最有效方法。我使用术语“子集”来指代链中后续选择器将从其运行的起始集。以下是我想到的方法:
- 从整个 DOM 中检索子查询的匹配项,并消除不属于子集的匹配项。这需要遍历每个匹配项的父项,直到找到根(并将其消除)或找到子集的成员(并且它是子项,因此包括在内)
- 为每个元素单独维护索引。
- 为每个元素维护一组父元素(通过消除遍历使 #1 更快)
- 为每个子查询重建整个索引。
- 只需手动搜索,主要选择器除外。
每种可能技术的成本很大程度上取决于正在执行的确切操作。 #1 在大多数情况下可能相当不错,因为大多数情况下,当您进行子选择时,您的目标是特定元素。所需的迭代次数为结果数 * 每个元素的平均深度。
第二种方法是迄今为止选择速度最快的方法,但代价是存储需求随深度呈指数增长,并且索引维护困难。我已经基本消除了这个。
第 3 种方法的内存占用相当差(尽管比第 2 种要好得多) - 这可能是合理的,但除了存储要求之外,添加和删除元素变得更加昂贵和复杂。
第 4 种方法无论如何都需要遍历整个选择,所以这似乎毫无意义,因为大多数子查询只会运行一次。仅当期望重复子查询时才有益。 (或者,我可以在遍历子集时执行此操作 - 除了某些选择器不需要搜索整个子域,例如 ID 和位置选择器)。
第 5 种方法适用于有限的子集,但比第 1 种方法适用于大部分 DOM 的子集。
关于如何最好地完成此任务有任何想法或其他想法吗?考虑到正在搜索的子集的大小与 DOM 的大小,我可以通过猜测哪个更有效来混合#1 和#4,但这很模糊,我宁愿找到一些通用的解决方案。现在我只使用#4(只有全 DOM 查询使用索引),这很好,但如果你决定做类似$('body').Find('#id') 的事情,那就太糟糕了
免责声明:这是早期优化。我没有需要解决的瓶颈,但作为一个学术问题我不能停止思考它......
解决方案
这是答案提出的数据结构的实现。可以完美地替代字典。
interface IRangeSortedDictionary<TValue>: IDictionary<string, TValue>
{
IEnumerable<string> GetRangeKeys(string subKey);
IEnumerable<TValue> GetRange(string subKey);
}
public class RangeSortedDictionary<TValue> : IRangeSortedDictionary<TValue>
{
protected SortedSet<string> Keys = new SortedSet<string>();
protected Dictionary<string,TValue> Index =
new Dictionary<string,TValue>();
public IEnumerable<string> GetRangeKeys(string subkey)
{
if (string.IsNullOrEmpty(subkey)) {
yield break;
}
// create the next possible string match
string lastKey = subkey.Substring(0,subkey.Length - 1) +
Convert.ToChar(Convert.ToInt32(subkey[subkey.Length - 1]) + 1);
foreach (var key in Keys.GetViewBetween(subkey, lastKey))
{
// GetViewBetween is inclusive, exclude the last key just in case
// there's one with the next value
if (key != lastKey)
{
yield return key;
}
}
}
public IEnumerable<TValue> GetRange(string subKey)
{
foreach (var key in GetRangeKeys(subKey))
{
yield return Index[key];
}
}
// implement dictionary interface against internal collections
}
代码在这里:http://ideone.com/UIp9R
【问题讨论】:
标签: c# data-structures