需要说明一点,要成功运行本贴代码,需要重新复制我第一篇随笔《简单的循环队列》代码(版本有更新)。
进入今天的主题。
今天这篇文章主要探讨广度优先搜索(BFS)结合队列和深度优先搜索(DFS)结合栈的示例应用。
一、队列和 BFS
众所周知,广度优先搜索(BFS)最为广泛使用场景是找出从根结点到目标结点的最短路径,而
结点的处理顺序与添加到队列的顺序是完全相同的顺序,即先进先出(FIFO)。
这就是我们在 BFS 中使用队列的原因。
二、栈和DFS
深度优先搜索(DFS),与 BFS 类似,也可用于查找从根结点到目标结点的路径。(请记住,是路径,但不是最短路径)
当我们到达最深的结点 时,我们需要回溯。当我们回溯时,我们将从栈中弹出最深结点
,这实际上是推入到栈中的最后一个结点
。
结点的处理顺序是完全相反的顺序
,就像是被添加
到栈中一样,它是后进先出(LIFO)。这就是我们在 DFS 中使用栈的原因。
以上分别说明了使用在BFS中使用队列与在DFS中使用栈的原因,下面展示一示例说明其应用。
例:给定一个由 land
(陆地)和 sea
(海水)组成的的二维网格,计算岛屿的数量。一个岛被水包围,并且它是通过水平方向或垂直方向上相邻的陆地连接而成的。你可以假设网格的四个边均被水包围
设计思路:通过BFS找出所有的land存入哈希表中,然后对其排序,最后通过DFS递归完成岛屿的统计。(其实本题不用这么复杂,直接递归即可,但为了阐述BFS和DFS,带此目的找了这个例题一起应用求解)。
测试用例为:
iSea iSea iSea iSea iSea iSea iSea
iSea Land iSea iSea Land iSea iSea
iSea Land Land iSea iSea iSea iSea
iSea iSea iSea Land iSea iSea iSea
iSea Land iSea Land Land Land iSea
iSea iSea iSea iSea iSea iSea iSea
岛屿的数量为:4
贴上源码(如只是阅读只看Main应该是可以理解):
1. IConnectable可连接器
package com.chengcai.util;
/**
* Created by chengcai on 2019/3/14.
*/
public interface IConnectable<T> {
Connector<T> connector();
}
package com.chengcai.util;
/**
* Created by chengcai on 2019/3/14.
*/
public class Connector<T> {
public boolean isConnect=false;
public Connector()
{
isConnect=true;
}
public boolean hasNext()
{
return isConnect;
}
}
package com.chengcai.util;
/**
* Created by chengcai on 2019/3/18.
*/
public interface Ilivable {
}
2.抽象地表对象AbstractEarthSurface
package com.chengcai.util;
import java.util.ArrayList;
import java.util.List;
/**
* Created by chengcai on 2019/3/18.
*/
public abstract class AbstractEarthSurface {
public static int HashCode=1;
private int icon=0;
private AbstractEarthSurface right=null;
private AbstractEarthSurface down=null;
private AbstractEarthSurface left=null;
private AbstractEarthSurface up=null;
int row=0;
int col=0;
private String name;
private List<AbstractEarthSurface> list=new ArrayList<AbstractEarthSurface>();
public AbstractEarthSurface getRight()
{
return right;
}
public AbstractEarthSurface getDown()
{
return down;
}
public AbstractEarthSurface getLeft(){return left;}
public AbstractEarthSurface getUp(){return up;}
public String getName()
{
return name;
}
public int getIcon(){return icon;}
public void setIcon(int s){
icon=s;
}
public int getRow() {
return row;
}
public int getCol(){
return col;
}
public void setRow(int r){
row=r;
}
public void setCol(int c){
col=c;
}
public void setRight(AbstractEarthSurface r)
{
if(r==null)
return;
list.add(r);
right=r;
}
public void setDown(AbstractEarthSurface d)
{
if (d==null)
return;
list.add(d);
down=d;
}
public void setLeft(AbstractEarthSurface l)
{
if(l==null)
return;
left=l;
}
public void setUp(AbstractEarthSurface u)
{
if(u==null)
return;
up=u;
}
public List<AbstractEarthSurface> getList()
{
return list;
}
}
3.定义统一对外操作对象Map,分别可操作sea,land,island对象
package com.chengcai.util;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
/**
* Created by chengcai on 2019/3/14.
*/
public class Map
{
private AbstractEarthSurface[][] map = null;
private island ild=null;
public Map() {
}
public AbstractEarthSurface getLand() {
return new Land();
}
public AbstractEarthSurface getSea() {
return new Sea();
}
public island getIsland()
{
if (ild==null) {
ild= new island();
}
return ild;
}
public void setMap(AbstractEarthSurface[][] imap)
{
map=imap;
}
public AbstractEarthSurface[][] getMap()
{
return this.map;
}
public class Land extends AbstractEarthSurface implements IConnectable
{
private int icon=1;
public Land()
{
}
public Connector connector()
{
return new Connector();
}
@Override
public String getName()
{
return "Land";
}
@Override
public void setIcon(int s)
{
icon=s;
}
@Override
public int getIcon()
{
return icon;
}
}
public class Sea extends AbstractEarthSurface
{
private int icon=0;
public Sea()
{
}
@Override
public String getName()
{
return "iSea";
}
@Override
public void setIcon(int s)
{
icon=s;
}
@Override
public int getIcon()
{
return icon;
}
}
public class island implements Ilivable
{
Hashtable<Integer,AbstractEarthSurface> islandList=null;
Hashtable<String,AbstractEarthSurface> coordinate=null;
List<AbstractEarthSurface> tempList=null;
public island()
{
islandList=new Hashtable<Integer,AbstractEarthSurface>();
tempList=new ArrayList();
}
public void addCoordinateHashTable(int r,int c,AbstractEarthSurface es)
{
if (coordinate==null)
coordinate=new Hashtable<String,AbstractEarthSurface>();
this.coordinate.put(Integer.toString(r)+Integer.toString(c),es);
}
public AbstractEarthSurface getEarthSurface(int r, int c)
{
return this.coordinate.get(Integer.toString(r)+Integer.toString(c));
}
public boolean isRowLastElement(String str,List<String> sls)
{
int index;
index=sls.indexOf(str)+1;
if(index>sls.size()-1)
return true;
return this.coordinate.get(str).getRow()< this.coordinate.get(sls.get(index)).getRow();
}
public Hashtable<String,AbstractEarthSurface> getCoordinateHashtable()
{
if (coordinate==null)
coordinate=new Hashtable<String,AbstractEarthSurface>();
return this.coordinate;
}
public void Put(Integer hash, AbstractEarthSurface es)
{
if (!(es instanceof IConnectable))
return;
this.islandList.put(hash,es);
}
public void remove()
{
this.islandList.remove(this);
}
public Hashtable getIsland()
{
return this.islandList;
}
public void Add(AbstractEarthSurface es)
{
if (!(es instanceof IConnectable))
return;
this.tempList.add(es);
}
public void ClearTemp()
{
if (tempList ==null )
return;
this.tempList.clear();
}
public List<AbstractEarthSurface> getTempList()
{
return this.tempList;
}
}
}
4.主函数
package com.chengcai;
import com.chengcai.util.*;
import com.chengcai.util.Map;
import java.util.*; public class Main {
//初始化测试用例
private static Map initMap()
{
Map map=new Map();
AbstractEarthSurface[]d1 ={map.getSea(),map.getSea(),map.getSea(),map.getSea(),
map.getSea(),map.getSea(),map.getSea()};
AbstractEarthSurface[]d2 ={map.getSea(),map.getLand(),map.getSea(),map.getSea(),map.getLand(),
map.getSea(),map.getSea()};
AbstractEarthSurface[]d3 ={map.getSea(),map.getLand(),map.getLand(),map.getSea(),
map.getSea(),map.getSea(),map.getSea()};
AbstractEarthSurface[]d4 ={map.getSea(),map.getSea(),map.getSea(),map.getLand(),
map.getSea(),map.getSea(),map.getSea()};
AbstractEarthSurface[]d5 ={map.getSea(),map.getLand(),map.getSea(),map.getLand(),
map.getLand(),map.getLand(),map.getSea()};
AbstractEarthSurface[]d6 ={map.getSea(),map.getSea(),map.getSea(),map.getSea(),
map.getSea(),map.getSea(),map.getSea()};
AbstractEarthSurface [][] data=new AbstractEarthSurface[6][7];
for (int i=0;i<6;i++)
{
for(int j=0;j<7;j++)
{
if (i==0) {
data[i][j] = d1[j];
data[i][j].setUp(null);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d1[j+1]);
data[i][j].setDown(d2[j]);
}
if (i==1) {
data[i][j] = d2[j];
data[i][j].setUp(data[i-1][j]);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d2[j+1]);
data[i][j].setDown(d3[j]);
}
if (i==2) {
data[i][j] = d3[j];
data[i][j].setUp(data[i-1][j]);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d3[j+1]);
data[i][j].setDown(d4[j]);
}
if (i==3) {
data[i][j] = d4[j];
data[i][j].setUp(data[i-1][j]);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d4[j+1]);
data[i][j].setDown(d5[j]);
}
if (i==4) {
data[i][j] = d5[j];
data[i][j].setUp(data[i-1][j]);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d5[j+1]);
data[i][j].setDown(d6[j]);
}
if (i==5) {
data[i][j] = d6[j];
data[i][j].setUp(data[i-1][j]);
if (j>0)
data[i][j].setLeft(data[i][j-1]);
if (j+1<7)
data[i][j].setRight(d6[j+1]);
data[i][j].setDown(null);
}
data[i][j].setRow(i);
data[i][j].setCol(j);
}
}
map.setMap(data);
return map;
}
private static int getIslandNumBFS(ICircleQueue q,Map map)
{
AbstractEarthSurface[][] data=map.getMap();
List<AbstractEarthSurface> cacheList=new ArrayList<AbstractEarthSurface>();
//用队列辅助实现广度优先查找所有Land
q.enQueue(data[0][0]);
while (!q.isEmpty())
{
int head=q.getQueueEntity().getHead();
AbstractEarthSurface queueFirstElement=(AbstractEarthSurface)q.getQueueEntity().getListCollection().get(head);
if (queueFirstElement instanceof IConnectable & !(map.getIsland().getCoordinateHashtable().containsValue(queueFirstElement))){
map.getIsland().addCoordinateHashTable(queueFirstElement.getRow(),queueFirstElement.getCol(),queueFirstElement);
}
for (AbstractEarthSurface obj:queueFirstElement.getList()) {
cacheList.add(obj);
}
Iterator<AbstractEarthSurface> it =cacheList.iterator();
while (it.hasNext())
{
if (q.enQueue(it.next()))
it.remove();
}
q.deQueue();
}
//对结果排序,便于AbstractEarthSurface.HashCode哈希值计算
Hashtable<String,AbstractEarthSurface> ht=map.getIsland().getCoordinateHashtable();
List<String> li=new ArrayList<String>(ht.keySet());
li.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
for (String str:li) {
if (ht.get(str).getIcon()==1)
{
AbstractEarthSurface.HashCode++;
getIslandConnected(ht.get(str));
map.getIsland().Put(AbstractEarthSurface.HashCode, ht.get(str));
}
}
return map.getIsland().getIsland().keySet().size();
}
//递归实现标识已访问的对象
private static void getIslandConnected(AbstractEarthSurface es)
{
if((es==null) || !(es instanceof IConnectable) || (es.getIcon()==3))
return;
es.setIcon(3);
getIslandConnected(es.getRight());
getIslandConnected(es.getLeft());
getIslandConnected(es.getUp());
getIslandConnected(es.getDown());
}
private static void printResult(Map map,int IslandCount)
{
AbstractEarthSurface[][] printMap=map.getMap();
StringBuilder sb =new StringBuilder();
for(int i=0;i<printMap.length;i++)
{
for (int j=0;j<printMap[0].length;j++)
{
sb.append(printMap[i][j].getName() +" ");
if (j==printMap[0].length-1)
sb.append("\n\r");
}
}
System.out.print("测试用例为:"+"\n\r");
System.out.print(sb.toString());
System.out.print("岛屿的数量为:"+ IslandCount +"\n\r");
}
public static void main(String[] args) {
Map map=null;
int islandNums=0;
ICircleQueue<AbstractEarthSurface> q=new CircleQueue<AbstractEarthSurface>();
map=initMap();
islandNums=getIslandNumBFS(q,map);
printResult(map,islandNums);
}
}
5.运行结果:
测试用例为:
iSea iSea iSea iSea iSea iSea iSea
iSea Land iSea iSea Land iSea iSea
iSea Land Land iSea iSea iSea iSea
iSea iSea iSea Land iSea iSea iSea
iSea Land iSea Land Land Land iSea
iSea iSea iSea iSea iSea iSea iSea
岛屿的数量为:4