《数据结构》第六章 树和二叉树 笔记整理(自用)

《数据结构》第六章 树和二叉树 笔记整理

本章要求

1.二叉树的概念、性质、存储结构

2.熟练掌握二叉树的前、中、后序遍历方法及代码

3.熟练掌握哈夫曼树的实现方法、构造哈弗曼编码的方法

4.了解森林与二叉树的转换,树的遍历

文章目录


一、树、二叉树的相关术语

1.结点的度:结点的子树的个数
2.结点的的层次:从根结点到该结点的层数(根结点算一层)
3.叶子、终端节点
4.树的度:所有结点度中的最大值,几叉树树的度就是几。
5.树的深度(高度):最大是层数
6.兄弟、双亲、祖先
7.森林:m棵互不相交的树的集合

二、二叉树

1.二叉树的定义和特点

①二叉树不是特殊的树,而是两个概念;②每个结点的度最大为2(二叉树的度为2)③二叉树为有序树!!!左右子树不能任意互换

2.特殊二叉树,二叉树(含特殊)的性质和存储结构

1)特殊二叉树:

a.满二叉树:每一层上的结点数都是最大结点数。
b.完全二叉树:
①若此树有n个结点,深度为k,与其满二叉树的结点从1到n编号一一对应则为完全二叉树。
②叶子结点仅在层次最大的两层上,除最后一层,前k-1层都是满的
③从满二叉树的最后一个结点开始连续去掉任意结点,即为完全二叉树。

2) 二叉树性质

性质1第i层上至少有1个结点。

性质2深度为k至少有k个结点。

性质3:

性质4图中的符号表示:不大于X的最大整数(取底),而且做题时可以求左右,就能把这个数求出来,记得还有**+1**。

性质5在这里插入图片描述

3)二叉树的存储结构

① 顺序存储

表示如下:

#define MAX_TREE_SIZE 100//二叉树最大节点数
typedef TElemType SqBiTree[MAX_TREE_SIZE];//0号单元存储根结点
SqBiTree T

特点:浪费空间,适用于满二叉树和完全二叉树

②链式存储

二叉链表
C语言的类型描述如下

typedef struct BiTNode{//结点结构
TElemType data;
struct BiTNode *lchild,*rchild;//左右孩子指针
}BiTNode,*BiTree

结点结构

练习:在n个结点的二叉链表中,有 n+1 个空指针域
分析::n个结点,必有2n个链域,每个结点除了根结点之外都只有一个双亲,占用一个链域,即n-1个,
所以有2n-(n-1)=n+1个空的。

考试作业常用浙大版

typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
    ElementType Data;
    BinTree Left;
    BinTree Right;
};

3.遍历二叉树和线索二叉树

1)遍历二叉树

为了将非线性结构转化为线性结构来解决问题,对其进行遍历,顺着一条路径,使每个结点都被访问到,且仅被访问1次,访问是指对结点的各种操作
例:

①先序遍历:根左右

思想:若为空树则空操作;
否则
(1)访问根结点;
(2)先序遍历左子树;
(3)先序遍历右子树;
算法的递归描述

void PreorderTraversal( BinTree BT )
{
    if(BT)//如果不为空树
    {
        printf(" %c",BT->Data);//访问结点
        PreorderTraversal(BT->Left);//遍历左子树
        PreorderTraversal(BT->Right);//遍历右子树
    }
}

便于理解,附加图:

②中序遍历:左根右

思想:若为空树则空操作;
否则
(1)中序遍历左子树;
(2)访问根节点
(3)中序遍历右子树;
算法的递归描述

void InorderTraversal( BinTree BT )
{
    if(BT)
    {
        InorderTraversal(BT->Left);//中序遍历左子树
        printf(" %c",BT->Data);//访问根结点
        InorderTraversal(BT->Right);//中序遍历右子树
    }

}

算法的非递归描述——运用
遇到一个结点,就把它压入栈,并且去遍历它的左子树;
当左子树遍历完,从栈顶弹出这个结点并去访问它;
再去按照这个流程去遍历该结点的右子树;

