迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

算法复杂度为n^2

这里有一篇讲解的很清晰的文章:http://blog.chinaunix.net/uid-26548237-id-3834514.html

下面说说我个人的理解:

就以这张图为例:

要找出A点到其他点的最短路径,应该怎么找?

这个算法的思路是:

    1. 先找出离A直接连通,且最近的那个点。怎么找?遍历所有的点与A的距离,可以知道AB=6,AC=3。DEF没有与A相连,那AD就设为无穷大。如此一轮比较下来就能知道离A最近的点是C。
    2. 最近的点找到了,就以这个点出发,继续找离C最近的点。同上也是遍历所有点,但在这个过程中,同时比较A经过C到该点的距离与A直接与该点连通的距离。比如遍历到B点时,比较ACB和AB,如果ACB小于AB,则将A到B的距离保存为ACB。到D点时,比较ACD和AD,以此类推。一轮比较后,在找到离C最近的点的同时,更新了A到各个点的最短距离。直到最后一个点遍历完毕,则A与所有点的最短距离也就知道了。

具体的实现如下:

 #include <iostream>
using namespace std; #define MAXVEX 20
#define MAXEDGE 20
#define INFINITY 65535 typedef struct
{
int vexs[MAXVEX];
int arc[MAXVEX][MAXVEX];
int numVertexes,numEdges;
}MGraph; typedef int Patharc[MAXVEX];
typedef int ShortPathTable[MAXVEX]; void CreateGraph(MGraph* G)
{
cout<<"请输入边数和顶点数:\n";
int d,n,i,j;
cin>>d>>n;
G->numVertexes = n;
G->numEdges = d; //给顶点和边初始化
for(i = ;i<G->numVertexes;i++)
G->vexs[i] = i;
for(i = ;i<G->numVertexes;i++)
{
for(j = ;j<G->numVertexes;j++)
{
if(i==j)
G->arc[i][j] = ;
else
G->arc[i][j] = G->arc[j][i] = INFINITY;
}
} G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=; G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=; G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=;
G->arc[][]=; G->arc[][]=; for(i = ;i<G->numVertexes;i++)
{
for(j = i;j<G->numVertexes;j++)
{
G->arc[j][i] = G->arc[i][j];
}
}
} /* Floyd算法,求网图G中各顶点v到其余顶点w的最短路径P[v][w]及带权长度D[v][w]。 */
void Dijkstra(MGraph G,int v0, Patharc *P, ShortPathTable *D)
{
int v,w,k,min; int final[MAXVEX];/* final[w]=1表示求得顶点v0至vw的最短路径 */ for(v = ;v<G.numVertexes;v++)
{
(*P)[v] = ;
/* 初始化P */
(*D)[v] = G.arc[v0][v];
/* D[v]值即为对应点间的权值 */
final[v] = ;
} (*D)[v0] = ;
final[v0] = ; /* 开始主循环,每次求得v0到某个v顶点的最短路径 */
for(v = ;v<G.numVertexes;v++)
{
min = INFINITY;/* 当前所知离v0顶点的最近距离 */
for(w = ;w<G.numVertexes;w++)/* 寻找离v0最近的顶点 */
{
if((*D)[w]<min && !final[w])
{
min = (*D)[w]; /* w顶点离v0顶点更近 */
k = w;
}
}
final[k] = ; /* 将目前找到的最近的顶点置为1 */ /* 修正当前最短路径及距离 */
for(w = ;w<G.numVertexes;w++)/* 寻找离v0最近的顶点 */
{
/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
if(min+G.arc[k][w] < (*D)[w] && !final[w])
{
/* 说明找到了更短的路径,修改D[w]和P[w] */
(*D)[w] = min+G.arc[k][w];
(*P)[w] = k;
}
}
} } int main()
{
int v0,i,j; MGraph G; Patharc P;
ShortPathTable D; CreateGraph(&G);
v0 = ;
Dijkstra(G,v0,&P,&D); cout<<"最短路径倒序如下:\n";
for(i = ;i<G.numVertexes;i++)
{
cout<<"v0 - v"<<i<<" : ";
j = i;
while(P[j]!=)
{
cout<<P[j]<<" ";
j = P[j];
}
cout<<endl;
} cout<<"\n源点到各顶点的最短路径长度为:\n"; for(i=; i<G.numVertexes; ++i)
{
cout<<"v"<<G.vexs[]<<" - v"<<G.vexs[i]<<" : "<<D[i]<<endl;
}
return ; }

