按照学校数据结构实验课安排,第三个实验(第三天)————图。
今天完成的内容:
1.深度搜索(DFS)
2.广度搜索(BFS)
3.最小生成树算法:Prim,Kruskal
4.最短路径:Dijkstra
那么就开始今天实验,实验主要围绕两幅图进行验证,所以可能存在错误。
而且我编写的算法都是作用在邻接矩阵的。
邻接矩阵的类如下:
int spot;
int side;
int [][]table;
boolean[] sure;
public tu(int e)
{
this.spot=e;
this.table=new int[e][e];
for(int i=0;i<e;i++)
for(int j=0;j<e;j++)
this.table[i][j]=10000;
}
因为两图的点都是有序从0/1开始递增,故我默认点中最小值为0,最大值为(点数目-1),而初始化的权重为10000,方便后续算法调用。而sure数组是我额外添加便于确保某个点是否已经进入/被搜索过。
创建矩阵如下:
public void tablefull(int []a)
{
this.table[a[0]][a[1]]=a[2];
this.table[a[1]][a[0]]=a[2];
}
public void create(int e)
{
this.sure=new boolean[this.spot];
for(int i=0;i<this.spot;i++)
this.sure[i]=false;
this.side=e;
Scanner scan=new Scanner(System.in);
for(int i=0;i<e;i++)
{
System.out.print("请输入两个点以及它们的权重:");
int []a=new int[3];//输入两个点和权重
a[0]=scan.nextInt();
a[1]=scan.nextInt();
a[2]=scan.nextInt();
this.tablefull(a);
}
}
tablefull函数可以建立也可以不建立,只是当时为了看着舒服才额外开了另一个算法去存进数据。
一.深度搜索
DFS算法,以深度优先,即先从出发点开始,然后进入出发点第一个连接的点,然后连接点的查找第一个查找到的点(不能是已经搜索过的点),当去到某个点它所连接的点都已经搜索过,则返回,然后返回,让上级点查找另外的点进行再深层的搜索,直到全部搜索完。
因此不需要额外借助如栈,队列之类的,只需要本身即可完成,而代码也十分简短。
传入的参数e为出发点,而我实验过程中默认从0号结点(即图中最小值)出发。
public void DFS(int e)
{
this.sure[e]=true;
System.out.println(e+" ");
int a=-1;
for(int i=0;i<this.spot;i++)
{
if(this.table[e][i]<10000&&!this.sure[i])
{
a=i;
DFS(a);
}
}
}
二.广度搜索
广度搜索,以搜索完该结点相连的结点后,再进入下标最接近那个点的其他未搜索过的点,重复此步骤直到所有点都搜索完,故需要借助队列存储从某结点出发时,先进入的结点。
队列类如下:
public class queue {
queue front;
queue rear;
queue next;
int e;
public queue()//初始化头结点
{
front=this;
rear=this;
next=null;
}
public queue(int e)
{
this.e=e;
next=null;
front=null;
rear=null;
}
public void put(int e)//放入队列
{
queue a=new queue(e);
this.rear.next=a;
this.rear=a;
}
public boolean empty()//检测是否为空
{
queue pre=this;
if(pre.rear==pre.front)
return true;
return false;
}
public void clear()//清空队列
{
this.rear=front;
this.next=null;
}
public int delete()//删除队首元素
{
queue pre=this;
if(pre.empty())
return -1;
queue last=pre.front.next;
int a=last.e;
pre.next=last.next;
return a;
}
}
该类和前天做的队列实验差不多,只不过把没用上的去掉;用上的保留。
我的广度搜索如图进行。步骤为:搜索完本结点,搜索相邻结点并按顺序加入进队列;完事后从队首提取出结点,搜索该结点的相邻且没搜索过的结点;按此步骤循环进行直到搜索结束。
{
queue q=new queue();
System.out.print((e+1)+" ");
this.sure[e]=true;
do
{
for(int i=0;i<this.spot;i++)
if(this.table[e][i]<10000&&!this.sure[i])
{
System.out.print((i+1)+" ");
q.put(i);
this.sure[i]=true;
}
e=q.delete();
}while(this.notalltrue()&&!q.empty());
q.clear();
}
notalltrue函数用于判断是否所有点都搜索过,如下:
public boolean notalltrue()
{
for(int i=0;i<this.spot;i++)
if(!this.sure[i])
return true;
return false;
}
三.最小生成树算法:Prim
Prim算法步骤:从出发点开始查找相邻点中权重最小的点,相连;然后从已相连的点中,查找和未相连的点中权重最小的点相连;重复步骤直到所有的点都连起来。
(图源某度搜索)
public void Prim(int e)
{
int []visited=new int[this.spot];
System.out.println("起点是"+(e+1));
this.sure[e]=true;
visited[0]=e;
int len=1;
while(this.notalltrue())
{
int index=-1;//存储点位置
int min=10000;//存储两点值
for(int j=0;j<len;j++)
{
for(int i=0;i<this.spot;i++)
if(this.table[visited[j]][i]<min&&!this.sure[i])
{
min=this.table[visited[j]][i];
index=i;
}
}
e=index;
System.out.println("下一个点为"+(e+1));
this.sure[e]=true;
visited[len]=e;
len++;
}
}
我的算法实现,是先输出了出发点,然后查询在visited数组中的点,从里面的点中找权重最小的点。(visited数组用于记录已搜索过的点)。sure数组用于确定某点是否已搜索,和notalltrue函数搭配(判断是否所有的点都遍历过)使用。
四.最短路径:Dijkstra
Dijkstra算法:我的思路是:先确定好出发点,然后通过出发点到各点的距离生成一个最短路径shortpath数组,非直接连接的置10000,自己的点置0。然后从出发点开始查找,先查找shortpath中权重最小且非本身点的点,然后确定为到那个点的最短路径。然后从那个点出发,计算那个点到相邻点的距离,再加上shortpath中那个点对应的数值,若比shortpath数组中到其他点的数值变小,则置换掉。然后选择shortpath数组中,未确定为最短路径的点,置为下次从那个点开始出发。直到ifsure数组(确定点是否已经找到最短路径),全为true;visited数组满了(按顺序放入已搜索过的点);即可结束。
public void Dijkstra(int e)
{
int []shortpath=new int[this.spot];//最短路径数值
int []visited=new int[this.spot];//已确定最短路径的集合
boolean []ifsure=new boolean[this.spot];
for(int i=0;i<this.spot;i++)
{
ifsure[i]=false;
shortpath[i]=this.table[e][i];
}
visited[0]=e;
ifsure[e]=true;
int min=0;
for(int i=1;i<this.spot;i++)
if(shortpath[min]>shortpath[i])
min=i;
shortpath[e]=0;
for(int i=1;i<this.spot;i++)
{
visited[i]=min;
ifsure[min]=true;
int max=10000;
for(int j=0;j<this.spot;j++)
{
if(shortpath[j]>shortpath[min]+this.table[min][j]&& !ifsure[j])
shortpath[j]=shortpath[min]+this.table[min][j];
}
int j=0;
for(;j<this.spot;j++)
if(!ifsure[j])
{
max=shortpath[j];
min=j;
break;
}
for(;j<this.spot;j++)
{
if(shortpath[j]<max&&!ifsure[j])
{
max=shortpath[j];
min=j;
}
}
}
for(int i=0;i<this.spot;i++)
System.out.print((visited[i])+" ");
}
总结
算法是根据两幅图进行编写,也通过了两幅图的实现,所以应该不存在问题。
Kruskal算法本来也想实现的,不过时间不够,而且时间范围内肝不出来,遂打算后补。