• 前序、中序、后序遍历的多种非递归实现


           我们先看看用前序递归如何访问整棵树:先访问根节点,然后访问左节点,再访问右节点。如果不用递归,那该怎么做呢?仔细看一下递归程序,就会发现,其实每次都是走树的左分支(left),直到左子树为空,然后开始从递归的最深处返回,然后开始恢复递归现场,访问右子树。

           其实过程很简单:一直往左走 root->left->left->left...->null,由于是前序遍历,因此一遇到节点,便需要立即访问;由于一直走到最左边后,需要逐步返回到父节点访问右节点,因此必须有一个措施能够对节点序列回溯。所以我们想到了用栈记忆的方法:在访问途中将依次遇到的节点保存下来。由于节点出现次序与恢复次序是反序的,因此是一个先进后出结构,需要用栈。使用栈记忆的实现有两个版本:第一个版本是跟踪指针移动用栈保存中间结果的实现方式,第二个版本是直接模拟递归。

    一、跟踪指针移动用栈保存中间结果

           前序遍历是在入栈前访问,中序遍历是在出栈时访问,后序遍历用这种方法不太好实现。

    节点定义:

    typedef struct BiNode{
      int data;
      struct BiNode * lchild;
      struct BiNode * rchild;
    }BiNode,*BiTree;

    前序遍历:

    void preOrderBiTree(BiNode * root){
    	if(root == NULL)
    		return;
    	BiNode * node = root;
    	stack<BiNode*> nodes;
    	while(node || !nodes.empty()){
    		while(node != NULL){
    			nodes.push(node);
    			printf("%d",node->data);
    			node = node -> lchild;
    		}
    		node = nodes.top();//回溯到父节点
    		nodes.pop();
    		node = node -> rchild;
    	}
    }

           每次都将遇到的节点压入栈,当左子树遍历完毕后才从栈中弹出最后一个访问的节点,访问其右子树。在同一层中,不可能同时有两个节点压入栈,因此栈的大小空间为O(h),h为二叉树高度。时间方面,每个节点都被压入栈一次,弹出栈一次,访问一次,复杂度为O(n)。

    中序遍历:

    void midOrderBinaryTree(BiNode * root){
    if(root == NULL)
    return;
    BiNode * node = root;
    stack<BiNode*> nodes;
    while(node || !nodes.empty()){
    while(node != NULL){
    nodes.push(node);
    node = node ->lchild;
    }
    node = nodes.top();
    printf("%d ",node ->data);
    nodes.pop();
    node = node ->rchild;
    }
    }


    二、直接用栈来模拟递归,先序非常简单,而中序与后序难度一致,并且需要一个标记位来标记该节点是否被入栈过(其左右子树是否被访问过)。中序遍历和后序遍历的区别是当节点弹出后如果其isPushed为false,前者是右子树、该节点、左子树的次序入栈,后者是该节点、右子树、左子树的次序入栈。

    前序遍历:

    void preOrderBinaryTree(BiNode * pRoot){
        if(pRoot == NULL)
            return;
    	stack<BiNode *> sData;
        sData.push(pRoot);
        BiNode * pNode = NULL;
    
        while(!sData.empty()){
            pNode = sData.top();
            printf("%d ",pNode -> data);
            sData.pop();
            
            if(pNode -> rchild != NULL){
                sData.push(pNode -> rchild);
            }
            if(pNode -> lchild != NULL){
                sData.push(pNode -> lchild);
            }
        }
    }

            每次将节点压入栈,然后弹出,压右子树,再压入左子树,在遍历过程中,遍历序列的右节点依次被存入栈,左节点逐次被访问。同一时刻,栈中元素为m-1个右节点和1个最左节点,最高为h。所以空间也为O(h);每个节点同样被压栈一次,弹栈一次,访问一次,时间复杂度O(n)


    节点定义:

    typedef struct BiNodeWithTag{
    	int data;
    	struct BiNodeWithTag * lchild;
    	struct BiNodeWithTag * rchild;
    	bool isPushed;
    }BiNodeWithTag,*BiNodeWithTagPointer;


    中序遍历:

    void midOrderBinaryTree(BiNodeWithTag * root){
    	if(root == NULL)
    		return;
    	stack<BiNodeWithTag *> nodes;
    	BiNodeWithTag * node = root;
    	nodes.push(node);
    	while(!nodes.empty()){
    		node = nodes.top();
    		nodes.pop();
    		if(node ->isPushed){
    			printf("%d ",node ->data);
    			continue;
    		}
    		if(node ->rchild != NULL)
    			nodes.push(node ->rchild);
    		nodes.push(node);
    		node ->isPushed = true;
    		if(node ->lchild != NULL)
    			nodes.push(node ->lchild);
    	}
    }

           对比先序遍历,这个算法需要额外的增加O(n)的标志位空间。另外,栈空间也扩大,因为每次压栈的时候都压入根节点与左右节点,因此栈空间为O(n)。时间复杂度方面,每个节点压栈两次,作为子节点压栈一次,作为根节点压栈一次,弹栈也是两次。

    后序遍历:

    void postOrderBinaryTree(BiNodeWithTag * root){
    	if(root == NULL)
    		return;
    	stack<BiNodeWithTag *> nodes;
    	BiNodeWithTag * node = root;
    	nodes.push(node);
    	while(!nodes.empty()){
    		node = nodes.top();
    		nodes.pop();
    		if(node ->isPushed){
    			printf("%d ",node ->data);
    			continue;
    		}
    		if(node ->rchild != NULL)
    			nodes.push(node ->rchild);
    		if(node ->lchild != NULL)
    			nodes.push(node ->lchild);
    		nodes.push(node);
    		node ->isPushed = true;
    	}
    }


    参考:

    http://blog.csdn.net/kofsky/article/details/2886453




  • 相关阅读:
    HDU1213How Many Tables并查集
    Redis新的存储模式diskstore
    GPFS文件系统笔记
    redis持久化
    360安全卫士qurl.f.360.cn分析
    [原创]解决DataSet的GetXml()方法空列不返回问题
    在多台服务器上简单实现Redis的数据主从复制
    史航416随笔
    史航416实验1总结
    史航416第二次作业总结
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3310528.html
Copyright © 2020-2023  润新知