From 9233a697e664c9c8a3891716728dced3e4df1e11 Mon Sep 17 00:00:00 2001 From: yinkanglong_lab Date: Mon, 5 Apr 2021 14:19:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +- 数据结构/7 图.md | 45 +++ 数据结构/7.cpp | 40 ++ .../1.1 图算法-Dijkstra算法 copy.md | 208 ++++++---- .../B类:数据结构算法/1.2 图算法-Floyd算法.md | 377 ++++++++---------- .../1.3 图算法-Bellman-Ford算法.md | 11 +- .../B类:数据结构算法/1.4 图算法-Kruskal算法.md | 2 +- 算法/B类:数据结构算法/1.5 图算法-Prim算法.md | 112 +----- 算法/B类:数据结构算法/1.cpp | 224 +++++++++++ 9 files changed, 617 insertions(+), 410 deletions(-) create mode 100644 数据结构/7.cpp create mode 100644 算法/B类:数据结构算法/1.cpp diff --git a/README.md b/README.md index 21b74f5e..1b2cd4d9 100644 --- a/README.md +++ b/README.md @@ -68,4 +68,10 @@ * 不能过度使用列表。 * 普通的论述直接分段。或者使用>表示引用。 * 而不是一直过度使用列表。用来缩进。 -* 换一个更好的md渲染引擎。 \ No newline at end of file +* 换一个更好的md渲染引擎。 + +### 补充 + +* 在写笔记的过程中应该控制好,“知识类”笔记的粒度。不能把一些没用的什么优势劣势啥玩意的也写上。又不是应付考试。你现在要做的是记住原理跟方法,当然就会知道这种思想的优势和劣势。 +* 粗粒度的笔记开始。尽量只记录关键点和关键点的解析。 + diff --git a/数据结构/7 图.md b/数据结构/7 图.md index 49c0dd0e..a2671e99 100644 --- a/数据结构/7 图.md +++ b/数据结构/7 图.md @@ -76,6 +76,51 @@ ### 创建 +* 图的邻接链表实现方法 + +```C++ +// 图的邻接链表实现方法 +#include +#include +#include +using namespace std; + +const int N = 1e5+1; +struct Edge +{ + int to; + int w; + Edge(int t = 0, int w_ = 0) : to(t), w(w_) {} +}; + +// edge向量的数组 +vector H1[N]; + +// edge向量的向量(查找需要遍历某个顶点所有边的集合) +vector> H2; + +// edge映射的向量(查找会更快) +vector> H3; + +int main() +{ + + H2.resize(N + 1); + H3.resize(N + 1); + for (int i = 1; i <= N; ++i) + { + int a, b, c; + cin >> a >> b >> c; + H1[a].push_back(Edge(b,c)); + H2[a].push_back(Edge(b, c)); + H3[a][b] = c; + } + + getchar(); + return 0; +} +``` + ### 搜索——广度优先搜索 * 广度优先搜索是一种图遍历算法,它从根节点开始遍历图并探索所有相邻节点。 然后,它选择最近的节点并浏览所有未探测的节点。 对于每个最近的节点,该算法遵循相同的过程,直到找到目标为止。 diff --git a/数据结构/7.cpp b/数据结构/7.cpp new file mode 100644 index 00000000..70920ea5 --- /dev/null +++ b/数据结构/7.cpp @@ -0,0 +1,40 @@ +// 图的邻接链表实现方法 +#include +#include +#include +using namespace std; + +const int N = 1e5+1; +struct Edge +{ + int to; + int w; + Edge(int t = 0, int w_ = 0) : to(t), w(w_) {} +}; + +// edge向量的数组 +vector H1[N]; + +// edge向量的向量(查找需要遍历某个顶点所有边的集合) +vector> H2; + +// edge映射的向量(查找会更快) +vector> H3; + +int main() +{ + + H2.resize(N + 1); + H3.resize(N + 1); + for (int i = 1; i <= N; ++i) + { + int a, b, c; + cin >> a >> b >> c; + H1[a].push_back(Edge(b,c)); + H2[a].push_back(Edge(b, c)); + H3[a][b] = c; + } + + getchar(); + return 0; +} \ No newline at end of file diff --git a/算法/B类:数据结构算法/1.1 图算法-Dijkstra算法 copy.md b/算法/B类:数据结构算法/1.1 图算法-Dijkstra算法 copy.md index e16a9eb9..cfb9e3a6 100644 --- a/算法/B类:数据结构算法/1.1 图算法-Dijkstra算法 copy.md +++ b/算法/B类:数据结构算法/1.1 图算法-Dijkstra算法 copy.md @@ -14,7 +14,7 @@ ## 1 问题分析 -* 最短路径算法。用于**计算一个节点到其他节点的最短路径。** +* 最短路径算法。用于**单源最短路算法** * Dijkstra算法算是**贪心思想**实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其他所有点的最短距离。 @@ -76,96 +76,136 @@ 时间复杂度$O(n^2)$ ## 5 算法实现 -``` +```C++ #include -#include +#include using namespace std; -const int Max=100; -string Int_to_String(int n)//int转换string -{ -ostringstream stream; -stream<> arc; //邻接矩阵 +public: + // 测试用的默认构造函数 + Graph(); + //构造函数,从命令行输入 + Graph(int vertex_num_,int edge_); + //打印所有的边 + void print_edge(); + // 打印整个邻接矩阵 + void print_arc(); + //求最短路径 + void Dijkstra(int begin);//单源最短路 + void Floyd();//多源最短路 + void Prim();//最小生成树 + void Kruskal();//最小生成树 }; -MGraph::MGraph(int n,int e){ - int i,j; - vertexNum=n; - arcNum=e; - for(int i=0;i>i>>j; - cin>>arc[i][j]; + +Graph::Graph(){ + this->vertex_num = 7; + this->edge=12; + this->arc = vector>(vertex_num,vector(vertex_num,INT_MAX)); + // cout<"+Int_to_String(G.vertex[i]); - else - path[i]=""; + +// 从命令行输入一个邻接矩阵 +Graph::Graph(int vertex_num_,int edge_){ + this->vertex_num=vertex_num_; + this->edge=edge_; + this->arc=vector>(vertex_num,vector(vertex_num,INT_MAX)); + int beg=0,end=0,weight=0; + for(int i=0;i>beg>>end>>weight; + arc[beg][end]=weight; + // 如果是无向图则需要添加反向的路径 + arc[end][beg]=weight; } - s[0]=v; - dist[v]=0; - int num=1; - for(int i=0;i(dist[k]+G.arc[k][j])){ - dist[j]=dist[k]+G.arc[k][j]; - path[j]=path[k]+"->"+Int_to_String(j); - } - } - dist[k]=0; - if(num==G.vertexNum) - break; - } - cout<<"找到终点的顺序为:"<>v; - cout<<"输入图的顶点数和边数:"<>n>>e; - MGraph G(n,e); - Dijkstra(G,v); - return 0; + +void Graph::print_edge(){ + for(int i=0;i result(vertex_num,INT_MAX); + result[begin]=0; + // 初始化距离集合,每次从中挑选 + vector distance(vertex_num,INT_MAX); + for(int i=0;i * [https://blog.csdn.net/jeffleo/article/details/53349825](https://blog.csdn.net/jeffleo/article/details/53349825) ## 1 问题分析 -* Floyd算法是一个经典的**动态规划算法**,它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。Floyd算法是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,算法目标是寻找从点i到点j的最短路径。 +* Floyd算法是一个经典的**动态规划算法**,它又被称为插点法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 +* Floyd算法是一种利用动态规划的思想寻找给定的加权图中**多源点最短路径的算法** ,算法目标是寻找从点i到点j的最短路径。 ## 2 算法原理 @@ -66,223 +67,181 @@ ```C++ #include -#include +#include using namespace std; -#define len 100 -#define INF 999999 +struct Dis { + string path; + int value; + bool visit; + Dis() { + visit = false; + value = 0; + path = ""; + } +}; class Graph{ - // 内部类 - private: - // 邻接表中表对应的链表的顶点 - class ENode{ - public: - int vex; // 顶点 - int weight; // 权重 - ENode *nextEdge; // 指向下一条弧 - }; - - // 邻接表中表的顶点 - class VNode{ - public: - char data; // 顶点信息 - ENode *firstEdge; // 指向第一条依付该顶点的弧 - }; - - // 私有成员 - private: - int n; // 节点个数 - int e; // 边的个数 - VNode mVexs[len]; - - public: - Graph(){ - ENode *node1, *node2; - n = 7; - e = 12; - - // 设置节点为默认数值 - string nodes = "ABCDEFG"; - // 输入节点 - for(int i=0; i < n; i++){ - mVexs[i].data = nodes[i]; - mVexs[i].firstEdge = NULL; - } - - // 设置边为默认值 - char edges[][2] = { - {'A', 'B'}, - {'A', 'F'}, - {'A', 'G'}, - {'B', 'C'}, - {'B', 'F'}, - {'C', 'D'}, - {'C', 'E'}, - {'C', 'F'}, - {'D', 'E'}, - {'E', 'F'}, - {'E', 'G'}, - {'F', 'G'} - }; - - // 边的权重 - int weights[len] = {12, 16, 14, 10, 7, 3, 5, 6, 4, 2, 8, 9}; - - // 初始化邻接表的边 - for(int i=0; i < e; i++){ - int start = get_Node_Index(edges[i][0]); - int end = get_Node_Index(edges[i][1]); - - // 初始化 node1 - node1 = new ENode(); - node1->vex = end; - node1->weight = weights[i]; - node1->nextEdge = NULL; - // 将 node 添加到 start 所在链表的末尾 - if(mVexs[start].firstEdge == NULL){ - mVexs[start].firstEdge = node1; - } - else{ - linkLast(mVexs[start].firstEdge, node1); - } - - // 初始化 node2 - node2 = new ENode(); - node2->vex = start; - node2->weight = weights[i]; - node2->nextEdge = NULL; - // 将 node 添加到 end 所在链表的末尾 - if(mVexs[end].firstEdge == NULL){ - mVexs[end].firstEdge = node2; - } - else{ - linkLast(mVexs[end].firstEdge, node2); - } - } - } - - // 相邻节点链接子函数 - void linkLast(ENode*p1, ENode*p2){ - ENode*p = p1; - while(p->nextEdge){ - p = p->nextEdge; - } - p->nextEdge = p2; - } - - // 返回顶点下标 - int get_Node_Index(char number){ - for(int i=0; i < n; i++){ - if(number == mVexs[i].data){ - return i; - } - } - return -1; //这句话永远不会执行的 - } - - // 输出邻接表 - void print(){ - for(int i=0; i < n; i ++){ - cout< "<vex; - temp = temp->nextEdge; - } - cout<vex == n){ - return enode->weight; - } - enode = enode->nextEdge; - } - return INF; - } - - // 弗洛伊德算法 - void floyd(){ - int dist[n][n]; // 距离矩阵 - int path[7][7]; // 路径矩阵, 7为节点数目 - int i, j, k; - int temp; - - // 初始化权重 - for(i = 0; i < n; i++){ - for(j = 0; j < n; j++){ - if(i == j){ - dist[i][j] = 0; - } - else{ - dist[i][j] = getWeight(i, j); - } - path[i][j] = i; - } - } - - // floyd 算法开始 - for(k = 0; k < n; k++){ - for(i = 0; i < n; i++){ - for(j = 0; j < n; j++){ - temp = (dist[i][k] == INF || dist[k][j] == INF)? INF : (dist[i][k] + dist[k][j]); - if(temp < dist[i][j]){ - dist[i][j] = temp; - path[i][j] = path[k][j]; - } - } - } - } - - // 打印出两点之间最短距离 + 路径 - for(i = 0; i < n-1; i++){ - for(j = i+1; j < n; j++){ - if(dist[i][j] < 10){ - cout< "< "<> arc; //邻接矩阵 +public: + // 测试用的默认构造函数 + Graph(); + //构造函数,从命令行输入 + Graph(int vertex_num_,int edge_); + //打印所有的边 + void print_edge(); + // 打印整个邻接矩阵 + void print_arc(); + //求最短路径 + void Dijkstra(int begin); + void Floyd(); }; +Graph::Graph(){ + this->vertex_num = 7; + this->edge=12; + this->arc = vector>(vertex_num,vector(vertex_num,INT_MAX)); + // cout<vertex_num=vertex_num_; + this->edge=edge_; + this->arc=vector>(vertex_num,vector(vertex_num,INT_MAX)); + int beg=0,end=0,weight=0; + for(int i=0;i>beg>>end>>weight; + arc[beg][end]=weight; + // 如果是无向图则需要添加反向的路径 + arc[end][beg]=weight; + } +} + +void Graph::print_edge(){ + for(int i=0;i result(vertex_num,INT_MAX); + result[begin]=0; + // 初始化距离集合,每次从中挑选 + vector distance(vertex_num,INT_MAX); + for(int i=0;i> distance(arc); + vector> path(vertex_num,vector(vertex_num,0)); + int i,j,k; + int temp; + + for(k=0;k 目录 >* 图算法-Dijkstra算法 >* 图算法-Floyd算法 >* 图算法-Bellman-Ford算法 >* 图算法-Prim算法 ->* 图算法-Kruskal算法 \ No newline at end of file +>* 图算法-Kruskal算法 + + diff --git a/算法/B类:数据结构算法/1.4 图算法-Kruskal算法.md b/算法/B类:数据结构算法/1.4 图算法-Kruskal算法.md index d3a3bde3..0996690d 100644 --- a/算法/B类:数据结构算法/1.4 图算法-Kruskal算法.md +++ b/算法/B类:数据结构算法/1.4 图算法-Kruskal算法.md @@ -10,7 +10,7 @@ > 参考文献 > [https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html](https://www.cnblogs.com/ggzhangxiaochao/p/9070873.html) ## 1 问题描述 -* Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。 +* Kruskal算法是一种用来寻找**最小生成树的算法**,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都是贪婪算法的应用。和Boruvka算法不同的地方是,Kruskal算法在图中存在相同权值的边时也有效。 ## 2 算法原理 diff --git a/算法/B类:数据结构算法/1.5 图算法-Prim算法.md b/算法/B类:数据结构算法/1.5 图算法-Prim算法.md index d5c48a74..dd55d790 100644 --- a/算法/B类:数据结构算法/1.5 图算法-Prim算法.md +++ b/算法/B类:数据结构算法/1.5 图算法-Prim算法.md @@ -11,7 +11,8 @@ ## 1 问题分析 -普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。 +* 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索**最小生成树**。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。 +* 主要是**顶点的贪心思想**。 ## 2 算法原理 @@ -116,112 +117,5 @@ ## 5 算法实现 ```C++ -#include -#include -#define VertexType int -#define VRType int -#define MAX_VERtEX_NUM 20 -#define InfoType char -#define INFINITY 65535 -typedef struct { - VRType adj; //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。 - InfoType * info; //弧额外含有的信息指针 -}ArcCell,AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; -typedef struct { - VertexType vexs[MAX_VERtEX_NUM]; //存储图中顶点数据 - AdjMatrix arcs; //二维数组,记录顶点之间的关系 - int vexnum,arcnum; //记录图的顶点数和弧(边)数 -}MGraph; -//根据顶点本身数据,判断出顶点在二维数组中的位置 -int LocateVex(MGraph G,VertexType v){ - int i=0; - //遍历一维数组,找到变量v - for (; ivexnum),&(G->arcnum)); - for (int i=0; ivexnum; i++) { - scanf("%d",&(G->vexs[i])); - } - for (int i=0; ivexnum; i++) { - for (int j=0; jvexnum; j++) { - G->arcs[i][j].adj=INFINITY; - G->arcs[i][j].info=NULL; - } - } - for (int i=0; iarcnum; i++) { - int v1,v2,w; - scanf("%d,%d,%d",&v1,&v2,&w); - int m=LocateVex(*G, v1); - int n=LocateVex(*G, v2); - if (m==-1 ||n==-1) { - printf("no this vertex\n"); - return; - } - G->arcs[n][m].adj=w; - G->arcs[m][n].adj=w; - } -} -//辅助数组,用于每次筛选出权值最小的边的邻接点 -typedef struct { - VertexType adjvex;//记录权值最小的边的起始点 - VRType lowcost;//记录该边的权值 -}closedge[MAX_VERtEX_NUM]; -closedge theclose;//创建一个全局数组,因为每个函数中都会使用到 -//在辅助数组中找出权值最小的边的数组下标,就可以间接找到此边的终点顶点。 -int minimun(MGraph G,closedge close){ - int min=INFINITY; - int min_i=-1; - for (int i=0; i0 && close[i].lowcost < min) { - min=close[i].lowcost; - min_i=i; - } - } - //返回最小权值所在的数组下标 - return min_i; -} -//普里姆算法函数,G为无向网,u为在网中选择的任意顶点作为起始点 -void miniSpanTreePrim(MGraph G,VertexType u){ - //找到该起始点在顶点数组中的位置下标 - int k=LocateVex(G, u); - //首先将与该起始点相关的所有边的信息:边的起始点和权值,存入辅助数组中相应的位置,例如(1,2)边,adjvex为0,lowcost为6,存入theclose[1]中,辅助数组的下标表示该边的顶点2 - for (int i=0; i +#include +#include +using namespace std; + +// 构造边的对象 +struct Edge +{ + int start; + int end; + int weight; + Edge(int s,int e,int w){ + start=s; + end=e; + weight=2; + } + // 重写<运算符 + bool operator<(const Edge& a)const{ + return a.weight < weight; + } +}; + +class Graph{ +private: + int vertex_num; //图的顶点个数 + int edge; + // 如果顶点的表示是离散的应该加一层映射 + vector> arc; //邻接矩阵 +public: + // 测试用的默认构造函数 + Graph(); + //构造函数,从命令行输入 + Graph(int vertex_num_,int edge_); + //打印所有的边 + void print_edge(); + // 打印整个邻接矩阵 + void print_arc(); + //求最短路径 + void Dijkstra(int begin);//单源最短路 + void Floyd();//多源最短路 + void Prim();//最小生成树 + void Kruskal();//最小生成树 +}; + +Graph::Graph(){ + this->vertex_num = 7; + this->edge=12; + this->arc = vector>(vertex_num,vector(vertex_num,INT_MAX)); + // cout<vertex_num=vertex_num_; + this->edge=edge_; + this->arc=vector>(vertex_num,vector(vertex_num,INT_MAX)); + int beg=0,end=0,weight=0; + for(int i=0;i>beg>>end>>weight; + arc[beg][end]=weight; + // 如果是无向图则需要添加反向的路径 + arc[end][beg]=weight; + } +} + +void Graph::print_edge(){ + for(int i=0;i result(vertex_num,INT_MAX); + result[begin]=0; + // 初始化距离集合,每次从中挑选 + vector distance(vertex_num,INT_MAX); + for(int i=0;i> distance(arc); + vector> path(vertex_num,vector(vertex_num,0)); + int i,j,k; + int temp; + + // 三层for循环。k表示中间点。i表示起点,j表示中点。如果经过起始点小于直接点,则更新。 + // 可以进行优化,例如i||j==k的时候,可以不用运算,直接continue。 + for(k=0;k result; + // 初始化优先队列,用来挑选满足要求的最小的边 + priority_queue> pri_edge; + + for(int k=0;k