void InOrderTra(BinTree BT)
{
    BinTree T=BT;
    Stack S=CreatStack(MAXSIZE);//创建并初始化栈S;
    while(T||!IsEmpty(S))
    {
        while(T)//一直向左将沿途结点压入栈;
        {
            Push(S,T);
            T=T->Left;
        }
        if(!IsEmpty(S))
        {
            T=Pop(S);//当左边结点没了,弹出栈顶元素,并且T当前就退回到该结点,也就是最近路过的
            printf(" %c",T->Data);//访问结点
            T=T->Right;//左根右,开始向右
        }
    }
}
③后序遍历:左右根

思想:若为空树则空操作;
否则
(1)后序遍历左子树;
(2)后序遍历右子树;
(3)访问根节点。

算法的递归描述

void PostorderTraversal( BinTree BT )
{
    if(BT)
    {
        PostorderTraversal(BT->Left);
        PostorderTraversal(BT->Right);
        printf(" %c",BT->Data);
    }
}
④层次遍历:从上到下,从左到右,一层一层

算法思想:
a.根结点先入队;
b.从队列取出一个元素;
c.访问该元素指的结点;
d.若该结点的左右孩子都不为空,则将左、右孩子的指针入队;

可以用队列实现,也可以用一维数组,本质相同;
队列Queue算法如下:

void LevelOrderTraversal(BinTree BT)
{
    Queue Q;
    BinTree T;
    if(!BT) return ;//树为空则直接返回
    Q=CreatQueue(MAXSIZE);//初始化队列
    EnQueue(Q,BT);//将根结点入队
    while(!Qempty(Q))//如果队列不为空的话进入循环
    {
        T=DeQueue(Q);//队首元素出队
        printf(" %c",T->Data);//访问该出队结点
        if(T->Left)//若左右不为空,将左右孩子入队,利用队列的特点:先入先出。
          EnQueue(Q,T->Left);
        if(T->Right)
          EnQueue(Q,T->Right);
    }
}
  

数组算法描述:

void LevelOrderTraversal(BinTree BT)
{
    BinTree T[1000];//建立数组;
    BinTree p;//定义一个指针类型,便于遍历
    int f=0,r=0;//相当于队列里的头指针,尾指针
    if(BT)//若不为空树,将根结点压入数组
    {
        T[r++]=BT;//相当于尾指针指向队尾元素的下一个
    }
    while(f!=r)
    {
        p=T[f];//注意这一步!!!每次输出的都是队首!
        printf(" %c",p->Data);
        f++;//相当于Q.f=(Q.f+1)%MAXSIZE;
        if(p->Left) T[r++]=p->Left;//是P,也就是原队首的左右孩子
        if(p->Right) T[r++]=p->Right;
    }
}
常见题型:

已知中序、后序;已知先序、中序则可以唯一确定一棵二叉树,但是先、后序不行。
例:

2)遍历算法的应用举例

①统计二叉树中的结点个数

基本思想:先序(中、后均可)遍历二叉树,在遍历过程中查找叶节点,计数–>相当于把遍历算法中的访问操作改为计数–>若为叶节点,加一。
算法1(用于C++,C不能用引用):

void CountLeaf(BinTree T,int &a)//传的计数器的引用-->int  main()里 定义了a=0;
{
    if(T)
    {
        if(!T->Left&&!T->Right)
            a++;//对叶结点计数;
        CountLeaf(T->Left,a);
        CountLeaf(T->Right,a);
    }
}

算法2 C

int CountLeaf(BinTree T)
{
    if(!T) return 0;
    else if(!T->Left&&!T->Right)
        return 1;//如果为叶节点返回1,
    else
        return CountLeaf(T->Left)+CountLeaf(T->Right);
}
②求二叉树的深度

二叉树的深度是左右子树的最大深度加1,因此–>分别求左右子树的深度,相当于把访问函数变为求左右子树的深度最大值加1。
算法如下:

int GetHeight( BinTree BT )
{
    int HL,HR,Hmax;//定义左子树高度,右子树高度,最大高度
    if(BT)
    {
        HL=GetHeight( BT->Left );
        HR=GetHeight( BT->Right );
        Hmax=HL>HR?HL:HR;//求左右子树高度的最大值
        return Hmax+1;//加1才是树的高度,不然是子树的高度
    }
    else
        return 0;
}

