树形结构是一种重要的非线性结构。其中,二叉树是我们学习中的重点中的重点——因为任何一颗树通过一定的变换都能变成二叉树。每颗树都只有一个根节点。n(n>=2)棵树可以形成森林。同样,森林可以转化成普通的树,而普通的树也可以转化成森林,更可以转化成二叉树。这就对我们很有利了。因为,我们只要把大部分的时间来研究二叉树就行了。
二叉树是每个节点至多有两颗子树的树(即二叉树不存在度大于2的节点)。而且,二叉树有严格的左右之分,不能随意互换。二叉树有很多的性质。比如说,在一颗树的第i层上至多有2^(i-1)个节点(根算作第0层)。深度为k的二叉树最多有2^k-1个节点(k>1)。对于任何一棵二叉树,如果其终端节点数为n0,度的节点数为n2,则n0 = n2 + 1。同时,具有n个节点的完全二叉树的深度为[logn/log2]+1个。
二叉树也有很多的存储结构,比如说顺序存储结构(仅仅使用于完全二叉树)、链式存储结构(可分为两种,一种是有存储数据域、左孩子的指针域、右孩子的指针域,所得二叉树成为二叉链表,另一种除了有存储数据域、左孩子的指针域、右孩子的指针域,还有指向双亲的指针域。
二叉树的遍历是一个重点。在Java中,我们依旧可以采用先序遍历(即先遍历根,再遍历左孩子,再右孩子)、中序遍历(即先遍历左孩子,再遍历根,最后遍历右孩子)、后序遍历(即先遍历左孩子,再遍历右孩子,最后遍历根),还有一种最容易理解的层次遍历。
实现一个遍历,既可以用递归的方法,也可以用非递归的方法。递归容易理解,而非递归则在空间利用率上有优势。
通过中序遍历和先序遍历或者是中序遍历和后序遍历,我们可以完全确定出一棵二叉树。
二叉树中有最优二叉树(赫夫曼树),带权路径长度最小的二叉树便称作最优二叉树或者赫夫曼树。赫夫曼树在计算图像压缩处理、数据压缩等方面有着很大的用途。
学好树,能够为我们提供一些新的解决问题的思路,因此十分重要。
下面提供一段对于存储在二叉树的固定表达式的求解。由于时间仓促,还没来得及写将普通的表达式中缀式转换为普通的二叉树(不含括号),我会慢慢完善的。将这个树完善为一个通用的能计算表达式的二叉树。
现行的表达式为:2+10*5-20/2
要计算这个表达式,我用了数据结构中的栈,用来储存数。
分为四个类:树的节点类、主函数类、栈接口类、栈接口实现类。
树的节点类
public class TreeNode {
//定义数据域
private Object obj;
//指向左孩子
private TreeNode lChild;
//定义右孩子
private TreeNode rChild;
//传入要构造的数据
public TreeNode(Object obj){
this.obj = obj;
}
//修改数据域
public void setObj(Object obj){
this.obj = obj;
}
//得到数据
public Object getObj(){
return obj;
}
//设置左孩子
public void setLChild(TreeNode lChild){
this.lChild = lChild;
}
//得到左孩子
public TreeNode getLChild(){
return lChild;
}
//设置右孩子
public void setRChild(TreeNode rChild){
this.rChild = rChild;
}
//得到右孩子
public TreeNode getRChild(){
return rChild;
}
}
主函数类。说实话,这里写的并不是那么好。一方面没有实现中缀表达式转化为二叉树,而采用了直接创立树的节点的方法;另一方面违背了面向对象的思想,一团乱麻。
public class BTreeTest {
private static TreeNode root;
private static Stack OPND = new Stack();
public static void main(String[] args) {
BTreeTest test = new BTreeTest();
test.buildBTree();
test.midOrder(root);
test.posOrder(root);
if (OPND.lengthGet() == 1) {
System.out.println("=" + OPND.delete());
}
}
/**
* 建立二叉树
*/
private void buildBTree() {
TreeNode node1 = new TreeNode('-');
TreeNode node2 = new TreeNode('+');
TreeNode node3 = new TreeNode('/');
TreeNode node4 = new TreeNode(2);
TreeNode node5 = new TreeNode('*');
TreeNode node6 = new TreeNode(20);
TreeNode node7 = new TreeNode(2);
TreeNode node8 = new TreeNode(10);
TreeNode node9 = new TreeNode(5);
root = node1;
node1.setLChild(node2);
node1.setRChild(node3);
node2.setLChild(node4);
node2.setRChild(node5);
node3.setLChild(node6);
node3.setRChild(node7);
node5.setLChild(node8);
node5.setRChild(node9);
}
/**
* 中序遍历二叉树,得到输入的表达式
*/
public void midOrder(TreeNode root){
//root为空,则自然地跳出这次递归;root不为空,则继续递归
if(root != null){
midOrder(root.getLChild());
System.out.print(root.getObj());
midOrder(root.getRChild());
}
}
/**
* 后序遍历二叉树,得到逆波兰式,同时用栈计算出值
* @param root根
*/
public void posOrder(TreeNode root) {
int a,b;
if (root != null) {
posOrder(root.getLChild());
posOrder(root.getRChild());
//判断是加法还是减法,还是操作数
if (root.getObj().equals('+')) {
OPND.add(OPND.delete() + OPND.delete());
} else if (root.getObj().equals('-')) {
//因为减法和除法的两个操作数有先后顺序,所以不能够将操作数按取出的顺序操作,而应该相反
a = OPND.delete();
b = OPND.delete();
OPND.add((b-a));
} else if (root.getObj().equals('*')) {
OPND.add(OPND.delete() * OPND.delete());
} else if (root.getObj().equals('/')) {
a = OPND.delete();
b = OPND.delete();
OPND.add((b/a));
} else {
// 这样能够保留Object这个通用接口,成功地将Object类中的int转换成整数
int num = Integer.parseInt(root.getObj().toString());
OPND.add(num);
}
}
}
}
栈接口
package LinkBinaryTree2;
public interface StackInterface {
//往栈中添加一个元素
void add(int e);
//删除一个元素
int delete();
//判断栈是否为空
boolean isEmpty();
//求得栈的长度
int lengthGet();
//清空栈
void initStack();
}
栈的实现:由于知识有限,没有设计成为泛型也是遗憾吧
import java.util.Arrays;
public class Stack implements StackInterface {
/**
* 往栈中添加一个元素
*/
public void add(int e) {
int[] temp = new int[++topOfStack];
temp = Arrays.copyOf(stack, topOfStack);
temp[topOfStack - 1] = e;
stack = temp;
}
/**
* 删除一个元素
*/
public int delete() {
int reElem = (int) stack[topOfStack - 1];
topOfStack--;
return reElem;
}
/**
* 判断栈是否为空
*/
public boolean isEmpty() {
if (topOfStack == 0)
return true;
else
return false;
}
/**
* 获得栈的长度
*/
public int lengthGet() {
return topOfStack;
}
/**
* 清空栈
*/
public void initStack() {
topOfStack = 0;
}
private int[] stack = new int[0];
private int topOfStack=0;
}