2021-03-31

本文详细介绍了树的基本概念,如根节点、子树、度、叶节点、有序与无序树,重点剖析了二叉树的特性,包括满二叉树、完全二叉树、深度与节点数量。此外,还涵盖了线索二叉树的创建、线索化过程以及各种遍历方法,如前序、中序和层次遍历。通过实例展示了如何构造和操作二叉树及其相关数据结构。
摘要由CSDN通过智能技术生成

1,根结点唯一
2,子树不相交。
结点分类:
1,结点所拥有的子树,就是结点的度,通俗的说就是该结点有几个孩子就有几个度,把度当成它孩子就行。
2,度为0的结点(就是没有孩子的结点),叫叶节点或终端结点,除了该结点和根结点以外的结点,也叫内部结点。
3,树的度就是拥有最大度的结点的度,谁的孩子多,这个树的度就是该结点的度。
4,树的深度,没什么好说的。
5,如果将树中结点的各子树看成从左至右是有次序的,不能互换的,则称该树为有序树,否则称为无序树(二叉树就是有序树,因为它不能换左右结点)。
6,:双亲表示法、孩子表示法、孩子兄弟表示法,其实我感觉不用太过于死记硬背,应该要随机应变,可以根据需求自己设置表示方法,不一定只用这三种表示方法。

二叉树

1,在二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都
同一层上,这样的二叉树称为满二叉树。
2,完全二叉树,就是满二叉树的一部分或者就完全和满二叉树相等,并且是满二叉树的左半部分。就是假如给每一个结点按顺序编号,那么完全二叉树的结点的编号一定和满二叉树的编号相等。
3,在二叉树的k层,该层结点最多有2的(k-1)次方个,比如在二叉树的第三层,结点最多有2的2次方。
4,深度为k的树,结点最多有2的k次方-1个。比如深度为3的二叉树,结点最多有2的3次方-1,即7个,因为深度为3的满二叉树结点有7个
5,对于任何一个二叉树,叶结点数为n1,度为2的结点数为n2,则n1=n2+1;
6,具有n个结点的完全叉树的深度为 【log2n】+1,注意是完全二叉树。
7,如果对一棵有n 个结点的完全二叉树(其深度为【log2n】+1 的结点按层
序编号(从第 层到第 【log2n】+1 层,每层从左到右) ,对任一结点

  1. 如果 i=1 ,则结点 是二叉树的根,无双亲;如果i> 1,则其双亲是结点
    【i/2】
    如果 2i>n ,则结点i 无左孩子(结点i 为叶子结点) ;否则其左孩子是结点
    2i
    如果 2i+1>n ,则结点 i无右孩子;否则其右孩子是结点 2i+1.
    8,二叉树的顺序存储结构,其中完全二叉树,不会浪费中间的空间,但是如果非完全二叉树,那么在数组中间可能会有很多的空间浪费,所有顺序存储结构适用性不强。
    9,链式存储基本结点就是一个数据,一个左孩子指针,一个右孩子指针。
    10,前序遍历,根左右。中序遍历,左根右。后序遍历,左右根。层次遍历,就是一层一层的遍历,方法是每一次都把左右孩子放入一个队列中。
void cengci(binode *t)//不要问我为什么用拼音,问就是喜欢
{

    lqueue *q;
    chu(q);
    if(t!=NULL)//如果树不为空,将根节点存入数组。
        insertr(t,q);
    while(!emptyr(q))
    {
     delet(q,t);//出队
     printf("%c",t->e);
        if(t->lchild!=NULL)
            insertr(t->lchild,q);
        if(t->rchild!=NULL)
            insertr(t->rchild,q);
    }

}
void creat(binode **t)//二叉树的建立
{//为什么用二级指针,因为如果你用一级指针,由于你的左右孩子指针还没有被分配地址,你传递的孩子指针是未知的,当分配地址后,由于传递的是形参,你的根结点的孩子指针还是老样子。
    char ch;
    scanf("%c",&ch);
    if(ch=='#')
        *t=NULL;
    else
    {
        *t=new binode;
        if(!*t)
            exit(1);
        else
        {
            (*t)->e=ch;
            creat(&(*t)->lchild);
            creat(&(*t)->rchild);
        }
    }
}``
11,线索二叉树,就是充分利用未利用指针的二叉树,它比二叉树多了前驱和后继,即如果左孩子指针为空,就将它指向前驱,如果右孩子指针为空,就将它指向后继,为了区分左右孩子指针指向的到底是前驱后继还是左右孩子,结点的结构体便多设置一个判断结构,利用枚举enum(它的用法是enum{x,y}q,q就代表枚举变量,与int相似,里面的x,y分别为01.因为enum{}里面的变量默认为0,并且以此加1,当然也可以自定义x为2,则y为3)。需要注意的是枚举类型所消耗的内存要小于结构体指针变量,因此并非你想像中我利用了俩个空指针域却要多设置俩个变量,导致内存不变的情况。
12,#include <stdio.h>
#include <stdlib.h>
//函数状态结果代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值是函数结果状态代码
typedef int Status;
typedef char TElemType;

typedef enum { Link, Thread } PointerTag;

typedef struct BiThrNode
{
    TElemType data;
    struct BiThrNode *lchild, *rchild;
    PointerTag LTag;
    PointerTag RTag;
} BiThrNode, *BiThrTree;

//线索二叉树初始化
Status CreateBiThrNode(BiThrTree * B)
{
    char ch;
    scanf("%c", &ch);
    if(ch=='#') *B = NULL;
    else
    {
        if(!((*B) = (BiThrNode *)malloc(sizeof(BiThrNode)))) exit(OVERFLOW);
        (*B)->data = ch;
        (*B)->LTag = Link;
        (*B)->RTag = Link;
        CreateBiThrNode(&(*B)->lchild);
        CreateBiThrNode(&(*B)->rchild);
    }
    return OK;
}

//线索二叉树线索化
void InThreading(BiThrTree B,BiThrTree *pre)
{
    if(!B) return;

    InThreading(B->lchild,pre);

    if(!B->lchild)
    {
        B->LTag = Thread;
        B->lchild = *pre;
    }

    if(!(*pre)->rchild)
    {
        (*pre)->RTag = Thread;
        (*pre)->rchild = B;
    }

    *pre = B;
    InThreading(B->rchild,pre);
}

//为线索二叉树添加头结点,使之可以双向操作
Status InOrderThreading(BiThrTree *Thrt,BiThrTree T)
{
    if(!(*Thrt = (BiThrTree)malloc(sizeof(BiThrNode)))) exit(OVERFLOW);
    (*Thrt)->LTag = Link;
    (*Thrt)->RTag = Thread;
    (*Thrt)->rchild = (*Thrt);
    if(!T)
    {
        (*Thrt)->lchild = (*Thrt);
        return OK;       //若根结点不存在,则该二叉树为空,让该头结点指向自身.
    }
    BiThrTree pre;
    //令头结点的左指针指向根结点
    pre = (*Thrt);
    (*Thrt)->lchild = T;
    //开始递归输入线索化
    InThreading(T,&pre);
    //此时结束了最后一个结点的线索化了,下面的代码把头结点的后继指向了最后一个结点.
    //并把最后一个结点的后继也指向头结点,此时树成为了一个类似双向链表的循环.
    pre->rchild = *Thrt;
    pre->RTag = Thread;
    (*Thrt)->rchild = pre;
    return OK;
}

//非递归遍历线索二叉树
Status InOrderTraverse(BiThrTree T)
{
    BiThrNode *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;
    }
    return OK;
}

