Dijkstra算法介绍及其优先队列优化和斐波那契堆优化

Posted 2020.7.30

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dijkstra算法介绍及其优先队列优化和斐波那契堆优化相关的知识,希望对你有一定的参考价值。

Dijkstra算法介绍及其优先队列优化和斐波那契堆优化



一、dijkstra算法概述

  • Dijkstra算法解决的是带权重的有向图上单源最短路径问题,使用此算法的前提条件是所有边的权重都为非负值。
  • 算法重复从节点集中选择最短路径估计估计最小的节点u,然后对所有从u发出的边进行松弛(更新节点u的所有子节点到节点u的距离)。

二、实现dijkstra算法需要掌握的算法知识

1、从数据中剔除最小项(函数ExtractMin())

  1. 如果只是处于练习的目的,可以通过遍历所有节点(通常可以将所有节点存储于链表中,链表关键字为与父节点间的距离,主要卫星数据是本节点名称,及本节点的所有子节点的名称和二者间的距离)找到与源节点相距最短的节点(源节点的距离初始化为0,其他节点距离初始化为max),删除并返回该节点。但是这样做及其耗费时间。

  2. 使用优先队列(二叉堆实现,在后面讲解)删除并返回一个节点,优先队列指的是某个节点的值至多与其父节点一样大。因此,堆中的最小元素存放在根节点中(A[1])。因此,我们只需将根节点作为返回值,并删除根节点即可。

  3. 使用斐波那契堆删除并返回一个节点,斐波那契堆含有一个指向具有最小关键字的指针,因此只需删除并返回该指针即可。

2、松弛操作

伪代码:

Relax(u,v,w)//u为节点v的父节点
{
	if v.d>u.d+w(u,v)//w(u,v)表示从节点u到节点v的距离
		v.d=u.d+w(u,v)//更新子节点距离
		v.Π=u//将节点u最为节点v的新的父节点,属性Π表示v的父节点,本人代码使用f_dis、f_time、或father表示
}

松弛操作对应优先队列和斐波那契堆中的关键字减值。

3、伪代码分析

实现Dijkstra的伪代码:

Dij(G,w,s)//G表示所有节点的集合,s表示源节点
	Q=G.V//使用一个最小优先队列保存节点(基于二叉堆的优先队列或者斐波那契堆)
	while Q!=NIL //只有图中还有节点
		u=ExtractMin(Q)
		for each vertex v∈G.Adj[u]//对于节点u的每一个子节点
			Relax(u,v,w)//w表示一个属性,即节点u与节点v间的距离

可见,算法本身很简单,但其涉及的函数是比较难以用高效的方法实现的。

三、最小优先队列概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、最小优先队列伪代码分析

1.维护堆的性质

在这里插入图片描述
图片中展示的是建立最大堆,建立最小堆需要改为:A[l]<A[i],以此类推。

2.建堆

在这里插入图片描述
所谓叶节点:就是下图中的节点8、7、6、9及其上面的节点。
在这里插入图片描述

3.剔除最小节点

ExtractMin(A)
	if A.heap-size<1//已经是空堆
		error "heap underflow"
	min=A[1]
	A[1]=A[A.heap-size]//这种直接赋值在实现中不可取,应该交换二者的值(地址)
	A.heap-size=A.heap-size-1//实现了删除一个节点
	MaxHeapIfy(A,1)
	return min

4.关键字减值(松弛操作,具体实现中将此函数命名为relax())

图片中描述的是最大堆的性质,类比即可。
在这里插入图片描述

HeapReduceKey(A,i,key)
	if key>A[i]
		error "new key is big than curren key"
	while i>and A[parent[i]]<A[i]
		exchange A[i] with A[parent[i]]
		i=parent[i];

五、例题分析

在这里插入图片描述

输入样例:

