Go版数据结构 -【6.2 深度优先搜索与广度优先搜索】

6.2 深度优先搜索与广度优先搜索

图是一个重要的数据结构,而遍历图的方式主要有两种:深度优先搜索广度优先搜索。这两种算法分别适合不同的场景。

本节我们将介绍 深度优先广度优先 搜索的基本概念、算法原理,并使用 Go 语言实现它们。

本节代码存放目录为 lesson13

概念与原理

深度优先搜索(DFS)

深度优先搜索 是一种沿着图的某一路径一直搜索到底的算法。当遇到某个顶点无法继续前进时,算法会回溯到上一个顶点,并尝试其他路径。

简单理解,DFS 就像是在迷宫中尽可能走到底,如果无法继续就回退,再走其他路径。

深度优先搜索算法步骤如下:

  1. 从起始顶点开始,访问该顶点并标记为已访问。

  2. 选择该顶点的一个未访问邻接顶点,递归访问该顶点。

  3. 如果当前顶点的所有邻接顶点都已访问,回退到前一个顶点,继续搜索未访问的顶点。

  4. 重复上述步骤,直到所有顶点都被访问。

DFS 示例如下

我们使用如下图结构示意 DFS 的遍历方式:

图结构:

	   A --- B
	   |     |
	   C --- D
	    \
	     E

从顶点 A 开始执行 DFS,遍历顺序为:

A -> B -> D -> C -> E
广度优先搜索(BFS)

广度优先搜索 是一种逐层搜索的算法,先访问某个顶点,然后再访问它的所有邻接顶点,接着依次访问这些邻接顶点的邻接顶点。

简单理解,BFS 就像是一层一层地涂满每个节点,不断扩展访问范围,直到所有节点都被访问。

广度优先搜索算法步骤如下:

  1. 从起始顶点开始,访问该顶点并标记为已访问。

  2. 将该顶点的所有未访问邻接顶点添加到队列中。

  3. 从队列中取出第一个顶点,访问并标记为已访问。

  4. 重复步骤 23,直到队列为空,即所有顶点都被访问。

BFS 示例如下:

同样使用如下图结构示意 BFS 的遍历方式:

图结构:

	   A --- B
	   |     |
	   C --- D
	    \
	     E

从顶点 A 开始执行 BFS,遍历顺序为:

A -> B -> C -> D -> E

总的来说,深度优先搜索就是沿着一条线一直搜到底,如果没有就回到上一个顶点接着再一直搜到底;而广度优先搜索则是全面的推进,一层一层的往下搜索。


深度优先搜索与广度优先搜索的区别如下

  • DFS 适用于:

    • 搜索深度未知的问题。

    • 需要找到所有路径或所有解的场景。

  • BFS 适用于:

    • 搜索广度未知的问题。

    • 需要找到最短路径或最优解的场景。

DFS 是深度优先,所以可能先深入图的某条路径后再回溯;BFS 是逐层搜索,所以能够保证先找到最短路径。

Go 语言的实现

深度优先搜索

Go 语言中,我们可以使用递归来实现 DFS

// Graph 定义图结构,使用邻接表
type Graph struct {
	// 图结构
	vertices map[string][]string
	// 已访问的节点
	visited map[string]bool
}

// NewGraph 创建一个新的图
func NewGraph() *Graph {
	return &Graph{
		vertices: make(map[string][]string),
		visited:  make(map[string]bool),
	}
}

// AddEdge 添加五向边
func (g *Graph) AddEdge(v1, v2 string) {
	g.vertices[v1] = append(g.vertices[v1], v2)
	g.vertices[v2] = append(g.vertices[v2], v1)
}

// DFS 实现深度优先搜索
func (g *Graph) DFS(v string) {
	// 标记为已访问
	g.visited[v] = true
	fmt.Printf("%s", v)

	// 递归访问所有未访问的节点
	for _, neighbor := range g.vertices[v] {
		// 未访问过
		if !g.visited[neighbor] {
			g.DFS(neighbor)
		}
	}
}

// ClearDFS 清除搜索标记
func (g *Graph) ClearDFS() {
	g.visited = make(map[string]bool)
}

func main() {
	// 创建图
	graph := NewGraph()
	graph.AddEdge("A", "B")
	graph.AddEdge("A", "C")
	graph.AddEdge("B", "D")
	graph.AddEdge("C", "E")
	graph.AddEdge("D", "C")
	graph.AddEdge("E", "A")

	fmt.Println("深度优先搜索结果:")
	// 从 A 开始深度优先搜索
	graph.DFS("A")
	graph.ClearDFS()
	fmt.Println()
	// 从 B 开始深度搜索
	graph.DFS("B")
}

执行结果输出如下所示:

深度优先搜索结果:
ABDCE
BACED

广度优先搜索

Go 语言中,我们可以使用队列来实现 BFS

// Graph 定义图结构,使用邻接表
type Graph struct {
	// 图结构
	vertices map[string][]string
}

// NewGraph 创建一个新的图
func NewGraph() *Graph {
	return &Graph{
		vertices: make(map[string][]string),
	}
}

// AddEdge 添加五向边
func (g *Graph) AddEdge(v1, v2 string) {
	g.vertices[v1] = append(g.vertices[v1], v2)
	g.vertices[v2] = append(g.vertices[v2], v1)
}

// BFS 实现广度优先搜索
func (g *Graph) BFS(start string) {
	// 创建队列
	queue := list.New()
	// 存储访问过的节点
	visited := make(map[string]bool)

	// 初始化队列,将起始节点入队
	queue.PushBack(start)
	visited[start] = true

	// 循环处理队列中的节点
	for queue.Len() > 0 {
		// 出队
		element := queue.Front()
		v := element.Value.(string)
		queue.Remove(element)
		fmt.Printf("%s ", v)

		// 访问邻接节点
		for _, neighbor := range g.vertices[v] {
			if !visited[neighbor] {
				queue.PushBack(neighbor)
				visited[neighbor] = true
			}
		}
	}
}

func main() {
	// 创建图
	graph := NewGraph()
	graph.AddEdge("A", "B")
	graph.AddEdge("A", "C")
	graph.AddEdge("B", "D")
	graph.AddEdge("C", "E")
	graph.AddEdge("D", "C")
	graph.AddEdge("E", "A")

	fmt.Println("广度优先搜索结果:")
	// 从 A 开始广度优先搜索
	graph.BFS("A")
	fmt.Println()
	// 从 B 开始广度优先搜索
	graph.BFS("B")
}

执行结果输出如下所示:

广度优先搜索结果:
A B C E D 
B A D C E

小结

本节我们针对深度优先搜索、广度优先搜索的基本概念以及Go语言的实现进行了讲解。

关于本节总结如下:

  • 深度优先搜索采用递归方式深入图的某条路径,适合用于找到所有解或路径。

  • 广度优先搜索采用队列逐层搜索,适合用于找到最优解或最短路径。


我的GitHub:https://github.com/swxctx

书籍地址:https://gs.golang.website/

书籍代码:https://github.com/YouCanGolang/GoStructedCode

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Swxctx

您的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值