介紹
深度優先遍歷:從根節點出發,沿着左子樹方向進行縱向遍歷,直到找到葉子節點為止。然后回溯到前一個節點,進行右子樹節點的遍歷,直到遍歷完所有可達節點為止。
廣度優先遍歷:從根節點出發,在橫向遍歷二叉樹層段節點的基礎上縱向遍歷二叉樹的層次。
DFS實現:
數據結構:棧
父節點入棧,父節點出棧,先右子節點入棧,后左子節點入棧。遞歸遍歷全部節點即可
BFS實現:
數據結構:隊列
父節點入隊,父節點出隊列,先左子節點入隊,后右子節點入隊。遞歸遍歷全部節點即可
樹的實現
public class TreeNode<V> {
private V value;
private List<TreeNode<V>> childList;//子節點列表
public TreeNode(V value) {
this.value = value;
}
public TreeNode(V value, List<TreeNode<V>> childList) {
this.value = value;
this.childList = childList;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public List<TreeNode<V>> getChildList() {
return childList;
}
public void setChildList(List<TreeNode<V>> childList) {
this.childList = childList;
}
}
深度優先搜索算法(DFS)
深度優先搜索算法是指沿着樹的深度遍歷樹的節點,盡可能深的搜索樹的分支。當節點v的所在邊都己被探尋過,搜索將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已發現從源節點可達的所有節點為止。如果還存在未被發現的節點,則選擇其中一個作為源節點並重復以上過程,整個進程反復進行直到所有節點都被訪問為止。屬於盲目搜索。
遞歸實現
public static <V> void dfs(TreeNode<V> tree, int depth) {
if (tree != null) {
//打印節點值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
}
}
非遞歸實現
public static <V> void dfsNotRecursive(TreeNode<V> tree) {
if (tree != null) {
//次數之所以用 Map 只是為了保存節點的深度,
//如果沒有這個需求可以改為 Stack<TreeNode<V>>
Stack<Map<TreeNode<V>, Integer>> stack = new Stack<>();
Map<TreeNode<V>, Integer> root = new HashMap<>();
root.put(tree, 0);
stack.push(root);
while (!stack.isEmpty()) {
Map<TreeNode<V>, Integer> item = stack.pop();
TreeNode<V> node = item.keySet().iterator().next();
int depth = item.get(node);
//打印節點值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (node.getChildList() != null && !node.getChildList().isEmpty()) {
for (TreeNode<V> treeNode : node.getChildList()) {
Map<TreeNode<V>, Integer> map = new HashMap<>();
map.put(treeNode, depth + 1);
stack.push(map);
}
}
}
}
}
分類
一般來說 DFS 算法又分為如下三種:
1.前序遍歷(Pre-Order Traversal) :指先訪問根,然后訪問子樹的遍歷方式
private static <V> void dfs(TreeNode<V> tree, int depth) {
if (d != null) {
//打印節點值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
}
}
2.后序遍歷(Post-Order Traversal):指先訪問子樹,然后訪問根的遍歷方式
private static <V> void dfs(TreeNode<V> tree, int depth) {
if (d != null) {
if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {
for (TreeNode<V> item : tree.getChildList()) {
dfs(item, depth + 1);
}
}
//打印節點值以及深度
System.out.println(tree.getValue().toString() + ", " + depth);
}
}
3.中序遍歷(In-Order Traversal):指先訪問左(右)子樹,然后訪問根,最后訪問右(左)子樹的遍歷方式。
中序遍歷一般是用二叉樹實現:
private static <V> void dfs(TreeNode<V> root, int depth) {
if (root.getLeft() != null){
dfs(root.getLeft(), depth + 1);
}
if (root.getRight() != null){
dfs(root.getRight(), depth + 1);
}
//打印節點值以及深度
System.out.println(d.getValue().toString() + ", " + depth);
}
廣度優先搜索算法(Breadth-First Search,BFS)
廣度優先搜索算法是從根節點開始,沿着樹的寬度遍歷樹的節點。如果所有節點均被訪問,則算法中止。
遞歸實現
public static <V> void bfs(List<TreeNode<V>> children, int depth) {
List<TreeNode<V>> thisChildren, allChildren = new ArrayList<>();
for (TreeNode<V> child: children) {
//打印節點值以及深度
System.out.println(child.getValue().toString() + ", " + depth);
thisChildren = child.getChildList();
if (thisChildren != null && thisChildren.size() > 0) {
allChildren.addAll(thisChildren);
}
}
if (allChildren.size() > 0) {
bfs(allChildren, depth + 1);
}
}
遞歸實現的方式我自己想了好久沒想出來,最后還是在網上搜到的算法。
可以看到非遞歸實現有個問題就是無法遍歷根節點,不過問題不大,而且我也還沒想出來其他更優雅的辦法來實現。
非遞歸實現
public static <V> void bfsNotRecursive(TreeNode<V> tree) {
if (tree != null) {
//跟上面一樣,使用 Map 也只是為了保存樹的深度,沒這個需要可以不用 Map
Queue<Map<TreeNode<V>, Integer>> queue = new ArrayDeque<>();
Map<TreeNode<V>, Integer> root = new HashMap<>();
root.put(tree, 0);
queue.offer(root);
while (!queue.isEmpty()) {
Map<TreeNode<V>, Integer> itemMap = queue.poll();
TreeNode<V> itemTreeNode = itemMap.keySet().iterator().next();
int depth = itemMap.get(itemTreeNode);
//打印節點值以及深度
System.out.println(itemTreeNode.getValue().toString() + ", " + depth);
if (itemTreeNode.getChildList() != null &&
!itemTreeNode.getChildList().isEmpty()) {
for (TreeNode<V> child : itemTreeNode.getChildList()) {
Map<TreeNode<V>, Integer> map = new HashMap<>();
map.put(child, depth + 1);
queue.offer(map);
}
}
}
}
}