首页/文章/ 详情

Dijkstra 最短路算法

3月前浏览147


指定一个点(源点)到其余各个顶点的最短路径,也叫做“单源最短路径”。例如求下图中的 1 号顶点到 2、3、4、5、6 号顶点的最短路径。

与 Floyd-Warshall 算法一样这里仍然使用二维数组 e 来存储顶点之间边的关系,初始值如下。

我们还需要用一个一维数组 dis 来存储 1 号顶点到其余各个顶点的初始路程,如下。

我们将此时 dis 数组中的值称为最短路的“估计值”。

既然是求 1 号顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 dis 可知当前离 1 号顶点最近是 2 号顶点。当选择了 2 号顶点后,dis[2]的值就已经从“估计值”变为了“确定值”,即 1 号顶点到 2 号顶点的最短路程就是当前 dis[2]值。为什么呢?你想啊,目前离 1 号顶点最近的是 2 号顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 1 号顶点到 2 号顶点的路程进一步缩短了。因为 1 号顶点到其它顶点的路程肯定没有 1 号到 2 号顶点短,对吧 O(∩_∩)O~

既然选了 2 号顶点,接下来再来看 2 号顶点有哪些出边呢。有 2->3 和 2->4 这两条边。先讨论通过 2->3 这条边能否让 1 号顶点到 3 号顶点的路程变短。也就是说现在来比较 dis[3]和 dis[2]+e[2][3]的大小。其中 dis[3]表示 1 号顶点到 3 号顶点的路程。dis[2]+e[2][3]中 dis[2]表示 1 号顶点到 2 号顶点的路程,e[2][3]表示 2->3 这条边。所以 dis[2]+e[2][3]就表示从 1 号顶点先到 2 号顶点,再通过 2->3 这条边,到达 3 号顶点的路程。

我们发现 dis[3]=12,dis[2]+e[2][3]=1+9=10,dis[3]>dis[2]+e[2][3],因此 dis[3]要更新为 10。这个过程有个专业术语叫做“松弛”。即 1 号顶点到 3 号顶点的路程即 dis[3],通过 2->3 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛 1 号顶点到其余各个顶点的路程。

同理通过 2->4(e[2][4]),可以将 dis[4]的值从 ∞ 松弛为 4(dis[4]初始为 ∞,dis[2]+e[2][4]=1+3=4,dis[4]>dis[2]+e[2][4],因此 dis[4]要更新为 4)。

刚才我们对 2 号顶点所有的出边进行了松弛。松弛完毕之后 dis 数组为:

接下来,继续在剩下的 3、4、5 和 6 号顶点中,选出离 1 号顶点最近的顶点。通过上面更新过 dis 数组,当前离 1 号顶点最近是 4 号顶点。此时,dis[4]的值已经从“估计值”变为了“确定值”。下面继续对 4 号顶点的所有出边(4->3,4->5 和 4->6)用刚才的方法进行松弛。松弛完毕之后 dis 数组为:

继续在剩下的 3、5 和 6 号顶点中,选出离 1 号顶点最近的顶点,这次选择 3 号顶点。此时,dis[3]的值已经从“估计值”变为了“确定值”。对 3 号顶点的所有出边(3->5)进行松弛。松弛完毕之后 dis 数组为:

继续在剩下的 5 和 6 号顶点中,选出离 1 号顶点最近的顶点,这次选择 5 号顶点。此时,dis[5]的值已经从“估计值”变为了“确定值”。对5号顶点的所有出边(5->6)进行松弛。松弛完毕之后 dis 数组为:

最后对 6 号顶点所有点出边进行松弛。因为这个例子中 6 号顶点没有出边,因此不用处理。到此,dis 数组中所有的值都已经从“估计值”变为了“确定值”。

最终 dis 数组如下,这便是 1 号顶点到其余各个顶点的最短路径。