算法优化:

参考这篇文章:http://blog.csdn.net/zhongyanghu27/article/details/8221276

算法复杂度为n^2,我们可以发现,如果边数远小于n^2,对此可以考虑用这种数据结构进行优化,取出最短路径的复杂度降为O(1);每次调整的复杂度降为O(elogn);e为该点的边数,所以复杂度降为O((m+n)logn)。

实现

1. 将与源点相连的点加入,并调整堆。
2. 选出堆顶元素u(即代价最小的元素),从堆中删除,并对堆进行调整。
3. 处理与u相邻的,未被访问过的,满足三角不等式的顶点
1):若该点在堆里,更新距离,并调整该元素在堆中的位置。
2):若该点不在堆里,加入堆,更新堆。
4. 若取到的u为终点,结束算法;否则重复步骤2、3。
 
 
优先队列+vector优化:
 
引用此处:http://blog.163.com/jiongjiong_zier/blog/static/192279231201171934931645/
优先级队列本质上是由堆来实现的,既然是队列,我们能进行的操作无非是入队,出队,判空和大小,查找是不可以的。创建一个优先级队列时有两种申请方式:priority_queue<ElemType>q或者priority_queue<ElemType,vector<ElemType>,greater<ElemType> >q,前者默认的东西比较多,比如底层的实现方式以及堆的性质;第二种就说明的比较清晰了,第一个参数是队列中元素的类型;第二个就是队列底层的实现方式,这里是用vector实现的;第三个是堆的性质,greater对应小顶堆,less对应大顶堆,当然这两个只能适用于一些基本类型,于是优先级就是这些基本类型元素的大小。如果遇到一些复杂的类型,比如结构体或类,那么就要重新定义这个优先级,于是便要重载bool operator(),有意思的是,在重载时,>对应的是小顶堆而<对应的是大顶堆,至于为什么,哥真的不懂。。。。
 
然后就要说说优化后的dij了,虽然和朴素的dij的思路完全一样,可还是有些细节需要注意。比如优化的dij中,d[i]的初值都是INF,除了d[source],然后将源点入队但并不访问,实际上进行了n次循环。还有一点就是对于pair这个二元组的使用,真的很方便啊有木有,如果采用结构体还得引入变量,赋值后才能进行入队。。。STL真是个好东西,就是有时候效率低了点。。。。
 
 /*
Dijkstra的算法思想:
在所有没有访问过的结点中选出dis(s,x)值最小的x
对从x出发的所有边(x,y),更新
dis(s,y)=min(dis(s,y),dis(s,x)+dis(x,y))
*/ #include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int Ni = ;
const int INF = <<; //这里是位运算<<,1<<3=8,1<<27即2的27次方 typedef struct node
{
int x;
int d;
node(){}
node(int a,int b)
{x = a;d = b;} //>对应小顶堆,<对应大顶堆
bool operator <(const node& a) const
{
if(d == a.d)
return x<a.x;
else
return d>a.d;
}
}node; vector<node> eg[Ni];//eg是一个node数组,保存邻接关系 int dis[Ni],n;//dis使用1-n的部分 void Dijkstra(int s)
{
int i,j;
for(i=;i<=n;i++)//要到n
dis[i] = INF;
priority_queue<node> q;
dis[s] = ;
q.push(node(s,dis[s]));
while(!q.empty())
{
node x = q.top();
q.pop();
for(j = ;j<eg[x.x].size();j++)//遍历x的所有邻接点
{
node y = eg[x.x][j];//y是x的邻接点
if(dis[y.x]>x.d+y.d)
{
dis[y.x] = x.d+y.d;
q.push(node(y.x,dis[y.x]));
}
}
} } int main()
{
int m,a,b,d;//关系个数
while(cin>>n>>m,m+n)
{
for(int i = ;i<=n;i++)
eg[i].clear();//初始化
while(m--)
{
cin>>a>>b>>d;
eg[a].push_back(node(b,d));
eg[b].push_back(node(a,d));
}
Dijkstra();
for(int i = ;i<=n;i++)
cout<<dis[i]<<" ";
} return ;
}
/*
6 6
1 2 2
3 2 4
1 4 5
2 5 2
3 6 3
5 6 3
*/
 此外还可以使用pair代替结构
