数据结构(c语言 第二版 严蔚敏)第五章 树 期末速成版

基本的概念

只陈述一些我觉得平时见的比较少的概念,考试大概率不怎么考概念

括号表示法创建二叉树:遇到左括号,那后面的就是左孩子,遇到右括号,后面的就是右孩子,在同一个括号内的字母是同一层的树。

树的深度:树中节点的最大层次称为树的深度或高度

二叉树的定义

二叉树(Binary Tree)是n(n≥0)个节点所构成的集合,它或为空树(n = 0),或为非空 树。对于非空树T:

(1)有且仅有一个称之为根的节点;

(2)除根节点以外的其余节点分为两个互不相交的子集T1和T2,分别称为T的左子树和右子 树,且T1和T2本身又都是二叉树。

二叉树的性质

在二叉树的第i层上至多有2i−1 (i≥1)个节点

利用二叉链表存储树时,右指针指向兄弟结点,因为根节点没有兄弟结点,故根节点的右指针指向空。

深度为k的二叉树至多有2k−1(k≥1)个节点

对任何一棵二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0 = n2 + 1

满二叉树:深度为k且含有2k −1个节点的二叉树。每一层上的节点数都是最大节点数

完全二叉树:深度为k的、有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉 树中编号从1至n的节点一一对应时,称之为完全二叉树

度为0的节点的数量等于总度数减去有度数节点的数量

具有n个节点的完全二叉树的深度为 (log2n)向下取整 +  1①

如果对一棵有n个节点的完全二叉树(其深度为 (log2n)向下取整 + 1)的节点按层序编号(从 第1层到第 (log2n)向下取整 + 1层,每层从左到右),则对任一节点i(1≤i≤n),以下结论成立。

(1)如果i = 1,则节点i是二叉树的根,无双亲;如果i>1,则其双亲PARENT(i)是节点i/2(向下取整)。

(2)如果2i>n,则节点i无左孩子(节点i为叶子节点);否则其左孩子LCHILD(i)是节点2i。

(3)如果2i + 1>n,则节点i无右孩子;否则其右孩子RCHILD(i)是节点2i + 1。

二叉树的存储

//- - - - -二叉树的二叉链表存储表示- - - - -

typedef struct BiTNode{

TElemType data;//节点数据域

struct BiTNode *lchild,*rchild; //左右孩子指针

}BiTNode,*BiTree;

二叉树的遍历

//中序遍历

void InOrderTraverse(BiTree T)

{//中序遍历二叉树T的递归算法

 if(T) //若二叉树非空

 {

InOrderTraverse(T-lchild); //中序遍历左子树

cout<<T-data; //访问根节点

InOrderTraverse(T-rchild); //中序遍历右子树

 }

}

//非递归的方法实现中序遍历

void InOrderTraverse(BiTree T)

{//中序遍历二叉树T的非递归算法

 InitStack(S);p=T;

 q=new BiTNode;

 while(p||!StackEmpty(S))

 {

if(p) //p非空

{

 Push(S,p); //根指针进栈

p=p-lchild; //根指针进栈,遍历左子树

}

else //p为空

{

 Pop(S,q); //退栈

cout<<q-data; //访问根节点

p=q-rchild; //遍历右子树

}

 } // while

}

//先序遍历

void PreOrderTraverse(BiTree T)

{

    if(T)

    {

        cout<<T->data;

        PreOrderTraverse(T->lchild);

        PreOrderTraverse(T->rchild);

    }

}

//后序遍历

void PostOrderTraverse(BiTree T)

{

    if(T)

    {

        PostOrderTraverse(T->lchild);

        PostOrderTraverse(T->rchild);

        cout<<T->data;

    }

}

 

可以根据先序遍历和中序遍历,或者后序遍历和中序遍历来确定一棵二叉树,先序遍历或者后序遍历来确定根节点,然后再根据中序遍历来确定左右子树的位置,然后这样循环使用,再确定根节点。

二叉树遍历算法的运用

创建二叉树

