一,二叉樹的遍歷html
二叉樹的遞歸遍歷很是簡潔,遞歸調用須要用到棧。所以,要想實現非遞歸遍歷,就相似於模擬程序的自動壓棧、出棧,就須要建立一個棧。java
本程序使用java.util.LinkedList 來表示棧。node
二,前序非遞歸遍歷實現算法
先序遍歷是先訪問該結點,再訪問左子樹,而後再訪問右子樹數組
所以,先訪問該結點;而後將該結點入棧。(第10行)dom
而後,不斷遍歷該結點的左孩子(左左孩子....)(第8行while循環),當走到空時(第8行while不成立)。說明最「裏層」的結點的左子樹已經訪問完畢ide
因而,接着訪問它的左子樹中的結點(第15行的 if 語句塊中的第18行)。當找到它的右孩子以後,又按照前面的步驟遍歷左孩子(左左孩子...)(回到第6行的大while循環,首先判斷第8行的while循環)post
第8行的while循環代表:結點還有左孩子...ui
第15行的 if 表示:結點的左孩子爲空了,須要「切換」到右孩子的路徑上去了(再優先訪問 右孩子的 左孩子...)this
1 private void nonRecurPreTraverse(BinaryNode root){ 2 LinkedList<BinaryNode> stack = new LinkedList<MyBinaryTree.BinaryNode>(); 3 BinaryNode currentNode; 4 BinaryNode tmp; 5 currentNode = root; 6 while(currentNode != null || !stack.isEmpty()) 7 { 8 while(currentNode != null)//一直往一個方向走 9 { 10 System.out.print(currentNode.ele + " ");//visit 11 stack.push(currentNode); 12 currentNode = currentNode.left; 13 } 14 15 if(!stack.isEmpty())//變換方向 16 { 17 tmp = stack.pop(); 18 currentNode = tmp.right; 19 } 20 } 21 }
前序的遞歸實現:(基準條件必定不能忘,這是遞歸的結束條件。)
1 private void preorder(BinaryNode root){ 2 if(root == null) 3 return;//base condition 4 System.out.print(root.ele + " ");//visit 5 preorder(root.left); 6 preorder(root.right); 7 }
三,中序遍歷的非遞歸實現
先一直沿着「左孩子方向」不斷地走,當走到了最左下結點時(第9行while不成立),準備出棧,訪問該結點。(第15行if語句)
當出棧訪問完該結點(第1八、19行)以後,切換到該結點的左孩子的「子樹」中,回到第6行大循環,與前面同樣,繼續對該「子樹」先沿着「左孩子方向」不斷地走....
1 private void nonRecurInTraverse(BinaryNode root){ 2 LinkedList<BinaryNode> stack = new LinkedList<BinaryNode>(); 3 BinaryNode currentNode, tmp; 4 currentNode = root; 5 6 while(currentNode != null || !stack.isEmpty()) 7 { 8 //先"走完"左孩子 9 while(currentNode != null) 10 { 11 stack.push(currentNode); 12 currentNode = currentNode.left; 13 } 14 //結點沒有左孩子了,出棧,訪問結點 15 if(!stack.isEmpty()) 16 { 17 tmp = stack.pop(); 18 System.out.print(tmp.ele + " ");//visit 19 currentNode = tmp.right; 20 } 21 } 22 }
中序的遞歸遍歷:
1 private void inorder(BinaryNode root){ 2 if(root == null) 3 return; 4 inorder(root.left); 5 System.out.print(root.ele + " ");//visit 6 inorder(root.right); 7 }
四,後序遍歷的非遞歸實現
後序遍歷的非遞歸實現比前序、中序的非遞歸實現 要複雜一點。須要一個標識來標記某結點是否第一次位於棧頂(該結點的左子樹已經遍歷完畢,從左子樹返回準備遍歷它的右子樹)
對於後序遍歷而言,結點的左右子樹都遍歷完成以後,才訪問該結點。某結點會兩次位於棧頂,第一次是該結點的左子樹都遍歷完了,而後 獲取 棧頂結點,切換到該結點的右孩子,準備遍歷它的右子樹,當該結點的右子樹也都遍歷完後,它就會第二次位於棧頂,此時將棧頂元素出棧。
1 private void postNonRecurTraverse(BinaryNode root){ 2 LinkedList<BinaryNode> stack = new LinkedList<MyBinaryTree.BinaryNode>(); 3 4 BinaryNode currentNode, tmp; 5 currentNode = root; 6 while(currentNode != null || !stack.isEmpty()) 7 { 8 while(currentNode != null) 9 { 10 stack.push(currentNode); 11 currentNode = currentNode.left; 12 } 13 if(!stack.isEmpty()) 14 { 15 tmp = stack.getFirst(); 16 //從左子樹返回,須要判斷它的右子樹是否已經訪問了 17 if(tmp.isFirst == false)//右子樹還未被訪問 18 { 19 tmp.isFirst = true; 20 currentNode = tmp.right; 21 } 22 else{//左右子樹都已經訪問了 23 tmp = stack.pop(); 24 System.out.print(tmp.ele + " ");//visit 25 // currentNode = null; 26 } 27 } 28 }//while 29 }
對於後序遍歷而言,須要判斷某個結點第一次位於棧頂,所以上面方法須要在結點類中添加一個boolean 屬性表示該節點是否第一次位於棧頂。
class BinaryNode{ BinaryNode left; BinaryNode right; int ele; boolean isFirst; public BinaryNode(int ele) { this.ele = ele; left = right = null; isFirst = false; } }
五,整個程序完整實現:
buildTree()方法是根據一維整型數組 隨機構造一棵二叉樹。而後,對該二叉樹進行各類遍歷操做。關於如何構建二叉樹,可參考:二叉查找樹的遞歸實現及遞歸分析
import java.util.LinkedList; import java.util.Random; /** * * @author psj * */ public class MyBinaryTree { private static final Random rand = new Random();//insert left or right private static class BinaryNode{ int ele; BinaryNode left; BinaryNode right; boolean isFirst; public BinaryNode(int ele) { this.ele = ele; this.left = this.right = null; this.isFirst = false; } } private BinaryNode root;//二叉樹的根結點 //隨機構建二叉樹 public void buildTree(){ int[] ndoes = {3,0,7,4,9,10,45}; for (int i : ndoes) { insert(i); } } public BinaryNode insert(int ele){ return root = insert(root, ele); } private BinaryNode insert(BinaryNode root, int ele){ if(root == null) return root = new BinaryNode(ele); if(rand.nextInt() %2 == 0) root.left = insert(root.left, ele); else root.right = insert(root.right, ele); return root; } //中序非遞歸遍歷 public void nonRecurInTraverse(){ if(root == null) return; nonRecurInTraverse(root); } private void nonRecurInTraverse(BinaryNode root){ LinkedList<BinaryNode> stack = new LinkedList<BinaryNode>(); BinaryNode currentNode, tmp; currentNode = root; while(currentNode != null || !stack.isEmpty()) { // while(currentNode != null) { stack.push(currentNode); currentNode = currentNode.left; } // if(!stack.isEmpty()) { tmp = stack.pop(); System.out.print(tmp.ele + " ");//visit currentNode = tmp.right; } } } //中序遞歸遍歷 public void inorder(){ inorder(root); } private void inorder(BinaryNode root){ if(root == null) return; inorder(root.left); System.out.print(root.ele + " ");//visit inorder(root.right); } //先序非遞歸遍歷 public void nonRecurPreTraverse(){ if(root == null) return; nonRecurPreTraverse(root); } private void nonRecurPreTraverse(BinaryNode root){ LinkedList<BinaryNode> stack = new LinkedList<MyBinaryTree.BinaryNode>(); BinaryNode currentNode; BinaryNode tmp; currentNode = root; while(currentNode != null || !stack.isEmpty()) { while(currentNode != null) { System.out.print(currentNode.ele + " ");//visit stack.push(currentNode); currentNode = currentNode.left; } if(!stack.isEmpty()) { tmp = stack.pop(); currentNode = tmp.right; } } } //先序遞歸遍歷 public void preOrder(){ preorder(root); } private void preorder(BinaryNode root){ if(root == null) return;//base condition System.out.print(root.ele + " ");//visit preorder(root.left); preorder(root.right); } //後序非遞歸遍歷 public void postNonRecurTraverse(){ if(root == null) return; postNonRecurTraverse(root); } private void postNonRecurTraverse(BinaryNode root){ LinkedList<BinaryNode> stack = new LinkedList<MyBinaryTree.BinaryNode>(); BinaryNode currentNode, tmp; currentNode = root; while(currentNode != null || !stack.isEmpty()) { while(currentNode != null) { stack.push(currentNode); currentNode = currentNode.left; } if(!stack.isEmpty()) { tmp = stack.getFirst(); //從左子樹返回,須要判斷它的右子樹是否已經訪問了 if(tmp.isFirst == false)//右子樹還未被訪問 { tmp.isFirst = true; currentNode = tmp.right; } else{//左右子樹都已經訪問了 tmp = stack.pop(); System.out.print(tmp.ele + " ");//visit // currentNode = null; } } }//while } public void postOrder(){ postOrder(root); } private void postOrder(BinaryNode root){ if(root == null) return; postOrder(root.left); postOrder(root.right); System.out.print(root.ele + " ");//visit } public static void main(String[] args) { MyBinaryTree mbt = new MyBinaryTree(); mbt.buildTree(); System.out.println("in order"); mbt.nonRecurInTraverse(); System.out.println(); mbt.inorder(); System.out.println("\npre order"); mbt.preOrder(); System.out.println(); mbt.nonRecurPreTraverse(); System.out.println("\n post order"); mbt.postOrder(); System.out.println(); mbt.postNonRecurTraverse(); } }
六,二叉查找樹中序遍歷應用
判斷一棵樹,是不是二叉查找樹。採用中序遍歷,若是遍歷是有序的,則是一棵二叉查找樹。代碼以下:
import java.util.LinkedList; import java.util.List; public class Solution { public boolean isValidBST(TreeNode root) { LinkedList<TreeNode> stack = new LinkedList<>(); TreeNode currentNode,tmp; // int currentVal = Integer.MIN_VALUE; long currentVal = Long.MIN_VALUE; currentNode = root; while (currentNode != null || !stack.isEmpty()) { while (currentNode != null) { stack.push(currentNode); currentNode = currentNode.left; } if (!stack.isEmpty()) { tmp = stack.pop(); if (currentVal >= tmp.val) { return false; }else { currentVal = tmp.val; } currentNode = tmp.right; } } return true; } public List<Integer> inOrderNonRecurse(TreeNode root, List<Integer> nodes) { LinkedList<TreeNode> stack = new LinkedList<>(); TreeNode currentNode, tmp; currentNode = root; while (currentNode != null || !stack.isEmpty()) { while (currentNode != null) { stack.push(currentNode); currentNode = currentNode.left; } if (!stack.isEmpty()) { tmp = stack.pop(); nodes.add(tmp.val); currentNode = tmp.right; } } return nodes; } public List<Integer> inOrder(TreeNode root, List<Integer> nodes) { if (root == null) { return nodes; } inOrder(root.left, nodes); nodes.add(root.val); inOrder(root.right, nodes); return nodes; } public static class TreeNode{ int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } }
參考資料: