不使用递归前序遍历一颗二叉树

不使用递归方式遍历一棵树在工业界有经常的应用,比如在解析一个html文档的时候就是遍历一棵html树。我们以如下图的html二叉树,以前序遍历为例子进行遍历。
在这里插入图片描述
在我的以前文章中有介绍到 以二叉树的销毁的例子 去深刻理解递归算法,使用递归遍历一颗二叉树,借用到栈讲述递归的原理。这里不用递归是因为,递归压栈是整个函数的压栈,也就是把当前整个现场环境压进栈中,这样会很消耗内存空间。如果在一棵巨大的树面前,递归遍历的方式会慢到难以置信,甚至压垮计算机。

树的节点

我们以Java语言进行描述,当然怎么方便怎么来。对上图的节点用一个Node类描述,value存放结点值。

public class Node {
    
    
    //节点中保存的值
    public String value;
    //左子树
    public Node leftChild;
    //右子树
    public Node rightChild;
}
创建树

然后把上图中的树表达在代码中,我们重点是关注如何遍历树的,创建树的过程我们就硬编码,先创建叶子节点,依次从叶子节点往上创建好节点,然后再用leftChild和rightChild关系串联起来就可以了。如下的代码,最终根节点就是html节点

//创建叶子节点,倒着建立的
Node meta = new Node("meta", null, null);
Node ttile = new Node("ttile", null, null);

Node h1 = new Node("h1", null, null);
Node p = new Node("p", null, null);

Node head = new Node("head", meta, ttile);
Node body = new Node("body", h1, p);

Node html = new Node("html", head, body);
以案例来描述前序遍历

前序遍历一股脑的说来就是先遍历头节点然后再遍历左边节点; 左边遍历完成后,再遍历右边节点
我们尝试着手动遍历上面的那棵树,
如下图,我们拿到一棵树,一直访问左节点下去,遇到左节点然后遍历打印出来。比如访问到html节点先打印它的值;向下访问html左节点发现到有节点,节点为head节点。依次进行下去,访问到meta节点
在这里插入图片描述
访问到meta节点,此时到叶子节点;不能再往左子树访问了,也不能往右子树访问了,这个时候需要访问title节点了。需要访问title节点是我们人类的想法,计算机可没那么聪明,我们需要告诉它。

我们这时就需要做一个小动作了,之前的递归遍历是操作系统使用栈保存现场,等回头来再使用这个现场。我们此时也用栈,只是不用栈保存现场,用栈来保存未访问兄弟节点。

在这里插入图片描述
比如第一次访问head节点时,需要将它的兄弟节点body节点压入栈中;访问到meta节点时,其兄弟节点title节点压入栈中。这样在访问到meta这个叶子节点后,只需要访问栈顶的节点即可。根据如上的栈图,栈顶是title节点,弹出该节点,title节点就可以访问到了。

依次进行下去,我们就能访问到p节点了,最后一步弹出这个栈顶就大功告成。
在这里插入图片描述
不使用递归前序遍历的Java代码如下,关于栈的查看方式,可以通过调试时候打印出当前栈的内容,就可以直观显示出来了。

public void preOrderTraverse(Node node) {
    
    
    //新建一个栈,用于暂存未遍历的节点
    Stack<Node> stack = new Stack<>();

    Node nextNode = node;
    while (nextNode != null) {
    
    
        System.out.println(nextNode.value);
        //访问左子树,并将未访问的兄弟节点压栈
        if (nextNode.leftChild != null) {
    
    
            if (nextNode.rightChild != null) {
    
    
                stack.push(nextNode.rightChild);
            }
            nextNode = nextNode.leftChild;
            continue;
        }
        //访问右子树
        if (nextNode.rightChild != null) {
    
    
            nextNode = nextNode.rightChild;
            continue;
        }
        if (stack.isEmpty()) {
    
    
            break;
        }
        nextNode = stack.pop();
    }
}

水平有限,如果存在错误,希望不吝指出,谢谢。

Guess you like

Origin blog.csdn.net/Hello_Ray/article/details/105595433