美文网首页
数据结构(五)树和二叉树

数据结构(五)树和二叉树

作者: AdRainty | 来源:发表于2021-08-22 00:48 被阅读0次

    5.1 树的基本概念

    5.1.1 树的定义

    树是n(n \ge 0)个结点的有限集,当n=0称为空树。任意一颗非空树

    1)有且仅有一个特定的称为根的结点

    2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集,其中每个集合本身又是一颗树,并且称为根的子树。

    ①树的根结点没有前驱,除了根结点外所有结点有且仅有一个前驱

    ②树中所有结点可以有零个或多个后继

    在n个结点中的树中有n-1条边

    1. 树中一个结点的孩子个数称为该结点的度
    2. 度大于0的结点称为分支结点,度为0的结点称为叶子结点
    3. 森林是m(m \ge 0)颗互不相交的树的集合

    5.1.2 树的性质

    ①结点树=度数+1

    ②度为m的树第i层至多有m^{i-1}个结点

    ③高度为h的树至多有(m^h-1)/(m-1)个结点

    ④高度为h的m叉树至少有h个结点;高度为h的度为m的树至少有h+m-1个结点

    ⑤具有n个结点的m叉树的最小高度为\lceil log_m{(n(m-1)+1)} \rceil

    5.2 二叉树

    5.2.1 二叉树的概念

    二叉树是一种特殊的树,每个结点至多只有两颗子树

    二叉树与度为2的有序树的区别:

    ①度为2的树至少有3个结点,而二叉树可以为空

    ②度为2的有序树孩子左右次序可以无序区分

    5.2.2 几个特俗的二叉树

    5.2.2.1 满二叉树

    树中每层都含有最多的结点;对于编号为i的结点,若有双亲,则其双亲为\lfloor i/2 \rfloor,若有左孩子,则左孩子为2i,若有右孩子,右孩子为2i+1。

    5.2.2.2 完全二叉树

    高度为h,有n个结点的二叉树,当且仅当其每个结点都与高度为h的满二叉树编号为1~n的结点一一对应时,称为完全二叉树

    ①若i \le \lfloor n/2 \rfloor,则结点i为分支结点,否则为叶子结点

    ②叶子结点只可能在最大两层上出现,对于最大层叶子结点,都一次排列在该层最左边的位置上

    ③若有度为1的结点,则只可能有1个,且该节点只有左孩子没有右孩子

    ④一旦出现某结点为叶子结点或只有左孩子,则编号大于i的结点均为叶子结点

    ⑤若n为奇数,则每个分支结点都有左孩子和右孩子,若n为偶数,则编号最大的分支结点(n/2)只有左孩子没有右孩子,其余分支结点左右孩子都有

    满二叉树和完全二叉树

    5.2.2.3 二叉排序树

    左子树上所有结点关键字均小于根结点关键字,右子树上的所有结点均大于根节点的关键字;左子树和右子树又各是一颗二叉排序树

    5.2.3 二叉树的性质

    ①非空二叉树上的叶子结点树等于度为2的结点数+1

    ②非空二叉树上的第k层上至多有2^{k-1}(k \ge 1)个结点

    ③高度为h的二叉树至多有2^{h}-1个结点

    ④对完全二叉树按从上到下,从左到右的顺序编号1~n,则

    1. i > 1,结点i双亲\lfloor i/2\rfloor
    2. 2i<n时结点i的左孩子编号为2i,否则无左孩子
    3. 2i+1 \le n时,结点i右孩子编号为2i+1,否则无右孩子
    4. 结点i的所在层次(深度)为\lfloor log_2i +1 \rfloor
    5. 具有n个结点的完全二叉树高度为\lceil log_2(n+1) \rceil\lfloor log_2n \rfloor +1

    5.3 二叉树的存储结构

    二叉树分为顺序存储和链式存储,但由于顺序存储空间利用率较低,因此二叉树存储一半都采用链式存储结构,用链表来存储二叉树中每个结点

    typedef struct BiTNode{
        ElemType data;
        struct BiTNode *lchild, *rchild;
    }BiTNode, *BiTree;
    

    在含有n个结点的二叉链表中,含有n+1个空链域

    5.4 二叉树的遍历

    遍历二叉树要决定对根节点N,左子树L和右子树R的访问顺序,常见的有先序(NLR)、中序(LNR)和后序(LRN)三种遍历方式

    5.4.1 先序遍历

    void PreOrder(BiTree T){
        if (T!=NULL){
            visit(T);  //访问根节点
            PreOrder(T->lchild); //递归遍历左子树
            PreOrder(T->rchild); //递归遍历右子树
        }
    }
    

    5.4.2 中序遍历

    void InOrder(BiTree T){
        if (T!=NULL){
            InOrder(T->lchild); //递归遍历左子树
            visit(T);  //访问根节点
            InOrder(T->rchild); //递归遍历右子树
        }
    }
    

    5.4.3 后序遍历

    void PostOrder(BiTree T){
        if (T!=NULL){
            PostOrder(T->lchild); //递归遍历左子树
            PostOrder(T->rchild); //递归遍历右子树
            visit(T);  //访问根节点
        }
    }
    

    5.4.4 层次遍历

    void LevelOrder(BiTree T){
        InitQueue(Q);  //初始化辅助队列
        BiTree p;
        EnQueue(Q,T); //根节点入队
        while(!IsEmpty(Q)){
            DeQueue(Q,p); //队头结点出队
            visit(p);  //访问出队结点
            if(p->lchild!=NULL) EnQueue(Q,p->lchild);
            if(p->rchild!=NULL) EnQueue(Q,p->rchild);
        }
    }
    

    只有中序遍历和其他任意一个遍历才可以唯一确定一颗二叉树

    5.5 线索二叉树

    在含有n个结点二叉树中,有n+1个空指针,为了利用这些空指针,引入线索二叉树

    规定:若无左子树,令lchild指向其前驱结点,若无右子树,令rchild指向其后继结点,并增加两个标志域标识指针域是指向其左右孩子还是前驱后继

    typedef struct ThreadNode{
        ElemType data;
        struct ThreadNode *lchild, *rchild;
        int ltag, rtag;
    }ThreadNode, *ThreadTree;
    

    5.5.1 中序线索二叉树的遍历

    void InThread(ThreadTree &p, ThreadTree &pre){
        if(p!=NULL){
            InThread(p->lchild, pre);
            if(p->lchild==NULL){
                p->lchild=pre;
                p->ltag=1;
            }
            if(pre!=NULL&&pre->rchild!=NULL){
                pre->rchild=p;
                pre->rtag=1;
            }
            pre=p;
            InThread(p->rchild,pre);
        }
    }
    

    5.6 树和森林

    5.6.1 树的存储结构

    双亲表示法:每个结点中保存指向双亲的“指针”

    #define MAX_TREE_SIZE 100 // 最多结点数
    typedef struct{
        ElemType data;
        int parent; //双亲位置域
    }PTNode;
    typedef struct{
        PTNode nodes[MAX_TREE_SIZE];  //双亲表示
        int n;  //结点数
    }PTree;
    

    孩子表示法:顺序存储各个节点,每个结点中保存孩子链表头指针

    struct CTNode{
        int child;//孩子结点在数组中的位置
        struct CTNode *next; //下一个孩子
    };
    typedef struct{
        ElemType data;
        struct CTNode *firstChild; //第一个孩子
    }CTBox;
    typedef struct {
        CTBox nodes[MAX_TREE_SIZE];
        int n, r;//结点数和根的位置
    }CTree;
    

    孩子兄弟法:有两个指针,分别指向第一个孩子的结点和下一个兄弟结点

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

    5.7 树与二叉树应用

    5.7.1 二叉排序树

    二叉排序树的查找

    BSTNode *BST_Search(BiTree T, ElemType key){
        while(T!=NULL&&key!=T->data){
            if(key<T->data) T=T->lchild;
            else T=T->rchild;
        }
        return T;
    }
    

    二叉排序树的插入

    int BST_Insert(BiTree &T,ElemType k){
        if(T==NULL){
            T=(BiTree)malloc(sizeof(BSTNode));
            T->key=k;
            T->lchild=T->rchild=NULL;
            return 1;
        }
        else if (k==T->key)return 0;
        else if (k<T->key) return BST_Insert(T-lchild, k);
        else return BST_Insert(T-rchild, k);
    }
    

    5.7.2 平衡二叉树

    任意结点左右子树高度差不超过1的树称为平衡二叉树

    调整平衡二叉树问题分成四类:LL、RR、LR、RL

    1. LL指的是A结点左孩子的左子树插入新的结点导致失衡,解决办法一般是进行一次右旋操作

    2. RR指的是A结点的右孩子的右子树插入新结点,导致失去平衡;这时我们会对A结点左旋

    3. LR指的是结点A的左孩子的右子树上插入新的结点导致失衡,需要先左旋再右旋

    4. RL指的是A结点的右孩子的左子树上插入新结点,导致二叉树不平衡,,这时需要先右旋再左旋

    ①设n(h)表示深度为h的平衡二叉树最少结点数,则
    n(h)=\left\{ \begin{array}{l} 0,h=0\\ 1,h=1\\ n(h-1)+n(h-2)+1,h\ge1\\ \end{array} \right.

    5.7.3 哈夫曼树

    树中所有叶结点带权路径长度之和称为该树的带权路径长度
    WPL=\sum_{i=1}^n{w_il_i}
    在含有n个带权叶结点二叉树中,WPL最小的树称为哈夫曼树

    哈夫曼树.png

    相关文章

      网友评论

          本文标题:数据结构(五)树和二叉树

          本文链接:https://www.haomeiwen.com/subject/qbaqiltx.html