首页 > 其他分享 >线索化二叉树

线索化二叉树

时间:2023-05-09 21:46:27浏览次数:43  
标签:node 线索 ThreadedTreeNode public 二叉树 节点

线索化二叉树

1. 问题分析

普通二叉树问题分析.png
  • 当对上面的二叉树进行中序遍历时,序列应为:[8,3,10,1,14,6];
  • 但存在一个问题也即,编号为6,8,10,14的几个节点的左右指针并没有完全利用上
  • 如果希望利用到各个节点的左右指针,让各个节点可以指向自己的前后节点,即使用线索化二叉树

2. 线索化二叉树基本介绍

  • n个节点的二叉链表中含有n + 1个空指针域;利用二叉链表中的空指针域,存放指向该节点在某种遍历次序(前序、中序或后序)下的前驱和后继节点的指针(这种附加的指针称为“线索”
    • 公式推导:n个节点共有2n个指针域,除了根节点外的每个节点都有被指针指向(n-1),所以空指针域有2n - (n - 1) = n + 1
  • 这种加上了“线索”的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded Binary Tree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树中序线索二叉树后序线索二叉树
  • 前驱节点:即一个节点的前一个节点。以上图的中序线索二叉树为例,中序遍历结果为:[8,3,10,1,14,6],10节点的前驱节点为3节点。但对8节点而言,其虽然左子节点为空但在序列中8节点没有前一个节点,因此8节点没有前驱节点
  • 后继节点:即一个节点的后一个节点。同样以上图的中序线索二叉树为例,中序遍历结果为:[8,3,10,1,14,6],10节点的后继节点为1节点。但对6节点而言,其虽然右子节点为空但在序列中6节点没有后一个节点,因此6节点没有后继节点

3. 中序线索化二叉树思路分析

中序线索化二叉树.png
  • 当线索化二叉树后,node节点的left和right指针有如下情况:
    • left指向左子树,也可能指向node节点的前驱节点。eg.1节点的left指向左子树,而10节点的left指向前驱节点3;
    • right指向右子树,也可能指向node节点的后继节点。eg.1节点的right指向右子树,而10节点的right指向后继节点1.
  • 此时就需要在节点类中定义两个变量leftTyperightType,分别表示left和right所指向的类型。默认为0表示指向对应的左/右子树;当对节点进行线索化时,就需要将相应的leftType/rightType置为1表示指向前驱节点/后继节点
  • 同时,在实现线索化的过程中,还需要定义一个preNode表示指向当前节点的前驱节点初始化为null
    • 需要注意的是,处理前驱节点和后继节点不是在同一轮递归中实现的;
    • 处理后继节点时,应以preNode为基准,执行preNode.setRight(node)操作,也即将前驱节点的右指针指向当前节点
    • 每处理完一个节点,就置preNode = node,也即令当前节点作为下一个待处理节点的前驱节点

4. 中序线索化二叉树遍历思路分析

  • 线索化后,各个节点的指向有所变化,因此不能直接使用原来的中序遍历方式。进行遍历时应判断节点的leftType和rightType,另外需注意,遍历的次序应与线索化二叉树的类型保持一致,即中序线索化二叉树必须用中序线索化遍历

5. 中序线索化二叉树实现 + 遍历代码实现

package com.datastructure;

/**
 * @author SnkrGao
 * @create 2023-05-09 19:29
 */
public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {

        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();

        ThreadedTreeNode root = new ThreadedTreeNode(1, "tom");
        ThreadedTreeNode node2 = new ThreadedTreeNode(3, "jerry");
        ThreadedTreeNode node3 = new ThreadedTreeNode(6, "mike");
        ThreadedTreeNode node4 = new ThreadedTreeNode(8, "marry");
        ThreadedTreeNode node5 = new ThreadedTreeNode(10, "smith");
        ThreadedTreeNode node6 = new ThreadedTreeNode(14, "lisa");

        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        threadedBinaryTree.setRoot(root);

        // 中序线索化二叉树
        threadedBinaryTree.threadedNodes(root);

        System.out.println("节点10的前驱节点为:" + node5.getLeft());
        System.out.println("节点10的后继节点为:" + node5.getRight());

        System.out.println("中序线索化二叉树遍历结果:");
        threadedBinaryTree.inorderThreadedTraversal();


    }
}

class ThreadedBinaryTree {
    private ThreadedTreeNode root;

    public void setRoot(ThreadedTreeNode root) {
        this.root = root;
    }

    // preNode指向当前节点的前驱节点
    private ThreadedTreeNode preNode = null;

    /**
     * 中序线索化二叉树
     * @param node 表示当前节点
     */
    public void threadedNodes(ThreadedTreeNode node) {
        // 判断当前节点是否为空
        if (node == null) {
            return;
        }

        // 线索化左子树
        threadedNodes(node.getLeft());

        // 线索化当前节点
        if (node.getLeft() == null) {
            // 当前节点的左指针指向前驱节点
            node.setLeft(preNode);
            node.setLeftType(1);
        }
        // 以preNode为基准,处理当前节点的后继节点
        if (preNode != null && preNode.getRight() == null) {
            preNode.setRight(node);
            preNode.setRightType(1);
        }
        // 每处理完一个节点,令当前节点作为下一个待处理节点的前置节点
        preNode = node;

        // 线索化右子树
        threadedNodes(node.getRight());
    }

