所以有:
k=【log2n】+1。
性质5:
如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第【log2n】+1层,每层从左到右),则对任一结点i(1<=i<=n),有:
(1)如果i=1,则结点i无双亲,是二叉树的根;如果i>1,则其双亲是结点【i/2】。
(2)如果2i>n,则结点i为叶子结点,无左孩子;否则,其左孩子是结点2i。
(3)如果2i+1>n,则结点i无右孩子;否则,其右孩子是结点2i+1。
二叉树的存储结构
1.顺序存储结构
它是用一组连续的存储单元存储二叉树的数据元素。
因此,必须把二叉树的所有结点安排成为一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系,用编号的方法:
#defineMAX-TREE-SIZE100
typedefTElemTypeSqBiTree[MAX-TREE-SIZE];
SqBiTreebt;
从树根起,自上层至下层,每层自左至右的给所有结点编号缺点是有可能对存储空间造成极大的浪费,在最坏的情况下,一个深度为h且只有h个结点的右单支树确需要2h-1个结点存储空间。
而且,若经常需要插入与删除树中结点时,顺序存储方式不是很好!
或:
#defineMaxSize树中结点的最大数
typedefstruct{
intdata[MaxSize];
intnode;
}SqBitree;
SqBitreeBT;
BT就是一维数组,用它来存储一颗二叉树,每个数组元素存储二叉树的一个结点的数据信息。
2.链式存储结构
二叉树存储的链式结构可以使用二叉链表,它的每个结点有一个数据域和两个指针域,一个指针指向左孩子,另一个指针指向右孩子。
二叉链表的结点结构描述如下:
typedefstructnode{
chardata;
structnode*lchild,*rchild;
}BinNode,*BinTree;
给出一颗二叉树的二叉链表存储结构就是给出指向根结点的BinTree型头指针Root.
在二叉链表中,容易找到一个结点的左右孩子,但是不易找结点的双亲。
为了方便查找孩子和双亲,还可以使用三叉链表,其链表结点结构描述如下:
typedefstructnode{
chardata;
structnode*lchild,*rchild,*parent;
}BinList,*BinTree;
3.二叉树二叉链表的生成算法
1,非递归的朴素生成算法
根据二叉树的二叉链表存储结构,使用一种有辅助数组的创建二叉树的二叉链表的算法。
该算法利用了二叉树的性质5,对任意二叉树,先按满二叉树对其进行编号,然后按编号输入数据,构建二叉链表。
算法思想:
把非完全二叉树及诶单按对应的完全二叉树进行编号,此时结点的编号并不连续。
s[i]中应该存放编号为i的结点的地址指针。
/*
样例输入:
1A2B3C4D5E7F00
样例输出:
ABDECF
*/
#include
#include
typedefstructnode{
chardata;
structnode*lchild,*rchild;
}BinNode,*BinTree;
BinTreecreate(){
inti,j;
charx;
BinTrees[120],t=NULL,q;//t=NULL,定义指向根结点的指针
scanf("%d%c",&i,&x);
while(i!
=0&&x!
=0){//结束条件
q=(BinTree)malloc(sizeof(BinNode));//产生一个新结点
q->data=x;//赋结点值
q->lchild=NULL;//赋左孩子值
q->rchild=NULL;//赋有孩子值
s[i]=q;//用数组第i个单元存放指向此结点的指针
if(i==1)t=q;//若是第1个,将t指向它为根
else{//否则,为子树,计算子树的位置j
j=i/2;
if((i%2)==0)//j为偶数,是左子树
s[j]->lchild=q;
else//j为奇数,是右子树
s[j]->rchild=q;
}
scanf("%d%c",&i,&x);
}
returnt;//返回一个指向根的指针
}
voidprint(BinTreep){//先序输出二叉树
if(p!
=NULL){
printf("%c",p->data);
print(p->lchild);
print(p->rchild);
}
}
intmain(){
BinTreeroot;
root=create();//指针变量root指向一棵建立好的二叉树的二叉链表
print(root);
return0;
}
------------------------------------------------------------------------------------------------------------------------------
2,构造二叉链表的递归算法
根据二叉树的扩展先序序列,使用递归方法构造二叉链表
该算法中输入的数据是二叉树的扩展先序序列,即在先序序列中加入虚结点以示空指针的位置。
算法思想:
算法的输入是二叉树的扩展先序序列,首先输入一个根节点,若输入的是空格""字符,表示该二叉树为空树,即T=NULL;否则把输入的字符赋给T->data,之后,依次递归地建立它的左子树T->lchild和右子树T->rchild.
样例输入:
ABDECF
样例输出:
ABDECF
*/
#include
#include
typedefstructNode{
chardata;
structNode*lchild,*rchild;
}BinNode,*BinTree;
voidCreateBin(BinTree*T){
charch;
if((ch=getchar())=='')
*T=NULL;
else{
*T=(BinTree)malloc(sizeof(BinNode));
(*T)->data=ch;
CreateBin(&((*T)->lchild));
CreateBin(&((*T)->rchild));
}
}
voidprint(BinTreep){//先序输出二叉树
if(p!
=NULL){
printf("%c",p->data);
print(p->lchild);
print(p->rchild);
}
}
intmain()
{
BinTreeroot;
CreateBin(&root);
print(root);
return0;
}
------------------------------------------------------------------------------------------------------------------------------
一,二叉树的递归遍历算法:
假如以L、D、R分别表示遍历左子树、遍历根结点和遍历右子树,遍历整个二叉树则有DLR、LDR、LRD、DRL、RDL、RLD六种遍历方案。
若规定先左后右,则只有前三种情况,分别规定为:
DLR——先(根)序遍历,
LDR——中(根)序遍历,
LRD——后(根)序遍历。
1、先序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树。
2、中序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树。
3、后序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点。
程序实现:
遍历二叉树是指以一定的顺序访问二叉树中的每个结点,并且每个结点仅被访问一次,所谓访问结点是对节点进行各种操作的简称。
遍历二叉树的过程,就是把二叉树的结点进行排列的过程。
样例输入:
1A2B3C4D5E7F00
样例输出:
ABDECF
DBEACF
DEBFCA
*/
#include
#include
typedefstructnode{
chardata;
structnode*lchild,*rchild;
}BinNode,*BinTree;
BinTreecreate()
{
inti,j;
charx;
BinTrees[120],t=NULL,q;//t=NULL,定义指向根结点的指针
scanf("%d%c",&i,&x);
while(i!
=0&&x!
=0){//结束条件
q=(BinTree)malloc(sizeof(BinNode));//产生一个新结点
q->data=x;//赋结点值
q->lchild=NULL;//赋左孩子值
q->rchild=NULL;//赋有孩子值
s[i]=q;//用数组第i个单元存放指向此结点的指针
if(i==1)t=q;//若是第1个,将t指向它为根
else{//否则,为子树,计算子树的位置j
j=i/2;
if((i%2)==0)//j为偶数,是左子树
s[j]->lchild=q;
else//j为奇数,是右子树
s[j]->rchild=q;
}
scanf("%d%c",&i,&x);
}
returnt;//返回一个指向根的指针
}
voidpreorder(BinTreep){//先序输出二叉树
if(p!
=NULL){
printf("%c",p->data);
preorder(p->lchild);
preorder(p->rchild);
}
}
voidinorder(BinTreep){//中序输出二叉树
if(p!
=NULL){
inorder(p->lchild);
printf("%c",p->data);
inorder(p->rchild);
}
}
voidpostorder(BinTreep){//后序输出二叉树
if(p!
=NULL){
postorder(p->lchild);
postorder(p->rchild);
printf("%c",p->data);
}
}
intmain()
{
BinTreeroot;
root=create();//指针变量root指向一棵建立好的二叉树的二叉链表
preorder(root);puts("");
inorder(root);puts("");
postorder(root);puts("");
return0;
}
------------------------------------------------------------------------------------------------------------------------------
二,二叉树的非递归遍历算法:
为了写出非递归遍历算法,我们可以观察到递归工作栈的状态变化。
(1)当栈顶记录中的指针非空时,应遍历左子树,即指向左子树根的指针进栈。
(2)若栈顶记录中的指针值为空,则应退至上一层,若是从左子树返回,则应访问当前层即栈顶指针上指针所指的根结点;
(3)若是从右子树返回,则表明当前层的遍历结束,应继续退栈。
由此可得以下两个中序遍历二叉树的非递归算法,先序遍历二叉树的非递归算法与此类似,但后序遍历二叉树的非递归算法要相对复杂一点。
程序实现:
/*
样例输入:
ABDECF
样例输出:
ABDECF
DBEACF
DEBFCA
*/
#include
#include
#defineMaxSize100
typedefstructNode{
chardata;
structNode*lchild,*rchild;
}BinNode,*BinTree;
voidCreateBin(BinTree*T){
charch;
if((ch=getchar())=='')
*T=NULL;
else{
*T=(BinTree)malloc(sizeof(BinNode));
(*T)->data=ch;
CreateBin(&((*T)->lchild));
CreateBin(&((*T)->rchild));
}
}
voidVisit(charch){//访问结点值
printf("%c",ch);
}
voidPreOrder2(BinTreeT){//先序遍历二叉树-非递归
inttop=0;
BinTrees[MaxSize];
BinTreep=T;
do{
while(p){
if(top>MaxSize-1)
return;
s[++top]=p;
Visit(p->data);
p=p->lchild;
}
if(top>0){
p=s[top--];
p=p->rchild;
}
}while(p||top>0);
printf("\n");
}
voidInOrder2(BinTreeT){//中序遍历二叉树-非递归
inttop=0;
BinTrees[MaxSize];
BinTreep=T;
do{
while(p){
if(top>MaxSize-1)
return;
s[++top]=p;
p=p->lchild;
}
if(top>0){
p=s[top--];
Visit(p->data);
p=p->rchild;
}
}while(p||top>0);
printf("\n");
}
voidPostOrder2(BinTreeT)
{//后序遍历二叉树-非递归
BinTrees[MaxSize];
BinTreep=T,q;
inttop=0;
while(p||top!
=0){
while(p){
if(top>MaxSize-1)
return;
top++;
s[top]=p;
p=p->lchild;
}
if(top>0){
p=s[top];
if(p->rchild==NULL||p->rchild==q){
Visit(p->data);
q=p;
top--;
p=NULL;
}
else
p=p->rchild;
}
}
printf("\n");
}
intmain()
{
BinTreeroot;
CreateBin(&root);
PreOrder2(root);//先序遍历二叉树-非递归
InOrder2(root);//中序遍历二叉树-非递归
PostOrder2(root);//后序遍历二叉树-非递归
return0;
}
三,二叉树的层次遍历算法:
在层次遍历中先访问的结点,其孩子结点也比后访问的结点的孩子结点先访问,因此,许使用队列这种数据结构实现此算法。
算法思想:
需要使用一个辅助队列,首先让根结点A入队,然后对队列进行操作,如果队列不空,队头结点出队,立即访问之,然后判断它是否有左,右孩子,如有孩子则让孩子结点入队,重复上述操作,直到队列为空为止。
程序实现:
样例输入:
ABD##E##C#F##
样例输出:
ABCDEF
ABCDEF
*/
#include
#include
#defineMaxSize100
typedefstructNode{
chardata;
structNode*lchild,*rchild;
}BinNode,*BinTree;
voidCreateBin(BinTree*T)
{//创建二叉树
charch;
if((ch=getchar())=='#')
*T=NULL;
else{
if(!
(*T=(BinTree)malloc(sizeof(BinNode)))){
exit
(1);
}
(*T)->data=ch;
CreateBin(&((*T)->lchild));
CreateBin(&((*T)->rchild));
}
}
voidlevelorder(BinTreep)
{//层次遍历二叉树
BinTreeq[MaxSize];
intfront=0,rear=0;
if(p!
=NULL){
rear++;
q[rear]=p;
}
while(front!
=rear){
front++;
p=q[front];
printf("%c",p->data);
if(p->lchild!
=NULL){
rear++;
q[rear]=p->lchild;
}
if(p->rchild!
=NULL){
rear++;
q[rear]=p->rchild;
}
}
printf("\n");
}
voidVisit(chare){//访问函数
printf("%c",e);
}
voidLayerOrder(BinTreeT)
{//层次遍历二叉树
BinTreeq[MaxSize];
BinTreep;
intfront=0,rear=0;
if(!
T)
return;
if((rear+1)>MaxSize)
return;
q[rear++]=T;
while(frontp=q[front++];
Visit(p->data);
if(p->lchil