我应该使用什么数据结构进行这些操作?

时间:2017-07-11 04:01:15

标签: algorithm data-structures amortized-analysis

我需要一个存储子集的数据结构 - 称之为{1 ,. 。 。 ,n}(n最初给出) 并支持这些操作:

•最初:给出n,S = {1 ,. 。 。 ,n}在开头。

•删除(i):从S中删除i。如果我已经不在S中,则无效。

•pred(i):返回i中S的前身。这意味着max {j∈S| j<我是S中最伟大的元素 这严格低于我。如果没有,则返回0.参数i保证在{1 ,. 。 。 ,n}, 但可能会或可能不会在S。

例如,如果n = 7且S = {1,3,6,7},则pred(1)返回0,pred(2)和pred(3)返回1.

我需要弄明白:

  • 表示S
  • 的数据结构
  • 初始化算法(O(n)时间)
  • 删除算法(O(α(n))摊销时间)
  • pred(O(α(n))摊销时间的算法

感谢任何帮助(我不需要代码 - 只需算法)。

3 个答案:

答案 0 :(得分:5)

您可以使用Disjoint-set data structure

让我们将子集表示为不相交集。不相交集的每个元素是子集driver.find_elements_by_partial_link_text("Wat Chedi Luang").click() 的元素(包括始终呈现零)与集合中所有缺少的元素联合,该元素大于i且小于下一个集合元素。

示例:

i

最初,我们有一个由n = 10 s = [1, 4, 7, 8], disjoint-set = [{0}, {1,2,3}, {4,5,6}, {7}, {8, 9, 10}] s = [3, 5, 6, 10], disjoint-set = [{0, 1, 2}, {3, 4}, {5}, {6, 7, 8, 9}, {10}] 不相交集元素表示的完整集(包括零)。通常,每个disjoint-set元素都是一个有根的树,我们可以在每个树根的元素中存储n+1个数字。

我们leftmost是包含leftmost(i)的不相交集元素的leftmost值。

i操作类似于不相交集的Find操作。我们只是从leftmost(i)转到元素的根,并返回为根存储的i数字。 复杂性leftmost

我们可以检查O(α(n))i比较的子集中是否i。如果它们相等(leftmost(i)),那么i > 0就在子集中。

如果i不在子集中,则

pred(i)将等于leftmost(i),如果i在子集中,则leftmost(i-1)等于i复杂性O(α(n))

在每个delete(i)操作中,我们首先检查i是否在子集中。如果i在子集中,我们应该将包含i的元素与左邻居元素(这是包含i-1的元素)结合起来。此操作类似于不相交集的Union操作。结果树的leftmost个数将等于leftmost(i-1)复杂性O(α(n))

编辑:我刚才注意到问题中“严格少于我”,稍微改了一下说明。

答案 1 :(得分:1)

我不确定是否存在可以在O(α(n))时间内保证所有这些属性的数据结构,但是良好的开端将是诸如van Emde Boas trees或{{3}之类的前任数据结构}

vEB树的工作原理是基于元素索引的二进制表示来递归定义的。假设对于某些b = 2 ^ k

,n = 2 ^ b
  • 如果我们只有两个元素,请存储最小值和最大值

  • 否则,我们将所有元素的二进制表示分为上部和下部b / 2位 我们为所有元素的高位构建vEB树('summary'),为低位构建√nvBE树(每个高位的选择一个)。此外,我们存储最小和最大元素。

这为您提供了O(n)空间使用和O(log log n)= O(k)搜索,插入和删除的时间。
但请注意,所涉及的常数因素可能非常大。如果您的n代表32位,至少我知道Dementiev等人的y-fast tries。当问题大小可以通过其他技术更容易解决时打破递归

y-fast尝试的想法建立在x-fast尝试上:
它们最简单地描述为基于其元素的二进制表示的trie,结合每个级别的哈希表和一些额外的指针。

y-fast尝试通过在几乎相等大小的分区中拆分元素并从中选择代表(最大值)来减少空间使用,在其上构建x-fast trie。然后使用常规平衡搜索树实现分区内的搜索。

空间使用和时间复杂度与vEB相当。我猜这些常数因素比vEB的天真实现要小一些,但声称只是基于直觉。

最后一点:始终牢记日志日志n< 6 ,在不久的将来可能不会改变

答案 2 :(得分:0)

在提供O(α(n))时间方面,它确实变得棘手。以下是我对此的看法:

  1. 由于我们知道的i范围是1n,我们可以先形成一个自平衡的BST,如AVL tree。该AVL树的节点应该是DataNode的对象。这是它的样子:

    public class DataNode{
    int value;
    boolean type;
    DataNode(int value, boolean type){
        this.value = value;
        this.type = type;
    
        }
    }
    

    value只包含范围1到n中的所有值。如果我们在树中插入的项目存在于集合S中,则type变量将被赋值为true。如果不是,则将其标记为false

  2. 创建需要O(n)时间。删除可以在O(logn)时间内完成。 对于pred(i),如果我是正确的,我们可以将平均时间复杂度设置为O(logn)左右。 pred(i)的算法应该是这样的:

    1. 在树中找到元素i。如果type为true,则返回此元素i的inorder前导码,如果此前一个的类型值为true
    2. 如果是false,则重复此元素的下一个前任(即i-1的前身),直到我们找到一个type = true的元素。
    3. 如果找不到type = true这样的前任,则返回0。
    4. 我希望我们可以进一步优化这种方法,使其成为对于pred(i)的O(α(n))。