二叉树非递归遍历

概要

数据结构中,树是相对较为复杂的一种数据结构,但也尤其重要,特别是二叉树。该文主要记录二叉树的几种遍历:前序遍历、中序遍历、后序遍历以及层序遍历。介绍各种遍历方式前,先要明确二叉树是:树上的任何一个节点,最多只可能有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 并不能够立即访问,因此,(这两种遍历)需要对每个节点进行二次压栈版本。

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

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

推荐文献

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

原文地址:https://www.cnblogs.com/tongy0/p/6633390.html