概述
-
一般的二叉树,有左右子节点,如下图
- 对于叶子节点,它的左右子节点为空,有的非叶子节点可能只有一个子节点,而每一个节点又会占据一个空间,这样就会节约了这些创立进去的空间
- 线索化二叉树,利用了这些空间
线索化二叉树
-
线索化二叉树
- 如果节点的左子节点为空,则指向该节点的前驱节点
- 如果节点的右子节点为空,则指向该节点的后继节点
-
前驱和后继:
- 前驱:一个节点的前一个节点
- 后继:一个节点的后一个节点
- n个节点的二叉链表中含有n+1个为空的空间
- 依据遍历秩序的不同(即线索不同),线索二叉树可分为:前序线索二叉树、中序线索二叉树和后序线索二叉树
- 举个例子,将上图的一般二叉树线索化,如下图所示:依据前序遍历的后果,16的前驱节点是20,所以16的左子节点指向了20,16的后继节点是42,所以16的右子节点指向了42,以此类推
创立
-
基本思路:
- 采纳中序线索化的办法,先线索化左子树,再线索化以后节点,最初线索化右子树
- 1)线索化左子树
- 2)解决大以后节点的前驱节点:判断以后节点的左子节点是否为空,如果为空就把以后节点的左子节点指向前一个节点
- 3)解决以后节点的后继节点:判断以后节点的前驱节点是否为空,如果前一个节点不为空并且前以一个节点的右子节点为空,则把前一个节点的右子节点指向以后节点
- 4)让前驱节点指向以后节点
- 5)线索化右子树
- 留神:每一次递归,只能判断以后节点的左子节点和前驱节点的右子节点,因为没有方法晓得以后节点的下一个节点,只能晓得上一个节点
遍历
-
基本思路:
- 先从左子树循环的找到线索化节点,而后再从找到的这个节点,循环的去找她的线索化的右子节点
- 最初让以后节点指向后继节点
- 留神:因为咱们是中序线索化二叉树,所以第一个节点肯定是一个左子树的叶节点
代码如下
数据结构:
package 树;
public class TreeNode {
private Integer val;//序号
private TreeNode left;//左孩子
private TreeNode right;//右孩子
//leftType:0示意指向的左子树,如果1示意指向前驱节点
private int leftType;
//rightType:0示意指向的是右子树,如果1示意指向后继节点
private int rightType;
public TreeNode(int val){
this.val = val;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode 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 "TreeNode{" +
"val=" + val +
'}';
}
}
具体算法:
package 树;
public class ThreadedBinaryTree {
//以后节点的上一个节点
private static TreeNode pre = null;
//根节点
private static TreeNode root;
private static final int IS_LEFT_CHILD = 0; //0示意是以后节点的左子节点指向的是左子树
private static final int IS_RIGHT_CHILD = 0; //0示意是以后节点的右子节点指向的是右子树
private static final int IS_PRE_NODE = 1; //1示意是以后节点的左子节点指向的是前驱节点
private static final int IS_AFTER_NODE = 1; //1示意是以后节点的右子节点指向的是后继节点
public static void setRoot(TreeNode root) {
ThreadedBinaryTree.root = root;
}
//线索化遍历二叉树
public static void threadedList(){
//设置以后节点
TreeNode node = root;
if (node == null) return;
while (node != null){
//找到leftType == 1的节点
while (node.getLeftType() == IS_LEFT_CHILD){
node = node.getLeft();
}
//打印以后节点
System.out.println(node.getVal());
//如果以后节点的右子节点指向的是后继节点,就统一输入
while (node.getRightType() == IS_AFTER_NODE){
node = node.getRight();
System.out.println(node.getVal());
}
//替换这个遍历的节点
node = node.getRight();
}
}
//对二叉树进行中序线索化的办法
public static void threadedNodes(TreeNode node){
if (node == null) return;
//1、先线索化左子树
threadedNodes(node.getLeft());
//2、线索化以后节点
//2.1 解决以后节点的前驱节点
if (node.getLeft() == null){
//批改以后节点的左子节点,指向前驱节点
node.setLeft(pre);
node.setLeftType(IS_PRE_NODE);
}
//2.2 解决以后节点的后继节点
if (pre != null && pre.getRight() == null){
//让前驱节点的右子节点指向以后节点
pre.setRight(node);
pre.setRightType(IS_AFTER_NODE);
}
//每次解决一个节点后,让下一个节点的前驱节点指向以后节点
pre = node;
//3、线索化右子树
threadedNodes(node.getRight());
}
public static void main(String[] args) {
TreeNode node1 = new TreeNode(1);
TreeNode node2 = new TreeNode(3);
TreeNode node3 = new TreeNode(6);
TreeNode node4 = new TreeNode(8);
TreeNode node5 = new TreeNode(10);
TreeNode node6 = new TreeNode(14);
node1.setLeft(node2);
node1.setRight(node3);
node2.setLeft(node4);
node2.setRight(node5);
node3.setLeft(node6);
setRoot(node1);
threadedNodes(node1);
// TreeNode right = node3.getRight();
// TreeNode preNode = node5.getLeft();
// TreeNode afterNode = node5.getRight();
// System.out.println(right);
// System.out.println(preNode);
// System.out.println(afterNode);
//
threadedList();
}
}
因为算法如果用字数可能不太分明,倡议还是联合代码和图片本人走一遍流程
发表回复