• 线索二叉树


    线索二叉树

    由于具有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
  • 相关阅读:
    python得到今天前的七天每天日期
    python 实现元组中的的数据按照list排序, python查询mysql得到的数据是元组格式,按照list格式对他们排序
    NoReverseMatch at /salesman/zhuce/ Reverse for '/zhuce/' with arguments '()' and keyword arguments '{}' not found. 0 pattern(s) tried: []
    为什么springMVC和Mybatis逐渐流行起来了?
    图像的七个不变矩 可用于图像的匹配
    【可视化必备】大数据时代的可视化工具
    常用机器视觉工具----图像分析工具(blob分析)
    【转】七种常见阈值分割代码(Otsu、最大熵、迭代法、自适应阀值、手动、迭代法、基本全局阈值法)
    C#编写滤镜 图片色调取反效果(Invert)
    Bitmap四种属性
  • 原文地址:https://www.cnblogs.com/geekfx/p/12423062.html
Copyright © 2020-2023  润新知