树:T={K,R}。K是包含n个结点的有穷集合(n>0),关系R满足以下条件:
(1)有且仅有一个结点k0∈K,它对于关系R来说没有前驱结点,结点k0称作树的根。
(2)除结点k0外,K中的每个结点对于关系R来说都有且仅有一个前驱结点。
(3)K中每个结点对于关系R来说可以有多个后继结点。
我这里主要讨论的是二叉树,因为这个是用的最广泛的,二叉树也称为二次树或二分树,它是有限的结点集合,这个集合或者是空,或者由一个根结点和两棵互不相交的称为左子树和右子树的二叉树组成。
二叉树的节点定义形式如下:
class TreeNode<E>{
Comparable<E> key;//节点的值
TreeNode<E> llink;//左子树的指针
TreeNode<E> rlink;//右子树的指针
}
假如我们有这样一棵树
2
/ \
4 5
/ \/ \
7 310 8
/\ /
1 9 6
1)树的遍历:就是访问所有的节点一遍。一般来说,有四种遍历的方式,前序遍历:先访问节点本身,在访问左子树,之后再访问右子树,例如上面的树,前序遍历的次序是2 4 7 1 9 3 6 5 10 8;中序遍历,就是先访问左子树,再访问节点本身,最后访问右子树,上面的树中序遍历的次序是1 7 9 4 6 3 2 10 5 8;后序遍历,就是先访问左子树,再访问右子树,最后访问节点,上面的树后序遍历的次序是1 9 7 6 3 4 10 8 5 2;层次遍历就是按层次访问树的节点,上面的树层次遍历的属次序是2 4 5 7 3 10 8 1 9 6
具体代码实现,可以采用递归的方式实现,也可以采用非递归的方式实现。
递归方式实现(注意,我写的大体过程,具体使用时大家根据实际情况修改一下):
前序遍历:
- public static void preOrder(TreeNode root){
- if(root==null) return;
- visit(root);
- preOrder(root.llink);
- preorder(root.rlink);
- }
中序遍历:
- public static void inOrder(TreeNode root){
- if(root==null) return;
- inOrder(root.link);
- visit(root);
- inOrder(root.rlink);
- }
后序遍历:
- public static void postOrder(TreeNode root){
- if(root==null) return;
- postOrder(root.llink);
- postOrder(root.rlink);
- visit(root);
- }
层次遍历:
- public static void layerOrder(TreeNode root){
- if(root==null) return;
- ListQueue<TreeNode> q=new ListQueue<TreeNode>();
- q.insert(root);
- while(!q.empty()){
- TreeNode temp=q.remove();
- visit(temp);
- if(temp.llink!=null) q.insert(temp.llink);
- if(temp.rlink!=null) q.insert(temp.rlink);
- }
- }
我前面已经说过,使用递归只不过由系统在为我们维护一个调用栈,我们也可以具体明确的使用堆栈来非递归的实现树的遍历。
非递归实现树的遍历,我们先构建好一棵树,初始化一个栈,之后我们进入一个循环,我们不断弹出堆栈里的元素,直到堆栈为空,如果弹出的树节点表示一棵空树,我们就访问此节点,否则,我们根据下面的规则执行一系列的push操作。
对于前序:压入右子树,然后是左子树,再后是节点
对于中序:压入右子树,然后是节点,再后是左子树
对于后序:压入节点,然后是右子树,再后是左子树
当我们把一个节点的左右节点及本节点都入栈后,我们的节点本身就可以看成一棵空树了。为了标识这一特性,我们修改树节点为:
class TreeNode<E>{
Comparable<E> key;//节点的值
TreeNode<E> llink;//左子树的指针
TreeNode<E> rlink;//右子树的指针
boolean flag;//是否是空树
}
前序遍历的代码:
- public static void preOrder(TreeNode<E> node){
- if(node==null)return;
- ListStack<TreeNode<E>> stack=new ListStack<TreeNode<E>>();
- stack.push(node);
- while(!stack.empty()){
- TreeNode<E> t=stack.pop();
- if(t.flag){
- System.out.print(t.item+",");
- }else{
- if(t.rlink!=null)
- stack.push(t.rlink);
- if(t.llink!=null)
- stack.push(t.llink);
- stack.push(t);
- t.flag=true;
- }
- }
- }
中序遍历的代码:
- public static void preOrder(TreeNode<E> node){
- if(node==null)return;
- ListStack<TreeNode<E>> stack=new ListStack<TreeNode<E>>();
- stack.push(node);
- while(!stack.empty()){
- TreeNode<E> t=stack.pop();
- if(t.flag){
- System.out.print(t.item+",");
- }else{
- if(t.rlink!=null)
- stack.push(t.rlink);
- stack.push(t);
- if(t.llink!=null)
- stack.push(t.llink);
- t.flag=true;
- }
- }
- }
后序遍历的代码:
- public static void preOrder(TreeNode<E> node){
- if(node==null)return;
- ListStack<TreeNode<E>> stack=new ListStack<TreeNode<E>>();
- stack.push(node);
- while(!stack.empty()){
- TreeNode<E> t=stack.pop();
- if(t.flag){
- System.out.print(t.item+",");
- }else{
- stack.push(t);
- if(t.rlink!=null)
- stack.push(t.rlink);
- if(t.llink!=null)
- stack.push(t.llink);
- t.flag=true;
- }
- }
- }