非递归方法实现二叉树前、中、后序遍历


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


一、非递归实现前序遍历

在这里插入图片描述

1.思路

  • 前序遍历的顺序是 :根——左——右

  • 模拟递归在栈上的执行过程

  • 用栈来实现

1.用cur代替root的移动,将当前cur压栈并打印

2.cur指向当前cur的左子树,cur.left压栈并打印,直到cur的左边遇到空为止

3.先序遍历时,根节点压栈先不着急取出,栈是先进后出的,先找根的左子树,后续会出栈来利用根节点找右子树,栈维护了根节点出现的顺序

4.当cur为空时,弹出栈顶结点,栈顶结点的左边是为空的cur,要利用栈顶结点来找他的右边

5.如果此时栈顶元素的右边也为空,该节点的根左右找完了,当前结点代表着上一个根结点的左树找完了

6.上一个根节点的根、左找完了,弹出栈顶元素(上一个结点),来找他的右边

  • 也就是说,先让cur来找根并打印,一直找下去,遇到空了找右边。当最小的左子树的根左右都找齐后,这个子树和它的上一级的根已经找到,需要开始找当前根的右边,形成循环。最后这个二叉树的左子树找齐后,开始找右子树。
  • 用两个while循环,小循环的条件是cur不为空,负责进栈,大循环嵌套小循环,小循环外负责出栈
  • 大循环的条件是:出栈结点的右边不为空,或者栈不为空

2.代码

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
         if (root==null){
            return res;
        }
        TreeNode cur = root;//cur指向root
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {//cur不等于空,进栈并打印
                stack.push(cur);
               // System.out.print(cur.val + " ");
               res.add(cur.val);
                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
            cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
            //如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
        }
        return res;
    }

二、非递归实现二叉树的中序遍历

在这里插入图片描述

1.思路

1.和上面的前序遍历类似,不过中序遍历打印的顺序是: 左——根——右

2.所以在小循环中,cur找到左树的根后,进栈时不打印,等出栈时再打印

3.也就是说,找到叶子结点后,叶子结点左边为空时,弹出叶子结点并打印(左、根)

4.找叶子结点的右边,右边为空时,说明左中右已找完,

5.此时左边已打印完,cur为空,出栈并打印根节点,利用根节点找右边的结点

  • 打印的位置与前序遍历不同

2.代码

    public List<Integer> inorderTraversal(TreeNode root) {
       List<Integer> res = new ArrayList<>();
         if (root==null){
            return res;
        }
        TreeNode cur = root;//cur指向root
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {//cur不等于空,进栈并打印
                stack.push(cur);
               // System.out.print(cur.val + " ");
               
                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.pop();//cur为空了,出栈找结点的右边
            res.add(top.val);
            cur = top.right;//cur指向结点的右边,如果cur不为空,进入大循环,再次进小循环入栈
            //如果cur==null,则判断栈空不空,栈不为空,进入大循环,不进小循环,只出栈
        }
   
        
        return res;
    }

三、非递归实现二叉树的后序遍历

在这里插入图片描述

1.思路

  • 1.后序遍历的具体思路类似,但是有不同的地方

  • 2.前序遍历中,找结点的右边要出栈找,维护出栈顺序,出栈的那一刻起,左边之前已经为空或者打印了,出栈取到结点找到右边,如果有结点新的结点会进栈,如果为空说明子树应该找完了,此时跟出栈的结点无关了

  • 3.中序遍历只是打印的位置不一样,前序和中序遍历,要看结点的右边,需要出栈来查看,出栈的目的是,根和左已经找完了,只需要取出来找右边就可以了,右边找完根据栈返回上一级

    1.后续遍历的顺序: 左——右——根

    2.因为根是最后打印的,先要看左节点,所以根节点先不能出栈,用peek取到 存的根节点,找根节点的右边

    3.右边为空时,打印栈顶的结点,弹出栈顶,cur为空,进入大循环,查看栈顶的右边,不为空,改变cur的指向

    4.出栈有要求,所以会造成peek元素过后,再次找到已经打印的结点

    5.所以在打印的时候还要判断是否打印过该节点

