单源最短路径dijkstra算法

介绍

    dijkstra算法也是解决单源最短路径问题的一种方法。它要求图中路径都是非负的。而且,它的效率要高于bellman算法。

实现

    首先定义图的结点信息和边信息。

struct _Node;

typedef struct _Vertex
{
	int distance;
	int vertex;
	struct _Node *parent;
}Vertex;

typedef struct _Node
{
	Vertex *pVertex;
	struct _Node *next;
}Node;

typedef struct Edge_
{
	int x;
	int y;
	bool operator == (const struct Edge_& l)
	{
		return x == l.x && y == l.y;
	}
}Edge;

bool operator < (const Edge& r, const Edge& l)
{
	if (r.x == l.x)
	{
		return r.y < l.y;
	}
	return r.x < l.x;
}


map<Edge, int> g_edges;

    这是图中的基本信息定义,图的信息初始化如下:

Node* initNode(Node **graph, int vertex)
{
	Node *p = new Node;
	p->pVertex = graph[vertex - 1]->pVertex;
	p->next = nullptr;

	return p;
}

void InsertVertex(Node **graph, Node *node, int vertex, int weight)
{
	Node *p = node;
	while (nullptr != p->next)
	{
		p = p->next;
	}
	p->next = initNode(graph, vertex);
	Edge edge;
	edge.x = node->pVertex->vertex;
	edge.y = vertex;
	g_edges.insert(std::pair<Edge, int>(edge, weight));
}

int getSentinel()
{
	return numeric_limits<int>::max();
}

void initGraph(Node **list, int vertexCount)
{
	for (int i = 0; i < vertexCount; ++ i)
	{
		list[i] = new Node;
		list[i]->next = nullptr;
		list[i]->pVertex = new Vertex;
		list[i]->pVertex->parent = nullptr;
		list[i]->pVertex->vertex = i + 1;
		list[i]->pVertex->distance = 0;
	}
}

    权重提取:

int w(int x, int y)
{
	Edge edge;
	edge.x = x;
	edge.y = y;
	return g_edges[edge];
}

    最小优先级队列

inline int parent(int node)
{
	return node >> 1;
}

inline int left(int node)
{
	return node << 1;
}

inline int right(int node)
{
	return (node << 1) + 1;
}

void minHeapify(Node **t, int heapLength, int node)
{
	/// 求出左子叶及右子叶
	int l = left(node);
	int r = right(node);
	int smallest = node;

	/// 然后把左子叶,右子叶与父节点比较,求出最小节点
	if (l < heapLength && t[l]->pVertex->distance < t[smallest]->pVertex->distance)
	{
		smallest = l;
	}
	if (r < heapLength && t[r]->pVertex->distance < t[smallest]->pVertex->distance)
	{
		smallest = r;
	}
	/// 交换最小节点,继续求下一节点
	if (node != smallest)
	{
		swap(t[node], t[smallest]);
		minHeapify(t, heapLength, smallest);
	}
}

void buildMinHeap(Node **t, int length)
{
	int heapLength = length;
	/// 从底向上把数组变成堆 从length/2开始,因为length/2+1到length是是叶子节点,不需要进行操作
	for (int i = length / 2; i >= 0; -- i)
	{
		minHeapify(t, heapLength, i);
	}
}

Node* heapExtractMin(Node **t, int heapLength)
{
	if (heapLength < 1)
	{
		throw std::exception("heap underflow");
	}
	/// 把最小值和末尾值进行交换,并重新建立最小堆
	Node *min= t[0];
	swap(t[0], t[heapLength - 1]);
	//t[0] = t[heapLength - 1];
	-- heapLength;
	minHeapify(t, heapLength, 0);

	return min;
}

int search(Node **t, int length, Node *node)
{
	for (int i = 0; i < length; ++ i)
	{
		if (node->pVertex->vertex == t[i]->pVertex->vertex)
		{
			return i;
		}
	}

	return -1;
}

void heapDecreaseKey(Node **t, int node, int key)
{
	if (key > t[node]->pVertex->distance)
	{
		throw std::exception("new key is bigger than current key");
	}
	t[node]->pVertex->distance = key;
	/// 遍历父节点,直到遇到不小于父节点为止
	while(node > 0 && t[parent(node)]->pVertex->distance > t[node]->pVertex->distance)
	{
		swap(t[node], t[parent(node)]);
		node = parent(node);
	}
}

