本文涉及三种常见的遍历方式 : 先序遍历、中序遍历、后序遍历,以及用C++实现的非递归版本代码。
首先我们定义一下二叉树结点类
struct BinaryTreeNode {
int val;
BinaryTreeNode* leftchild;
BinaryTreeNode* rightchild;
BinaryTreeNode(int const& _val, BinaryTreeNode* _leftchild=NULL, BinaryTreeNode* _rightchild=NULL) :
val(_val), leftchild(_leftchild), rightchild(_rightchild) {}
};
三种遍历方式 访问节点的顺序是一致的,不同之处在于,有的遍历流程把访问到的节点暂存起来,达成某种条件后再将节点输出。约定, 根节点V, 其左孩子为L, 右孩子为R, 那么遍历顺序可以记为:
Pre-Order Traversal : 到达一个节点后,即刻输出该节点的值,并继续遍历其左右子树。 VLR
In-Order Traversal : 到达一个节点后,将其暂存,遍历完左子树后,再输出该节点的值,然后遍历右子树。LVR
Post-Order Traversal: 到达一个节点后,将其暂存,遍历完左右子树后,再输出该节点的值。 LRV
Pre-Order Traversal In-Order Traversal Post-Order Traversal
先序遍历
先序遍历与中序、后序遍历都使用了栈结构来暂存节点
void pre_traversal(BinaryTreeNode* root) {
std::stack<BinaryTreeNode*> node_stack; //用来暂存节点的栈
while (root != nullptr || !node_stack.empty()) {
if (root != nullptr) { //若当前的节点非空,
std::cout << root->val << " "; //则输出该节点的值
node_stack.push(root); //该节点压入栈中。
root = root->leftchild; // 我们继续向左子树前进
}
else {
root = node_stack.top();
node_stack.pop();
root = root->rightchild;
}
}
}
中序遍历
与先序遍历类似,唯一区别是到达当前节点时 并不直接输出该节点。
void in_traversal(BinaryTreeNode* root) {
std::stack<BinaryTreeNode*> stack_node;
while (root != nullptr || !stack_node.empty()) {
if (root != nullptr) {
stack_node.push(root);
root = root->leftchild;
}
else {
root = stack_node.top();
std::cout << root->val << " ";
stack_node.pop();
root = root->rightchild;
}
}
}
后序遍历
与先序、中序遍历有所不同,后序遍历在决定是否可以输出当前节点的值的时候,需要考虑其左右子树是否都已经遍历完成。
因此我们需要设置一个lastvisit游标。若lastvisit等于当前考查节点的右子树,表示该节点的左右子树都已经遍历完成,则可以输出当前节点(否则,继续向右子树进发)。并把lastvisit节点设置成当前节点,将当前游标节点root设置为空,下一轮就可以访问栈顶元素。
void post_traversal(BinaryTreeNode* root) {
std::stack<BinaryTreeNode*> stack_node;
BinaryTreeNode* lastvisit = root;
while (root != nullptr || !stack_node.empty()) {
if (root != nullptr) {
stack_node.push(root);
root = root->leftchild;
}
else {
root = stack_node.top();
if (root->rightchild == nullptr || root->rightchild == lastvisit) {
std::cout << root->val << " ";
stack_node.pop();
lastvisit = root;
root = nullptr;
}
else {
root = root->rightchild;
}
}
}
}
下面是测试用的代码:
int main(int argc, char* argv[]) {
BinaryTreeNode* A = new BinaryTreeNode(1);
BinaryTreeNode* B = new BinaryTreeNode(2);
BinaryTreeNode* C = new BinaryTreeNode(4);
BinaryTreeNode* D = new BinaryTreeNode(7);
BinaryTreeNode* E = new BinaryTreeNode(3);
BinaryTreeNode* F = new BinaryTreeNode(5);
BinaryTreeNode* G = new BinaryTreeNode(6);
BinaryTreeNode* H = new BinaryTreeNode(8);
A->leftchild = B;
A->rightchild = E;
B->leftchild = C;
C->rightchild = D;
E->leftchild = F;
E->rightchild = G;
G->leftchild = H;
std::cout << "Pre order traversal:" << std::endl;
pre_traversal(A);
std::cout << std::endl<< "In order traversal:" << std::endl;
in_traversal(A);
std::cout << std::endl << "Post order traversal:" << std::endl;
post_traversal(A);
return 0;
}