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

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

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 8

bool 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 8

bool 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行里输出最小的公路总长度。

样例输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
样例输出:
3
5

#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  
****************************************************************/



  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值