ps:
pair定义在头文件utility,且在std命名空间中;删除后无影响,应该是引入其他头文件是间接引入了utility,比如map头文件。
我们知道map和multimap的作用,这两种数据类型在存储数据时,会根据pair<>的first成员进行排序,不同的时前者将不会插入对first成员重复的结构,后者可以。那如果我们只想存储pair对,而不需要对其排序,则需要用到vector

 /*
使用pair代替结构
*/ #include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
using namespace std;
const int Ni = ;
const int INF = <<; typedef pair<int,int> pa; int dis[Ni],n;//dis使用1-n的部分 vector<pair<int,int> > eg[Ni]; void Dijkstra(int s)
{
int i,j;
for(i=;i<=n;i++)//要到n
dis[i] = INF;
priority_queue< pa,vector<pa>,greater<pa> >q; //优先级队列:小顶堆
dis[s] = ;
q.push(make_pair(s,dis[s])); while(!q.empty())
{
pa x = q.top();
q.pop();
int w = x.first;
for(j = ;j<eg[w].size();j++)//遍历x的所有邻接点
{
pa y = eg[w][j];//y是x的邻接点
int u = y.first;
if(dis[u]>x.second+y.second)
{
dis[u] = x.second+y.second;
q.push(make_pair(u,dis[u]));
}
}
} } int main()
{
int m,a,b,d;//关系个数
while(cin>>n>>m,m+n)
{
for(int i = ;i<=n;i++)
eg[i].clear();//初始化
while(m--)
{
cin>>a>>b>>d;
eg[a].push_back(make_pair(b,d));
eg[b].push_back(make_pair(a,d));
}
Dijkstra();
for(int i = ;i<=n;i++)
cout<<dis[i]<<" ";
} return ;
}
/*
6 6
1 2 2
3 2 4
1 4 5
2 5 2
3 6 3
5 6 3
*/
 
 
 
优化思路:
 
权值排序优化策略:
  问题:S集合中未确定最短路径的顶点无序存放在数组中,每一次选择权值最小的弧段必须将所有未选择顶点对应的数组元素完全扫描才能找到,在数据量较大的情况下,计算速度受到严重制约。
  优化策略:将要扫描的结点按其对应弧的权值进行顺序排列,每循环一次即可得到符合条件的结点,大大提高了算法的执行效率

A*算法优化策略
  问题:Dijkstra算法基于广度优先搜索策略,即从源点出发,通过权值迭代遍历所有其他结点后,最后得到从源点到其他各结点的最短路径。整个搜索好似一个圆形向外展开,直到到达目的地,这样的搜索方式是盲目的。很明显这样求解一定能找到最优解,但节点展开的数量和距离成级数增加,会导致大量无效点的搜索,大大的降低搜索的效率。
  优化策略:采用改进的Dijkstra算法——A*算法。A*算法是人工智能运用在游戏中的一个重要实践,它主要是解决路径搜索问题。A*算法实际是一种启发式搜索。发表论文。所谓启发式搜索,就是利用一个估价函数judge()评估每次决策的价值,决定先尝试哪一种方案。这样可以极大地优化普通的广度优先搜索。从Dijkstra算法到A*算法是判断准则的引入,如果这个判断条件不成立,同样地,只能采用Dijkstra算法。所以A*算法中的估价函数是至关重要[3]。

