二叉树是非线性结构,即每个数据结点至多只有一个前驱,但可以有多个后继。它可采用顺序存储结构和链式存储结构。
二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点。因此,必须把二叉树的所有结点安排成为一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系,用编号的方法从树根起,自上层至下层,每层自左至右地给所有结点编号,缺点是有可能对存储空间造成极大的浪费。依据二叉树的性质,完全二叉树和满二叉树采用顺序存储比较合适。
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。
通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。其结点结构为:
其中,data域存放某结点的数据信息;lchild与rchild分别存放指向左孩子和右孩子的指针,当左孩子或右孩子不存在时,相应指针域值为空。利用这样的结点结构表示的二叉树的链式存储结构被称为二叉链表,如下图所示:
二叉树链式存储结构定义为:
1 2 3 4 5 |
typedef struct bnode { char data; struct bnode *lchild,*rchild; }B,*BTree; |
二叉树的遍历
1.先序遍历:根节点、左子树、右子树
先序遍历算法(递归):
1 2 3 4 5 6 7 8 9 |
void preorder(BTree t) //先序遍历 { if(t) { visit(t->data ); //访问节点数据,自定义访问函数visit preorder (t->lchild ); //递归遍历左子树 preorder (t->rchild ); //递归遍历右子树 } } |
2.中序遍历:左子树、根节点、右子树
中序遍历算法(递归):
1 2 3 4 5 6 7 8 9 |
void inorder(BTree t) //中序遍历 { if(t) { inorder (t->lchild ); visit(t->data ); //访问节点数据 inorder (t->rchild ); } } |
中序遍历结果:D、B、A、E、C、F
3.后序遍历:左子树、右子树、根节点
后序遍历算法(递归):
1 2 3 4 5 6 7 8 9 |
void postorder(BTree t) //后序遍历 { if(t) { postorder (t->lchild ); postorder (t->rchild ); visit(t->data ); //访问节点数据 } } |
节点数、高度、每层节点数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
int count(BTree t) //计算二叉树节点数 { int lcount,rcount; if(t==NULL) return 0; lcount=count(t->lchild ); //左子树节点数 rcount =count(t->rchild ); //右子树节点数 return(lcount +rcount +1); } int height(BTree t) //计算二叉树的高度 { int h1,h2; if(t==NULL) return 0; h1=height (t->lchild ); //左子树高度 h2=height (t->rchild ); //右子树高度 if(h1>h2) return h1+1; return h2+1; } void Levcount(BTree t,int L,int num[]) //计算二叉树每层节点数 { if(t) { num[L]++; Levcount (t->lchild ,L+1,num); Levcount (t->rchild ,L+1,num); } } |
二叉排序树
任一节点左子树中的所有节点值都不大于根节点值;右子树中所有节点的值都不小于根节点值;左右子树也均为二叉排序树。
根据二叉树的中序遍历算法可以推断,二叉排序树的中序遍历结果是严格递增的。
二叉排序树建立
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void insertBTree(BTree &t,char x) { BTree r; if(t==NULL) //二叉树为空时 { r=(BTree )malloc(sizeof(B )); r->data =x; r->lchild =r->rchild =NULL; t=r; return; } else { if(x<t->data) //新节点数据小于根节点 insertBTree (t->lchild ,x); //递归调用插入左子树 else insertBTree (t->rchild ,x); //递归调用插入右子树 } } |
二叉排序树搜索
1 2 3 4 5 6 7 8 9 |
BTree searchBTree(BTree t,char s)//在二叉排序树中查找值为s的节点 { if(t==NULL) return NULL; if(t->data ==s) return t; if(t->data >s) return searchBTree (t->lchild ,s); //在左子树中查找 else return searchBTree (t->rchild ,s); //在右子树中查找 } |
查找双亲节点
1 2 3 4 5 6 7 8 9 |
BTree searchBTree2(BTree t,char s) //查找双亲节点 { if(t==NULL || t->data ==s) return NULL; //s节点对应根节点 if(t->lchild ->data ==s || t->rchild ->data ==s) return t; //节点的左或右孩子节点值为s,即为双亲节点 if(t->data >s) return searchBTree (t->lchild ,s); //在左子树中查找 else return searchBTree (t->rchild ,s); //在右子树中查找 } |
p为被删节点,f为双亲节点,需要分下面三中情况:
1. p节点为叶子节点(即左右子树均为空),直接删除,不影响树的结构;
2. p节点没有左子树,把f节点指向p节点的指针指向p的右子树;
3. p节点有左子树,从p节点的左子树中选择节点值最大的节点s,将s的值复制给p,再将指向s的指针指向s的左子树。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
int deleteBTree(BTree &t,char x) { BTree f,p,q,s; p=searchBTree (t,x); //找到x所在节点 f=searchBTree2 (t,x); //找到p节点的双亲节点 if(p==NULL) return 0; if(p->lchild ==NULL) { if(f==NULL) //被删节点为跟节点 t=p->rchild ; else if(f->lchild ==p) //p为双亲节点的左节点 f->lchild =p->rchild ; else f->rchild =p->rchild ; free(p); free(f); } else if(p->lchild !=NULL) //p节点有左子树 { q=p; s=p->lchild ; while(s->rchild ) //找到p节点左子树中值最大的节点 { q=s; s=s->rchild ; } if(q==p) //p节点没有右子树 q->lchild =s->lchild ; else q->rchild =s->lchild ; p->data =s->data ; //将s节点值复制给p free(s); return 1; } else //p为叶子节点 { if(f->lchild ==p) //p为双亲节点的左孩子 f->lchild =NULL; else //p为双亲节点的右孩子 f->rchild =NULL; free(p); return 1; } } |