思路有二
- 深度优先搜索(depth first search,简称DFS)
- 广度优先搜索(breadth first search,简称BFS),又叫宽度优先搜索
解法一
深度优先搜索基于栈。
之所以将结构类型命名为Earth是因为如果命名为Node或Point可能和做题网站的内部常用类型名冲突。
注意Earth中的I和J,根据矩阵的行列个数对它们的类型做了优化,没有使用int。
行和列个数都在200以内(牛客 <=200),可以用uint8。
行和列个数都在300以内(力扣 <=300),可以用uint16。
import "container/list"
func numIslands(grid [][]byte) int {
var c int // 记录岛屿个数
for i, row := range grid {
for j, n := range row {
if n == '1' {
c++ // 每次dfs都加1
dfs(grid, i, j)
}
}
}
return c
}
type Earth struct {
I uint16 // row
J uint16 // col
}
// 深度优先搜索
// 每次调用dfs,将格子grid[i][j]周围连通的一片'1'都找到了
func dfs(grid [][]byte, i, j int) {
rows := uint16(len(grid))
cols := uint16(len(grid[0]))
sk := NewStack()
sk.Push(Earth{I:uint16(i), J:uint16(j)})
grid[i][j] = '0' // 置'0'就相当于记录该格子已经访问过了
for !sk.IsEmpty() {
p := sk.Peek().(Earth)
sk.Pop()
// right
if p.J+1 < cols && grid[p.I][p.J+1] == '1' { // 如果没访问过,就加入搜索集
sk.Push(Earth{I:uint16(p.I), J:uint16(p.J+1)})
grid[p.I][p.J+1] = '0'
}
// left
if p.J >= 1 && grid[p.I][p.J-1] == '1' {
sk.Push(Earth{I:uint16(p.I), J:uint16(p.J-1)})
grid[p.I][p.J-1] = '0'
}
// up
if p.I >= 1 && grid[p.I-1][p.J] == '1' {
sk.Push(Earth{I:uint16(p.I)-1, J:uint16(p.J)})
grid[p.I-1][p.J] = '0'
}
// down
if p.I+1 < rows && grid[p.I+1][p.J] == '1' {
sk.Push(Earth{I:uint16(p.I)+1, J:uint16(p.J)})
grid[p.I+1][p.J] = '0'
}
}
}
// 栈实现
type Stack struct {
l *list.List
}
func NewStack() *Stack {
return &Stack{
l: list.New(),
}
}
func (o *Stack) Push(v interface{}) {
o.l.PushBack(v)
}
func (o *Stack) Peek() interface{} {
if o.IsEmpty() {
panic("Stack is empty")
}
return o.l.Back().Value
}
func (o *Stack) Pop() {
if o.IsEmpty() {
panic("Stack is empty")
}
o.l.Remove(o.l.Back())
}
func (o *Stack) IsEmpty() bool {
return o.l.Len() == 0
}
func (o *Stack) Len() int {
return o.l.Len()
}
解法二
广度优先搜索基于队列。
func numIslands(grid [][]byte) int {
var c int // 记录岛屿个数
for i, row := range grid {
for j, n := range row {
if n == '1' {
c++ // 每次搜索加1
bfs(grid, i, j)
}
}
}
return c
}
type Earth struct {
I uint16 // row
J uint16 // col
}
// 广度优先搜索
// 每次调用bfs,将格子grid[i][j]周围连通的一片'1'都找到了
func bfs(grid [][]byte, i, j int) {
rows := uint16(len(grid))
cols := uint16(len(grid[0]))
q := NewQueue()
q.Push(Earth{I:uint16(i), J:uint16(j)})
grid[i][j] = '0' // 标记格子已访问过
for !q.IsEmpty() {
p := q.Pop().(Earth)
// right
if p.J+1 < cols && grid[p.I][p.J+1] == '1' { // 如果没访问过,就加入搜索集
q.Push(Earth{I:uint16(p.I), J:uint16(p.J+1)})
grid[p.I][p.J+1] = '0'
}
// left
if p.J >= 1 && grid[p.I][p.J-1] == '1' {
q.Push(Earth{I:uint16(p.I), J:uint16(p.J-1)})
grid[p.I][p.J-1] = '0'
}
// up
if p.I >= 1 && grid[p.I-1][p.J] == '1' {
q.Push(Earth{I:uint16(p.I-1), J:uint16(p.J)})
grid[p.I-1][p.J] = '0'
}
// down
if p.I+1 < rows && grid[p.I+1][p.J] == '1' {
q.Push(Earth{I:uint16(p.I+1), J:uint16(p.J)})
grid[p.I+1][p.J] = '0'
}
}
}
// 队列实现
type Queue struct {
l *list.List
}
func NewQueue() *Queue {
return &Queue{
l: list.New(),
}
}
func (o *Queue) Push(v interface{}) {
o.l.PushBack(v)
}
func (o *Queue) Pop() interface{} {
if o.IsEmpty() {
panic("Queue is empty")
}
e := o.l.Front()
o.l.Remove(e)
return e.Value
}
func (o *Queue) Front() interface{} {
if o.IsEmpty() {
panic("Queue is empty")
}
return o.l.Front().Value
}
func (o *Queue) Back() interface{} {
if o.IsEmpty() {
panic("Queue is empty")
}
return o.l.Back().Value
}
func (o *Queue) IsEmpty() bool {
return o.l.Len() == 0
}
func (o *Queue) Len() int {
return o.l.Len()
}