求单源最短(长)路学习心得

    在学习图论时,求一个图的最短(长)路是一个比较基本的问题。

    无权图即有权图,只不过权值都是为1,无权图也可以用有权图的操作。

无权图

当处理一个无权值的图时,可以采用DFS或BFS来对图进行搜索并及时更新某些标记数组,从而求得最短(长)路。

若采用BFS时,根据入队的顺序性(求最短路的层次性),可以将先访问到的节点打上标记并更新权值,从而实现最短路问题的求解。BFS时间复杂度:O(n+e)

queue<int>q;
for( i =1 ; i <= n ; i ++)
{
    path[i] = -1; dist[i] = -1;     //初始化
}
dist[v] = 0;                       //v是起点
q.push(v);

while (!q.empty()) 
{
    u = q.front();             // 队头顶点u出队
    q.pop();
    for(int j = 0;j<(a[u][j].size());j++)
    {
        k = a[u][j];
        if (dist[k] == -1) 
        {
             q.push(k)        // u未访问的邻接顶点入队
             dist[k] = dist[u] + 1;
             path[k] = u;
        }
     }
 }

而当求解最长路时(DAG),选择用DFS较为合适

在这里我们可以用一个vector<int>temp 作为临时数组存放节点,path作为最终数组,当其.size()大小关系发生改变时,path清空并赋值为temp即可。DFS时间复杂度:O(n*e)

void Dfs(int n,vector<int>&temp)
{
	if(temp.size()>path.size())        //更新
    {
		path.clear();
		path=temp;
	}
	for(int i=0;i<a[n].size();i++)
    {
		temp.push_back(a[n][i]);       //试探
		Dfs(a[n][i],temp);
		temp.pop_back();               //回溯
	}
}

有权图

若要求最短路,对于权值全部为正数的图,可以采用Dijkstra算法,每次选未被标记且路径值最小的点,并对该点的邻接点进行松弛。在每次选择路径值最小点时,可以考虑选择堆优化。

void dijkstra(int v)
{
    for (int i = 1;i<=n;i++)                            //初始化
    {
        path[i] = 0;dist[i] = INF;s[i] = 0;
    }
    int k;
    priority_queue<pair<long long int,int>>q;              //优先队列堆优化
    dist[v] = 0;
    q.push(make_pair(0,v));                                //存二元数对,方便存取
    while (!q.empty())
    {
        int u = q.top().second;                            //找到最小路径点
        q.pop();
        if (s[u])   continue;
        s[u] = 1;
        for (int i = 1;i<=int(a[u].size());i++)
        {
            k = a[u][i-1].to;
            if (dist[u] + a[u][i-1].cost<dist[k])         //松弛
            {
                dist[k] = dist[u] + a[u][i-1].cost;
                q.push(make_pair(-dist[k],k));            //存负数,因为要取最小值
                path[k] = u;

            }
        }
    }
}

优化之后时间复杂度为O(E*logN),较为良好。

Dijkstra也能求最长路,但是所求的最长路只能是全是正数。只需要将权值全部改成负数即可。

不过这样就不能使用堆优化了。

_____________________________________________________________________________

对于权值有正有负的图来说,SPFA算法较为合适。SPFA是bellman-ford算法的改进版本

即构建一个队列,若队列中的点能够松弛,先让此点出列,再让此点将所松弛的但是不在队列中的点进入队列,直到队列为空。该算法时间复杂度为O(kE),k一般认为是2左右。

另外该算法也可以判断图中是否存在负环。可以记录一个点的入队次数,若入队次数 > V,这个图就存在负环。

for (int i = 1;i<=n;i++)                                //初始化
    {
        d[i] = INF;mark[i] = 0;times[i] = 0;
    }
    d[v] = 0;mark[v] = 1;
    q.push(v);
    while (!q.empty())
    {
        u = q.front();                                    //取队首元素
        q.pop();
        mark[u] = 0;
        for (int j = 0;j<int(a[u].size());j++)
        {
            k = a[u][j].to;
            if (d[u] + a[u][j].cost < d[k])                //可松弛且不在队列中的元素入队
            {
                d[k] = d[u] + a[u][j].cost;
                if (!mark[k])
                {
                    q.push(k);                
                    mark[k] = 1;
                    times[k]++;                            //判断负环的标志   
                    if (times[k] == n)
                         return;             
                }
            }
        }

若要求最长路,只需要在建图的时候将权值赋值为其相反数,最后在取反即可!

=========================================================================总结:

对于无权图,求最短路可用BFS;求最长路用DFS;

对于有权图,最长路、最短路用SPFA即可!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值