• 二叉树非递归遍历


    概要

    数据结构中,树是相对较为复杂的一种数据结构,但也尤其重要,特别是二叉树。该文主要记录二叉树的几种遍历:前序遍历、中序遍历、后序遍历以及层序遍历。介绍各种遍历方式前,先要明确二叉树是:树上的任何一个节点,最多只可能有1个父节点(当然也有无父节点的 -------- 即:根节点),同时也最多只有2个子节点(这两个子节点分别为:左子节点与右子节点,当然也有可能没有子节点的 -------- 即:叶子节点)。其实二叉树它就是一棵树,只是节点的子节点个数受到约束的一种特殊的树。因此,二叉树也是递归定义的。

    前序遍历

    前序遍历是优先访问树的当前节点,之后按顺序分别访问该当前节点的左、右两个节点。假设 node 为二叉树中的任一节点,则该节点的前序访问顺序一定是: visit(node) ---> visit(node.left) ---> visit(node.right)。根据二叉树的递归定义,当访问 visit(node.left) 或 visit(node.right) 时,其实是与访问 visit(node) 是一样的。因此,这是典型的递归处理。因此,前序遍历的递归实现参考如下:

    1 void TraversalBinaryTree::doPreOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
    2     if (nullptr != pTree && nullptr != printer) {
    3         printer(*pTree);  // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    4         this->doPreOrderRecursion(pTree->pLeft, printer);
    5         this->doPreOrderRecursion(pTree->pRight, printer);
    6     }
    7 }

    递归实现代码简洁、通俗易懂,但效率肯定会有一定折扣,因此,下面介绍非递归实现版本。根据前序遍历的定义,对于任一节点(假设节点为 node),总是优先访问节点本身,而后再按顺序访问该节点的左子节点(假设为 node.left)与右子节点(假设为 node.right)。因此,利用一个递归栈,在访问完节点 node 后,先将 node.right 压入栈,再压入 node.left。这样即可保证在访问完 node 后,接着先访问 node 的左子节点,而后再右子节点。然后再处理 node.left 节点时,其实与访问 node 是完全一样的流程(即:如此循环下去,直到所有二叉树的所有节点都访问完毕即可)。因此,前序遍历的非递归实现版本代码参考如下:

     1 void TraversalBinaryTree::doPreOrder(BinaryTree* pTree, const TPrinter& printer) {
     2     if (nullptr == pTree || nullptr == printer) {
     3         return;
     4     }
     5     std::stack<BinaryTreeNode*> stackNodes;
     6     stackNodes.push(pTree);
     7     BinaryTreeNode* pNode = nullptr;
     8     while (!stackNodes.empty()) {
     9         pNode = stackNodes.top();
    10         stackNodes.pop();
    11         printer(*pNode); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    12         if (nullptr != pNode->pRight) {
    13             stackNodes.push(pNode->pRight);
    14         }
    15         if (nullptr != pNode->pLeft) {
    16             stackNodes.push(pNode->pLeft);
    17         }
    18     }
    19 }

    中序遍历

    中序遍历中,对于任何节点 node 的访问顺序是 visit(node.left) ---> visit(node) ---> visit(node.right)。即:优先访问节点 node 的左子节点,然后再访问节点本身,最后再访问节点的右子节点。同样,可根据递归定义实现递归版本的中序遍历,参考代码如下:

    1 void TraversalBinaryTree::doInOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
    2     if (nullptr != pTree && nullptr != printer) {
    3         this->doInOrderRecursion(pTree->pLeft, printer);
    4         printer(*pTree); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    5         this->doInOrderRecursion(pTree->pRight, printer);
    6     }
    7 }

    非递归实现中,由于节点 node 不是最先访问的,而是要等到访问完其左子节点后,才可以访问节点 node。因此,如果继续参照前序遍历那样,仅仅一次压栈肯定不够,压一次栈后,弹出来是不可以立即访问节点 node,除非 node 没有左子节点又或者 node 的左子节点已经被访问完成了。因此,对于任一节点的中序访问处理如下:

    1. node 节点第一次压栈
    2. node 节点第一次弹栈,弹出来后,如果右子节点非空,立刻将其右子节点第一次压栈(因为右子节点一定是最后访问的,所以要优先入栈)
    3. 判断 node.left 为空,则立即访问 node;
      如果 node.left 非空,必需要第二次将 node 压入,然后紧接着压入 node.left(栈是后进先出,后压入 node.left,则后面才会优先弹出),然后后面才是优先访问 node.left,继而访问到 node(此时访问该节点就已经是第二次访问,并且保证了栈顶的 node.left 节点已经访问完成,所以此时可以真正访问visit(node)了)

    注意:由于第2. node.right 已经先于 node 以及 node.left 入栈,所以最终保证了对于任意节点 node 的中序访问顺序为:左、本身、右的正确顺序。

    中序遍历的非递归实现参考如下:

     1 void TraversalBinaryTree::doInOrder(BinaryTree* pTree, const TPrinter& printer) {
     2     if (nullptr == pTree || nullptr == printer) {
     3         return;
     4     }
     5     struct TCounterTreeNode
     6     {
     7         BinaryTreeNode*         pTreeNode;          // 树节点
     8         int                     nPushCounter;       // 压栈计数器
     9     };//struct TCounterTreeNode
    10 
    11     std::stack<TCounterTreeNode> stackNode;
    12     TCounterTreeNode counterNode;
    13     counterNode.pTreeNode       = pTree;
    14     counterNode.nPushCounter    = 1;
    15     stackNode.push(counterNode);
    16     BinaryTreeNode* pNode       = nullptr;
    17     while (!stackNode.empty()) {
    18         auto& topCounterNode    = stackNode.top();
    19         pNode                   = topCounterNode.pTreeNode;
    20         if (2 == topCounterNode.nPushCounter || nullptr == pNode->pLeft) {
    21             printer(*pNode); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    22             stackNode.pop(); // 注意:特地将弹栈操作放在此次,可减少不必要的弹栈后再次压栈的操作
    23             if (nullptr != pNode->pRight) {
    24                 counterNode.pTreeNode    = pNode->pRight;
    25                 counterNode.nPushCounter = 1;
    26                 stackNode.push(counterNode);
    27             }
    28         } else {
    29             ++topCounterNode.nPushCounter; // 因为开始时,并没有立即弹栈,所以此时可以直接将计数器 +1,从而达到弹栈再压栈的操作,提高了效率.
    30             if (nullptr != pNode->pLeft) {
    31                 counterNode.pTreeNode    = pNode->pLeft;
    32                 counterNode.nPushCounter = 1;
    33                 stackNode.push(counterNode);
    34             }
    35         }
    36     }
    37 }

    后序遍历

    后序遍历是优先访问节点的左子节点,再访问右子节点,最后才访问节点本身。同样,其递归实现版本代码参考如下:

    1 void TraversalBinaryTree::doPostOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
    2     if (nullptr != pTree && nullptr != printer) {
    3         this->doPostOrderRecursion(pTree->pLeft, printer);
    4         this->doPostOrderRecursion(pTree->pRight, printer);
    5         printer(*pTree); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    6     }
    7 }

    非递归实现的思路与前面的中序遍历的非递归实现思路十分类似,由于节点 node 不是最先访问的,而是要等到访问完其左、右子节点后,才可以访问节点 node。因此,对于节点 node 也是需要两次压栈,压一次栈后,弹出来是不可以立即访问节点 node,除非 node 没有左、右子节点又或者 node 的左、右子节点都已经被访问完成了。因此,对于任一节点的后序访问处理如下:

    1. node 节点第一次压栈
    2. node 节点(第一次弹栈)弹出来后,
      如果左子节点非空和右子节点都为空,则可以立即访问节点 node,
      否则再次(即:第二次压栈)将 node 压栈(因为是最后访问 node,所以要先于左、右子节点压栈),然后再按顺序压入 node.right 节点以及 node.left 节点。从而保证了对于节点 node 的后序访问为:visit(node.left) ---> visit(node.right) ---> visit(node) 的正确顺序。

    后序遍历的非递归实现参考如下:

     1 void TraversalBinaryTree::doPostOrder(BinaryTree* pTree, const TPrinter& printer) {
     2     if (nullptr == pTree || nullptr == printer) {
     3         return;
     4     }
     5     struct TCounterTreeNode
     6     {
     7         BinaryTreeNode*         pTreeNode;          // 树节点
     8         int                     nPushCounter;       // 压栈计数器
     9     };//struct TCounterTreeNode
    10 
    11     std::stack<TCounterTreeNode> stackNode;
    12     TCounterTreeNode counterNode;
    13     counterNode.pTreeNode = pTree;
    14     counterNode.nPushCounter = 1;
    15     stackNode.push(counterNode);
    16     BinaryTreeNode* pNode = nullptr;
    17     while (!stackNode.empty()) {
    18         auto& topCounterNode = stackNode.top();
    19         pNode = topCounterNode.pTreeNode;
    20         if (2 == topCounterNode.nPushCounter || (nullptr == pNode->pLeft && nullptr == pNode->pRight)) {
    21             printer(*pNode); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    22             stackNode.pop(); // 注意:特地将弹栈操作放在此次,可减少不必要的弹栈后再次压栈的操作
    23             continue;
    24         } else {
    25             ++topCounterNode.nPushCounter; // 因为开始时,并没有立即弹栈,所以此时可以直接将计数器 +1,从而达到弹栈再压栈的操作,提高了效率.
    26             if (nullptr != pNode->pRight) {
    27                 counterNode.pTreeNode = pNode->pRight;
    28                 counterNode.nPushCounter = 1;
    29                 stackNode.push(counterNode);
    30             }
    31             if (nullptr != pNode->pLeft) {
    32                 counterNode.pTreeNode = pNode->pLeft;
    33                 counterNode.nPushCounter = 1;
    34                 stackNode.push(counterNode);
    35             }
    36         }
    37     }
    38 }

    层序遍历

    层序遍历是指按从上到下,从左到右顺序访问整棵二叉树。即:对于任意节点 node 的层序访问顺序为:
    visit(node) --->
    visit(node.left) --->
    visit(node.right) --->
    visit(node.left.left) --->
    visit(node.left.right) --->
    visit(node.right.left) --->
    visit(node.right.right) --->
    ...。

    注意:层序遍历与前序遍历看上去有点类似,但其实是有区别的,读者自行作图即可明白。

    根据定义,层序遍历是先访问节点 node ,再访问 node.left、node.right。因引,使用队列实现如下:

    1. 将节点 node 入队
    2. 将节点 node 出队并 visit(node)
    3. 如果 node.left 非空,则将其入队;
      如果 node.right 非空,则将其入队;
      注意:队列是先进先出,所以根据层序遍历的定义,需要先入 left 再入 right

    因此,层序遍历的编码实现参考如下:

     1 void TraversalBinaryTree::doLevelOrderTraversal(BinaryTree* pTree, const TPrinter& printer) {
     2     if (nullptr == pTree || nullptr == printer) {
     3         return;
     4     }
     5 
     6     std::deque<BinaryTreeNode*> dequeNodes;
     7     dequeNodes.push_back(pTree);
     8     BinaryTreeNode* pNode = nullptr;
     9     while (!dequeNodes.empty()) {
    10         pNode = dequeNodes.front();
    11         dequeNodes.pop_front();
    12         printer(*pNode); // 打印节点.其实就是前文所述的 visit(node),具体定义见后面内容
    13         if (nullptr != pNode->pLeft) {
    14             dequeNodes.push_back(pNode->pLeft);
    15         }
    16         if (nullptr != pNode->pRight) {
    17             dequeNodes.push_back(pNode->pRight);
    18         }
    19     }
    20 }

    完整编码

     1 /******************************************************************************
     2 
     3                              I'm jacc.kim
     4 
     5     CreateDate: 2017-03-27 14:04:21 
     6     FileName  : TraversalBinaryTree.h
     7     Version   : 1.00
     8     Author    : jacc.kim
     9     Summary   : binary tree traversal.
    10 
    11 ******************************************************************************/
    12 #pragma once
    13 
    14 #include <functional>
    15 
    16 #include "JK/Common/Basic/JKDefine.h"
    17 #include "Algorithm/AlgorithmCommon/AlgorithmDefine.h"
    18 
    19 NS_ALGORITHM_BEGIN
    20 
    21 // 
    22 // create   : (jacc.kim) [03-27-2017]
    23 // summary  : struct BinaryTreeNode
    24 struct BinaryTreeNode
    25 {
    26     int                             nValue;
    27     BinaryTreeNode*                 pLeft;
    28     BinaryTreeNode*                 pRight;
    29     BinaryTreeNode() : nValue(0), pLeft(nullptr), pRight(nullptr)
    30     {}
    31     ~BinaryTreeNode() {
    32         if (nullptr != pLeft) {
    33             SAFE_DELETE_NULL(pLeft);
    34         }
    35         if (nullptr != pRight) {
    36             SAFE_DELETE_NULL(pRight);
    37         }
    38     }
    39 };//struct BinaryTreeNode
    40 
    41 typedef BinaryTreeNode              BinaryTree;
    42 
    43 /******************************************************************************
    44  * create   : (jacc.kim) [03-27-2017]
    45  * summary  : class TraversalBinaryTree
    46 ******************************************************************************/
    47 class TraversalBinaryTree
    48 {
    49 public:
    50     typedef std::function<void(BinaryTreeNode& treeNode)>   TPrinter;
    51 
    52 public:
    53     // 
    54     // summary     : 创建测试树
    55     // in param    : --
    56     // return      : --
    57     // !!!note     : 01.如果创建成功 *ppTree 需要由用户自己维护释放!!!
    58     static bool                     createTestTree(BinaryTree** ppTree);
    59 
    60 public:
    61     void                            doPreOrderRecursion(BinaryTree* pTree, const TPrinter& printer);
    62     void                            doPreOrder(BinaryTree* pTree, const TPrinter& printer);
    63     void                            doInOrderRecursion(BinaryTree* pTree, const TPrinter& printer);
    64     void                            doInOrder(BinaryTree* pTree, const TPrinter& printer);      // 使用辅助计数器
    65     void                            doPostOrderRecursion(BinaryTree* pTree, const TPrinter& printer);
    66     void                            doPostOrder(BinaryTree* pTree, const TPrinter& printer);
    67 
    68     void                            doLevelOrderTraversal(BinaryTree* pTree, const TPrinter& printer);
    69 
    70 };//class TraversalBinaryTree
    71 
    72 NS_ALGORITHM_END
      1 #include <stack>
      2 #include "Algorithm/TraversalBinaryTree/TraversalBinaryTree.h"
      3 
      4 NS_ALGORITHM_BEGIN
      5 
      6 ///////////////////////////////////////////////////////////////////////////////
      7 // class TraversalBinaryTree
      8 bool TraversalBinaryTree::createTestTree(BinaryTree** ppTree) {
      9     auto bIsSuccess     = false;
     10     if (nullptr == ppTree) {
     11         return bIsSuccess;
     12     }
     13     auto pNode8         = new BinaryTreeNode();
     14     pNode8->nValue      = 8;
     15     auto pNode6         = new BinaryTreeNode();
     16     pNode6->nValue      = 6;
     17     auto pNode10        = new BinaryTreeNode();
     18     pNode10->nValue     = 10;
     19     auto pNode5         = new BinaryTreeNode();
     20     pNode5->nValue      = 5;
     21     auto pNode7         = new BinaryTreeNode();
     22     pNode7->nValue      = 7;
     23     auto pNode9         = new BinaryTreeNode();
     24     pNode9->nValue      = 9;
     25     auto pNode11        = new BinaryTreeNode();
     26     pNode11->nValue = 11;
     27 
     28     if (true) {    // 测试数据1
     29         pNode8->pLeft = pNode6;
     30         pNode8->pRight = pNode10;
     31         pNode6->pLeft = pNode5;
     32         pNode6->pRight = pNode7;
     33         pNode10->pLeft = pNode9;
     34         pNode10->pRight = pNode11;
     35     }
     36     
     37     if (false) {    // 测试数据2
     38         pNode8->pLeft = pNode6;
     39         pNode8->pRight = pNode10;
     40         pNode6->pLeft = pNode5;
     41         pNode6->pRight = pNode7;
     42         pNode7->pLeft = pNode9;
     43         pNode7->pRight = pNode11;
     44     }
     45 
     46     *ppTree             = pNode8;
     47 
     48     bIsSuccess          = nullptr != (*ppTree);
     49     return bIsSuccess;
     50 }
     51 
     52 void TraversalBinaryTree::doPreOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
     53     if (nullptr != pTree && nullptr != printer) {
     54         printer(*pTree); 
     55         this->doPreOrderRecursion(pTree->pLeft, printer);
     56         this->doPreOrderRecursion(pTree->pRight, printer);
     57     }
     58 }
     59 
     60 void TraversalBinaryTree::doPreOrder(BinaryTree* pTree, const TPrinter& printer) {
     61     if (nullptr == pTree || nullptr == printer) {
     62         return;
     63     }
     64     std::stack<BinaryTreeNode*> stackNodes;
     65     stackNodes.push(pTree);
     66     BinaryTreeNode* pNode = nullptr;
     67     while (!stackNodes.empty()) {
     68         pNode = stackNodes.top();
     69         stackNodes.pop();
     70         printer(*pNode); 
     71         if (nullptr != pNode->pRight) {
     72             stackNodes.push(pNode->pRight);
     73         }
     74         if (nullptr != pNode->pLeft) {
     75             stackNodes.push(pNode->pLeft);
     76         }
     77     }
     78 }
     79 
     80 void TraversalBinaryTree::doInOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
     81     if (nullptr != pTree && nullptr != printer) {
     82         this->doInOrderRecursion(pTree->pLeft, printer);
     83         printer(*pTree); 
     84         this->doInOrderRecursion(pTree->pRight, printer);
     85     }
     86 }
     87 
     88 void TraversalBinaryTree::doInOrder(BinaryTree* pTree, const TPrinter& printer) {
     89     if (nullptr == pTree || nullptr == printer) {
     90         return;
     91     }
     92     struct TCounterTreeNode
     93     {
     94         BinaryTreeNode*         pTreeNode;          // 树节点
     95         int                     nPushCounter;       // 压栈计数器
     96     };//struct TCounterTreeNode
     97 
     98     std::stack<TCounterTreeNode> stackNode;
     99     TCounterTreeNode counterNode;
    100     counterNode.pTreeNode       = pTree;
    101     counterNode.nPushCounter    = 1;
    102     stackNode.push(counterNode);
    103     BinaryTreeNode* pNode       = nullptr;
    104     while (!stackNode.empty()) {
    105         auto& topCounterNode    = stackNode.top();
    106         pNode                   = topCounterNode.pTreeNode;
    107         if (2 == topCounterNode.nPushCounter || nullptr == pNode->pLeft) {
    108             printer(*pNode); 
    109             stackNode.pop(); // 注意:特地将弹栈操作放在此次,可减少不必要的弹栈后再次压栈的操作
    110             if (nullptr != pNode->pRight) {
    111                 counterNode.pTreeNode    = pNode->pRight;
    112                 counterNode.nPushCounter = 1;
    113                 stackNode.push(counterNode);
    114             }
    115         } else {
    116             ++topCounterNode.nPushCounter; // 因为开始时,并没有立即弹栈,所以此时可以直接将计数器 +1,从而达到弹栈再压栈的操作,提高了效率.
    117             if (nullptr != pNode->pLeft) {
    118                 counterNode.pTreeNode    = pNode->pLeft;
    119                 counterNode.nPushCounter = 1;
    120                 stackNode.push(counterNode);
    121             }
    122         }
    123     }
    124 }
    125 
    126 void TraversalBinaryTree::doPostOrderRecursion(BinaryTree* pTree, const TPrinter& printer) {
    127     if (nullptr != pTree && nullptr != printer) {
    128         this->doPostOrderRecursion(pTree->pLeft, printer);
    129         this->doPostOrderRecursion(pTree->pRight, printer);
    130         printer(*pTree); 
    131     }
    132 }
    133 
    134 void TraversalBinaryTree::doPostOrder(BinaryTree* pTree, const TPrinter& printer) {
    135     if (nullptr == pTree || nullptr == printer) {
    136         return;
    137     }
    138     struct TCounterTreeNode
    139     {
    140         BinaryTreeNode*         pTreeNode;          // 树节点
    141         int                     nPushCounter;       // 压栈计数器
    142     };//struct TCounterTreeNode
    143 
    144     std::stack<TCounterTreeNode> stackNode;
    145     TCounterTreeNode counterNode;
    146     counterNode.pTreeNode = pTree;
    147     counterNode.nPushCounter = 1;
    148     stackNode.push(counterNode);
    149     BinaryTreeNode* pNode = nullptr;
    150     while (!stackNode.empty()) {
    151         auto& topCounterNode = stackNode.top();
    152         pNode = topCounterNode.pTreeNode;
    153         if (2 == topCounterNode.nPushCounter || (nullptr == pNode->pLeft && nullptr == pNode->pRight)) {
    154             printer(*pNode); 
    155             stackNode.pop(); // 注意:特地将弹栈操作放在此次,可减少不必要的弹栈后再次压栈的操作
    156             continue;
    157         } else {
    158             ++topCounterNode.nPushCounter; // 因为开始时,并没有立即弹栈,所以此时可以直接将计数器 +1,从而达到弹栈再压栈的操作,提高了效率.
    159             if (nullptr != pNode->pRight) {
    160                 counterNode.pTreeNode = pNode->pRight;
    161                 counterNode.nPushCounter = 1;
    162                 stackNode.push(counterNode);
    163             }
    164             if (nullptr != pNode->pLeft) {
    165                 counterNode.pTreeNode = pNode->pLeft;
    166                 counterNode.nPushCounter = 1;
    167                 stackNode.push(counterNode);
    168             }
    169         }
    170     }
    171 }
    172 
    173 void TraversalBinaryTree::doLevelOrderTraversal(BinaryTree* pTree, const TPrinter& printer) {
    174     if (nullptr == pTree || nullptr == printer) {
    175         return;
    176     }
    177 
    178     std::deque<BinaryTreeNode*> dequeNodes;
    179     dequeNodes.push_back(pTree);
    180     BinaryTreeNode* pNode = nullptr;
    181     while (!dequeNodes.empty()) {
    182         pNode = dequeNodes.front();
    183         dequeNodes.pop_front();
    184         printer(*pNode); 
    185         if (nullptr != pNode->pLeft) {
    186             dequeNodes.push_back(pNode->pLeft);
    187         }
    188         if (nullptr != pNode->pRight) {
    189             dequeNodes.push_back(pNode->pRight);
    190         }
    191     }
    192 }
    193 
    194 NS_ALGORITHM_END

    测试用例及结果

     1 // TraversalBinaryTree_project.cpp : 定义控制台应用程序的入口点。
     2 //
     3 
     4 #include "stdafx.h"
     5 
     6 #include "Algorithm/AlgorithmCommon/ValidateProjectConfig.h"
     7 #include "Algorithm/TraversalBinaryTree/TraversalBinaryTree.h"
     8 
     9 int _tmain(int argc, _TCHAR* argv[])
    10 {
    11     nsalgorithm::ValidateProjectConfig::validate("log/TraversalBinaryTree");
    12 
    13     {
    14         // test pre order
    15         nsalgorithm::BinaryTree* pTree = nullptr;
    16         if (nsalgorithm::TraversalBinaryTree::createTestTree(&pTree)) {
    17             std::cout << "-------- pre order traversal --------" << std::endl;
    18             nsalgorithm::TraversalBinaryTree instance;
    19             instance.doPreOrderRecursion(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    20             std::cout << std::endl;
    21             instance.doPreOrder(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    22             std::cout << std::endl;
    23             std::cout << std::endl;
    24             SAFE_DELETE_NULL(pTree);
    25         }
    26     }
    27 
    28     {
    29         // test in order
    30         nsalgorithm::BinaryTree* pTree = nullptr;
    31         if (nsalgorithm::TraversalBinaryTree::createTestTree(&pTree)) {
    32             std::cout << "-------- in order traversal --------" << std::endl;
    33             nsalgorithm::TraversalBinaryTree instance;
    34             instance.doInOrderRecursion(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    35             std::cout << std::endl;
    36             instance.doInOrder(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    37             std::cout << std::endl;
    38             std::cout << std::endl;
    39             SAFE_DELETE_NULL(pTree);
    40         }
    41     }
    42 
    43     {
    44         // test post order
    45         nsalgorithm::BinaryTree* pTree = nullptr;
    46         if (nsalgorithm::TraversalBinaryTree::createTestTree(&pTree)) {
    47             std::cout << "-------- post order traversal --------" << std::endl;
    48             nsalgorithm::TraversalBinaryTree instance;
    49             instance.doPostOrderRecursion(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    50             std::cout << std::endl;
    51             instance.doPostOrder(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    52             std::cout << std::endl;
    53             std::cout << std::endl;
    54             SAFE_DELETE_NULL(pTree);
    55         }
    56     }
    57 
    58     {
    59         // test level order traversal
    60         nsalgorithm::BinaryTree* pTree = nullptr;
    61         if (nsalgorithm::TraversalBinaryTree::createTestTree(&pTree)) {
    62             std::cout << "-------- level order traversal --------" << std::endl;
    63             nsalgorithm::TraversalBinaryTree instance;
    64             instance.doLevelOrderTraversal(pTree, [](nsalgorithm::BinaryTreeNode& node){ std::cout << node.nValue << " "; });
    65             std::cout << std::endl;
    66             std::cout << std::endl;
    67             SAFE_DELETE_NULL(pTree);
    68         }
    69     }
    70 
    71     system("pause");
    72     return 0;
    73 }

    总结

    对于前序、中序、后序遍历的递归实现版本,代码通俗易懂且简洁明了,但递归毕竟耗时、空间资源。因此,一旦树的深度稍微大点,就有可能出现堆栈溢出。对于非递归版本,这三种遍历方式主要利用递归栈来模拟递归的实现。另外,对于中序以及后序遍历的非递归版本中,由于节点 node 并不能够立即访问,因此,(这两种遍历)需要对每个节点进行二次压栈版本。

    另外,关于这三种遍历的非递归实现版本中,二叉树的非递归遍历 这个博文,则提供了另一种的编码实现方式。个人感觉写的挺不错的。其整体思路与本文中的思路类似,只是实现技巧上有区别。特别是对于中序遍历的实现上,该博文并没有利用辅助计数器实现。

    层序遍历根据定义是按顺序从上到下,从左到右顺序访问,而队列刚好符合这一特点,所以利用队列来处理之。

    推荐文献

    博文:海子 之 《二叉树的非递归遍历》

  • 相关阅读:
    idea 导入spring 源码注意的问题
    如何在plsql/developer的命令窗口执行sql脚本
    使用babel把es6代码转成es5代码
    解决es6中webstrom不支持import的一个简单方法
    nodejs npm insttall 带不带-g这个参数的区别
    nodejs 喜欢报cannot find module .....的简单解决方案
    用npm安装express时报proxy的错误的解决方法
    angularjs的验证信息的写法
    Emacs as a Python IDE(转)
    消息摘要java.security.MessageDigest
  • 原文地址:https://www.cnblogs.com/tongy0/p/6633390.html
Copyright © 2020-2023  润新知