【问题标题】:Time complexity of a stack-based tree traversal基于堆栈的树遍历的时间复杂度
【发布时间】:2011-03-17 23:30:35
【问题描述】:

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

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

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

【问题讨论】:

    标签: algorithm tree stack time-complexity traversal


    【解决方案1】:

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

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

    这也给了我们可能的出队数量的界限,因为出队的数量不能超过入队的数量。由于每个节点最多入队一次,因此它也最多出队一次。最后,我们知道整个函数的复杂度受限于执行的入队和出队的数量,因此复杂度为 O(n),其中 n 是节点数。

    哇!分析起来并不有趣。我更喜欢递归版本。 :-)

    【讨论】:

      猜你喜欢
      • 2016-07-14
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      • 2015-02-16
      • 2021-08-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多