0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

グラフ探索の基本概念

グラフ探索の目的は以下のような課題を解決することにあります。

  • パス探索
    • あるノードから別のノードへの経路を見つける
  • 到達可能性判定
    • 特定のノードが別のノードに到達可能か確認する
  • 最短経路探索
    • 2つのノード間の最短経路を発見する
  • 連結性の確認
    • グラフ全体が一つの塊(連結グラフ)であるか調べる

主要なグラフ探索アルゴリズム

1. 深さ優先探索 (Depth First Search, DFS)

DFSはスタック(または再帰)を用いてグラフを探索します。探索は可能な限り深く進み、行き止まりに到達した場合は引き返して次の道を探します。

Goサンプルコード

package main

import "fmt"

func dfs(graph map[int][]int, node int, visited map[int]bool) {
    // 現在のノードを訪問済みとする
    visited[node] = true
    fmt.Println(node)

    // 隣接ノードを再帰的に探索
    for _, neighbor := range graph[node] {
        if !visited[neighbor] {
            dfs(graph, neighbor, visited)
        }
    }
}

func main() {
    // グラフの表現(隣接リスト)
    graph := map[int][]int{
        0: {1, 2},
        1: {0, 3, 4},
        2: {0, 4},
        3: {1},
        4: {1, 2},
    }

    // 訪問状態を保持するマップ
    visited := make(map[int]bool)
    dfs(graph, 0, visited)
}
0
1
3
4
2

2. 幅優先探索 (Breadth First Search, BFS)

BFSはキューを用いてグラフを探索します。探索は現在の階層をすべて探索してから次の階層に進みます。

Goサンプルコード

package main

import "fmt"

func bfs(graph map[int][]int, start int) {
	// 訪問状態を保持するマップ
	visited := make(map[int]bool)

	// キューに開始ノードを追加
	queue := []int{start}
	visited[start] = true

	for len(queue) > 0 {
		// キューから現在のノードを取得
		current := queue[0]
		queue = queue[1:]
		fmt.Println(current)

		// 隣接ノードを探索
		for _, neighbor := range graph[current] {
			if !visited[neighbor] {
				visited[neighbor] = true
				queue = append(queue, neighbor)
			}
		}
	}
}

func main() {
	// グラフの表現(隣接リスト)
	graph := map[int][]int{
		0: {1, 2, 5},
		1: {0, 3, 4},
		2: {0, 4},
		3: {1},
		4: {1, 2},
		5: {0, 6},
		6: {5, 7},
		7: {6},
	}

	bfs(graph, 0)
}
0
1
2
5
3
4
6
7

3. ダイクストラ法

ダイクストラ法は、(負ではない)重みを持つグラフにおける最短経路を探索するアルゴリズムです。

Goサンプルコード

package main

import (
	"container/heap"
	"fmt"
	"math"
)

type Edge struct {
	to     string
	weight int
}

type Node struct {
	id   string
	dist int
}

// PriorityQueue implements heap.Interface
type PriorityQueue []Node

func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
	return pq[i].dist < pq[j].dist
}
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) {
	*pq = append(*pq, x.(Node))
}
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	*pq = old[0 : n-1]
	return item
}

func dijkstra(graph map[string][]Edge, start string) map[string]int {
	dist := make(map[string]int)
	for node := range graph {
		dist[node] = math.MaxInt
	}
	dist[start] = 0

	pq := &PriorityQueue{}
	heap.Push(pq, Node{start, 0})

	for pq.Len() > 0 {
		current := heap.Pop(pq).(Node)

		for _, edge := range graph[current.id] {
			newDist := dist[current.id] + edge.weight
			if newDist < dist[edge.to] {
				dist[edge.to] = newDist
				heap.Push(pq, Node{edge.to, newDist})
			}
		}
	}
	return dist
}

func main() {
	// グラフの表現(隣接リスト)
	graph := map[string][]Edge{
		"A": {{"B", 1}, {"C", 4}},
		"B": {{"A", 1}, {"C", 2}, {"D", 5}},
		"C": {{"A", 4}, {"B", 2}, {"D", 1}},
		"D": {{"B", 5}, {"C", 1}},
	}

	start := "A"
	distances := dijkstra(graph, start)

	for node, dist := range distances {
		fmt.Printf("Node %s: Distance from start = %d\n", node, dist)
	}
}
Node A: Distance from start = 0
Node B: Distance from start = 1
Node C: Distance from start = 3
Node D: Distance from start = 4
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?