【问题标题】:List with O(log n) indexOf operation具有 O(log n) indexOf 操作的列表
【发布时间】:2020-11-28 10:30:56
【问题描述】:

我正在寻找一种可以存储元素列表的数据结构,同时还支持 sub-O(n) 查找给定元素的索引和给定索引的元素,以及在索引处插入。

元素密集(整数0..n)且唯一,但未排序。

例如,在 Rust 中,这个数据结构会这样使用:

fn main() {
    let mut list = List::new();
    list.extend(vec![5, 2, 0, 4, 1, 3]);

    assert_eq!(list.get(2), 0);
    assert_eq!(list.get(3), 4);
    assert_eq!(list.index_of(0), 2);
    assert_eq!(list.index_of(4), 3);
}

O(√n) 操作是可以接受的,O(log n) 是理想的。我在这里画一个空白;非常感谢任何帮助!

【问题讨论】:

  • 这能回答你的问题吗? List as O(log(n))
  • @Stargateur 我发现github.com/masyamandev/indexable-set 实现了这一点。
  • @Stargateur 不,AVL 树仅根据索引进行排序,因此它确实也是元素的索引列表。因此,按索引搜索、插入和删除是 O(log n)。聪明的一点是找到一个元素的索引,为此使用了一个单独的哈希映射。 OP是正确的。
  • @Max 这是一个很好的问题,你在 github 上找到的答案是最有趣的。请考虑自己添加一个带有数据结构描述的答案,以防链接在某个时候过期。
  • @Stargateur 我检查了它,它似乎有效。我自己不会认为这是可能的,但它实际上非常简单和聪明。当然,具有 O(log n) 操作的 AVL 树在实践中比具有 O(n) 操作的简单列表的实际大 n 更有效。哈希映射根本不支持所需的操作。我确实希望 OP 自己回答这个问题!

标签: vector indexing data-structures rust time-complexity


【解决方案1】:

This library提供了一个“IndexedTreeListSet”数据结构,实现了O(log n)中所需的三个操作:

  • 查找索引 -> 元素
  • 查找元素 -> 索引(也称为索引)
  • 在索引处插入元素

作为 Mo B.notes,它使用辅助哈希图将元素映射到树中的节点,然后向上遍历到根。由于树中的每个节点都包含其相对索引,因此可以在根部计算绝对索引。

我从幼稚的方法(使用 O(n) 插入)切换到此方法,并将插入的墙执行时间(每秒发生约 100 次)从约 100 毫秒减少到约 1 毫秒。

【讨论】:

  • 出于兴趣,你有没有在 Rust 中实现过这个,如果有,它是开源的吗?出于兴趣,我正在考虑尝试一下并了解有关此数据结构的更多信息。
  • @Jason 我确实在 Rust 中实现了它,但不幸的是我还不能开源它(还没有!)
【解决方案2】:

单独维护Vec<T>HashMap<usize, T> 是否足够? HashMap 将有一个 lower lookup time 而不是 O(log n),它是 O(1)~

这样的缺点似乎是:

  • 您必须将Vec<T>HashMap<usize, T> 都存储在内存中。
  • 删除元素后,您还必须减少 HashMap<usize, T> 中每个元素的索引,这可能会增加删除成本。
use std::collections::HashMap;

struct List {
    index_to_value: Vec<i32>,
    value_to_index: HashMap<i32, usize>,
}

impl List {
    fn new<I>(index_to_value: I) -> Self
    where
        I: Into<Vec<i32>>,
    {
        let index_to_value = index_to_value.into();
        let value_to_index = index_to_value
            .iter()
            .copied()
            .enumerate()
            .map(|(index, value)| (value, index))
            .collect();

        Self {
            index_to_value,
            value_to_index,
        }
    }

    fn get(&self, n: usize) -> Option<i32> {
        self.index_to_value.get(n).copied()
    }

    fn index_of(&self, i: i32) -> Option<usize> {
        self.value_to_index.get(&i).copied()
    }
}

fn main() {
    let list = List::new(vec![5, 2, 0, 4, 1, 3]);

    assert_eq!(list.get(2), Some(0));
    assert_eq!(list.get(3), Some(4));
    assert_eq!(list.index_of(0), Some(2));
    assert_eq!(list.index_of(4), Some(3));
}

【讨论】:

  • 您似乎还需要在插入时增加 HashMap 中每个元素的索引?进行插入O(n) 以及删除。
  • 啊,正确,我忽略了“在索引处插入”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多