简介
二叉树是一种非常重要的数据结构,二叉搜索树,AVL树,红黑树等都是以二叉树为基础实现的搜索结构,作为一种搜索结构,遍历算法是非常重要的。下面我们就来讲解一下二叉树的各种遍历算法。
层序遍历
层序遍历即从根结点开始一层一层的访问二叉树的结点,直到所有结点被访问。在代码实现中我们利用队列先进先出的性质,把结点从根结点开始一一的按照先存根再存左右孩子的原则存入队列。
vector<int> LevelOrder(TreeNode* root)
{
vector<int> result;//利用vector存放层序遍历序列
queue<TreeNode*> q;
if (root == NULL)
{
return result;
}
q.push(root);
while (!q.empty())
{
TreeNode* front = q.front();
if (front->left)
{
q.push(front->left);
}
if (front->right)
{
q.push(front->right);
}
result.push_back(front->val);
q.pop(front);
}
}
不同于层序遍历,后面三种遍历方式都具有递归的思想在其中,这也是二叉树的一种特性。
前序遍历
前序遍历即先访问根结点再访问左孩子,最后访问右孩子。
中序遍历
中序遍历即先访问左孩子再访问根结点,最后访问右孩子。
后序遍历
后序遍历即先访问左孩子再访问右孩子,最后访问根结点。
使用递归的思想很容易实现他们遍历的代码。
void PreOrder(TreeNode* root)
{//前序
if (root == NUll)
return;
if (root)
{
cout << root->val;
PreOrder(root->left);
PreOrder(root->right);
}
}
void InOrder(TreeNode* root)
{//中序
if (root == NULL)
return;
if (root)
{
InOrder(root->left);
cout << root->val;
InOrder(root->right);
}
}
void PostOrder(TreeNode* root)
{//后序
if (root == NULL)
return;
if (root)
{
PostOrder(root->left);
PostOrder(root->right);
cout << root->val;
}
}
非递归实现
现在我们再使用非递归的方式尝试实现以下二叉树的前中后序的遍历。基本思想是利用栈的先入后出的特性,把结点按照前中后序的要求存入栈,再按照需要从栈中弹出。
前序遍历
大体思路是给出一个栈,从根节点开始向左访问每个结点并把它们入栈,直到左孩子全被访问,此时弹出一个结点以和上面同样的方式访问其右孩子。直到栈空。
vector<int> NPreOrder(TreeNode* root)
{
vector<int> result;
stack<TreeNode*> s;
if (root == NULL)
return result;
while (root || !s.empty())
{//结束遍历的条件是root为空且栈为空
while(root)
{//找到最左结点,并把路径上的所有结点一一访问后入栈
s.push(root);
result.push_back(root->val);
root = root->left;
}
root = s.top();//取栈顶结点
s.pop();//弹出栈顶结点
root = root->right;//左和中都访问了再往右访问
}
return result;
}
中序遍历
中序遍历和前序遍历大同小异,只是访问元素的时间不同,中序遍历访问是在元素出栈的时候访问,而前序遍历是在元素入栈的时候访问。
vector<int> NInOrder(TreeNode* root)
{
vector<int> result;
stack<TreeNode*> s;
if (root == NULL)
return result;
while (root || !s.empty())
{
while (root)
{
s.push(root);
root = root->left;
}
root = s.top();
result.push_back(root->val);
s.pop();
root = root->right;
}
return result;
}
后序遍历
后序遍历相对复杂一点,因为后序遍历访问一个结点的时候需要满足两个条件,一是该结点右孩子为空,二是该结点的右孩子已经被访问过,这两个条件满足一个则表示该结点可以被访问。
vector<int> PostOrder(TreeNode* root)
{
vector<int> result;
stack<int> s;
TreeNode* cur = root;
TreeNode* pre = NULL;
if (root == NULL)
return result;
while (cur)
{//走到最左孩子
s.push(cur);
cur = cur->left;
}
while (!s.empty())
{
cur = s.top();
if (cur->right == NULL || cur->right == pre)
{//当一个结点的右孩子为空或者被访问过的时候则表示该结点可以被访问
result.push_back(cur->val);
pre = cur;
s.pop();
}
else
{//否则访问右孩子
cur = cur->right;
while (cur)
{
s.push(cur);
cur = cur->left;
}
}
}
return result;
}