树与BFS
上一章我们了解了搜索树,并想办法把他给深度优先遍历了。今天就来讲BFS,即宽度优先搜索。
如下图:
其编号就是BFS遍历的顺序——就是按层遍历。BFS将依次遍历每一层,层层推进。这有什么好处呢?如果题目要求输出一个解,就意味着找到解就退出。如果我们估计这个解的节点的层次不很深,但是各节点的子节点都很多,那么BFS在时间上显然可以占优——DFS会一直深入下去,导致大量的浪费。此外,对于求一系列状态的最优解(要求不断更新)的问题(比如单源最短路径或动态规划),BFS可以尽可能大地将子问题重叠处理,效率较DFS高相当多:DFS先解决新问题再解决老问题,但是有相当多的问题是这样的:新问题由若干老问题决定。宽搜将老问题解决后再解决新问题,可以做到一锤定音(想想拓扑排序),而深搜则需要反复迭代。
BFS的致命弱点是空间问题。我们对于每个状态都要保存,如此根本无法解决状态过复杂的问题。究其原因,其遍历顺序是不连续的,比如我做完节点5就去做节点6,而没有什么直接的措施可以将节点5的状态转换为节点6的状态——DFS则不然,他会从5到2再到6,沿着边用全局变量走得很爽。
因此,BFS的价值在于充分利用空间而节省时间。在时间金贵的NOIP中,BFS还是很吃香的。
BFS的具体实现:用队列。就是说,队首碰到新状态要做了,扔队尾去,到时候再说。
实现如下:
t1,t2:integer;{t1表示队首,t2表示队尾}
queue:array[1..1000]of node{各题表示状态不一,此处用node表示}
t1=1;
t2=1;
queue:={根节点}
repeat
u=queue[t1];{队首元素出队}
inc(t1);
{扩展u,扩产出若干节点,扔t2那头}
until t1=t2;{队列为空,做完了}
注意:多结合哈希表!BFS入门请寻找八数码问题或某年IOI的“魔版”,其中八数码去了解下康托展开。