IDC

Java编程内功-数据结构与算法「线索化二叉树」

作者:admin 2021-04-20 我要评论

先看一个问题 将数列{1,3,6,8,10,14}构建成一颗二叉树.n+1=7,如下图所示 问题分析: 当我们对上面的二叉树进行中序遍历时,数列为{8,3,10,1,14,6} 但是6,8,10...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

先看一个问题

将数列{1,3,6,8,10,14}构建成一颗二叉树.n+1=7,如下图所示


问题分析:

  1. 当我们对上面的二叉树进行中序遍历时,数列为{8,3,10,1,14,6}
  2. 但是6,8,10,14这几个节点的左右指针,并没有完全的利用上。
  3. 如果我们希望充分的利用各个节点的左右指针,让各个节点指向自己的前后节点怎么办?
  4. 解决方案-线索二叉树

线索二叉树基本介绍

  1. n 个节点的二叉链表中含有n+1【公式 2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针域,存放该节点在某种遍历次序下的前驱和后继点的指针(这种附加的指针称为线索)。
  2. 这种加了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索的性质不同,线索二叉树可分为前序线索二叉树、中序线索二叉树、后序线索二叉树三种。
  3. 一个节点的前一个节点,称为前驱节点。
  4. 一个节点的后一个节点,称为后继节点。

中序线索二叉树图解


中序遍历的结果{8,3,10,1,14,6}

说明:当线索化二叉树后,Node节点的属性left和right,有如下情况:

  1. left指向的值左子树,也可能是指向的前驱节点,比如①节点left指向的左子树,而⑩节点的left指向的就是前驱节点。
  2. right指向的右子树,也可能是指向后继节点,比如①节点right指向的是右子树,而⑩节点的right指向的是后继节点。

