BFS
概念
BFS,即 广度优先搜索。
有的小朋友就问了:“哥哥哥哥,我就是想画画,你能不能讲人话?”
说人话就是,我们按照这个步骤来做——
实现
这里就要介绍一下 队列,因为 广度优先搜索 和 队列 是好基友。
什么是队列?就是一个先进先出的数组,和我们日常生活中的排队很像。当我们向队列插入一个新数的时候,它插在最后,当我们取出一个数的时候,要从头取。就像小朋友刚刚买的画笔或者画板或者颜料,都是要排队的(假设没有网购,不许杠!)。
补充——关于在 Python 中使用队列
在 Python 中,可以使用以下几种方法实现队列
collections包里的deque,对应操作 pop()从尾取出 appendleft() 从头插入 queue包中的queue,对应操作 put() 插入 get() 取出 直接使用list,只要保证只使用 pop() 取出 insert(0,) 插入 或者只使用 append() 插入 list[0]并且del list[0] 取出 两者使用list方法的不同就区别于你把哪个当头,哪个当尾
三种方法比较
第一种是正统的Python的双端队列,缺点是调用的函数有点复杂,可能一不小心写了append,就不对了。
第二种使用封装的函数很直接,put()和get()不容易搞混淆。但是queue类型其实里面本身就装了一个deque,有点脱裤子放X的感觉。
第三种优势在于不用调包,但是函数使用逻辑可能造成混淆。在这里,完整版代码采用第二种,好理解,精简版代码采用第三种,省行数。三种方式可以按照你的喜好互相替换,完全不影响结果。
这时候小朋友又问了:“叔叔叔叔,为什么 广度优先搜索 和 队列 能勾搭到一块儿?”
分析
我们可以这样利用 队列 实现 广度优先搜索。
我们设置一个队列,先把初始点添加进去
规定每次从队列取出一个坐标
对这个坐标染色,并且把这个坐标的邻居(符合要求且不重复的好邻居),放到队列中。
当这个队列为空的时候,说明染色完成
因为队列每次取出的是最后的,而每次添加的是放在最前面,所以可以想象到,每次先处理的都是层级最少的,最接近初始点的,然后慢慢扩大,这样就实现了 广度优先搜索。
实例
题目
有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。
为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。
最后返回经过上色渲染后的图像。
思路
首先找到初始节点,给它染色,这个初始节点当作第一层。
找到初始节点周围四个节点,给它们染色(符合条件的才能染),这四个节点当作第二层。
再找到这四个节点周围八个节点,给它们染色,这八个节点当作第三层。
重复以往,层层递进,直到找不到符合要求的节点。
思路很好理解对吧,就是一个从中间向外扩散的过程。可是怎么实现呢?现在给您键盘,恐怕还写不出。
代码
class Solution: def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]: if newColor == image[sr][sc]:return image que, old, = [(sr, sc)], image[sr][sc] while que: point = que.pop() image[point[0]][point[1]] = newColor for new_i, new_j in zip((point[0], point[0], point[0] + 1, point[0] - 1), (point[1] + 1, point[1] - 1, point[1], point[1])): if 0 <= new_i < len(image) and 0 <= new_j < len(image[0]) and image[new_i][new_j] == old: que.insert(0,(new_i,new_j)) return image
DFS
概念
DFS,即 深度优先搜索。
小朋友抢着说:“从你的 BFS 里,我好想猜到了 DFS 是什么了,它是不是——”
思路
先定个四个方向的顺序,例如上下左右,先上后下后左最后右
首先找到初始节点,给它染色。
按照方向的顺序,这里是上,就先把这个点的上方点先染色。
一直往上一直往上,直到不符合要求,便退一步,再找这个点的下方向
重复这个步骤。
换句话说,先把这个点上方的都弄完,再把这个点下边的都弄完,再左边的,最后下边的。
思路很好理解对吧,就是一个从中间向一个方向深入的过程。可是怎么实现呢?现在给您键盘,恐怕还写不出。
实现
还记得标题写的两个方法,三种实现吗?
这是因为 DFS 通常有两种实现方法,一种是递归,另一种是使用栈。
这里就要介绍一下栈,因为深度优先搜索和栈是好基友。
什么是栈?就是一个后进先出的数组,和我们日常生活中的插队很像。当我们向栈插入一个新数的时候,它插在最前面,当我们取出一个数的时候,要从头取。就像小朋友插队去买画笔,他不排队,直接插到第一个位置,下一个服务的就是它。
补充——关于在Python中使用栈
直接使用list即可,使用它的这两个方法
pop() append()
这时候小朋友又问了:“叔叔叔叔,为什么广度优先搜索和堆栈能勾搭到一块儿?”
我们可以这样利用堆栈实现深度优先搜索。
分析
我们设置一个栈,先把初始点添加进去
规定每次从栈中取出一个坐标
对这个坐标染色,并且把这个坐标的一个方向上的邻居(符合要求且不重复的好邻居),放到栈中。
当这个方向没有复合要求的邻居的时候,进入下一个方向
当这个栈为空的时候,说明染色完成
因为栈每次取出的是最后的,而每次添加的也在最后,所以可以想象到,每次先处理的都是最深的,然后慢慢扩大,这样就实现了深度优先搜索。
实例
题目同上方BFS,这里不再过多赘述
思路
先定个四个方向的顺序,例如上下左右,先上后下后左最后右
首先找到初始节点,给它染色。
按照方向的顺序,这里是上,就先把这个点的上方点先染色。
一直往上一直往上,直到不符合要求,便退一步,再找这个点的下方向
重复这个步骤。
换句话说,先把这个点上方的都弄完,再把这个点下边的都弄完,再左边的,最后下边的。
思路很好理解对吧,就是一个从中间向一个方向深入的过程。可是怎么实现呢?现在给您键盘,恐怕还写不出。
代码
一、使用栈
class Solution: def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]: if newColor == image[sr][sc]: return image stack, old = [(sr, sc)], image[sr][sc] while stack: point = stack.pop() image[point[0]][point[1]] = newColor for new_i, new_j in zip((point[0], point[0], point[0] + 1, point[0] - 1), (point[1] + 1, point[1] - 1, point[1], point[1])): if 0 <= new_i < len(image) and 0 <= new_j < len(image[0]) and image[new_i][new_j] == old: stack.append((new_i, new_j)) return image
二、使用递归
class Solution: def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -> List[List[int]]: if image[sr][sc] != newColor: old, image[sr][sc] = image[sr][sc], newColor for i, j in zip((sr, sr+1, sr, sr-1), (sc+1, sc, sc-1, sc)): if 0 <= i < len(image) and 0 <= j < len(image[0]) and image[i][j] == old: self.floodFill(image, i, j, newColor) return image