Algorithm 具有固定时间后继者和前继者的平衡树(给定节点指针)?

Algorithm 具有固定时间后继者和前继者的平衡树(给定节点指针)?,algorithm,data-structures,avl-tree,Algorithm,Data Structures,Avl Tree,有人问我这个问题,我个人觉得很难: 创建一个数据结构,该结构可以: 插入元素, 移除元素, 搜索元素, 时间O(日志n) 另外,, 它应具有以下两个功能,在时间O(1)内工作: 下一(x): 给定一个指向值为x的节点的指针,返回一个指向值大于x的最小节点的指针 上一次(x) 给定一个指向值为x的节点的指针,返回一个指向值大于x的节点的指针 如果您的元素是整数,您可以使用它来支持O(log m)中提到的所有操作。此外,几乎任何搜索树都允许在O(logn)时间内执行这些操作,只需先上后下(但这需要大

有人问我这个问题,我个人觉得很难:

创建一个数据结构,该结构可以:

插入元素, 移除元素, 搜索元素, 时间O(日志n)

另外,, 它应具有以下两个功能,在时间O(1)内工作:

  • 下一(x): 给定一个指向值为x的节点的指针,返回一个指向值大于x的最小节点的指针

  • 上一次(x) 给定一个指向值为x的节点的指针,返回一个指向值大于x的节点的指针


  • 如果您的元素是整数,您可以使用它来支持O(log m)中提到的所有操作。此外,几乎任何搜索树都允许在O(logn)时间内执行这些操作,只需先上后下(但这需要大量精力才能不打乱顺序)

    与大多数自平衡树一样,a提供了O(logn)时间复杂度的插入、删除和搜索操作

    在B+树中,叶节点在一个数组中承载多个键,因此“指向具有值x的节点的指针”的概念实际上并不存在,但我们可以将其定义为元组(指针,索引),其中指针指向节点,索引是存储x的槽

    在B+树中,最底层的节点包含所有键,这些节点通常是链接的,通常仅在正向(即向右)链接,但也很可能在反向保持链接,而不会增加上述操作的时间复杂度


    记住这两点,显然可以在O(1)时间内执行prev-next操作。

    如果每个节点包含一个指向其后续节点的指针和一个指向其前置节点的指针,或者等效地-如果您同时维护一个双链接列表和一个树,树中的每个节点都指向列表中的等效节点,反之亦然-您将得到您想要的。在插入/删除时更新列表是O(1)(在树中找到最近的节点之后)。在树上执行搜索。成功者/前任者在列表上执行。

    @RogerLindsjö从评论中得出的想法是好的。基本上,保持一个规则的、平衡的BST,然后在节点之间穿行一个双链接列表,保持它们的排序顺序。这样,给定一个指向树中某个节点的指针,只需跟随每个节点中的下一个或上一个指针,就可以找到小于它的最大值或大于它的最小值

    您可以通过插入和删除来维护此列表,而无需更改插入或删除的总体运行时。例如,以下是插入元素x的方法:

  • 执行标准BST后续查询以查找树中大于x的最小值,执行标准BST前置查询以查找树中小于x的最大值。每次搜索都需要时间O(日志n)才能完成
  • 执行常规BST插入以插入x。然后,将其下一个和上一个指针设置为在上一步中找到的两个元素,并更新这些节点以指向新节点x。这也需要时间O(logn)
  • 然后插入的总时间为O(logn),与平衡树所能提供的时间相匹配


    我将留给您来解决删除问题,它同样可以在不改变操作总体成本的情况下维护链表指针。

    它说后续和前置访问需要O(log M)时间,而不是O(1)。虽然这是非常有效的,但我不认为它符合O(1)。好吧,是的,但在现实生活中,我严重怀疑是否有可能使所有提到的时间都是常数,所以这个常数将小于y-fast trie中的log。对于列表中的10^9项,log n将<1它的M,而不是n,因此时间复杂度取决于最大值,而不是值的数量。它仍然是一个增长非常缓慢的函数。更重要的问题是,插入和删除只是摊销不足,但它们的实际时间可能高达O(n),因此它们不适合实时应用程序。“我严重怀疑是否有可能将所有提到的内容都设置为常数时间”线程二叉树为您提供O(1)访问下一个和上一个以及O(logn)插入的权限,删除和搜索。这难道不能实现为一个常规的AVL树,其中每个节点不仅指向左侧和右侧,还指向下一个和上一个节点吗?在添加/删除过程中更新下一个/上一个?是的,我也这么想。但是如何更新add/remove函数,使每个节点始终指向下一个和上一个节点?把它们保持在O(长)?更进一步,我想为每个节点保留每个子树的最大值和最小值。如果计算左子树的最大值和右子树的最小值,则显然会返回前导树和后继树。但是,您如何存储它们以确保保持O(logn)。对于不同时具有两个子节点的节点,您会怎么做?@saraspagno:请记住O(50 logn)=O(logn)。因此,如果需要,您可以在不丢失O(logn)保证的情况下进行大量记账。因此,在执行实际插入之前,
    add
    方法可以在logn时间中找到即将成为前一个或即将成为下一个元素。(而且
    remove
    方法更简单,因为节点本身知道需要更新的邻居是什么,就像使用双链接列表一样。)@RogerLindsjö也许不是一个“常规”的AVL树,但将AVL树变成一棵树是很容易的。为什么标题中提到“AVL”,当您的问题询问数据结构时?在尝试定位要插入的位置时,只需保持左、右看到最后一个节点即可。