<C/C++算法> 图论基础算法小结(邻接矩阵实现)

来源:互联网 发布:国外刻录软件 编辑:程序博客网 时间:2024/05/16 06:20

以前用邻接表实现的图算法程序写得有点复杂,无法立刻看懂,重新用邻接矩阵写得简单点,某年某月能一看就回忆起该算法。

1,广度优先搜索算法

广度优先算法(Breadth-First-Search),又称作宽度优先搜索,或横向优先搜索,简称BFS,是一种图形搜索演算法。简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。广度优先搜索的实现一般采用open-closed表。

#include<iostream>  #include<windows.h>  #include<vector>  #include<queue>  #include<set>  using namespace std;#define vertex_nums 8bool BFS(vector<vector<int>> &graph , vector<int> &path,int start_vertex);//广度优先遍历要快速想到队列  int _tmain(int argc, _TCHAR* argv[]){system("color 0A");//初始化图vector<int> row(vertex_nums, -1);vector<vector<int>> graph(vertex_nums, row);for (int i = 0; i<vertex_nums; i++)graph[i][i] = 0;//自定义简单图的边及其权值graph[3][0]=1;graph[0][5]=1;graph[1][2]=3,graph[2][1]=3;graph[1][6]=1,graph[6][1]=1;graph[2][3]=5;graph[2][4]=1;graph[3][5]=3;graph[3][6]=1;graph[4][5]=1;graph[4][3]=3;graph[5][7]=1;graph[6][7]=3;graph[7][3]=3;vector<int> bfs_path(vertex_nums, -1);int s_v = 2, e_v = 7;BFS(graph, bfs_path, s_v);system("pause");return 0;}//广度优先检查图//时间复杂度O(v^2)//连接表的速度是O(v+e),对于稀疏图而言,邻接表更有优势!  bool BFS(vector<vector<int>> &graph, vector<int> &bfs_path, int start_vertex){vector<int> visited(vertex_nums,0);visited[start_vertex] = 1;queue<int> que;que.push(start_vertex);//用于记录路径int idx = 0;bfs_path[idx++]=start_vertex;while (!que.empty()){int cur_ver = que.front();que.pop();//广度搜索当前节点的相邻节点for (int i = 0; i < vertex_nums; i++){if (graph[cur_ver][i] > 0 && visited[i] == 0){visited[i] = 1;bfs_path[idx++] = i;que.push(i);}}}return true;}



2,深度优先搜索算法

深度优先搜索算法(Depth-First-Search),是搜索算法的一种。是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。

#include<stdio.h>  #include<string.h>  #include<iostream>  #include<windows.h>  #include<vector>  #include<queue>  #include<set>  using namespace std;#define vertex_nums 8bool DFS(vector<vector<int>> &graph, vector<int> &path, vector<int> &visited, int &idx, int start_vertex);int _tmain(int argc, _TCHAR* argv[]){system("color 0A");//初始化图vector<int> row(vertex_nums, -1);vector<vector<int>> graph(vertex_nums, row);for (int i = 0; i<vertex_nums; i++)graph[i][i] = 0;//自定义简单图的边及其权值graph[3][0]=1;graph[0][5]=1;graph[1][2]=3,graph[2][1]=3;graph[1][6]=1,graph[6][1]=1;graph[2][3]=5;graph[2][4]=1;graph[3][5]=3;graph[3][6]=1;graph[4][5]=1;graph[4][3]=3;graph[5][7]=1;graph[6][7]=3;graph[7][3]=3;vector<int> dfs_path(vertex_nums, -1);vector<int> visited(vertex_nums, 0);int s_v = 2, e_v = 7;int idx = 0;visited[s_v] = 1;DFS(graph, dfs_path, visited,idx, s_v);getchar();return 0;}//深度优先检查图//时间复杂度O(v^2)//连接表的速度是O(v+e),对于稀疏图而言,邻接表更有优势!  bool DFS(vector<vector<int>> &graph, vector<int> &dfs_path, vector<int> &visited , int &idx, int start_vertex){dfs_path[idx++] = start_vertex;for (int i = 0; i < vertex_nums; i++){if (graph[start_vertex][i] > 0 && visited[i] == 0){visited[i] = 1;DFS(graph,dfs_path,visited,idx,i);}}return true;}


3,最短路径算法dijstra

始终记住最短路径算法的核心内容,每次迭代始终在更新shortpath中的最短路径值

贪心的最短路径:shortpath[j]=min{shortpath[j],shortpath[i]+graph[i][j]}  
shortpath[j]始终存放起点到j的当前最短路径,i为当前中心点

