基于堆栈的树遍历的时间复杂度

时间:2011-03-17 23:30:35

标签: algorithm tree stack time-complexity traversal

下面实现二叉树遍历的时间复杂度是多少?

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 ;
         }
    }    
}

你能解释一下如何计算复杂性吗?

1 个答案:

答案 0 :(得分:4)

表征困难函数的大O复杂性的一种方法是考虑它访问的某些资源,然后限制访问该资源的次数。在这种情况下,当您进行树遍历时,您可能会考虑从堆栈中推送或弹出每个节点的次数。这是一个很好的约束的原因是这个函数的所有艰苦工作 - 通过一系列节点下行的内部循环和处理堆栈顶部的外部循环 - 可以受节点推送次数的限制到堆栈或从堆栈弹出。这是因为最外面的循环在堆栈为空时终止,因此它不能运行多次,而不是堆栈有一些东西被推到它上面,并且最里面的循环确实与某些东西被推到堆栈上的次数成正比。循环的过程。

那么让我们看看我们如何限制这些数量。第一个问题是每个节点可以添加到堆栈的次数。好吧,只有当循环开始执行时,如果节点或其左侧路径中的一个祖先是当前节点,则该节点才会添加到堆栈中。这可能发生多少次?我的主张是这种情况最多发生一次。证明这是基于树中节点深度的归纳。我们使用观察结果,如果节点是堆栈中节点的直接右子节点,则仅将节点再次选择为当前节点。作为归纳的基本情况,如果节点是根(它在深度为零),则不能再次选择它,因为它没有父节点。对于归纳步​​骤,如果确实深度为d的节点不能被选择为当前节点两次,那么深度为d + 1的节点不能被选择两次,因为深度为d + 1的节点仅在其父节点被选择时才被选择再次,但通过归纳假设我们知道这不是真的。因此,我们没有选择任何节点作为当前节点两次。我们通过简单的观察来跟进这一点,因为当前节点是根(不是左子)或者是某个节点的正确子节点,因此没有任何左子节点可以成为循环开始时的当前节点。 。这个主张与没有访问任何节点两次的事实相结合,意味着一个节点最多只被添加到队列一次,这发生在它的最左边的祖先成为当前节点时。

这也为我们提供了可能出列的数量的界限,因为出队的数量不能超过排队的数量。由于每个节点最多排队一次,因此它最多也会出列一次。为了完成这些任务,我们知道整个函数的复杂性受到所执行的队列和队列数量的限制,因此复杂度为O(n),其中n是节点数。

呼!分析并不好玩。我更喜欢递归版本。 : - )