Localhost8080

知行合一,自强不息

 

dijkstra算法裸实现+heap优化

Dijkstra算法简述

  Dijkstra算法是图论中的一种求单源最短路的算法,即从一个点开始到所有其他点的最短路。其基本原理是:集合T包含所有当前已经找到最短路径的点,初始情况下T中只有源点。U维护当前源点通过T中各点到达其他各点的距离。从源点开始,每次新扩展一个属于U、距离T中点最短的点,用该点更新U中的与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有可能就破坏了已经更新的点距离不会改变的性质。

Dijkstra算法流程

  在以下说明中,start为源,Adj[u,v]为点u和v之间的边的长度,结果保存在Closest[]

  • 初始化:源的距离Closest[start]设为0,其他的点距离设为无穷大,同时把所有的点的状态设为没有扩展过。
  • 以下过程循环n-1次:
    在没有扩展过的点中取一距离最小的点u,并将其状态设为已扩展。
    对于每个与u相邻的点v,如果Closest[u]+Adj[u,v] < Closest[v],那么把Closest[v]更新成更短的距离Closest[u]+Adj[u,v]。此时到点v的最短路径上,前一个节点即为u。
  • 结束。此时对于任意的u,Closest[u]就是start到u的距离。


    //呵呵,有点简陋,但是思想还是正确地

    #include 
    <iostream>
    using namespace std;
    const int max_vertexes = 3;
    const int infinity = 10001;
    int graph[max_vertexes][max_vertexes]={
        
    0,1,5,
        infinity,
    0,2,
        infinity,infinity,infinity
    };
    //typedef **int Graph;
    int path[max_vertexes];
    int n,s,t;
    int Dijkstra(int G[max_vertexes][max_vertexes],int n,int s,int t, int path[])
    {
        
    int i,j,w,minc,d[max_vertexes],flag[max_vertexes];
        memset(flag,
    0,sizeof(flag));
        
    for (i=0;i<n;i++)
        {   
            d[i]
    =G[s][i];
            path[i]
    =s;//都从S直接到达的
        }
        flag[s]
    =1;
        path[s]
    =0;//s点为第一步
        d[s]=0;
        
    for (i=1;i<n;i++)
        {   
            minc
    =infinity;
            w
    =0;
            
    for(j=0;j<n;j++)//找出一个到S距离最短的未添加的节点
                if ((flag[j]==0)&&(minc>=d[j])) 
                {
                    minc
    =d[j];
                    w
    =j;
                }
                flag[w]
    =1;
                
    for (j=0;j<n;j++)//更新路径
                    if((flag[j]==0)&&(G[w][j]!=infinity)&&(d[j]>d[w]+G[w][j]))
                    {   
                        d[j]
    =d[w]+G[w][j];
                        path[j]
    =w;     
                    }
        }
        
    return d[t];
    }

    int main(void)
    {
        
    int s,t;
        
    while(cin>>s>>t)
        {
            
    int result = Dijkstra(graph,max_vertexes,s,t,path);
            cout
    <<result<<endl;
        }
        
    return 0;
    }


堆优化版:
描述:

回忆经典dijkstra算法
每次在没标记的节点中找离源点最近的,此步骤可以用HEAP维护,每次取根(最小值),然后删除根,在其左右儿子中选更小的作为堆的根,但此时选中的子树有无根了,递归处理.
找到最近点后,要更新各点离源点的距离,若能更新,则该点的值变小了,可能比其父亲更小,与其比较,更小就交换,不断往上比较,直到是整个堆的根或不比父亲小.

heap里存顶点(按dist的顺序)

repeat

relax从heap[1]发出的每一条边,当然,一边relax一边fixup,

delete(heap[1]),fixdown

until empty(heap);

 

复杂度分析:

众所周知没有经过优化的dijkstra算法的复杂度为O(V^2)

