IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Java实现前中后序线索化二叉树以及遍历 -> 正文阅读

[数据结构与算法]Java实现前中后序线索化二叉树以及遍历

一、线索化二叉树的原理

在前面介绍二叉树的文章中提到,二叉树可以使用两种存储结构:顺序存储和链式存储,在使用链式存储时,会存在大量的空指针域,n个节点的二叉链表中含有n+1[ 2n-(n-1)=n+1 ]个空指针域,为了可以充分利用这些空指针域,引申出了线索化二叉树,将这些空指针域利用起来,提高检索效率。

在这里插入图片描述

上图中,紫色区域就是没有指向的空指针域,从存储空间的角度来看,这些空指针域浪费了内存资源。

从另外的角度思考,如果需要知道C节点的前驱节点和后继节点,每次都都需要进行一次遍历,是否可以提前存储C节点的前驱节点和后继节点从而省去遍历的操作从而提高效率呢?

综合以上分析,可以通过充分利用二叉链表中的空指针域,存入节点在某种遍历方式下的前驱和后继节点的指针。

这种指向前驱和后继的指针成为线索,加上线索的二叉链成为线索链表,而对应的二叉树就称为线索化二叉树(Threaded Binary Tree)


二、构建线索化二叉树

  • 先对二叉树进行中序遍历(不了解二叉树遍历的可以参考数据结构:二叉树),将所有节点右指针为空的指针域指针它的后继节点,如下图:

在这里插入图片描述

通过中序遍历到D节点,得到了D节点right指针为空,并且D节点后继节点为B,于是将D的right节点指向B节点(如上图中的①操作),而B节点right指针也为空,于是将right指针指向A节点(如上图中的②操作),以此类推,到F节点的后继节点为Null,则F的right指针指向Null。

  • 接下来将这颗二叉树所有左指针为空的指针指向它的前驱节点,如下图:

    在这里插入图片描述

    如上图,D节点的left指针域指向Null(第⑤部操作),E节点的前驱节点是A,将E的left指针指向A节点,以此类推,F节点的left节点指向C节点。

  • 通过上述两步操作完成了整个二叉树的线索化。

通过线索化后,可以看出(蓝色箭头表示前驱,粉色箭头表示后继)线索化二叉树相当于是把二叉树转换成一个特殊的双向链表,这样对新增、删除以及查找节点都带来了方便,提高了效率,转换为链表后的图示如下:

在这里插入图片描述

线索化需要对节点的结构进行修改,修改之后的数据结构如下:

class Node
{
    private int num;
    private Object data;	//数据域
    private Node left;		//左指针域
    private Node right;		//右指针域

    /**
     * leftType == 0表示指向的是左子树,如果是1表示指向前驱节点
     */
    private int leftType;
    
    /**
     * rightType == 0表示指向的是右子树,如果是1表示指向后继节点
     */
    private int rightType;
}    

