排序哈希表(map、dictionary)数据结构设计
Posted
技术标签:
【中文标题】排序哈希表(map、dictionary)数据结构设计【英文标题】:Sorted hash table (map, dictionary) data structure design 【发布时间】:2011-01-01 16:59:49 【问题描述】:下面是数据结构的说明:
它的操作类似于带有get
、put
和remove
方法的常规地图,但有一个可以调用的sort
方法来对地图进行排序。但是,地图记住它的排序结构,因此后续调用 sort 会更快(如果在调用 sort
之间结构没有太大变化)。
例如:
我调用put
方法 1,000,000 次。
我调用sort
方法。
我再调用put
方法100 次。
我调用了sort
方法。
我第二次调用sort
方法应该是一个更快的操作,因为地图的结构没有太大变化。请注意,地图不必在对sort
的调用之间保持排序顺序。
我知道这可能是不可能的,但我希望 O(1) get
、put
和 remove
操作。 TreeMap 之类的东西为这些操作提供了有保证的 O(log(n)) 时间成本,但始终保持排序顺序(没有 sort
方法)。
那么这个数据结构是怎么设计的呢?
编辑 1 - 返回前 K 个条目
虽然我很高兴听到上述一般案例的答案,但我的用例变得更加具体:我不需要对整个事情进行排序;只是前 K 个元素。
Data structure for efficiently returning the top-K entries of a hash table (map, dictionary)
谢谢!
【问题讨论】:
【参考方案1】:对于“O(1) 获取、放置和删除操作”,您基本上需要 O(1) 查找,这意味着一个散列函数(如您所知),但一个好的散列函数的要求通常会打破要求很容易排序。 (如果您有一个哈希表,其中相邻值映射到同一个存储桶,它会在大量常见数据上退化为 O(N),这是您通常希望哈希函数避免的更糟糕的情况。)
我能想到如何让你完成 90% 的路程。在已排序的并行索引旁边设置一个哈希表。索引有一个干净的部分(有序)和一个脏的部分(无序)。索引会将键映射到值(或对存储在哈希表中的值的引用 - 在性能或内存使用方面适合您)。当您添加到哈希表时,新条目被推到脏列表的后面。当您从哈希表中删除时,该条目将从索引的干净和脏部分中删除/删除。您可以对索引进行排序,它只对脏条目进行排序,然后将它们合并到索引中已经排序的“干净”部分。显然你可以遍历索引。
据我所知,除了删除操作之外,这在任何地方都为您提供 O(1),并且使用标准容器(至少 C++、Java 或 Python 提供)实现起来仍然相当简单。它还为您提供“第二次排序更便宜”的条件,只需对脏索引条目进行排序,然后让您进行 O(N) 合并。这一切的代价显然是索引的额外内存和使用它时的额外间接性。
【讨论】:
您也可以在删除时获得 O(1),方法是让哈希表条目包含指向索引中相应位置的链接。【参考方案2】:为什么需要 sort() 函数? 您可能想要和需要的是一棵红黑树。
http://en.wikipedia.org/wiki/Red-black_tree
这些树自动通过您提供的比较器对您的输入进行排序。它们很复杂,但具有出色的 O(n) 特性。将您的树条目作为键与哈希耦合 映射为字典,你就得到了你的数据结构。
在 Java 中,它被实现为 TreeMap 作为 SortedMap 的实例。
【讨论】:
【参考方案3】:我不知道是否有名称,但您可以将每个项目的当前索引存储在哈希中。
也就是说,你有一个HashMap< Object, Pair( Integer, Object ) >
和一个List<Object>
对象
当您put
时,添加到列表的尾部或头部,并使用您的数据和插入索引插入到哈希图中。这是O(1)
。
当您get
时,从哈希图中拉出并忽略索引。这是O(1)
。
当您remove
时,您从地图中拉出。获取索引并从列表中删除。这是O(1)
当您sort
时,只需对列表进行排序。在排序期间更新映射中的索引,或者在排序完成后更新。这不会影响O(nlgn)
排序,因为它是一个线性步骤。 O(nlgn + n) == O(nlgn)
【讨论】:
我不认为remove
是O(1)
...当您调用remove
时,它会从列表中删除并弄乱索引。
仅仅因为你remove
来自排序列表的对象并不意味着它仍然没有排序......即。 [3、5、6]。列表中没有 4,但它仍然正确排序......所以排序只需要在 put
之后发生。【参考方案4】:
有序字典
最新版本的 Python(2.7、3.1)具有“有序字典”,听起来就像您所描述的那样。
官方 Python“有序字典”实现的灵感来自之前的第 3 方实现,如 PEP 372 中所述。
参考资料:
collections.OrderedDict
documentation for Python 2.7
collections.OrderedDict
documentation for Python 3.1
PEP 372
ActiveState Ordered Dictionary recipe 适用于 Python ≥ 2.4
【讨论】:
OrderedDict 以插入顺序维护条目,而不是自然键排序顺序。 是的,但它是可排序的,如 Python 文档中所述。这似乎非常适合问题中的描述。【参考方案5】:您正在查看的是一个哈希表,其中条目中的指针按排序顺序指向下一个条目。它很像 java 中的 LinkedHashMap,只是链接跟踪的是排序顺序而不是插入顺序。实际上,您可以通过包装 LinkedHashMap 并让排序实现将条目从 LinkedHashMap 转移到 TreeMap 中,然后再返回到 LinkedHashMap 中来完全实现这一点。
这是一个对数组列表中的条目进行排序而不是转移到树形图的实现。我认为 Collection.sort 使用的排序算法可以很好地将新条目合并到已经排序的部分。
public class SortaSortedMap<K extends Comparable<K>,V> implements Map<K,V>
private LinkedHashMap<K,V> innerMap;
public SortaSortedMap()
this.innerMap = new LinkedHashMap<K,V>();
public SortaSortedMap(Map<K,V> map)
this.innerMap = new LinkedHashMap<K,V>(map);
public Collection<V> values()
return innerMap.values();
public int size()
return innerMap.size();
public V remove(Object key)
return innerMap.remove(key);
public V put(K key, V value)
return innerMap.put(key, value);
public Set<K> keySet()
return innerMap.keySet();
public boolean isEmpty()
return innerMap.isEmpty();
public Set<Entry<K, V>> entrySet()
return innerMap.entrySet();
public boolean containsKey(Object key)
return innerMap.containsKey(key);
public V get(Object key)
return innerMap.get(key);
public boolean containsValue(Object value)
return innerMap.containsValue(value);
public void clear()
innerMap.clear();
public void putAll(Map<? extends K, ? extends V> m)
innerMap.putAll(m);
public void sort()
List<Map.Entry<K,V>> entries = new ArrayList<Map.Entry<K,V>>(innerMap.entrySet());
Collections.sort(entries, new KeyComparator());
LinkedHashMap<K,V> newMap = new LinkedHashMap<K,V>();
for (Map.Entry<K,V> e: entries)
newMap.put(e.getKey(), e.getValue());
innerMap = newMap;
private class KeyComparator implements Comparator<Map.Entry<K,V>>
public int compare(Entry<K, V> o1, Entry<K, V> o2)
return o1.getKey().compareTo(o2.getKey());
【讨论】:
【参考方案6】:我不知道具有这种确切行为的数据结构分类,至少在 Java 集合(或非线性数据结构类)中没有。也许你可以实现它,以后它就会被称为RudigerMap
。
【讨论】:
可能是某种混合哈希图/树图?插入到哈希图中,排序然后将哈希图项传输到树图中。不知道你将如何获得 O(1) 的移除以及...嗯..以上是关于排序哈希表(map、dictionary)数据结构设计的主要内容,如果未能解决你的问题,请参考以下文章