扇形优化策略
  问题:Dijkstra算法的搜索过程好似以源点为圆心的一系列同心圆向外展开,搜索过程中没有考虑到终点所在方向或位置,搜索是盲目的。这样导致大量的无用临时结点被反复搜索,成为实际应用中的瓶颈。
  优化策略:从尽量减少最短路径分析过程中搜索的临时结点数量,限制范围搜索和限定方向搜索考虑进行优化。那么这种有损算法是否可行呢?我们知道,现实生活中行进,不会向着目的地的相反方向行进,否则就是南辕北辙。所以,当所研究的网络可以抽象化为平面网络的条件下,也不必搜索全部结点,可以在以源点到终点所在直线为轴线的扇形区域内搜索最短路径。这样,搜索方向明显地趋向终点,提高了搜索速度,虽然抛弃了部分结点,但基本上不影响搜索的成功率[5]。

邻接点优化策略
  问题:Dijkstra算法在提取最短路径结点时需要访问所有的未确定最短路径的结点,算法的时间复杂度为O(n2),如果只希望找到从源点到某一特定的终点的最短路径也不例外。结点数n越大,算法的计算效率和存储效率越低。
  优化策略:只对最短路径上结点的邻接点作处理,不涉及其他结点。即(1)只从源点的邻接点集合中选择权值最小的结点作为转接点,将此转接点加入已确定最短路径的结点集合S中;(2)对此转接点的邻接点集合与S的差集中的结点的权值进行更新;(3)从S中所有结点的邻接点集合的并集与S的差集中选择权值最小的结点作为下一个转接点,并将此转接点加入S中。重复(2),(3)操作,直至所有的结点确定最短路径。优化算法在更新最短路径值与选择最短路径值最小的结点时,仅仅涉及相关结点的邻接点集合及S集合中所有结点的邻接点集合与S集合的差集,时间复杂度取决于转接点的邻接点的数量多少,减少了计算次数与比较次数

参考文献:
[1] 严蔚敏,吴伟民. 数据结构(C语言版)[M]. 北京:清华大学出版社,1997,186~190.
[2] 谢柏青,佘晓歌. 算法与数据结构[M]. 北京:高等教育出版社,2001,230~232.
[3] 陈益富,卢 潇,丁豪杰. 对Dijkstra算法的优化策略研究[J]. 计算机技术与发展,2006,16(9):73~75.
[4] 章永龙. Dijkstra最短路径算法优化[J]. 南昌工程学院学报,2006,25(3):30~33.
[5] 胡树玮,张修如,赵 洋.扇形优化Dijkstra算法[J]. 计算机技术与发展,2006,16(12):49~51.