OK,现在来总结一下刚才的算法。算法的基本思想是:每次找到离源点(上面例子的源点就是 1 号顶点)最近的一个顶点,然后以该顶点为中心进行扩展,最终得到源点到其余所有点的最短路径。基本步骤如下:

  • 将所有的顶点分为两部分:已知最短路程的顶点集 合 P 和未知最短路径的顶点集 合 Q。最开始,已知最短路径的顶点集 合 P 中只有源点一个顶点。我们这里用一个 book[ i ]数组来记录哪些点在集 合 P 中。例如对于某个顶点 i,如果 book[ i ]为 1 则表示这个顶点在集 合 P 中,如果 book[ i ]为 0 则表示这个顶点在集 合 Q 中。

  • 设置源点 s 到自己的最短路径为 0 即 dis=0。若存在源点有能直接到达的顶点 i,则把 dis[ i ]设为 e[s][ i ]。同时把所有其它(源点不能直接到达的)顶点的最短路径为设为 ∞。

  • 在集 合 Q 的所有顶点中选择一个离源点 s 最近的顶点 u(即 dis[u]最小)加入到集 合 P。并考察所有以点 u 为起点的边,对每一条边进行松弛操作。例如存在一条从 u 到 v 的边,那么可以通过将边 u->v 添加到尾部来拓展一条从 s 到 v 的路径,这条路径的长度是 dis[u]+e[u][v]。如果这个值比目前已知的 dis[v]的值要小,我们可以用新值来替代当前 dis[v]中的值。

  • 重复第 3 步,如果集 合  Q 为空,算法结束。最终 dis 数组中的值就是源点到所有顶点的最短路径。

完整的 Dijkstra 算法代码如下:

    #include <stdio.h>    int main()    {        int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;        int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值        //读入n和m,n表示顶点个数,m表示边的条数        scanf("%d %d",&n,&m);        //初始化        for(i=1;i<=n;i++)            for(j=1;j<=n;j++)                if(i==j) e[i][j]=0;                  else e[i][j]=inf;        //读入边        for(i=1;i<=m;i++)        {            scanf("%d %d %d",&t1,&t2,&t3);            e[t1][t2]=t3;        }        //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程        for(i=1;i<=n;i++)            dis[i]=e[1][i];        //book数组初始化        for(i=1;i<=n;i++)            book[i]=0;        book[1]=1;        //Dijkstra算法核心语句        for(i=1;i<=n-1;i++)        {            //找到离1号顶点最近的顶点            min=inf;            for(j=1;j<=n;j++)            {                if(book[j]==0 && dis[j]<min)                {                    min=dis[j];                    u=j;                }            }            book[u]=1;            for(v=1;v<=n;v++)            {                if(e[u][v]<inf)                {                    if(dis[v]>dis[u]+e[u][v])                        dis[v]=dis[u]+e[u][v];                }            }        }        //输出最终的结果        for(i=1;i<=n;i++)            printf("%d ",dis[i]);        getchar();        getchar();        return 0;    }

可以输入以下数据进行验证。第一行两个整数 n m。n 表示顶点个数(顶点编号为 1~n),m 表示边的条数。接下来 m 行表示,每行有 3 个数 x y z。表示顶点 x 到顶点 y 边的权值为 z。

    6 9    1 2 1    1 3 12    2 3 9    2 4 3    3 5 5    4 3 4    4 5 13    4 6 15    5 6 4

运行结果是

    0 1 8 4 13 17

通过上面的代码我们可以看出,这个算法的时间复杂度是 O(N2)。其中每次找到离 1 号顶点最近的顶点的时间复杂度是 O(N),这里我们可以用“堆”(以后再说)来优化,使得这一部分的时间复杂度降低到 O(logN)。另外对于边数 M 少于 N2 的稀疏图来说(我们把 M 远小于 N2 的图称为稀疏图,而 M 相对较大的图称为稠密图),我们可以用邻接表(这是个神马东西?不要着急,下周再仔细讲解)来代替邻接矩阵,使得整个时间复杂度优化到 O( (M+N)logN )。请注意!在最坏的情况下 M 就是 N2,这样的话 MlogN 要比 N2 还要大。但是大多数情况下并不会有那么多边,因此(M+N)logN 要比 N2 小很多。


来源:灵境地平线
通用
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2024-08-04
最近编辑:3月前
周末--电磁仿真
博士 微波电磁波
获赞 22粉丝 14文章 163课程 0
点赞
收藏
作者推荐

python3+oauth2发微博遇到的问题

