【数算-21】线索二叉树
发布日期:2021-05-07 08:58:01 浏览次数:25 分类:精选文章

本文共 11863 字,大约阅读时间需要 39 分钟。

文章目录

1、线索二叉树的引入

在这里插入图片描述

简而言之就是:因为叶子结点或仅一个子结点存在时,二叉树的左右指针会有资源浪费的现象,因此引入线索化二叉树,重新利用起无指向的指针情况

2、线索二叉树的概念

在这里插入图片描述

3、线索二叉树的应用与图解

  • 问题描述
    在这里插入图片描述
  • 由于线索二叉树是通过无指向的指针来指向前驱结点与后继结点的,因此线索二叉树一定与某种特定的遍历方式有关。所以,这里以中序遍历线索二叉树为例,对线索二叉树进行图解分析
    在这里插入图片描述
    由上图的二叉树可以得出其中序遍历序列:8-3-10-1-14-6
    由线索二叉树的概念可知,将无指向的左指针指向遍历序列中的前驱结点,将无指向的右指针指向遍历序列中的后继结点
    对上图进行修改,可以得到下面的二叉树结构
    在这里插入图片描述
  • 具体说明
    在这里插入图片描述

4、线索二叉树的代码实现

由于在未经线索化的二叉树结构中,结点的左右指针分别指向其左右子树;而在线索化二叉树结构中,左右指针分别指向其前驱结点和后继结点

因此需要对其左右指针所指向的内容需要进行区分
这里通过为左右指针新增对应的type属性来判断该指针指向的到底代表哪种含义

Node.class

