二叉树的遍历

来源:互联网 发布:观星台软件 编辑:程序博客网 时间:2024/06/06 13:16

二叉树的遍历 递归非递归 思路和 java实现


二叉树遍历首先弄清遍历流程。

中序遍历:第一次经过从它找左,第二次经过找右,第三次经过回来。第二次经过访问它。
算法实现:


第二次访问是 pop(&S,&p);Visit(p->data);因为中序遍历跟第三次经过没关系。所以第二次就弹出栈了。
Java版的程序如下:

[java] view plain copy
  1. package com.tree;  
  2. import java.util.Stack;  
  3. public class BinaryTree {  
  4.     protected Node root;    
  5.         
  6.     public BinaryTree(Node root) {    
  7.         this.root = root;    
  8.     }    
  9.     
  10.     public Node getRoot() {    
  11.         return root;    
  12.     }    
  13.     
  14.     /** 构造树 */    
  15.     public static Node init() {    
  16.         Node a = new Node('A');    
  17.         Node b = new Node('B'null, a);    
  18.         Node c = new Node('C');    
  19.         Node d = new Node('D', b, c);    
  20.         Node e = new Node('E');    
  21.         Node f = new Node('F', e, null);    
  22.         Node g = new Node('G'null, f);    
  23.         Node h = new Node('H', d, g);    
  24.         return h;// root    
  25.     }    
  26.     
  27.     /** 访问节点 */    
  28.     public static void visit(Node p) {    
  29.         System.out.print(p.getKey() + " ");    
  30.     }    
  31.     
  32.     /** 递归实现前序遍历 */    
  33.     protected static void preorder(Node p) {    
  34.         if (p != null) {    
  35.             visit(p);    
  36.             preorder(p.getLeft());    
  37.             preorder(p.getRight());    
  38.         }    
  39.     }    
  40.     
  41.     /** 递归实现中序遍历 */    
  42.     protected static void inorder(Node p) {    
  43.         if (p != null) {    
  44.             inorder(p.getLeft());    
  45.             visit(p);    
  46.             inorder(p.getRight());    
  47.         }    
  48.     }    
  49.     
  50.     /** 递归实现后序遍历 */    
  51.     protected static void postorder(Node p) {    
  52.         if (p != null) {    
  53.             postorder(p.getLeft());    
  54.             postorder(p.getRight());    
  55.             visit(p);    
  56.         }    
  57.     }    
  58.   /**********************************************************************************************/  
  59.     /** 非递归实现前序遍历 */    
  60.     protected static void iterativePreorder(Node p) {    
  61.         Stack<Node> stack = new Stack<Node>();    
  62.         if (p != null) {    
  63.             stack.push(p);    
  64.             while (!stack.empty()) {    
  65.                 p = stack.pop();    
  66.                 visit(p);    
  67.                 if (p.getRight() != null)    
  68.                     stack.push(p.getRight());    
  69.                 if (p.getLeft() != null)  //为什么p.getLeft() 在后,getRight()在前应为while 循环第一句就是pop visit所以要把left放上,先访问。之中方法是即压即访问法。  
  70.                     stack.push(p.getLeft());    
  71.             }    
  72.         }    
  73.     }    
  74.       
  75.     /** 非递归实现中序遍历 */  //思路与上面iterativePreorder 一致。  
  76.     protected static void iterativeInorder(Node p) {    
  77.         Stack<Node> stack = new Stack<Node>();    
  78.         while (p != null) {    
  79.             while (p != null) {    
  80.                 if (p.getRight() != null)    
  81.                     stack.push(p.getRight());// 当前节点右子入栈    
  82.                     stack.push(p);// 当前节点入栈    
  83.                     p = p.getLeft();    
  84.             }    
  85.             p = stack.pop();    
  86.             while (!stack.empty() && p.getRight() == null) {    
  87.                 visit(p);    
  88.                 p = stack.pop();    
  89.             }    
  90.             visit(p);    
  91.             if (!stack.empty())    
  92.                 p = stack.pop();    
  93.             else    
  94.                 p = null;    
  95.         }    
  96.     }  
  97.   
  98. /*******************************************************************************************/  
  99.       
  100. /*******************************************************************************************/    
  101.     /** 非递归实现前序遍历2 */    
  102.     protected static void iterativePreorder2(Node p) {    
  103.         Stack<Node> stack = new Stack<Node>();    
  104.         Node node = p;    
  105.         while (node != null || stack.size() > 0) {    
  106.             while (node != null) {//压入所有的左节点,压入前访问它。左节点压入完后pop访问右节点。像这样算法时思考规律性的东西在哪。不管哪个节点都要压所节点判断右节点。    
  107.                 visit(node);    
  108.                 stack.push(node);    
  109.                 node = node.getLeft();    
  110.             }    
  111.             if (stack.size() > 0) {//    
  112.                 node = stack.pop();    
  113.                 node = node.getRight();    
  114.             }    
  115.         }    
  116.     }    
  117.       
  118.     /** 非递归实现中序遍历2 */    
  119.     protected static void iterativeInorder2(Node p) {    
  120.         Stack<Node> stack = new Stack<Node>();    
  121.         Node node = p;    
  122.         while (node != null || stack.size() > 0) {    
  123.             while (node != null) {    
  124.                 stack.push(node);    
  125.                 node = node.getLeft();    
  126.             }    
  127.             if (stack.size() > 0) {    
  128.                 node = stack.pop();    
  129.                 visit(node);   //与iterativePreorder2比较只有这句话的位置不一样,弹出时再访问。  
  130.                 node = node.getRight();    
  131.             }    
  132.         }    
  133.     }  
  134.       
  135.  /*******************************************************************************************/  
  136.     
  137.     /** 非递归实现后序遍历 */    
  138.     protected static void iterativePostorder(Node p) {    
  139.         Node q = p;    
  140.         Stack<Node> stack = new Stack<Node>();    
  141.         while (p != null) {    
  142.             // 左子树入栈    
  143.             for (; p.getLeft() != null; p = p.getLeft())    
  144.                 stack.push(p);    
  145.             // 当前节点无右子或右子已经输出    
  146.             while (p != null && (p.getRight() == null || p.getRight() == q)) {    
  147.                 visit(p);    
  148.                 q = p;// 记录上一个已输出节点    
  149.                 if (stack.empty())    
  150.                     return;    
  151.                 p = stack.pop();    
  152.             }    
  153.             // 处理右子    
  154.             stack.push(p);    
  155.             p = p.getRight();    
  156.         }    
  157.     }    
  158.     
  159.     /** 非递归实现后序遍历 双栈法 */    
  160.     protected static void iterativePostorder2(Node p) {//理解左子树   右子树 根递归性质,把它运用到循环当中去。    
  161.         Stack<Node> lstack = new Stack<Node>();//左子树栈    
  162.         Stack<Node> rstack = new Stack<Node>();//右子树栈  
  163.         Node node = p, right;    
  164.         do {    
  165.             while (node != null) {    
  166.                 right = node.getRight();    
  167.                 lstack.push(node);    
  168.                 rstack.push(right);    
  169.                 node = node.getLeft();    
  170.             }    
  171.             node = lstack.pop();    
  172.             right = rstack.pop();    
  173.             if (right == null) {    
  174.                 visit(node);    
  175.             } else {    
  176.                 lstack.push(node);    
  177.                 rstack.push(null);    
  178.             }    
  179.             node = right;    
  180.         } while (lstack.size() > 0 || rstack.size() > 0);    
  181.     }    
  182.     
  183.     /** 非递归实现后序遍历 单栈法*/    
  184.     protected static void iterativePostorder3(Node p) {    
  185.         Stack<Node> stack = new Stack<Node>();    
  186.         Node node = p, prev = p;    
  187.         while (node != null || stack.size() > 0) {    
  188.             while (node != null) {    
  189.                 stack.push(node);    
  190.                 node = node.getLeft();    
  191.             }    
  192.             if (stack.size() > 0) {    
  193.                 Node temp = stack.peek().getRight();    
  194.                 if (temp == null || temp == prev) {    
  195.                     node = stack.pop();    
  196.                     visit(node);    
  197.                     prev = node;    
  198.                     node = null;    
  199.                 } else {    
  200.                     node = temp;    
  201.                 }    
  202.             }    
  203.     
  204.         }    
  205.     }    
  206.     
  207.     /** 非递归实现后序遍历4 双栈法*/    
  208.     protected static void iterativePostorder4(Node p) {    
  209.         Stack<Node> stack = new Stack<Node>();    
  210.         Stack<Node> temp = new Stack<Node>();    
  211.         Node node = p;    
  212.         while (node != null || stack.size() > 0) {    
  213.             while (node != null) {    
  214.                 temp.push(node);    
  215.                 stack.push(node);    
  216.                 node = node.getRight();    
  217.             }    
  218.             if (stack.size() > 0) {    
  219.                 node = stack.pop();    
  220.                 node = node.getLeft();    
  221.             }    
  222.         }    
  223.         while (temp.size() > 0) {//把插入序列都插入到了temp。  
  224.             node = temp.pop();    
  225.             visit(node);    
  226.         }    
  227.     }    
  228.     
  229.     /**  
  230.      * @param args  
  231.      */    
  232.     public static void main(String[] args) {    
  233.         BinaryTree tree = new BinaryTree(init());   
  234.         System.out.print(" 递归遍历 \n");    
  235.         System.out.print(" Pre-Order:");    
  236.         preorder(tree.getRoot());    
  237.            
  238.         System.out.print(" \n In-Order:");    
  239.         inorder(tree.getRoot());  
  240.           
  241.         System.out.print("\n Post-Order:");    
  242.         postorder(tree.getRoot());    
  243.           
  244.         System.out.print(" \n非递归遍历");  
  245.         System.out.print(" \n Pre-Order:");    
  246.         iterativePreorder(tree.getRoot());    
  247.           
  248.         System.out.print("\n Pre-Order2:");    
  249.         iterativePreorder2(tree.getRoot());    
  250.            
  251.         System.out.print(" \n In-Order:");    
  252.         iterativeInorder(tree.getRoot());  
  253.           
  254.         System.out.print("\n In-Order2:");    
  255.         iterativeInorder2(tree.getRoot());    
  256.           
  257.         System.out.print("\n Post-Order:");    
  258.         iterativePostorder(tree.getRoot());    
  259.          
  260.         System.out.print("\n Post-Order2:");    
  261.         iterativePostorder2(tree.getRoot());    
  262.            
  263.         System.out.print("\n Post-Order3:");    
  264.         iterativePostorder3(tree.getRoot());    
  265.            
  266.         System.out.print("\n Post-Order4:");    
  267.         iterativePostorder4(tree.getRoot());    
  268.         
  269.     
  270.     }    
  271.   
  272. }  
  273.    
  274.   
  275. class Node {    
  276.     private char key;    
  277.     private Node left, right;    
  278.     
  279.     public Node(char key) {    
  280.         this(key, nullnull);    
  281.     }    
  282.     
  283.     public Node(char key, Node left, Node right) {    
  284.         this.key = key;    
  285.         this.left = left;    
  286.         this.right = right;    
  287.     }    
  288.     
  289.     public char getKey() {    
  290.         return key;    
  291.     }    
  292.     
  293.     public void setKey(char key) {    
  294.         this.key = key;    
  295.     }    
  296.     
  297.     public Node getLeft() {    
  298.         return left;    
  299.     }    
  300.     
  301.     public void setLeft(Node left) {    
  302.         this.left = left;    
  303.     }    
  304.     
  305.     public Node getRight() {    
  306.         return right;    
  307.     }    
  308.     
  309.     public void setRight(Node right) {    
  310.         this.right = right;    
  311.     }    
  312. }    

