大话数据结构读书笔记 4---树

大话数据结构读书笔记—树

数据结构 读书笔记


树是n(n>=0)个结点的有限集。
n=0时称为空树。
(注意:n>0时根节点是唯一的;
子树的个数没有限制,但是它们一定是互不相交的;

树中结点的最大层次称为树的深度或高度;(结点的层次从根开始定义起,根为第一层,根的孩子为第二层)
森林是m(m>=0)棵互不相交的树的集合;

树的储存结构

1 双亲表示法

在每个结点中,附设一个只是器指示其双亲结点在数组中的位置

#define MAX_TREE_SIZE 100
typedef int TElemType;
typedef struct PTNode  //结点结构
{
    TElemType data;
    int parent;
}PTNode;
typedef struct  //树结构
{
    PTNode  nodes[MAX_TREE_SIZE];
    int r,n;  //根的位置和结点数
}PTree;

由于根结点没有双亲,我们约定根节点的parent域为-1;

2 孩子表示法

把每个结点的孩子排列起来,以单链表作为储存结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序储存结构,存放进一个一维数组中。

#define MAX_TREE_SIZE 100
typedef struct CTNode //孩子结点
{
    int child;
    struct CTNode *next;//指向某当前结点的下一个孩子结点
}*ChildPtr;
typedef struct //表头结构
{
    TElemType data;
    ChildPtr firstchild;
}CTBox;

typedef struct//树结构
{
    CTBox nodes[MAX_TREE_SIZE];
    int r,n;
}CTree;

3 孩子兄弟表示法

我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟

typedef struct CSNode
{
    TElemType data;
    struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;

二叉树的定义

二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(即称为空二叉树),或者由一个根结点两棵互不相交的,分别称为根结点的左子树和右子树的二叉树构成。

二叉树的特点

1 每个结点最多有两棵子树;
2左子树和右子树是有顺序的,不能任意颠倒;
3即使某结点只有一棵子树,也要区分它是左子树还是右子树;

特殊二叉树

1:斜树
所有的结点只有左子树或只有右子树,分别称为左斜树和右斜树,统称为斜树
2:满二叉树
所有的分支结点都有左子树和右子树,并且所有的叶子都在同一层上(没有子树的结点称为叶结点(Leaf));
特点:
叶子(即叶结点)只能出现在最下一层;
同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多;
3:完全二叉树:
对一棵二叉树按层序编号(从左到右),若每个结点的编号和同样深度的满二叉树的相同位置的结点的编号是相同的,则为完全二叉树;
特点:
叶子结点只能出现在最下面两层;
最下层叶子一定集中在左部连续位置;
倒数第二层,若有叶子结点,一定在右部连续位置;
若结点度为(结点的度就是结点孩子的个数)1,则一定只有左子树;
同样结点数的二叉树,完全二叉树的深度最小;

二叉树的性质

1:在二叉树中第i层上至多有2^(i-1)个结点(i>=1);
2:深度为k的二叉树至多有2^k-1个结点(k>=1);
3:如果一棵二叉树的叶子结点(终端结点)个数为n0,度为2的结点数为n2,则n0=n2+1;
4:具有n个结点的完全二叉树的深度为|log2(n)|+1(|x|在这里表示不超过x的最大整数);
5:对一棵完全二叉树按层序编号(从左到右)对任一结点i
1:若i>1,则其双亲是结点|i/2|(这里表示不超过i/2的最大整数)
2:若2i>n,则结点无孩子,否则其左孩子是结点2i;
3:若2i+1>n,则结点无右孩子,否则其右孩子是2i+1;

二叉树树的储存结构

1顺序储存
2二叉链表

typedef struct BiTNode
{
    TElemType data;
    struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;//即BiTree 是一个指针,其默认指向二叉树的根结点

二叉树的遍历(重点)

1前序遍历

void PreOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    printf("%c",T->data);//这里是显示节点数据,还可以更改为其他操作,
    PreOrderTraverse(T->lchild);
    PreOrderTraverse(T->rchild);
}

2中序遍历

void InOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    InOrderTraverse(T->lchild);
    printf("%c",T->data);
    InOrderTraverse(T->rchild);
}

3后序遍历

void void PostOrderTraverse(BiTree T)
{
    if(T==NULL)
    return;
    PostOrderTraverse(T->lchild);
    PostOrderTraverse(T->rchild);
    printf("%c",T->data);
}

前序遍历根结点最先对其进行相关操作(如打印结点的数据等),后续遍历最后对根结点进行相关操作

二叉树的建立

//按前序输入二叉树中结点的值(一个字符)。#表示空树
void CreateBiTree(BiTree *T)
{
TElemType ch;
scanf(“%c”,&ch);
if(ch==’#’)
*T=NULL;
else
{
*T=(BiTree)malloc(sizeof(BiTNode));
if(!*T)//分配地址失败
exit(OVERFLOW);
(*T)->data=ch;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
}

当然也可以中序或者后序来构造二叉树,不过每次输入的值不一样

线索二叉树

若在二叉树的每个结点中分别利用原本指向NULL的指针(度不为2的结点包含的指针)使其指向该结点的前驱和后继(这样的指针称为线索),则该二叉树称为线索二叉树。(相应的二叉链表称为线索链表)。

我们可以已某种次序遍历二叉树使其线索化。(线索化的实质就是将二叉链表中空指针改为指向前驱或后继的线索)

线索二叉树的结构实现

//二叉树的线索储存结构定义
typedef enum{Link,Thread} PointerTag;//Link==0表示指针指向左右孩子
        //Thread表示指向前驱或后继
typedef struct BiThrNode
{
    TElemType data;
    struct BiThrNode *lchild, *rchild;
    PointerTag LTag;
    PointerTag RTag;//左右标志位,判断指针指向的是孩子还是前驱或后继
}BiThrNode, * BiThrTree;


中序遍历线索化的代码:
BiThrTree pre;
void InThreading(BiThrTree p)
{
    if(p)
    {
    InTreading(p->lchild);
    if(!p->lchild)//没有左孩子
    {
        p->LTag=Thread;
        p->lchild=pre;//左孩子指针指向前驱
    }
    if(!pre->rchild)//前驱没有右孩子
    {
        pre->RTag=Thread;
        pre->rchild=p;
    }
    pre=p;//保持pre指向p的前驱
    InThreading(p->rchild);
}

//使用一个中序遍历二叉线索表表示的二叉树T,(T指向一个新增的一个头结点,头结点的lchild指向根结点,头结点的rchild指向中序遍历的最后一个结点)
Status InOrderTraverse_Thr(BiThrTree T)
{
    BiThrTree p;
    p=T->lchild;
    while(p!=T)//空树或者遍历结束
    {
        while(p->LTag==Link)
        p=p->lchild;//此时循环到中序序列的第一个结点
        printf("%c",p->data);
        while(p->RTag==Thread&&p->rchild!=T)
        {
            p=p->rchild;
            printf("%c",p->data);
        }
        p=p->rchild;//p指向其右子树根;
    }
    return OK;
}    

树,森林与二叉树之间的相互转换

树转换成二叉树:

1加线。在所有的兄弟结点之间加一条连线
2去线。对每个结点,只保留它与第一个孩子的连线,删除与其他孩子的连线
3层次调整。以根结点为轴心,将整棵树旋转一定的角度,使之层次分明。注意第一个孩子是二叉树结点的左孩子,兄弟转换过来的孩子是结点的右孩子。

森林转换成二叉树

1把每棵树转换成二叉树。
2第一棵树不动,依次将之后的二叉树的根节点作为前一棵二叉树的根节点的右孩子。

二叉树转换成树

1加线。对每个结点如果左孩子存在,就将该结点与左孩子的n个右结点依次用线分别连接起来。
2.去线。删除原二叉树中所有结点与其右孩子结点的连线。
3.层次调整。

二叉树转换成森林

如果一棵二叉树的根结点有右孩子,则这棵二叉树能转换成森林。
1.从根结点开始,若右孩子存在,则把与右孩子结点的连线删除。依次对分离后的二叉树进行同样的操作。
2.将每棵分离后的二叉树转换成树

树和森林的遍历

树的遍历分为两种:
1.先根遍历树:先访问数的根结点,再依次先根遍历根的每棵子树。
2.后根遍历树:先依次后根遍历每棵子树,然后再访问根结点。

森林的遍历分为两种
1.前序遍历:依次先根遍历每棵树
2.后序遍历:依次后根遍历每棵树

赫夫曼树及其应用

假设有n个权值{w1,w2,…,wn}构造一棵有n个结点的二叉树,每个叶子结点带权wk,每个叶子的路径长度为lk,我们通常记作,则其中带权路径长度WPL最小的二叉树称做赫夫曼树。

赫夫曼树的构造

赫夫曼树可以用来做赫夫曼编码。

你可能感兴趣的:(编程基础)