二叉树前序、中序、后序遍历Java和C非递归实现

写在前面

      正巧在看《算法:C语言实现》这本书,翻到树遍历这一篇。文章中生动形象的列出了前、中、后序遍历二叉树的过程,和算法思路。恰逢我又失眠,然后想看下Java实现。发现大多人的实现并没有书中实现的那么直观,甚至有些晦涩,于是我整理了下书中提供的算法,供大家参考。

话不多说

       书中提到 ”为简单起见,我们从一个抽象栈开始考察,这个栈能够保存数据项或树,以将被遍历的树初始化。然后,我们进入一个循环,在这个循环中我们弹出并处理栈顶的元素,如此继续,直到栈空为止。如果弹出的元素是一个数据项,就访问它;如果弹出的元素是一颗树,就按照希望的顺序执行一系列的入栈操作:“     

  • 对于前序,我们压入右子树,然后左子树,最后是节点。
  • 对于中序,我们压入右子树,然后是节点,最后是左子树。
  • 对于后序,我们压入节点,然后是右子树,最后是左子树。

       下面先看下书中前序遍历的实现C :

void traverse(link h, void (*visit)(link))
{
    STACKinit(max); STACKpush(h);
    while(!STACKempty())
    {
        (*visit)(h = STACKpop());
        if(h -> r != NULL) STACKpush(h -> r)
        if(h -> r != NULL) STACKpush(h -> l);    
    }
}

       对比下其他的实现:

void TreversePreorder(struct BTree *T)
{
    struct BTree *stack[1000],*p=T;
    int top=0;
    while(p||top)
    {
        if(p)
        {
            printf("%d ",p->data);
            stack[top++]=p;
            p=p->left;
        }
        else
        {
            p=stack[--top];
            p=p->right;
        }
    }
}

这种实现节点的顺序没有直接在栈中表现,一开始就直接找到最左边节点,然后从下往上走,出栈后只找右节点。

相比之下,我还是更倾向书中的实现,更简洁,更容易理解接受。只考虑当前节点的关系。

完整实现

Java:

//  遍历接口:Traverse
public interface Traverse<E> {
    void traverse( VisitHandler<E> visit);
}
//item处理接口
public interface VisitHandler<E> {
    void visit(E item);
}
//二叉树结点
public class Node<E> {
    private E item;
    private Node left;
    private Node right;

    public Node(E item, Node left, Node right) {
        this.setItem(item);
        this.setLeft(left);
        this.setRight(right);
    }


    public E getItem() {
        return item;
    }

    public void setItem(E item) {
        this.item = item;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }
}

 

//打印操作
public class PrintVisitHandler<E> implements VisitHandler<E> {
    @Override
    public void visit(E item) {
        System.out.print(item.toString()+" ");
    }
}
//前序遍历
public class PreorderTraverse<E> implements Traverse<E> {

    private Stack<Node<E>> stack;

    public PreorderTraverse(Node<E> node) {
        stack = new Stack<>();
        stack.push(node);
    }

    /**
     * 如果弹出的元素是一个数据项,就访问它
     * 否则对于前序,我们压入右子树,然后左子树,最后是节点。
     */
    @Override
    public void traverse(VisitHandler<E> handler) {

        while (!stack.isEmpty()) {
            Node<E> node = stack.pop();
            handler.visit(node.getItem());
            if (node.getRight() != null)
                stack.push(node.getRight());
            if (node.getLeft() != null)
                stack.push(node.getLeft());
        }
    }
}

 

//中序实现
public class MidorderTraverse<E> implements Traverse<E> {
    private Stack<Object> stack;

    public MidorderTraverse(Node<E> node) {
        stack = new Stack<>();
        stack.push(node);
    }

    /**
     * 如果弹出的元素是一个数据项,就访问它
     * 否则对于中序,我们压入右子树,然后是节点,最后是左子树。
     */
    @Override
    public void traverse(VisitHandler<E> handler) {
        while (!stack.isEmpty()) {
            Object obj = stack.pop();
            if (obj instanceof Node) {
                Node node = (Node) obj;
                if (node.getRight() != null)
                    stack.push(node.getRight());
                stack.push(node.getItem());
                if (node.getLeft() != null)
                    stack.push(node.getLeft());
            } else {
                handler.visit((E)obj);
            }
        }
    }
}
//后序实现
public class PostorderTraverse<E> implements Traverse<E>  {
    private Stack<Object> stack;

    public PostorderTraverse(Node<E> node) {
        stack = new Stack<>();
        stack.push(node);
    }

    /**
     * 如果弹出的元素是一个数据项,就访问它
     * 否则对于后序,我们压入节点,然后是右子树,最后是左子树。
     */
    @Override
    public void traverse(VisitHandler<E> handler) {
        while (!stack.isEmpty()) {
            Object obj = stack.pop();
            if (obj instanceof Node) {
                Node node = (Node) obj;
                stack.push(node.getItem());
                if (node.getRight() != null)
                    stack.push(node.getRight());
                if (node.getLeft() != null)
                    stack.push(node.getLeft());
            } else {
                handler.visit((E)obj);
            }
        }
    }
}

附上于书中相同的测试用例:

public class Main {

    public static void main(String[] args) {
        Node<Character> tree = makeTree();
        new PreorderTraverse<Character>(tree).traverse(new PrintVisitHandler<Character>());
        System.out.println();
        new MidorderTraverse<Character>(tree).traverse(new PrintVisitHandler<Character>());
        System.out.println();
        new PostorderTraverse<Character>(tree).traverse(new PrintVisitHandler<Character>());
    }

    public static Node<Character> makeTree(){
        Node<Character> nodeE = new Node('E',null,null);
        Node<Character> nodeD = new Node('D',null,null);
        Node<Character> nodeH = new Node('H',null,null);
        Node<Character> nodeB = new Node('B',null,null);
        Node<Character> nodeF = new Node('F',null,null);
        Node<Character> nodeA = new Node('A',null,null);
        Node<Character> nodeC = new Node('C',null,null);
        Node<Character> nodeG = new Node('G',null,null);

        nodeE.setLeft(nodeD); nodeE.setRight(nodeH);
        nodeD.setLeft(nodeB);
        nodeH.setLeft(nodeF);
        nodeB.setLeft(nodeA);
        nodeB.setRight(nodeC);
        nodeF.setRight(nodeG);
        return nodeE;
    }
}

结果:

E D B A C H F G 
A B C D E F G H 
A C B D G F H E 

写在最后

          几年前,还在大厂的时候,遍历目录树,还是自己想了一个递归方法,来匹配层级数据。写完之后,总觉得有点怪异,

直到看过书本,才有种醍醐灌顶的感觉。还是非递归的方式,更能又清晰,又有条理的解决问题。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章