package threaded_bin_tree;/** * @author zhihua.li * @date 2021/2/15 - 23:46 **/public class Node {       private Integer no;    private String name;    private Node left;    private Node right;//    若 leftType/rightType 为 0则表示为指向其左/右子树,为1表示指向其前驱/后继结点    private int leftType;    private int rightType;    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;    }    public Node(Integer no, String name) {           this.no = no;        this.name = name;    }    //    前序遍历: 根 左 右    public void preOrderIterate() {   //        不用判断该节点是否为空        System.out.println(this);        if (this.left != null) {               this.left.preOrderIterate();        }        if (this.right != null) {               this.right.preOrderIterate();        }    }    //    中序遍历:左 根 右    public void midOrderIterate() {           if (this.left != null) {               this.left.midOrderIterate();        }        System.out.println(this);        if (this.right != null) {               this.right.midOrderIterate();        }    }    //    后序遍历:左 右 根    public void postIterate() {           if (this.left != null) {               this.left.postIterate();        }        if (this.right != null) {               this.right.postIterate();        }        System.out.println(this);    }    @Override    public String toString() {           return "binary_tree.Node{" +                "no=" + no +                ", name='" + name + '\'' +                '}';    }    public Integer getNo() {           return no;    }    public void setNo(Integer no) {           this.no = no;    }    public String getName() {           return name;    }    public void setName(String name) {           this.name = name;    }    public Node getLeft() {           return left;    }    public void setLeft(Node left) {           this.left = left;    }    public Node getRight() {           return right;    }    public void setRight(Node right) {           this.right = right;    }}

ThreadedBinaryTree.class

package threaded_bin_tree;/** * @author zhihua.li * @date 2021/2/16 - 19:41 **//** * @program: DataStructure * @description: 线索二叉树代码实现 * @author: zhihua li * @create: 2021-02-16 19:41 */public class ThreadedBinaryTree {       private Node root;    //    设置前驱结点,初始化为null    private Node pre = null;    public ThreadedBinaryTree(Node root) {           this.root = root;    }    public Node getPre() {           return pre;    }    public void setPre(Node pre) {           this.pre = pre;    }    //    先序线索化二叉树    public void preThreadedNodes(Node node) {   //        若传入结点为空,则直接返回        if (node == null) {               return;        }//        对当前节点进行线索化://        如果当前节点的左子节点为空,则设置当前结点的左子节点为pre,并且设置type为1        if (node.getLeft() == null) {               node.setLeft(pre);            node.setLeftType(1);        }//        若保存的pre结点不为空且该节点的右子节点为空,说明可以保存其后继结点到right中        if (pre != null && pre.getRight() == null) {   //            给pre结点(上一次递归的结点,也为本次执行的前驱结点)设置后继结点为node            pre.setRight(node);            pre.setRightType(1);        }//        让pre后移为当前节点        pre = node;//        对其左子节点进行递归线索化.不加判断的话会造成死循环//        比如 3-8-3-8....        if(node.getLeftType()==0) {               preThreadedNodes(node.getLeft());        }//        对其右子节点进行递归线索化        if(node.getRightType()==0) {               preThreadedNodes(node.getRight());        }    }    //中序线索化二叉树    public void midThreadedNodes(Node node) {   //        当前递归的终止条件        if (node == null) {               return;        }//        线索化左子树        midThreadedNodes(node.getLeft());//        线索化当前结点//        1.处理当前结点的前驱结点:        if (node.getLeft() == null) {   //            让当前节点的左指针指向前驱结点            node.setLeft(pre);//            修改左指针类型            node.setLeftType(1);        }//        2.处理当前结点的后继结点        if (pre != null && pre.getRight() == null) {               pre.setRight(node);            pre.setRightType(1);        }//        将当前结点设置为前驱结点以便下次递归设置,pre也是动态的        pre = node;//        线索化右子树        midThreadedNodes(node.getRight());    }    //后序线索化二叉树    public void postThreadedNodes(Node node) {           if (node == null) {               return;        }//        线索化当前结点的左子结点        postThreadedNodes(node.getLeft());//        线索化当前结点的右子结点        postThreadedNodes(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也是动态的        pre = node;    }    public void preThreadedList(){           if(root!=null){               root.preOrderIterate();        }        System.out.println("当前根节点为空");    }    public void midThreadedList(){           if(root!=null){               root.midOrderIterate();        }        System.out.println("当前根节点为空");    }    public void postThreadedList(){           if(root!=null){               root.postIterate();        }        System.out.println("当前根节点为空");    }}

注意:先序遍历线索化二叉树时,需要对当前结点的左右结点进行判断,看其是否为前驱/后继结点,如果当前结点的左指针已经修改为其前驱结点,就不对其左子树进行递归操作了,同理,右子树也一样

5、线索二叉树的测试

设计二叉树结构为下图,分别对三种遍历方式的线索化进行测试:

在这里插入图片描述

1、先序线索化

ThreadedBinaryTreeTest

package threaded_bin_tree;/** * @author zhihua.li * @date 2021/2/16 - 21:16 **//** *@program: DataStructure *@description: 测试线索二叉树 *@author: zhihua li *@create: 2021-02-16 21:16 */public class ThreadedBinaryTreeTest {       public static void main(String[] args) {           Node root = new Node(1,"tom");        Node node2 = new Node(3,"jack");        Node node3 = new Node(6,"smith");        Node node4 = new Node(8,"mary");        Node node5 = new Node(10,"king");        Node node6 = new Node(14,"dim");        root.setLeft(node2);        root.setRight(node3);        node2.setLeft(node4);        node2.setRight(node5);        node3.setLeft(node6);//        测试线索化,以node5为例,若正常线索化,则包含前驱结点和后继结点//		  由下图中的先序遍历分析可知//        node5(no=10)的前驱结点为8,后继结点为6        ThreadedBinaryTree tree = new ThreadedBinaryTree(root);        tree.preThreadedNodes(root);                System.out.println(node5.getLeft());        System.out.println(node5.getRight());    }}

测试结果:

在这里插入图片描述

2、中序线索化

package threaded_bin_tree;/** * @author zhihua.li * @date 2021/2/16 - 21:16 **//** *@program: DataStructure *@description: 测试线索二叉树 *@author: zhihua li *@create: 2021-02-16 21:16 */public class ThreadedBinaryTreeTest {       public static void main(String[] args) {           Node root = new Node(1,"tom");        Node node2 = new Node(3,"jack");        Node node3 = new Node(6,"smith");        Node node4 = new Node(8,"mary");        Node node5 = new Node(10,"king");        Node node6 = new Node(14,"dim");        root.setLeft(node2);        root.setRight(node3);        node2.setLeft(node4);        node2.setRight(node5);        node3.setLeft(node6);//        测试线索化,以node5为例,若正常线索化,则包含前驱结点和后继结点//		  由下图中的先序遍历分析可知//        node5(no=10)的前驱结点为3,后继结点为1        ThreadedBinaryTree tree = new ThreadedBinaryTree(root);        tree.midThreadedNodes(root);                System.out.println(node5.getLeft());        System.out.println(node5.getRight());    }}

测试结果:

在这里插入图片描述

3、后序线索化

package threaded_bin_tree;/** * @author zhihua.li * @date 2021/2/16 - 21:16 **//** *@program: DataStructure *@description: 测试线索二叉树 *@author: zhihua li *@create: 2021-02-16 21:16 */public class ThreadedBinaryTreeTest {       public static void main(String[] args) {           Node root = new Node(1,"tom");        Node node2 = new Node(3,"jack");        Node node3 = new Node(6,"smith");        Node node4 = new Node(8,"mary");        Node node5 = new Node(10,"king");        Node node6 = new Node(14,"dim");        root.setLeft(node2);        root.setRight(node3);        node2.setLeft(node4);        node2.setRight(node5);        node3.setLeft(node6);//        测试线索化,以node5为例,若正常线索化,则包含前驱结点和后继结点//		  由下图中的先序遍历分析可知//        node5(no=10)的前驱结点为8,后继结点为3        ThreadedBinaryTree tree = new ThreadedBinaryTree(root);        tree.postThreadedNodes(root);                System.out.println(node5.getLeft());        System.out.println(node5.getRight());    }}

测试结果:

在这里插入图片描述

6、线索二叉树的遍历

1、问题描述

在这里插入图片描述

注意点: 遍历线索化二叉树的前提是与线索化的顺序相同,比如中序遍历线索化二叉树时,对象必须是经过中序线索化后的二叉树

2、代码实现

public void midThreadedList() {           //        定义变量接收当前结点的root结点        Node node = root;        while (node != null) {   //            循环的找到leftType为1的点,当leftType为1时,说明该节点已经历过线索化处理            while (node.getLeftType() == 0) {                   node = node.getLeft();            }            System.out.println(node);//            循环找到rightType为1的点并打印            while (node.getRightType() == 1) {                   node = node.getRight();                System.out.println(node);            }            node = node.getRight();        }    }

3、代码测试

public class ThreadedBinaryTreeTest {       public static void main(String[] args) {           Node root = new Node(1,"tom");        Node node2 = new Node(3,"jack");        Node node3 = new Node(6,"smith");        Node node4 = new Node(8,"mary");        Node node5 = new Node(10,"king");        Node node6 = new Node(14,"dim");        root.setLeft(node2);        root.setRight(node3);        node2.setLeft(node4);        node2.setRight(node5);        node3.setLeft(node6);		ThreadedBinaryTree tree = new ThreadedBinaryTree(root);        tree.midThreadedNodes(root);        // 遍历顺序与线索化顺序必须一致        tree.midThreadedList();

在这里插入图片描述

4、扩展

1、先序遍历线索化二叉树

前序遍历代码如下:

//    遍历线索化二叉树(先序)的方法:    public void preThreadedList() {           Node node = root;        while (node != null) {   //            先序遍历线索二叉树//            先打印当前二叉树的根节点            System.out.println(node);            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();        }    }

在这里插入图片描述

2、后序遍历线索化二叉树【推导较难】

后序遍历遇到的问题:

在本例中,由所给二叉树后序遍历序列为:8-10-3-14-6-1
由后序线索化可得下图中的线索二叉树
在这里插入图片描述

在遍历的过程中会遇到这样的情况:

  • 1-3-8-10的过程省略。。。

    当遍历到10结点时,其后继结点为3,而3的右指针指向的仅仅为右子树而并不代表后继结点,所以在这里会造成一直访问3-8-10-3…而无法直接跳转到右子树中的14结点

  • 因此我想到了需要对左右子树分别遍历,左子树中断后跳转到右子树对右子树进行遍历,最后遍历根结点

  • 其次对于一直3-8-10-3-8-…的循环,为了打破这个死循环,我能想到的只有为遍历过的结点添加一个标志,来避免一直循环该结点

具体代码实现

//    遍历线索化二叉树(后序)的方法:    public void postThreadedList() {           Node node = root;        //为了避免死循环,为Node.class新增hasList属性        while(node.getLeftType()==0&&node.hasList == false){               node.hasList = true;            node = node.getLeft();        }        System.out.println(node);        while (node.getRightType()==1) {               node = node.getRight();            System.out.println(node);        }        //左子树遍历结束,开始遍历右子树        node = root.getRight();        //对右子树进行相同遍历操作        while(node.getLeftType()==0&&node.hasList == false){               node.hasList = true;            node = node.getLeft();        }        System.out.println(node);        while (node.getRightType()==1) {               node = node.getRight();            //注意这里不需要对根结点再次输出,因为6结点的后缀结点就为1结点,因此直接循环即可打印出root结点            System.out.println(node);        }        }

在这里插入图片描述

上一篇:IDEA中的鼠标光标变为黑色粗线
下一篇:JSP基础语法:脚本元素、指令元素、动作元素、注释

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2025年04月07日 11时37分02秒