寻找制高点
- 前言
- 广度优先遍历算法在搜索答案时不像深度优先那样一条路走到底,而是采用由近及远的方式,先访问离起始点最近的所有点,再访问远一些的点。由于这种一层层搜索的策略,又叫做层次遍历算法。
- 简介
- 比较基础的BFS问题,但是是很多算法题的模板。
- 问题描述
- 现在,有一个矩阵,矩阵的每个值代表山的高度(均大于1),现在要求找到这个山的所有制高点。
- 制高点指的是通过这个点可以从上下左右四个边界走出去这个矩阵平面,注意,是四个边界都能走出去。
- 在整个平面的移动规则是从一个点只可以向上下左右四个方向走,并且只能走到不大于自己的值的位置上去。
- 问题分析
- 这是一个典型的搜索问题,一开始的思路一定是从每个位置出发搜索其能否到达四个边缘。很显然,对矩阵的每个值都要进行这样的搜索,显然,这样有些费时了。(如果在LeetCode上,这就是Exceed Time Limit报错)
- 那么,如何优化算法呢?
- 这条思路不行,那么就换一个思路处理问题,既然从每个点走向边缘的搜索有些低效,那么不妨以边缘的所有点为起点向内部进行搜索,看下一个节点是否不小于自己的值(这和下山的规则相反)。
- 标记能够到达的点为True,继续搜索,直到不能走为止。按照这样的思路,标记四个边界可以到达的点,都为True的点为制高点。(也就是四个集合的交集)
- 这就是典型的逆向思维,算法中经常要求这种逆向思维。
- 为了获取每个点,定义每个点的坐标为四个集合的记录值。
- 代码
def bfs(set,m,n,matrix): #queue为队列
dir = [[0, 1], [0, -1], [1, 0], [-1, 0]] # 数组中四个元素代表四个方向
queue = list(set) # list()函数创建queue为set的数列版本
while len(queue) > 0:
x, y = queue.pop() #取出队头的元素,由于是一个坐标,我们使用两个变量来接收
for d in dir: #循环遍历4个方向
nx = x + d[0]
ny = y + d[1]
if 0 <= nx and nx < m and 0 <= ny and ny < n: # 如果新的点在二位数组内
if matrix[nx][ny] >= matrix[x][y]: # 如果新的点比原点高则可以走
if (nx, ny) not in set:
queue.append((nx, ny))
set.add((nx, ny))
def solve(matrix): #其中matrix就是存储地图的二维数组
if not matrix:
return matrix #如果二维数组为空那么就返回空
m = len(matrix) # 表示二维数组有多少行
n = len(matrix[0]) # 表示二维数组有多少列
topPoint = set([(0, y) for y in range(n)]) # 上边的点横坐标为0,纵坐标为0到n-1
leftPoint = set([(x, 0) for x in range(m)]) # 左边的点横坐标为0到m-1,纵坐标为0
bottomPoint = set([(m - 1, y) for y in range(n)]) #下边的点横坐标为m-1,纵坐标为0到n-1
rightPoint = set([(x, n - 1) for x in range(m)]) # 右边的点横坐标为0到m-1,纵坐标为n-1
bfs(topPoint, m, n, matrix) # 依次调用4次bfs
bfs(leftPoint, m, n, matrix)
bfs(bottomPoint, m, n, matrix)
bfs(rightPoint, m, n, matrix)
result = topPoint & leftPoint & bottomPoint & rightPoint #求集合的交集
return result
matrix=[[1, 3, 2, 3, 5], #测试程序
[3, 4, 5, 6, 3],
[2, 7, 4, 3, 3],
[5, 2, 2, 3, 1]]
s=solve(matrix)
print(s)
#=====================
#{(1, 2), (1, 3), (2, 1)}
广度优先遍历本质上是查找算法,顺序查找,二分查找,哈希查找都是为了解决查找问题。广度优先遍历更多针对图的搜索,除此之外,广度优先搜索可以解决单一起点的最短路径问题。