二叉树也是一棵树,先讨论下什么是树,树就是一种植物,一种有根,有枝叶的植物。单独看树的每一个分枝,分枝也可以看成一棵树。分枝上还有小分枝,小分枝也可以看成一棵树。到了树的最末端,就没有分支了,就只剩叶子了。
用术语描述,树就是一些节点的集合。这个集合有一个根节点,然后有子节点,子节点又有子节点,哪一个子节点没有子节点那就是叶子节点了。大概就是长酱紫:
0号就是根节点,3号、4号、5号就是叶子节点,1号、2号就是子节点。路径就是一个节点到另一个节点的序列,这样的序列只有一个。路径的长就是序列所包含的边数。任意节点的深度就是从该节点到根节点的路径的长。任意节点的高度就是从该节点到叶子节点的最长路径。一棵树的深度就是最深叶子节点的深度,显然这个深度等于树的高度。
显然根0号的深度是0而高度是2,1号的深度是1高度也是1,深度就是向根的方向,高度就是向叶子的方向。
树最典型的应用就是文件目录结构,一个目录可以包含多个子目录,子目录又可以包含子目录或文件。而对一个文件目录的遍历,实操就是对一颗树的遍历,常见的遍历操作就是递归。
public void listFile(File file) { if(file.isDirectory()) { for(File temp:file.listFiles()) { if(temp.isDirectory()) { listFile(temp.getAbsolutePath()); } System.out.println(temp.getAbsolutePath()); } }}
算法的核心就是递归方法listFile。递归方法深度一旦过大,程序执行时会爆出栈溢出异常,相信递归计算过阶乘、斐波那契数列等的童鞋都遇到过。
二叉树是在树的概念上延伸来的,其中每个节点都不能有多两个的子节点。二叉树的一个重要性质就是树的深度远远小于节点的个数,这个性质非常重要。这使得在一些平衡二叉树中,节点检索的次数大大降低。数据库索引B树也是利用这个性质,当然为了维持这个性质,避免二叉树出现一边倒的情形,还有许多延伸的带平衡条件的树。
先来定义一颗二叉树,一个包含左子树,右子树,包含自身value的类,左右子节点也是一颗树的类:
class TreeNode {
int value;
TreeNode left;
TreeNode right;
TreeNode(int value) {
this.value = value;
}
}
按照序号0~5构造一颗树:
TreeNode[] tree = new TreeNode[6]; for (int i = 0; i < 6; i++) { tree[i] = new TreeNode(i); } for (int i = 0; i < 6; i++) { if (i * 2 + 1 < 6) { tree[i].left = tree[i * 2 + 1]; } if (i * 2 + 2 < 6) { tree[i].right = tree[i * 2 + 2]; } }
遍历一下这棵树,遍历树和遍历文件目录是一样样的。由于节点包含两个子节点,所以按照根节点的遍历顺序可以分为三种,先根遍历,中根遍历,后根遍历。
简单看下先根遍历,先根遍历就是先把根结点遍历出来,然后一次把左子树和右子树遍历出来,左右子树同样作为一颗二叉树进行遍历。
public void preOrderRe(TreeNode rootTree) { System.out.print(rootTree.value); TreeNode left = rootTree.left; if (left != null) { preOrderRe(left); } TreeNode right = rootTree.right; if (right != null) { preOrderRe(right); } }
关于后根和中根类似先跟遍历,这里说一下先根遍历的路子,如图1,先根遍历就是0,1,2;因为1号非叶子节点,需要递归遍历,所以1的遍历是1,3,4;同理2的遍历是2,5;整体遍历结果就是:0,1(3,4),2(5)。