实验九

目录

  • 学号 2019-2020-1823 《数据结构与面向对象程序设计》实验六报告
    • 1.实验内容
    • 2实验过程及结果
    • 3. 实验过程中遇到的问题和解决过程
    • 其他(感悟、思考等)

学号 2019-2020-1823 《数据结构与面向对象程序设计》实验六报告

班级: 1823

姓名: 杨凯涵

学号:20182321

实验教师:王志强

实验日期:2019年10月22日

必修/选修: 必修

1.实验内容

(1) 初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)

(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)

(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)

(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)

(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)

PS:本题12分。目前没有明确指明图的顶点和连通边,如果雷同或抄袭,本次实验0分。

2实验过程及结果

实验一

初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)

首先我们现在稿纸上设计好要花的图

实验九_第1张图片

接着,我们开始初始化图,以下为主程序的代码(初始化图的代码与之前的周总结内容和实验相同,再次不做过多论述)

Vertex a = main.new Vertex("V0",0);//0到第一个节点的最短路径设置为0
            Vertex b = main.new Vertex("V1");
            Vertex c = main.new Vertex("V2");
            Vertex d = main.new Vertex("V3");
            Vertex e = main.new Vertex("V4");
            Vertex f = main.new Vertex("V5");
            vertexs.add(a);
            vertexs.add(b);
            vertexs.add(c);
            vertexs.add(d);
            vertexs.add(e);
            vertexs.add(f);
            int[][] edges = {
                    {5,3,Integer.MAX_VALUE,Integer.MAX_VALUE,3,Integer.MAX_VALUE},
                    {Integer.MAX_VALUE,Integer.MAX_VALUE,4,Integer.MAX_VALUE,Integer.MAX_VALUE,Integer.MAX_VALUE},
                    {6,2,Integer.MAX_VALUE,3,Integer.MAX_VALUE,6},
                    {Integer.MAX_VALUE,9,7,Integer.MAX_VALUE,4,5},
                    {Integer.MAX_VALUE,5,2,1,Integer.MAX_VALUE,7},
                    {Integer.MAX_VALUE,Integer.MAX_VALUE,Integer.MAX_VALUE,3,Integer.MAX_VALUE,Integer.MAX_VALUE}

            };
            Graph graph = main.new Graph(vertexs, edges);

            List vertexs1 = new ArrayList();
            Vertex a1 = main.new Vertex("V0",0);//0到第一个节点的最短路径设置为0
            Vertex b1 = main.new Vertex("V1");
            Vertex c1 = main.new Vertex("V2");
            Vertex d1 = main.new Vertex("V3");
            Vertex e1 = main.new Vertex("V4");
            Vertex f1 = main.new Vertex("V5");
            vertexs1.add(a1);
            vertexs1.add(b1);
            vertexs1.add(c1);
            vertexs1.add(d1);
            vertexs1.add(e1);
            vertexs1.add(f1);
            int[][] edges1 = {
                    {5,3,0,0,3,0},
                    {0,0,4,0,0,0},
                    {6,2,0,3,0,6},
                    {0,9,7,0,4,5},
                    {0,5,2,1,0,1},
                    {0,0,0,3,0,0}

            };
            Graph graph1 = main.new Graph(vertexs1,edges1);

接着输出结果为:

有向图

实验九_第2张图片

无向图

实验九_第3张图片

实验二

(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)

有向图和无向图的深度遍历和广度遍历的核心代码为

