# 20202304 2021-2022-1 《数据结构与面向对象程序设计》实验九报告
课程:《程序设计与数据结构》
班级: 2023
姓名: 何锐
学号:20202304
实验教师:王志强
实验日期:2021年12月16日
必修/选修: 必修
一、实验内容
(1) 初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)
(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)
(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)
(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)
(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)
二、实验过程及结果
(1) 初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)
我在这里使用了邻接表:(邻接表用自己定义的边和点进行了实现,如果换成邻接矩阵的话用一组二维数组就可以)
选取图为:
带权无向图为:
(直接截图考试图好吧hhh)
实现代码:(第一部分 只到初始化完毕,另外一提,在实现有向图的初始化的同时便将出入度的计算也完成了)
class edge{//边 node代表构成边的节点,该类为邻接表中 int node ;//另一个点 int weight;//权重 edge nextedge;//节点有多条边,构成链表 } class point{//点(其中出入度只对有向图进行了实现) int pname;//编号 edge edgehead;//头指针(边链表) int in=0;//入度 int out=0;//出度 } public class mymap { public point[] points; private boolean isDirected;//是否有向图,true是有向图(如果实现题目中的一二可以从main函数里做处理) public int[][] inmap;//第一个数组量存放该点,第二个量则是给他入入度的点 mymap(int ps[], int[][] edges, boolean isDirected) { inmap = new int[ps.length+1][ps.length+1]; //ps包含各顶点的编号,若有n个顶点,顶点的编号范围是0~n-1 //edges是一个n×3的数组,,边的权值等于edges[i][2],若为有向图,那么edges[i][0]指向edges[i][1] points = new point[ps.length]; this.isDirected = isDirected; if (isDirected == false) {//无向图的初始化 for (int j = 0; j < ps.length; j++) { points[j] = new point(); points[j].pname = ps[j]; edge cur = null; for (int i = 0; i < edges.length; i++) { if (edges[i][0] == ps[j]) { if (points[j].edgehead == null) { points[j].edgehead = new edge(); points[j].edgehead.node = edges[i][1]; points[j].edgehead.weight = edges[i][2]; cur = points[j].edgehead; } else { while (cur.nextedge != null) cur = cur.nextedge; cur.nextedge = new edge(); cur = cur.nextedge; cur.node = edges[i][1]; cur.weight = edges[i][2]; } } else if (edges[i][1] == ps[j]) { if (points[j].edgehead == null) { points[j].edgehead = new edge(); points[j].edgehead.node = edges[i][0]; points[j].edgehead.weight = edges[i][2]; cur = points[j].edgehead; } else { while (cur.nextedge != null) cur = cur.nextedge; cur.nextedge = new edge(); cur = cur.nextedge; cur.node = edges[i][0]; cur.weight = edges[i][2]; } } } } } else {//有向图的初始化 for (int j = 0; j < ps.length; j++) { points[j] = new point(); points[j].pname = ps[j]; } for (int j = 0; j < ps.length; j++) { edge cur = null; for (int i = 0; i < edges.length; i++) { if (edges[i][0] == ps[j]) { if (points[j].edgehead == null) { points[j].edgehead = new edge(); points[j].edgehead.node = edges[i][1]; points[j].edgehead.weight = edges[i][2]; points[edges[i][0]].out++; points[edges[i][1]].in++; inmap[edges[i][1]][edges[i][0]] = 1; cur = points[j].edgehead; } else { while (cur.nextedge != null) cur = cur.nextedge; cur.nextedge = new edge(); cur = cur.nextedge; cur.node = edges[i][1]; cur.weight = edges[i][2]; points[edges[i][0]].out++; points[edges[i][1]].in++; inmap[edges[i][1]][edges[i][0]] = 1; } } } } } }
成功截图:
(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)
紧接上面的,在mymap类中实现两种便利
代码:
void bfs() {//广度优先遍历 if (points == null || points.length == 0) return; Queue<point> queue = new LinkedList<point>(); boolean[] visited = new boolean[points.length]; for (int i = 0; i < visited.length; i++) { visited[i] = false; } queue.offer(points[0]); visited[0] = true; System.out.print(points[0].pname + " "); while (!queue.isEmpty()) { point v = queue.remove(); edge e = v.edgehead; while (e != null && visited[e.node] == false) { queue.offer(points[e.node]); visited[e.node] = true; System.out.print(e.node + " "); e = e.nextedge; } } } void dfs() {//深度优先遍历 if (points == null || points.length == 0) return; boolean[] visited = new boolean[points.length]; for (int i = 0; i < visited.length; i++) { visited[i] = false; } Stack<point> stack = new Stack<point>(); stack.push(points[0]); visited[0] = true; while (!stack.isEmpty()) { point v = stack.pop(); visited[v.pname] = true; System.out.print(v.pname + " "); edge e = v.edgehead; while (e != null && visited[e.node] != true) { stack.push(points[e.node]); e = e.nextedge; } } }
成功截图:
(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)
代码:
void toposort() {//拓扑排序 Stack s = new Stack(); for (int l = 0; l < points.length; l++) { if (points[l].in == 0) { s.push(points[l].pname); points[l].in = -1; } } while (!s.isEmpty()) { int m = (int) s.peek(); System.out.print(s.peek() + " "); s.pop(); for (int f = 0; f < points.length; f++) { if (inmap[f][m] == 1) { points[f].in--; } } for (int k = 0; k < points.length; k++) { if (points[k].in == 0) { s.push(points[k].pname); points[k].in = -1; } } } }
成功截图:
(4) 完成无向图的最小生成树Prim算法
public void prim(int s) { int[] distance=new int[points.length]; //记录到起点的距离 //初始化到起点的距离 for(int i=0;i<points.length;i++) { distance[i]=getWeight(s, i); //起点到顶点i的权值 } int[] prims=new int[points.length]; //记录访问的顶点序号 int index=0; //prims[]的索引 prims[index++]=s; //第一个访问的是起点s //遍历所有顶点,并更新到起点的距离 for(int i=0;i<points.length;i++) { if(i==s) { continue; } int min=INF; int k=-1; for(int j=0;j<points.length;j++) { if(distance[j]!=0 && distance[j]<min) { min=distance[j]; k=j; } } prims[index++]=k; distance[k]=0; for(int j=0;j<points.length;j++) { if(getWeight(k, j)==INF) { continue; } int tmp=distance[k]+getWeight(k, j); if(distance[j]!=0 && distance[j]>tmp) { distance[j]=tmp; } } } //打印最小生成树 System.out.printf("prim(%c)\n",vertex[s].data); for(int i=0;i<vertex.length-1;i++) { System.out.print(vertex[prims[i]].data+"-->"); } System.out.print(vertex[prims[vertex.length-1]].data); int sum=0; //最小权值和 for(int i=1;i<vertex.length;i++) { int min=INF; for(int j=0;j<i;j++) { if(getWeight(prims[i], prims[j])<min) { min=getWeight(prims[i], prims[j]); } } sum+=min; } System.out.print("最小权值和为:"+sum); System.out.println(); } //判断v是不是u的邻接点 public boolean getAdjVertex(int u,int v) { Node tmp=vertex[u].firstEdge; while(tmp!=null) { if(tmp.index==v) { return true; } tmp=tmp.nextNode; } return false; }
(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)
public void dijkstra(int s) {
int[] path=new int[points.length]; //记录到起点经过的顶点路径 int[] distance=new int[points.length]; //记录到起点的距离 boolean[] visited=new boolean[points.length]; //标记是否访问过 //初始化到起点的距离 for(int i=0;i<points.length;i++) { distance[i]=getWeight(s, i); if(i!=s && distance[i]<INF) { path[i]=s; }else { path[i]=-1; } } visited[s]=true; //起点已经访问过了 //遍历所有顶点,并更新到起点的距离 for(int i=0;i<points.length;i++) { if(i==s) { continue; } int min=INF; int k=-1; //找到距离起点距离最短的顶点 //distance[j]=0,表示已经访问过了 for(int j=0;j<points.length;j++) { if(visited[j]==false && distance[j]<min) { min=distance[j]; k=j; } } visited[k]=true; //第k个顶点已经访问过了 //更新顶点k的邻接点到起点的最小距离 for(int j=0;j<points.length;j++) { //如果不是k的邻接点 if(getWeight(k, j)==INF) { continue; } int tmp=distance[k]+getWeight(k, j); //如果是未被访问过的邻接点,则更新其到起点的距离 if(visited[j]==false && distance[j]>tmp) { distance[j]=tmp; path[j]=k; } } } for(int i=0;i<points.length;i++) { System.out.print(path[i]+" "); int tmp=i; while(tmp!=-1) { System.out.print(vertex[tmp].data+"<--"); tmp=path[tmp]; } System.out.print(" 最小权值为:"+distance[i]); System.out.println(); } private int getWeight(int start, int end) { if (start==end) return 0; edge edge1 = points[start].node.edgehead; while (edge1!=null) { if (end==edge1.node) return edge1.weight; edge1 = edge1.nextedge; } return INF; }
##思考、感悟和其他
课程也临近尾声了,也是最后一个实验了,时间很快,学习java的一路上也是磕磕绊绊,但也充满了乐趣。
很开心是王老师叫我们java和数据结构,在学习的路上带来了好多快乐,幽默风趣的课堂让本来不简单的java一点都不枯燥。
课程结束,缘分还远远不止,以后也会经常叨扰超人的哈哈哈哈。
撒花!