Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Оказалось, что на самом деле очень легко писать Дейкстру за O(n * log(n) + m * log(n))!
На интуитивном уровне понятно, что если до какой-то вершины путь сейчас минимальный, то еще меньше сделать его мы не сможем.
Откуда вы взяли лишнее log(n) при m? Стандартная сложность этого алгоритма при использовании кучи — O(m + n* log(n)).
А вот и нет, интуитивно понятно, но неверно. Точнее, верно только в том случае, если вы соблюдаете ограничение применимости этого алгоритма, про которое в посте ни слова.
Ну и да, легко это писать, когда за вас куча реализована фреймворком, а вот когда вы эту кучу делаете ручками, все не так радужно.
struct XVert{
int backidx;
int backway;
double dist;
};
struct Edge{
int idx;
double dist;
};
struct Vert{
int NEdges;
Edge *Edges;
};
struct Graph{
int NV;
Vert *Verts;
};
double Dejkstra(Graph *G,int Start,int End){
int NV=G->NV;
int LQ=NV;
XVert *Verts=new XVert[NV];
Edge *Que=new Edge[NV];
for(int i=0;i<NV;i++){
int j=(i==0 || i==Start) ? Start-i : i;
Verts[i].dist=100;
Verts[i].backidx=j;
Verts[i].backway=-1;
Que[i].dist=100;
Que[i].idx=j;
}
Verts[Start].dist=Que[0].dist=0;
Verts[Start].backway=-1;
for(;;){
Edge top=Que[0];
int v0=top.idx;
if(top.idx==End) break;
Edge btm=Que[--LQ];
int a=0,b;
while((b=2*a+2)<=LQ){
if(b==LQ || Que[b].dist>Que[b-1].dist) b--;
if(Que[b].dist>=btm.dist) break;
Que[a]=Que[b];
Verts[Que[a].idx].backidx=a;
a=b;
}
Que[a]=btm;
Verts[btm.idx].backidx=a;
Verts[top.idx].backidx=-1;
for(int k=0;k<G->Verts[v0].NEdges;k++){
Edge *e=G->Verts[v0].Edges+k;
double d=top.dist+e->dist;
int h=Verts[e->idx].backidx;
if(h<0 || d>=Verts[e->idx].dist) continue;
Verts[e->idx].dist=d;
Verts[e->idx].backway=v0;
Edge current=Que[h];
while(h>0){
int h1=(h-1)/2;
if(Que[h1].dist<d) break;
Que[h]=Que[h1];
Verts[Que[h].idx].backidx=h;
h=h1;
}
current.dist=d;
Que[h]=current;
Verts[e->idx].backidx=h;
}
}
double res=Que[0].dist;
delete[] Verts;
delete[] Que;
return res;
}
Ну и да, легко это писать, когда за вас куча реализована фреймворком, а вот когда вы эту кучу делаете ручками, все не так радужно.
Откуда вы взяли лишнее log(n) при m?
O(log|E|), что для полного графа равно O(log(|V|^2))… против O(log(|V|)
O(log (|V|^2)) = O(2*log |V|) = O(log |V|),
Между O(2*log(|V|) и O(log(|E|*log(|V|)
А если мы умеем быстро искать элемент, то изменить его вес так же просто, как добавить новый
При вытаскивании элемента из середины — не важно, для удаления или для перевесовки, — нужно сделать либо то, либо другое, в зависимости от отношения весов элементов.
Собственно, когда я реализовывал этот самый алгоритм Дийкстры, я при реализации «кучи» сделал только утопление, считая, что если я поменял с последним элементом, этот элемент должен потонуть обратно вниз. Потом сутки пытался понять почему у меня с вероятностью 70% получается неверный результат.
От обхода в ширину к алгоритму Дейкстры