三、代码实现线索二叉树

  • 节点结构

    class Node
    {
        private int num;
        private Object data;
        private Node left;
        private Node right;
    
        /**
         * leftType == 0表示指向的是左子树,如果是1表示指向前驱节点
         */
        private int leftType;
    
        /**
         * rightType == 0表示指向的是右子树,如果是1表示指向后继节点
         */
        private int rightType;
    
        public Node(int num, Object data)
        {
            this.num = num;
            this.data = data;
        }
    
        /**
         * 前序遍历
         */
        public void preOrder()
        {
            //先输出父节点信息
            System.out.println(this);
            //判断此节点的左节点是否为空
            //如果左节点不为空并且指针类型不是前驱类型
            if (this.left != null && this.leftType != 1)
            {
                //向左前序遍历
                this.left.preOrder();
            }
            //判断此节点的右节点是否为空
            //如果右节点不为空并且指针类型不是后继类型
            if (this.right != null && this.rightType != 1)
            {
                //向右前序遍历
                this.right.preOrder();
            }
        }
    
    
        /**
         * 中序遍历线索化二叉树
         */
        public void infixOrder()
        {
            //判断此节点的左节点是否为空
            if (this.left != null && this.leftType != 1)
            {
                //向左中序遍历
                this.left.infixOrder();
            }
            //输出父节点信息
            System.out.println(this);
            //判断此节点的右节点是否为空
            if (this.right != null && this.rightType != 1)
            {
                //向右中序遍历
                this.right.infixOrder();
            }
        }
    
        /**
         * 后序遍历
         */
        public void postOrder()
        {
            //判断此节点的左节点是否为空
            if (this.left != null && this.leftType != 1)
            {
                //向左后序遍历
                this.left.postOrder();
            }
            //判断此节点的右节点是否为空
            if (this.right != null && this.rightType != 1)
            {
                //向右后序遍历
                this.right.postOrder();
            }
            //输出父节点信息
            System.out.println(this);
        }
    	
        //get set方法省略
        
        @Override
        public String toString()
        {
            return "Node{" +
                    "num=" + num +
                    ", data=" + data +
                    '}';
        }
    }
    
  • 线索化二叉树结构

    /**
     * 定义ThreadedBinaryTree 实现了线索化功能的二叉树
     */
    class ThreadedBinaryTree
    {
        /**
         * 根节点
         */
        private Node root;
    
        /**
         * 为了实现线索化,需要创建要指向当前节点的前驱节点的指针
         * 在递归进行线索化时,pre总是保留前一个节点
         */
        private Node pre = null;
    
        public void setRoot(Node root)
        {
            this.root = root;
        }
    
        public void createBinaryTree(ArrayList<Node> list)
        {
            this.createBinaryTree(list,0);
        }
    
        /**
         * 创建二叉树
         * @param list 节点列表
         * @param index 用于创建的索引
         * @return
         */
        private Node createBinaryTree(ArrayList<Node> list, int index)
        {
            Node node = null;
    
            if (isEmpty())
            {
                setRoot(list.get(0));
            }
    
            if (index < list.size())
            {
                node = list.get(index);
                node.setLeft(createBinaryTree(list, (2*index+1)));
                node.setRight(createBinaryTree(list, (2*index+2)));
            }
            return node;
        }
    
        public boolean isEmpty()
        {
            return root == null;
        }
    
    
        public void preThreadNodes()
        {
            this.preThreadNodes(root);
        }
    
        public void infixThreadNodes()
        {
            this.infixThreadNodes(root);
        }
    
        public void postThreadNodes()
        {
            this.postThreadNodes(root);
        }
    
        /**
         * 前序线索化二叉树
         * @param node 当前需要线索化的节点
         */
        private void preThreadNodes(Node node)
        {
            //递归回溯条件
            if (node == null)
            {
                return;
            }
    
            //遍历到叶子节点(前驱指针未利用)
            if (node.getLeft() == null)
            {
                node.setLeft(pre);
                node.setLeftType(1);
            }
    
            //pre.getLeft() != node 一定不可少
            //否则在第一次回溯后到父节点后,如果不加上此判断会让pre节点后驱指针指向该父节点
            //此时pre的前驱后继节点均指向该父节点,必然会发生死循环无法结束递归
            if (pre != null && pre.getRight() == null && pre.getLeft() != node)
            {
                pre.setRight(node);
                pre.setRightType(1);
            }
    
            pre = node;
    
            if (node.getLeftType() == 0)
            {
                preThreadNodes(node.getLeft());
            }
    
            preThreadNodes(node.getRight());
        }
    
        /**
         * 中序线索化二叉树
         * @param node 当前需要线索化的节点
         */
        private void infixThreadNodes(Node node)
        {
            if (node == null)
            {
                return;
            }
            //1.递归线索化左子树
            infixThreadNodes(node.getLeft());
    
            //2.线索化当前节点
            //处理当前节点的前驱节点
            if (node.getLeft() == null)
            {
                //让当前节点的左指针指向前驱节点
                node.setLeft(pre);
                //修改当前节点的左指针的类型
                node.setLeftType(1);
            }
            //处理后继节点
            if (pre != null && pre.getRight() == null)
            {
                //让前驱节点的右指针指向当前节点
                pre.setRight(node);
                //修改前驱节点的右指针类型
                pre.setRightType(1);
            }
    
            //每处理一个节点之后让当前节点是下一个节点的前驱节点
            pre = node;
    
            //3.递归线索化右子树
            infixThreadNodes(node.getRight());
        }
    
        /**
         * 后序线索化二叉树
         * @param node 当前需要线索化的节点
         */
        private void postThreadNodes(Node node)
        {
            if (node == null)
            {
                return;
            }
    
            postThreadNodes(node.getLeft());
            postThreadNodes(node.getRight());
    
            if (node.getLeft() == null)
            {
                node.setLeft(pre);
                node.setLeftType(1);
            }
    
            if ( pre!= null && pre.getRight() == null )
            {
                pre.setRight(node);
                pre.setRightType(1);
            }
            pre = node;
        }
    
    
    
        /**
         * 非递归方式中序遍历线索化二叉树的方法
         */
        public void threadedInfixList()
        {
            Node node = root;
            while (node != null)
            {
                //循环地找到leftType == 1的节点
                while (node.getLeftType() == 0)
                {
                    node = node.getLeft();
                }
    
                System.out.println(node);
    
                while (node.getRightType() == 1)
                {
                    node = node.getRight();
                    System.out.println(node);
                }
    
                node = node.getRight();
            }
        }
    
    
        public void threadedTreePreList()
        {
            if (root != null)
            {
                root.preOrder();
            }else
            {
                System.out.println("二叉树为空,无法遍历");
            }
        }
    
        public void threadedTreeInfixList()
        {
            if (root != null)
            {
                root.infixOrder();
            }else
            {
                System.out.println("二叉树为空,无法遍历");
            }
        }
    
        public void threadedTreePostList()
        {
            if (root != null)
            {
                root.postOrder();
            }else
            {
                System.out.println("二叉树为空,无法遍历");
            }
        }
    }
    

以上。

如有不足或者错误欢迎评论指正。

整理不易,留个三连再走吧!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-20 15:22:16  更:2021-08-20 15:22:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:52:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码