20172305 2018-2019-1 《Java软件结构与数据结构》第九周学习总结

20172305 2018-2019-1 《Java软件结构与数据结构》第九周学习总结

教材学习内容总结

本周内容主要为书第十五章内容:

  • (结点和结点之间的链接构成)
    • 顶点:结点
    • 边:结点之间的链接
    • 邻接:两个结点之间有一条连通边,则两个结点是邻接的,有时邻接顶点称为邻居。
    • 自循环(环):连通一个顶点及其自身的边
    • 环路:一种首顶点和末顶点相同且没有重边的路径,没有环路的图称为无环的。
  • 无向图:一种边为无序结点对的图html

    • 若是无向图拥有最大数目的连通顶点的边,则认为这个无向图是彻底的java

      • 对有n个顶点的无向图,要使图彻底就要求有n(n-1)/2条边。
    • 若是无向图中的任意两个顶点之间都存在一条路径,则认为这个无向图是连通的git

    • 无向图的路径是双向的。
    • 路径:图中一系列边,每条边连通两个顶点。
    • 无向树是一种连通的无环无向图,其中一个元素被指定为树根。算法

  • 有向图(双向图):边为有序顶点对的图,eg:边(A,B)容许从A向B游历,但不容许反方向的游历。数组

    • 若是有向图的每两个顶点之间都有两条方向相反的边链接,则认为这个有向图是彻底的网络

    • 若是有向图的任意两个顶点之间都存在一条路径,且链接两个顶点的路径中全部的边都必须同向,则认为这个有向图是连通的数据结构

    • 拓扑序(排列获得的顶点次序):若是有向图中没有环路,且有一条从A到B的边,则能够把顶点A安排在顶点B以前。
    • 有向图的路径是单向的。
    • 路径:图中连通两个顶点的有向边序列。
    • 有向树是一种指定了一个元素做为树根的有向图。学习

      • 不存在其余顶点到树根的链接
      • 每一个非树根元素刚好有一个链接
      • 树根到每一个其余顶点都有一条路径
  • 网络(加权图):一种每条边都带有权重或代价的图
    • 三元组来表示每条边:起始顶点、终止顶点、权重测试

      • 无向网络的起始顶点与终止顶点能够互换
      • 有向图必须包含每一个有向链接的三元组
  • 图算法--遍历
    • 广度优先遍历(相似树的层序遍历)优化

      • 首先从一个未走到过的顶点做为起始顶点,好比元素0顶点做为起点。
      • 沿0顶点的边去尝试访问其它未走到过的顶点,首先发现2顶点尚未走到过,因而来到了2顶点。
      • 返回到0顶点,再以0顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了1顶点。
      • 返回到0顶点,再以0顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了5顶点。
      • 可是,此时沿0顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到2顶点。
      • 沿2顶点的边去尝试访问其它未走到过的顶点,(由于1顶点已经走过了)首先发现3顶点尚未走到过,因而来到了3顶点。
      • 返回到2顶点,再以2顶点做为出发点继续尝试访问其它未走到过的顶点,这样来到了4顶点。
      • 可是,此时沿4顶点的边,已经不能访问到其它未走到过的顶点了,至此,全部顶点咱们都走到过了,遍历结束。
    • 深度优先遍历(相似树的前序遍历)

      • 首先从一个未走到过的顶点做为起始顶点,好比元素0顶点做为起点。
      • 沿0顶点的边去尝试访问其它未走到过的顶点,首先发现2顶点尚未走到过,因而来到了2顶点。
      • 再以2顶点做为出发点继续尝试访问其它未走到过的顶点,这样又来到了1顶点。
      • 再以1号顶点做为出发点继续尝试访问其它未走到过的顶点。
      • 可是,此时沿1顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到2顶点。
      • 返回到2号顶点后,以2顶点做为出发点继续尝试访问其它未走到过的顶点,此时又会来到3顶点,再以3号顶点做为出发点继续访问其它未走到过的顶点,因而又来到了5号顶点。
      • 可是,此时沿5顶点的边,已经不能访问到其它未走到过的顶点了,因此须要返回到3顶点。
      • 返回到3顶点后,以3顶点做为出发点继续尝试访问其它未走到过的顶点,此时会来到4顶点,再以4顶点做为出发点继续访问其它未走到过的顶点。
      • 可是,此时沿4顶点的边,已经不能访问到其它未走到过的顶点了,至此,全部顶点咱们都走到过了,遍历结束。
    • 图的遍历能够从任一顶点开始
    • 深度优先遍历和广度优先遍历的位移不一样是使用到了栈而不是队列来管理遍历。

  • 图算法--测试连通性
    • 在一个图中,不管哪一个为起始顶点,当且仅当广度优先遍历中的顶点数等于图中的顶点数目时,则该图是连通的。
  • 图算法--最小生成树
    • 生成树是一棵含有图中全部顶点和部分边(可能不是全部边)的树。
    • 最小生成树是边的权重总和和小于或等于同一个图中其余任何一棵生成树的权重总和。

      • 非连通的无向图,不存在最小生成树
      • 权重不必定和距离成正比
      • 权重多是0或负数
      • 若存在相等的权重,那么最小生成树可能不惟一
    • Prim算法:
  • 图算法--判断最短路径
    • 方法一:断定起始顶点与目标顶点之间的字面意义上的最短路径,即两个顶点之间的最小边数。
    • 方法二:Dijkstra算法

    • 以0顶点为开始位置,0顶点到1顶点的权重为3,到2顶点的权重为max,到3顶点的权重为4,到4顶点的权重为5。

    • 选取权重最小的顶点为1顶点,由于全部的权重都为正,那么0顶点到1顶点的最短距离就是3,再以1顶点开始,1顶点能够到4顶点和2顶点,到4顶点的位置是3+1=4<5,即0顶点到4顶点的最短距离是经过1顶点的,再看到2顶点的位置是3+10=13<max,即0顶点直接到2顶点的距离为max,可是经过1顶点能够缩短为13。

    • 扣除0顶点和1顶点已经肯定好了最短距离以后,选取最短的权重为0顶点到3顶点为4。

    • 以3顶点开始,3顶点能够到2顶点,到2顶点的位置是4+7=11<13,即0顶点到2顶点的最短距离,从max到经过1顶点再到经过3顶点到2顶点。

    • 扣除0顶点、1顶点和3顶点,已经肯定好了最短距离以后,选取最小的权重为0顶点到1顶点再到4顶点为4,4顶点能够到1顶点、3顶点和2顶点,可是3顶点和1顶点已经肯定好了,只剩2顶点,那么到2顶点的位置为3+1+6=10<11,即到2顶点的最短距离改成经过1顶点、4顶点最后到2顶点。

    • 最后扣除0顶点、1顶点、3顶点和4顶点,只剩下2顶点,距离只剩10,因此到2顶点的最短距离为10,结束算法。

  • 图的实现策略
    • 邻接列表

      • 在邻接表的表示中,无向图的同一条边在邻接表中存储的两次。若是想要知道顶点的读,只须要求出所对应链表的结点个数便可。
      • 有向图中每条边在邻接表中只出现一此,求顶点的出度只须要遍历所对应链表便可。求出度则须要遍历其余顶点的链表。
        无向图的邻接列表:

        有向图的邻接列表:
    • 邻接矩阵

      • 在邻接矩阵表示中,无向图的邻接矩阵是对称的。矩阵中第 i 行或 第 i 列有效元素个数之和就是顶点的度。
      • 在有向图中 第 i 行有效元素个数之和是顶点的出度,第 i 列有效元素个数之和是顶点的入度。
        无向图的邻接矩阵:

        有向图的邻接矩阵:
    • 邻接矩阵与邻接表优缺点:邻接矩阵的优势是能够快速判断两个顶点之间是否存在边,能够快速添加边或者删除边。而其缺点是若是顶点之间的边比较少,会比较浪费空间。由于是一个 n∗nn∗n 的矩阵。而邻接表的优势是节省空间,只存储实际存在的边。其缺点是关注顶点的度时,就可能须要遍历一个链表。还有一个缺点是,对于无向图,若是须要删除一条边,就须要在两个链表上查找并删除。
    • 边集数组

      • 边集数组由两个一维数组构成:一个存储顶点信息; 一个存储边的信息,这个边数组每一个数据元素由一条边的起点下标(begin)、终点下标(end)、和权(weight)组成。
      • 边集数组关注的是边的集合,在边集数组中要查找一个顶点的度须要扫描整个边数组,效率并不高。所以它更适合对边依次进行处理的操做,而不适合对顶点相关的操做。
        无向图的边集数组:

        有向图的边集数组:
    • 十字链表

      • 十字链表是为了便于求得图中顶点的度(出度和入度)而提出来的。用十字链表来存储有向图,能够达到高效的存取效果。它是综合邻接表和逆邻接表形式的一种链式存储结构。

        绿色链表表示以结点A为弧头的弧组成的链表。黄色链表表示以结点A为弧尾的弧组成的链表。
    • 邻接多重表

      • 邻接多重表主要用于存储无向图。若是用邻接表存储无向图,每条边的两个边结点分别在以该边所依附的两个顶点为头结点的链表中,这给图的某些操做带来不便。所以,在进行这一类操做的无向图的问题中采用邻接多重表做存储结构更为适宜。

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

  • 问题1:如何测试有向图和无向图的连通性?
  • 问题1解决方案:无向图的连通性在书上已经给出,字面理解就是任意两点之间都存在一条路径,而从编写的角度就是一个顶点开始深度优先遍历或是广度优先遍历得出的顶点数。而有向图的是具备方向的,若是这样的话咱们如何断定有向图的连通性?在查询的过程当中,发现有向图的连通性能够分红强连通图、弱连通图以及单向连通图。书上的图15.4的连通图就是一个弱连通图,若是是强连通图则须要在此基础上每两点之间都添加一条与之方向相反的线。针对有向图的测试连通性的方法能够用到Tarjan算法

    • 在有向图中, 若对于任意两个两点之间, 都存在互逆的路径,则称此图是强连通图。即有向图中,若对于任意两个不一样的顶点x和y,都存在从x到y以及从y到x的路径,则称G是强连通图。
    • 有向图的全部的有向边替换为无向边,所获得的图称为原图的基图。若是一个有向图的基图是连通图,则有向图是弱连通图。
    • 若是有向图中,对于任意节点v1和v2,至少存在从v1到v2和从v2到v1的路径中的一条,则原图为单向连通图。
    • 强连通图、连通图、单向连通图三者之间的关系是,强连通图必然是单向连通的,单向连通图必然是弱连通图。
    • 在Tarjan算法中为每一个节点i维护了如下几个变量:
      DFN[i]:深度优先搜索遍历时节点i被搜索的次序。
      low[i]:节点i可以回溯到的最先位于栈中的节点。
      flag[i]:标记几点i是否在栈中。
    • Tarjan算法的运行过程:
      (1)首先就是按照深度优先搜索算法搜索的次序对图中全部的节点进行搜索。
      (2)在搜索过程当中,对于任意节点u和与其相连的节点v,根据节点v是否在栈中来进行不一样的操做:
      a.节点v不在栈中,即节点v尚未被访问过,则继续对v进行深度搜索。
      b.节点v已经在栈中,即已经被访问过,则判断节点v的DFN值和节点u的low值的大小来更新节点u的low值。若是节点v的 DFN值要小于节点u的low值,根据low值的定义(可以回溯到的最先的已经在栈中的节点),咱们须要用DFN值来更新u 的low值。
      (3)在回溯过程当中,对于任意节点u用其子节点v(其实不能算是子节点,只是在深度遍历的过程当中,v是在u以后紧挨着u的节点)的   low值来更新节点u的low值。由于节点v可以回溯到的已经在栈中的节点,节点u也必定可以回溯到。由于存在从u到v的直接路径,因此v可以到的节点u也必定可以到。
      (4)对于一个连通图,咱们很容易想到,在该连通图中有且仅有一个节点u的DFN值和low值相等。该节点必定是在深度遍历的过程当中,该连通图中第一个被访问过的节点,由于它的DFN值和low值最小,不会被该连通图中的其余节点所影响。

