Algorithm 在BST中寻找k个后继子的时间复杂度
给定高度为h的二元搜索树(BST),需要O(k+h)时间来连续应用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
get_kth_successor(node):
for times = 1 to k:
node = successor(node)
return node
我如何证明这种时间复杂性
特别是,我试图在k和访问的节点数之间建立一个关系,但在这里找不到任何模式。这里有一个非常简单的算法来实现这一点 创建一个空堆栈
S
和一个变量curr=NULL
s
S
中弹出一个节点top
,检查curr
是否为正确的子节点。如果不是,则按顺序遍历它的右子树
- 如果我们在遍历时发现k个或更多节点,我们就完成了
curr=top
,然后重复2次,直到找到k个节点总体时间完整性为
O(h+k)
。步骤1需要O(h)
时间。步骤2需要O(h+k)
time(步骤2的所有迭代组合需要O(h+k)
time.) 获取关于后续遍历的以下事实:
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);
}
}