zoukankan      html  css  js  c++  java
  • 线索二叉树

    线索二叉树

    由于具有NN个节点的二叉查找树有N+1N+1NULLNULL指针,因此在二叉查找树中指定给指针信息的空间的一半被浪费了。
    若一个节点有一个NULLNULL左孩子,我们使它的左儿子指向它的中缀前驱(inorder predecessor),若一个节点有一个NULLNULL右孩子,我们让它的右儿子指向它的中缀后继(inorder successor)。这就叫做线索二叉树(threaded tree),而附加的指针就叫做线索(thread)

    • 为使从实际的儿子指针中区分出线索,需要在每个节点增加一个成员,用以标志指针是线索还是孩子。
    typedef enum
    {
        Linked,	// 表示正常孩子
        Thread	// 表示线索
    } PointerTag;
    
    typedef int ElementType;
    struct ThreadTree;
    typedef struct ThreadTree *Tree;
    typedef struct ThreadTree *Position;
    struct ThreadTree
    {
        ElementType Element;
        Tree Left;
        Tree Right;
        PointerTag LTag;
        PointerTag RTag;
    };
    

    算法实现

    线索二叉树的线索算法

    /**
     * 以结点 p 为根的子树中序线索化
     * 1. 如果 p 非空,左子树递归线索化
     * 2. 如果 p 的左孩子为空,则给 p 加上左线索,将其 Ltag 置为 1,让 p 的左孩子指针指向 pre(前驱);否则将 p 的 LTag 置为 0 。
     * 3. 如果 pre 的右孩子为空,则给 pre 加上右线索,将其 RTag 置为 1,让 pre 的右孩子指针指向 p(后继);否则将 pre 的 RTag 置为 0 。
     * 4. 将 pre 指向刚访问过的结点 p,即 pre = p 。
     * 5. 右子树递归线索化。
     * */
    void InThreading(Tree p)
    {
        // pre 是全局变量,初始化时右孩子指针为空,便于在树的最左点开始建立线索
        if (p != NULL)
        {
            InThreading(p->Left);
            if (p->Left == NULL)
            {
                p->LTag = Thread;
                p->Left = pre;
            }
            else
                p->LTag = Linked;
            if (pre->Right == NULL)
            {
                pre->RTag = Thread;
                pre->Right = p;
            }
            else
                p->RTag = Linked;
            pre = p;
            InThreading(p->Right);
        }
    }
    
    void InOrderThreading(Tree *Thrt, Tree T)
    {
        // 中序遍历二叉树 T,并将其中序线索化,Thrt 指向头结点
        (*Thrt) = (Tree)malloc(sizeof(struct ThreadTree)); // 建立头结点
        (*Thrt)->LTag = Linked;                            // 头结点有左孩子,如树为空,则其左孩子为树根
        (*Thrt)->RTag = Thread;                            // 头结点的右孩子指针为右线索
        (*Thrt)->Right = (*Thrt);
        if (!T)
            (*Thrt)->Left = (*Thrt); // 若树为空,则作指针也指向自己
        else
        {
            (*Thrt)->Left = T;
            pre = (*Thrt);
            InThreading(T);       //中序线索化
            pre->Right = (*Thrt); // pre 为最右结点,pre 的右线索指向头结点
            pre->RTag = Thread;
            (*Thrt)->Right = pre;
        }
    }
    

    线索二叉树的中序遍历

    /**
     * 遍历线索二叉树
     * 1. 指针 p 指向根节点。
     * 2. p 为非空树或遍历未结束时,循环执行以下操作:
     *  - 沿着左孩子向下,到达最左下结点 *p,它是中序的第一个结点;
     *  - 访问 *p
     *  - 沿着右线索反复查找当前结点 *p 的后继结点并访问后继结点,直至右线索为 0 或者遍历结束
     *  - 转向 p 的右子树
     * 时间复杂度:O(N)
     * 空间复杂度:O(1)
     * 因为没有使用栈实现递归操作
     * */
    void InOrderTraverse_Thr(Tree T)
    {
        // T 指向头结点,头结点的左孩子 Left 指向根结点
        // 中序遍历二叉线索树 T 的非递归算法,对数据元素直接输出
        Tree p;
        p = T->Left;
        while (p != T) // 空树或遍历结束时, p == T
        {
            while (p->LTag == Linked)
                p = p->Left; // 沿着左孩子向下
            PrintElement(p); // 访问其左子树为空的结点
            while (p->RTag == Thread && p->Right != T)
            {
                p = p->Right;
                PrintElement(p);
            }
            p = p->Right;
        }
    }
    

    完整代码

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef enum
    {
        Linked,
        Thread
    } PointerTag;
    
    typedef int ElementType;
    struct ThreadTree;
    typedef struct ThreadTree *Tree;
    typedef struct ThreadTree *Position;
    struct ThreadTree
    {
        ElementType Element;
        Tree Left;
        Tree Right;
        PointerTag LTag;
        PointerTag RTag;
    };
    
    Tree pre; // 头结点声明
    
    void PrintElement(Tree T)
    {
        printf("%d ", T->Element);
    }
    
    /**
     * 以结点 p 为根的子树中序线索化
     * 1. 如果 p 非空,左子树递归线索化
     * 2. 如果 p 的左孩子为空,则给 p 加上左线索,将其 Ltag 置为 1,让 p 的左孩子指针指向 pre(前驱);否则将 p 的 LTag 置为 0 。
     * 3. 如果 pre 的右孩子为空,则给 pre 加上右线索,将其 RTag 置为 1,让 pre 的右孩子指针指向 p(后继);否则将 pre 的 RTag 置为 0 。
     * 4. 将 pre 指向刚访问过的结点 p,即 pre = p 。
     * 5. 右子树递归线索化。
     * */
    void InThreading(Tree p)
    {
        // pre 是全局变量,初始化时右孩子指针为空,便于在树的最左点开始建立线索
        if (p != NULL)
        {
            InThreading(p->Left);
            if (p->Left == NULL)
            {
                p->LTag = Thread;
                p->Left = pre;
            }
            else
                p->LTag = Linked;
            if (pre->Right == NULL)
            {
                pre->RTag = Thread;
                pre->Right = p;
            }
            else
                p->RTag = Linked;
            pre = p;
            InThreading(p->Right);
        }
    }
    
    void InOrderThreading(Tree *Thrt, Tree T)
    {
        // 中序遍历二叉树 T,并将其中序线索化,Thrt 指向头结点
        (*Thrt) = (Tree)malloc(sizeof(struct ThreadTree)); // 建立头结点
        (*Thrt)->LTag = Linked;                            // 头结点有左孩子,如树为空,则其左孩子为树根
        (*Thrt)->RTag = Thread;                            // 头结点的右孩子指针为右线索
        (*Thrt)->Right = (*Thrt);
        if (!T)
            (*Thrt)->Left = (*Thrt); // 若树为空,则作指针也指向自己
        else
        {
            (*Thrt)->Left = T;
            pre = (*Thrt);
            InThreading(T);       //中序线索化
            pre->Right = (*Thrt); // pre 为最右结点,pre 的右线索指向头结点
            pre->RTag = Thread;
            (*Thrt)->Right = pre;
        }
    }
    
    /**
     * 遍历线索二叉树
     * 1. 指针 p 指向根节点。
     * 2. p 为非空树或遍历未结束时,循环执行以下操作:
     *  - 沿着左孩子向下,到达最左下结点 *p,它是中序的第一个结点;
     *  - 访问 *p
     *  - 沿着右线索反复查找当前结点 *p 的后继结点并访问后继结点,直至右线索为 0 或者遍历结束
     *  - 转向 p 的右子树
     * 时间复杂度:O(N)
     * 空间复杂度:O(1)
     * 因为没有使用栈实现递归操作
     * */
    void InOrderTraverse_Thr(Tree T)
    {
        // T 指向头结点,头结点的左孩子 Left 指向根结点
        // 中序遍历二叉线索树 T 的非递归算法,对数据元素直接输出
        Tree p;
        p = T->Left;
        while (p != T) // 空树或遍历结束时, p == T
        {
            while (p->LTag == Linked)
                p = p->Left; // 沿着左孩子向下
            PrintElement(p); // 访问其左子树为空的结点
            while (p->RTag == Thread && p->Right != T)
            {
                p = p->Right;
                PrintElement(p);
            }
            p = p->Right;
        }
    }
    
    Position Insert(ElementType X, Tree T)
    {
        if (T == NULL)
        {
            T = (Tree)malloc(sizeof(struct ThreadTree));
            T->Element = X;
            T->Left = T->Right = NULL;
            T->LTag = T->RTag = Linked;
        }
        else if (X < T->Element)
            T->Left = Insert(X, T->Left);
        else if (X > T->Element)
            T->Right = Insert(X, T->Right);
        return T;
    }
    
    int main()
    {
        Tree T;    // 树的根节点
        Tree Thrt; // 树的头结点
        T = NULL;
        T = Insert(3, T);
        T = Insert(1, T);
        T = Insert(4, T);
        T = Insert(5, T);
        T = Insert(9, T);
        InOrderThreading(&Thrt, T);
        InOrderTraverse_Thr(Thrt);
        printf("
    ");
        system("pause");
        return 0;
    }
    

    输出结果

    1 3 4 5 9
    请按任意键继续. . .
    
    不一定每天 code well 但要每天 live well
  • 相关阅读:
    Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
    DHCP "No subnet declaration for xxx (no IPv4 addresses)" 报错
    Centos安装前端开发常用软件
    kubernetes学习笔记之十:RBAC(二)
    k8s学习笔记之StorageClass+NFS
    k8s学习笔记之ConfigMap和Secret
    k8s笔记之chartmuseum搭建
    K8S集群集成harbor(1.9.3)服务并配置HTTPS
    Docker镜像仓库Harbor1.7.0搭建及配置
    Nginx自建SSL证书部署HTTPS网站
  • 原文地址:https://www.cnblogs.com/geekfx/p/12423062.html
Copyright © 2011-2022 走看看