代码学习中的问题和解决过程

  • 问题1:如何理解书中深度和广度的遍历代码?递归形式如何表示?
  • 问题1的解决方案:广度优先遍历和深度优先遍历的区别是在于广度优先遍历用的是队列,深度优先遍历用的是栈,一个是先进先出另外一个后进先出。
    • 遍历是建立了一个无序列表和栈或是队列,队列或栈用来存放的是索引值,无序列表存储的是结果,先进行判断若是你想查找的的索引值是超出用来存放内容的数组的长度的话,就会直接迭代无序列表,注意此时的无序列表是没有内容的,因此迭代出的内容也什么也没有。若是没有超过则建立一个布尔的一维数组用来记录每一个索引值对应的内容是否被查过,先记录每一个索引值都未被查到,再将开始查找元素的索引值存入栈或队列中,并将该索引值对应的布尔型数组内存放的false改成true来肯定是该元素已经查过。进入while循环变量x是刚入栈或是队列的索引值,广度优先遍历是将该索引值对应的内容添加到无序列表的尾部,再进入for循环不断将为未存元素存入队列中,经过判断存入队列的元素是否与x元素有边,若是有边还未被查找的元素就会被存入队列中,而后跳出for循环在while循环中此时更换出队的索引值,再进入for循环经过判断条件来肯定继续存放在队列中的索引值,这样经过不一样的索引值来实现图上的每个元素都会连着下一个元素,相似层序遍历的思路。而深度优先遍历循环体条件和判断条件都相同,可是判断条件内部不一样,须要辅助一个布尔型的变量来判断是否与查找元素有联系,深度优先遍历与广度优先遍历不一样的是深度有可能会遇到该元素没有与之相连的边的元素未被查到,而与它相连的边的被查元素还有边和未查元素,这样就须要返回到上一个,在从上一个开始进行循环,而多出的那个判断条件就是肯定该元素是否符合上述可能状况若是符合的话就会弹出,到上一个元素位置进行查找,这是广度优先遍历不会遇到的状况,也是改用栈的缘由,深度优先遍历相似与前序遍历的思路。根据书上所写能够用递归的方法编写代码,附递归版代码(广度优先遍历的代码在网上查找的)
    public void DFSRecursion() {
          boolean[] visited = new boolean[numVertices];
          for (int i = 0; i < numVertices; i++)
              visited[i] = false;
          for(int i=0;i<numVertices;i++) {
              if (!visited[i]) {
                  depthFirstSearch(visited,i);
              }
          }
      }
      private void depthFirstSearch(boolean[] isVisited,int  i) {
          System.out.print(vertices[i]+" ");
          isVisited[i]=true;
          int w=getFirstNeighbor(i);
          while (w!=-1) {
              if (!isVisited[w]) {
                  depthFirstSearch(isVisited,w);
              }
              w=getNextNeighbor(i, w);
          }
      }
    
      public void broadFirstSearch() {
          boolean[] visited = new boolean[numVertices];
          for (int i = 0; i < numVertices; i++)
              visited[i] = false;
          for(int i=0;i< numVertices;i++) {
              if(!visited[i]) {
                  broadFirstSearch(visited, i);
              }
          }
      }
      private void broadFirstSearch(boolean[] isVisited,int i) {
          int u,w;
          LinkedList queue=new LinkedList();
    
          System.out.print(vertices[i]+"  ");
          isVisited[i]=true;
          queue.addLast(vertices[i]);
          while (!queue.isEmpty()) {
              u=((Integer)queue.removeFirst()).intValue();
              w=getFirstNeighbor(u);
              while(w!=-1) {
                  if(!isVisited[w]) {
                      System.out.print(vertices[w]+"  ");
                      isVisited[w]=true;
                      queue.addLast(w);
                  }
                  w=getNextNeighbor(u, w);
              }
          }
      }

  • 问题2:PP15.7如何肯定最短路径和最便宜路径?
  • 问题2的解决方案:根据题目要求容许客户输入两个城市以及两个城市之间的价格,这就相似加权图,根据GraphADT接口以及实现的添加顶点和添加边的方法都没有涉及到权重(这里的权重为两个城市之间的价格),而无向图实现的是肯定两个顶点之间是否有边,用一个二维布尔型数组存储对应顶点是否有边,咱们的想法是能够将是否有边来改成数字,用票价来实现是否有联系。那么问题又来了,没有联系的如何来界定?定义为最大仍是最小?若是定义为最小为零的话,咱们的选择就要在排除零的前提下进行Dijkstra算法,可是若是咱们将无联系的看做是无穷大的状况下,就会产生不同的效果,在选取权重较小即最便宜路径的时候就会很方便,并且事实上提供的算法也是要将无联系的设为最大,就如同教材内容总结部分叙述的算法同样。可是,不管是无穷大仍是无穷小可能是用什么表示?和数序符号同样么?API提供了解决办法(侯泽洋同窗提供):Double.POSITIVE_INFINITY表示的是正无穷大值,Double.NEGATIVE_INFINITY表示的是负无穷大值。这样就能够实现了,可是Dijkstra算法如何书写呢?又是一个问题好丧...在网上查了几个,没有适用成功因此经过看侯泽洋的博客找到了一个不错的博客,用他的算法来实现的具体代码,并且侯泽洋的代码层次设计的很是好,实现的也干净利落,很强大,是我值得学习的。此外,最短路径就能够用到Graph内的shortestPathLength方法和iteratorShortestPath实现,我在此基础上经过返回最短路径的数值,若是是1的话就会存在直达列车,大于1的话就会是不可直达,在同经过遍历最短路径的迭代方法进行输出最短路径就好。