void CreateBiTree(BiTree &T

{//按先序次序输入二叉树中节点的值(单字符),创建二叉链表表示的二叉树T

 cin>>ch;

 if(ch=='#') T=NULL; //递归结束,建空树

 else //递归创建二叉树

 {

T=new BiTNode; //生成根节点

T-data=ch; //根节点数据域置为ch

CreateBiTree(T-lchild); //递归创建左子树

CreateBiTree(T-rchild); //递归创建右子树

 } //else

}

复制二叉树

void Copy(BiTree T,BiTree &NewT)

{//复制一棵和T完全相同的二叉树

 if(T==NULL) //如果是空树,递归结束

 {

NewT=NULL;

return;

}

 else

 {

NewT=new BiTNode;

NewT-data=T-data; //复制根节点

Copy(T-lchild,NewT-lchild); //递归复制左子树

Copy(T-rchild,NewT-rchild); //递归复制右子树

} //else

}

计算二叉树的深度

int Depth(BiTree T)

{//计算二叉树T的深度

 if(T==NULL) return 0; //如果是空树,深度为0,递归结束

 else

 {

m=Depth(T->lchild); //递归计算左子树的深度记为m

n=Depth(T->rchild); //递归计算右子树的深度记为n

if(m>n) return(m+1); //二叉树的深度为mn的较大者加1

else return(n+1);

 }

}

线索二叉树

n个节点必然存在n+1个空链域,因此要充分利用这些空链域

若节点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱; 若节点有右子树,则其rchild域指示其右孩子,否则令rchild域指示其后继。为了避免混淆,尚需改 变节点结构,增加两个标志域

以节点p为根的子树中序线索化

void InThreading(BiThrTree p)

{//pre是全局变量,初始化时其右孩子指针为空,便于在树的最左点开始建线索

 if(p)

 {

InThreading(p-lchild); //左子树递归线索化

if(!p-lchild) //p的左孩子为空

{

p-LTag=1; //p加上左线索

p-lchild=pre; //p的左孩子指针指向pre(前驱)

} //if

else p->LTag=0;

if(!pre-rchild) //pre的右孩子为空

{

pre-RTag=1; //pre加上右线索

pre-rchild=p; //pre的右孩子指针指向p(后继)

} //if

else pre->RTag=0;

pre=p; //保持pre指向p

InThreading(p-rchild); //右子树递归线索化

 }

}

① 如果p非空,左子树递归线索化。

 ② 如果p的左孩子为空,则给p加上左线索,将其LTag置为1,让p的左孩子 指针指向pre(前驱);否则将p的LTag置为0。

 ③ 如果pre的右孩子为空,则给pre加上右线索,将其RTag置为1,让pre的右 孩子指针指向p(后继);否则将pre的RTag置为0。

 ④ 将pre指向刚访问过的节点p,即pre = p。

⑤ 右子树递归线索化

(1)在中序线索二叉树中查找

 ① 查找p指针所指节点的前驱:

若p->LTag为1,则p的左链指示其前驱;

若p->LTag为0,则说明p有左子树,节点的前驱是遍历左子树时最后访问的一个节点 (左子树中最右下的节点)。

 ② 查找p指针所指节点的后继: l 若p->RTag为1,则p的右链指示其后继;

若p->RTag为0,则说明p有右子树。根据中序遍历的规律可知,节点的后继应是遍历其 右子树时访问的第一个节点,即右子树中最左下的节点。例如在找节点*的后继时,首先沿右指针 找到其右子树的根节点-,然后顺其左指针往下直至其左标志为1的节点为节点*的后继,

 (2)在先序线索二叉树中查找

① 查找p指针所指节点的前驱:

若p->LTag为1,则p的左链指示其前驱;

 若p->LTag为0,则说明p有左子树。此时p的前驱有两种情况:若*p是其双亲的左孩子, 则其前驱为其双亲节点;否则应是其双亲的左子树上先序遍历最后访问到的节点

② 查找p指针所指节点的后继:

若p->RTag为1,则p的右链指示其后继;

若p->RTag为0,则说明p有右子树。按先序遍历的规则可知,*p的后继必为其左子树根 (若存在)或右子树根

(3)在后序线索二叉树中查找

 ① 查找p指针所指节点的前驱:

 若p->LTag为1,则p的左链指示其前驱;

若p->LTag为0,当p->RTag也为0时,则p的右链指示其前驱;若p->LTag为0,而p- RTag1时,则p的左链指示其前驱

② 查找p指针所指节点的后继情况比较复杂,分以下情况讨论:

 若*p是二叉树的根,则其后继为空;

 若*p是其双亲的右孩子,则其后继为双亲节点;

 若*p是其双亲的左孩子,且*p没有右兄弟,则其后继为双亲节点;

 若*p是其双亲的左孩子,且*p有右兄弟,则其后继为双亲的右子树上按后序遍历列出的 第一个节点(右子树中最左下的叶节点)。

树和森林

哈夫曼树

哈夫曼树中没有度为一的结点

【数据结构——五分钟搞定哈夫曼树,会求WPL值,不会你打我】 数据结构——五分钟搞定哈夫曼树,会求WPL值,不会你打我_哔哩哔哩_bilibili

注意一个比较特殊的哈夫曼树ht存储结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值