基本概念
树是非线性数据结构,它能很好的描述数据的层次关系。树形结构的现实场景很常见,例如,文件目录,书本的目录就是典型的树形结构。
二叉树是最常用的树形结构,特别适合程序设计,常常将一般的树转换成二叉树来处理。
二叉树的存储
二叉树的性质
二叉树的每个结点最多有两个子结点,分别是左孩子,右孩子,以它们为根的子树称为左子树和右子树。
二叉树的第i层最多有2的i-1次
方给结点。如果每一层的节点数都是满的称它为满二叉树
。一个n层的满二叉树,结点数量一共有2的n次方-1个
。如果满二叉树只在最后一层有确实,并且确实的编号都在最后,那么称为完全二叉树
。
二叉树的存储结构
二叉树一般使用指针来实现,并指向左右子结点。
struct node{
int value; //结点的值
node *left,*right; //指向左右结点
}node;
在新建一个node时,用new运算符动态申请内存。使用完毕后,应该用delete释放它,否则会内存泄漏。
二叉树也可以用数组来实现。特别是完全二叉树,用数组来表示父结点和子结点的关系非常简便。
二叉树的遍历
以如下图作为参考:
宽度优先遍历
有时候需要按层次一层层的遍历二叉树。如上图按EBGADFICH的顺序进行访问,那么用宽度优先搜索
是最合适的。使用队列进行实现。
深度优先遍历
用深度优先搜索遍历二叉树,代码及其简单。
按深度搜索的顺序访问二叉树,对根结点,左儿子,右儿子进行组合。有先序遍历,中序遍历,后续遍历。默认左儿子在右儿子的前面
先序遍历
按父结点,左儿子,右儿子的顺序进行遍历,上图返回的顺序就是:EBADCGFIH
伪代码如下
void preorder(node *root){
cout<<root->value;
preoder(root->left);
preorder(root->right);
}
中序遍历
按左儿子,父结点,右儿子的顺序进行遍历,返回的顺序就是:
ABCDEFGHI 。这不是巧合,因为图这棵树是一个二叉搜索树
。在二叉搜索树中,中序遍历实现了排序的功能,返回的就是一个 有序排列。
后序遍历
按左儿子,右儿子,父结点的顺序进行遍历。上图的返回顺序就是:ACDBFHIGE。后序遍历的最后一个结点就是根结点。
如果已知某颗二叉树的3种遍历,可以把这颗树构造出来,即“中序遍历+先序遍历”,“中序遍历+后序遍历”都能确定一颗树,但是“先序遍历+后序遍历不一定能确定一颗树”
例题
题目
输入二叉树的先序和中序遍历,求后序遍历
(1)输入样例:
先序:1 2 4 7 3 5 8 9 6
中序:4 7 2 1 8 5 9 3 6
(2):输出样例:
后序:7 4 2 8 9 5 6 3 1
思路
根据中序遍历和先序遍历的结果,可以确定根结点为1,对比中序遍历,即1的左边的全为左子树上的,1的右边的全为右子树上的。
源码
#include<iostream>
using namespace std;
const int N = 1024;
int pre[N], in[N], post[N]; //用来存放先序,中序,后序的数据
int k = 1; //用来存放树的结点数
struct node {
int value;
node* left, * right;
node(int value = 0, node* left = NULL, node* right = NULL) :value(value),left(left),right(right){}
};
//构建树
void buildtree(int left,int right,int &t,node* &root) {
int flag = -1;
for (int i = left; i <= right; i++) {
if (in[i] == pre[t]) { //根据先序遍历的根结点匹配中序遍历
//根结点的为止区分出左右子树
flag = i;
break;
}
}
if (flag == -1) {
return; //如果没有根结点了,就直直接返回,不做处理
}
root = new node(in[flag]); //如果找到了根结点,就在堆上为该结点申请空间
t++;//传入先序遍历的下一个,寻找下一个根结点
if (flag > left)buildtree(left, flag - 1, t, root->left);//建立左子树
if (flag < right)buildtree(flag + 1, right, t, root->right);//建立右子树
}
//先序遍历输出
void preorder(node* root) {
if (!root) {
pre[k++] = root->value;
preorder(root->left);
preorder(root->right);
}
}
//中序遍历输出
void inorder(node* root) {
if (!root) {
inorder(root->left);
in[k++] = root->value;
inorder(root->right);
}
}
//后序遍历输出
void postorder(node* root) {
if (root) {
postorder(root->left);
postorder(root->right);
post[k++] = root->value;
}
}
//释放空间
void remove_tree(node* root) {
if (!root) return;
remove_tree(root->left);
remove_tree(root->right);
delete(root); //该结点左,右儿子均没有就释放该空间
}
int main() {
cout << "输入结点总数:";
int n;
while (scanf_s("%d", &n)!=EOF) {
cout << "输入先序遍历数据:";
for (int i = 1; i <= n; i++) scanf_s("%d", &pre[i]);
cout << "输入后序遍历数据:";
for (int i = 1; i <= n; i++) scanf_s("%d", &in[i]);
//传参依据先序和中序的数据构建原始树
int t = 1;//先序结果中,第一个结点一定是根结点
node* root;
buildtree(1, n, t, root);
//后序输出
postorder(root);
//打印结果
for (int i = 1; i < k; i++) printf("%d %c", post[i], i == k - 1 ? '\n' : ' ');
remove_tree(root);//释放申请的内存空间
}
system("pause");
return 0;
}
运行结果
同理,已知中序和后序,输出先序
源码
#include<iostream>
using namespace std;
const int N = 1024;
int pre[N], in[N], post[N];
int k = 1;
//创建树的结构体
struct node {
int value;
node* left, * right;
node(int value=0, node* left=NULL,node* right=NULL ):value(value),left(left),right(right){}//构造函数
};
//创建树
void buildtree(int left, int right, int &t, node* &root) {
int flag = -1;
for (int i = left; i <= right; i++) {
if (in[i] == post[t]) {
flag = i;
break;
}
}
if (flag == -1) {
return;
}
t--;
root = new node(in[flag]); //堆山申请结点
if (flag < right)buildtree(flag + 1, right, t, root->right);
if (flag > left) buildtree(left, flag - 1, t, root->left); //基于中序数据,分为左右子树
}
//先序输出
void preorder(node* root) {
if (!root) {
return;
}
else {
pre[k++] = root->value;
preorder(root->left);
preorder(root->right);
}
}
//释放内存
void remove_tree(node* root) {
if (!root) return;
else {
remove_tree(root->left);
remove_tree(root->right);
delete(root);
}
}
int main() {
int n;
cout << "输入结点个数:";
while (scanf_s("%d", &n)) {
cout << "输入中序数据:";
for (int i = 1; i <= n; i++) scanf_s("%d", &in[i]);
cout << "输入后序数据:";
for (int i = 1; i <= n; i++) scanf_s("%d", &post[i]);
int t = n;
node* root;
//创建树
buildtree(1, n, n, root);
//先序输出
preorder(root);
//打印先序数据
for (int i = 1; i < k; i++) printf("%d %c", pre[i], i == k - 1 ? '\n' : ' ');
//释放内存空间
remove_tree(root);
}
system("pause");
return 0;
}