#include<stdio.h>    #include<string.h>    #include<iostream>    #include<windows.h>    #include<vector>    #include<queue>    #include<set>    using namespace std;#define vertex_nums 8  int dijkstra(vector<vector<int>>  &graph, int start_vertex, int end_vertex, vector<int> &path);int _tmain(int argc, _TCHAR* argv[]){system("color 0A");//初始化图  vector<int> row(vertex_nums, INT_MAX);vector<vector<int>> graph(vertex_nums, row);for (int i = 0; i<vertex_nums; i++)graph[i][i] = 0;//自定义简单图的边及其权值  graph[3][0] = 1;graph[0][5] = 1;graph[1][2] = 3, graph[2][1] = 3;graph[1][6] = 1, graph[6][1] = 1;graph[2][3] = 5;graph[2][4] = 1;graph[3][5] = 3;graph[3][6] = 1;graph[4][5] = 1;graph[4][3] = 3;graph[5][7] = 1;graph[6][7] = 3;graph[7][3] = 3;vector<int> path(vertex_nums, -1);int resukt = dijkstra(graph, 2, 7, path);for (int k = 7; path[k] != -1; k = path[k])cout << k << " ";cout << 2;getchar();return 0;}//最短路径:shortpath[j]=min{shortpath[j],shortpath[i]+graph[i][j]}    //shortpath[j]始终存放起点到j的当前最短路径  int dijkstra(vector<vector<int>>  &graph, int start_vertex, int end_vertex, vector<int> &path){vector<int> shortpath(vertex_nums, INT_MAX);//存储起点到各点的最短路径  vector<int> visited(vertex_nums, 0);//已访问过的顶点   for (int i = 0; i < vertex_nums; i++)//初始化当前原点到其他店的距离,如果不相连则为INT_MAX  {if (graph[start_vertex][i] != INT_MAX)shortpath[i] = graph[start_vertex][i];}visited[start_vertex] = 1;//起点初始化为被访问,并以他为当前中心点开始找最短路径  int min_weight = INT_MAX;int u = -1;int nums = vertex_nums - 1;bool first = true;while (nums){min_weight = INT_MAX;u = -1;for (int i = 0; i < vertex_nums; i++){if (visited[i] == 0 && shortpath[i] < min_weight){u = i;min_weight = shortpath[i];}}if (first){path[u] = start_vertex;//更新记录前驱顶点,供最后回溯最短路径 first = false;}visited[u] = 1;//已经确定起点到u的最短路径  nums--;if (end_vertex == u)//如果u就是终点的话,就不用再找了!  break;//以u为中间点寻找起点到顶点w的最短路径    for (int w = 0; w < vertex_nums; w++){if (visited[w] == 0 && graph[u][w] != INT_MAX && min_weight + graph[u][w] < shortpath[w]){shortpath[w] = min_weight + graph[u][w];//更新起点到w的最短路径值    path[w] = u;//更新记录前驱顶点,供最后回溯最短路径 }}}return shortpath[end_vertex];}




4,Prim算法

Prim算法是以图的顶点起始点,每次选取所有顶点上对应的权值最小的边进行构建,所以prim的时间开销和边无关,对于定点数为n时Prim的时间复杂度为 O(n^2),所以prim算法更适合求解边数很多的稠密图的MST。

具体步骤:将整个顶点集合分为两个子集U、V,U中存放已经在生成树中的顶点,V中存放未在生成树中的顶点。算法核心的每一步将从U、V中各选一顶点,并且边的权值w(u,v)是最小的,然后将该顶点v从V中移到U中,如此直到集合V为空,即完成。