3)线索二叉树

①定义

由遍历二叉树可知:用一定的规则(前中后序)将二叉树结点排列成线性序列,使每个结点有一个前驱和后继,但是,当用二叉链表作为存储结构时只能得到左右孩子的信息,无法得到任意结点的直接前驱和后继,这种信息只有在遍历的动态过程中才能得到,因此引入 线索二叉树,利用空链域存放前驱和后继的信息

②方法

---->若结点有左孩子,则Left指向左孩子;否则指向直接前驱(给定序列的)
---->若结点有右孩子,则Right指向右孩子;否则指向直接后继(给定序列的)
为避免混淆,设定两个标志域LTag和RTag

例:

后序和中序同上

三、树和森林

1、树的存储结构

1)双亲表示法

利用的是静态链表

特点:找双亲容易,找孩子难

C语言描述:

#define MAX_SIZE 100
typedef struct PTNode{
ELem data;
int parent;//双亲位置域
}PTNode;//结点结构

typedef struct {
PTNode nodes [MAX_SIZE];
int r,n;//根结点的位置和结点个数
}PTree;//树结构

2) 孩子链表表示法

多重链表,把每个结点的孩子结点排列起来,看成一个线性表,n个结点n个孩子链表(叶子的孩子链表为空表),n个头指针又组成一个线性表,可采用顺序存储

特点:找孩子容易,找双亲难
C语言描述如下:

typedef struct CTNode//孩子结点结构
{
    int child;
    struct CTNode *next;//指向的它的下一个兄弟,也就是他双亲的下一个孩子;
}*ChildPtr;

typedef struct//双亲结点结构
{
    Elem data;
    ChildPtr firstchild;//孩子链表的头指针
}CTBox;

typedef struct //树结构
{
    CTBox nodes[MAXSIZE];
    int n,r;//结点数和根结点的位置
}CTree;

3)孩子兄弟表示法(二叉链表表示法)

链表中两个链域分别指向该结点的第一个孩子下一个兄弟结点

由于二叉树和树均可以用二叉链表来表示,树的操作可对应二叉树的操作来完成
但是解释不同,由于树的根结点无兄弟,树对应的二叉树无右子树

typedef struct CSNode
{
    ElemType data;
    struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;

2.树、二叉树、森林之间的转换

1)树转换为二叉树(兄弟相连留长子)

①加线:在兄弟间加连线;
②抹线:对每一个结点,除左孩子外,去除与其他孩子的连线;
③:以根结点为轴心,将整棵树顺时针旋转45°

2)二叉树转换为树(左孩右右连双亲,去掉原来右孩线)

①加线:p为双亲结点的左孩子,则将p的右孩子,右孩子的右孩子…沿分支找到所有右孩子,与双亲连接起来
②抹线:抹去原二叉树中双亲与右孩子之间的连线;
③调整

3)森林转换为二叉树(树变二叉根相连)

①将每棵树分别转化为二叉树
②每棵树的根结点相连
③以第一棵树的根结点为大树的根

4)二叉树变森林(去掉全部右孩线,孤立二叉再还原)

①抹线:去掉二叉树根结点与其右孩线,及沿右分支搜索到的所有右孩子间的连线全部抹掉,变为孤立的二叉树;
②还原

3、树、森林的遍历

1)树的遍历:

①先根遍历:若树不为空,先访问根结点,然后先根遍历各棵子树
②后根遍历:若树不为空,进行后根遍历各棵子树,然后访问根结点
③按层次遍历:自上至下,自左至右;

2)森林的遍历:

首先清楚:将森林分为三部分,第1部分是第一棵树的根结点,第2部分是第一棵树的子树,第3部分是除第一棵树外的所有树。

①先序遍历

若森林不空,则访问森林中第一 棵树的根结点;先序遍历森林中第一棵树的子树森林;先序遍历森林中(除第一棵树之外)其余树构成的森林。

②中序遍历

若森林不空,则中序遍历森林中第一棵树的子树森林;访问森林中第一棵树的根结点; 中序遍历森林中(除第一棵树之外)其余树构成的森林。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值