BFS广度优先遍历和DFS深度优先遍历
广度优先搜索(Breadth First Search,简称BFS)
假设从A节点出发,在访问了该节点之后依次访问这个节点各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点。重复此步骤,直到所有的节点都被访问完为止。
如上图,广度优先搜索的顺序是:ABCDEFG
深度优先搜索(Depth First Search,简称DFS)
假设从A节点出发,首先访问该节点,然后依次从它的各个未被访问的邻接点出发,访问邻接点的邻接点。重复此步骤,直到所有的节点都被访问完为止。
如上图,深度优先搜索的顺序是:ABDECFG
实战
现在以一个实际的例子用C#代码来实现,下面是Unity中的一个Hierarchy层级图,根节点是Root,下面有GameObject,GameObject (1), GameObject (2)三个节点,其中GameObject (1)下又有GameObject_A和GameObject_B两个节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
| using System.Collections.Generic; using UnityEngine;
public class DM06Composite : MonoBehaviour { void Start() { DMComposite root = new DMComposite("Root"); DMLeaf leaf1 = new DMLeaf("GameObject"); DMLeaf leaf2 = new DMLeaf("GameObject (2)"); DMComposite gameObject1 = new DMComposite("GameObject (1)"); root.AddChild(leaf1); root.AddChild(gameObject1); root.AddChild(leaf2); DMLeaf child1 = new DMLeaf("GameObject_A"); DMLeaf child2 = new DMLeaf("GameObject_B"); gameObject1.AddChild(child1); gameObject1.AddChild(child2);
BreadthFirstSearch(root); }
private void BreadthFirstSearch(DMComponent component) { Queue<DMComponent> q = new Queue<DMComponent>(); q.Enqueue(component); Debug.Log(component.Name); while (q.Count > 0) { DMComponent temp = q.Dequeue(); List<DMComponent> children = temp.Children; foreach (DMComponent child in children) { Debug.Log(child.Name); q.Enqueue(child); } } }
private void DepthFirstSearch(DMComponent component) { Debug.Log(component.Name); List<DMComponent> children = component.Children; if (children == null || children.Count == 0) return; foreach (DMComponent child in children) { DepthFirstSearch(child); } } }
public abstract class DMComponent { protected string mName; public string Name { get { return mName; } } public DMComponent(string name) { mName = name; mChildren = new List<DMComponent>(); } protected List<DMComponent> mChildren; public List<DMComponent> Children { get { return mChildren; } } public abstract void AddChild(DMComponent c); public abstract void RemoveChild(DMComponent c); public abstract DMComponent GetChild(int index); }
public class DMLeaf : DMComponent { public DMLeaf(string name) : base(name) { }
public override void AddChild(DMComponent c) { return; }
public override void RemoveChild(DMComponent c) { return; }
public override DMComponent GetChild(int index) { return null; } }
public class DMComposite : DMComponent { public DMComposite(string name) : base(name) { }
public override void AddChild(DMComponent c) { mChildren.Add(c); }
public override void RemoveChild(DMComponent c) { mChildren.Remove(c); }
public override DMComponent GetChild(int index) { return mChildren[index]; } }
|
广度优先搜索:
深度优先搜索:
解析
上面的代码稍微有点复杂,下面详细的解释一下,首先,使用设计模式中的组合模式:组合多个对象形成树形结构,以表示具有“整体-部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。
抽象组件类(Component):它可以是接口或抽象类,为叶子组件和容器组件对象声明接口,在该角色中可以包含所有子类共有行为的声明和实现。在抽象组件类中,定义了访问及管理它的子组件的方法;
叶子类(Leaf):在组合模式中,表示叶子节点对象,叶子节点没有子节点,实现了在抽象组件类中定义的行为。
容器类(Composite):在组合模式中,表示容器节点对象,容器节点包含子节点,其子节点可以叶子节点,也可以是容器节点,提供了一个集合用于存储子节点,实现了在抽象组件类中定义的行为,包括访问及管理子组件的方法。
使用组合模式的目的是为了组装上面的层次结构,建立好层次结构后,就可以进行检索了。
广度优先搜索实现的核心思想:
建立一个队列Queue,利用它的先进先出原则,把根节点放入队列中,在一个while循环中,检索根节点的所有子节点,把根节点从队列中移除,然后把子节点放入队列中,检索子节点的所有子节点,依次循环执行。
深度优先搜索实现的核心思想:
首先遍历根节点的所有子节点,然后使用递归,依次遍历子节点下面的子节点。
ref:
https://zhuanlan.zhihu.com/p/349093576