深度遍历

   public void  DFS(String vertexName){
            int id=getIdOfVertexName(vertexName);
            if(id==-1)return;
            vertexs.get(id).setMarked(true);
            System.out.print(vertexs.get(id).getName()+" ");
            List neighbors = getNeighbors(vertexs.get(id));
            for(int i=0;i

广度遍历

 public void BFS(String vertexName){
            int startID=getIdOfVertexName(vertexName);
            if(startID==-1) return;
            List q=new ArrayList();
            q.add(vertexs.get(startID));
            vertexs.get(startID).setMarked(true);
            while(!q.isEmpty()){
                Vertex curVertex=q.get(0);
                q.remove(0);
                System.out.print(curVertex.getName()+" ");
                List neighbors = getNeighbors(curVertex);
                for(int i=0;i

运行结果如下

实验九_第4张图片

实验三

(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)

 拓扑排序定义: 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若 ∈E(G),则u在线性序列中出现在v之前。
 通常,这样的线性序列称为满足拓扑次序(TopoiSicai Order)的序列,简称拓扑序列。

实验九_第5张图片

部分代码

VertexNode v0 = new VertexNode(0, 1, null);
        EdgeNode v0e0 = new EdgeNode(1, 0, null);
        EdgeNode v0e1 = new EdgeNode(2, 0, null);

        v0.setFirstEdge(v0e0);
        v0e0.setNext(v0e1);

        VertexNode v1 = new VertexNode(0, 1, null);
        EdgeNode v1e0 = new EdgeNode(3, 0, null);
        EdgeNode v1e1 = new EdgeNode(4, 0, null);

        v1.setFirstEdge(v1e0);
        v1e0.setNext(v1e1);

        VertexNode v2 = new VertexNode(1, 2, null);
        EdgeNode v2e0 = new EdgeNode(3, 0, null);
        EdgeNode v2e1 = new EdgeNode(5, 0, null);

        v2.setFirstEdge(v2e0);
        v2e0.setNext(v2e1);


        VertexNode v3 = new VertexNode(2, 3, null);
        EdgeNode v3e0 = new EdgeNode(5, 0, null);

运行结果

实验四

(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)

求最小生成树的方法由下面的图来演示

实验九_第6张图片

实验九_第7张图片

实验九_第8张图片

实验九_第9张图片

实验九_第10张图片

实验九_第11张图片

通过以上的算法,我们的实现代码为

   for(int i=0;i
 for(int i=0;i
  initMinTree();//初始化最小生成树
            while(!allVisited()){
                Vertex vertex = vertexs.get(getNotMarkedMinVertex());//设置处理节点
                //System.out.println("处理:节点"+vertex.getName());
                //顶点已经计算出最短路径,设置为"已访问"
                vertex.setMarked(true);
                //获取所有"未访问"的邻居
                List neighbors = getNeighbors(vertex);
                //System.out.println("邻居个数为:"+neighbors.size());
                //更新最小生成树
                updateMinEdge(vertex, neighbors);
            }
            //System.out.println("search over");
            setMinTree();

            return minTree;//获得最小生成树
   public void  updateMinEdge(Vertex vertex, List neighbors){
            //参数检测
            if(!isInGraph(vertex)){
                System.out.println("当前节点不在图中");
                return ;
            }

            for(Vertex neighbor: neighbors){
                int distance = edges[getIdOfVertexName(neighbor.getName())][getIdOfVertexName(vertex.getName())];
                if(neighbor.getAnotherIDinminEdge()==-1){
                    neighbor.setAnotherIDinminEdge(getIdOfVertexName(vertex.getName()));
                    //System.out.println(neighbor.getName()+" setEdge To "+vertex.getName()+edges[neighbor.getAnotherIDinminEdge()][getIdOfVertexName(neighbor.getName())]);
                }
                else if(distance <  edges[getIdOfVertexName(neighbor.getName())][neighbor.getAnotherIDinminEdge()]){
                    neighbor.setAnotherIDinminEdge(getIdOfVertexName(vertex.getName()));
                    //System.out.println(neighbor.getName()+" setEdge To "+vertex.getName()+edges[neighbor.getAnotherIDinminEdge()][getIdOfVertexName(neighbor.getName())]);
                }
            }
        }//更新最小生成树

实验五

(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)

单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。

一.最短路径的最优子结构性质

该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么P'(i,j)=P(i,k)+P'(k,s)+P(s,j)

二.Dijkstra算法

由上述性质可知,如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点。那么(Vi...Vk)也必定是从i到k的最短路径。为了求出最短路径,Dijkstra就提出了以最短路径长度递增,逐次生成最短路径的算法。譬如对于源顶点V0,首先选择其直接相邻的顶点中长度最短的顶点Vi,那么当前已知可得从V0到达Vj顶点的最短距离dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根据这种思路,

假设存在G=,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。

1.从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;

2.更新与i直接相邻顶点的dist值。(dist[j]=min{dist[j],dist[i]+matrix[i][j]})

3.知道U=V,停止。

代码如下


        /**
         * 搜索各顶点最短路径
         */
        public void search(){
            while(!unVisited.isEmpty()){
                Vertex vertex = unVisited.element();
                //顶点已经计算出最短路径,设置为"已访问"
                vertex.setMarked(true);
                //获取所有"未访问"的邻居
                List neighbors = getNeighbors(vertex);
                //更新邻居的最短路径
                updatesDistance(vertex, neighbors);
                pop();
            }
            System.out.println("最短路径");
        }

        /**
         * 获取顶点到目标顶点的距离
         */
        private int getDistance(Vertex source, Vertex destination) {
            if(!isInGraph(source)||!isInGraph(destination)){
                System.out.println("当前节点不在图中");
                return -1;
            }

   /**
         * 获取顶点所有(未访问的)邻居
         */
        public List getNeighbors(Vertex v) {
            //参数检测
            if(!isInGraph(v)){
                System.out.println("当前节点不在图中");
                return null;
            }
            List neighbors = new ArrayList();
            int position = vertexs.indexOf(v);
            Vertex neighbor = null;
            int distance;
            for (int i = 0; i < vertexs.size(); i++) {
                if (i == position) {
                    //顶点本身,跳过
                    continue;
                }
                distance = edges[position][i];    //到所有顶点的距离
                if (distance < Integer.MAX_VALUE) {
                    //是邻居(有路径可达)
                    neighbor = getVertex(i);
                    if (!neighbor.isMarked()) {
                        //如果邻居没有访问过,则加入list;
                        neighbors.add(neighbor);
                    }
                }
            }
            return neighbors;
        }

        /**
         * 根据顶点位置获取顶点
         */
        private Vertex getVertex(int index) {
            if(index<0||index>vertexs.size()+1){
                System.out.println("获取ID为"+index+"失败");
                return null;
            }
            return vertexs.get(index);
        }

运行结果如图

实验九_第12张图片

3. 实验过程中遇到的问题和解决过程

  • 问题1:实现迪杰斯特拉算法
  • 问题1解决方法:

一、算法思想

Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法。该算法被称为是“贪心算法”的成功典范。

1、令G = (V,E)为一个带权无向图。G中若有两个相邻的节点,i和j。aij(在这及其后面都表示为下标,请注意)为节点i到节点j的权值,在本算法可以理解为距离。每个节点都有一个值di(节点标记)表示其从起点到它的某条路的距离。

  2、算法初始有一个数组V用于储存未访问节点的列表,我们暂称为候选列表。选定节点1为起始节点。开始时,节点1的d1=0, 其他节点di=无穷大,V为所有节点。
初始化条件后,然后开始迭代算法,直到V为空集时停止。具体迭代步骤如下:

   将d值最小的节点di从候选列表中移除。(本例中V的数据结构采用的是优先队列实现最小值出列,最好使用斐波那契对,在以前文章有过介绍,性能有大幅提示)。对于以该节点为起点的每一条边,不包括移除V的节点, (i, j)属于A, 若dj > di + aij(违反松弛条件),则令

  dj = di + aij , (如果j已经从V中移除过,说明其最小距离已经计算出,不参与此次计算)

  可以看到在算法的运算工程中,节点的d值是单调不增的。

代码如下

public class Dijkstra {
    class Item
    {   String endString;
        ArrayListlujingArrayList=new ArrayList();
        int distance;
    }
    public void GetShortWay(Graph graph,String startpoint,String endpoint) {
    int startindex=graph.vertexList.indexOf(startpoint);
    ArrayListSarray=new ArrayList();//S列表中存已知最短路径的对象,U列表中存未知最短路径的对象
    ArrayListUarray=new ArrayList();//S列表中存已知最短路径的对象,U列表中存未知最短路径的对象
    //初始化U列表
    for(int i=0;iUarray.get(i).distance)
            {
                t=i;
                tempdistance=Uarray.get(i).distance;
            }
        }
        Sarray.add(Uarray.remove(t));
        for(int i=0;i( Sarray.get(j).lujingArrayList);
                        Uarray.get(i).lujingArrayList.add(graph.vertexList.get(index1));
                    }
                }
            }
        }
        
    }
    for(int i=0;i"+Sarray.get(i).endString+"的最短路径为:");
        for(int j=0;j
  • 问题2:如何代码打出广度遍历和深度遍历
  • 问题2解决方法:
    在上面的基础上,我们进行编写广度遍历和深度遍历的方法,这两个方法用到了堆栈的方式来实现,以下给出代码

深度遍历

 public String DFS(String startnode,ArrayList vertexList,int [][]edges) {
        if (!vertexList.contains(startnode)) {
            System.out.print("输入节点不在该图内");
            return null;
        }
        int startindex=vertexList.indexOf(startnode);
        int numOfNodes=vertexList.size();
        boolean[]visted=new boolean[numOfNodes];
        StringBuilder resultBuilder=new StringBuilder();
        Stack stack=new Stack();
        stack.push(startindex);
        visted[startindex]=true;
        while (!stack.isEmpty()) {
            int v=stack.pop();
            resultBuilder.append(vertexList.get(v)+",");
            for(int i=0;i0?resultBuilder.substring(0,resultBuilder.length()-1):null;
    }

广度遍历

  public String BFS(String startnode,ArrayList vertexList,int [][]edges) {
        if (!vertexList.contains(startnode)) {
            System.out.print("输入节点不在该图内");
            return null;
        }
        StringBuilder resultBuilder=new StringBuilder();
        boolean []visited=new boolean[vertexList.size()];
        int startIndex=vertexList.indexOf(startnode);
        Queuequeue=new LinkedList();
        queue.offer(startIndex);
        visited[startIndex]=true;
        while (!queue.isEmpty()) {
            int v=queue.poll();
            resultBuilder.append(vertexList.get(v)+",");
            for(int i=0;i0?resultBuilder.substring(0,resultBuilder.length()-1):null;
    }

其他(感悟、思考等)

  • 很高兴能圆满完成这学期的所有实验,感悟最深的不是学了多少的数据结构、多少的java算法、多少的关于安卓、关于java的各种各样的知识,是感觉自己自学能力的提高,自己对于解决苦难的能力的提高,在困境中的不懈努力和寻求解决难题的方法,才是我觉得这门课给我最多的东西
  • 完成这个实验后还有这最后一个难度最大的app在等着我们,加油!

    参考资料

    《Java程序设计与数据结构教程(第二版)》

《Java程序设计与数据结构教程(第二版)》学习指导

你可能感兴趣的:(实验九)