顺序存储二叉树&线索化二叉树
文章目录
- 顺序存储二叉树&线索化二叉树
- 顺序存储二叉树
- 介绍
- 代码实现
- 线索化二叉树
- 介绍
- 代码实现
顺序存储二叉树
介绍
背景:从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组,看下面的示意图。
特点:
1.顺序二叉树通常只考虑完全二叉树
2.第n个元素的左子节点为 2 * n + 1
3.第n个元素的右子节点为 2 * n + 2
4.第n个元素的父节点为 (n-1) / 2
5.n : 表示二叉树中的第几个元素(按0开始编号如图所示)
代码实现
package com.datestructures.tree;
public class ArrBinaryTree {
//数组存储二叉树
private int[] arr;
public ArrBinaryTree(int[] arr) {
this.arr = arr;
}
public void preOrder() {
this.preOrder(0);
}
/**
* 用来以树的形式遍历数组 前序遍历
*
* @param index
*/
private void preOrder(int index) {
//先判断是否为空
if (arr == null || arr.length == 0) {
System.out.println("数组为空,不能遍历顺序二叉树");
return;
}
System.out.print(arr[index]+" ");
if ((2 * index + 1) < arr.length) {//向左递归
preOrder(2 * index + 1);
}
if ((2 * index + 2) < arr.length) {//向右递归
preOrder(2 * index + 2);
}
}
public void infixOrder() {
this.infixOrder(0);
}
/**
* 用来以树的形式遍历数组 中序遍历
*
* @param index
*/
private void infixOrder(int index) {
//先判断是否为空
if (arr == null || arr.length == 0) {
System.out.println("数组为空,不能遍历顺序二叉树");
return;
}
if ((2 * index + 1) < arr.length) {//向左递归
infixOrder(2 * index + 1);
}
System.out.print(arr[index]+" ");
if ((2 * index + 2) < arr.length) {//向右递归
infixOrder(2 * index + 2);
}
}
public void postOrder() {
this.postOrder(0);
}
/**
* 用来以树的形式遍历数组 后序遍历
* @param index
*/
private void postOrder(int index){
//先判断是否为空
if (arr == null || arr.length == 0) {
System.out.println("数组为空,不能遍历顺序二叉树");
return;
}
if ((2 * index + 1) < arr.length) {//向左递归
postOrder(2 * index + 1);
}
if ((2 * index + 2) < arr.length) {//向右递归
postOrder(2 * index + 2);
}
System.out.print(arr[index]+" ");
}
}
测试类
package com.datestructures.tree;
public class ArrBinaryTreeDemo {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7};
ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
System.out.println("前序遍历");
arrBinaryTree.preOrder();
System.out.println();
System.out.println("中序遍历");
arrBinaryTree.infixOrder();
System.out.println();
System.out.println("后序遍历");
arrBinaryTree.postOrder();
}
}
线索化二叉树
介绍
概念:遍历二叉树是以一定规则将二叉树中的结点排列成一个线性序列,得到二叉树中结点的先序序列、中序序列或后序序列。这实质上是对一个非线性结构进行线性化操作。使每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。
介绍:
1.n个结点的二叉链表中含有n+1(公式 2n-(n-1)=n+1)个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")
2.这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
3.一个结点的前一个结点,称为前驱结点
4.一个结点的后一个结点,称为后继结点
应用案例说明:将下面的二叉树,进行中序线索二叉树。中序遍历的数列为 {8, 3, 10, 1, 14, 6}
思路分析: 中序遍历的结果:{8, 3, 10, 1, 14, 6}
说明:
当线索化二叉树后,Node节点的 属性 left 和 right ,有如下情况:
1.left 指向的是左子树,也可能是指向的前驱节点. 比如 ① 节点 left 指向的左子树, 而 ⑩ 节点的 left 指向的就是前驱节点.
2.right指向的是右子树,也可能是指向后继节点,比如 ① 节点right 指向的是右子树,而⑩ 节点的right 指向的是后继节点.
代码实现
package com.datestructures.tree;
public class ThreadedBinaryTree {
private HeroNode root;
//为了实现线索化 需要创建要给指向当前节点的前驱节点的指针
//在递归线索化时 pre总是保留前一个节点
public HeroNode pre = null;//中序线索化前驱节点指针
public HeroNode pre1 = null;//前序索引化前驱节点指针
public void setRoot(HeroNode root) {
this.root = root;
}
public void infixthreadedNodes() {
infixthreadedNodes(root);
}
public void prethreadedNodes() {
prethreadedNodes(root);
}
public void postthreadedNodes() {
postthreadedNodes(root);
}
/**
* 编写对二叉树进行前序线索化的方法
*
* @param node 就是当前需要线索化的节点
*/
public void prethreadedNodes(HeroNode node) {
//如果node==null 不能线索化
if (node == null) {
return;
}
//(1)先线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱节点
node.setLeft(pre1);
//修改当前节点的左指针类型
node.setLeftType(1);
}
//处理前驱节点的后继节点
if (pre1 != null && pre1.getRight() == null) {
//让前驱节点的右指针指向当前节点
pre1.setRight(node);
//修改前驱节点的右指针类型
pre1.setRightType(1);
}
//!!! 每处理一个节点后让当前节点是下一个节点的前驱
pre1 = node;
//(2)线索化左子树
if (node.getLeftType() == 0) {
prethreadedNodes(node.getLeft());
}
//(3)线索化右子树
if (node.getRightType() == 0) {
prethreadedNodes(node.getRight());
}
}
/**
* 遍历前序线索化二叉树
*/
public void prethreadedList() {
//定义一个变量 存储当前遍历节点 从root开始
HeroNode node = root;//1 3 8 10 6 14
while (node != null) {
//先输出当前节点
System.out.println(node);
if (node.getLeftType() == 0) {
node = node.getLeft();
} else {
node = node.getRight();
}
}
}
/**
* 编写对二叉树进行中序线索化的方法
*
* @param node 就是当前需要线索化的节点
*/
public void infixthreadedNodes(HeroNode node) {
//如果node==null 不能线索化
if (node == null) {
return;
}
//(1)先线索化左子树
infixthreadedNodes(node.getLeft());
//(2)线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱节点
node.setLeft(pre);
//修改当前节点的左指针类型
node.setLeftType(1);
}
//处理前驱节点的后继节点
if (pre != null && pre.getRight() == null) {
//让前驱节点的右指针指向当前节点
pre.setRight(node);
//修改前驱节点的右指针类型
pre.setRightType(1);
}
//!!! 每处理一个节点后让当前节点是下一个节点的前驱
pre = node;
//(3)线索化右子树
infixthreadedNodes(node.getRight());
}
/**
* 遍历中序线索化二叉树
*/
public void infixthreadedList() {
//定义一个变量 存储当前遍历节点 从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 postthreadedNodes(HeroNode node) {
//如果node==null 不能线索化
if (node == null) {
return;
}
//(1)线索化左子树
prethreadedNodes(node.getLeft());
//(2)线索化右子树
prethreadedNodes(node.getRight());
//(3)先线索化当前节点
if (node.getLeft() == null) {
//让当前节点的左指针指向前驱节点
node.setLeft(pre1);
//修改当前节点的左指针类型
node.setLeftType(1);
}
//处理前驱节点的后继节点
if (pre1 != null && pre1.getRight() == null) {
//让前驱节点的右指针指向当前节点
pre1.setRight(node);
//修改前驱节点的右指针类型
pre1.setRightType(1);
}
//!!! 每处理一个节点后让当前节点是下一个节点的前驱
pre1 = node;
}
/**
* 遍历后序线索化二叉树
*/
public void postthreadedList() {
HeroNode node = root;
while (node != null) {
while (node.getLeftType() == 0) {
node = node.getLeft();
}
while (node != null && node.getRightType() == 1) {
System.out.println(node);
pre = node;
node = node.getRight();
}
//若node结点为根节点,则遍历完成
if (node == root) {
System.out.println(node);
return;
}
//若node.getRight() == pre则说明以node为根结点的子树遍历完成,应遍历node兄弟结点。
// 先获取node结点的父节点,再获取node结点的兄弟结点。
//若node结点无兄弟结点,则继续向上寻找。
while (node != null && node.getRight() == pre) {
System.out.println(node);
pre = node;
//node = node.getParent();
}
if (node != null && node.getRightType() == 0) {
node = node.getRight();
}
}
}
}
节点类
package com.datestructures.tree;
public class HeroNode {
//节点类
private int no;
private String name;
private HeroNode left;//默认为空
private HeroNode right;//默认为空
//以下属性为线索化二叉树要用的属性
private int leftType;// 0 表示是节点类型 1 表示是线索
private int rightType;// 0 表示是节点类型 1 表示是线索
public int getLeftType() {
return leftType;
}
public int getRightType() {
return rightType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
//构造器
public HeroNode(int no, String name) {
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;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
//前序遍历
public void preOrder() {
System.out.println(this);//先输出父节点
//向左递归
if (this.left != null) {
this.left.preOrder();
}
//向右递归
if (this.right != null) {
this.right.preOrder();
}
}
//前序遍历查找
public HeroNode preOrderSearch(int no) {
System.out.println("进入前序遍历查找");
//先判断当前节点是否为目标值
if (this.no == no) {
return this;
}
//定义一个res来接收结果
HeroNode resNode = null;
//如果不是向左递归
if (this.left != null) {
resNode = this.left.preOrderSearch(no);
}
//如果找到就返回
if (resNode != null) {
return resNode;
}
//没有找到就向右递归
if (this.right != null) {
resNode = this.right.preOrderSearch(no);
}
//最后有没有找到都返回
return resNode;
}
//中序遍历
public void infixOrder() {
//向左递归
if (this.left != null) {
this.left.infixOrder();
}
//输出当前节点
System.out.println(this);
//向右递归
if (this.right != null) {
this.right.infixOrder();
}
}
//中序遍历查找
public HeroNode infixOrderSearch(int no) {
//定义一个结果来存放结果
HeroNode resNode = null;
//先判断左子节点是否为空
if (this.left != null) {
resNode = this.left.infixOrderSearch(no);
}
//如果resNode不为空,表明找到 返回即可
if (resNode != null) {
return resNode;
}
System.out.println("进入中序遍历查找");
//为空 就和当前节点比较
if (this.no == no) {
return this;
}
//当前节点不是目标值 则向右查找
if (this.right != null) {
resNode = this.right.infixOrderSearch(no);
}
//最后返回
return resNode;
}
//后序遍历
public void postOrder() {
//向左递归
if (this.left != null) {
this.left.postOrder();
}
//向右递归
if (this.right != null) {
this.right.postOrder();
}
//输出当前节点
System.out.println(this);
}
//后序遍历查找
public HeroNode postOrderSearch(int no) {
//先定义一个结果
HeroNode resNode = null;
//向左递归
if (this.left != null) {
resNode = this.left.postOrderSearch(no);
}
//如果不为空 说明找到 返回即可
if (resNode != null) {
return resNode;
}
//否则向右递归
if (this.right != null) {
resNode = this.right.postOrderSearch(no);
}
//如果不为空 说明找到 返回即可
if (resNode != null) {
return resNode;
}
System.out.println("进入后序遍历查找");
//否则和当前节点对比
if (this.no == no) {
return this;
}
return resNode;
}
//删除节点
public void delNode(int no){
//因为二叉树是单向的 所以先判断当前节点的子节点是否是要删除的节点,而不是先判断当前节点
//先判断当前节点的子节点是否为要删除的节点
if(this.left!=null&&this.left.no==no){
this.left=null;
return;
}
if(this.right!=null&&this.right.no==no){
this.right=null;
return;
}
//向左递归 进行删除
if(this.left!=null){
this.left.delNode(no);
}
//向右递归 进行删除
if(this.right!=null){
this.right.delNode(no);
}
}
}
测试类
package com.datestructures.tree;
public class ThreadedBinaryTreeDemo {
public static void main(String[] args) {
ThreadedBinaryTree threadedBinaryTree =new ThreadedBinaryTree();
//再创建四个节点
HeroNode root = new HeroNode(1,"tom");
HeroNode hero2 = new HeroNode(3,"jack");
HeroNode hero3 = new HeroNode(6,"smith");
HeroNode hero4 = new HeroNode(8,"mary");
HeroNode hero5 = new HeroNode(10,"king");
HeroNode her06 = new HeroNode(14,"dim");
//建立联系
root.setLeft(hero2);
root.setRight(hero3);
hero2.setLeft(hero4);
hero2.setRight(hero5);
hero3.setLeft(her06);
threadedBinaryTree.setRoot(root);
//中序线索化二叉树
/*threadedBinaryTree.infixthreadedNodes();
System.out.println("中序线索化");
System.out.println(hero5.getLeft());
System.out.println(hero5.getRight());*/
//遍历中序线索化二叉树
/*System.out.println("中序遍历");
threadedBinaryTree.infixthreadedList();*/
//前序线索化二叉树
/*threadedBinaryTree.prethreadedNodes();
System.out.println("前序线索化");
System.out.println(hero5.getLeft());
System.out.println(hero5.getRight());
System.out.println("前序遍历");
threadedBinaryTree.prethreadedList();*/
//后序线索化二叉树
threadedBinaryTree.postthreadedNodes();
System.out.println(hero5.getLeft());
System.out.println(hero5.getRight());
}
}