下面实现二叉树遍历的时间复杂度是多少?
void Tree::nonRecInOrder()
{
// nonrecursive inOrder Traversal using Stack
Stack< TreeNode* > s ; // declare and initialize stack
TreeNode* currentNode = root ;
while( true )
{
while( currentNode )
{
// move down leftChild fields
s.add( currentNode ) ;
currentNode = currentNode->leftChild ;
}
if( ! s.isEmpty() ) // stack is not empty
{
currentNode = *s.del( currentNode ) ; // delete from stack
cout << currentNode->data ;
currentNode = currentNode->rightChild ;
}
else
{
break ;
}
}
}
你能解释一下如何计算复杂性吗?
答案 0 :(得分:4)
表征困难函数的大O复杂性的一种方法是考虑它访问的某些资源,然后限制访问该资源的次数。在这种情况下,当您进行树遍历时,您可能会考虑从堆栈中推送或弹出每个节点的次数。这是一个很好的约束的原因是这个函数的所有艰苦工作 - 通过一系列节点下行的内部循环和处理堆栈顶部的外部循环 - 可以受节点推送次数的限制到堆栈或从堆栈弹出。这是因为最外面的循环在堆栈为空时终止,因此它不能运行多次,而不是堆栈有一些东西被推到它上面,并且最里面的循环确实与某些东西被推到堆栈上的次数成正比。循环的过程。
那么让我们看看我们如何限制这些数量。第一个问题是每个节点可以添加到堆栈的次数。好吧,只有当循环开始执行时,如果节点或其左侧路径中的一个祖先是当前节点,则该节点才会添加到堆栈中。这可能发生多少次?我的主张是这种情况最多发生一次。证明这是基于树中节点深度的归纳。我们使用观察结果,如果节点是堆栈中节点的直接右子节点,则仅将节点再次选择为当前节点。作为归纳的基本情况,如果节点是根(它在深度为零),则不能再次选择它,因为它没有父节点。对于归纳步骤,如果确实深度为d的节点不能被选择为当前节点两次,那么深度为d + 1的节点不能被选择两次,因为深度为d + 1的节点仅在其父节点被选择时才被选择再次,但通过归纳假设我们知道这不是真的。因此,我们没有选择任何节点作为当前节点两次。我们通过简单的观察来跟进这一点,因为当前节点是根(不是左子)或者是某个节点的正确子节点,因此没有任何左子节点可以成为循环开始时的当前节点。 。这个主张与没有访问任何节点两次的事实相结合,意味着一个节点最多只被添加到队列一次,这发生在它的最左边的祖先成为当前节点时。
这也为我们提供了可能出列的数量的界限,因为出队的数量不能超过排队的数量。由于每个节点最多排队一次,因此它最多也会出列一次。为了完成这些任务,我们知道整个函数的复杂性受到所执行的队列和队列数量的限制,因此复杂度为O(n),其中n是节点数。
呼!分析并不好玩。我更喜欢递归版本。 : - )