本文參考自《劍指offer》一書,代碼採用Java語言。html
更多:《劍指Offer》Java實現合集 java
(一)從上往下打印出二叉樹的每一個結點,同一層的結點按照從左到右的順序打印。node
(二)從上到下按層打印二叉樹,同一層的結點按從左到右的順序打印,每一層打印到一行。面試
(三)請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印,其餘行以此類推。編程
(一)不分行從上往下打印二叉樹:該題即爲對二叉樹的層序遍歷,結點知足先進先出的原則,採用隊列。每從隊列中取出頭部結點並打印,若其有子結點,把子結點放入隊列尾部,直到全部結點打印完畢。數組
/* * 不分行從上往下打印二叉樹 */ // 題目:從上往下打印出二叉樹的每一個結點,同一層的結點按照從左到右的順序打印。 public void printTree1(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; while (queue.size()!=0) { node = queue.poll(); System.out.print(node.val + " "); if (node.left != null) queue.offer(node.left); if (node.right != null) queue.offer(node.right); } System.out.println(); }
(二)分行從上到下打印二叉樹:一樣使用隊列,但比第一題增長兩個變量:當前層結點數目pCount,下一層結點數目nextCount。根據當前層結點數目來打印當前層結點,同時計算下一層結點數目,以後令pCount等於nextCount,重複循環,直到打印完畢。ide
/* * 分行從上到下打印二叉樹 */ // 題目:從上到下按層打印二叉樹,同一層的結點按從左到右的順序打印,每一層 // 打印到一行。 public void printTree2(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; int pCount = 0; //當前層結點數目 int nextCount = 1; //下一層結點數目 while (!queue.isEmpty()) { pCount = nextCount; nextCount = 0; //打印當前層數字,並計算下一層結點數目 for (int i = 1; i <= pCount; i++) { node = queue.poll(); System.out.print(node.val + " "); if (node.left != null) { queue.offer(node.left); nextCount++; } if (node.right != null) { queue.offer(node.right); nextCount++; } } System.out.println(); } }
(三)之字形打印二叉樹:函數
(1)本身開始想的方法:在(二)的基礎上,多定義一個表示當前層數的變量level。每層結點不直接打印,放入一個數組中,根據此時的層數level的奇偶來決定正向仍是反向打印數組。post
/* * 之字形打印二叉樹 */ // 題目:請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順 // 序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印, // 其餘行以此類推。 /** * 本身開始想的方法,採用數組存儲每層的數字,根據當前層數肯定正反向打印數組 */ public void printTree3_1(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; int pCount = 0; //當前層結點數目 int nextCount = 1; //下一層結點數目 int level=1; //層數 int[] pNums=null; //用於存儲當前層的數字 while (!queue.isEmpty()) { pCount = nextCount; nextCount = 0; pNums=new int[pCount]; //存儲當前層數字,並計算下一層結點數目 for (int i = 0; i < pCount; i++) { node = queue.poll(); pNums[i]=node.val; if (node.left != null) { queue.offer(node.left); nextCount++; } if (node.right != null) { queue.offer(node.right); nextCount++; } } //根據當前層數肯定正向或者反向打印數組 if((level&1)!=0 ) { for(int i=0;i<pCount;i++) { System.out.print(pNums[i]+" "); } }else { for(int i=pCount-1;i>=0;i--) { System.out.print(pNums[i]+" "); } } level++; System.out.println(); } }
(2)書中提供的方法:採用兩個棧,對於不一樣層的結點,一個棧用於正向存儲,一個棧用於逆向存儲,打印出來就正好是相反方向。測試
/** * 採用兩個棧進行操做的方法 */ public void printTree3_2(TreeNode root) { if (root == null) return; Stack<TreeNode> stack1 = new Stack<TreeNode>(); Stack<TreeNode> stack2 = new Stack<TreeNode>(); TreeNode node = null; stack1.push(root); while(!stack1.empty() || !stack2.empty()) { while(!stack1.empty()) { node=stack1.pop(); System.out.print(node.val + " "); if (node.left != null) stack2.push(node.left); if (node.right != null) stack2.push(node.right); } System.out.println(); while(!stack2.empty()) { node=stack2.pop(); System.out.print(node.val + " "); if (node.right != null) stack1.push(node.right); if (node.left != null) stack1.push(node.left); } System.out.println(); } }
測試算例
1.功能測試(徹底二叉樹;左斜樹;右斜樹)
2.特殊測試(null;一個結點)
含測試代碼:
import java.util.LinkedList; import java.util.Stack; /** * * @Description 面試題32:從上往下打印二叉樹 * * @author yongh */ public class PrintTreeFromTopToBottom { public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } /* * 不分行從上往下打印二叉樹 */ // 題目:從上往下打印出二叉樹的每一個結點,同一層的結點按照從左到右的順序打印。 public void printTree1(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; while (queue.size()!=0) { node = queue.poll(); System.out.print(node.val + " "); if (node.left != null) queue.offer(node.left); if (node.right != null) queue.offer(node.right); } System.out.println(); } /* * 分行從上到下打印二叉樹 */ // 題目:從上到下按層打印二叉樹,同一層的結點按從左到右的順序打印,每一層 // 打印到一行。 public void printTree2(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; int pCount = 0; //當前層結點數目 int nextCount = 1; //下一層結點數目 while (!queue.isEmpty()) { pCount = nextCount; nextCount = 0; //打印當前層數字,並計算下一層結點數目 for (int i = 1; i <= pCount; i++) { node = queue.poll(); System.out.print(node.val + " "); if (node.left != null) { queue.offer(node.left); nextCount++; } if (node.right != null) { queue.offer(node.right); nextCount++; } } System.out.println(); } } /* * 之字形打印二叉樹 */ // 題目:請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順 // 序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印, // 其餘行以此類推。 /** * 本身開始想的方法,採用數組存儲每層的數字,根據當前層數肯定正反向打印數組 */ public void printTree3_1(TreeNode root) { if (root == null) return; LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); TreeNode node = null; int pCount = 0; //當前層結點數目 int nextCount = 1; //下一層結點數目 int level=1; //層數 int[] pNums=null; //用於存儲當前層的數字 while (!queue.isEmpty()) { pCount = nextCount; nextCount = 0; pNums=new int[pCount]; //存儲當前層數字,並計算下一層結點數目 for (int i = 0; i < pCount; i++) { node = queue.poll(); pNums[i]=node.val; if (node.left != null) { queue.offer(node.left); nextCount++; } if (node.right != null) { queue.offer(node.right); nextCount++; } } //根據當前層數肯定正向或者反向打印數組 if((level&1)!=0 ) { for(int i=0;i<pCount;i++) { System.out.print(pNums[i]+" "); } }else { for(int i=pCount-1;i>=0;i--) { System.out.print(pNums[i]+" "); } } level++; System.out.println(); } } /** * 採用兩個棧進行操做的方法 */ public void printTree3_2(TreeNode root) { if (root == null) return; Stack<TreeNode> stack1 = new Stack<TreeNode>(); Stack<TreeNode> stack2 = new Stack<TreeNode>(); TreeNode node = null; stack1.push(root); while(!stack1.empty() || !stack2.empty()) { while(!stack1.empty()) { node=stack1.pop(); System.out.print(node.val + " "); if (node.left != null) stack2.push(node.left); if (node.right != null) stack2.push(node.right); } System.out.println(); while(!stack2.empty()) { node=stack2.pop(); System.out.print(node.val + " "); if (node.right != null) stack1.push(node.right); if (node.left != null) stack1.push(node.left); } System.out.println(); } } //============測試代碼============== private void test(int testNum,TreeNode root) { System.out.println("=========test"+testNum+"==========="); System.out.println("method1:"); printTree1(root); System.out.println("method2:"); printTree2(root); System.out.println("method3_1:"); printTree3_1(root); System.out.println("method3_2:"); printTree3_2(root); } //null private void test1() { TreeNode node=null; test(1, node); } //單個結點 private void test2() { TreeNode node=new TreeNode(1); test(2, node); } //左斜 private void test3() { TreeNode node1=new TreeNode(1); TreeNode node2=new TreeNode(2); TreeNode node3=new TreeNode(3); node1.left=node2; node2.left=node3; test(3, node1); } //右斜 private void test4() { TreeNode node1=new TreeNode(1); TreeNode node2=new TreeNode(2); TreeNode node3=new TreeNode(3); node1.right=node2; node2.right=node3; test(4, node1); } //徹底二叉樹 private void test5() { TreeNode[] nodes = new TreeNode[15]; for(int i=0;i<15;i++) { nodes[i]= new TreeNode(i+1); } for(int i=0;i<7;i++) { nodes[i].left=nodes[2*i+1]; nodes[i].right=nodes[2*i+2]; } test(5, nodes[0]); } public static void main(String[] args) { PrintTreeFromTopToBottom demo= new PrintTreeFromTopToBottom(); demo.test1(); demo.test2(); demo.test3(); demo.test4(); demo.test5(); } }
=========test1=========== method1: method2: method3_1: method3_2: =========test2=========== method1: 1 method2: 1 method3_1: 1 method3_2: 1 =========test3=========== method1: 1 2 3 method2: 1 2 3 method3_1: 1 2 3 method3_2: 1 2 3 =========test4=========== method1: 1 2 3 method2: 1 2 3 method3_1: 1 2 3 method3_2: 1 2 3 =========test5=========== method1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 method2: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 method3_1: 1 3 2 4 5 6 7 15 14 13 12 11 10 9 8 method3_2: 1 3 2 4 5 6 7 15 14 13 12 11 10 9 8
1.層序遍歷時,通常都要用到隊列,能夠用LinkedList類(方法:poll() 和 offer(Obj) )。
2.在分行打印時,定義的兩個int有明顯實際意義(當前層結點數目,下一層結點數目)。本身編程時,一開始只知道要設置兩個變量,但沒有去想這兩個變量的實際意義。當明白變量意義時,本身的思路會更清晰,並且代碼可讀性也更好。