二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被访问依次且仅被访问一次。
四种遍历方式分别为:先序遍历、中序遍历、后序遍历、层序遍历。
![](https://i-blog.csdnimg.cn/blog_migrate/33fcff8a913d77d947e73c2bf57f6101.png)
二叉树的四种遍历方式
![](https://i-blog.csdnimg.cn/blog_migrate/c4bf9fc3193c970d19e8271fe738c364.png)
例二叉树
直接用上面的二叉树上代码了,关于先序遍历、中序遍历、后序遍历的递归算法比较简单,不做注释,关于所有非递归算法的思想都在代码里写了注释。
先序遍历:ABDHEICFJKG
中序遍历:DHBEIAJFKCG
后序遍历:HDIEBJKFGCA
层序遍历:ABCDEFGHIJK
#include <stack>
#include <queue>
#include <cstdio>
using namespace std;
struct TreeNode {
char val;
TreeNode* leftChild;
TreeNode* rightChild;
TreeNode(char val) : val(val), leftChild(NULL), rightChild(NULL) {}
};
void PreorderTraversal(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
printf("%c", treeNode->val);
PreorderTraversal(treeNode->leftChild);
PreorderTraversal(treeNode->rightChild);
}
void InorderTraversal(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
InorderTraversal(treeNode->leftChild);
printf("%c", treeNode->val);
InorderTraversal(treeNode->rightChild);
}
void PostorderTraversal(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
PostorderTraversal(treeNode->leftChild);
PostorderTraversal(treeNode->rightChild);
printf("%c", treeNode->val);
}
/*
* 先序遍历比较容易理解,首先将根节点入栈。
* 从栈中取出栈顶节点,打印该点值,接着先将右孩子入栈。
* 再将左孩子入栈(因为栈的特点是先进后出,要先遍历左孩子就得后入栈)。
* 不断重复该步骤直至栈为空。
*/
void PreorderTraversalUnRecur(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
stack<TreeNode*> treeNodeStack;
treeNodeStack.push(treeNode);
while (!treeNodeStack.empty())
{
treeNode = treeNodeStack.top();
treeNodeStack.pop();
printf("%c", treeNode->val);
if (treeNode->rightChild)
{
treeNodeStack.push(treeNode->rightChild);
}
if (treeNode->leftChild)
{
treeNodeStack.push(treeNode->leftChild);
}
}
}
/*
* 中序遍历
* 步骤1:比先把当前节点入栈,然后不停让treeNode=treeNode->leftChild,重复此步骤。
* 即把treeNode下的所有左孩子节点入栈,直到treeNode为空。
* 步骤2:从栈中弹出栈顶给treeNode,打印该节点,然后令treeNode=treeNode->rightChild,回到步骤1。
* 步骤3:当栈为空时且treeNode为空时,过程结束。
*/
void InorderTraversalUnRecur(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
stack<TreeNode*> treeNodeStack;
while (!treeNodeStack.empty() || NULL != treeNode)
{
while (NULL != treeNode)
{
treeNodeStack.push(treeNode);
treeNode = treeNode->leftChild;
}
treeNode = treeNodeStack.top();
treeNodeStack.pop();
printf("%c", treeNode->val);
treeNode = treeNode->rightChild;
}
}
/*
* 后序遍历(方法1)
* 申请两个栈treeNodeStackl、treeNodeStack2,先将头节点入栈treeNodeStackl。
* 从treeNodeStackl中弹出栈顶节点记为treeNode,然后依次将cur的左孩子和右孩子压入treeNodeStackl。
* 在这过程中每一个从treeNodeStackl中弹出的节点均压入treeNodeStack2。
* 当treeNodeStackl为空后,从treeNodeStack2中依次弹出的节点便是后序遍历的次序。
* 主要就是利用两个栈来进行"倒腾",压入treeNodeStack2的顺序为中、右、左,弹出的顺序就变成了左、右、中。
*/
void PostorderTraversalUnRecur(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
stack<TreeNode*> treeNodeStackl, treeNodeStack2;
treeNodeStackl.push(treeNode);
while (!treeNodeStackl.empty())
{
treeNode = treeNodeStackl.top();
treeNodeStackl.pop();
treeNodeStack2.push(treeNode);
if (treeNode->leftChild)
{
treeNodeStackl.push(treeNode->leftChild);
}
if (treeNode->rightChild)
{
treeNodeStackl.push(treeNode->rightChild);
}
}
while (!treeNodeStack2.empty())
{
printf("%c", treeNodeStack2.top()->val);
treeNodeStack2.pop();
}
}
/*
* 后序遍历(方法2)
* 申请一个栈,将头节点压入栈。
* 当遍历到一个节点时,如何判断这次遍历是打印该点,还是先处理它的孩子?有以下几种情况:
* 1、该节点的左右孩子皆为空,即该节点为叶子节点,那么这次遍历就是打印该点。
* 2、如果上一次打印的节点为该节点的右孩子,说明该节点的子树处理完毕,这次遍历就是打印该点。
* 3、如果上一次打印的节点为该节点的左孩子,且该节点的右孩子为空,说明该节点的子树处理完毕,这次遍历就是打印该点。
* 4、否则说明子树没有被访问过,按照右孩子、左孩子的顺序入栈。
*/
void PostorderTraversalUnRecur2(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
stack<TreeNode*> treeNodeStack;
treeNodeStack.push(treeNode);
TreeNode* last = NULL;
TreeNode* top = NULL;
while (!treeNodeStack.empty())
{
top = treeNodeStack.top();
if ((top->leftChild == NULL && top->rightChild == NULL) ||
(top->rightChild == NULL && last == top->leftChild) ||
last == top->rightChild)
{
printf("%c", top->val);
last = top;
treeNodeStack.pop();
}
else
{
if (top->rightChild)
{
treeNodeStack.push(top->rightChild);
}
if (top->leftChild)
{
treeNodeStack.push(top->leftChild);
}
}
}
}
/*
* 层序遍历
* 首先将二叉树的根节点push到队列中,如果队列不为空,就输出队头的元素并出队列。
* 判断节点如果有孩子,就将孩子push到队列中。
* 循环以上操作直到队列为空。
*/
void LevelOrder(TreeNode* treeNode)
{
if (NULL == treeNode)
{
return;
}
queue<TreeNode*> queue;
queue.push(treeNode);
while (!queue.empty())
{
treeNode = queue.front();
queue.pop();
printf("%c", treeNode->val);
if (treeNode->leftChild)
{
queue.push(treeNode->leftChild);
}
if (treeNode->rightChild)
{
queue.push(treeNode->rightChild);
}
}
}
int main()
{
TreeNode nodeA('A');
TreeNode nodeB('B');
TreeNode nodeC('C');
TreeNode nodeD('D');
TreeNode nodeE('E');
TreeNode nodeF('F');
TreeNode nodeG('G');
TreeNode nodeH('H');
TreeNode nodel('T');
TreeNode nodeJ('J');
TreeNode nodeK('K');
nodeA.leftChild = &nodeB;
nodeA.rightChild = &nodeC;
nodeB.leftChild = &nodeD;
nodeB.rightChild = &nodeE;
nodeD.rightChild = &nodeH;
nodeE.rightChild = &nodel;
nodeC.leftChild = &nodeF;
nodeC.rightChild = &nodeG;
nodeF.leftChild = &nodeJ;
nodeF.rightChild = &nodeK;
TreeNode* root = &nodeA;
printf(" 前序遍历:"); //ABDHEICFJKG
PreorderTraversal(root);
printf("\n");
printf("前序遍历非递归:"); //ABDHEICFJKG
PreorderTraversalUnRecur(root);
printf("\n");
printf(" 中序遍历:"); //DHBEIAJFKCG
InorderTraversal(root);
printf("\n");
printf("中序遍历非递归:"); //DHBEIAJFKCG
InorderTraversalUnRecur(root);
printf("\n");
printf(" 后序遍历:"); //HDIEBJKFGCA
PostorderTraversal(root);
printf("\n");
printf("后序遍历非递归:"); //HDIEBJKFGCA
PostorderTraversalUnRecur(root);
printf("\n");
printf("后序遍历非递归:"); //HDIEBJKFGCA
PostorderTraversalUnRecur2(root);
printf("\n");
printf(" 层序遍历:"); //ABCDEFGHIJK
LevelOrder(root);
}