从宏观来讲,二叉树的遍历归结为两类:深度优先遍历和广度优先遍历。深度优先遍历包括前序遍历、中序遍历和后序遍历,广度优先遍历主要指层序遍历,这篇文章主要介绍深度优先遍历。
在介绍如何理解二叉树递归之前,我们回顾一下三种方式遍历二叉树的顺序。
前序:根左右
中序:左根右
后序:左右根
1.根据上面的遍历顺序,我们给出一个简单的例子。已知:前序遍历序列为{1,2,4,7,3,5,6,8},中序遍历序列为{4,7,2,1,5,3,8,6},请画出这颗二叉树。
毫无疑问,我们的二叉树应该长下面这样。接下来我们会以这个例子为例来说明二叉树中的递归。
2.二叉树的结构体定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
};
下面我们开始介绍二叉树中的递归。
一、前序遍历
我们先看前序遍历输出二叉树的代码。简单解释一下:r为要遍历的二叉树的根节点指针,用p指针来指向每次要输出的节点,二叉树的前序遍历为:根左右,先打印根节点,然后前序遍历左子树,再前序遍历右子树。
void pre_order(TreeNode *r) {
TreeNode *p = r;
if (p != NULL) {
cout << p->val << " ";
pre_order(p->left);
pre_order(p->right);
}
}
还是以上面的那颗二叉树为例来说明,前序遍历的执行顺序如下图所示。我们以每次递归为一层,用蓝色数字表示输出,实线箭头表示实际的执行顺序,虚线箭头表示等待执行。这里只画出根节点的左子树的执行顺序,右子树同理即可。我们看到当左子树为空时,便遍历右子树,当右子树也为空时,返回到上上层的右子树继续遍历,此处返回时2的右子树已经为空,所以开始遍历根节点的右子树,然后右子树又可以按这样的方式继续遍历...
二、中序遍历
中序遍历的代码如以下所示,当根节点非空时,先中序遍历左子树,再打印根节点,最后中序遍历右子树。
void vin_order(TreeNode* r) {
TreeNode* p = r;
if (p != NULL) {
vin_order(p->left);
cout << p->val << " ";
vin_order(p->right);
}
}
同样我们画出中序遍历根节点的左子树的执行顺序图。当遍历到左子树为空时,便输出节点值,接着遍历右子树;当右子树没有叶子节点时便返回上一层,并输出上一层节点值。
三、后序遍历
在解释后序遍历前我们不妨先算出上面的二叉树的后序遍历结果!后序遍历:{7,4,2,5,8,6,3,1}
后序遍历执行顺序为:左右根。后序遍历左子树,后序遍历右子树,输出根节点。只需记住,根节点一定在最后输出。
void post_order(TreeNode* r) {
TreeNode* p = r;
if (p != NULL) {
post_order(p->left);
post_order(p->right);
cout << p->val << " ";
}
}
我们将后序顺序完整画出,执行时候一定是按照左右根的顺序,当左子树遍历完毕后一定是返回的上层的右子树继续遍历,在输出上层的节点值。图中黄色虚线表示上层的右子树为空,直接输出节点值。
说明:本文以牛客网上的剑指Offer的第四题重建二叉树进行知识回顾。下面给出该题的实现以及遍历的代码。
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
using namespace std;
#include <vector>;
#include <iostream>;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {} //初始化TreeNode(int x),把x赋值给val,left和right都置为空
};
/*
思想:先找到根节点,然后依据根节点,又划分左子树前序和左子树中序;右子树前序和右子树中序,然后递归调用重建二叉树的方法
*/
class Solution {
public:
TreeNode* reConstructBinaryTree(vector<int> pre, vector<int> vin) {
if (pre.size() == 0 || vin.size() == 0)
return NULL;
TreeNode* p = new TreeNode(pre[0]);
int i;
for (i = 0; i < vin.size(); i++)
if (pre[0] == vin[i])
break;
vector<int> left_pre, left_vin, right_pre, right_vin;
for (int j = 0; j < i; j++)
{
left_pre.push_back(pre[j + 1]);
left_vin.push_back(vin[j]);
}
for (int j = i + 1; j < pre.size(); j++)
{
right_pre.push_back(pre[j]);
right_vin.push_back(vin[j]);
}
p->left = reConstructBinaryTree(left_pre, left_vin);
p->right = reConstructBinaryTree(right_pre, right_vin);
return p;
}
};
void pre_order(TreeNode *r) {
TreeNode *p = r;
if (p != NULL) {
cout << p->val << " ";
pre_order(p->left);
pre_order(p->right);
}
}
void vin_order(TreeNode *r) {
TreeNode* p = r;
if (p != NULL) {
vin_order(p->left);
cout << p->val << " ";
vin_order(p->right);
}
}
void post_order(TreeNode *r) {
TreeNode* p = r;
if (p != NULL) {
post_order(p->left);
post_order(p->right);
cout << p->val << " ";
}
}
int main() {
vector<int> pre = { 1,2,4,7,3,5,6,8 };
vector<int> vin = { 4,7,2,1,5,3,8,6 };
Solution s;
TreeNode *p = s.reConstructBinaryTree(pre, vin); //重建二叉树
pre_order(p); //前序遍历输出
cout << endl;
vin_order(p); //中序遍历输出
cout << endl;
post_order(p); //后序遍历输出
return 0;
}