上篇中學習了二叉樹的DFS深度優先搜索算法,這次學習另外一種二叉樹的搜索算法:BFS,下面看一下它的概念:

有些抽象是不?下面看下整個的遍歷過程的動畫演示就曉得是咋回事啦:

了解其概念之后,下面看下如何實現它?在正式實現逐層遍歷之前,需要解決一個問題,那就是:得知道該樹有多少層,也就是樹的深度如何計算,下面來解決這個問題:
還是基於上篇的搜索二叉樹的代碼進行實現:
public class BinarySearchTree { TreeNode root = null; class TreeNode{ int value; int position; TreeNode left = null, right = null; TreeNode(int value, int position){ this.value = value; this.position = position; } } public void add(int value, int position){ if(root == null){//生成一個根結點 root = new TreeNode(value, position); } else { //生成葉子結點 add(value, position, root); } } private void add(int value, int position, TreeNode node){ if(node == null) throw new RuntimeException("treenode cannot be null"); if(node.value == value) return; //ignore the duplicated value if(value < node.value){ if(node.left == null){ node.left = new TreeNode(value, position); }else{ add(value, position, node.left); } }else{ if(node.right == null){ node.right = new TreeNode(value, position); }else{ add(value, position, node.right); } } } //打印構建的二叉搜索樹 static void printTreeNode(TreeNode node) { if(node == null) return; System.out.println("node:" + node.value); if(node.left != null) { printTreeNode(node.left); } if(node.right != null) { printTreeNode(node.right); } } //搜索結點 public int search(int value){ return search(value, root); } private int search(int value, TreeNode node){ if(node == null) return -1; //not found else if(value < node.value){ System.out.println("Searching left"); return search(value, node.left); } else if(value > node.value){ System.out.println("Searching right"); return search(value, node.right); } else return node.position; } //二叉樹DFS遍歷 public void travel(){ travel(root); } public void travel(TreeNode node){ if(node == null) return; travel(node.left); travel(node.right); System.out.println(" " + node.value); } //二叉樹的深度數 public int depth(){ return depth(root); } private int depth(TreeNode node){ if(node == null) return 0; int leftDepth = depth(node.left); int rightDepth = depth(node.right); return Math.max(leftDepth, rightDepth) + 1; } public static void main(String[] args) { BinarySearchTree bst = new BinarySearchTree(); int a[] = { 5, 8, 3, 4, 1, 7, 6}; for(int i = 0; i < a.length; i++){ bst.add(a[i], i); } System.out.println("Tree Depth:" + bst.depth()); } }
其上面搜索二叉樹再貼一下,以便可以直觀的可以查看:

編譯運行:

下面來debug看一下程序看如何計算出樹的深度的:

a、將root = TreeNode(5, 0)傳給帶參數的depth方法進行遞歸遍歷。
Loop1:其參數node = TreeNode(5, 0)
b、
,條件不滿足,執行步驟c;
c、
,int leftDepth = depth(node.left = TreeNode(3, 2)),繼續遞歸左結點:
cb、
,條件不滿足,執行步驟cc;
cc、
,int leftDepth = depth(node.left = TreeNode(1, 4)),繼續遞歸左結點:
ccb、
,條件不滿足,執行步驟ccc;
ccc、
,int leftDepth = depth(node.left = null),繼續遞歸左結點:
cccb、
,條件滿足退出返回leftDepth = 0,執行步驟ccd;
ccd、
,int rightDepth = depth(node.right = null),繼續遞歸右結點:
cccb、
,條件滿足退出返回rightDepth = 0,執行步驟cce;
cce、
,result = 1;
所以這時leftDepth = 1;
cd、
,int rightDepth = depth(node.right = TreeNode(4, 3)),繼續遞歸右結點:
cdb、
,條件不滿足,執行步驟cdc;
cdc、
,int leftDepth = depth(node.left = null),繼續遞歸左結點:
cdcb、
,條件滿足退出返回leftDepth = 0,執行步驟cdd;
cdd、
,int rightDepth = depth(node.right = null),繼續遞歸右結點:
cddb、
,條件滿足退出返回rightDepth = 0,執行步驟cde;
cde、
,result = 1;
所以這時leftDepth = 1;
ce、
,result = max(1, 1) + 1 = 2;
所以這時leftDepth = 2;
d、
,int rightDepth = depth(node.right = TreeNode(8, 1)),繼續遞歸右結點:
db、
,條件不滿足,執行步驟dc;
dc、
,int leftDepth = depth(node.left = TreeNode(7, 5)),繼續遞歸左結點:
dcb、
,條件不滿足,執行步驟dcc;
dcc、
,int leftDepth = depth(node.left = TreeNode(6, 6)),繼續遞歸左結點:
dccb、
,條件不滿足,執行步驟dccc;
dccc、
,int leftDepth = depth(node.left = null),繼續遞歸左結點:
dcccb、
,條件滿足退出返回leftDepth = 0,執行步驟dccd;
dccd、
,int rightDepth = depth(node.right = null),繼續遞歸右結點:
dccdb、
,條件滿足退出返回rightDepth = 0,執行步驟dcce;
dcce、
,result = max(0, 0) + 1 = 1;
所以這時leftDepth = 1;
dcd、
,int rightDepth = depth(node.right = null),繼續遞歸右結點:
dcdb、
,條件滿足退出返回rightDepth = 0,執行步驟dce;
dce、
,result = max(1, 0) + 1 = 2;
所以這時leftDepth = 2;
dd、
,int rightDepth = depth(node.right = null),繼續遞歸右結點:
ddb、
,條件滿足退出返回rightDepth = 0,執行步驟de;
所以這時rightDepth = 0;
de、
,result = max(2, 0) + 1 = 3;
所以這時rightDepth = 3;
e、
,result = max(2,3) + 1 = 4,所以最終此樹的深度為4!
總結其實現思路:
1、遞歸的邊界結束條件是傳過來的節點為空了。
2、遞歸左結點的深度
3、遞歸右結點的深度
4、總結點的深度為左結點的深度+右結點的深度+1
上面已經實現了樹的深度的計算,接下來則是利用DFS來將二叉樹進行遍歷啦,先上代碼:
public class BinarySearchTree { TreeNode root = null; class TreeNode{ int value; int position; TreeNode left = null, right = null; TreeNode(int value, int position){ this.value = value; this.position = position; } } public void add(int value, int position){ if(root == null){//生成一個根結點 root = new TreeNode(value, position); } else { //生成葉子結點 add(value, position, root); } } private void add(int value, int position, TreeNode node){ if(node == null) throw new RuntimeException("treenode cannot be null"); if(node.value == value) return; //ignore the duplicated value if(value < node.value){ if(node.left == null){ node.left = new TreeNode(value, position); }else{ add(value, position, node.left); } }else{ if(node.right == null){ node.right = new TreeNode(value, position); }else{ add(value, position, node.right); } } } //打印構建的二叉搜索樹 static void printTreeNode(TreeNode node) { if(node == null) return; System.out.println("node:" + node.value); if(node.left != null) { printTreeNode(node.left); } if(node.right != null) { printTreeNode(node.right); } } //搜索結點 public int search(int value){ return search(value, root); } private int search(int value, TreeNode node){ if(node == null) return -1; //not found else if(value < node.value){ System.out.println("Searching left"); return search(value, node.left); } else if(value > node.value){ System.out.println("Searching right"); return search(value, node.right); } else return node.position; } //二叉樹DFS遍歷 public void travel(){ travel(root); } public void travel(TreeNode node){ if(node == null) return; travel(node.left); travel(node.right); System.out.println(" " + node.value); } //二叉樹的深度數 public int depth(){ return depth(root); } private int depth(TreeNode node){ if(node == null) return 0; int leftDepth = depth(node.left); int rightDepth = depth(node.right); return Math.max(leftDepth, rightDepth) + 1; } //二叉樹的BFS遍歷 public void levelOrder(){ int depth = depth(); for(int level = 0; level < depth; level ++){ printLevel(root, level); System.out.println("\n-------------------"); } } private void printLevel(TreeNode node, int level){ if(node == null) return; if(level == 0){ System.out.print(" " + node.value); }else{ printLevel(node.left, level - 1); printLevel(node.right, level - 1); } } public static void main(String[] args) { BinarySearchTree bst = new BinarySearchTree(); int a[] = { 5, 8, 3, 4, 1, 7, 6}; for(int i = 0; i < a.length; i++){ bst.add(a[i], i); } System.out.println("Tree Depth:" + bst.depth()); bst.levelOrder(); } }
編譯運行:

下面再來debug一下其利用遞歸來BFS遍歷的整個過程:

a、depth = 4
b、根據樹的層次依次進行遍歷打印,具體如下:
Loop1:level = 0,level < 4條件為真,進入循環體:
①、遞歸打印第一層的所有結點:printLevel(root = TreeNode(5, 0), 0):
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"5"【level=0表示當前就是要打印的結點,因為每遞歸一次層會遞減一,等到指定層也就減為0了】
②、打印一個分隔行以便結果可以看起來比較直觀。"-------------------"
level = level + 1 = 1;
Loop2:level = 1,level < 4條件為真,進入循環體:
①、遞歸打印第一層的所有結點:printLevel(root = TreeNode(5, 0), 1):
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(3, 2), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"3";
②、printLevel(node.right = TreeNode(8, 1), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"8";
②、打印一個分隔行以便結果可以看起來比較直觀。"-------------------"
level = level + 1 = 2;
Loop3:level = 2,level < 4條件為真,進入循環體:
①、遞歸打印第一層的所有結點:printLevel(root = TreeNode(5, 0), 2):
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(3, 2), 1);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(1, 4), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"1";
②、printLevel(node.right = TreeNode(4, 3), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"4";
②、printLevel(node.right = TreeNode(8, 1), 1);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(7, 5), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"7";
②、printLevel(node.right = null);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、打印一個分隔行以便結果可以看起來比較直觀。"-------------------"
level = level + 1 = 3;
Loop4:level = 3,level < 4條件為真,進入循環體:
①、遞歸打印第一層的所有結點:printLevel(root = TreeNode(5, 0), 3):
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(3, 2), 2);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(1, 4), 1);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = null, 0);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、printLevel(node.right = null, 0);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、printLevel(node.right = TreeNode(4, 3), 1);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = null, 0);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、printLevel(node.right = null, 0);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、printLevel(node.right = TreeNode(8, 1), 2);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(7, 5), 1);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為假,繼續執行e;
e、分別遞歸左右結點:
①、printLevel(node.left = TreeNode(6, 6), 0);
c、判斷node是否為null,條件為假,繼續執行d;
d、條件為真,直接打印"6";
②、printLevel(node.right = null);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、printLevel(node.right = null);
c、判斷node是否為null,條件為真,直接返回遞歸結束。
②、打印一個分隔行以便結果可以看起來比較直觀。"-------------------"
level = level + 1 = 4;
Loop5:level = 4,level < 4條件為假,結束循環。
總結其實現思路:
1、首先獲得樹的層數,然后進行逐層打印。
2、每層打印時,都是從根節點開始來遍歷的【很顯示這種做法不是很高效,這節先學一種,未來會有更高效的做法】
3、在遞歸函數中有三個條件:
a、如果當前節點是null,則直接返回遞歸結束。
b、如果當前的層數為0,那證明就是要打印的層,則直接打印當前節點。
c、以上兩個條件都不滿足,則說明該結點還有子結點,於是乎分別再次遞歸它的左結點和右結點,並且將層數減一。
下面來分析一下它的時間復雜度:

實際上整個算法是比較低效的,而上面的時間復雜度是O(n^2)級別的,未來會有更高效的O(n)線性級別的算法待學習,加油!