10 15
0 1 0 1 1
8 0 0 1 1
4 8 1 1 1
5 4 0 2 3
5 9 1 1 4
0 6 0 1 1
7 3 1 1 2
8 3 1 1 2
2 5 0 2 2
2 1 1 1 1
1 5 0 1 3
1 4 0 1 1
9 7 1 1 3
3 1 0 2 5
6 3 1 2 1
5 3

输出样例:

Time = 6: 5 => 4 => 8 => 3
Distance = 3: 5 => 1 => 3

1. 首先设法存储输入的数据

我们将所有节点数和道路条数分别记为all_pos、all_way,将起点和终点分别记为start、end。将道路信息存储至结构中。

typedef struct min{
	int pos;//本节点名称
	int son[3][1000];//子节点名称及属性,son[0][i]表示子节点名称,
	//对应的son[1][i]表示pos节点到son[0][i]节点的距离,son[2][i]表示对应时间
	int k;//子节点数目(松弛操作需要)
	int about_dis;//距离源节点可能的最小距离
	int about_time;
	int point;//根据题目表述建立的一个变量,表示节点个数
	struct min *f_time;
	struct min *f_dis;//父节点
}Min;

2. 设计最小优先队列

typedef struct heap{
	Min *A[1000];//用于存储节点
	int  sta[1000];//用于存储每一个关键字(节点名称)在数组A中的位置
	//如H->sta[key]表示关键字key在数组A中的位置,他是优化过程的核心。
	int heap_size;//数组A中符合优先队列的元素个数
	int length;//数组A的长度(为节约空间,应与all_pos一致)
}Heap;

3.实现Dijkstra算法(限于题目要求,略显臃肿)

/*dij*/
void dij(Heap *H,int opt)
{
	/*求距离*/ 
	if(opt==1)
	{
		while(!IsEmpty(H))
		{
			Min *u=ExtractMin(H,opt);
			for(int i=0;i<u->k;i++)
				relax(H,u,i,opt);
		}
	}
	/*求时间*/
	else
	{
		while(!IsEmpty(H))
		{
			Min *u=ExtractMin(H,opt);
			for(int i=0;i<u->k;i++)
				relax(H,u,i,opt);
		}
	}
}
Min *ExtractMin(Heap *H,int opt)
{
	if(opt==1)
	{
		Min *u=H->A[1];
		
		H->sta[H->A [1]->pos]=H->heap_size ;//更新关键字在数组A中的位置,关键操作。
		H->sta [H->A [H->heap_size ]->pos]=1;
		
		H->A [1]=H->A[H->heap_size ];
		H->A [H->heap_size ]=u;
		H->heap_size--;
		MaxHeapIfy(H,1,opt);
		return u;
	}
	else
	{
		Min *u=H->A[1];
		H->sta[H->A [1]->pos]=H->heap_size ;
		H->sta [H->A [H->heap_size ]->pos]=1;
		H->A [1]=H->A[H->heap_size ];
		H->A [H->heap_size ]=u;
		H->heap_size--;
		MaxHeapIfy(H,1,opt);
		return u;
	}
} 
void relax(Heap *H,Min *u,int p,int opt)
{
	Min *s=H->A[H->sta[u->son[0][p]]];
	int i=H->sta[u->son[0][p]];
	if(opt==1)
	{
		if(s->about_dis>(u->about_dis +u->son[1][p]))
		{
			s->f_dis =u;
			s->about_dis=u->about_dis +u->son[1][p];
			s->point=u->point+1;
			while((i>1)&&(H->A[i/2]->about_dis >H->A[i]->about_dis))
			{
				H->sta[H->A[i/2]->pos]=i;
				H->sta[H->A[i]->pos]=i/2;
				Min *temp=H->A[i/2];
				H->A[i/2]=H->A[i];
				H->A[i]=temp;
				i=i/2;
			}
		}
		else if((s->about_dis==(u->about_dis +u->son[1][p]))&&(s->point>=u->point+1))
		{
			s->f_dis =u;
			s->point=u->point+1;
			s->about_dis=u->about_dis +u->son[1][p];
			while((i>1)&&(H->A[i/2]->about_dis >H->A[i]->about_dis))
			{
				H->sta[H->A[i/2]->pos]=i;
				H->sta[H->A[i]->pos]=i/2;
				Min *temp=H->A[i/2];
				H->A[i/2]=H->A[i];
				H->A[i]=temp;
				i=i/2;
			}
		}
	}
	/*求时间*/
	else
	{
		if(s->about_time>(u->about_time+u->son[2][p]))
		{
			s->f_time=u;
			s->about_time=u->about_time+u->son[2][p];
			while((i>1)&&(H->A[i/2]->about_time>H->A[i]->about_time))
			{
				H->sta[H->A[i/2]->pos]=i;
				H->sta[H->A[i]->pos]=i/2;
				Min *temp=H->A[i/2];
				H->A[i/2]=H->A[i];
				H->A[i]=temp;
				i=i/2;
			}
		}
		else if((s->about_time==(u->about_time+u->son[2][p]))&&(s->about_dis>=(u->about_dis+u->son[1][p])))
		{
			s->f_time =u;
			s->about_time=u->about_time+u->son[2][p];
			while((i>1)&&(H->A[i/2]->about_time>H->A[i]->about_time))
			{
				H->sta[H->A[i/2]->pos]=i;
				H->sta[H->A[i]->pos]=i/2;
				Min *temp=H->A[i/2];
				H->A[i/2]=H->A[i];
				H->A[i]=temp;
				i=i/2;
			}
		}
	}
}
bool IsEmpty(Heap *H)
{
	if(H->heap_size ==0)
		return true;
	return false;
}