最短路问题(dijkstral 算法)(优化待续)的更多相关文章

  1. 最短路问题(floyd算法)(优化待续)

    问题描述: 最短路问题(short-path problem):若网络中的每条边都有一个数值(长度.成本.时间等),则找出两节点(通常是源节点和阱节点)之间总权和最小的路径就是最短路问题.最短路问题是 ...

  2. SQL Server 聚合函数算法优化技巧

    Sql server聚合函数在实际工作中应对各种需求使用的还是很广泛的,对于聚合函数的优化自然也就成为了一个重点,一个程序优化的好不好直接决定了这个程序的声明周期.Sql server聚合函数对一组值 ...

  3. Harris角点检测算法优化

    Harris角点检测算法优化 一.综述 用 Harris 算法进行检测,有三点不足:(1 )该算法不具有尺度不变性:(2 )该算法提取的角点是像素级的:(3 )该算法检测时间不是很令人满意. 基于以上 ...

  4. RSA算法优化

    RSA算法优化 大数乘法 模乗优化 剩余定理(孙子定理) RSA加解密 python的RSA计算优化 #-*- coding: utf-8 -*- ''' /********************* ...

  5. 内部元素一一相应的集合的算法优化,从list到hashmap

    说是算法优化,基本上是在吹牛,仅仅只是算是记录下,我写代码时候的思路.毕竟还是小菜鸟. 我要开一个party,与会者都是情侣,可是情侣并非一起过来的,而是有先有后,可是每位与会者来的时候都拿着一束鲜花 ...

  6. Spark Mllib里的协调过滤的概念和实现步骤、LS、ALS的原理、ALS算法优化过程的推导、隐式反馈和ALS-WR算法

    不多说,直接上干货! 常见的推荐算法 1.基于关系规则的推荐 2.基于内容的推荐 3.人口统计式的推荐 4.协调过滤式的推荐 (广泛采用) 协调过滤的概念 在现今的推荐技术和算法中,最被大家广泛认可和 ...

  7. LeetCode :2.两数相加 解题报告及算法优化思路

    题目连接:2.两数相加 题意 题目难度标为 中等, 因为题意上有一部分理解难度,以及需要数据结构的链表基础. 还不知道到链表的童鞋可以粗略的看下百度百科或者是翻出数据结构的书看一看,通俗一点的语言来解 ...

  8. 最短路问题---Dijkstra算法学习

    Dijkstra又称单源最短路算法,就从一个节点到其他各点的最短路,解决的是有向图的最短路问题 此算法的特点是:从起始点为中心点向外层层扩展,直到扩展到中终点为止. 该算法的条件是所给图的所有边的权值 ...

  9. 任意两点间的最短路问题(Floyd-Warshall算法)

    /* 任意两点间的最短路问题(Floyd-Warshall算法) */ import java.util.Scanner; public class Main { //图的顶点数,总边数 static ...

随机推荐

  1. AutoCad2012新增类AcRxVariablesDictionary 可以获取所有变量名和值

    //AutoCad2012新增类 获取所有变量名和值 AcRxVariablesDictionary *dic=AcRxVariablesDictionary::get(); const AcArra ...

  2. 0基础浅谈反射型xss(2)

    0x1:回顾前文疑惑“先闭合,在构造” 上一篇,我们说到了xss的一个触发精髓,“先闭合,在构造”,对于前面的先闭合,我们来简单的解释一下:   首先说,为什么要闭合? 因为HTML标签都是成对出现的 ...

  3. javascript IE事件处理及跨浏览器事件处理程序

    一.javascript事件处理中 addEventListener/removeEventListener 用于绑定事件和解除事件,但大多用于chrome/火狐/IE9这些比较高级的浏览器中,IE8 ...

  4. Android脚本打包

    最近项目中需要添加应用渠道,我使用的是友盟统计,对于不同渠道需要编译不同版本,对于开发者说编译一次,手动操作还是可以接受的,但是项目发布版本频率较高,而且渠道很多,这就是一个体力活,而且手动打包还比较 ...

  5. 隔离python 运行环境和Pycharm 设置代码同步

    隔离python 运行环境 查看当前有哪些虚拟环境:workon 进入虚拟环境:workon django 退出虚拟环境:deactivate 创建虚拟环境: mkvirtualenv -p /usr ...

  6. Java类型简介

    1 java基本数据类型 1.1 基本数据类型 java的基础数据类型有多少个,每个在内存的分配是多少呢? 类型 分配字节 byte 1 short 2 char 2 int 4 long 8 ref ...

  7. 博客主题皮肤探索-主题的本地化和ChromeCacheView使用

    还有前言 用了大佬的主题之后,有些资源是无法在暴露的接口处更改的,需要自己去css更改.但后台给的都是压缩过的,找起来比较困难,所以特意记录了这篇. 本地资源替换 侧边栏: .introduce-bo ...

  8. Visual Studio 跨平台開發實戰(4) - Xamarin Android 基本控制項介紹 (转帖)

    前言 不同於iOS, Xamarin 在Visual Studio中針對Android, 可以直接設計使用者介面. 在本篇教學文章中, 筆者會針對Android的專案目錄結構以及基本控制項進行介紹, ...

  9. linux 下 vi 编辑器 使用

    命令模式(command mode).插入模式(Insert mode)和底行模式(last line mode) 1.进入插入模式 按「i」切换进入插入模式「insert mode」,按“i”进入插 ...

  10. 通过java代码给log4j指定appender

    工具代码 import org.apache.log4j.ConsoleAppender; import org.apache.log4j.LogManager; import org.apache. ...