void heapDecreaseKey(Node **t, int length, Node *node)
{
	int index = search(t, length, node);
	if (-1 == index)
	{
		return;
	}else
	{
		heapDecreaseKey(t, index, node->pVertex->distance);
	}
}

void minHeapInsert(Node **t, int length, Node *node)
{
	int heapLength = length;
	++ heapLength;
	t[heapLength - 1] = node;
	int key = node->pVertex->distance;
	/// 使用哨兵作为特殊值,任何值都比他大,然后调用增加某个值的函数
	t[heapLength - 1]->pVertex->distance = numeric_limits<int>::max();
	heapDecreaseKey(t, heapLength - 1, key);
}

    dijkstra算法主体:

void initializeSingleSource(Node **graph, Node *root)
{
	for (int i = 0; i < VERTEX_NUMBER; ++ i)
	{
		graph[i]->pVertex->distance = getSentinel();
		graph[i]->pVertex->parent = nullptr;
	}
	root->pVertex->distance = 0;
}

void relax(Node *u, Node *v, int weight)
{
	if (v->pVertex->distance > u->pVertex->distance + weight)
	{
		v->pVertex->distance = u->pVertex->distance + weight;
		v->pVertex->parent = u;
	}
}

void dijkstra()
{
	Node *graph[VERTEX_NUMBER] = {0};
	initGraph(graph, VERTEX_NUMBER);

	InsertVertex(graph, graph[0], 2, 10);
	InsertVertex(graph, graph[0], 4, 5);
	InsertVertex(graph, graph[1], 3, 1);
	InsertVertex(graph, graph[1], 4, 2);
	InsertVertex(graph, graph[2], 5, 4);
	InsertVertex(graph, graph[3], 2, 3);
	InsertVertex(graph, graph[3], 3, 9);
	InsertVertex(graph, graph[3], 5, 2);
	InsertVertex(graph, graph[4], 1, 7);
	InsertVertex(graph, graph[4], 3, 6);

	initializeSingleSource(graph, graph[0]);
	buildMinHeap(graph, VERTEX_NUMBER);
	int queueLength = VERTEX_NUMBER;
	while(0 != queueLength)
	{
		Node *node = heapExtractMin(graph, queueLength);
		-- queueLength;
		cout << node->pVertex->vertex << " ";

		Node *p = node->next;
		while(nullptr != p)
		{
			int weight = w(node->pVertex->vertex, p->pVertex->vertex);
			if (p->pVertex->distance > node->pVertex->distance
				&& p->pVertex->distance > node->pVertex->distance + weight)
			{
				p->pVertex->distance = node->pVertex->distance + weight;
				p->pVertex->parent = node;
				heapDecreaseKey(graph, queueLength, p);
			}
			p = p->next;
		}
	}

	for (int i = 0; i < VERTEX_NUMBER; ++ i)
	{
		if (nullptr != graph[i]->pVertex->parent)
		{
			cout << "edge:" << graph[i]->pVertex->parent->pVertex->vertex
				<< "," << graph[i]->pVertex->vertex << endl;
		}
	}
}

    首先,初始化图各个结点的距离为无穷大以及父结点为空并选任意结点初始化距离为0,然后全部加入到最小优先级队列当中去。接着循环从最小优先级队列中提取出最小值,并计算它与它的邻接表结点的距离,如果它们之间的权重小于当前结点保存的距离值,则重置当前结点的distance值,并重置父结点。直到算法结束时,单源最短路径便生成了。

int _tmain(int argc, _TCHAR* argv[])
{
	copyright();

	cout << "dijkstra algorithm: " << endl;

	dijkstra();


	return 0;
}

    可以看下运算结果:

dijkstra算法代码运行结果

dijkstra算法代码运行结果

    dijkstra算法图解过程如下:

dijkstra算法过程图解

dijkstra算法过程图解

    从上面可以看出,dijkstra算法和prim算法很相似。都是使用最小优先级队列来计算它们之间的最小权重。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z小偉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值