定义
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
类型
1. 满二叉树
除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。满二叉树结点数为
2
k
−
1
2^k-1
2k−1(k为深度,k>=1)。
2. 完全二叉树
若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
3. 平衡二叉树
平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
4. 二叉排序树
二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
(2)若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
相关概念
树的结点(node):包含一个数据元素及若干指向子树的分支;
孩子结点(child node):结点的子树的根称为该结点的孩子;
双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲;
兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点;
祖先结点: 从根到该结点的所经分支上的所有结点
子孙结点:以某结点为根的子树中任一结点都称为该结点的子孙
结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推;
树的深度:树中最大的结点层
结点的度:结点子树的个数
树的度: 树中最大的结点度。
叶子结点:也叫终端结点,是度为 0 的结点;
分枝结点:度不为0的结点;
有序树:子树有序的树,如:家族树;
无序树:不考虑子树的顺序;
二叉树的存储结构
顺序存储
二叉树的顺序存储结构就是使用一维数组存储二叉树中的结点,并且结点的存储位置,就是数组的下标索引。
上图所示的二叉树(D、H、I为不存在的结点)采用顺序存储方式,如下图表示:
其中,∧表示数组中此位置没有存储结点。此时可以发现,顺序存储结构中已经出现了空间浪费的情况。采用顺序存储的方式是比较浪费空间的。因此,顺序存储一般适用于完全二叉树。
链式存储
由二叉树定义可知,二叉树的每个结点最多有两个孩子。因此,可以将结点数据结构定义为一个数据和两个指针域。表示方式如下图所示:
上图所示的二叉树可以采用下图表示,下图采用一种链表结构存储二叉树,这种链表称为二叉链表。
二叉树的遍历
遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。
设L、D、R分别表示遍历左子树、访问根结点和遍历右子树, 则对一棵二叉树的遍历有三种情况:DLR(称为先序遍历),LDR(称为中序遍历),LRD (称为后序遍历)
二叉树的实现
示例中二叉树结构如下图:
- 定义
public class MyTree {
char data; // 跟节点数据
MyTree left; //左子树
MyTree right; //右子树
}
- 创建
创建上图二叉树,输入序列为:A,B,D,H,#,#,I,#,#,E,J,#,#,#,C,F,#,#,G,#,#
/**
* 先序递归创建二叉树
* @return
*/
public static MyTree createTree(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入结点数据,输入#表示空结点:");
String c = sc.next();
if(c.equals("#") ){
return null;
}else{
MyTree tree = new MyTree();
tree.data = c.charAt(0);
tree.left = createTree();
tree.right = createTree();
return tree;
}
}
- 先序遍历
上图二叉树先序遍历结果:A B D H I E J C F G
/**
* 先序递归遍历
* @param tree
*/
public static void preOrderTravers(MyTree tree){
if(tree == null)
return;
System.out.print(tree.data+" ");
preOrderTravers(tree.left);
preOrderTravers(tree.right);
}
- 中序遍历
上图二叉树中序遍历结果:H D I B J E A F C G
/**
* 中序递归遍历
* @param tree
*/
public static void inOrderTravers(MyTree tree){
if(tree == null)
return;
inOrderTravers(tree.left);
System.out.print(tree.data+" ");
inOrderTravers(tree.right);
}
- 后序遍历
上图二叉树后序遍历结果:H I D J E B F G C A
/**
* 后序递归遍历
* @param tree
*/
public static void postOrderTravers(MyTree tree){
if(tree == null)
return;
postOrderTravers(tree.left);
postOrderTravers(tree.right);
System.out.print(tree.data+" ");
}
- 层次遍历
/**
* 层次遍历(通过队列实现)
* @param tree
*/
public static void levelOrderTravers(MyTree tree){
if(tree == null)
return;
LinkedList queue = new LinkedList();
queue.offer(tree); //根结点入队
while (!queue.isEmpty()){
MyTree top = (MyTree)queue.poll(); //队头结点出队
System.out.print(top.data+" ");
if(top.left != null)
queue.offer(top.left); //左子树根结点入队
if(top.right != null)
queue.offer(top.right); //右子树根结点入队
}
}
- 求二叉树的高度
/**
* 获取树的高度
* @param node
* @return
*/
public static int heigh(MyTree node){
if(node==null){
return 0; //递归结束,空子树高度为0
}else{
//递归获取左子树高度
int l = heigh(node.left);
//递归获取右子树高度
int r = heigh(node.right);
//高度应该算更高的一边,(+1是因为要算上自身这一层)
return l>r? (l+1):(r+1);
}
}