1.学习总结(2分)

1.1图的思维导图

 

1.2 图结构学习体会

 图是数据结构的一章很重要的内容,属于复杂的非线性数据结构,但无论多么复杂的图都是由顶点和边构成的。图可分为有向图和无向图,图的存储结构有邻接表存储和邻接矩阵存储,邻接表表示不唯一,邻接矩阵表示是唯一的。图的基本运算包括创建图,输出图,遍历图等等。图的其它运算包括图的遍历和生成最小树以及最短路径和拓扑排序,每项内容都有对应的一到两个算法,刚开始学一个两个还好,到后面累计起来就有一定的难度,所以需要我们多花时间去理解和区分这些算法。经过一段时间的学习后,我对图有了一定的了解,发现它还将之前学习的并查集和数组,广义表等内容联系起来,这也就是图的重要性和难点之一。当然,除了一些理论知识外,实践操作也是很重要的,数据结构终归还是需要我们去敲代码,从实际例子中更好的理解算法。而经过PTA的题目后,发现一些原本很复杂的综合应用问题能灵活的运用图来解决,但我本身对于有些题目还是有些不应手,需要借助百度和同学的帮助,但也在这些过程中对图有了更好的理解。相信经过日积月累,一定能把这章内容得心应手的运用。

      其中:

  • 深度遍历算法:深度遍历算法可以邻接表为存储结构,也可以邻接矩阵为存储结构,二者类似。
  • 广度遍历算法:广度遍历算法可以邻接表为存储结构,这时需要一个队列,以类似于层次遍历二叉树的方式遍历图,也可以以邻接矩阵为存储结构。
  • Prim和Kruscal算法:Prim和Kruscal算法都是用来构造最小生成树的两种算法,而Prim算法是一种构造性算法,Kruskal算法是一种按权值的递增次序选择合适的边来构造。同时Kruscal可以进行改进,一是将边集排序改为边排序,二是采用并查集。
  • Dijkstra算法:该算法是用来求最短路径的算法,需要注意的是此算法不适合含有负权值的带权图求单源最短路径,且一旦某个顶点u添加到S中,则在算法后面的执行中,不会再修改源点v到顶点u的最短路径长度。
  • 拓扑排序算法:拓扑排序是指在一个有向图中找一个拓扑排序的过程,对于给定的有向图,采用邻接表作为存储结构,为每个顶点设立一个链表。

    2.PTA实验作业(4分)

  • 2.1 题目1:图着色问题

  • 2.2 设计思路(伪代码或流程图)

  • 2.3 代码截图

  • 2.4 PTA提交列表说明

一开始在只有一分,只对了一个最小图的测试点,在dev上修改了好久都还是这样,后来发现其中一个错误在于每次进入for循环判断的时候没有把数组刷新。修改好之后变成了6分,但还是部分正确。通过提交列表的提示发现部分正确的原因是一些情况的答案错误,也就是在结果的判断代码上出了错误,经过一番观察发现最后方案行不通的情况有两种,一是方案使用的颜色数不是k种,第二种也就是我漏掉的一种是查到了不符合条件的边,加上这种情况后就正确了。

2.1 题目2:排座位

2.2 设计思路

 通过“朋友的朋友也是朋友这句话”知道要用并查集完成,由于敌对关系无传递性,要通过一个二位数组存储两两之间的关系,之后通过条件选择判断按要求输出即可。

2.3 代码截图

 

 

2.4 PTA提交列表说明

这题的题目有好几个弯,对语文的理解能力要求还有点高,一开始用了二维数组来解题无法通过,无奈之下参考了百度才发现要用到之前学过的并查集的知识,因为朋友的朋友也是朋友,说明在一个集合里。但修改后还是无法完全正确,认真推敲之后发现题目中说敌人的敌人不一定是朋友,朋友的敌人也不一定是敌人,所以敌对是有两种情况的,因此就不是用到并查集而是要建立一个环,绕过这好几个弯才把题目解对了。

2.1 题目3:公路村村通

2.2 设计思路

2.3 代码截图

2.4 PTA提交列表说明

这题用Prim算法生成最小生成树,虽然课上对Prim算法有了一定理解,一些选择填空题也能做出来,但到了代码这里还是不能成功,于是向同学询问了思路,一开始部分正确是由于粗心在两个函数的变量定义中,将两个变量用了一个字母表示,从而混乱导致错误,发现问题后修改后就正确了。

3.截图本周题目集的PTA最后排名

3.1 PTA排名

 

3.2 我的总分:200分:2分

4. 阅读代码:拓扑排序

(1)概念

拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:1.每个顶点出现且只出现一次。2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。有向无环图(DAG)才有拓扑排序,非DAG图没有拓扑排序一说。

(2)应用

通常用来“排序”具有依赖关系的任务。比如,如果用一个DAG图来表示一个工程,其中每个顶点表示工程中的一个任务,用有向边<A,B><A,B>表示在做任务 B 之前必须先完成任务 A。故在这个工程中,任意两个任务要么具有确定的先后关系,要么是没有关系,绝对不存在互相矛盾的关系(即环路)。

(3)实现

根据上面讲的方法,我们关键是要维护一个入度为0的顶点的集合。

图的存储方式有两种:邻接矩阵和邻接表。这里我们采用邻接表来存储图,C++代码如下:

#include<iostream>
#include <list>
#include <queue>
using namespace std;

/************************类声明************************/
class Graph
{
    int V;             // 顶点个数
    list<int> *adj;    // 邻接表
    queue<int> q;      // 维护一个入度为0的顶点的集合
    int* indegree;     // 记录每个顶点的入度
public:
    Graph(int V);                   // 构造函数
    ~Graph();                       // 析构函数
    void addEdge(int v, int w);     // 添加边
    bool topological_sort();        // 拓扑排序
};

/************************类定义************************/
Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V];

    indegree = new int[V];  // 入度全部初始化为0
    for(int i=0; i<V; ++i)
        indegree[i] = 0;
}

Graph::~Graph()
{
    delete [] adj;
    delete [] indegree;
}

void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w); 
    ++indegree[w];
}

bool Graph::topological_sort()
{
    for(int i=0; i<V; ++i)
        if(indegree[i] == 0)
            q.push(i);         // 将所有入度为0的顶点入队

    int count = 0;             // 计数,记录当前已经输出的顶点数 
    while(!q.empty())
    {
        int v = q.front();      // 从队列中取出一个顶点
        q.pop();

        cout << v << " ";      // 输出该顶点
        ++count;
        // 将所有v指向的顶点的入度减1,并将入度减为0的顶点入栈
        list<int>::iterator beg = adj[v].begin();
        for( ; beg!=adj[v].end(); ++beg)
            if(!(--indegree[*beg]))
                q.push(*beg);   // 若入度为0,则入栈
    }

    if(count < V)
        return false;           // 没有输出全部顶点,有向图中有回路
    else
        return true;            // 拓扑排序成功
}

 

 

posted @ 2018-06-18 14:04  zhqiieng  阅读(234)  评论(1编辑  收藏  举报