图的广度优先遍历(BFS)
bfs总是先访问完同一层的结点,然后才继续访问下一层结点,如下图 从0 ->(1、2)-> (3、4、6) -> (5) 一层一层向距离起始点更远的顶点遍历。它最有用的性质是可以遍历一次就生成中心结点到所遍历结点的最短路径,这一点在求无权图的最短路径时非常有用。
scala 实现
下面的代码:
1 创建一个队列,遍历的起始点放入队列,新建一个 boolean 数组
2 从队列中取出一个元素,收集它,将其标记为已访问,并将其未访问过的子结点放到队列中
3 重复2,直至队列空
GraphBFS.scala
import com.datastructure.Graph
import scala.collection.mutable.{ArrayBuffer, Queue}
class GraphBFS {
private var G: Graph = _
private var visited: Array[Boolean] = _
private var order = ArrayBuffer[Int]()
def this(g: Graph) {
this()
val lenth = g.getV()
G = g
visited = Array.ofDim[Boolean](lenth)
for (i <- 0 until lenth) {
if (!visited(i)) {
bfs(i)
}
}
}
def bfs(s: Int) = {
val queue = Queue[Int]()
visited(s) = true
queue.enqueue(s)
while (!queue.isEmpty) {
val first = queue.dequeue()
order += first
for (w <- G.getAdjacentSide(first)) {
if (!visited(w)) {
queue.enqueue(w)
visited(w) = true
}
}
}
}
def bfsOrder(): Array[Int] = {
order.toArray
}
}
object GraphBFS {
def apply(g: Graph): GraphBFS = new GraphBFS(g)
def main(args: Array[String]): Unit = {
val g = Graph("./data/graph/g.txt")
val graphBFS = GraphBFS(g)
graphBFS.bfsOrder().foreach(println)
}
}
// 0
// 1
// 2
// 3
// 4
// 6
// 5
Graph.scala
import java.io.File
import java.util.Scanner
import scala.collection.mutable.TreeSet
class Graph {
var V = 0 // 顶点
var E = 0 // 边
var adj: Array[TreeSet[Int]] = _ // 红黑树数组
def this(filename: String) {
this()
val scanner = new Scanner(new File(filename))
V = scanner.nextInt()
adj = Array.ofDim[TreeSet[Int]](V)
for (i <- 0 until V) {
adj(i) = TreeSet.empty
}
E = scanner.nextInt()
for (i <- 0 until E) {
val a = scanner.nextInt()
val b = scanner.nextInt()
//println(s"$a + $b")
var as = adj(a)
//if (as == null) as = TreeSet.empty
as.add(b)
adj(a) = as
var bs = adj(b)
//if (bs == null) bs = TreeSet.empty
bs.add(a)
adj(b) = bs
}
}
override def toString: String = {
val sb = new StringBuffer()
for (i <- 0 until adj.length) {
sb.append(s"顶点 $i ->\t")
if (adj(i) != null) {
for (y <- adj(i)) {
sb.append(s"$y\t")
}
}
sb.append("\n")
}
sb.toString
}
/**
* 获得 图中顶点的个数
*
* @return
*/
def getV() = {
V
}
/**
* 获得 图中边的个数
*
* @return
*/
def getE() = {
E
}
/**
* 两个顶点间是否有边
*
* @param v
* @param w
* @return
*/
def hasEdge(v: Int, w: Int) = {
adj(v).contains(w)
}
/**
* 获取v的所有邻边
*
* @param v
* @return
*/
def getAdjacentSide(v: Int) = {
if (adj(v) != null) {
adj(v).toList
} else {
List.empty
}
}
}
object Graph {
def main(args: Array[String]): Unit = {
val graph = Graph("./data/graph/g.txt")
println(graph)
println("-------------------------")
println(s"顶点个数:${graph.getV()}")
println("-------------------------")
println(s"边的个数:${graph.getE()}")
println("-------------------------")
println(s"2的邻边:${graph.getAdjacentSide(2)}")
println("-------------------------")
println(s"1 和 2 是否有边:${graph.hasEdge(1, 2)}")
}
def apply(filename: String): Graph = new Graph(filename)
}
g.txt
7 7
0 1
0 2
1 3
1 4
2 3
2 6
5 6