存储:顺序存储和链式存储
顺序存储:
顺序存储是将二叉树结点存储在一个一位数组中,因此,必须把二叉树的所有结点安排成为一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系,用编号的方法从树根起,自上层至下层,每层自左至右地给所有结点编号。
在这里为了更好地说明问题,我们举一个例子:
有一个表达式:a+b*(c-d)-e/f,我们现在需要采用顺序存储的方式来建立起这颗二叉树。
首先,我们需要清楚的是,这个表达式所建立起来的二叉树是什么样子的,这样我们才能正确的存入结点和正确的遍历这个二叉树。
因为这是一个表达式,我们遵循表达式的结合顺序来建立这颗二叉树,它应该是这个样子的:
注意虚线处的结点本身是不存在的,但是为了方便后面的说明,我在这里加了上去。
所以我们的一位数组中的存储就是将这颗二叉树的结点从上到下,从左往右依次填充在数组中(注意这里以完全二叉树存储),也就是图中所标注结点的序号和数组的下标一一对应,对于空节点,我们用0表示。
/*
二叉树的顺序存储以及遍历
*/
#include<stdio.h>
#define max_size 100
char Btree[max_size] = { 0 };
int len = 0;
void Btree_create()
{
char c;
int i = 0;
while (i<max_size)
{
scanf_s("%c", &c);
if (c=='&')
{
break;
}
Btree[i] = c;
++i;
}
if (i>=100)
{
return;
}
len = i;
}
void Btree_pre(char Btree[],int start)
{
int tmp = start;
if (Btree[tmp]==0 || tmp >= len)
{
return ;
}
//打印
if (Btree[tmp]!='0')
{
printf("%c", Btree[tmp]);
}
//左子树
Btree_pre(Btree, 2 * tmp + 1);
//右子树
Btree_pre(Btree, 2 * (tmp+1));
}
void Btree_mid(char Btree[], int start)
{
int tmp = start;
if (Btree[tmp] == 0 || tmp >= len)
{
return;
}
//左子树
Btree_mid(Btree, 2 * tmp + 1);
//打印
if (Btree[tmp] != '0')
{
printf("%c", Btree[tmp]);
}
//右子树
Btree_mid(Btree, 2 * (tmp + 1));
}
void Btree_tail(char Btree[], int start )
{
int tmp = start;
if (Btree[tmp] == 0 || tmp >= len)
{
return;
}
//左子树
Btree_tail(Btree, 2 * tmp + 1);
//右子树
Btree_tail(Btree, 2 * (tmp + 1));
//打印
if (Btree[tmp] != '0')
{
printf("%c", Btree[tmp]);
}
}
int main()
{
//顺序存储建立二叉树
Btree_create();
//先序遍历
printf("先序遍历\n");
Btree_pre(Btree,0);
printf("\n");
//中序遍历
printf("中序遍历\n");
Btree_mid(Btree, 0,max_size);
printf("\n");
//后序遍历
printf("后序遍历\n");
Btree_tail(Btree, 0,max_size);
printf("\n");
system("pause");
return 0;
}
顺序存储可能会浪费空间(在非完全二叉树的时候),但是读取某个指定的节点的时候效率比较高O(0)
链式存储相对二叉树比较大的时候浪费空间较少,但是读取某个指定节点的时候效率偏低O(nlogn)
二叉树的顺序存储,寻找后代节点和祖先节点都非常方便,但对于普通的二叉树,顺序存储浪费大量的存储空间,同样也不利于节点的插入和删除。因此顺序存储一般用于存储完全二叉树。
链式存储相对顺序存储节省存储空间,插入删除节点时只需修改指针,但寻找指定节点时很不方便。不过普通的二叉树一般是用链式存储结构。
遍历:先序遍历、中序遍历、后序遍历
/*
二叉树的链式存储与遍历
*/
#include<iostream>
using namespace std;
//定义节点
typedef struct node
{
struct node *lchild;
struct node *rchild;
char data;
}BiTreeNode, *BiTree;
//*BiTree的意思是给 struct node*起了个别名,叫BiTree,故BiTree为指向节点的指针。
//按照前序顺序建立二叉树
void createBiTree(BiTree &T) //&的意思是传进来节点指针的引用,括号内等价于 BiTreeNode* &T,目的是让传递进来的指针发生改变
{
char c;
cin >> c;
if ('#' == c) //当遇到#时,令树的根节点为NULL,从而结束该分支的递归
T = NULL;
else
{
T = new BiTreeNode;
T->data = c;
createBiTree(T->lchild);
createBiTree(T->rchild);
}
}
//前序遍历二叉树并打印
void preTraverse(BiTree T)
{
if (T)
{
cout << T->data << " ";
preTraverse(T->lchild);
preTraverse(T->rchild);
}
}
//中序遍历二叉树并打印
void midTraverse(BiTree T)
{
if (T)
{
midTraverse(T->lchild);
cout << T->data << " ";
midTraverse(T->rchild);
}
}
//后续遍历二叉树并打印
void postTraverse(BiTree T)
{
if (T)
{
postTraverse(T->lchild);
postTraverse(T->rchild);
cout << T->data << " ";
}
}
int main()
{
BiTree T; //声明一个指向二叉树根节点的指针
createBiTree(T);
cout << "二叉树创建完成!" << endl;
cout << "前序遍历二叉树:" << endl;
preTraverse(T);
cout << endl;
cout << "中序遍历二叉树:" << endl;
midTraverse(T);
cout << endl;
cout << "后序遍历二叉树:" << endl;
postTraverse(T);
system("pause");
return 0;
}