二叉樹 - DFS與BFS
深度優先遍歷 (DFS
Depth First Search
) 就是一個節點不到頭(葉子節點為空) 不回頭 廣度有點遍歷(BFS
Breadth First Search
) 就是一層一層輸出 , 輸出到最下層的葉子節點, 為空的時候結束
其中深度遍歷就是我們所說的 先序遍歷 中序遍歷 后序遍歷 , 先中后指的是根節點輸出的時機,先就是根左右
數據結構如下, 全文都是
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) {
val = x;
}
輸出要求
List<Integer> search(TreeNode root) {
// do 需要返回一個數組
}
測試數據
TreeNode root = new TreeNode(1);
TreeNode left = new TreeNode(2);
TreeNode right = new TreeNode(3);
root.left = left;
root.right = right;
left.left = new TreeNode(4);
left.right = new TreeNode(5);
right.left = new TreeNode(6);
1. DFS
1. 遞歸實現
遞歸實現代碼相當之簡單 , 所以很容易寫, 就算不會也能記憶下來
1. 先序遍歷
private List<Integer> preOrder(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
recursion(list, root);
return list;
}
// 遞歸
private void recursion(List<Integer> list, TreeNode root) {
if (null == root) return;
// 根
list.add(root.val);
// 左
recursion(list, root.left);
// 右
recursion(list, root.right);
}
2. 中序遍歷
private List<Integer> midOrder(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
recursion(list, root);
return list;
}
private void recursion(List<Integer> list, TreeNode root) {
if (null == root) return;
recursion(list, root.left);
// 調換到中間
list.add(root.val);
recursion(list, root.right);
}
3. 后序遍歷
private List<Integer> aftOrder(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
recursion(list, root);
return list;
}
private void recursion(List<Integer> list, TreeNode root) {
if (null == root) return;
recursion(list, root.left);
recursion(list, root.right);
// 調換到最后
list.add(root.val);
}
2. 遞歸執行流程
三種流程基本都差不多
好多人對於遞歸並不了解, 執行流程 , 我們知道方法的出棧需要一個return, 所以遞歸就是在找這個 , 就拿我們上面說的那個先序遍歷為例子吧 .
3. 非遞歸實現(很重要)
遞歸的壞處就是 , 出入棧消耗大量的內存, 每一次方法的調用都會保存大量的變量, 多以對於遍歷來說並不好 ,
非遞歸遍歷的實現 , 基於棧的實現, 對於遍歷節點保存在棧中, 出入棧 , 主要利用棧的后進先出的特性 , 很好的保證了, 后進的優先遍歷 .
1. 先序遍歷
非遞歸實現先序遍歷
private List<Integer> preOrderUnRecursion(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
// 棧
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
// 壓棧
stack.push(root);
while (stack.size() > 0) {
// 出棧
TreeNode node = stack.pop();
TreeNode right = node.right;
if (null != right) {
stack.push(right);
}
TreeNode left = node.left;
if (null != left) {
stack.push(left);
}
list.add(node.val);
}
return list;
}
2. 中序遍歷
這個實現就比較麻煩了 , 因為先序遍歷, 根節點有先天的優勢可以先出去 ,所以很
private List<Integer> midOrderUnRecursion(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
// 棧
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
// 壓棧
stack.push(root);
while (stack.size() > 0) {
// 出棧
TreeNode node = stack.pop();
TreeNode right = node.right;
TreeNode left = node.left;
if (null != right) {
node.right = null;
stack.push(right);
}
// 重復入棧 , 是因為根節點不是最先出來的
if (null != right || null != left) {
stack.push(node);
}
if (null != left) {
node.left = null;
stack.push(left);
}
if (null == left && null == right) {
list.add(node.val);
}
}
return list;
}
3. 后序遍歷
private List<Integer> aftFirstSearchUnRecursion(TreeNode root) {
if (null == root) return Collections.emptyList();
List<Integer> list = new ArrayList<>();
// 棧
LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
// 壓棧
stack.push(root);
while (stack.size() > 0) {
// 出棧
TreeNode node = stack.pop();
TreeNode right = node.right;
TreeNode left = node.left;
if (null != right || null != left) {
stack.push(node);
}
if (null != right) {
node.right = null;
stack.push(right);
}
if (null != left) {
node.left = null;
stack.push(left);
}
if (null == left && null == right) {
list.add(node.val);
}
}
return list;
}
4.非遞歸實現流程圖
1. 先序遍歷
2. 中序遍歷和后序遍歷一樣
流程從左往右, 從上往下看 .
2. BFS
廣度優先遍歷就是一層 一層遍歷 , 同一層, 從左到右輸出,
基於隊列實現的 , FIFO特性 , offer 和 poll , 操作
代碼實現
private List<Integer> breadthFirstSearch(TreeNode root) {
if (null == root) return Collections.emptyList();
ArrayList<Integer> list = new ArrayList<>();
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (queue.peek() != null) {
TreeNode poll = queue.poll();
TreeNode left = poll.left;
if (null != left) {
queue.offer(left);
}
TreeNode right = poll.right;
if (null != right) {
queue.offer(right);
}
list.add(poll.val);
}
基本流程圖
3. 求樹的深度
利用樹的先序遍歷遞歸進行求樹的深度
private int countDepth(TreeNode root) {
if (null == root) return 0;
int left = countDepth(root.left);
int right = countDepth(root.right);
return left >= right ? left + 1 : right + 1;
}
4. 求數的節點個數
也是遞歸遍歷
private int countNode(TreeNode root) {
if (null == root) return 0;
return countNode(root.left) + countNode(root.right)+1;
}