• Shortest Path


    图论中的经典问题:给一个图,求出起点到终点的最短路径。

    Graph Representation

    在这里插入图片描述

    • adjacency matrix
    i/j 0 1 2 3 4
    0 (infty) -1 4 (infty) (infty)
    1 (infty) (infty) 3 2 2
    2 (infty) (infty) (infty) (infty) (infty)
    3 (infty) 1 5 (infty) (infty)
    4 (infty) (infty) (infty) -3 (infty)

    vector<vector<int>> g(n, vector<int>(n, INF))表示,g[i][j]表示从顶点(i)到顶点(j)的权重,空间复杂度(O(|V|^2)),适用于稠密图,用的不多;

    • adjacency list
      链表比较少用,基本都用动态数组:
    0 (1,-1) (2,4) - - -
    1 (2,3) (3,2) (4,2) - -
    2 - - - - -
    3 (1,1) (2,5) - - -
    4 (3,-3) - - - -

    vector<vector<pair<int, int>>> g表示,g[i][j].first表示从顶点(i)出发到达的顶点(k)g[i][j].second表示从顶点(i)到顶点(k)的权值,空间复杂度(O(|V|+|E|))

    • edge list
      (0,1,-1),(0,2,4),(1,2,3),(1,3,2),(1,4,2),(3,1,1),(3,2,5),(4,3,-3)
      vector<vector<int>> e表示,e[i][0]表示顶点(u)e[i][1]表示顶点(v)e[i][2]表示(u)(v)的权值,空间复杂度(O(|E|))

    Dijkstra

    单源最短路径算法,只能用于所有边权值为正的图,本质上还是贪心,思想:

    1. 将所有结点分为两类:已经确定最短路径的点集(u)、未确定最短路径的点(v)
    2. 初始化(dis[start]=0),其余结点(dis)设为无穷大;
    3. 找出一个(dis)最小的(v)中的点(x),将其标记为(u),遍历(x)的所有出边,若(dis[y]>dis[x]+weight),则更新(y)点到源点的最短路径长度(dis[y]=dis[x]+weight)
    4. 重复,直到所有点都在(u)中。
    /*邻接矩阵版,适合顶点比较少的图,复杂度O(V^2)*/
    int vertexNum, adjMatrix[MAXN][MAXN];
    int dis[MAXN];   //起点到各点最短路径长度
    bool visited[MAXN] = {false};
    
    void Dijkstra(int start)
    {
    	for (int i = 0; i < vertexNum; i++)
    	{
    		dis[i] = INFINITY;
    	}
    	dis[start] = 0;
    
    	for (int i = 0; i < vertexNum; i++)
    	{
    		//寻找未访问点中dis最小的
    		int u = -1, minDis = INFINITY;
    		for (int j = 0; j < vertexNum; j++)
    		{
    			if (visited[j] == false && dis[j] < minDis)
    			{
    				u = j;
    				minDis = dis[j];
    			}
    		}
    
    		if (u == -1)  //剩余的点与起点不连通
    			return;
    
    		visited[u] = true;
    		//以u为中介试图优化dis[j]
    		for (int j = 0; j < vertexNum; j++)
    		{
    			//未访问、u可达、路过u可以使dis[j]更优
    			if (visited[j] == false && adjMatrix[u][j] != INFINITY && dis[u] + adjMatrix[u][j] < dis[j])
    				dis[j] = dis[u] + adjMatrix[u][j];
    		}
    	}
    }
    
    /*邻接表版,复杂度O(V^2+E)*/
    int vertexNum;
    int dis[MAXN];  //dis[i]表示源点到i点的最小距离 
    bool visited[MAXN] = {false};
    
    struct Edge{
    	int des;  //边的目标顶点 
    	int weight;    //边的权值 
    };
    vector<Edge> adjList[MAXN];   //邻接表 
    
    void dijkstra(int start)
    {
    	for(int i = 0;i < vertexNum;i++)
    	{
    		dis[i] = INT_MAX;
    	}
    	dis[start] = 0;
    	
    	for(int i = 0;i < vertexNum;i++)
    	{
    		int u = -1,minDis = INT_MAX;
    		for(int j = 0;j < vertexNum;j++)
    		{
    			if(visited[j] == false && dis[j] < minDis)
    			{
    				u = j;
    				minDis = dis[j];
    			}
    		}
    		
    		if(u == -1)
    			return;
    			
    		visited[u] = true;
    		for(int j = 0;j < adjList[u].size();j++)
    		{
    			//直接获得u能到达的点 
    			int v = adjList[u][j].des;
    			if(visited[v] == false && dis[v] > dis[u] + adjList[u][j].weight)
    			{
    				dis[v] = dis[u] + adjList[u][j].weight;
    			}
    		}
    	}
    }
    

    寻找(dis[u])的循环可以用堆来优化,使得复杂度降为(O(VlogV+E))

    Bellman-Ford

    可以计算含有负权的边,检测是否存在一个从源结点可以到达的权重为负值的环路(负权环):

    bool bellmanFord(vector<vector<int>>& edge, vector<int>& dis, int start) {
    	for (int i = 0; i < dis.size(); ++i) {
    		if (i == start)
    			dis[i] = 0;
    		else
    			dis[i] = 0x3f3f3f3f;
    	}
    
    	for (int i = 0; i < dis.size(); ++i)
    		for (int j = 0; j < edge.size(); ++j)
    			dis[edge[j][1]] = min(dis[edge[j][1]], dis[edge[j][0]] + edge[j][2]);
    
    	bool negCycle = false;
    	for (int i = 0; i < edge.size(); ++i)
    		if (dis[edge[i][1]] > dis[edge[i][0]] + edge[i][2]) {
    			negCycle = true;
    			break;
    		}
    
    	return negCycle;
    }
    

    复杂度(O(VE))

    Floyd-Warshall

    多源最短路径算法。

    void floyd(vector<vector<int>>& m) {
    	for(size_t k = 0;k < m.size();++k)
    		for(size_t i = 0;i < m.size();++i)
    			for (size_t j = 0; j < m.size(); ++j) {
    				// m不能用INT_MAX初始化,会溢出,用0x3f3f3f3f
    				if (m[i][k] + m[k][j] < m[i][j])
    					m[i][j] = m[i][k] + m[k][j];
    			}
    }
    

    不能用于含有负权环的图,这种图不存在最短路径。

  • 相关阅读:
    MySQL之SELECT用法
    Linux中find常见用法示例
    date命令
    backupMysql.sh
    Shell脚本中使用function(函数)示例
    zabbix邮件告警
    Zabbix系统数据采集方法总结
    TOMCAT原理详解及请求过程
    tomcat-users.xml 配置
    zabbix_sender用法实例
  • 原文地址:https://www.cnblogs.com/EIMadrigal/p/12328847.html
Copyright © 2020-2023  润新知