1. 程式人生 > >《演算法導論》第24章——單源最短路徑

《演算法導論》第24章——單源最短路徑

  雖然寫這個部落格主要目的是為了給我自己做一個思路記憶錄,但是如果你恰好點了進來,那麼先對你說一聲歡迎。我並不是什麼大觸,只是一個菜菜的學生,如果您發現了什麼錯誤或者您對於某些地方有更好的意見,非常歡迎您的斧正!

在這裡插入圖片描述
在這裡插入圖片描述
最短路徑的最優子結構
最短路徑的子路徑也是最短路徑。
負權重的邊
權重為負數的邊。(我的理解:大概知道這個就可以了)
環路
最短路徑既不能包含正的環路,也不能包含負的環路。(正的環路,我覺得正常人想走最短的路,都不會在那裡打圈,除非路就是圓的;負環路你多繞幾圈,最後權重想變多小變多小,就很沒意思。)
最短路徑的表示
每個節點保持一個前驅屬性v.π,指向它的前一個結點。(這樣可以串成一串)
鬆弛操作


說白了就是就是檢查一下繞小路會不會更近。
v.d:表示s到v的最短路徑估計
在這裡插入圖片描述
在這裡插入圖片描述
接下來書中還有一堆性質,我就放圖,我自己也沒看,感覺很每意思,就是感覺把一些簡單的東西說的很複雜,顯得十分高大上。在這裡插入圖片描述

24.1Bellman-Ford演算法(貝爾曼-福特演算法)

在這裡插入圖片描述
在這裡插入圖片描述
我的思路:
這邊為什麼要先對點迴圈,然後再套一個對邊的迴圈鬆弛呢?因為第一次鬆弛的時候,部分地方還不是最短路徑。這邊思路真的不是很好描述:最好的辦法就是自己按照它的流程走一遍!

24.2有向無環圖中的單源最短路徑問題

1、對有向無環圖進行拓撲排序
2、再進行Bellman類似操作

在這裡插入圖片描述
在這裡插入圖片描述在這裡插入圖片描述
這一章我模稜兩可地看了看,emmm不知道它到底要講些什麼,如果你知道的話,請告訴我,謝謝!那麼我就要跳過這部分了。

24.3Dijkstra演算法(迪傑斯特拉演算法)

Dijkstra演算法解決的是帶權重的有向圖上單源最短路徑問題,要求所有邊權重大於0 。
我的感覺:它就是一個改進的Prim演算法。
我的思路:
1、每個點都有一個mark屬性標誌著是否已經在路徑中
2、將邊從小排到大
3、如果點沒有全部加入到路徑中,從小到大遍歷每條邊,如果邊的起點在路徑中,邊的終點沒在路徑中,就把終點的前驅設定為起點,同時終點的mark屬性變為true
4、開始重新遍歷邊
在這裡插入圖片描述
在這裡插入圖片描述24.4差分約束和最短路徑
24.5最短路徑性質的證明
四五兩章都非常的理論,我就不看了,真的很討厭這種看的頭疼又看不懂的東西。

以下就是程式碼部分了(建議貼上到自己的編輯器中執行)

Bellman.h

#pragma once
#define BELLV 5/*點的數量*/
#define BELLE 10/*邊的數量*/

/*點*/
typedef struct BellV
{
	char data;/*資料*/
	int d;/*源結點到該點的最短距離*/
	BellV* π;/*前驅結點*/
}Bellv;
/*邊*/
typedef struct
{
	Bellv* start;/*起點*/
	Bellv* end;/*終點*/
	int w;/*權重*/
}Belle;
/*圖*/
typedef struct
{
	Bellv* v[BELLV];/*點集*/
	Belle* e[BELLE];/*邊集*/
}Bellg;

/*初始化*/
void InitBell(Bellg* &G, Bellv* root);
/*鬆弛操作*/
void BellRelax(Bellv* u, Bellv* v, int w);
/*Bellman演算法*/
bool Bellman(Bellg* &G, Bellv* root);
/*列印路徑*/
void BellPrint(Bellv *root, Bellv *end);
/*測試函式*/
void TestBellman();

