20192328牛梓萌 2019-2020-1 《数据结构与面向对象程序设计》实验九报告

20192328牛梓萌 2019-2020-1 《数据结构与面向对象程序设计》实验九报告

课程:《程序设计与数据结构》
班级: 1923
姓名:牛梓萌
学号:20192328
实验教师:王志强
实验日期:2020年12月17日
必修/选修: 必修

1.实验内容

(1) 初始化:根据屏幕提示(例如:输入1为无向图,输入2为有向图)初始化无向图和有向图(可用邻接矩阵,也可用邻接表),图需要自己定义(顶点个数、边个数,建议先在草稿纸上画出图,然后再输入顶点和边数)(2分)
(2) 图的遍历:完成有向图和无向图的遍历(深度和广度优先遍历)(4分)
(3) 完成有向图的拓扑排序,并输出拓扑排序序列或者输出该图存在环(3分)
(4) 完成无向图的最小生成树(Prim算法或Kruscal算法均可),并输出(3分)
(5) 完成有向图的单源最短路径求解(迪杰斯特拉算法)(3分)

2. 实验过程及结果

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

import java.util.Scanner;

/*
 * 图类,在构造方法中完成图的构造
 */
public class Graph {
    int verNum;
    int edgeNum;
    Vertex[] verArray;

    /*
     * Graph类的构造方法,依次读取节点、边等信息,完成图的构建。
     */
    public Graph() {
        Scanner scan = new Scanner(System.in);
        System.out.println("请选择你想要构建有向图还是,无向图(0 or 1): ");
        int choose = scan.nextInt();
        System.out.println("请输入节点个数和边的个数:");
        verNum = scan.nextInt();
        edgeNum = scan.nextInt();
        verArray = new Vertex[verNum];

        System.out.println("请依次输入节点的名称:");
        for (int i=0;i<verNum;i++){
            Vertex vertexmem = new Vertex();
            vertexmem.verName = scan.next();
            vertexmem.edgeLink = null;
            verArray[i] = vertexmem;
        }

        System.out.println("请按 '头节点 尾节点 回车’的形式依次输入边的信息");
        for (int i=0;i<edgeNum;i++){
            String preName = scan.next();
            String folName = scan.next();

            Vertex preV = getVertex(preName);
            Vertex folV = getVertex(folName);
            if (preV == null || folV == null){
                System.out.println("输入错误!请重新输入");
                i--;
                continue;
            }

            Edge edge = new Edge();
            edge.tailName = folName;

            edge.broEdge = preV.edgeLink;
            preV.edgeLink = edge;
            if(choose==1){
                Edge edgeelse = new Edge();
                edgeelse.tailName = preName;
                edgeelse.broEdge  = folV.edgeLink;
                folV.edgeLink = edgeelse;}

        }
    }

    public Vertex getVertex(String verName){
        for (int i=0;i<verNum;i++){
            if (verArray[i].verName.equals(verName))
                return verArray[i];
        }
        return null;
    }
}

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

import java.util.*;

/*
 * 使用java实现图的图的广度优先 和深度优先遍历算法。
 */
public class GraphLoopTest {
    private Map<String, List<String>> graph = new HashMap<String, List<String>>();

    /*
     * 初始化图数据:使用邻居表来表示图数据。
     */
    public void initGraphData() {
//        图结构如下
//          9
//        /   
//       8     1
//      /    /
//     4  5  6
//       | /
//        7
        graph.put("9", Arrays.asList("8", "1"));
        graph.put("8", Arrays.asList("9", "4", "5"));
        graph.put("1", Arrays.asList("9", "6"));
        graph.put("4", Arrays.asList("8", "7"));
        graph.put("5", Arrays.asList("8", "7"));
        graph.put("6", Arrays.asList("1", "7"));
        graph.put("7", Arrays.asList("4", "5", "6"));
    }

    /*
     * 宽度优先搜索(BFS, Breadth First Search)
     */
    private Queue<String> queue = new LinkedList<String>();
    private Map<String, Boolean> status = new HashMap<String, Boolean>();


    public void BFSSearch(String startPoint) {
        //1.把起始点放入queue;
        queue.add(startPoint);
        status.put(startPoint, false);
        bfsLoop();
    }