运行结果如下:
[java] view plain copy
  1.  递归遍历   
  2.  Pre-Order:H D B A C G F E    
  3.  In-Order:B A D C H G E F   
  4.  Post-Order:A B C D E F G H    
  5. 非递归遍历   
  6.  Pre-Order:H D B A C G F E   
  7.  Pre-Order2:H D B A C G F E    
  8.  In-Order:B A D C H G E F   
  9.  In-Order2:B A D C H G E F   
  10.  Post-Order:A B C D E F G H   
  11.  Post-Order2:A B C D E F G H   
  12.  Post-Order3:A B C D E F G H   
  13.  Post-Order4:A B C D E F G H   


交换左右子树结点:
如下图:
   交换后:

[java] view plain copy
  1. protected  void reverserLeftAndRight(Node p) {  
  2.         if(p==null){//这句话绝对不能丢。不然有空指针异常,因为后面操作了p.getLeft();递归的话递归结束条件很重要,所以p==null 判断很重要,不要根节点就不会。要考虑所有情况。  
  3.             return;  
  4.         }  
  5.         if(null==p.getLeft()&&null==p.getRight())  
  6.             return;  
  7.         Node temp=p.getLeft();  
  8.         p.setLeft(p.getRight());  
  9.         p.setRight(temp);  
  10.         reverserLeftAndRight(p.getLeft());  
  11.         reverserLeftAndRight(p.getRight());  
  12.     }  
  13.       
  14.     protected  void reverserLeftAndRight1(Node root) {  
  15.         if(root==null){  
  16.             return;  
  17.         }  
  18.         if(null==root.getLeft()&&null==root.getRight())  
  19.             return;  
  20.         Queue<Node> qu=new LinkedList<Node>();  
  21.         qu.add(root);  
  22.         Node temp;  
  23.         Node q=root;  
  24.         while(!qu.isEmpty()){  
  25.             if(null!=q.getLeft()){//这个必须有。不然把是null 的也加进来了。  
  26.                 qu.add(q.getLeft());  
  27.             }  
  28.             if(null!=q.getRight()){  
  29.                 qu.add(q.getRight());  
  30.             }  
  31.             temp=q.getLeft();  
  32.             q.setLeft(q.getRight());  
  33.             q.setRight(temp);  
  34.             q=qu.remove();  
  35.         }  
  36.     }  
