Algorithm 什么样的数据结构最能混合字典和列表的优点
我最近问了一个问题,这让我想知道以下几点: 存储集合(键、值)对的数据结构是什么,以便:Algorithm 什么样的数据结构最能混合字典和列表的优点,algorithm,data-structures,Algorithm,Data Structures,我最近问了一个问题,这让我想知道以下几点: 存储集合(键、值)对的数据结构是什么,以便: 它的元素是有序的 d[key]->val具有复杂性O(dict) d(索引)->(键,val)具有复杂性O(列表) 提供反向查找d{val}->(索引,键)的复杂性O(dict) 使用尽可能少的存储空间 当我键入O(type)时,我的意思是操作的复杂性与数据结构type相同 例如,如果有序集合为: c = {key_1:val_1, key_2:val_2, key_3:val_3} 我想获得 c[
- 它的元素是有序的
具有复杂性d[key]->val
O(dict)
具有复杂性d(索引)->(键,val)
O(列表)
- 提供反向查找
的复杂性d{val}->(索引,键)
O(dict)
- 使用尽可能少的存储空间
O(type)
时,我的意思是操作的复杂性与数据结构type
相同
例如,如果有序集合为:
c = {key_1:val_1, key_2:val_2, key_3:val_3}
我想获得
c[key_1] # returns val_1, as in a dictionary
c(1) # returns val_2, as in a list
c{val_3} # returns (2, key_3) as in a sort of "indexed dictionary"
您要求按键和索引进行O(1)查找,以及O(1)值查找。您可以通过维护键/值的哈希数据结构、反向查找的第二个哈希结构以及有序列表->键映射的列表数据结构来实现这一点。不过,您仍将有O(n)个插入和删除,而且您的空间复杂性将是正常情况下的3倍
如果你愿意在速度上妥协,你可以节省空间,有很多基于树的集合数据结构(例如Java中的TreeSet),它们的操作具有复杂性日志(n)
这总是一种折衷您要求按键和索引进行O(1)查找,以及O(1)值查找。您可以通过维护键/值的哈希数据结构、反向查找的第二个哈希结构以及有序列表->键映射的列表数据结构来实现这一点。不过,您仍将有O(n)个插入和删除,而且您的空间复杂性将是正常情况下的3倍
如果你愿意在速度上妥协,你可以节省空间,有很多基于树的集合数据结构(例如Java中的TreeSet),它们的操作具有复杂性日志(n)
这始终是一种权衡您没有提到插入成本,这也是一个重要的问题。您可以使用按词典顺序排列的词典来完成这项工作,并使用二进制搜索(即
log(n)
)来处理查找。但是,你需要维护两个这样的结构,一个是KEY-VALL,一个是Val->key,所以插入成本将是两倍,而在中间插入元素的需要使得在<代码> O(n)< /代码>(即,与列表相同)。 你没有提到插入成本,这也是一个重要的问题。您可以使用按词典顺序排列的词典来完成这项工作,并使用二进制搜索(即
log(n)
)来处理查找。但是,你需要维护两个这样的结构,一个是KEY-VALL,一个是Val->key,所以插入成本会增加一倍,而在中间插入元素的需要会导致在<代码> O(n)< /代码>(即,与列表相同)。< P>我也遇到了同样的问题。因此,我获取了java.util.TreeMap的源代码并编写了IndexedTreeMap。它实现了我自己的索引导航地图:
public interface IndexedNavigableMap<K, V> extends NavigableMap<K, V> {
K exactKey(int index);
Entry<K, V> exactEntry(int index);
int keyIndex(K k);
}
public interface IndexedNavigableMap扩展了NavigableMap{
K精确键(int索引);
条目精确条目(int索引);
int键索引(K);
}
该实现基于在红黑树发生更改时更新节点权重。权重是给定节点下的子节点数加上一个self。例如,当树向左旋转时:
private void rotateLeft(Entry<K, V> p) {
if (p != null) {
Entry<K, V> r = p.right;
int delta = getWeight(r.left) - getWeight(p.right);
p.right = r.left;
p.updateWeight(delta);
if (r.left != null) {
r.left.parent = p;
}
r.parent = p.parent;
if (p.parent == null) {
root = r;
} else if (p.parent.left == p) {
delta = getWeight(r) - getWeight(p.parent.left);
p.parent.left = r;
p.parent.updateWeight(delta);
} else {
delta = getWeight(r) - getWeight(p.parent.right);
p.parent.right = r;
p.parent.updateWeight(delta);
}
delta = getWeight(p) - getWeight(r.left);
r.left = p;
r.updateWeight(delta);
p.parent = r;
}
}
private void rotateLeft(条目p){
如果(p!=null){
条目r=右侧;
int delta=getWeight(左)-getWeight(右);
p、 右=右=左;
p、 更新视图(增量);
如果(右左!=null){
r、 left.parent=p;
}
r、 父母=父母;
如果(p.parent==null){
根=r;
}else if(p.parent.left==p){
delta=getWeight(r)-getWeight(p.parent.left);
p、 parent.left=r;
p、 parent.updatewight(delta);
}否则{
delta=getWeight(r)-getWeight(p.parent.right);
p、 parent.right=r;
p、 parent.updatewight(delta);
}
delta=重量(p)-重量(r.左);
r、 左=p;
r、 更新视图(增量);
p、 父代=r;
}
}
UpdateView只更新根目录下的权重:
void updateWeight(int delta) {
weight += delta;
Entry<K, V> p = parent;
while (p != null) {
p.weight += delta;
p = p.parent;
}
}
void updatewight(int delta){
重量+=增量;
条目p=父项;
while(p!=null){
p、 重量+=增量;
p=p.parent;
}
}
当我们需要通过索引查找元素时,这里是使用权重的实现:
public K exactKey(int index) {
if (index < 0 || index > size() - 1) {
throw new ArrayIndexOutOfBoundsException();
}
return getExactKey(root, index);
}
private K getExactKey(Entry<K, V> e, int index) {
if (e.left == null && index == 0) {
return e.key;
}
if (e.left == null && e.right == null) {
return e.key;
}
if (e.left != null && e.left.weight > index) {
return getExactKey(e.left, index);
}
if (e.left != null && e.left.weight == index) {
return e.key;
}
return getExactKey(e.right, index - (e.left == null ? 0 : e.left.weight) - 1);
}
public K exactKey(int索引){
如果(索引<0 | |索引>大小()-1){
将新的ArrayIndexOutOfBoundsException()抛出;
}
返回getExactKey(根,索引);
}
私有K getExactKey(条目e,int索引){
if(e.left==null&&index==0){
返回e.key;
}
if(e.left==null&&e.right==null){
返回e.key;
}
if(e.left!=null&&e.left.weight>index){
返回getExactKey(如左,索引);
}
if(e.left!=null&&e.left.weight==index){
返回e.key;
}
返回getExactKey(e.right,index-(e.left==null?0:e.left.weight)-1);
}
查找键的索引也非常方便:
public int keyIndex(K key) {
if (key == null) {
throw new NullPointerException();
}
Entry<K, V> e = getEntry(key);
if (e == null) {
throw new NullPointerException();
}
if (e == root) {
return getWeight(e) - getWeight(e.right) - 1;//index to return
}
int index = 0;
int cmp;
if (e.left != null) {
index += getWeight(e.left);
}
Entry<K, V> p = e.parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
while (p != null) {
cmp = cpr.compare(key, p.key);
if (cmp > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
} else {
Comparable<? super K> k = (Comparable<? super K>) key;
while (p != null) {
if (k.compareTo(p.key) > 0) {
index += getWeight(p.left) + 1;
}
p = p.parent;
}
}
return index;
}
public int-keyIndex(K键){
if(key==null){
抛出新的NullPointerException();
}
条目e=获取条目(键);
如果(e==null){
抛出新的NullPointerException();
}
if(e==根){
return getWeight(e)-getWeight(e.right)-1;//要返回的索引
}
int指数=0;
int-cmp;
如果(e.left!=null){
索引+=获取权重(如左);
}
条目p=e.parent;
//拆分比较器和可比较路径
Comparator我也有同样的问题,所以我使用java.util.TreeMap的源代码编写了IndexedTreeMap.I