    private void bfsLoop() {
        String currentQueueHeader = queue.poll();
        status.put(currentQueueHeader, true);
        System.out.print(currentQueueHeader);
        List<String> neighborPoints = graph.get(currentQueueHeader);
        for (String poinit : neighborPoints) {
            if (!status.getOrDefault(poinit, false)) {
                if (queue.contains(poinit)) continue;
                queue.add(poinit);
                status.put(poinit, false);
            }
        }
        if (!queue.isEmpty()) {
            bfsLoop();
        }
    }


    /*
     * 深度优先搜索(DFS, Depth First Search)
     */

    private Stack<String> stack = new Stack<String>();

    public void DFSSearch(String startPoint) {
        stack.push(startPoint);
        status.put(startPoint, true);
        dfsLoop();
    }

    private void dfsLoop() {
        if (stack.empty()) {
            return;
        }
        String stackTopPoint = stack.peek();
        List<String> neighborPoints = graph.get(stackTopPoint);
        for (String point : neighborPoints) {
            if (!status.getOrDefault(point, false)) {
                stack.push(point);
                status.put(point, true);
                dfsLoop();
            }
        }
        String popPoint = stack.pop();
        System.out.print(popPoint);
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("请选择深度,还是广度遍历(1 or 2)");
        int choose = scan.nextInt();
        GraphLoopTest test = new GraphLoopTest();
        test.initGraphData();
        if (choose == 2) {
            System.out.println("广度优先遍历 :");
            test.BFSSearch("1");
        }
        if (choose == 1) {
            System.out.println("深度优先遍历: ");
            test.DFSSearch("1");
        }

    }
}

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

import java.io.*;

public class TestTopoSort {
    public static void main(String[] args) throws IOException {

        DirectedGraph directedGraph = new DirectedGraph();
        try{
            directedGraph.topoSort();
        }catch(Exception e){
            System.out.println("graph has circle");
            e.printStackTrace();
        }
    }
}

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

import java.util.ArrayList;

import java.util.Arrays;

import java.util.HashSet;

import java.util.Set;
/*
 * 图的最小树生成算法
 *
 */

public class Prim {

    /*

     * 求图最小生成树的PRIM算法

     */

    public static void PRIM(int [][] graph,int start,int n){

        int [][] mins=new int [n][2];

        for(int i=0;i<n;i++){
            if(i==start){

                mins[i][0]=-1;

                mins[i][1]=0;

            }else if( graph[start][i]!=-1){
                mins[i][0]=start;

                mins[i][1]= graph[start][i];

            }else{

                mins[i][0]=-1;

                mins[i][1]=Integer.MAX_VALUE;

            }


        }

        for(int i=0;i<n-1;i++){

            int minV=-1,minW=Integer.MAX_VALUE;

            for(int j=0;j<n;j++){



                if(mins[j][1]!=0&&minW>mins[j][1]){

                    minW=mins[j][1];

                    minV=j;

                }

            }



            mins[minV][1]=0;

            System.out.println("第"+i+"条最小边=<"+(mins[minV][0]+1)+","+(minV+1)+">,权重="+minW);

            for(int j=0;j<n;j++){
                if(mins[j][1]!=0){

                    if( graph[minV][j]!=-1&& graph[minV][j]<mins[j][1]){

                        mins[j][0]=minV;

                        mins[j][1]= graph[minV][j];

                    }

                }

            }

        }

    }




    public static void main(String [] args){
        int [][] tree={

                {1,3,1,5,-1,-1},
                {3,-1,5,-1,6,-1},
                {1,5,-1,5,2,4},
                {5,-1,5,-1,-1,2},
                {-1,6,4,-1,-1,4},
                {-1,-1,4,2,6,-1}

        };
        Prim.PRIM(tree, 0, 6);


    }



}

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

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
import java.util.Stack;