找了一些使用微博pythonsdk的代码,但是都是python2.7版本,pipinstallsinaweibopy命令不能执行,因为python版本更新到3.7找到了一个经网友修改后的可执行的代码,按提示将网址复制到浏览器得到所需的code但是输入运行后出先如下图所示的问题。代码附上,以后慢慢修改#!/usr/bin/envpython#-*-coding:utf-8-*-__version__=&#39;1.04&#39;__author__=&#39;LiaoXuefeng(askxuefeng@gmail.com)&#39;__publish__=&#39;http://www.cnblogs.com/txw1958/&#39;&#39;&#39;&#39;Python3clientSDKforsinaweiboAPIusingOAuth2.&#39;&#39;&#39;try:importjsonexceptImportError:importsimplejsonasjsonimporttimeimporturllib.requestimportloggingdef_obj_hook(pairs):&#39;&#39;&#39;convertjsonobjecttopythonobject.&#39;&#39;&#39;o=JsonObject()fork,vinpairs.items():o[str(k)]=vreturnoclassAPIError(Exception):&#39;&#39;&#39;raiseAPIErrorifgotfailedjsonmessage.&#39;&#39;&#39;def__init__(self,error_code,error,request):self.error_code=error_codeself.error=errorself.request=requestException.__init__(self,error)def__str__(self):return&#39;APIError:%s:%s,request:%s&#39;%(self.error_code,self.error,self.request)classJsonObject(dict):&#39;&#39;&#39;generaljsonobjectthatcanbindanyfieldsbutalsoactasadict.&#39;&#39;&#39;def__getattr__(self,attr):returnself[attr]def__setattr__(self,attr,value):self[attr]=valuedef_encode_params(**kw):&#39;&#39;&#39;Encodeparameters.&#39;&#39;&#39;args=[]fork,vinkw.items():qv=v.encode(&#39;utf-8&#39;)ifisinstance(v,str)elsestr(v)args.append(&#39;%s=%s&#39;%(k,urllib.parse.quote(qv)))return&#39;&amp;&#39;.join(args)def_encode_multipart(**kw):&#39;&#39;&#39;Buildamultipart/form-databodywithgeneratedrandomboundary.&#39;&#39;&#39;boundary=&#39;----------%s&#39;%hex(int(time.time()*1000))data=[]fork,vinkw.items():data.append(&#39;--%s&#39;%boundary)ifhasattr(v,&#39;read&#39;):filename=getattr(v,&#39;name&#39;,&#39;&#39;)n=filename.rfind(&#39;.&#39;)ext=filename[n:].lower()ifn!=(-1)else&quot;&quot;content=v.read()content=content.decode(&#39;ISO-8859-1&#39;)data.append(&#39;Content-Disposition:form-data;name=&quot;%s&quot;;filename=&quot;hidden&quot;&#39;%k)data.append(&#39;Content-Length:%d&#39;%len(content))data.append(&#39;Content-Type:%s\r\n&#39;%_guess_content_type(ext))data.append(content)else:data.append(&#39;Content-Disposition:form-data;name=&quot;%s&quot;\r\n&#39;%k)data.append(vifisinstance(v,str)elsev.decode(&#39;utf-8&#39;))data.append(&#39;--%s--\r\n&#39;%boundary)return&#39;\r\n&#39;.join(data),boundary_CONTENT_TYPES={&#39;.png&#39;:&#39;image/png&#39;,&#39;.gif&#39;:&#39;image/gif&#39;,&#39;.jpg&#39;:&#39;image/jpeg&#39;,&#39;.jpeg&#39;:&#39;image/jpeg&#39;,&#39;.jpe&#39;:&#39;image/jpeg&#39;}def_guess_content_type(ext):return_CONTENT_TYPES.get(ext,&#39;application/octet-stream&#39;)_HTTP_GET=0_HTTP_POST=1_HTTP_UPLOAD=2def_http_get(url,authorization=None,**kw):logging.info(&#39;GET%s&#39;%url)return_http_call(url,_HTTP_GET,authorization,**kw)def_http_post(url,authorization=None,**kw):logging.info(&#39;POST%s&#39;%url)return_http_call(url,_HTTP_POST,authorization,**kw)def_http_upload(url,authorization=None,**kw):logging.info(&#39;MULTIPARTPOST%s&#39;%url)return_http_call(url,_HTTP_UPLOAD,authorization,**kw)def_http_call(url,method,authorization,**kw):&#39;&#39;&#39;sendanhttprequestandexpecttoreturnajsonobjectifnoerror.&#39;&#39;&#39;params=Noneboundary=Noneifmethod==_HTTP_UPLOAD:params,boundary=_encode_multipart(**kw)else:params=_encode_params(**kw)http_url=&#39;%s?%s&#39;%(url,params)ifmethod==_HTTP_GETelseurlhttp_body=Noneifmethod==_HTTP_GETelseparams.encode(encoding=&#39;utf-8&#39;)req=urllib.request.Request(http_url,data=http_body)ifauthorization:req.add_header(&#39;Authorization&#39;,&#39;OAuth2%s&#39;%authorization)ifboundary:req.add_header(&#39;Content-Type&#39;,&#39;multipart/form-data;boundary=%s&#39;%boundary)resp=urllib.request.urlopen(req)body=resp.read().decode(&quot;utf-8&quot;)r=json.loads(body,object_hook=_obj_hook)if&#39;error_code&#39;inr:raiseAPIError(r.error_code,r[&#39;error_code&#39;],r[&#39;request&#39;])returnrclassHttpObject(object):def__init__(self,client,method):self.client=clientself.method=methoddef__getattr__(self,attr):defwrap(**kw):ifself.client.is_expires():raiseAPIError(&#39;21327&#39;,&#39;expired_token&#39;,attr)return_http_call(&#39;%s%s.json&#39;%(self.client.api_url,attr.replace(&#39;__&#39;,&#39;/&#39;)),self.method,self.client.access_token,**kw)returnwrapclassAPIClient(object):&#39;&#39;&#39;APIclientusingsynchronizedinvocation.&#39;&#39;&#39;def__init__(self,app_key,app_secret,redirect_uri=None,response_type=&#39;code&#39;,domain=&#39;api.weibo.com&#39;,version=&#39;2&#39;):self.client_id=app_keyself.client_secret=app_secretself.redirect_uri=redirect_uriself.response_type=response_typeself.auth_url=&#39;https://%s/oauth2/&#39;%domainself.api_url=&#39;https://%s/%s/&#39;%(domain,version)self.access_token=Noneself.expires=0.0self.get=HttpObject(self,_HTTP_GET)self.post=HttpObject(self,_HTTP_POST)self.upload=HttpObject(self,_HTTP_UPLOAD)defset_access_token(self,access_token,expires_in):self.access_token=str(access_token)self.expires=float(expires_in)defget_authorize_url(self,redirect_uri=None,display=&#39;default&#39;):&#39;&#39;&#39;returntheauthroizeurlthatshouldberedirect.&#39;&#39;&#39;redirect=redirect_uriifredirect_urielseself.redirect_uriifnotredirect:raiseAPIError(&#39;21305&#39;,&#39;Parameterabsent:redirect_uri&#39;,&#39;OAuth2request&#39;)return&#39;%s%s?%s&#39;%(self.auth_url,&#39;authorize&#39;,\_encode_params(client_id=self.client_id,\response_type=&#39;code&#39;,\display=display,\redirect_uri=redirect))defrequest_access_token(self,code,redirect_uri=None):&#39;&#39;&#39;returnaccesstokenasobject:{&quot;access_token&quot;:&quot;your-access-token&quot;,&quot;expires_in&quot;:12345678},expires_inisstandardunix-epoch-time&#39;&#39;&#39;redirect=redirect_uriifredirect_urielseself.redirect_uriifnotredirect:raiseAPIError(&#39;21305&#39;,&#39;Parameterabsent:redirect_uri&#39;,&#39;OAuth2request&#39;)r=_http_post(&#39;%s%s&#39;%(self.auth_url,&#39;access_token&#39;),\client_id=self.client_id,\client_secret=self.client_secret,\redirect_uri=redirect,\code=code,grant_type=&#39;authorization_code&#39;)r.expires_in+=int(time.time())returnrdefis_expires(self):returnnotself.access_tokenortime.time()&gt;self.expiresdef__getattr__(self,attr):returngetattr(self.get,attr)defmain():try:#step1定义appkey,appsecret,回调地址:APP_KEY=&quot;525566084&quot;APP_SECRET=&quot;98e8559d04d5bfd168322d4ecfd014fb&quot;CALLBACK_URL=&#39;https://api.weibo.com/oauth2/default.html&#39;#step2引导用户到授权地址client=APIClient(app_key=APP_KEY,app_secret=APP_SECRET,redirect_uri=CALLBACK_URL)print(client.get_authorize_url())#step3换取AccessToken#将上面的地址复制到浏览器中运行,输入账号密码授权,新的网址里面就有coder=client.request_access_token(input(&quot;Inputcode:&quot;))#输入授权地址中获得的CODEclient.set_access_token(r.access_token,r.expires_in)##此时获取到access_token和expires_in应该保存下来,因为有一个有效期,如果短时间需要多次发微博,可以重复使用,而不需要每次获取。##step4使用获得的OAuth2.0AccessToken调用APIprint(client.get.account__get_uid())print(client.post.statuses__update(status=&#39;测试Python3+OAuth2.0发微博&#39;+str(time.time())))#print(client.upload.statuses__upload(status=&#39;测试Python3OAuth2.0带图片发微博&#39;+str(time.time()),pic=open(&#39;test.png&#39;,&#39;rb&#39;)))exceptExceptionaspyOauth2Error:print(pyOauth2Error)if__name__==&#39;__main__&#39;:main()来源:灵境地平线

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习 福利任务 兑换礼品
下载APP
联系我们
帮助与反馈