1、二叉树顺序存储(数组)
按满二叉树的结点层次编号,依次存放二叉树中的数据元素。
必须按照满二叉树顺序编号,空的位置存为0
// 二叉树顺序存储表示
#define MAXTSIZE 100 //最大节点数
typedef TElemType SqBiTree[MAXSTIZE]; // TElemType 可为Int、char
SqBiTree bt;
缺点:存储密度小、存储空间大小确定,适合满二叉树、完全二叉树、堆
2、二叉树链式存储
(1)二叉链表
空的孩子指针存为^。
typedef struct BiNode
{
TElemType data;
struct BiNode *lchild, *rchild;
//左右孩子指针
} BiNode, *BiTree;
(2)三叉链表
在二叉链表基础上增加一个指向双亲结点的指针域。
typedef struct TriTNode
{
TelemType data;
struct TriTNode *lchild, *parent, *rchild;
} TriTNode, *TriTree;
3、遍历二叉树
(1)先序遍历(DLR)
若二叉树为空,则空操作;否则
(1)访问根结点;
(2)先序遍历左子树
(3)先序遍历右子树
*遍历至子树为空
Status PreQrderTraverse(BiTree T){
if (T == NULL)
return OK; // 空树
else
{
visit(T); // 访问根结点
PreOrderTraverse(T->lchild);//递归遍历左子树
PreOrderTraverse(T->rchild);//递归遍历右子树
}
}
(2)中序遍历(LDR)
若二叉树为空,则空操作;否则
(1)中序遍历左子树;
(2)访问根结点;
(3)中序遍历右子树。
Status InOrderTraverse(BiTree T)
{
if (T == NULL)
return OK; // 空二叉树else{
lnOrderTraverse(T->lchild); // 递归遍历左子树
visit(T);//访问根结点;
InOrderTraverse(T->rchild); // 递归遍历右子树}
}
非递归算法:
1.栈
二叉树中序遍历的非递归算法的关键:在中序遍历过某结点的整个左子树后,如何找到该结点的根以及右子树。
基本思想:
(1)建立一个栈
(2根结点进栈,遍历左子树
(3)根结点出栈,输出根结点,遍历右子树。
Status InOrderTraverse(BiTree T)
{ BiTree p;
lnitStack(S);
p = T;
while (p || !StackEmpty(S))
{
if (p)
{
Push(S, p);
p = p->lchild;
}
else
{
Pop(S,q);
cout << q->data;
p =q->rchild;
}
} // while
return OK;
}
时间、空间复杂度:O(n)
2.层次遍历
按层数遍历:先遍历1层、后2层……利用队列实现
(1)根节点入队
(2)队不空时循环:从队列中出列一个节点*p并访问(出队并输出)
有左孩子时入队、有右孩子时入队
void LevelOrder(BTNode *b)
{
BTNode *p;
SqQueue *qu;
InitQueue(qu);
enQueue(qu,b);//根节点指针入队
while (!QueueEmpty(qu)) {
deQueue(qu, p); //出队p
printf("%c ",p->data);
if (p-> Ichild!=NUL) enQueue(qu, p-> Ichild);//有左孩子时入队
if (p-> rchild!=NULL) enQueue(qu, p-> rchild);//有右孩子时入队
}
}
(3)后序遍历(LRD)
若二叉树为空,则空操作;否则
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根结点。
Status PostOrderTraverse(BiTree T)
{
if (T == NULL)
return OK; // 空二叉树else{
PostOrderTraverse(T->Ichild); // 递归遍历左子树
PostOrderTraverse(T->rchild);//递归遍历右子树
visit(T);//访问根结点
}
如果去掉输出语句,从递归的角度看,三种算法是完全相同的,或说这三种算法的访问路径是相同的,只是访问结点的时机不同。
时间效率:O(n)//每个结点只访问一次·
空间效率:O(n)//栈占用的最大辅助空间
若二叉树中各结点的值均不相同,则二叉树结点的先序序列、中序序列、后序列都是唯一的。
由二叉树的先序序列和中序序列,或由二叉树的后序序列和中序序列可以确定唯一一棵二叉树(只知道先后不行)
先序序列和中序序列:
先序确定根(首位),中序确定左右
//根据先序序列pre[pre_low..pre_low+len-1]和中序序列in[in_low..in_low+len-1]建树t
void BuildTree(BiTree& t, char pre[], int pre_low, char in[], int in_low, int len)
{
t= new BiTNode;
t->data=pre[pre_low];
t->lchild=NULL;
t->rchild=NULL;
int i;
for(i=in_low;i<len;i++)
{
if(in[i]==pre[pre_low])
break;
}
int l = i-in_low;
if(i>in_low) BuildTree(t->lchild,pre,pre_low+1,in,in_low,i);
if(i+1<len)BuildTree(t->rchild,pre,pre_low+l+1,in,i+1,len);
}
后序序列和中序序列:
后序确定根(尾部)中序确定左右
4、二叉链表创建
void CreateBiTree(BiTree &T)
{
//按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T
TElemType ch;
//要处理多组数据,输入ch如果遇到EOF,应该结束程序 所以main函数用while(1)
//if(!(cin >> ch)) exit(0);
if(ch=='#') T=NULL ;//空树
else{
T = new BiTNode;//生成根节点
T->data=ch;
CreateBiTree(T->lchild);//递归创造左子树
CreateBiTree(T->rchild);//递归创造右子树
}
} //CreateBiTree
5、销毁二叉树
void DestroyBitree(BiTree &T)
{
if(T->data=='#')
free(T);
DestroyBitree(T->lchild);
DestroyBitree(T->rchild);
free(T);
T=NULL;//不可丢
}
6、复制二叉树
如果是空树,递归结束;
否则,申请新结点空间,复制根结点
➢递归复制左子树
➢递归复制右子树
int Copy(BiTree T, BiTree &NewT){
if(T==NULL){ //空树递归结束
NewT= NULL;
return 0;}
else {
NewT=new BiTNode;
NewT-> data=T-> data;
Copy(T->IChild,NewT-> Ichild);
Copy(T->rChild,NewT-> rchild);
}
}
7、计算深度
如果是空树,则深度为0;
否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1。
int Depth( BiTree T){
if(T==NULL)return 0; //空树度为0
else {
m= Depth(T-> lChild);
n = Depth(T-> rChild);
if(m>n) return (m+1);
else return(n+1);
}
8、统计节点个数
如果是空树,则结点个数为0;
否则,结点个数为左子树的结点个数+右子树的结点个数再+1。
int NodeCount(BiTree T){
if(T == NULL)return 0;
else
return NodeCount(T-> lchild)+NodeCount(T-> rchild)+1;
}
叶子节点:
如果是空树,则叶子结点个数为0;
否则,为左子树的叶子结点个数+右子树的叶子结点个数。
int LeadCount(BiTree T)
{
if(T= =NULL)
return 0;
if (T->lchild == NULL && T->rchild == NULL)return 1;
else return LeafCount(T-> lchild) +LeafCount(T-> rchild);
}