8. 数据结构之顺序储存二叉树

数据结构之顺序储存二叉树

1. 顺序储存二叉树

1.1 顺序存储二叉树的概念
  • 从数据存储来看,数组存储方式和树的存储方式可以相互转换,即 数组可以转换成树, 树也可以转换成数组。

在这里插入图片描述

1.2 顺序存储二叉树的特点☆
  1. 顺序二叉树通常只考虑完全二叉树
  2. 第 n 个元素的左子节点为 2 * n + 1
  3. 第 n 个元素的右子节点为 2 * n + 2
  4. 第 n 个元素的父节点为 (n-1) / 2
  5. n : 表示二叉树中的第几个元素(按 0 开始编号如图所示)
1.3 顺序存储二叉树遍历
  • 要求在遍历数组 arr 时,仍然可以以前序遍历,中序遍历和后序遍历的方式完成结点的遍历
  • 给你一个数组 {1,2,3,4,5,6,7},要求以二叉树前序遍历,中序遍历和后序遍历的方式进行遍历。
    • 前序遍历的结果应当为1,2,4,5,3,6,7
    • 中序遍历的结果应当为4 2 5 1 6 3 7
    • 后序遍历的结果应当为4 5 2 6 7 3 1
1.3.1 代码实现
public class ArrBinaryTreeDemo {

	public static void main(String[] args) {
		
		//测试
		int[] arr = {1,2,3,4,5,6,7};
		
		//创建一个ArrBinaryTree
		ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
		System.out.println("顺序储存的二叉树的前序遍历:");
		arrBinaryTree.preOrder();//1 2 4 5 3 6 7 
		
		System.out.println("顺序储存的二叉树的中序遍历:");
		arrBinaryTree.infixOrder();// 4 2 5 1 6 3 7 
		
		System.out.println("顺序储存的二叉树的后序遍历:");
		arrBinaryTree.postOrder();// 4 5 2 6 7 3 1
	}

}

/**
 * 编写一个ArrBinaryTree实现顺序储存二叉树遍历
 * 
 * @author DuanChaojie
 * @date 2020年3月14日 下午1:28:28
 * @version 1.0
 */
class ArrBinaryTree {

	private int[] arr;// 储存数据节点的数组

	public ArrBinaryTree(int[] arr) {
		this.arr = arr;
	}

	//方法的重载
	public void preOrder() {
		this.preOrder(0);
	}
	
	public void infixOrder() {
		this.infixOrder(0);
	}
	
	public void postOrder() {
		this.postOrder(0);
	}
	
	
	
	/**
	 * 顺序储存数组的前序遍历
	 * @param index 数组的下标
	 */
	public void preOrder(int index) {
		// 如果数组为空,或者arr.length = 0
		if (arr == null || arr.length == 0) {
			System.out.println("数组为空,不能按照二叉树的前序遍历");
		}

		// 输出当前这个元素
		System.out.println(arr[index]);

		// 向左递归遍历
		if ((index * 2 + 1) < arr.length) {
			preOrder((index * 2 + 1));
		}

		// 向右递归遍历
		if ((index * 2 + 2) < arr.length) {
			preOrder((index * 2 + 2));
		}

	}
	
	
	/**
	 * 顺序储存数组的中序遍历
	 * @param index 数组的下标
	 */
	public void infixOrder(int index) {
		// 如果数组为空,或者arr.length = 0
		if (arr == null || arr.length == 0) {
			System.out.println("数组为空,不能按照二叉树的前序遍历");
		}

	
		// 向左递归遍历
		if ((index * 2 + 1) < arr.length) {
			infixOrder((index * 2 + 1));
		}
		
		// 输出当前这个元素
		System.out.println(arr[index]);


		// 向右递归遍历
		if ((index * 2 + 2) < arr.length) {
			infixOrder((index * 2 + 2));
		}

	}
	
	
	/**
	 * 顺序储存数组的后序遍历
	 * @param index 数组的下标
	 */
	public void postOrder(int index) {
		// 如果数组为空,或者arr.length = 0
		if (arr == null || arr.length == 0) {
			System.out.println("数组为空,不能按照二叉树的前序遍历");
		}

	
		// 向左递归遍历
		if ((index * 2 + 1) < arr.length) {
			postOrder((index * 2 + 1));
		}

		// 向右递归遍历
		if ((index * 2 + 2) < arr.length) {
			postOrder((index * 2 + 2));
		}
		
		// 输出当前这个元素
		System.out.println(arr[index]);


	}
	
	
}

2. 线索化二叉树

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

将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}