Bellman.cpp

#include "Bellman.h"
#include<stdio.h>
#include<stdlib.h>
#include<iostream>
using namespace std;

/*初始化*/
void InitBell(Bellg* &G,Bellv* root)
{
	for (int i = 0; i < BELLV; i++)
	{
		G->v[i]->d = 100;/*我們假裝這個100就是∞*/
		G->v[i]->π = NULL;/*前驅為空*/
	}
	root->d = 0;/*源結點*/
}
/*鬆弛操作*/
void BellRelax(Bellv* u, Bellv* v, int w)
{
	if (v->d > u->d + w)
	{
		v->d = u->d + w;
		v->π = u;
	}
}
/*Bellman演算法*/
bool Bellman(Bellg* &G,Bellv* root)
{
	InitBell(G, root);
	int i, j;

	for (i = 0; i < BELLV; i++)/*對頂點進行遍歷*/
	{
		if (G->v[i] != root)/*如果不是根結點*/
		{
			for (j = 0; j < BELLE; j++)
				BellRelax(G->e[j]->start, G->e[j]->end, G->e[j]->w);
		}
	}

	for (i = 0; i < BELLE; i++)/*檢測是否有負迴路*/
		if (G->e[i]->end->d > G->e[i]->start->d + G->e[i]->w)
			return false;
	return true;
}
/*列印路徑*/
void BellPrint(Bellv *root, Bellv *end)
{
	Bellv* tmp = end;
	if (tmp != root)
	{
		BellPrint(root, tmp->π);
		cout << tmp->data << " ";
	}
}

/*測試函式*/
void TestBellman()
{
	/*圖*/
	Bellg* G = new Bellg();
	/*點集*/
	Bellv* s = new Bellv(); s->data = 's';
	Bellv* t = new Bellv(); t->data = 't';
	Bellv* x = new Bellv(); x->data = 'x';
	Bellv* y = new Bellv(); y->data = 'y';
	Bellv* z = new Bellv(); z->data = 'z';

	G->v[0] = s; G->v[1] = t;
	G->v[2] = x; G->v[3] = y; G->v[4] = z;

	int i;

	for (i = 0; i < BELLE; i++)
		G->e[i] = new Belle();
	G->e[0]->start = s; G->e[0]->end = t; G->e[0]->w = 6;
	G->e[1]->start = s; G->e[1]->end = y; G->e[1]->w = 7;
	G->e[2]->start = t; G->e[2]->end = x; G->e[2]->w = 5;
	G->e[3]->start = t; G->e[3]->end = y; G->e[3]->w = 8;
	G->e[4]->start = t; G->e[4]->end = z; G->e[4]->w = -4;
	G->e[5]->start = x; G->e[5]->end = t; G->e[5]->w = -2;
	G->e[6]->start = y; G->e[6]->end = x; G->e[6]->w = -3;
	G->e[7]->start = y; G->e[7]->end = z; G->e[7]->w = 9;
	G->e[8]->start = z; G->e[8]->end = s; G->e[8]->w = 2;
	G->e[9]->start = z; G->e[9]->end = x; G->e[9]->w = 7;

	Bellv* root = s;

	if (Bellman(G, root))
	{
		for (i = 0; i < BELLV; i++)
		{
			if (i != 0)
			{
				cout << root->data << " ";
				BellPrint(root, G->v[i]);
				cout << endl;
			}
		}
	}
	else
		cout << "Error!";
}

主函式

#include "Bellman.h"
#include <stdio.h>

int main()
{
	TestBellman();
	getchar();
	getchar();
	return 0;
}

執行結果
在這裡插入圖片描述
Dijstra.h

#pragma once
#define DIJV 5/*點的數量*/
#define DIJE 10/*邊的數量*/

