树
双亲表示法:
就是定义一个数组给出它的数据和双亲的下标。
可用来找节点双亲。
#include<stdio.h>
#define max 10000
typedef struct
{
int data;
int parent;
}PTNode;
typedef struct pTree
{
PTNode a[max];
int count;
}PTree;
void creat(PTree *t,int n);
int main()
{
PTree t;
int n;
scanf("%d",&n);
creat(&t,n);
}
void creat(PTree *t,int n)
{
int i;
printf("请输入数据和其双亲的位置\n");
for(i=0;i<n;i++)
{
scanf("%d%d",&t->a[i].data,&t->a[i].parent);
}
}
还可以多加一个firstchild就方便找当前节点的孩子,其他孩子可以用第一孩子下标得到之后的孩子下标。就是需要判断双亲下标是不是自己。因为是用层序输入的数据。
孩子表示法
方案一:
定义一个数组将树按照层序的顺序存储进入数组,然后将每一个节点的孩子所在的下标给出连接即可。
#include<stdio.h>
#define max 10000
#define degree 3
typedef struct pTNode
{
int data;
struct pTNode *child[degree];//根据树的最大度来定义
}PTNode;
typedef struct pTree
{
PTNode a[max];
}PTree;
void creat(PTree *t,int n);
int main()
{
PTree t;
int n;
scanf("%d",&n);
creat(&t,n);
}
void creat(PTree *t,int n)
{
int i,j;
printf("请输入数据\n");
for(i=0;i<n;i++)
{
scanf("%d",&t->a[i].data);
}
printf("请输入每个节点的孩子下标:\n格式: 第一个孩子下标 第二个孩子下标...(没有孩子输入0)\n");
for(i=0;i<n;i++)
{
for(int k=0;k<degree;k++)
{
scanf("%d",&j);
if(j)
t->a[i].child[k]=&t->a[j];
else
t->a[i].child[k]=0;
}
}
}
方案二:
定义一个数组将树按照层序的顺序存储进入数组,给出度的个数(孩子数),然后通过下标得到指针,并创建指针存放空间,将他们连接起来。需要多建立一个firstchild和child指针,firstchild是孩子链表的头。
孩子兄弟表示法
定义一个数组将树按照层序的顺序存储进入数组,然后给出每一个节点的左孩子,以及它所有的右孩子。(树转二叉树的方法)
连接规则:
1、右孩子是连接到此节点左孩子的rightsib指针的,每一个右孩子rightsib连接下一个此节点的右孩子。形成一个右孩子链表。
2、左孩子是连接到此节点的firstchild指针的
#include<stdio.h>
#define max 10000
#define degree 3
typedef struct pTNode
{
int data;
struct pTNode *firstchild,*rightsib;//根据树的最大度来定义
}PTNode;
typedef struct pTree
{
PTNode a[max];
}PTree;
void creat(PTree *t,int n);
int main()
{
PTree t;
int n;
scanf("%d",&n);
creat(&t,n);
}
void creat(PTree *t,int n)
{
int i,j,k;
PTNode *p;
printf("请输入数据\n");
for(i=0;i<n;i++)
{
scanf("%d",&t->a[i].data);
t->a[i].firstchild=t->a[i].rightsib=0;
}
for(i=0;i<n;i++)
{
printf("请输入节点的左孩子下标:(没有输出0)\n");
scanf("%d",&j);
if(j==0)
break;
p=t->a[i].firstchild=t->a+j;
printf("请输入此节点的右孩子数:\n");
scanf("%d",&k);
for(int x=0;x<k;x++)
{
printf("请输入节点的右孩子下标:\n");
scanf("%d",&j);
p->rightsib=&t->a[j];
p=p->rightsib;
}
}
}
二叉树
分为
斜数:只有右子树或者左子树。
满二叉树:
每个分子有左子树和右子树,叶节点在同一层。
完全二叉树:
就是按照层序编号树节点,不会出现编号不连续。
完全二叉树不一定是满二叉树,满二叉树一定是完全二叉树。
二叉树性质
1、第I层最多有2的i-1次方个节点。
2、深度为K的二叉树最多有2的k-1次方个节点。
3、n0为叶节点数,n1为度为1的节点数,n2为度为2的节点数,n0=n2+1.
4、n个节点的完全二叉树深度为(log2 n)+1
5、对于节点i
i/2为双亲节点
i2为该节点的左孩子
i2+1为该节点的右孩子
二叉树的存储结构
顺序存储:用数组存储,按照层序顺序赋值,按照完全二叉树的规格来处理,没有值的赋值为0.
链式存储:
定义左孩子指针和右孩子指针,没有的为0.
可以通过前序,中序,后序的遍历方法建立二叉链表。
二叉树的遍历
前序遍历方法:根->左子树->右子树
中序遍历方法:左子树->根->右子树
后序遍历方法:左子树->右子树->根
已知前序遍历序列和中序遍历序列可以得到二叉树。
已知中序遍历序列和后序遍历序列可以得到二叉树。
如果遍历有疑惑可以通过知道遍历是递归调用函数,来确定下一个值。
typedef struct tree
{
int data;
struct tree *lchild,*rchild;
}Tree;
void travel(Tree *t)
{
if(t==NULL)
return;
else
{
printf("%d\n",t->data);//前序
travel(t->lchild);
travel(t->rchild);
/*
travel(t->lchild);
printf("%d\n",t->data);//中序
travel(t->rchild);
*/
/*
travel(t->lchild);//后序
travel(t->rchild);
printf("%d\n",t->data);
}
}
二叉树的存储
输入数据是以完全二叉树为标准没有值的地方输入#,需要按照前序、中序、后序其中一个的顺序输入数据。
typedef struct tree
{
char data;
struct tree *lchild,*rchild;
}Tree;
void creat(Tree **t)//t二级指针,接的是一个指针的指针。
{
cahr c;
scanf("%c",&c);
if(ch=='#')
*t=NULL;
else
{
*t=(Tree *)malloc(sizeof(Tree));//前序
(*t)->data=ch;
travel(t->lchild);
travel(t->rchild);
线索二叉树
就是将二叉树不用的lchild和rchild用来存储前继和后继,最后形成一个双向链表。
多定义了
flag1(判断是前继1还是左孩子0)
flag2(判断是后继1还是右孩子0)
#include<stdio.h>
typedef struct Tree
{
char data;
struct Tree *lchild,*rchlid;
int flag1,flag2;
}Tree;
Tree *pre;//前一个节点
void creat(Tree **t);
void intravel(Tree *t);
Tree *inhead(Tree *t);
void travel(Tree *t);
int main()
{
Tree *t;
creat(&t);//创建树
intravel(t);//创建线索树
travel(t);
}
void creat(Tree **t)//中序创建二叉树
{
char c;
scanf("%c",&c);
if(c=='#')
*t=NULL;
else
{
creat(*t->lchild);
*t=(Tree *)malloc(sizeof(Tree));
(*t)->data=c;
(*t)->flag1=(*t)->flag2=0;//默认为孩子
creat(*t->rchild);
}
}
void intravel(Tree *t)
{
if(t)
{
creat(t->lchild);
if(t->lchild==NULL)
{
t->lchild=pre;
t->flag1=1;
}
if(pre->rchild==NULL)//因为当前节点无法知道后继节点,所以求的是前一个的。
{
pre->rchild=t;
pre->flag2=1;
}
pre=t;
}
}
Tree *inhead(Tree *t)//插入头节点
{
Tree *head;
static flag=1;
if(flag)
{
head=(Tree *)malloc(sizeof(Tree));
head->lchild=t;
head->flag1=0;
flag=0;
}
inhead(t->lchild);
if(t->lchild==NULL)//找到中序第一个节点
{
t->lchild=head;
t->flag1=1;
}
if(t->rchild==NULL)//最后一个节点,因为已经是一个双向链表了。
{
t->rchild=head;
head->rchild=t;
t->flag2=1;
head->flag2=1;
return head;
}
inhead(t->rchild);
}
void travel(Tree *t)
{
Tree *p=t->lchild;
while(p)
{
while(p->flag1==0&&p->lchild1!=t)//第一个节点是指向头节点的
{
p=p->lchild;
}
printf("%c ",p->data);
while(p->flag2==1&&p->rchlid!=t)//中序最后一个节点是指向头节点的
{
p=p->rchild;
printf("%c ",p->data);
}
p=p->rchlid;
}
}
树、森林与二叉树的转换
树转二叉树
方法:
1、在所有同一个双亲兄弟之间连线
2、删除所有孩子与双亲的连线除了第一个孩子。
3、第一个孩子作为左孩子,其他作为节点右孩子。
二叉树转树
方法:
1、所有的右孩子与它双亲的双亲连线。
2、删除右节点与它双亲的连线。
森林转二叉树
方法:
1、像树一样将每一个树转成二叉树
2、后一个二叉树作为前一个二叉树根的右节点连接。
二叉树转森林
方法:
1、从根节点开始查看是否有右节点,有就分离,然后查看分离的二叉树是否有右节点,然后分离,直到没有右节点。
树与森林的遍历方法
前序:根->左子树->右子树
后序:左子树->右子树->根
森林需要一棵树,一棵树的遍历。
赫夫曼树
创建:
1、把每一个节点当作一个只有一个节点的二叉树
2、找到两个权值最小的二叉树,连接到新的节点,新的节点权值是两节点权值的和。
3、最后一个节点是根节点,n个节点创建的树有2*n-1个节点。
#include<stdio.h>
#include<stdlib.h>
typedef struct tree
{
int data;
int parent,lchild,rchild;
}Tree;
Tree *creat(int a[],int n);
void find(Tree *p,int *x1,int *x2,int n);
int main()
{
int n,i;
Tree *t;
printf("请输入节点个数\n");
scanf("%d",&n);
int a[n];
printf("请输入节点值\n");
for(i=0;i<n;i++)
scanf("%d",a+i);
t=creat(a,n);
}
Tree *creat(int a[],int n)
{
Tree *p;
int i,x1,x2;
p=(Tree *)malloc((n*2)*sizeof(Tree));
for(i=1;i<=n;i++)//初始化节点
{
p[i].data=a[i];
p[i].parent=p[i].lchild=p[i].rchild=0;
}
for(i=n+1;i<=n*2-1;i++)//初始化剩下的节点
{
p[i].data=0;
p[i].parent=p[i].lchild=p[i].rchild=0;
}
for(i=n+1;i<=n*2-1;i++)
{
find(p,&x1,&x2,n);//寻找两个最小权值下标
p[i].data=p[x1].data+p[x2].data;
p[x1].parent=p[x2].parent=i;
}
return p+(n*2)-1;//最后一个就是二叉树的头节点
}
void find(Tree *p,int *x1,int *x2,int n)
{
int flag=1,i;
for(i=1;i<=n;i++)
{
if(p[i].parent!=0)
{
*x1=i;
break;
}
}
for(i=1;i<=n;i++)
{
if(p[i].parent==0)
if(p[i].data<p[*x1].data)
{
*x1=i;
}
else
{
if(flag)
{
*x2=i;
flag=0;
}
else if(p[i].data<p[*x2].data)
*x2=i;
}
}
}