Dijkstra算法的二叉堆優化


算法原理

每次擴展一個距離最小的點,再更新與其相鄰的點的距離。

如何尋找距離最小的點

普通的Dijkstra算法的思路是直接For i: 1 to n

優化方案是建一個小根堆,小根堆里存儲由當前結點更新距離的所有點,那么堆頂就是距離最小的點

如何尋找與源點相鄰的點

當然是鄰接表

具體實現

建一個小根堆heap[] ,用來存儲結點的序號,用一個數組pos[i] 來存儲第i個結點在堆中的位置,用一個標記數組in_heap[] 來記錄結點是否在堆中,dis[i] 表示到第i個結點的最短距離

對於小根堆的操作還是基本的put()get() ,但由於有的結點已經在堆中了,所以可以把put() 拆為插入堆和調整位置兩個部分

完整操作如下:

1.將與源點相鄰的點進行松弛操作后加入堆

2.取出位於堆頂的結點

3.若取出的點為終點,則結束算法

4.將與當前結點相鄰的點進行松弛操作

​ (1)如果該點已經在堆中,就調整在堆中的位置

​ (2)如果該點不在堆中,就加入堆

5.繼續第二步

例題

最短路徑問題

時間限制:1秒 內存限制:256兆

題目描述

平面上有n個點(n<=100),每個點的坐標均在-10000~10000之間,其中的一些點之間有連線。若有連線,則表示可從一個點到達另一個點,即兩點間有通路,通路的距離為兩點間的之間距離。現在的任務是找出一點到另一點之間的最短路徑、

輸入

輸入共有n+m+3行,其中:
第一行為整數n
第2行到第n+1行(共n行),每行兩個整數x和y,描述了一個點的坐標(以一個空格分隔)
第n+2行為一個整數m,表示圖中連線的個數
此后的m行,每行描述一條連線,由兩個整數i和j組成,表示第i個點和第j個點之間有連線
最后一行:兩個整數s和t,分別表示源點和目標點

輸出

輸出僅一行,一個實數(保留兩位小數),表示從s到t的最短路徑長度

樣例輸入

5
0 0
2 0
2 2
0 2
3 1
5
1 2
1 3
1 4
2 5
3 5
1 5

樣例輸出

3.41

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 100+5

struct Edge//鄰接表
{
	int to;
	double v;
	Edge *next;
};

struct Node
{
	int x,y;
}node[MAXN];

int n,m,pos[MAXN],heap_size,s,t,heap[MAXN];
double dis[MAXN];
Edge *first[MAXN];
bool in_heap[MAXN];

void add_edge(int u,int v,double len)
{
	Edge *temp=new Edge;
	temp->to=v;
	temp->v=len;
	temp->next=first[u];
	first[u]=temp;
}

void calc(int i,int j)
{
	double len=sqrt(pow((double)(node[i].x-node[j].x),2)+pow((double)(node[i].y-node[j].y),2));
	add_edge(i,j,len);
	add_edge(j,i,len);
}

void swapp(int i,int j)
{
	int temp=heap[i];
	heap[i]=heap[j];
	heap[j]=temp;
	pos[heap[j]]=j;//調整指針
	pos[heap[i]]=i;
}

void shift_up(int now)//調整位置
{
	int next=0;
	while(now>1)
	{
		next=now>>1;
		if(dis[heap[next]]>dis[heap[now]])
			swapp(next,now);
		now=next;
	}
}

void put(int x)//插入堆
{
	in_heap[x]=true;
	heap[++heap_size]=x;
	pos[x]=heap_size;
	shift_up(heap_size);
}

int get()//取堆頂元素
{
	int now=1,next,res=heap[1];
	in_heap[heap[1]]=false;
	heap[1]=heap[heap_size--];
	pos[heap[1]]=1;
	while(now*2<=heap_size)
	{
		next=now<<1;
		if(next<heap_size&&dis[heap[next+1]]<dis[heap[next]])
			++next;
		if(heap[now]<=heap[next])
			return res;
		swapp(now,next);
		now=next;
	}
	return res;
}

void dijkstra()
{
	put(s);
	dis[s]=0;
	while(heap_size>0)
	{
		int top=get();
		if(top==t)
			break;
		Edge *temp=first[top];
		while(temp!=NULL)
		{
			if(dis[temp->to]>dis[top]+temp->v)
			{
				dis[temp->to]=dis[top]+temp->v;
              	//結點在堆中就只調整位置,否則插入堆並調整位置
				if(in_heap[temp->to])
					shift_up(pos[temp->to]);
				else
					put(temp->to);
			}
			temp=temp->next;
		}
	}
}

int main()
{
	int i,x,y;
	scanf("%d",&n);
	for(i=1;i<=n;++i)
		scanf("%d%d",&node[i].x,&node[i].y);
	scanf("%d",&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		calc(x,y);
	}
	scanf("%d%d",&s,&t);
	memset(dis,127,sizeof(dis));
	dijkstra();
	printf("%.2lf\n",dis[t]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM