线索二叉树

0.遗留问题(只实现中序,没有实现前序和后序)

1.为什么需要线索二叉树

1660391306918

答:

  1. 如上图所示,有些结点的左右指针并没有完全得到利用
  2. 为了使所有的指针都得到利用,线索二叉树是有效的解决办法。

2.线索二叉树基本介绍

  1. n个结点的二叉链表中含有n+1个空指针{公式2n-(n-1)=n+1;2n表示总指针域数,n-1表示已经被占用的指针数},利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针
  2. 增加的指针称为线索,这种增加线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树、后序线索二叉树。
  3. 一个结点的前一个结点称为前驱结点
  4. 一个结点的后一个结点称为后继结点

3.线索化后的说明

线索化之后,如何判断指针是左指针、右指针还是前驱指针、后继指针呢?

解决办法:

  1. 当线索化之后,Node结点的属性left和right有一些说明;
  2. left指向的是左子树,也可能指向前驱结点,
  3. right指向的是右子树,也可能是后继结点,
  4. 增加标志位,在代码中体现在属性上:leftType==0 表示指向的是左子树,leftType==1表示指向的是前驱结点
  5. rightType==0表示指向的是右子树,rightType==1表示指向的是后继结点

4.代码实现

public class ThreadBinaryTree {

	public static void main(String[] args) {
		//测试中序线索二叉树的功能
		HeroNode root = new HeroNode(1,"tom");
		HeroNode node2 = new HeroNode(3,"jack");
		HeroNode node3= new HeroNode(6,"smith");
		HeroNode node4 = new HeroNode(8,"mary");
		HeroNode node5 = new HeroNode(10,"king");
		HeroNode node6 = new HeroNode(14,"jim");
		
		//手动创建二叉树
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(node6);
		
		//测试中序线索化
		TreadBinayTree treadBinayTree = new TreadBinayTree();
		treadBinayTree.setRoot(root);
		treadBinayTree.infixTreadNodes();

		
		// 测试:以no==10为测试结点
		HeroNode leftNode = node5.getLeft();
		HeroNode rightNode = node5.getRight();
		System.out.println("no=10结点的前驱结点是:" + leftNode);
		System.out.println("no=10结点的后继结点是:" + rightNode);
		
		System.out.println("使用线索化的方式遍历 中序线索遍历");
		treadBinayTree.threadList();

	}

}

//第二:定义TreadBinaryTree实现线索功能的二叉树
class TreadBinayTree{
	private HeroNode root;//定义根结点
	
	// 为了实现线索化,需要创建指向当前结点的前驱结点的指针
	// 在递归进行线索化时,pre总是保留前一个结点
	private HeroNode pre = null;//不可以少

	public HeroNode getRoot() {
		return root;
	}

	public void setRoot(HeroNode root) {
		this.root = root;
	}
	
	//编写中序线索化二叉树的重载方法
	public void infixTreadNodes(){
		this.infixTreadNodes(root);
	}
	
	// 遍历线索化二叉树的方法
	public void threadList(){
		// 定义一个变量,存储当前遍历的结点,从root开始
		HeroNode 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();
			
		}
	}
	
	
	
	// 编写对二叉树进行中序线索化的方法
	/**
	 * 
	 * @param node 表示当前需要线索化的结点
	 */
	public void infixTreadNodes(HeroNode node){
		// 如果node == null 不能线索化
		if (node == null) {
			return;
		}
		
		// 线索化左子树
		infixTreadNodes(node.getLeft());
		
		//线索化当前结点(难点)
		//处理当前结点的前驱结点
		if (node.getLeft() == null) { //说明当前结点的左指针没有被利用起来,于是将它之前前驱结点
			// 让当前结点的左指针指向前驱结点
			node.setLeft(pre);
			// 修改当前结点的左指针的类型,表示指向前驱结点,而不是左子树
			node.setLeftType(1);
		}
		
		//处理当前结点的后继结点(难点)
		// 注意:pre!=null 没搞懂为什么?
		//node.getRight() == null 写法错误
		if (pre!=null && pre.getRight() == null) {
			// 让当前结点的右指针指向后继结点
			// 写成node.setRight(pre); 出现栈溢出
			pre.setRight(node);
			// 修改当前结点的右指针的类型,表示指向后继结点,而不是右子树
			//node.setRightType(1);
			pre.setRightType(1);
		}
		// 易错点:每次处理完一个结点后,让当前结点是下一个结点的前驱结点
		pre = node;
		
		// 再线索化右子树
		infixTreadNodes(node.getRight());
		
	}
	
}


// 第一:先创建HeroNode结点
class HeroNode{
	private int no;
	private String name;
	private HeroNode left;
	private HeroNode right;
	// 说明
	// leftType==0 表示指向的是左子树,leftType==1表示指向的是前驱结点
	// rightType==0表示指向的是右子树,rightType==1表示指向的是后继结点
	private int leftType;
	private int rightType;
	
	public HeroNode(int no, String name) {
		super();
		this.no = no;
		this.name = name;
	}

	public int getNo() {
		return no;
	}

	public void setNo(int no) {
		this.no = no;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public HeroNode getLeft() {
		return left;
	}

	public void setLeft(HeroNode left) {
		this.left = left;
	}

	public HeroNode getRight() {
		return right;
	}

	public void setRight(HeroNode 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 "HeroNode [no=" + no + ", name=" + name + "]";
	}
}

5.测试结果(中序)截图

1660398051359

posted @ 2022-08-13 21:56  半路_出家ren  阅读(305)  评论(0编辑  收藏  举报
返回顶端