前言
首先我们需要了解什么是二叉树的前序遍历,中序遍历和后序遍历。
前序遍历是按照根节点——左子树——右子树的方式遍历二叉树(根——左——右)。
中序遍历是按照左子树——根节点——右子树的方式遍历二叉树(左——根——右)。
后序遍历是按照左子树——右子树——根节点的方式遍历二叉树(左——右——根)。
例如,上图所给出的二叉树前序遍历的结果为(此处忽略空节点):
1→2→4→5→8→9→0→3→6→7
中序遍历的结果为:
4→2→8→5→9→0→1→6→3→7
后序遍历的结果为:
4→8→0→9→5→2→6→7→3→1
二叉树在访问完根节点后访问左子树或者右子树的时候,我们按照同样的方式进行遍历,直到遍历完整棵树。由上述过程可知,二叉树的遍历过程天然具有递归的性质,所以我们可以通过递归的方式来完成这个过程。
除此之外,我们也可以使用迭代的方式进行遍历,递归遍历二叉树时,系统会隐式地维护一个栈,而迭代进行遍历就是通过代码显式地维护这个栈。
一、前序遍历
递归
void preorderTraversal(TreeNode* root) {
if (!root) {
return;
}
cout << root->val;
preorderTraversal(root->left);
preorderTraversal(root->right);
}
非递归
void preorderTraversal(TreeNode* root) {
//如果二叉树为空,直接返回
if (!root) {
return;
}
//初始化栈
stack<TreeNode*> stk;
//栈不为空或当前节点不为空时表示二叉树未遍历结束
while (!stk.empty() || root) {
//遍历该节点下的所有左孩子节点
while (root) {
cout << root->val;
//将所有左孩子节点压入栈顶
stk.push(root);
root = root->left;
}
//取栈顶节点遍历右孩子节点
root = stk.top();
stk.pop();
root = root->right;
}
}
二、中序遍历
递归
void inorderTraversal(TreeNode* root) {
while (!root) {
return;
}
inorderTraversal(root->left, ans);
cout << root->val;
inorderTraversal(root->right, ans);
}
非递归
void inorderTraversal(TreeNode* root) {
if(!root) {
return;
}
stack<TreeNode*> stk;
while (!stk.empty() || root) {
while (root) {
stk.push(root);
root = root->left;
}
root = stk.top();
cout << root->val;
stk.pop();
root = root->right;
}
}
三、后序遍历
递归
void postorderTraversal(TreeNode* root) {
if (!root) {
return;
}
postorderTraversal(root->left);
postorderTraversal(root->right);
cout << root->val;
}
非递归
void postorderTraversal(TreeNode* root) {
if (!root) {
return;
}
stack<TreeNode*> stk;
TreeNode* flag = nullptr;
while (!stk.empty() || root) {
//遍历所有左孩子节点
while (root) {
stk.push(root);
root = root->left;
}
//取栈顶节点
root = stk.top();
stk.pop();
//判断该节点右孩子节点是否为空或右孩子节点已被标记(标记指该节点的右孩子节点及其子节点全部遍历过)
if (!root->right || root->right == flag) {
//输出该节点并标记
cout << root->val;
flag = root;
root = nullptr;
}
else {
//否则表示该节点的右孩子节点及其子节点存在节点未被遍历,遍历其右孩子节点
stk.push(root);
root = root->right;
}
}
}
小结
可以看到,二叉树的前序、中序和后序遍历在递归下基本一致,只需要将输出语句简单的调换一下位置即可。而在非递归的情况下,二叉树的前序和中序遍历也基本相同,而后序遍历相较于前序和中序遍历,因为是左——右——根,所以在输出根节点之前需要判断其右孩子节点是否已经遍历。总的来说,二叉树的前序、中序和后序遍历思想基本一致,掌握了其中一种遍历方式就可以很快地理解其他两种遍历方式。