代码案例

  1. package com.xie.tree.threadedbinarytree; 
  2.  
  3. public class ThreadedBinaryTreeDemo { 
  4.     public static void main(String[] args) { 
  5.         //测试中序线索二叉树 
  6.         HeroNode root = new HeroNode(1, "tom"); 
  7.         HeroNode node2 = new HeroNode(3, "jack"); 
  8.         HeroNode node3 = new HeroNode(6, "smith"); 
  9.         HeroNode node4 = new HeroNode(8, "mary"); 
  10.         HeroNode node5 = new HeroNode(10, "king"); 
  11.         HeroNode node6 = new HeroNode(14, "dim"); 
  12.  
  13.         root.setLeft(node2); 
  14.         root.setRight(node3); 
  15.  
  16.         node2.setLeft(node4); 
  17.         node2.setRight(node5); 
  18.  
  19.         node3.setLeft(node6); 
  20.  
  21.         ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree(); 
  22.         threadedBinaryTree.setRoot(root); 
  23.         threadedBinaryTree.threadedNodes(); 
  24.  
  25.         //测试:以10号节点测试 
  26.         HeroNode left = node5.getLeft(); 
  27.         System.out.println("10号节点的前驱节点是:" + left); 
  28.         HeroNode right = node5.getRight(); 
  29.         System.out.println("10号节点的后继节点是:" + right); 
  30.  
  31.         System.out.println("使用线索化的方式遍历 线索二叉树"); 
  32.         threadedBinaryTree.threadedBinaryTreeList(); 
  33.  
  34.         /** 
  35.          * 10号节点的前驱节点是:HeroNode{no=3, name=jack} 
  36.          * 10号节点的后继节点是:HeroNode{no=1, name=tom} 
  37.          * 使用线索化的方式遍历 线索二叉树 
  38.          * HeroNode{no=8, name=mary} 
  39.          * HeroNode{no=3, name=jack} 
  40.          * HeroNode{no=10, name=king} 
  41.          * HeroNode{no=1, name=tom} 
  42.          * HeroNode{no=14, name=dim} 
  43.          * HeroNode{no=6, name=smith} 
  44.          */ 
  45.  
  46.     } 
  47.  
  48. //实现了线索化功能的二叉树 
  49. class ThreadedBinaryTree { 
  50.     private HeroNode root; 
  51.     //为了实现线索化,需要创建一个指向当前节点的前驱节点的指针 
  52.     //在递归进行线索化时,pre总是保留前一个节点 
  53.     private HeroNode pre; 
  54.  
  55.     public void setRoot(HeroNode root) { 
  56.         this.root = root; 
  57.     } 
  58.  
  59.     /** 
  60.      * 遍历线索化二叉树的方法。 
  61.      */ 
  62.     public void threadedBinaryTreeList() { 
  63.         //定义一个变量,存储当前遍历的节点,从root开始 
  64.         HeroNode node = root; 
  65.         while (node != null) { 
  66.             //循环找到leftType==1的节点,第一个找到就是8节点 
  67.             //后面随着遍历而变化,因为当leftType==1时,说明该节点是按照线索化处理后的有效节点 
  68.             while (node.getLeftType() == 0) { 
  69.                 node = node.getLeft(); 
  70.             } 
  71.             //打印当前节点 
  72.             System.out.println(node); 
  73.             //如果当前节点的右指针指向的是后继节点,就一直输出 
  74.             while (node.getRightType() == 1) { 
  75.                 //获取到当前节点的后继节点 
  76.                 node = node.getRight(); 
  77.                 System.out.println(node); 
  78.             } 
  79.             //替换这个遍历的节点 
  80.             node = node.getRight(); 
  81.         } 
  82.     } 
  83.  
  84.     /** 
  85.      * 重载threadedNodes方法 
  86.      */ 
  87.     public void threadedNodes() { 
  88.         threadedNodes(root); 
  89.     } 
  90.  
  91.     /** 
  92.      * 编写对二叉树进行线索化的方法 
  93.      * 
  94.      * @param node 当前需要线索化的节点 
  95.      */ 
  96.     public void threadedNodes(HeroNode node) { 
  97.         if (node == null) { 
  98.             return
  99.         } 
  100.  
  101.         //先线索化左子树 
  102.         threadedNodes(node.getLeft()); 
  103.         //线索化当前节点【有难度】 
  104.  
  105.         //处理当前节点的前驱节点 
  106.         //以8节点来理解,8节点.left=null 
  107.         if (node.getLeft() == null) { 
  108.             //让当前节点的左指针指向前驱节点 
  109.             node.setLeft(pre); 
  110.             //修改当前节点的左指针类型 
  111.             node.setLeftType(1); 
  112.         } 
  113.  
  114.         //处理后继节点 
  115.         if (pre != null && pre.getRight() == null) { 
  116.             //让前驱节点的右指针指向当前节点 
  117.             pre.setRight(node); 
  118.             //修改前驱节点的右指针类型 
  119.             pre.setRightType(1); 
  120.         } 
  121.         //每处理一个节点后,让当前节点是下一个节点的前驱节点 
  122.         pre = node; 
  123.  
  124.         //再线索化右子树 
  125.         threadedNodes(node.getRight()); 
  126.     } 
  127.  
  128.  
  129. //创建HeroNode节点 
  130. class HeroNode { 
  131.     static int preCount = 0; 
  132.     static int infoxCount = 0; 
  133.     static int postCount = 0; 
  134.  
  135.     private int no
  136.     private String name
  137.     private HeroNode left
  138.     private HeroNode right
  139.  
  140.     //0 表示指向的是左子树,1 表示指向的是前驱节点 
  141.     private int leftType; 
  142.     //0 表示指向右子树,1 表示指向的是后继节点 
  143.     private int rightType; 
  144.  
  145.     public HeroNode(int no, String name) { 
  146.         this.no = no
  147.         this.name = name
  148.     } 
  149.  
  150.     public int getNo() { 
  151.         return no
  152.     } 
  153.  
  154.     public void setNo(int no) { 
  155.         this.no = no
  156.     } 
  157.  
  158.     public String getName() { 
  159.         return name
  160.     } 
  161.  
  162.     public void setName(String name) { 
  163.         this.name = name
  164.     } 
  165.  
  166.     public HeroNode getLeft() { 
  167.         return left
  168.     } 
  169.  
  170.     public void setLeft(HeroNode left) { 
  171.         this.left = left
  172.     } 
  173.  
  174.     public HeroNode getRight() { 
  175.         return right
  176.     } 
  177.  
  178.     public void setRight(HeroNode right) { 
  179.         this.right = right
  180.     } 
  181.  
  182.     public int getLeftType() { 
  183.         return leftType; 
  184.     } 
  185.  
  186.     public void setLeftType(int leftType) { 
  187.         this.leftType = leftType; 
  188.     } 
  189.  
  190.     public int getRightType() { 
  191.         return rightType; 
  192.     } 
  193.  
  194.     public void setRightType(int rightType) { 
  195.         this.rightType = rightType; 
  196.     } 
  197.  
  198.     @Override 
  199.     public String toString() { 
  200.         return "HeroNode{" + 
  201.                 "no=" + no + 
  202.                 ", name=" + name + 
  203.                 '}'
  204.     } 
  205.  
  206.     //前序遍历 
  207.     public void preOrder() { 
  208.         System.out.println(this); 
  209.         //递归向左子树前序遍历 
  210.         if (this.left != null) { 
  211.             this.left.preOrder(); 
  212.         } 
  213.  
  214.         //递归向右子树前序遍历 
  215.         if (this.right != null) { 
  216.             this.right.preOrder(); 
  217.         } 
  218.     } 
  219.  
  220.     //中序遍历 
  221.     public void infixOrder() { 
  222.         //递归向左子树中序遍历 
  223.         if (this.left != null) { 
  224.             this.left.infixOrder(); 
  225.         } 
  226.         System.out.println(this); 
  227.         //递归向右子树中序遍历 
  228.         if (this.right != null) { 
  229.             this.right.infixOrder(); 
  230.         } 
  231.     } 
  232.  
  233.     //后序遍历 
  234.     public void postOrder() { 
  235.         //递归向左子树后序遍历 
  236.         if (this.left != null) { 
  237.             this.left.postOrder(); 
  238.         } 
  239.         //递归向右子树后序遍历 
  240.         if (this.right != null) { 
  241.             this.right.postOrder(); 
  242.         } 
  243.         System.out.println(this); 
  244.     } 
  245.  
  246.     //递归删除节点 
  247.     //1.如果删除的节点是叶子节点,则删除该节点。 
  248.     //2.如果删除的节点是非叶子节点,则删除该树。 
  249.     public void delNo(int no) { 
  250.         /** 
  251.          * 1.因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否是需要删除的节点,而不能去判断当前节点是否是需要删除的节点。 
  252.          * 2.如果当前节点的左子节点不为空,并且左子节点就是需要删除的节点,就将this.left = null;并且返回(结束递归)。 
  253.          * 3.如果当前节点的右子节点不为空,并且右子节点就是需要删除的节点,将将this.right = null;并且返回(结束递归)。 
  254.          * 4.如果第2步和第3步没有删除节点,那么就要向左子树进行递归删除。 
  255.          * 5.如果第4步也没有删除节点,则应当向右子树进行递归删除。 
  256.          */ 
  257.         if (this.left != null && this.left.no == no) { 
  258.             this.left = null
  259.             return
  260.         } 
  261.  
  262.         if (this.right != null && this.right.no == no) { 
  263.             this.right = null
  264.             return
  265.         } 
  266.  
  267.         if (this.left != null) { 
  268.             this.left.delNo(no); 
  269.         } 
  270.  
  271.         if (this.right != null) { 
  272.             this.right.delNo(no); 
  273.         } 
  274.  
  275.     } 
  276.  
  277.     //前序遍历查找 
  278.     public HeroNode preOrderSearch(int no) { 
  279.  
  280.         HeroNode res = null
  281.  
  282.         preCount++;//这里必须放在this.no == no 判断之前,才进行实际的比较 
  283.         //若果找到,就返回 
  284.         if (this.no == no) { 
  285.             return this; 
  286.         } 
  287.         //没有找到,向左子树递归进行前序查找 
  288.         if (this.left != null) { 
  289.             res = this.left.preOrderSearch(no); 
  290.         } 
  291.         //如果res != null 就直接返回 
  292.         if (res != null) { 
  293.             return res; 
  294.         } 
  295.         //如果左子树没有找打,向右子树进行前序查找 
  296.         if (this.right != null) { 
  297.             res = this.right.preOrderSearch(no); 
  298.         } 
  299.         //如果找到就返回 
  300.         if (res != null) { 
  301.             return res; 
  302.         } 
  303.         return res; 
  304.     } 
  305.  
  306.     //中序遍历查找 
  307.     public HeroNode infixOrderSearch(int no) { 
  308.  
  309.         HeroNode res = null
  310.         if (this.left != null) { 
  311.             res = this.left.infixOrderSearch(no); 
  312.         } 
  313.         if (res != null) { 
  314.             return res; 
  315.         } 
  316.         infoxCount++;//这里必须放在this.no == no 判断之前,才进行实际的比较 
  317.         if (this.no == no) { 
  318.             return this; 
  319.         } 
  320.         if (this.right != null) { 
  321.             res = this.right.infixOrderSearch(no); 
  322.         } 
  323.         if (res != null) { 
  324.             return res; 
  325.         } 
  326.         return res; 
  327.     } 
  328.  
  329.     //后序遍历查找 
  330.     public HeroNode postOrderSearch(int no) { 
  331.  
  332.         HeroNode res = null
  333.         if (this.left != null) { 
  334.             res = this.left.postOrderSearch(no); 
  335.         } 
  336.         if (res != null) { 
  337.             return res; 
  338.         } 
  339.  
  340.         if (this.right != null) { 
  341.             res = this.right.postOrderSearch(no); 
  342.         } 
  343.         if (res != null) { 
  344.             return res; 
  345.         } 
  346.         postCount++;//这里必须放在this.no == no 判断之前,才进行实际的比较 
  347.         if (this.no == no) { 
  348.             return this; 
  349.         } 
  350.         return res; 
  351.     } 

本文转载自网络,原文链接:https://www.toutiao.com/i6935054324175307268/

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • Java编程内功-数据结构与算法「线索化

    Java编程内功-数据结构与算法「线索化

  • 太棒了!Python和Excel过了这么久终于

    太棒了!Python和Excel过了这么久终于

  • HarmonyOSAPP组件分享(三)

    HarmonyOSAPP组件分享(三)

  • Java基础之System类和Static方法

    Java基础之System类和Static方法

腾讯云代理商
海外云服务器