在这里插入图片描述

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

  1. left 指向的是左子树,也可能是指向的前驱节点.
    ① 节点 left 指向的左子树,
    ⑩ 节点的 left 指向的就是前驱节点
  2. right 指向的是右子树,也可能是指向后继节点。
    ① 节点 right 指向的是右子树
    ⑩ 节点的 right 指向的是后继节点
2.2 代码实现
2.2.1 HeroNode 部分
  1. 如果 leftType == 0 表示指向的是左子树,如果是1 表示指向的是前驱节点
  2. 如果 rightType == 0 表示指向是右子树, 如果 1 表示指向后继结点
/**
 *创建HeroNode节点 
 * */
class HeroNode{
	
	private int no;
	
	private String name;
	
	private HeroNode left;//默认为null
	
	private HeroNode right;//默认为null
    
	
	//1. 如果leftType == 0 表示指向的是左子树,如果是1 表示指向的是前驱节点
	//2. 如果 rightType == 0 表示指向是右子树, 如果 1 表示指向后继结点
	private int leftType;
	
	private int rightType;
    

	//构造器
	public HeroNode(int no, String name) {
		super();
		this.no = no;
		this.name = name;
	}

	//get和set方法
	//省略....
	


	//重写toString方法
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + "]";
	}
   
   //.......
	
}
2.2.2 ThreadedBinaryTree 部分

public void threadedList();遍历线索化二叉树的方法

public void threadedNode();

public void threadedNode(HeroNode node);

/**
 * 定义ThreadedBinaryTree 实现了线索化功能的二叉树
 * @author Administrator 
 * @date 2020年3月14日 下午1:21:01
 * @version 1.0
 */
class ThreadedBinaryTree{
	
	private HeroNode root;
	
	private HeroNode pre = null;
	/*
	 * 为了实现线索化,需要创建要给指向当前节点的前驱节点的指针
	 * 在递归进行线索化时,pre 总是保留前一个节点
	 * */

	
	//get和set方法
	public HeroNode getRoot() {
		return root;
	}

	public void setRoot(HeroNode root) {
		this.root = root;
	}

	public HeroNode getPre() {
		return pre;
	}

	public void setPre(HeroNode pre) {
		this.pre = pre;
	}

	
	/**
	 *遍历线索化二叉树的方法 	
	 */
	public void threadedList() {
		//定义一个变量,存储当前遍历的节点,从root开始
		HeroNode node = root;
		while(node != null) {
			//循环找到leftType == 1的节点,从root开始
			//后面随着遍历的变化,因为当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 threadedNode() {
		this.threadedNode(root);
	}

	/**
	 * 
	 * @param node 当前需要线索化的节点
	 */
	public void threadedNode(HeroNode node) {
		//如果node == null 不能线索化
		if(node == null) {
			return;
		}
		
		//先线索化左子树
		threadedNode(node.getLeft());
		
		
		//线索化当前节点
		//处理当前节点的前驱节点
		if(node.getLeft() == null) {
			//让当前节点的左指针指向前驱节点
			node.setLeft(pre);
			
			//修改当前节点的左指针的类型,指向前驱节点
			node.setLeftType(1);
		}
		
		//处理后继节点
		if(pre != null && pre.getRight() == null) {
			//让前驱节点的右指针指向当前节点
			pre.setRight(node);
			//修改前驱节点的右指针
			pre.setRightType(1);
			
		}
		
		//每处理一个节点,让当前节点是下一个节点的前驱节点
		pre = node;
		
		//线索化右子树
		threadedNode(node.getRight());
	}
	
	//.....
	
}
2.2.3 测试
public class ThreadedBinaryTreeDemo {

	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, "dim");
		
		//二叉树,后面我们要递归创建, 现在简单处理使用手动创建
		root.setLeft(node2);
		root.setRight(node3);
		node2.setLeft(node4);
		node2.setRight(node5);
		node3.setLeft(node6);
		
		//测试中序线索化
		ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
		threadedBinaryTree.setRoot(root);
		threadedBinaryTree.threadedNode();
		
		//测试: 以 10 号节点测试
		HeroNode leftNode = node5.getLeft();
		HeroNode rightNode = node5.getRight();
		System.out.println("10 号结点的前驱结点是 =" + leftNode); //3
		System.out.println("10 号结点的后继结点是=" + rightNode); //1
		//当线索化二叉树后,能在使用原来的遍历方法
		//threadedBinaryTree.infixOrder();
		System.out.println("使用线索化的方式遍历 线索化二叉树");
		threadedBinaryTree.threadedList(); // 8, 3, 10, 1, 14, 6
        
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值