2.代码

    public void postorderNor(TreeNode root) {
        if (root == null) {
            return;
        }
        TreeNode cur = root;//cur指向root
        TreeNode prev = null;//记录打印过的结点
        Deque<TreeNode> stack = new LinkedList<>();

        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {

                cur = cur.left;//先找左树所有的根
            }
            TreeNode top = stack.peek();//根节点不出栈,用peek取到根节点

            if (top.right==null||top.right ==prev ){//排除打印过的元素
                //左边找到了,右边为空,打印栈中的结点
                System.out.print(top.val+" ");
                stack.push(top);//打印完后弹出
                //此时cur为空
                prev = top;//记录打印过的结点

            }else {
                cur = top.right;//右边不为空,cur指向右边
            }
        }
    }

点击移步博客主页,欢迎光临~

偷cyk的图

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.先序遍历非递归算法#define maxsize 100typedef struct{ Bitree Elem[maxsize]; int top;}SqStack;void PreOrderUnrec(Bitree t){ SqStack s; StackInit(s); p=t; while (p!=null || !StackEmpty(s)) { while (p!=null) //遍历左子树 { visite(p->data); push(s,p); p=p->lchild; }//endwhile if (!StackEmpty(s)) //通过下一次循环的内嵌while实现右子树遍历 { p=pop(s); p=p->rchild; }//endif }//endwhile }//PreOrderUnrec2.序遍历非递归算法#define maxsize 100typedef struct{ Bitree Elem[maxsize]; int top;}SqStack;void InOrderUnrec(Bitree t){ SqStack s; StackInit(s); p=t; while (p!=null || !StackEmpty(s)) { while (p!=null) //遍历左子树 { push(s,p); p=p->lchild; }//endwhile if (!StackEmpty(s)) { p=pop(s); visite(p->data); //访问根结点 p=p->rchild; //通过下一次循环实现右子树遍历 }//endif }//endwhile}//InOrderUnrec3.后序遍历非递归算法#define maxsize 100typedef enum{L,R} tagtype;typedef struct { Bitree ptr; tagtype tag;}stacknode;typedef struct{ stacknode Elem[maxsize]; int top;}SqStack;void PostOrderUnrec(Bitree t){ SqStack s; stacknode x; StackInit(s); p=t; do { while (p!=null) //遍历左子树 { x.ptr = p; x.tag = L; //标记为左子树 push(s,x); p=p->lchild; } while (!StackEmpty(s) && s.Elem[s.top].tag==R) { x = pop(s); p = x.ptr; visite(p->data); //tag为R,表示右子树访问完毕,故访问根结点 } if (!StackEmpty(s)) { s.Elem[s.top].tag =R; //遍历右子树 p=s.Elem[s.top].ptr->rchild; } }while (!StackEmpty(s));}//PostOrderUnrec
二叉树的遍历方式有三种:前序遍历序遍历和后序遍历前序遍历:先遍历根节点,然后遍历左子树,最后遍历右子树。其遍历顺序为:根节点 -> 左子树 -> 右子树。 序遍历:先遍历左子树,然后遍历根节点,最后遍历右子树。其遍历顺序为:左子树 -> 根节点 -> 右子树。 后序遍历:先遍历左子树,然后遍历右子树,最后遍历根节点。其遍历顺序为:左子树 -> 右子树 -> 根节点。 下面是二叉树后序遍历的递归实现代码: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def preorderTraversal(root: TreeNode) -> List[int]: res = [] def dfs(node): if not node: return res.append(node.val) dfs(node.left) dfs(node.right) dfs(root) return res def inorderTraversal(root: TreeNode) -> List[int]: res = [] def dfs(node): if not node: return dfs(node.left) res.append(node.val) dfs(node.right) dfs(root) return res def postorderTraversal(root: TreeNode) -> List[int]: res = [] def dfs(node): if not node: return dfs(node.left) dfs(node.right) res.append(node.val) dfs(root) return res ``` 以上代码,TreeNode 表示二叉树的节点,preorderTraversal、inorderTraversal 和 postorderTraversal 分别表示前序遍历序遍历和后序遍历的函数。每个函数都是通过递归实现的,dfs 函数用来遍历节点,将节点的值加入 res

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值