    // 中序遍历线索化二叉树
    public void inorderThreadedTraversal() {
        // 表示当前遍历的节点,从root开始遍历
        ThreadedTreeNode node = root;

        while (node != null) {
            // 循环遍历找到第一个leftType == 1的节点,即左子节点为空的节点
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }
            System.out.println(node);
            // 若当前节点的右指针指向的是后继节点,一直输出
            while (node.getRightType() == 1) {
                System.out.println(node.getRight());
                node = node.getRight();
            }
            // 后移,继续遍历
            node = node.getRight();
        }
    }
}

class ThreadedTreeNode {
    private int no;
    private String name;
    private ThreadedTreeNode left;
    private ThreadedTreeNode right;

    // 若leftType == 0表示指向左子树,若为1表示指向前驱节点
    private int leftType;
    // 若rightType == 0表示指向右子树,若为1表示指向后继节点
    private int rightType;

    public ThreadedTreeNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ThreadedTreeNode getLeft() {
        return left;
    }

    public void setLeft(ThreadedTreeNode left) {
        this.left = left;
    }

    public ThreadedTreeNode getRight() {
        return right;
    }

    public void setRight(ThreadedTreeNode right) {
        this.right = right;
    }

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    @Override
    public String toString() {
        return "ThreadedTreeNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

标签:node,线索,ThreadedTreeNode,public,二叉树,节点
From: https://www.cnblogs.com/SnkrGao/p/17386371.html

相关文章

  • 二叉树
    相关知识点:结点拥有的子树数称为结点的度树的度是树内各结点度的最大值树中结点的最大层数称为树的高度或深度 根结点:无双亲,唯一叶结点:无孩子,可以多个中间结点:一个双亲多个孩子 二叉树的特点:每个结点最多有两棵子树左子树和右子树是由顺序的特殊二叉树:斜树,每......
  • 顺序存储二叉树
    顺序存储二叉树1.介绍从数据存储来看,数组的存储方式和树的存储方式是可以互相转换的,即数组可以转换成树,树也可以转换成数组;遍历数组arr时,仍然可以以前序遍历、中序遍历、后序遍历的方式得到二叉树。2.顺序存储二叉树的特点顺序存储二叉树通常只考虑完全二叉树;下标为......
  • 二叉树
    树是有限个(n>0)元素组成的集合。树中每个结点拥有的孩子结点的个数称为该结点的度,度为0的结点为叶子结点或终端结点。树的度是树中结点的度的最大值。在有序树中,孩子结点沿用左边大、右边小的原则。二叉树是有限个(n>=0)结点的集合。可以为空,或者有一个结点作为根结点,其他结点......
  • 二叉树的线索化与遍历
    废话不说,上代码l1packagedataSrtuct.TreeAlgorithm;23importcom.sun.source.tree.Tree;45publicclassThreadBinaryTree{6publicstaticvoidmain(String[]args){7TreeNode2root=newTreeNode2(1,"M");8......
  • 线索二叉树
    (39条消息)线索二叉树,画图教你秒懂线索二叉树(线索二叉树的建立和简单操作)逻辑代码分析_IC00的博客-CSDN博客摘自这位大佬的内容,感觉写的很不错......
  • C/C++二叉树应用[2023-05-08]
    C/C++二叉树应用[2023-05-08]湖南应用技术学院实验(训)报告课程名称 数据结构与算法 课程代码 221031203 成绩评定 学院 信息工程学院 专业 物联网工程 指导老师 聂作财学生姓名 xxxx 学号 xxxxx 班级 物联xxxx实验地点 实验日期 年月日小组成员 无实验类型 □验......
  • leetcode 101 对称二叉树 Simple
    题目给你一个二叉树的根节点root,检查它是否轴对称。输入:root=[1,2,2,3,4,4,3]输出:true输入:root=[1,2,2,null,3,null,3]输出:false题解考察二叉树的遍历,使用广度优先BFS方法.BFS的关键在于使用队列,遍历树时,读到的节点先入队,再出队,出队时读取值,放入结......
  • (第26章)LinuxC本质中链表、二叉树和哈希表
    文章目录一、单链表的结构决定只能出栈,入栈1.链表的结构2.链表与数组的区别3.单链表所有基本操作代码(1)链表的插入(2)链表的查找(3)链表的删除(3)遍历整个链表(4)销毁整个链表4.习题5.C++NULL指针二、双向链表结构决定可以出队和入队1.在上面的单项链表上改改,得到双向链表2.改进双向链表:新增......
  • 平衡二叉树
    classSolution{public:boolres=true;intdfs(TreeNode*root)//返回以root为根节点的子树深度{if(root==NULL)return0;intl=dfs(root->left),r=dfs(root->right);if(abs(l-r)>1)res=false;returnmax(l......
  • 线索二叉树
    线索二叉树为什么要研究线索二叉树?如何解决上面的问题?我们使用第三种方法二叉链表当中右很多空的指针域线索二叉树定义例子线索二叉树增设了这些指针之后,会难以区分是指向孩子的指针还是指向前驱结点或者后继结点的指针所以要加上两个标志域线索二叉树的结点结......