先放上数据结构中有关树的一些定义:
节点:节点包括一个数据元素及若干指向其子树的分支
节点的度:节点所拥有的子树的个数成为该节点的度
叶节点:度为0的节点称为叶结点
分支节点:度不为0的节点称为分支节点
树的度:树中所有节点的度的最大值
二叉树:是n(n>=0)个有限节点构成的集合。n=0的树称为空二叉树;n=1的树只有一个根结点;
n>1的二叉树由一个根节点和至多两个互不相交的,分别称为左子树和右子树的子二叉树构成
二叉树不是有序树,这是因为,二叉树中某个节点即使只有一个子树也要区分是左子树还是右子树;
而对于有序树来说,如果某个节点只有一个子树就必定是第一个子树二叉树所有结点的形态有5种:空节点,无左右子树节点,只有左子树节点,只有右子树节点和左右子树均存在的节点
- 满二叉树:在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子节点都在同一层,则这样的二叉树称作满二叉树
- 完全二叉树:如果一颗具有n个节点的二叉树的结构与满二叉树的前n个节点的结构相同,这样的二叉树称为完全二叉树
- 二叉树的性质:
1)若规定根节点的层数为0,则一棵非空二叉树的第i层上最多有2^i(i>=0)个节点
2)若规定只有根节点的二叉树的深度为0,则深度为k的二叉树的最大节点数是2^(k+1)-1(k>=-1)
3)对于一棵非空的二叉树,如果叶节点个数为n0,度为2的节点个数为n2,则有n0=n2+1
4)具有n个节点的完全二叉树的深度k为大于或等于ln(n+1)-1的最小整数
5)对于具有n个节点的完全二叉树,如果按照从上至下和从左至右的顺序对所有节点序号从0开始顺序编号,则对于序号为i(0<=i< n)的节点有:
如果i>0,则序号为i节点的双亲节点的序号为(i-1)/2(/为整除);如果i=0,则序号为i节点为根节点,无双亲节点 如果2i+1<
n,则序号为i节点的左孩子节点的序号为2i+1;如果2i+1>=n,则序号为i节点无左孩子 如果2i+2<
n,则序号为i节点的右孩子节点的序号为2i+2;如果2i+2>=n,则序号为i节点无右孩子
11.二叉树的存储结构
1.二叉树的顺序存储结构 利用性质5,对于完全二叉树可以利用一维数组存储,如果不是完全二叉树,则可以补空节点,使成为完全二叉树在进行存储,
但是对于非完全二叉树,可能要浪费很多的空间。
2.二叉树的链式存储结构 二叉树的链式存储结构就是用指针建立二叉树中节点之间的关系,二叉树最常用的链式存储结构是二叉链。二叉树的二叉链存储结构是一种常用的
二叉树存储结构。二叉链存存储结构的优点时,结构简单,可以方便的构造任何形状的二叉树,并可以方便的实现二叉树的大多数操作。
二叉链存储结构的缺点是,查找当前节点的双亲节点操作实现比较麻烦
3.二叉树的仿真指针存储结构 利用一维数组和结构体实现,利用数组的下标进行仿真指针进行二叉树的操作
下面为具体代码实现:
package tree;
import java.util.LinkedList;
import java.util.List;
/** * * @author xmhzwy@163.com @date: 2017-3-7 * */
public class BinTreeTraverse {
private int arr[]={1,2,3,4,5,6,7};
private static List<Node> nodeList=null;
private static class Node{
Node leftchild;
Node rightchild;
int data;
Node(int newdata){
leftchild=null;
rightchild=null;
data=newdata;
}
}
public void createBintree(){//新建一个二叉树
nodeList=new LinkedList<Node>();
for (int nodIndex = 0; nodIndex < arr.length; nodIndex++) {
nodeList.add(new Node(arr[nodIndex]));
}
//以下定义主要依据上面讲解的第五条的特性实现
for (int parentIndex = 0; parentIndex < arr.length/2-1; parentIndex++) {
//左孩子
nodeList.get(parentIndex).leftchild=nodeList.get(parentIndex*2+1);
//右孩子
nodeList.get(parentIndex).rightchild=nodeList.get(parentIndex*2+2);
}
//最后一个父节点,因为最后一个父节点可能没有右孩子,所以单独拿出来处理
int lastParentIndex=arr.length/2-1;
//左孩子
nodeList.get(lastParentIndex).leftchild=nodeList.get(lastParentIndex*2+1);
if (arr.length%2==1) {
nodeList.get(lastParentIndex).rightchild=nodeList.get(lastParentIndex*2+2);
}
}
/** * 先序遍历 * * 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已 * * @param node * 遍历的节点 */
public static void preOrderTraverse(Node node) {
if (node == null)
return;
System.out.print(node.data + " ");
preOrderTraverse(node.leftchild);
preOrderTraverse(node.rightchild);
}
/** * 中序遍历 * * 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已 * * @param node * 遍历的节点 */
public static void inOrderTraverse(Node node) {
if (node == null)
return;
inOrderTraverse(node.leftchild);
System.out.print(node.data + " ");
inOrderTraverse(node.rightchild);
}
/** * 后序遍历 * * 这三种不同的遍历结构都是一样的,只是先后顺序不一样而已 * * @param node * 遍历的节点 */
public static void postOrderTraverse(Node node) {
if (node == null)
return;
postOrderTraverse(node.leftchild);
postOrderTraverse(node.rightchild);
System.out.print(node.data + " ");
}
public static void main(String[] args) {
BinTreeTraverse binTree = new BinTreeTraverse();
binTree.createBintree();
// nodeList中第0个索引处的值即为根节点
Node root = nodeList.get(0);
System.out.println("先序遍历:");
preOrderTraverse(root);
System.out.println();
System.out.println("中序遍历:");
inOrderTraverse(root);
System.out.println();
System.out.println("后序遍历:");
postOrderTraverse(root);
}
}
输出的结果为:
先序遍历:
1 2 4 5 3 6 7
中序遍历:
4 2 5 1 6 3 7
后序遍历:
4 5 2 6 7 3 1