/*點*/
typedef struct dijV
{
	char data;/*資料*/
	dijV* π;/*前驅*/
	bool mark;/*標記是否已經加入到最短路徑*/
}dijv;
/*邊*/
typedef struct
{
	dijv* start;/*起點*/
	dijv* end;/*終點*/
	int w;/*權重*/
}dije;
/*圖*/
typedef struct
{
	dijv* v[DIJV];/*點集*/
	dije* e[DIJE];/*邊集*/
}dijg;

/*比較函式*/
bool DijCmp(dije* x, dije* y);
/*Dijkstra演算法*/
void Dijkstra(dijg* &g, dijv* root);
/*列印路徑*/
void DijPrint(dijv *root, dijv *end);
/*測試函式*/
void TestDijkstra();

Dijstra.cpp

#include "Dijstra.h"
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>/*sort排序函式所需標頭檔案*/
#include<iostream>
using namespace std;


/*比較函式*/
bool DijCmp(dije* x, dije* y)
{
	return x->w < y->w;/*從小到大*/
}
/*Dijkstra演算法*/
void Dijkstra(dijg* &g,dijv* root)
{
	int i, j;

	for (i = 0; i < DIJV; i++)
	{
		g->v[i]->mark = false;/*沒有被加入最短路徑中*/
		g->v[i]->π = NULL;
	}
	root->mark = true;
	sort(g->e, g->e + DIJE, DijCmp);/*按權重排序*/

	j = 0;
	while (j < DIJV - 1)/*還有點沒有被選中*/
	{
		for (i = 0; i < DIJE; i++)
		{
			if (g->e[i]->start->mark && !(g->e[i]->end->mark))/*起點在路徑中,終點沒有*/
			{
				g->e[i]->end->mark = true;
				g->e[i]->end->π = g->e[i]->start;
				j++;
				i = 20;/*結束這個for迴圈*/
			}
		}
	}
}
/*列印路徑*/
void DijPrint(dijv *root, dijv *end)
{
	dijv* tmp = end;
	if (tmp != root)
	{
		DijPrint(root, tmp->π);
		cout << tmp->data << " ";
	}
}
/*測試函式*/
void TestDijkstra()
{
	int i;
	dijg* g = new dijg();/*圖*/
	dijv* s = new dijv(); s->data = 's';
	dijv* t = new dijv(); t->data = 't';
	dijv* x = new dijv(); x->data = 'x';
	dijv* y = new dijv(); y->data = 'y';
	dijv* z = new dijv(); z->data = 'z';

	g->v[0] = s; g->v[1] = t;
	g->v[2] = x; g->v[3] = y; g->v[4] = z;

	for (i = 0; i < DIJE; i++)
		g->e[i] = new dije();

	g->e[0]->start = s; g->e[0]->end = t; g->e[0]->w = 10;
	g->e[1]->start = s; g->e[1]->end = y; g->e[1]->w = 5;
	g->e[2]->start = t; g->e[2]->end = x; g->e[2]->w = 1;
	g->e[3]->start = t; g->e[3]->end = y; g->e[3]->w = 2;
	g->e[4]->start = x; g->e[4]->end = z; g->e[4]->w = 4;
	g->e[5]->start = y; g->e[5]->end = t; g->e[5]->w = 3;
	g->e[6]->start = y; g->e[6]->end = x; g->e[6]->w = 9;
	g->e[7]->start = y; g->e[7]->end = z; g->e[7]->w = 2;
	g->e[8]->start = z; g->e[8]->end = s; g->e[8]->w = 7;
	g->e[9]->start = z; g->e[9]->end = x; g->e[9]->w = 6;

	dijv* root = s;
	Dijkstra(g, s);

	for (i = 0; i < DIJV; i++)
	{
		if (i != 0)
		{
			cout << root->data << " ";
			DijPrint(root, g->v[i]);
			cout << endl;
		}
	}
}

主函式

#include "Dijstra.h"
#include <stdio.h>

int main()
{
	TestDijkstra();
	getchar();
	getchar();
	return 0;
}

執行結果
在這裡插入圖片描述