算法使用了堆,每次以最小代价加入顶点,即deletemin操作(O(logn))
然后根据该点调整其他顶点到当前树的距离,主要用到decreasekey操作(logn)
因为decrease最多m次,所以decrease花掉 O(mlogn)的时间,deletemin执行n次,它花掉O(nlogn)时间,一共O((m+n)logn)时间。而其初始化部分的复杂度不超过这个值,所以算法总复杂度O((m+n)logn)(当然这个复杂度只适用于邻接表表示的稀疏图)

#include <iostream>
#include 
<vector>
using namespace std;
const unsigned int NoEdge=0xfffffff;
struct vertex{
    
int n;
    
int weight;
};

vertex heap[
100];
int ph[100];//ph[i]存放顶点i在堆中的下标,当i的位置改变时它也随之改变 
int size;//堆的大小
inline int parent(int i)
{
    
return (i+1)/2-1;
}
inline 
int left(int i)
{
    
return (i+1)*2-1;
}
inline 
int right(int i)
{
    
return (i+1)*2;
}
void heapify(int i)
{
    
int smallest=i;
    
if(left(i)<size&&heap[left(i)].weight<heap[smallest].weight)
    {
        smallest
=left(i);
    }
    
if(right(i)<size&&heap[right(i)].weight<heap[smallest].weight)
    {
        smallest
=right(i);
    }
    
if(smallest!=i)
    {
//交换heap[smallest]和heap[i]中的值,并实ph[]中的值与交换后的情况一致
        ph[heap[smallest].n]=i;
        ph[heap[i].n]
=smallest;
        vertex temp;
        temp
=heap[smallest];
        heap[smallest]
=heap[i];
        heap[i]
=temp;
        heapify(smallest);
    }
}
vertex extract()
{
    
//得到从原点出发的可到达的最近的点
    vertex min=heap[0];
    heap[
0]=heap[--size];
    ph[heap[
0].n]=0;
    heapify(
0);
    
return min;
}
void build()
{
    
//initialize a heap from an array , but not used in Dijkstra.
    for(int i = parent(size-1) ; i >=0 ; --i)
    {
        heapify(i);
    }
}
void decrease(int i,int key)
{
    
//更新heap[i]的值为key,且key必须为非负
    heap[i].weight=key;
    
while(i>0&&heap[i].weight<heap[parent(i)].weight)
    {
        ph[heap[i].n]
=parent(i);
        ph[heap[parent(i)].n]
=i;
        vertex temp;
        temp
=heap[i];
        heap[i]
=heap[parent(i)];
        heap[parent(i)]
=temp;
        i
=parent(i);
    }
}
bool empty()
{
//测试堆是否为空
    return size==0;
}
int n;//顶点的个数
int d[100];//存放从原点到此顶点的最短距离
vector<vertex> v[100];
void Dijkstra(int source)
{
    
for(int i = 0 ; i < n ; ++i )
    {
        heap[i].n
=i;
        heap[i].weight
=NoEdge;
        ph[i]
=i;
    }
    decrease(source,
0);
    vertex min;
    
while(!empty())
    {
        min
=extract();
        d[min.n]
=min.weight;
        cout 
<< endl;
        
for(vector<vertex>::iterator itr=v[min.n].begin();itr!=v[min.n].end();++itr)
        {
            
if(heap[ph[itr->n]].weight>min.weight+itr->weight)
            {
                decrease(ph[itr
->n],min.weight+itr->weight);
            }
        }
    }

int main()
{
    
int a,b,w;
    
int edges;
    vertex temp;
    cin 
>> n;
    cin 
>> edges;
    size
=n;
    
for(int i = 0 ; i < n ; ++i )
    {
        d[i]
=NoEdge;
    }
    
for(int i = 0 ; i < edges; ++i )
    {
        cin 
>> a >> b >> w;
        temp.n
=b;
        temp.weight
=w;
        v[a].push_back(temp);
    }

    
int s,t;
    
while(cin>>s>>t)
    {
        Dijkstra(s);
        cout
<<d[t]<<endl;
    }
    
return 0;
}

posted on 2010-10-18 20:32 superKiki 阅读(2286) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

统计

常用链接

留言簿

随笔档案

文章分类

我的好友

一些常去的网站

搜索

最新评论

阅读排行榜

评论排行榜