深度優先遍歷簡介和實現
深度優先遍歷(Depth First Search)簡介:
先從根節點沿着(左或者右)分支走到底,當再也沒有葉子節點之後,返回上一節點,如果上一個節點還有另外一個子節點,那麼遍歷這個節點分支直至再也沒有葉子節點,之後重複上面的過程
例如上圖按照先遍歷左分支再遍歷右分支的深度優先遍歷順序就是:
A B D E C F G
不難發現其實就是前序遍歷
實現:
深度優先遍歷的實現方式一般分爲遞歸式和非遞歸式
遞歸式:
遞歸實現比較簡單,由於是前序遍歷,所以我們依次遍歷當前節點,左節點,右節點即可,對於左右節點來說,依次遍歷它們的左右節點即可,依此不斷遞歸下去,直到葉節點(遞歸終止條件),代碼如下
package search;
import java.util.Stack;
/**
* @author huchenfei
* @version 1.0
* @className DeepFirstSearch
* @description 二叉樹深度遍歷算法
* @date 2020/6/2 15:38
**/
public class DepthFirstSearch {
private static class Node {
/**
* 節點值
*/
public int value;
/**
* 左節點
*/
public Node left;
/**
* 右節點
*/
public Node right;
public Node(int value) {
this.value = value;
}
}
public static void main(String... args) {
Node head = new Node(1);
Node second = new Node(2);
Node three = new Node(3);
Node four = new Node(4);
Node five = new Node(5);
Node six = new Node(6);
Node seven = new Node(7);
head.right = three;
head.left = second;
second.right = five;
second.left = four;
three.right = seven;
three.left = six;
System.out.println("遞歸方式 -> 深度優先遍歷結果: ");
recursiveDfs(head);
System.out.println();
}
/**
* 遞歸方式實現 DFS
*
* @param head 二叉樹
*/
public static void recursiveDfs(Node head) {
if (head == null) {
return;
}
System.out.print(head.value);
System.out.print(" ");
// 遍歷左節點
recursiveDfs(head.left);
// 遍歷右節點
recursiveDfs(head.right);
}
}
遞歸的表達性很好,也很容易理解,不過如果層級過深,很容易導致棧溢出。所以我們重點看下非遞歸實現
非遞歸式:
仔細觀察深度優先遍歷的特點,對二叉樹來說,由於是先序遍歷(先遍歷當前節點,再遍歷左節點,再遍歷右節點),所以我們有如下思路
- 對於每個節點來說,先遍歷當前節點,然後把右節點壓棧,再壓左節點(這樣彈棧的時候會先拿到左節點遍歷,符合深度優先遍歷要求)
- 彈棧,拿到棧頂的節點,如果節點不爲空,重複步驟 1, 如果爲空,結束遍歷。
package search;
import java.util.Stack;
/**
* @author huchenfei
* @version 1.0
* @className DeepFirstSearch
* @description 二叉樹深度遍歷算法
* @date 2020/6/2 15:38
**/
public class DepthFirstSearch {
private static class Node {
/**
* 節點值
*/
public int value;
/**
* 左節點
*/
public Node left;
/**
* 右節點
*/
public Node right;
public Node(int value) {
this.value = value;
}
}
public static void main(String... args) {
Node head = new Node(1);
Node second = new Node(2);
Node three = new Node(3);
Node four = new Node(4);
Node five = new Node(5);
Node six = new Node(6);
Node seven = new Node(7);
head.right = three;
head.left = second;
second.right = five;
second.left = four;
three.right = seven;
three.left = six;
System.out.println("非遞歸方式 -> 深度優先遍歷結果: ");
unRecursiveDfs(head);
System.out.println();
}
/**
* 非遞歸的使用棧來實現 DFS
* 依據棧的先進後出特性
* 1. 將二叉樹的頭結點放入棧中
* 2. 將頭結點 1 從棧中彈出,並查看頭結點是否有左右節點,將右節點和左節點壓入棧中【3,2】
* 3. 將頭結點 2 從棧中彈出,並查看 2 節點是否有左右節點,將右節點和左節點壓入棧中【3,5,4】
* 4. 將頭結點 4 從棧中彈出,無左右節點【3,5】
* 5. 將頭結點 5 從棧中彈出,無左右節點【3】
* 6. 將頭結點 3 從棧中彈出,並查看 3 節點是否有左右節點,將右節點和左節點壓入棧中【7,6】
* 7. 將頭結點 6 從棧中彈出,無左右節點【7】
* 8. 將頭結點 7 從棧中彈出,無左右節點
*
* @param head 二叉樹
*/
public static void unRecursiveDfs(Node head) {
if (head == null) {
return;
}
Stack<Node> stack = new Stack<>();
// 先將根結點壓入棧中
stack.push(head);
while (!stack.isEmpty()) {
Node node = stack.pop();
System.out.print(node.value);
System.out.print(" ");
// 將節點右邊的壓入棧中
if (node.right != null) {
stack.push(node.right);
}
// 壓入左邊的棧中
if (node.left != null) {
stack.push(node.left);
}
}
}
}
拓展:
參考:
二叉樹的廣度優先遍歷和深度優先遍歷(Java實現)
圖文詳解 DFS 算法 和 BFS 算法
二叉樹的深度優先搜索和廣度優先搜索(java實現)