public class DijstraAlgorithm {
    public static void main(String[] args) {
        int vertexNum = 5;
        char[] vertexs = new char[] { 'N', 'Z', 'M', 'A', 'B' };
        int[][] matrix = new int[][] { { 0, 1, Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 3 },
                { Integer.MAX_VALUE / 2, 0, 8, Integer.MAX_VALUE / 2, 9 },
                { Integer.MAX_VALUE / 2, Integer.MAX_VALUE / 2, 0, 5, Integer.MAX_VALUE / 2 },
                { 7, Integer.MAX_VALUE / 2, 9, 0, Integer.MAX_VALUE / 2 },
                { Integer.MAX_VALUE / 2, 1, 2, 3, 0 } };
        // matrix[i][j]为0表示i==j,matrix[i][j]为Integer.MAX_VALUE/2表示两个顶点不是图的边,否则表示边的权值
        Graph g = new Graph(vertexNum, vertexs, matrix);
        Scanner sc = new Scanner(System.in);
        int srcIndex;
        do{
            System.out.print("请输入源点索引(0~4):");
            srcIndex = sc.nextInt();
        }while(srcIndex < 0 || srcIndex > 4);
        System.out.println(g.vertexs[srcIndex] + "作为源点");
        Info info = dijkstra(g, srcIndex);
        for(int i : info.pathSerials){
            System.out.print(g.vertexs[i] + " ");
        }
        System.out.println();
        int index = 0;
        for(int[] path : info.paths){
            for(int i : path){
                System.out.print(g.vertexs[i]);
            }
            System.out.println(": " + info.distances[index++]);
        }
        sc.close();
    }

    public static Info dijkstra(Graph g, int srcIndex) {
        if(srcIndex < 0 || srcIndex >= g.vertexNum){
            return null;
        }
        int[] pathSerials = new int[g.vertexNum];
        int[] path = new int[g.vertexNum];
        int index = 0;
        pathSerials[index] = srcIndex;
        g.visited[srcIndex] = true;
        Arrays.fill(path, -1);
        int[] distances = new int[g.vertexNum];
        for (int i = 0; i < g.vertexNum; i++) {
            distances[i] = g.matrix[srcIndex][i];
        }
        int minIndex = srcIndex;
        while (minIndex != -1) {
            index++;
            for (int i = 0; i < g.vertexNum; i++) {
                if (!g.visited[i]) {
                    distances[i] = Math.min(distances[i], distances[minIndex] + g.matrix[minIndex][i]);

                    if(distances[i] == distances[minIndex] + g.matrix[minIndex][i] && distances[i] != Integer.MAX_VALUE / 2){ // distances[i] != Integer.MAX_VALUE / 2表示仍不可达,就没有前驱
                        path[i] = minIndex;
                    }
                }
            }
            minIndex = indexOf(g, distances);
            if(minIndex == -1){
                break;
            }
            pathSerials[index] = minIndex;
            g.visited[minIndex] = true;
        }
        return new Info(distances, pathSerials, getPathOfAll(path, pathSerials));
    }

    public static int indexOf(Graph g, int[] distances) {
        int min = Integer.MAX_VALUE / 2;
        int minIndex = -1;
        for(int i = 0; i < g.vertexNum; i++){
            if(!g.visited[i]){
                if(distances[i] < min){
                    min = distances[i];
                    minIndex = i;
                }
            }
        }
        return minIndex;
    }


    public static int[] getPath(int[] path, int i){
        Stack<Integer> s = new Stack<Integer>();
        s.push(i);
        int pre = path[i];
        while(pre != -1){
            s.push(pre);
            pre = path[pre];
        }
        int size = s.size();
        int[] pathOfVertex = new int[size];
        while(!s.isEmpty()){
            pathOfVertex[size - s.size()] = s.pop();
        }
        return pathOfVertex;
    }

    public static ArrayList<int[]> getPathOfAll(int[] path, int[] pathSerials){
        ArrayList<int[]> paths = new ArrayList<int[]>();
        for(int i = 0; i < pathSerials.length; i++){
            paths.add(getPath(path, i));
        }
        return paths;
    }

    public static class Graph{
        private int vertexNum;
        private char[] vertexs;
        private int[][] matrix;
        private boolean visited[];

        public Graph(int vertexNum, char[] vertexs, int[][] matrix){
            this.vertexNum = vertexNum;
            this.vertexs = vertexs;
            this.matrix = matrix;
            visited = new boolean[vertexNum];
        }
    }

    public static class Info{
        private int[] distances;
        private int[] pathSerials;
        private ArrayList<int[]> paths;

        public Info(int[] distances, int[] pathSerials, ArrayList<int[]> paths) {
            this.distances = distances;
            this.pathSerials = pathSerials;
            this.paths = paths;
        }

    }
}

码云:https://gitee.com/besti1923/niu-zimeng-20192328/tree/master/src/Graph

其他(感悟、思考等)

图的实验还是有很大难度的,作为本课程的最后一个实验,在这次的实践中又加深了对图的认识。

参考资料

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

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

原文地址:https://www.cnblogs.com/niuzimeng/p/14181759.html