2017-2018-1 20162330 实验四 图的实现与应用

 课程名称:《程序设计与数据结构》

 学生班级:1623班

 学生姓名:刘伟康

 学生学号:20162330

 实验时间:2017年11月20日—2017年11月24日

 实验名称:图的实现与应用

 指导老师:娄嘉鹏、王志强老师


目录


  • 实验要求
  • 实验步骤及代码实现

    • 代码托管汇总
    • 图的实现与应用-1:用邻接矩阵实现无向图
    • 图的实现与应用-2:用十字链表实现有向图
    • 图的实现与应用-3:实现PP19.9
  • 测试过程及遇到的问题

    • 1. 第一个实验无向图的边输入错误
  • 分析总结及PSP时间统计
  • 参考资料



实验要求:

实验四 图的实现与应用

  • 1.用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器。
    给出伪代码,产品代码,测试代码(不少于5条测试);
  • 2.用十字链表实现有向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器。
    给出伪代码,产品代码,测试代码(不少于5条测试);
  • 3.实现PP19.9。
    给出伪代码,产品代码,测试代码(不少于5条测试)。

【返回目录】


实验步骤及代码实现:


  • 1. 图的实现与应用-1:

    用邻接矩阵实现无向图:
    (边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器。

  • 关于邻接矩阵实现无向图中边和顶点的存储策略,可以将图的顶点用一个一维数组存放,将图的边用一个二维数组存放,其中这个边集合就相当于邻接矩阵了:
    private Object[] vexs;  //顶点集合
    private int[][] arcs;  //边集合,邻接矩阵

    另外我设定了顶点数目和边数目,其中我将顶点数目定义为决定图的大小的变量:

    private int gSize, arcNum;  //顶点数目、边数目

    于是,判断一个图是否为空的 isEmpty() 方法和返回图的大小的 size() 方法就可以根据顶点数目定义了:

    //判断图的大小(顶点个数)
    public int size() {
        return gSize;
    }
    
    //判断图是否为空
    public boolean isEmpty() {
        return gSize == 0;
    }

    关于添加一条边和删除一条边我还没有考虑那么多种情况(还会补充),所以只实现了基本的功能,即邻接矩阵对角线上对应的两边数值添加时都变为1,删除时都变为0:

    //添加一条边
    public void addArc(int x, int y) {
        arcs[x][y] = 1;
        arcs[y][x] = 1;
        arcNum++;
    }
    
    //删除一条边
    public void removeArc(int x, int y) {
        arcs[x][y] = 0;
        arcs[y][x] = 0;
        arcNum--;
    }

    关于添加和删除顶点,我参考了网上的一些思路,然而还没有测试成功,所以这部分内容待补充,实现代码如下:

    //添加一个顶点
    public void addVex() {
        for (int i = 0; i < gSize; i++) {
            arcs[i][gSize] = INFINITY;
        }
        for (int i = 0; i < gSize; i++) {
            arcs[gSize][i] = INFINITY;
        }
        arcs[gSize][gSize] = 0;
        gSize++;
        System.out.println("Insert successfully.");
    }
    
    //删除一个顶点
    public void removeVex(int v) {
        if (v >= gSize) {
            System.out.println("The graph doesn't have this vertex.");
        }
        for (int i = 0; i < gSize; i++) {
            arcs[v][i] = 0;
            arcs[v][i] = 0;
        }
        if (v == gSize - 1) {
            gSize--;
        }
        for (int i = v + 1; i < gSize; i++) {
            for (int j = 0; j < gSize; j++) {
                arcs[i - 1][j] = arcs[i][j];
            }
        }
        gSize--;
    }
  • 之后来说说图的遍历方法的实现,首先常见的图的遍历方法有两种:① 广度优先遍历;② 深度优先遍历。(代码就先不贴出来了)
    对于广度优先遍历,先访问某个顶点,再依次访问每一个未被访问过的邻接点,然后按照这个顺序访问其他顶点,之后访问各个还未被访问过的邻接点,以此类推,直到所有顶点都被访问过为止,可以参考下图:

    使用队列实现广度优先遍历的具体思路如下:
    (1)顶点v入队列。
    (2)当队列非空时则继续执行,否则算法结束。
    (3)出队列取得队头顶点v;访问顶点v并标记顶点v已被访问。
    (4)查找顶点v的第一个邻接顶点w。
    (5)若v的邻接顶点w未被访问过的,则w入队列。
    (6)继续查找顶点v的另一个新的邻接顶点w,转到步骤(5)。
    直到顶点 v 的所有未被访问过的邻接点处理完。转到步骤(2)。

  • 实现广度优先遍历的关键就在于要设立一个访问标志数组,初值为0,某顶点被访问后,相应下标元素置为1。
  • 对于深度优先遍历,先从图的某个顶点 v 开始访问,然后访问它的任意一个邻接点w1,再从w1出发,访问与w1邻接但未被访问的顶点w2,然后从w2出发,依次访问,直至所有的邻接点被访问过。之后,退到前一次访问过的顶点,看是否还有其他未被访问过的邻接点。如果有,则访问此顶点,没有再退到前一次访问过的顶点,重复这一过程,直到所有顶点都被访问过为止。(递归)

    使用队列实现深度优先遍历的具体思路如下:
    (1)输入要访问的结点Vi;
    (2)访问顶点vi;visited[vi]=1;
    (3)在邻接矩阵的第i行中查找,若vi有邻接点vj,且vj未被访问过,则设 i=j;
    (4)重复步骤1至3,直到所有结点均被访问到。

  • 和广度优先遍历相同的是,深度优先遍历也要设立一个访问标志数组visited[N],初值为0,某点被访问,则相应下标变量置为1。
  • 在测试时,我直接使用了根据输入的边和顶点创建邻接矩阵的方法,测试运行截图如下:(部分)


  • 2. 图的实现与应用-2:

    用十字链表实现有向图:
    (边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器。

  • 实现这个就有点难了,原因是十字链表是针对有向图的,这需要考虑到所有边对应的权值,我参考了网上相关资料的代码,关于顶点和边,可以单独定义两个类:
    在定义十字链表的边的类时,要设定入弧顶点和出弧顶点两个形参:

    public class CrossEdge<E> {
    E data;
    int fromVertexIndex;
    int toVertexIndex;
    CrossEdge<E> nextSameFromVertex;
    CrossEdge<E> nextSameToVertex;
    
    public CrossEdge(E data, int fromVertexIndex, int toVertexIndex) {
        this.data = data;
        this.fromVertexIndex = fromVertexIndex;
        this.toVertexIndex = toVertexIndex;
    }
    }

    在定义十字链表的顶点的类时,需要定义顶点的data形参:

    public class CrossVertex<E, T> {
    E data;
    CrossEdge<T> firstIn;
    CrossEdge<T> firstOut;
    
    public CrossVertex(E data) {
        this.data = data;
    }
    }

    我还没太搞懂方法类中的代码,方法类中的设计思路之后会有补充。

  • 测试运行截图如下:


  • 3. 图的实现与应用-3:

    实现PP19.9(最短路径)。

  • 对于如何解决有向图中顶点间的最短路径问题,我在这里使用了戴克斯特拉算法,具体实现如下:
    public void DIJ(MGraph G, int v0){
        int vexNum = G.getVexNum(); // 顶点数
        this.P = new boolean[vexNum][vexNum];
        this.D = new int[vexNum];
        //finish[v]为true当且仅当v属于S,即已经求得从v0到v的最短路径
        boolean[] finish = new boolean[vexNum];
    
        //初始化所有数据
        for(int v = 0; v < vexNum; v++){
            finish[v] = false;
            D[v] = G.getArcs()[v0][v];
            for(int w = 0; w < vexNum; w++){
                P[v][w] = false;
            }
            if(D[v] < INFINITY){
                P[v][v0] = true;
                P[v][v] = true;
            }
        }
    
        D[v0] = 0;  //从v0开始,并入S集
        finish[v0] = true;
    
        int v = -1 ;
        //开始主循环,每次求得v0到某个v顶点的最短路径,并将v加入到S集.循环n-1次
        for(int i = 1; i < vexNum; i++){
            int min = INFINITY; //当前所知离v0最近的距离
            for(int w = 0; w < vexNum; w++){
                if( !finish[w]){
                    if(D[w] < min){
                        v = w;
                        min = D[w];
                    }
                }
            }
            finish[v] = true; //离v0最近的v并入S
    
            //更新当前最短路径和距离
            for(int w = 0; w < vexNum; w++){
                if( !finish[w] && G.getArcs()[v][w] < INFINITY && (min + G.getArcs()[v][w] < D[w])){
                    D[w] = min + G.getArcs()[v][w];
                    //下面两句这么理解,现在路径是v0-v-w,所以经过了v点,那么v0到v的最小路径自然要给w,同时再加上w点(P[W][W] = true)
                    System.arraycopy(P[v], 0, P[w], 0, P[v].length);
                    P[w][w] = true;
                }
            }
        }
    }
  • 测试运行截图如下:(其中,vo表示网络中的点,分别计算vo到各个结点的最短路径)

【返回目录】


测试过程及遇到的问题:

  • 1. 在做第一个实验时为什么无向图的边会出现输入错误,导致抛出下标越界的异常?

  • 解决办法:(使用debug单步跟踪进行调试)
    调试之后我发现是我的输入格式出了问题,各个边之间是要加一个空格的,因为我在调用了 Scanner 中的 next 方法依次检测输入的边,而在各个边的间隔之间,是要留出一个空格的距离的:

    知道了这一“特定格式”之后,再运行时就没有出错了。

【返回目录】


分析总结:

  • 本周的实验主要是对于图的应用,这次的实验比较难,第一个实验就有点无从下手,第二个实验更是参考了网上的代码,很勉强地做了出来,这让我对此感到一些无力。不过,图的设计思路值得学习,因为图在生活中的应用不少,大概也正是因为图和树的密切联系以及其在生活中的应用(网络爬虫等),才诞生了这么多相关的算法,这大概也是很多程序员喜欢研究图的原因。这篇博客写得不全,有待完善。


PSP(Personal Software Process)时间统计:

  • 步骤 耗时 百分比
    需求分析 60min 15%
    设计 60min 15%
    代码实现 120min 30%
    测试 80min 20%
    分析总结 80min 20%

【返回目录】


参考资料:

【返回目录】

时间: 2024-10-10 06:11:24

2017-2018-1 20162330 实验四 图的实现与应用的相关文章

数据结构实验报告-实验四 图的构造与遍历

实验四   图的构造与遍历   l  实验目的 1.图的的定义和遍历 (1)掌握图的邻接矩阵.邻接表的表示方法. (2)掌握建立图的邻接矩阵的算法. (3)掌握建立图的邻接表的算法. (4)加深对图的理解,逐步培养解决实际问题的能力. l  实验内容 1.图的定义和遍历 (一)基础题 1.编写图基本操作函数: (1)CreateALGraph(ALGraph &G) 建立无向图的邻接表表示: (2)LocateVex(ALGraph &G,char v)图查找信息: (3)DFSTrave

实验四 图的实现和应用 实验报告 20162305

实验四 图的实现和应用 实验报告 20162305 实验一 邻接矩阵实现无向图 实验要求 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器.给出伪代码,产品代码,测试代码(不少于5条测试) 实验过程 用邻接矩阵表示无向图,首先我们先明确什么是邻接矩阵.邻接矩阵就是用矩阵的方式来表示不同结点之间的关系,对于无向图来说,如果结点(i,j)之间有联系,则在矩阵中(i,j)所对应的的点的值为1,

20162313 苑洪铭 实验四 图的实现与应用

20162313 苑洪铭 实验四 图的实现与应用 实验1 要求 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器 给出伪代码,产品代码,测试代码(不少于5条测试) 内容 邻接矩阵(Adjacency Matrix):是表示顶点之间相邻关系的矩阵.设G=(V,E)是一个图,其中V={v1,v2,-,vn}. 对无向图而言,邻接矩阵一定是对称的,而且主对角线一定为零,副对角线不一定为0. 总

20162304 2017-2018-1 实验四-图的实现与应用

实验四-图的实现与应用 实验四 图的实现与应用-1 试验内容 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器 给出伪代码,产品代码,测试代码(不少于5条测试) 实验结果 要想用邻接矩阵实现图,首先需要使用一个一维数组存放顶点,一个二维数组存放边: 然后清楚两个顶点之间的关系如何表示,用邻接矩阵表示出来是什么样的: 其中值得注意的是对角线上的应该是0,因为自己和自己是没有路径的. 广度优

20162327WJH实验四——图的实现与应用

20162327WJH实验四--图的实现与应用 实 验 报 告 课程:程序设计与数据结构 班级: 1623 姓名: 王旌含 学号:20162327 成绩: 指导教师:娄嘉鹏 王志强 实验日期:11月20日 实验密级: 非密级 预习程度: 已预习 实验时间:15:25-17:15 必修/选修: 必修 实验序号: cs_23 实验内容 实验一 1.实验内容 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,

20162328蔡文琛 实验四 图的实现与应用

20162328蔡文琛 大二 实验四 任务详情 实验1 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器 实现类 public class MatrixUDG { private char[] mVexs; // 顶点集合 private int[][] mMatrix; // 邻接矩阵 /* * 创建图(自己输入数据) */ public boolean isEmpty(){ bool

实验四 图的遍历算法设计与实现

一.实验名称:图的遍历算法设计与实现 二.实验目的: 1.掌握图的深度优先遍历的算法. 2.掌握图的广度优先遍历的算法. 3.实验章节:算法设计与分析 第四章 三.实验内容.实验问题和程序运行结果 第一部分 广度优先遍历算法 1. 分析Graph类,画出Graph类初始化以后的Graph对象的数据结构图. 2. 分析BFS函数,画出流程图. 3. 上述程序   int data[7][7]={{ 1,-1,-1,-1,-1,-1,-1}, { 6, 3, 2,-1,-1,-1,-1}, { 0,

20162320刘先润大二 实验四 图及应用

实验涉及代码 AMatrix.AMatrixTest.CrossList.CrossListTest.Road.RoadTest 图的实现与应用-1 实验目的:用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器 实现思路:实现邻接矩阵得确定一个表示方法,对于结点于结点之间的关系得使用二元数组来实现.由于无向图的邻接矩阵是对称矩阵,且其左上角到右下角的对角线上值为零,这是其中应用的一条性质.

实验四 图的实现与应用

图的实现与应用 - 1 题目要求: 用邻接矩阵实现无向图(边和顶点都要保存),实现在包含添加和删除结点的方法,添加和删除边的方法,size(),isEmpty(),广度优先迭代器,深度优先迭代器 给出伪代码,产品代码,测试代码(不少于5条测试) 上方提交代码链接 附件提交测试截图 实验内容: 邻接矩阵:假设图G=(V,E)有n个结点,即V={v0,v1,-,vn-1},E可用如下形式的矩阵A描述,对于A中的每一个元素aij,满足:aij=1表示i和j节点有边相连,aij=0表示i和j没有边相连.