int main()
{
    BiThrTree B,T;
    CreateBiThrNode(&B);
    InOrderThreading(&T,B);
    printf("中序遍历二叉树的结果为:");
    InOrderTraverse(T);
    printf("\n");
}

//测试数据:abc##de#g##f###
13,树变为二叉树:兄弟连线,保留第一个孩子的连线,其他连线去掉,然后该结点的孩子是左孩子,兄弟转变成的孩子是右孩子。
14,森林转换成二叉树,把每一个树都转换成二叉树,然后第一个树不动,其他树的根节点都依次转换成上一个树根节点的右孩子。
15,二叉树转换树,就是反过来,将右孩子转换成兄弟。
16,森林的遍历:先根遍历就是每一次都先遍历根节点,从左往右,其实就是二叉树的前序遍历。
后根遍历就是其实就是二叉树的中序遍历。
17,赫夫曼树,就是带权路径长度 WPL 最小的二叉树称做赫夫曼树,wpl就是根结点到每一个有权值的结点的路径乘权值的和。
18,构造方法是先将权值排序,然后构造一个树结点,让该结点的左右孩子等于最小的那俩个权值结点,然后该树节点就等于俩孩子权值相加,然后有序加入去掉俩个最小权值结点的所有结点中。
19,哈夫曼编码就是哈夫曼树的基础上,把左孩子权值全部设置为0,右孩子设置为1.



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值