树
基本概念
1、有根树
如上图所示:圆圈代表结点,连接结点的线代表边。树由结点和连接结点的边组成。
2、有根结点的父子关系:如图根结点为0,连接根结点0到结点4最后一条边连接着结点1和4,则结点1称为结点4的父节点,结点4 为结点1 的子节点。结点2,3为结点4的兄弟结点。
3、根结点:没有父节点的结点(唯一)
外部结点(叶结点):没有子节点的结点(如4,5)
内部结点:除叶节点以外的结点
结点度、高、深度
1、结点的度:一个结点拥有的子节点总数称为结点的度(如节点2,有三个子节点,则节点2的度为3);
2、结点的深度:根结点到某一节点(x)的路径的长度称为 结点x 的深度(如节点2,根结点到2 只经过一条边,则x的深度为1);
3、结点的高:某一节点(x)到子结点的最大路经长称为结点x的高(如节点2,到子节点11(12)路径最长,高度位2)
一颗数的根结点的高度最大,(如节点8,其深度为2,高为1,树高为3)
二叉树
1、概念:拥有一个根结点,且其他结点的子结点数都不超过2。
2、有序树:在二叉树中,每个结点的子节点数不超过2个,而且还分左节点和右节点,像这种子节点有特定顺序的树称为有序树。
// 二叉树的表达
//输入结点个数N,每个结点信息,id(结点编号)left(左子结点编号)
right(右子结点编号。
//输出node:id :parent = p,sibling = s,degree = deg (度)
,depth = dep(深度),height = h(高),
type(结点类型(根,叶或者内结点)
#include <iostream>
#define MAX 10000
#define NIL -1
using namespace std;
typedef struct Node
{
int parent;
int left;
int right;
}Node;
Node T[MAX];
int N,D[MAX],H[MAX];
int getHeight(int u)
{
int h1 = 0,h2 = 0;
if(T[u].left != NIL)
{
h1 = getHeight(T[u].left)+1;
}
if(T[u].right != NIL)
{
h2 = getHeight(T[u].right)+1;
}
H[u] = max(h1,h2);
return H[u];
}
void getDepth(int u, int d)
{
D[u] = d;
if(T[u].left!= NIL)
{
getDepth(T[u].left,d+1);
}
if(T[u].right!= NIL)
{
getDepth(T[u].right,d);
}
}
int getSibling(int u)
{
if(T[u].parent == NIL)
{
return NIL;
}
if(T[T[u].parent].left!=u && T[T[u].parent].left!=NIL)
{
return T[T[u].parent].left;
}
if(T[T[u].parent].right!=u && T[T[u].parent].right!=NIL)
{
return T[T[u].parent].right;
}
return NIL;
}
void Print(int u)
{
cout<<"node:"<<u;
cout<<"parent="<<T[u].parent;
cout<<"sibling="<<getSibling(u);
int degree = 0;
if(T[u].left != NIL) degree++;
if(T[u].right != NIL) degree++;
cout<<"degree = "<<degree<<",";
cout<<"depth = "<<D[u]<<",";
cout<<"height = "<<H[u]<<",";
if(T[u].parent == NIL) cout<<"root"<<endl;
else if(T[u].left == NIL && T[u].right == NIL) cout<<"leaf"<<endl;
else cout<<"internal node"<<endl;
//int c;
/*int node= T[u].left;
cout<<"[";
for(int i=0;node!=NIL;i++)
{
if(i) cout<<", ";
cout<<node;
node=T[u].right;
}
cout<<"]"<<endl;*/
}
int main()
{
int v,l,r,root=0;
cout<<"请输入结点个数:";
cin>>N;
for(int i=0;i<N;i++)
{
T[i].parent = NIL;
T[i].left = NIL;
T[i].right = NIL;
}
for(int i=0;i<N;i++)
{
cout<<"请输入各结点信息:";
cin>>v>>l>>r;
T[v].left = l;
T[v].right = r;
if(l!=NIL) T[l].parent = v;
if(r != NIL) T[r].parent = v;
}
for(int i=0;i<N;i++)
{
if(T[i].parent == NIL)
root = i;
}
getDepth(root,0);
getHeight(root);
for(int i=0;i<N;i++)
{
Print(i);
}
return 0;
}
树的遍历
1、前序遍历:按照根结点、左子树、右子树的顺序输出结点编号。
下面展示一些 内联代码片
。
// 前序,先访问u,打印出结点u,然后执行preParse(T[u].left)
访问u的左子树,处理完毕再执行preParse(T[u].right)访问u的右子树,
u=NIL时没有后续结点,函数结束。其他两种只是打印顺序的调整
void preParse(int u)
{
if(u == NIL)
return;
cout<<u<<" ";
preParse(T[u].left);
preParse(T[u].right);
}
2、中序遍历:按照左子树、根结点、右子树的顺序输出结点编号。
void inParse(int u)
{
if(u == NIL)
{
return;
}
inParse(T[u].left);
cout<<u<<" ";
inParse(T[u].right);
}
3、后续遍历:按照左子树、右子树、根结点的顺序输出结点编号。
void postParse(int u)
{
if(u == NIL)
{
return;
}
postParse(T[u].left);
postParse(T[u].right);
cout<<u<<" ";
}
树的重建
比如当我们知道一个树的前序排列,中序排列,让我们来求这个数的后序排列,这其实就是一种树的重建。
下面这段代码就是根据已知的前序,中序排列,求树的后序排列。
//前序排列为1 2 3 4 5 6 7 8 9
//中序排列为3 2 5 4 6 1 8 7 9
//先遍历前序排列,当前结点为c,然后可以通过查询结点c在in中的位置m,
m的左边就是c的左子树,右边就是c的右子树,然后一步一步找下去。
void reconstruction(int left,int right)
{
if (left>=right)
{
return;
}
int root = pre(pos++);
int m = distance(in.begin(),find(in.begin(),in.end(),root));
reconstruction(left,m);
reconstruction(m+1,right);
post.push_back(root);
}