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("二叉树为空,无法遍历");
            }
        }
    }
    

以上。

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

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

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
线索二叉树是一种对普通二叉树进行优化的数据结构,其中每个节点除了左右子树指针之外,还有指向其前驱和后继节点的线索指针。线索化后,可以使用线索指针直接遍历整个树,而无需使用递归或栈等数据结构。 下面是使用 C 语言实现线索二叉树前中后序遍历的代码: ```c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // 定义线索二叉树节点结构体 typedef struct ThreadedBinaryTreeNode { int data; // 节点数据 struct ThreadedBinaryTreeNode *leftChild; // 左子树指针 struct ThreadedBinaryTreeNode *rightChild; // 右子树指针 bool leftTag; // 左线索标记 bool rightTag; // 右线索标记 } ThreadedBinaryTreeNode; // 全局变量,记录上一次遍历的节点 ThreadedBinaryTreeNode *preNode = NULL; // 创建新节点 ThreadedBinaryTreeNode* createNode(int data) { ThreadedBinaryTreeNode *newNode = (ThreadedBinaryTreeNode*)malloc(sizeof(ThreadedBinaryTreeNode)); newNode->data = data; newNode->leftChild = NULL; newNode->rightChild = NULL; newNode->leftTag = false; newNode->rightTag = false; return newNode; } // 中序线索化二叉树 void threadedInorder(ThreadedBinaryTreeNode *root) { if (root != NULL) { // 先线索化左子树 threadedInorder(root->leftChild); // 处理当前节点 if (root->leftChild == NULL) { root->leftChild = preNode; root->leftTag = true; } if (preNode != NULL && preNode->rightChild == NULL) { preNode->rightChild = root; preNode->rightTag = true; } preNode = root; // 线索化右子树 threadedInorder(root->rightChild); } } // 中序遍历线索二叉树 void inorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p != NULL) { while (p->leftTag == false) { p = p->leftChild; } printf("%d ", p->data); while (p->rightTag == true) { p = p->rightChild; printf("%d ", p->data); } p = p->rightChild; } } // 前序遍历线索二叉树 void preorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p != NULL) { printf("%d ", p->data); if (p->leftTag == false) { p = p->leftChild; } else if (p->rightTag == false) { p = p->rightChild; } else { while (p != NULL && p->rightTag == true) { p = p->rightChild; } if (p != NULL) { p = p->rightChild; } } } } // 后序遍历线索二叉树 void postorderTraversal(ThreadedBinaryTreeNode *root) { ThreadedBinaryTreeNode *p = root; while (p->leftTag == false || p->rightTag == false) { if (p->leftTag == false) { p = p->leftChild; } else if (p->rightTag == false) { p = p->rightChild; } } printf("%d ", p->data); while (p != root) { ThreadedBinaryTreeNode *q = root; while (q->leftChild != p && q->rightChild != p) { if (q->rightTag == false) { q = q->rightChild; } else { q = q->rightChild; while (q != NULL && q->leftTag == true) { q = q->leftChild; } } } p = q; printf("%d ", p->data); } } int main() { // 创建示例线索二叉树 ThreadedBinaryTreeNode *root = createNode(1); root->leftChild = createNode(2); root->rightChild = createNode(3); root->leftChild->leftChild = createNode(4); root->leftChild->rightChild = createNode(5); root->rightChild->leftChild = createNode(6); root->rightChild->rightChild = createNode(7); // 中序线索化二叉树 threadedInorder(root); // 遍历线索二叉树 printf("Inorder traversal: "); inorderTraversal(root); printf("\n"); printf("Preorder traversal: "); preorderTraversal(root); printf("\n"); printf("Postorder traversal: "); postorderTraversal(root); printf("\n"); return 0; } ``` 上述代码中,`createNode` 函数用于创建新节点;`threadedInorder` 函数用于中序线索化二叉树,其中使用全局变量 `preNode` 记录上一次遍历的节点;`inorderTraversal` 函数用于中序遍历线索二叉树;`preorderTraversal` 函数用于前序遍历线索二叉树;`postorderTraversal` 函数用于后序遍历线索二叉树。最后在 `main` 函数中创建示例线索二叉树,并测试各种遍历方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又蠢又笨的懒羊羊程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值