#include<stdio.h>    #include<string.h>    #include<iostream>    #include<windows.h>    #include<vector>    #include<queue>    #include<set>    using namespace std;#define vertex_nums 8  int Prim(vector<vector<int>>   &graph);int _tmain(int argc, _TCHAR* argv[]){system("color 0A");//初始化图  vector<int> row(vertex_nums, INT_MAX);vector<vector<int>> graph(vertex_nums, row);for (int i = 0; i<vertex_nums; i++)graph[i][i] = 0;//自定义简单图的边及其权值  graph[3][0] = 1;graph[0][5] = 1;graph[1][2] = 3, graph[2][1] = 3;graph[1][6] = 1, graph[6][1] = 1;graph[2][3] = 5;graph[2][4] = 1;graph[3][5] = 3;graph[3][6] = 1;graph[4][5] = 1;graph[4][3] = 3;graph[5][7] = 1;graph[6][7] = 3;graph[7][3] = 3;cout<<"最小生成树的权值为: " << Prim(graph) << endl;getchar();return 0;}//lowcost[i]:表示以u为起点,i为终点的边的权值//closest[i]:表示对应lowcost[i]的起点//u为当前最短边的目的顶点(举例:2--->1,1就是目的顶点)int Prim(vector<vector<int>>   &graph){int sum = 0;int u = 0;//起点vector<int> lowcost(vertex_nums, 0);vector<int> closest(vertex_nums,0);vector<bool> visited(vertex_nums,false);for (int i = 0; i < vertex_nums; i++)lowcost[i] = graph[u][i];//start_ver为起点visited[u] = true;for (int i = 1; i < vertex_nums; i++){int min = INT_MAX;u = -1;//寻找未被加入mst的最小权值边for (int k = 0; k < vertex_nums; k++){if ((lowcost[k] < min) && visited[k] == false){min = lowcost[k];u = k;}}if (u == -1)//不是连通图return -1;cout << closest[u] << "--->" << u << endl;//输出最小生成树的连接情况路径。sum += min;visited[u] = true;//更新当前节点u到其他节点的权值for (int k = 0; k < vertex_nums; k++){if ((graph[u][k] < lowcost[k]) && visited[k] == false){lowcost[k] = graph[u][k];closest[k] = u;}}}return sum;}



5,Kruskal算法

秒杀该算法:<C/C++数据结构>并查集及其常见面试题


该问题就是最小生成树问题:练习3,还是畅通工程

题目描述:
    某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入:

    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
    当N为0时,输入结束,该用例不被处理。

输出:

    对每个测试用例,在1行里输出最小的公路总长度。

样例输入:
31 2 11 3 22 3 441 2 11 3 41 4 12 3 32 4 23 4 50
样例输出:
35

#include "vector"  #include "string"  #include "algorithm"  #include <iostream>  #include "stack"  #include <cmath>  #include <set>    using namespace std;        class Edge  {  public:      int acity;//城市a        int bcity;//城市b        int cost;  //建成a到b的路的花费        bool operator < (const Edge &q) const//注意返回值的类型,运算符重载。        {          return cost<q.cost;      }  };    Edge edge[10000];    class UFSet  {  public:      UFSet(int nsize)      {          size = nsize;          parent = new int[size + 1];      };      ~UFSet()      {          delete[] parent;          parent = NULL;      };            // 初始化每个元素的祖先       void makeSet(int n);           // 找到元素x的祖先元素       int findSet(int x);           // 获取最小花费        int getMinCost(int m);   private:      int *parent;//存放祖先节点,例如x=parent[i],元素i的祖先节点为元素x          int size;  };    void UFSet::makeSet(int n) //初始化      {      //初始化每一个元素都各自为一个独立的集合,其祖先均设定为自身          for (size_t i = 1; i <= n; i++)          parent[i] = i;  }    int UFSet::findSet(int x)  {      //找到元素所在的集合,也就是找到自己的最高的祖先,          //这也是判断两个元素是否在同一个集合中的主要依据。          if (parent[x] == x)//递归截止条件(最高祖先的祖先是其自身)            return x;        parent[x] = findSet(parent[x]);//递归,最终找到x的最高祖先,并且沿途找到所有的最高祖先          return parent[x];  }      int UFSet::getMinCost(int m)  {      sort(edge, edge + m);//必须先对边排序(根据边的修建费用),这样才能贪心的形成最小花费        int sum = 0;      for (int i = 0; i<m; i++)      {          int baseA = findSet(edge[i].acity);//找到城市a的祖先(要么是自身要么是城市b的编号)            int baseB = findSet(edge[i].bcity);          if (baseA != baseB)          {              parent[baseA] = baseB;//将城市a的祖先设置成b的祖先这个式子等价于parent[edge[i].acity] = edge[i].bcity                sum += edge[i].cost;          }      }      return sum;  }    int main()  {      int n = 0;      while (cin >> n, n > 0)      {          int m = n*(n - 1) / 2;            UFSet uset(100);          uset.makeSet(n);//初始化每个城市的祖先为自身              for (int i = 0; i < m; i++)              cin >> edge[i].acity >> edge[i].bcity >> edge[i].cost;            int mincost = uset.getMinCost(m);          cout << mincost << endl;      }        return 0;  }    /**************************************************************      Problem: 1017      User: EbowTang      Language: C++      Result: Accepted      Time:30 ms      Memory:1636 kb  ****************************************************************/



3 0
原创粉丝点击