20182323 2019-2020-1 《数据结构与面向对象程序设计》第10周学习总结

目录

学号20182323 2019-2020-1 《数据结构与面向对象程序设计》第10周学习总结

教材学习内容总结

第19章

图的基本概念

  • 线性表和树两类数据结构,线性表中的元素是“一对一”的关系,树中的元素是“一对多”的关系,本章所述的图结构中的元素则是“多对多”的关系。

    图是一种复杂的非线性结构,在图结构中,每个元素都可以有零个或多个前驱,也可以有零个或多个后继,也就是说,元素之间的关系是任意的。

  • 无向图:无向图是由顶点和边构成。

  • 有向图:有向图是由顶点和有向边构成。

  • 完全图:如果任意两个顶点之间都存在边叫完全图,有向的边叫有向完全图。如果无重复的边或者顶点到自身的边叫简单图。

图的节点访问

/**
 * 无向简单图的节点
 * @author caoqian
 */
public class GraphNode<T> {
    T data;
    List<GraphNode<T>> neighborList;
    boolean visited;

    public GraphNode(T data){
        this.data = data;
        neighborList = new ArrayList<GraphNode<T>>();
        visited = false;
    }

    public boolean equals(GraphNode<T> node){
        return this.data.equals(node.data);
    }

    /**
     * 还原图中所有节点为未访问
     */
    public void restoreVisited(){
        restoreVisited(this);
    }

    /**
     * 还原node的图所有节点为未访问
     * @param node
     */
    private void restoreVisited(GraphNode<T> node){
        if(node.visited){
            node.visited = false;
        }

        List<GraphNode<T>> neighbors = node.neighborList;
        for(int i = 0; i < neighbors.size(); i++){
            restoreVisited(neighbors.get(i));
        }

    }
}

图的深度优先和广度优先搜索

1.深度优先

1.1、介绍:

图的深度优先搜索,和树的先序遍历比较类似。

思路:假设初始状态是图中所有顶点均未被访问,则从某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通的顶点都被访问到。 若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。显然,深度优先搜索是一个递归的过程。

1.2、无向图深度优先搜索图解

对上面的图G1进行深度优先遍历,从顶点A开始。

第1步:访问A。

第2步:访问(A的邻接点)C。在第1步访问A之后,接下来应该访问的是A的邻接点,即”C,D,F”中的一个。但在本文的实现中,顶点ABCDEFG是按照顺序存储,C在”D和F”的前面,因此,先访问C。

第3步:访问(C的邻接点)B。在第2步访问C之后,接下来应该访问C的邻接点,即”B和D”中一个(A已经被访问过,就不算在内)。而由于B在D之前,先访问B。

第4步:访问(C的邻接点)D。

第5步:访问(A的邻接点)F。

第6步:访问(F的邻接点)G。

第7步:访问(G的邻接点)E。

访问顺序是:A -> C -> B -> D -> F -> G -> E

1.3、无向图深度优先搜索图解

对上面的图G2进行深度优先遍历,从顶点A开始。

访问顺序是:A -> B -> C -> E -> D -> F -> G

2.广度优先

2.1、介绍

从图中某顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。换句话说,广度优先搜索遍历图的过程是以v为起点,由近至远。

2.2、无向图广度优先搜索图解

访问顺序是:A -> C -> D -> F -> B -> G -> E

2.3、有向图广度优先搜索图解

访问顺序是:A -> B -> C -> E -> F -> D -> G

图的邻接表和邻接矩阵

图的存储结构主要分两种,一种是邻接矩阵,一种是邻接表。

1.邻接矩阵

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。
设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

看一个实例,下图左就是一个无向图。

从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

从这个矩阵中,很容易知道图中的信息。

(1)要判断任意两顶点是否有边无边就很容易了;

(2)要知道某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;

(3)求顶点vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点;

而有向图讲究入度和出度,顶点vi的入度为1,正好是第i列各数之和。顶点vi的出度为2,即第i行的各数之和。

若图G是网图,有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

2.邻接表

邻接矩阵是不错的一种图存储结构,但是,对于边数相对顶点较少的图,这种结构存在对存储空间的极大浪费。因此,找到一种数组与链表相结合的存储方法称为邻接表。

邻接表的处理方法是这样的:

(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。

(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
例如,下图就是一个无向图的邻接表的结构。

从图中可以看出,顶点表的各个结点由data和firstedge两个域表示,data是数据域,存储顶点的信息,firstedge是指针域,指向边表的第一个结点,即此顶点的第一个邻接点。边表结点由adjvex和next两个域组成。adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标,next则存储指向边表中下一个结点的指针。

对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。如下图所示。

教材学习中的问题和解决过程

  • 问题1:图的广度优先搜索和深度优先搜索实现

  • 问题1解决方案:

public class GraphSearch<T> {

    public StringBuffer searchPathDFS = new StringBuffer();
    public StringBuffer searchPathBFS = new StringBuffer();

    /**
     * 深度优先搜索实现
     *
     */
    public void searchDFS(GraphNode<T> root) {
        if (root == null) {
            return;
        }

        // visited root
        if (searchPathDFS.length() > 0) {
            searchPathDFS.append("->");
        }
        searchPathDFS.append(root.data.toString());
        root.visited = true;

        for (GraphNode<T> node : root.neighborList) {
            if (!node.visited) {
                searchDFS(node);
            }
        }
    }

    /**
     * 广度优先搜索实现,使用队列
     *
     */
    public void searchBFS(GraphNode<T> root) {
        IQueue<GraphNode<T>> queue = new Queue<GraphNode<T>>();

        // visited root
        if (searchPathBFS.length() > 0) {
            searchPathBFS.append("->");
        }
        searchPathBFS.append(root.data.toString());
        root.visited = true;

        // 加到队列队尾
        queue.enqueue(root);

        while (!queue.isEmpty()) {
            GraphNode<T> r = queue.dequeue();
            for (GraphNode<T> node : r.neighborList) {
                if (!node.visited) {
                    searchPathBFS.append("->");
                    searchPathBFS.append(node.data.toString());
                    node.visited = true;

                    queue.enqueue(node);
                }
            }
        }
    }
}

代码调试中的问题和解决过程

  • 问题1:代码出现问题

  • 解决过程:后来在对之前写过的代码进行排查发现,是LinkedList类没有声明StackADT这个接口。

上周错题

上周无错题

代码托管

结对及互评

点评模板:

  • 博客中值得学习的或问题:

    • 随着学习内容难度增加,问题分析更加深刻
    • 不断查阅资料,努力解决出现的问题
  • 代码中值得学习的或问题:

    • 代码的逻辑性有待提高
    • 代码中适当加入注释会更好
  • 基于评分标准,我给本博客打分:12分。得分情况如下:

  1. 正确使用Markdown语法(加1分):

    • 不使用Markdown不加分
    • 有语法错误的不加分(链接打不开,表格不对,列表不正确...)
    • 排版混乱的不加分
  2. 模板中的要素齐全(加1分)

    • 缺少“教材学习中的问题和解决过程”的不加分
    • 缺少“代码调试中的问题和解决过程”的不加分
    • 代码托管不能打开的不加分
    • 缺少“结对及互评”的不能打开的不加分
    • 缺少“上周考试错题总结”的不能加分
    • 缺少“进度条”的不能加分
    • 缺少“参考资料”的不能加分
  3. 教材学习中的问题和解决过程(2分)

  4. 代码调试中的问题和解决过程(2分)

  5. 本周有效代码超过300分行的(加0分)

  6. 其他加分:

    • 周五前发博客的加1分
    • 感想,体会不假大空的加1分
    • 进度条中记录学习时间与改进情况的加1分
    • 有动手写新代码的加1分
    • 错题学习深入的加1分
    • 点评认真,能指出博客和代码中的问题的加1分
    • 结对学习情况真实可信的加1分

点评过的同学博客和代码

  • 本周结对学习情况
    • 结对同学学号20182315
    • 结对照片
    • 结对学习内容
      • 图的概念,图的节点访问,图的深度优先和广度优先搜索,图的邻接表和邻接矩阵

其他(感悟、思考等,可选)

继续潜心学习,只问初心,无问西东。

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 10000行 30篇 400小时
第一周 77/77 2/2 15/15
第三周 424/501 3/5 30/30
第四周 393/894 2/7 30/30
第五周 320/1214 1/8 30/30
第六周 904/2118 2/10 30/30
第7周 1350/3468 3/13 30/30
第8周 342/3810 1/14 30/30
第9周 6048/9858 4/18 30/30
第10周 1063/10921 2/20 30/30
  • 计划学习时间:25小时

  • 实际学习时间:20小时

  • 改进情况:

参考资料

posted @ 2019-11-30 15:42  20182323曹骞  阅读(240)  评论(0编辑  收藏  举报