课程:《程序设计与数据结构》
班级: 2023
姓名: 陈欢
学号:20202320
实验教师:王志强
实验日期:2021年12月23日
必修/选修: 必修
##1.实验内容
(1) 初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)
(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)
(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)
(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)
(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)
##2.实验过程及结果
(1)初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)
(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)
如上图所示:其中邻接矩阵表示的有向图是(由于作图原因,其中顶点0直接与顶点5相连通):
无向图的遍历:
完整代码如下:
public void Depth(int i){
//标记第i个节点以遍历
visited[i]=true;
//打印当前已经遍历的节点
visit(i);
//遍历邻接矩阵中第i个节点的直接连通节点
for(int j=0;j<nodenum;j++){
if(Adjacency[i][j]==1&&visited[j]==false){
Depth(j);
}
}
}
//深度优先遍历
public void dfs(){
//初始化节点访问标记
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
//从没有被遍历的节点开始深度优先遍历
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
Depth(i);
}
}
System.out.println();
}
//广度遍历
public void bfs(){
//初始化节点访问标记
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
Queue<Integer> queue=new LinkedList<Integer>();
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
visited[i]=true;
//打印当前已经遍历的节点
visit(i);
//添加到队列里
queue.add(i);
//只要队列不为空
while(!queue.isEmpty()){
//出队节点,也就是这一层的节点
int k=queue.poll();
//遍历所有未被访问的邻接节点,放入队列
for(int j=0;j<nodenum;j++){
//也就是访问这一层剩下的未被访问的节点
if(Adjacency[k][j]==1&&visited[j]==false){
visited[j]=true;
visit(j);
queue.add(j);
}
}
}
}
}
System.out.println();
}
(3)完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)
有向图的拓扑排序如实验内容(1)所示。
完整代码如下:
public void Topo(int i){ int sum=0;
public void Topo(int i){
itn sum=0;
for(int j=0;j<nodenum;j++){
sum=sum+rear[j][i];//行
}
if(sum==0){//第i个节点没有前驱
visited[i]=true;
visit(i);
for(int k=0;k<nodenum;k++){
rear[i][k]=0;//列
}
}
}
public void Topological(){
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
int sum0=0;
do {
int i;
for (i = 0; i < nodenum; i++) {
if (visited[i] == false) {
Topo(i);
}
}
if(i==nodenum&&visited[i-1]==false) {
System.out.println("该图存在环!");
sum0=nodenum;
}else {
for (int j = 0; j < nodenum; j++) {//判断是否全部遍历
if (visited[j] == true) {
sum0++;
} else {
sum0 = 0;
break;
}
}
}
}while(sum0!=nodenum);
System.out.println();
}
(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)
使用的是Prime 算法,完整代码如下:
public void Prime(){
for(int i=0;i<nodenum;i++){
visited[i]=false;
}
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
Primehelp(i);
}
}
rear=Adjacency;
}//生成最小生成树
public void Primehelp(int i) {//无向图使用
int key = 0;
int a = 0, b = 0;
if (visited[i] == false) {
for (int j = 0; j < nodenum; j++) {
if (rear[i][j] > key)
key = rear[i][j];//第i行的最大值
}
for (int j = 0; j < nodenum; j++) {
if (rear[i][j] < key && rear[i][j] != 0) {
key = rear[i][j];//第i行的最小值
a = i;
b = j;
}
}
for (int num = 0; num < nodenum; num++) {
if (visited[num] == true) {
for (int p = 0; p < nodenum; p++) {
if (rear[num][p] < key && rear[num][p] != 0) {
key = rear[num][p];
a = num;
b = p;
}
}
}
}
}
if (visited[b] == false) {
rear[a][b] = 0;
rear[b][a] = 0;
visited[i] = true;
visit(i);
System.out.print("(" + key + ")");
Primehelp(b);
} else {
rear[a][b] = 0;
rear[b][a] = 0;
int sum = 0;
for (int ni = 0; ni < nodenum; ni++) {
if (visited[ni] == true) {
sum++;
}
}
if (sum != nodenum)
if (sum == nodenum - 1) {
for (int ni = 0; ni < nodenum; ni++) {
if (visited[ni] == false)
System.out.println(vertices[ni]);
}
} else
Primehelp(i);
}
}
(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)
暂未完成
##3.实验中遇到的问题和解决方法
--问题一:进行拓扑排序时总是不能够在最后输出正确的邻接矩阵。
--解决方法:给邻接矩阵做一个“分身”,在进行拓扑排序时,只对“分身”进行操作,而要输出的邻接矩阵并不做改变。
同时该“分身”不能够定义为rear=Adjacency,因为这样定义出的“分身”矩阵和Adjacency矩阵是同一个“存储空间”(应该是这么叫吧)。
--问题二:递归是难以退出。
--解决方法:通过调试,找出能够退出的唯一条件,这个条件需要做到所谓“当且仅当”(maybe)。
##4.实验心得与体会
首先是后悔没有早点动手,到了考试周才匆匆忙忙的做。
其次是递归的运用,在这次的实验中,递归的重要作用比之前的更加突出,递归非常考验人的逻辑(我觉得)。
最后,最后一次实验值得好好……
##5.参考资料
--数据结构和算法动态可视化 (Chinese) - VisuAlgo
--(3条消息) JAVA实现图的深度优先遍历._今天又是充满希望的一天-CSDN博客_java实现深度优先搜索
--《Java程序设计教程(第九版)》
--《Java软件结构与数据结构(第四版)》