- 树是一种数据结构,再介绍二叉树之前,先解释一下什么是树;
1.树由 根结点 开始,它从 根结点 一直延申到 叶结点结束,根结点 与 叶结点之间的结点统称为 内部结点;
2.树的每一个结点上都可以存储数据,同时存储着下一个 子节点 的地址,因此 树 可以看作是由一个根结点起始的 多条多向链表;
3.从图的概念来看,树也可以看作是 不包含回路的连通无向图;
有关树的基础概念:
根节点:一棵树只有一个根结点,不过也存在没有根结点的树,这种树称为空树;
叶节点:树的末尾结点,叶结点的下一级不会再有结点,即 叶结点指向 空;
内部节点:根结点与叶结点之间的所有结点;
边:连通两个结点的路径称为 边;一棵树的 边数=结点数-1;
子节点,父节点,兄弟结点,祖宗结点:即字面意思,不解释;
树的层次:即树从根结点至叶结点最大有多少层;
结点的度:结点的子树棵数成为 结点的度;
树的度:也叫树的宽度,它从所有 结点的度中 挑选最大的作为 树的度;
森林:多棵树组合在一起成为森林;
【注意】对于任意一个结点,它自己既是自己的祖宗结点,也是自己的子孙结点;
- 二叉树
二叉树是 树里面应用非常广泛的一种 特殊树的结构;
对 二叉树的定义:二叉树的每一个结点至多有两个子结点;言外之意即 二叉树也可以是一颗空树;
满二叉树:树的每层的结点个数都达到了当层能达到的最大值;
完全二叉树:除最下面一层外,其它各层都达到了当层能达到的结点最大值,且对于最下面一层,只有从左至右存在若干连续结点,而它的右边无结点;
故满二叉树包含在完全二叉树之内;
堆也是一种特殊的完全二叉树;
- 有关二叉树的基本操作
(1)利用链表的方式定义二叉树:
struct node{
int data;
node *lchild;//指向左子树结点的指针
node *rchild;//指向右子树结点的指针
}
(1’’)利用动态数组的方式定义二叉树:
struct node{
int data;
int lchild;
int rchild;
}Node[maxn];
(2)新建结点:
node *newnode(int x)
{
node *root=new node;
root->data=x;
root->lchild=root->rchild=NULL;
return root;//建立了一个左右子结点暂时指向空的新结点,且该结点的数据域为x;
}
(2’’)静态实现新建结点
int index=0;
node *newnode(x)
{
Node[index].data=x;
Node[index].lchild=Node[index].rchild=-1;//-1用来表示NULL;
return index++;//返回下标,并自加;
}
(3)查找,修改结点
void search(node *root,int x,int data)
{
if(root==NULL)
return;//递归边界
if(root->data==x)
root->data=data;//实现修改
search(root->lchild,x,data);
search(root->lchild,x,data);//递归
}
(3’’) 静态实现查找,修改结点
void search(node *root,int x,int data)
{
if(root==-1)
return;
if(Node[root].data==x)
Node[root].data=data;
search(Node[root].lchild,x,data);
search(Node[root].rchild,x,data);
}
可以看出 用链表实现与静态实现差别不大,下面的代码中将省略静态实现
(4)插入结点
void insert(node *&root,int x)//这里需要对root用引用,因为涉及到对二叉树结构的修改
{
if(root=NULL){ //找到插入位置
root=newnode(x);//新建结点
return;
}
if()//根据需要选择插入左子树还是右子树
insert(root->lchild,x);
else
insert(root->rchild,x);
}
(5)创建二叉树
node *create(int data[],int n)
{
node *root=NULL;
for(int i=0;i<n;i++)
{
insert(root,data[i]);
}
return root;
}
- 二叉树的遍历
二叉树的遍历有四种遍历方法,分别是 先序遍历,中序遍历,后序遍历以及层序遍历;其中前三种遍历方法一般用DFS实现,层序遍历一般用BFS实现;
另外注意,这里的先序,中序,以及后序都是相对于根结点来说的,遍历过程中左子树的遍历始终在右子树之前,只是根结点的遍历顺序进行了更改;
先序遍历:
void preorder(node *root)
{
if(node==NULL)
return;
cout<<root->data;
preorder(root->lchild);
preorder(root->rchild);
}
中序遍历:
void inorder(node *root)
{
if(node==NULL)
return;
preorder(root->lchild);
cout<<root->data;
preorder(root->rchild);
}
后序遍历:
void postorder(node *root)
{
if(node==NULL)
return;
preorder(root->lchild);
preorder(root->rchild);
cout<<root->data;
}
注意 cout<data; 语句的顺序;
层序遍历:
void C_order(node *root)
{
queue<node *> q;
q.push(root);
while(!q.empty())
{
node *now=q.front();
q.pop();
cout<<now->data;
if(now->lchild!=NULL) q.push(root->lchild);
if(now->rchild!=NULL) q.push(root->rchild);
}
}
- 利用遍历序列构建二叉树
通过遍历序列,我们可以还原二叉树;
我们只需要两种方式的序列数据即可还原,但这两种遍历方法中必须要有一个为中序遍历;
一个例子,利用 后序遍历和中序遍历的数据还原二叉树,并在之后输出它的 层序遍历数据:
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=50;
struct node{
int data;
node *lchild;
node *rchild;
};
int n;
int pre[maxn],in[maxn],post[maxn];
node *create(int postL,int postR,int inL,int inR)
{
if(postL>postR)
return NULL;
node *root=new node;
root->data=post[postR];
int k;
for(k=inL;k<=inR;k++)
{
if(in[k]==post[postR])
break;
}
int numLeft=k-inL;
root->lchild=create(postL,postL+numLeft-1,inL,k-1);
root->rchild=create(postL+numLeft,postR-1,k+1,inR);
return root;
}
int num=0;
void bfs(node *root)
{
queue<node *> q;
q.push(root);
while(!q.empty())
{
node *now=q.front();
q.pop();
cout<<now->data;
num++;
if(num<n) cout<<" ";
if(now->lchild!=NULL) q.push(now->lchild);
if(now->rchild!=NULL) q.push(now->rchild);
}
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>post[i];
}
for(int i=0;i<n;i++)
{
cin>>in[i];
}
node *root=create(0,n-1,0,n-1);
bfs(root);
return 0;
}