个人整理,原创发布,转载请标注博客来源。https://editor.csdn.net/md/?articleId=102799813
很多时候,总会忽略了一些你认为不需要的知识体系,但最终你发现,你又要花大量的时间去弥补这个空缺。
狄克斯特拉算法,用于计算出在非负权重的情况下,图中起点到终点的最短路径......
案例一
图示(MarkDown画的图)
首先列出起点到各相连节点的耗费时间
父节点 | 节点 | 耗时 |
---|---|---|
起点 | A | 6分钟 |
起点 | B | 2分钟 |
… | 终点 | ∞ 无穷大 |
获取耗时最短的节点,由上表可以看出是起点->B节点花费时间最少,计算节点B前往各个邻居节点的所需时间,并更新原本需要花费更多的时间的节点:
父节点 | 节点 | 耗时 |
---|---|---|
B | A | 5分钟 (更新耗时) |
起点 | B | 2分钟 |
B | 终点 | 7 (更新耗时) |
此时,B节点添加进已处理的列表中,不再进行处理,现在只剩余A节点到各邻居节点的所需要的耗时,并更新原本需要花费更多的时间的节点。
父节点 | 节点 | 耗时 |
---|---|---|
B | A | 5分钟 |
起点 | B | 2分钟 |
A | 终点 | 6 (更新耗时) |
有表格可以看出,最终路径是起点-> B -> A -> 终点,耗时6分钟。
/*
* Dijkstra.cpp
*
* Created on: Oct 21, 2019
* Author: kjh
*/
#include
#include
#include
#include
#include
#include
using std::map;
using std::string;
using std::set;
class Dijkstra {
public:
typedef std::map<std::string, int> KeyValueNode;
typedef std::map<std::string, KeyValueNode> GraphMap;
void init();
void process(const std::string& startKey, const std::string& target);
private:
std::string getMinCost(KeyValueNode& keyVals);
void process_node(const std::string& key, KeyValueNode& keyVals);
void print(const std::string& target);
private:
// for graph.
GraphMap _map;
// for visited node
std::set<string> _visited_set;
// for parents relation
std::map<string, string> _node_parents;
// 存放到各节点需要的最小时间
KeyValueNode _cost_path;
};
/*******************************
* start --> A (6)
* --> B (2)
* B --> A (3)
* --> fin(5)
* A --> fin (1)
******************************/
void Dijkstra::init() {
_map.clear();
_visited_set.clear();
_cost_path.clear();
_node_parents.clear();
// init graph
KeyValueNode start = {
{
"A", 6}, {
"B", 2}};
KeyValueNode b = {
{
"A", 3}, {
"FIN", 5}};
KeyValueNode a = {
{
"FIN", 1}};
_map = {
{
"START", start}, {
"B", b}, {
"A", a}};
}
std::string Dijkstra::getMinCost(KeyValueNode& keyVals) {
int min_cost = std::numeric_limits<int>::max();
string min_key;
for (auto& it : keyVals) {
if (it.second < min_cost && _visited_set.count(it.first) != 1) {
min_cost = it.second;
min_key = it.first;
}
}
return min_key;
}
void Dijkstra::process(const std::string& startKey, const std::string& target) {
// find start key.
auto iter = _map.find(startKey);
if (iter == _map.end()) {
return ;
}
// 处理起点的邻居节点,讲邻居节点的耗时加入cost_path.
for (auto & node :iter->second) {
int cost = node.second;
_cost_path.insert(node);
_node_parents.insert(std::make_pair(node.first, startKey));
}
// 查找cost最小的节点
std::string mini_key = getMinCost(_cost_path);
while(!mini_key.empty()) {
std::cout << "process node: " << mini_key << std::endl;
auto it = _map.find(mini_key);
if (it == _map.end()) {
break;
}
// 处理cost最小节点邻居节点,并更新cost_path
process_node(mini_key, it->second);
_visited_set.insert(mini_key);
mini_key = getMinCost(_cost_path);
}
// 输出结果
print(target);
}
void Dijkstra::process_node(const std::string& key, KeyValueNode& keyVals) {
auto it = _cost_path.find(key);
if (it == _cost_path.end()) {
return;
}
int has_cost = it->second;
for (auto& node : keyVals) {
int cost = node.second;
cost = has_cost + cost;
auto cit = _cost_path.find(node.first);
if (cit == _cost_path.end()) {
_cost_path.insert(std::make_pair(node.first, cost));
_node_parents.insert(std::make_pair(node.first, key));
} else {
int old_cost = cit->second;
if (cost < old_cost) {
_cost_path.erase(cit);
_cost_path.insert(std::make_pair(node.first, cost));
_node_parents.erase(node.first);
_node_parents.insert(std::make_pair(node.first, key));
}
}
}
}
void Dijkstra::print(const std::string& target) {
auto citer = _node_parents.find(target);
if (citer != _node_parents.end()) {
std::vector<std::string> path;
std::string parents = citer->second;
path.push_back(target);
while(!parents.empty()) {
path.push_back(parents);
auto piter = _node_parents.find(parents);
if (piter != _node_parents.end()) {
parents = piter->second;
} else {
parents = "";
}
}
int size = path.size();
std::cout << "Path : ";
for (int i = size -1 ; i >= 0; i--) {
std::cout << path[i];
if (i > 0) {
std::cout << " --> ";
}
}
auto cost_iter = _cost_path.find(target);
if (cost_iter != _cost_path.end()) {
std::cout << " cost: " << cost_iter->second;
}
std::cout << std::endl;
} else {
std::cout << "Does not exists to " << target << " path" << std::endl;
}
}
int main() {
Dijkstra test;
test.init();
test.process("A", "H");
return 0;
}
// g++ Dijkstra.cpp -std=c++11
案例二
案例一中选择了最简单的图,案例二将图复杂一步,来说明算法的思想
按照案例一中的步骤,进行分析:
父节点 | 节点 | 耗时 |
---|
A | B | 5
A | C | 1
A | H | +∝
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 |
A | C | 1 | C已处理
A | H | +∝ |
C | D | 6 |
C | F | 7 |
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 | B已处理
A | C | 1 | C已处理
A | H | +∝ |
C | D | 6 |
C | F | 7 |
B | E | 15 |
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 | B已处理
A | C | 1 | C已处理
A | H | +∝ |
C | D | 6 | D已处理
C | F | 7 |
D | E | 9 |
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 | B已处理
A | C | 1 | C已处理
E | H | +∝ |
C | D | 6 | D已处理
C | F | 7 | F已处理
D | E | 9 |
F | G | 9 |
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 | B已处理
A | C | 1 | C已处理
E | H | 12 |
C | D | 6 | D已处理
C | F | 7 | F已处理
D | E | 9 | E已处理
F | G | 9 |
父节点 | 节点 | 耗时 | 标志 |
---|
A | B | 5 | B已处理
A | C | 1 | C已处理
E | H | 12 |
C | D | 6 | D已处理
C | F | 7 | F已处理
D | E | 9 | E已处理
F | G | 9 | G已处理
经过上述的过程推敲,可以看出,最后一步处理G,其用时没有比之前更小了,固不更新列表值。
有次的出最小权重:12, 路径:A–>C–>D–>E–>H
// 替换案例一中初始化函数,生成新的图
void Dijkstra::init() {
_map.clear();
_visited_set.clear();
_cost_path.clear();
_node_parents.clear();
// init graph
KeyValueNode a = {
{
"B", 5}, {
"C", 1}};
KeyValueNode b = {
{
"E", 10}};
KeyValueNode c = {
{
"D", 5}, {
"F", 6}};
KeyValueNode d = {
{
"E", 3}};
KeyValueNode e = {
{
"H", 3}};
KeyValueNode f = {
{
"G", 2}};
KeyValueNode g = {
{
"H", 10}};
_map = {
{
"A", a}, {
"B", b}, {
"C", c}, {
"D", d}, {
"E", e}, {
"F", f}, {
"G", g}};
}