4.与优先队列有关的函数

/*与优先队列有关的函数*/
void BuildMinHeap(Heap *H,int opt)
{
	/*求距离*/
	if(opt==1)
	{
		H->heap_size=H->length;
		for(int i=H->length/2;i>=1;i--)
			MaxHeapIfy(H,i,opt);
	}
	/*求时间*/
	else
	{
		H->heap_size=H->length;
		for(int i=H->length/2;i>=1;i--)
			MaxHeapIfy(H,i,opt);
	}
}
void MaxHeapIfy(Heap *H,int i,int opt)
{
	/*求距离*/
	if(opt==1)
	{
		int l=2*i,r=2*i+1,min;
		if((l<=H->heap_size)&&(H->A[l]->about_dis<H->A[i]->about_dis))
			min=l;
		else
			min=i;
		if((r<=H->heap_size )&&(H->A [r]->about_dis<H->A[min]->about_dis))
			min=r;
		if(min!=i)
		{
			H->sta[H->A[min]->pos]=i;
			H->sta[H->A[i]->pos]=min;
			Min *temp=H->A[i];
			H->A[i]=H->A[min];
			H->A[min]=temp;
			MaxHeapIfy(H,min,opt);
		}	
	}
	/*求时间*/
	else
	{
		int l=2*i,r=2*i+1,min;
		if((l<=H->heap_size)&&(H->A[l]->about_time<H->A[i]->about_time))
			min=l;
		else
			min=i;
		if((r<=H->heap_size )&&(H->A [r]->about_time<H->A[min]->about_time))
			min=r;
		if(min!=i)
		{
			H->sta[H->A[min]->pos]=i;
			H->sta[H->A[i]->pos]=min;
			Min *temp=H->A[i];
			H->A[i]=H->A[min];
			H->A[min]=temp;
			MaxHeapIfy(H,min,opt);
		}
	} 
}
void InitializeHeap(Heap **H,int all_pos)
{
	if(*H==NULL)
		*H=(Heap 以上是关于Dijkstra算法介绍及其优先队列优化和斐波那契堆优化的主要内容,如果未能解决你的问题,请参考以下文章

算法先生,您点的查找套餐到了(二分插入和斐波那契查找)

七大查找算法

斐波那契数列的介绍?

介绍下斐波那契数列。

递归2之对青蛙跳台阶和斐波那契数列的思考

常见的查找算法