代码托管

上周考试错题总结

错题已经在上周博客写过...

结对与互评

点评(王禹涵)

  • 博客中值得学习的或问题:
    • 广度优先遍历和深度优先遍历的分析最好和最小生成树的算法分析的那么细致就好,建议问题二的代码能够附上去。
  • 代码中值得学习的或问题:
    • 分析了最小生成树的Prim算法,很强大,经过你的分析有助于我对算法的理解,很棒!
  • 基于评分标准,我给本博客打分:7分。
    • 得分状况以下:
    • 正确使用Markdown语法(加1分)
    • 模板中的要素齐全(加1分)
    • 教材学习中的问题和解决过程, 三个问题加3分
    • 代码调试中的问题和解决过程, 一个问题加1分
    • 感想,体会不假大空的加1分
    • 点评认真,能指出博客和代码中的问题的加1分

点评(方艺雯)

  • 博客中值得学习的或问题:
    • 图片清晰,总结的也很到位,也能够对本章的算法进行简单的总结,博客中的问题二和代码中的问题二能够整合一下。
  • 代码中值得学习的或问题:
    • 代码中的问题总结的很好,特别是那个迭代器的广度优先遍历用到分析的特别细致,建议能够和算法内容进行联系,或是在分析代码的过程当中查一些算法的相关知识。
  • 基于评分标准,我给本博客打分:9分。
  • 得分状况以下:
    • 正确使用Markdown语法(加1分)
    • 教材学习中的问题和解决过程, 二个问题加2分
    • 代码调试中的问题和解决过程, 四个问题加4分
    • 感想,体会不假大空的加1分
    • 点评认真,能指出博客和代码中的问题的加1分

互评对象

感悟

第十五章的图感受和红黑树同样,要构造很乱的数据结构来实现,此外还有一堆算法能够优化图的操做(算法做用很好,可是难于理解,有的思路就难理解)。好在有些代码给出了,可是书上的遍历代码就没给全,致使始终读不懂一个方法具体干吗的,具体实现的策略也不一样于以前的数据结构。要保持好的心态来学习代码,切莫急功近利...

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 0/0 1/1 15/15
第二周 703/703 1/2 20/35
第三周 762/1465 1/3 20/55
第四周 2073/3538 1/4 40/95
第五周 981/4519 2/6 40/135
第六周 1088/5607 2/8 50/185
第七周 1203/6810 1/9 50/235
第八周 2264/9074 2/11 50/285
第九周 2045/11119 1/12 50/335

参考资料

相关文章
相关标签/搜索