Algorithm 在BST中寻找k个后继子的时间复杂度

Algorithm 在BST中寻找k个后继子的时间复杂度,algorithm,binary-search-tree,Algorithm,Binary Search Tree,给定高度为h的二元搜索树(BST),需要O(k+h)时间来连续应用k次,从任何节点开始,在上一次调用返回的节点上应用下一次调用 伪代码: get_kth_successor(node): for times = 1 to k: node = successor(node) return node 我如何证明这种时间复杂性 特别是,我试图在k和访问的节点数之间建立一个关系,但在这里找不到任何模式。这里有一个非常简单的算法来实现这一点 创建一个空堆栈S和一个变量cu

给定高度为h的二元搜索树(BST),需要O(k+h)时间来连续应用k次,从任何节点开始,在上一次调用返回的节点上应用下一次调用

伪代码:

get_kth_successor(node):
    for times = 1 to k:
        node = successor(node)
    return node
我如何证明这种时间复杂性


特别是,我试图在k和访问的节点数之间建立一个关系,但在这里找不到任何模式。

这里有一个非常简单的算法来实现这一点

创建一个空堆栈
S
和一个变量
curr=NULL

  • 在树中查找起始节点。另外,将它的所有祖先(以及节点本身)推入堆栈
    s
  • 现在从堆栈
    S
    中弹出一个节点
    top
    ,检查
    curr
    是否为正确的子节点。如果不是,则按顺序遍历它的右子树
    • 如果我们在遍历时发现k个或更多节点,我们就完成了
  • 如果我们还没有发现k个后续节点,请设置
    curr=top
    ,然后重复2次,直到找到k个节点

  • 总体时间完整性为
    O(h+k)
    。步骤1需要
    O(h)
    时间。步骤2需要
    O(h+k)
    time(步骤2的所有迭代组合需要
    O(h+k)
    time.)

    获取关于后续遍历的以下事实:

  • 您可以遍历分支不超过两次:向下和向上

  • 分支机构的每两次访问都对应于找到至少一个后续机构:当您通过分支机构向上回溯时,您在向下方向上访问的后续机构将至少比您第一次通过同一分支机构时多

  • 只遍历一次的分支数不能超过2h。最糟糕的情况是,从树左下方的一片叶子开始,必须一直到根(后续叶),然后再到底部叶子,才能找到根的后续叶。但是如果在此之后需要更多的后续分支,则必须再次访问其中一些分支(回溯),然后才能首次遍历其他分支。因此,仅遍历一次的分支总数不能增加到2h以上

  • 因此,要找到k后续分支,您最多将k分支遍历两次(向下和向上,参见第2点)和2h分支遍历一次(第3点),这归结为最坏情况下的分支遍历计数2k+2h,即O(h+k).

    我将为这个问题编写完整的实现,以便很容易证明我关于所花时间的论点

    假设BST的每个节点具有以下结构:

    typedef struct node{
        int vale;
        struct node* left;
        struct node* right;
    }node;
    
    该算法将有两个步骤:

    a.从树的根节点开始,找到起始节点和该节点的所有祖先。将所有这些存储在传递的堆栈中:

    //root -> root node of the tree.
    //val  -> value of the node to find.
    // s   -> stack to store all ancestor.
    node* find_node(node* root, int val,std::stack<node*> &s)
    {
        if(root != NULL)  s.push(root);
        if(root == NULL || root->value == val) return root;
        if(root->value > val) return find_node(root->left);
        else return find_node(root->right);
    
    
    }
    
    时间分析:

  • 方法
    find_node
    O(h),因为它可以达到最大根到叶路径的长度
  • 方法
    print_k_biger
    O(h+k)因为在循环的每次迭代中,堆栈的大小在减小,或者k的值在减小。请注意,当从while循环内部调用inorder()时,它不会增加额外的开销,因为对inorder()的所有调用都将占用maxO(k)
    你的意思是从任何一个节点开始,找到k个后继节点,或者找到任意k个节点的后继节点?你连续调用k个是为了什么???这是什么树后继算法。请给出一个清晰的解释1.)从任何节点开始并找到k个后续节点。2.)树后继者算法在BST中查找给定节点的后继者。我调用此过程k次。@Gameofches,因此您有k个节点,并且希望为所有k个给定节点查找后继者。我说的对吗?@GameOfChess,我对你的问题做了不少编辑,希望它能进一步澄清。你能检查一下这确实反映了你的问题吗?看来你的解释需要改进。如果先前弹出的元素是当前弹出元素的正确子元素,该怎么办?请查看我的答案以获得更好的解释@Shasha99你是对的。不过这只是一张微不足道的支票。更新了我的答案。这是一个很好的答案,但我认为它太详细了,可能会让OP感到困惑,他只是想了解时间复杂性。我也不认为OP在寻找实现或如何完成工作,尽管提供代码可能有助于了解时间复杂性。对
    ptr->right
    的第一个引用可能需要是
    prev->right
    。短语“它不会增加额外的开销,因为对inoorder()的所有调用都将以max O(k)的形式进行”,这一点很重要,我认为OP将重点关注这一方面。但我不是OP:-)我投你一票。
    //Assuming that the root is the root node of the tree.
    std::stack<node*> s;
    node* ptr = find_node(root,s); // we have all ancestors of ptr along with ptr in stack s now.
    
    // s  -> stack of all ancestor of the node.
    // k  -> k-successor to find.
    void print_k_bigger(stack<node*> s, int k)
    {
        //since the top element of stack is the starting node. So we won't print it.
        // We will just pop the first node and perform inorder traversal on its right child.
        prev = s.top();
        s.pop();
        inorder(prev->right,k);
    
        // Now all the nodes present in the stack are ancestor of the node. 
        while(!s.empty() && k>0)
        {
            //pop the node at the top of stack.
            ptr = s.top();
            s.pop();
    
            //if the node popped previously (prev) was the right child of the current 
            //node, i.e. prev was bigger than the current node. So we will have to go 
            //one more level up to search for successors. 
            if(prev == ptr->right) continue;
    
            //else the current node is immidiate bigger than prev node. Print it.
            printf("%d",ptr->value);
    
            //reduce the count.
            k--;
    
            //inorder the right subtree of the current node.
            inorder(ptr->right);
    
            //Note this.
            prev = ptr;
    
        }
    
    }
    
    void inorder(node* ptr, int& k)
    {
        if(ptr != NULL)
        {
            inorder(ptr->left,k);
            printf("%d",ptr->value);
            k--;
            inorder(ptr->right,k);
        }
    }