概要
数据结构中,树是相对较为复杂的一种数据结构,但也尤其重要,特别是二叉树。该文主要记录二叉树的几种遍历:前序遍历、中序遍历、后序遍历以及层序遍历。介绍各种遍历方式前,先要明确二叉树是:树上的任何一个节点,最多只可能有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 的左子节点已经被访问完成了。因此,对于任一节点的中序访问处理如下:
- node 节点第一次压栈
- node 节点第一次弹栈,弹出来后,如果右子节点非空,立刻将其右子节点第一次压栈(因为右子节点一定是最后访问的,所以要优先入栈)
- 判断 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 的左、右子节点都已经被访问完成了。因此,对于任一节点的后序访问处理如下:
- node 节点第一次压栈
- 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。因引,使用队列实现如下:
- 将节点 node 入队
- 将节点 node 出队并 visit(node)
- 如果 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 并不能够立即访问,因此,(这两种遍历)需要对每个节点进行二次压栈版本。
另外,关于这三种遍历的非递归实现版本中,二叉树的非递归遍历 这个博文,则提供了另一种的编码实现方式。个人感觉写的挺不错的。其整体思路与本文中的思路类似,只是实现技巧上有区别。特别是对于中序遍历的实现上,该博文并没有利用辅助计数器实现。
层序遍历根据定义是按顺序从上到下,从左到右顺序访问,而队列刚好符合这一特点,所以利用队列来处理之。