定义
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分 。
结论:
前序:先根,再左,再右
中序:先在,再根,再右
后序:先在,再右,再根
遍历
前序
public void preOrder() {
System.out.println(this.name);
//递归向左子树前序遍历
if (this.left != null) {
this.left.preOrder();
}
//递归向右子树前序遍历
if (this.right != null) {
this.right.preOrder();
}
}
1 2 4 5 3 6 7
中序
public void infixOrder() {
//递归向左子树中序遍历
if (this.left != null) {
this.left.infixOrder();
}
//输出父结点
System.out.println(this);
//递归向右子树中序遍历
if (this.right != null) {
this.right.infixOrder();
}
}
84251637
后序
public void postOrder() {
if (this.left != null) {
this.left.postOrder();
}
if (this.right != null) {
this.right.postOrder();
}
System.out.println(this.name);
}
8 4 5 2 6 7 3 1
小结
前序,中,后序只是过程操作有所改动,基本遍历方式一致
查找
前序查找
public void preOrder(HeroNode node) {
if (this.id == node.id){
//找到了
return
}
//递归向左子树前序遍历
if (this.left != null) {
this.left.preOrder();
}
//递归向右子树前序遍历
if (this.right != null) {
this.right.preOrder();
}
}
中,后序也是如此就不列举了
顺序存储
上图的二叉树的结点,要求以数组的方式来存放arr : [1,2,3,4,5,6,7,8]
特点
特点:
- 顺序二叉树通常只考虑完全二叉树2)
- 第n个元素的左子节点为惇2*n+13)
- 第n个元素的右子节点为2*n+24)
- 第n个元素的父节点为(n-1)/ 2
- n:表示二叉树中的第几个元素(按О开始编号如图所示)
代码实现
public static void preOrderStorage(int index, HeroNode heroNode) {
if (arr == null || arr.length == 0) {
return;
}
System.out.println(heroNode);
if (index * 2 + 1 < arr.length) {
HeroNode heroNode1 = new HeroNode(2 * index + 1,arr[2 * index + 1] + "");
heroNode.setLeft(heroNode1);
preOrderStorage(2 * index + 1,heroNode1);
}
if (index * 2 + 2 < arr.length) {
HeroNode heroNode2 = new HeroNode(2 * index + 2,arr[2 * index + 2] + "");
heroNode.setRight(heroNode2);
preOrderStorage(2 * index + 2,heroNode2);
}
}
线索化二叉树
优势
(1)利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。
(2)任意一个结点都能直接找到它的前驱和后继结点。 [2]
不足
(1)结点的插入和删除麻烦,且速度也较慢。
(2)线索子树不能共用。 [2]
我们先把分析一下二叉树,其中的 8,7,5,6,7的一部分指针没有用上
前驱后继,定义规则如下:
若结点的左子树为空,则该结点的左孩子指针指向其前驱结点。
若结点的右子树为空,则该结点的右孩子指针指向其后继结点。
- n个结点的二叉链表中含有n+1【公式2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针域,存放指向
- 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。
线索化如下
-
左子树线索化
-
对空指针线索化:
①如果p的左孩子为空,则给p加上左线索,将其leftType置为1,让p的左孩子指针指向pre(前驱);
②如果pre的右孩子为空,则给pre加上右线索,将其rightType置为1,让pre的右孩子指针指向p(后继);
-
将pre指向刚访问过的结点p,即pre = p;
public void threadedNodes(HeroNode1 node) {
//如果node==null, 不能线索化
if(node == null) {
return;
}
//1.先线索化左子树
threadedNodes(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;
//3.在线索化右子树
threadedNodes(node.getRight());
}
我们结合上面的顺序存储来测试一下
HeroNode1 leftNode = root.getLeft().getLeft().getLeft(); // 8
HeroNode1 rightNode = root.getLeft().getRight(); // 5
System.out.println(leftNode.getName() +" left = " + leftNode.getLeft());
System.out.println(rightNode.getName()+" right = " + rightNode.getRight());