添加上面两个函数交换左右子树,一个递归算法,一个非递归(不一定用栈这里用的是队列)。
运行结果如下:

[java] view plain copy
  1. 未交换前的路径:  
  2. 路径:  
  3.   H  D  B  A  
  4. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  5. 路径:  
  6.   H  D  C  
  7. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  8. 路径:  
  9.   H  G  F  E  
  10. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  11.  递归交换子树>>>>>>  
  12.   
  13. 路径:  
  14.   H  G  F  E  
  15. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  16. 路径:  
  17.   H  D  C  
  18. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  19. 路径:  
  20.   H  D  B  A  
  21. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  22.  非递归交换左右子树(又回到原来状态)  
  23.   
  24. 路径:  
  25.   H  G  F  E  
  26. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  27. 路径:  
  28.   H  D  C  
  29. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  30. 路径:  
  31.   H  D  B  A  
  32. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  


转自:http://www.iteye.com/topic/459058
关于二叉树的遍历还说一点:
前序序列获得的是根(第一个节点);
中序序列获得的是左右子树划分;
后序序列获得的是根(最后一个节点)。
前序序列和后序序列只能获得根,不能获得左右子树划分,所以不能唯一确定一颗二叉树。
(前序+中序)或者(后序+中序)都能唯一确定一颗二叉树。
如下图所示:

可以看到首先就通过先序a确立了根。通过中序确立了a 的左右子树。
遍历应用1:输出叶子跟结点到叶子结点的路径。
代码如下:

[java] view plain copy
  1. /** 得到根结点到叶子结点的路径*/    
  2.     protected  void getPathFromRootToLeaf(Node p) {    
  3.         //this.path;  
  4.         if(null==p.getLeft()&&null==p.getRight()){  
  5.             path[pathLength++]=p.getKey();  
  6.             System.out.print("\n路径:\n");  
  7.             for(int i=0;i<pathLength;i++){  
  8.                 System.out.print("  "+path[i]);  
  9.             }  
  10.             System.out.print("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");  
  11.             pathLength--;//注意:每pathLength++,就要对应pathLength--;  
  12.             return;  
  13.         }else{  
  14.             if(null!=p.getLeft()){  
  15.                 path[pathLength++]=p.getKey();  
  16.                 getPathFromRootToLeaf(p.getLeft());  
  17.                 pathLength--;//每pathLength++,就要对应pathLength--;  
  18.             }  
  19.             if(null!=p.getRight()){  
  20.                 path[pathLength++]=p.getKey();  
  21.                 getPathFromRootToLeaf(p.getRight());  
  22.                 pathLength--;//每pathLength++,就要对应pathLength--;  
  23.             }  
  24.         }  
  25.         //pathLength--;  
  26.     }  
main 函数里:
[java] view plain copy
  1. tree.getPathFromRootToLeaf(tree.getRoot());  
所以结果输出为:
[java] view plain copy
  1. 路径:  
  2.   H  D  B  A  
  3. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  4. 路径:  
  5.   H  D  C  
  6. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
  7. 路径:  
  8.   H  G  F  E  
  9. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>  
注意:每pathLength++,就要对应一个pathLength- -;
0 0
原创粉丝点击