二叉树的递归和非递归前、中、后序遍历

xiaoxiao2021-02-28  69

1、二叉树树的递归遍历,程序简洁,前、中、后遍历相差不大。

本例中用到的二叉树为: 前序遍历为:10 6 4 8 14 12 16 中序遍历为:4 6 8 10 12 14 16 后续遍历为:4 8 6 12 16 14 10 非递归代码:`

递归版的前序遍历:

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) { if(root==null) return ; list.add(root.val); if(root.leftchild!=null) TraverseTree(root.leftchild,list); if(root.rightchild!=null) TraverseTree(root.rightchild,list); }

递归版的中序遍历:

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) { if(root==null) return ; if(root.leftchild!=null) TraverseTree(root.leftchild,list); list.add(root.val); if(root.rightchild!=null) TraverseTree(root.rightchild,list); }

递归版的后序遍历

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) { if(root==null) return ; if(root.leftchild!=null) TraverseTree(root.leftchild,list); if(root.rightchild!=null) TraverseTree(root.rightchild,list); list.add(root.val); }

从上面的代码块可以看出,递归版的前、中、后序遍历唯一的差别处就是什么时候将根节点保存到list中;

2、非递归版的二叉树遍历 很多的递归问题都可以用循环来解决,事实上递归也是基于栈来实现的,但是利用栈实现递归的时候,就得考虑栈的劣势可能引发的问题。

这儿顺便总结一下递归用栈的缺点:

递归由于是函数调用自身,而函数调用是有时间和空间消耗的:每一次函数调用都需要在内存栈中分配空间以保存参数、临时变量及返回地址,而且往栈里亚人和弹出数据都需要时间。递归中有很多计算都是重复的,从而对性能能带来很大的负面影响。这一点在斐波那契数列的计算上体现尤其的明显。除了效率之外,递归还有可能引起更严重的问题:调用栈溢出。每一调用函数都需要在内存栈中为其分配空间,而每个进程的栈容量是有限的,当递归调用的层级太多时,就会超出栈的容量,从而导致调用栈溢出。

虽然栈的递归使用有很多的问题,但是在非递归计算过程中,栈的使用又是不可避免的,特别是在树的循环遍历中需要保存之前访问过的节点,那么这时也只能使用栈来中转啦!

非递归前序遍历代码:

public static void preOrderTraversal(BinaryTreeNode root,ArrayList<Integer> list){ if(root==null) return ; Stack<BinaryTreeNode> s=new Stack<BinaryTreeNode>(); BinaryTreeNode p=root; while(p!=null||!s.isEmpty()){ while(p!=null){ list.add(p.val); s.push(p); p=p.leftchild; } if(!s.isEmpty()){ p=s.pop(); p=p.rightchild; } } }

非递归中序遍历代码:

public static void InOrderTraversal(BinaryTreeNode root ,ArrayList<Integer> list){ Stack<BinaryTreeNode> stack=new Stack<BinaryTreeNode>(); if(root==null) return ; BinaryTreeNode p=root; while(p!=null||!stack.isEmpty()){ while(p!=null){ stack.push(p); p=p.leftchild; } if(!stack.isEmpty()){ p=stack.pop(); list.add(p.val); p=p.rightchild; } } }

非递归后续遍历代码:

public static void PostOrderTraversal(BinaryTreeNode root,ArrayList<Integer> list){ if(root==null) return ; Stack<BinaryTreeNode> stack=new Stack<BinaryTreeNode>(); Stack<Integer> s=new Stack<Integer>(); BinaryTreeNode p=root; while(p!=null||!stack.isEmpty()){ while(p!=null){ stack.push(p); s.push(p.val); p=p.rightchild; } if(!stack.isEmpty()){ p=stack.pop(); p=p.leftchild; } } while(!s.isEmpty()){ list.add(s.pop()); } }

后续遍历代码较前、中序遍历代码有较大的改动因为根节点是最后被访问的。但是通过观察后续遍历的结果:4 8 6 12 16 14 10 ,其逆过来看为:10 14 16 12 6 8 4,再看前序遍历的结果:10 6 4 8 14 12 16,二者比较发现,逆后序遍历就是在前序遍历的基础上稍作改动:先访问根节点,再访问右孩子,再访问做孩子,结果就是逆后序遍历,得到逆后序遍历,再将其反转,就可以得到后续遍历的结果。

测试用的代码如下:

class BinaryTreeNode{ int val; BinaryTreeNode leftchild=null; BinaryTreeNode rightchild=null; public BinaryTreeNode(int val){ this.val=val; } } public class TreeTraverse { public static void main(String[] args) { BinaryTreeNode root=new BinaryTreeNode(10); BinaryTreeNode node6=new BinaryTreeNode(6); BinaryTreeNode node14=new BinaryTreeNode(14); root.leftchild=node6; root.rightchild=node14; BinaryTreeNode node4=new BinaryTreeNode(4); BinaryTreeNode node8=new BinaryTreeNode(8); node6.leftchild=node4; node6.rightchild=node8; BinaryTreeNode node12=new BinaryTreeNode(12); BinaryTreeNode node16=new BinaryTreeNode(16); node14.leftchild=node12; node14.rightchild=node16; ArrayList<Integer> list= new ArrayList<Integer>(); TraverseTree(root,list); preOrderTraversal(root,list); InOrderTraversal(root,list); PostOrderTraversal(root,list); System.out.println(list); } }

前、中、后序遍历结果如下:

前序遍历结果为: [10, 6, 4, 8, 14, 12, 16] 中序遍历结果为: [4, 6, 8, 10, 12, 14, 16] 后序遍历结果为: [4, 8, 6, 12, 16, 14, 10]
转载请注明原文地址: https://www.6miu.com/read-48630.html

最新回复(0)