字符串型图中的 Dijkstra 算法
Dijkstra's Algorithm in String-type Graph
我正在制作一个城际路线规划程序,其中形成的图形具有字符串类型的节点(例如 LHR、ISB、DXB)。它是无向但有权重的,初始化为:
map<pair<string, string>, int> city;
然后我可以添加边,例如:
Graph g;
g.addEdge("DXB", "LHR", 305);
g.addEdge("HTR", "LHR", 267);
g.addEdge("HTR", "ISB", 543);
结果输出将是:
ISB LHR
DXB 0 305
HTR 543 267
现在,问题是...我正在尝试在此图中实现 Dijkstra 算法,但到目前为止无法在字符串类型节点上正确 运行 它并且反对在int 类型的节点。有人可以指导我以最有效的方式完成实施的正确步骤吗?
核心问题是,当我们处理具有整数顶点的图时,邻接表的索引代表节点(因为索引也是数字)。现在我们可以使用 map<string,vector<string, int> > adj
而不是像 vector<pair<int, int> > adj[N]
这样的邻接表。现在 adj["DXB"]
将包含一个形式为 <string, int>
的向量对,它是连接到 "DXB"
.
的城市的 <name, weight>
如果这种方法看起来很复杂,那么您可以使用一些额外的内存将一个城市映射到一个数字,然后考虑到该图具有整数顶点,您可以对所有内容进行编码。
图形应用程序使用的数据结构对编码的效率和易用性有很大影响。
许多设计都是从节点开始的。我想在被建模的问题中,节点通常具有物理现实,而 link 可以是抽象关系。所以开始写一个节点class比较自然,后面再补上link
然而,在编写解决图论问题的算法时,很明显 link 才是真正的重点。所以,让我们从 link class.
开始
class cLink
{
public:
cLink(int c = 1)
: myCost(c)
{
}
int myCost; // a constraint e.g. distance of a road, max xapacity of a pipe
int myValue; // a calculated value, e.g. actual flow through a pipe
};
如果我们将节点的外边存储在以目标节点索引为键的映射中,则该映射将是源节点的属性。
class cNode
{
public:
cNode(const std::string &name = "???")
: myName(name)
{
}
std::string myName;
std::map<int, cLink> myLink; // out edges of node, keyed by destination
};
我们有link个节点,所以我们准备构建一个class来存储图
class cGraph {
public:
std::map<int, cNode> myG; // the graph, keyed by internal node index
};
节点索引从何而来?人类的计算能力很差,所以最好计算机在添加节点时生成索引。
cGraph::createNode( const std::string& name )
{
int n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
不要执行这个!它有一个障碍——它可以创建两个同名的节点。我们需要能够检查具有指定名称的节点是否存在。
int cGraph::find(const std::string &name)
{
for (auto n : myG)
{
if (n.second.myName == name)
{
return n.first;
}
}
return -1;
}
这是低效的。但是,只需要在添加节点时执行一次。然后搜索图的算法可以使用索引号快速查找节点。
现在我们可以防止创建两个具有相同名称的节点
int cGraph::findoradd(const std::string &name)
{
// search among the existing nodes
int n = find(name);
if (n < 0)
{
// node does not exist, create a new one
// with a new index and add it to the graph
n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
return n;
}
人类除了是可怕的计数器之外,还对自己的计数能力过于自信。当他们指定这样的图表时
1 -> 2
1 -> 3
大家不要上当,我们把这些数字当成名字,继续使用我们自己的节点索引系统。
/** Add costed link between two nodes
*
* If the nodes do not exist, they will be added.
*
*/
void addLink(
const std::string & srcname,
const std::string & dstname,
double cost = 1)
{
int u = findoradd(srcname);
int v = findoradd(dstname);
myG.find(u)->second.myLink.insert(
std::make_pair(v, cLink(cost)));
if (!myfDirected)
myG.find(v)->second.myLink.insert(
std::make_pair(u, cLink(cost)));
}
添加了一些 getter 和 setter 之后,我们就可以开始实现图形算法了!
要查看使用这些想法的完整实现,包括 Dijsktra,请查看 PathFinder。
我正在制作一个城际路线规划程序,其中形成的图形具有字符串类型的节点(例如 LHR、ISB、DXB)。它是无向但有权重的,初始化为:
map<pair<string, string>, int> city;
然后我可以添加边,例如:
Graph g;
g.addEdge("DXB", "LHR", 305);
g.addEdge("HTR", "LHR", 267);
g.addEdge("HTR", "ISB", 543);
结果输出将是:
ISB LHR
DXB 0 305
HTR 543 267
现在,问题是...我正在尝试在此图中实现 Dijkstra 算法,但到目前为止无法在字符串类型节点上正确 运行 它并且反对在int 类型的节点。有人可以指导我以最有效的方式完成实施的正确步骤吗?
核心问题是,当我们处理具有整数顶点的图时,邻接表的索引代表节点(因为索引也是数字)。现在我们可以使用 map<string,vector<string, int> > adj
而不是像 vector<pair<int, int> > adj[N]
这样的邻接表。现在 adj["DXB"]
将包含一个形式为 <string, int>
的向量对,它是连接到 "DXB"
.
<name, weight>
如果这种方法看起来很复杂,那么您可以使用一些额外的内存将一个城市映射到一个数字,然后考虑到该图具有整数顶点,您可以对所有内容进行编码。
图形应用程序使用的数据结构对编码的效率和易用性有很大影响。
许多设计都是从节点开始的。我想在被建模的问题中,节点通常具有物理现实,而 link 可以是抽象关系。所以开始写一个节点class比较自然,后面再补上link
然而,在编写解决图论问题的算法时,很明显 link 才是真正的重点。所以,让我们从 link class.
开始class cLink
{
public:
cLink(int c = 1)
: myCost(c)
{
}
int myCost; // a constraint e.g. distance of a road, max xapacity of a pipe
int myValue; // a calculated value, e.g. actual flow through a pipe
};
如果我们将节点的外边存储在以目标节点索引为键的映射中,则该映射将是源节点的属性。
class cNode
{
public:
cNode(const std::string &name = "???")
: myName(name)
{
}
std::string myName;
std::map<int, cLink> myLink; // out edges of node, keyed by destination
};
我们有link个节点,所以我们准备构建一个class来存储图
class cGraph {
public:
std::map<int, cNode> myG; // the graph, keyed by internal node index
};
节点索引从何而来?人类的计算能力很差,所以最好计算机在添加节点时生成索引。
cGraph::createNode( const std::string& name )
{
int n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
不要执行这个!它有一个障碍——它可以创建两个同名的节点。我们需要能够检查具有指定名称的节点是否存在。
int cGraph::find(const std::string &name)
{
for (auto n : myG)
{
if (n.second.myName == name)
{
return n.first;
}
}
return -1;
}
这是低效的。但是,只需要在添加节点时执行一次。然后搜索图的算法可以使用索引号快速查找节点。
现在我们可以防止创建两个具有相同名称的节点
int cGraph::findoradd(const std::string &name)
{
// search among the existing nodes
int n = find(name);
if (n < 0)
{
// node does not exist, create a new one
// with a new index and add it to the graph
n = myG.size();
myG.insert(std::make_pair(n, cNode(name)));
}
return n;
}
人类除了是可怕的计数器之外,还对自己的计数能力过于自信。当他们指定这样的图表时
1 -> 2
1 -> 3
大家不要上当,我们把这些数字当成名字,继续使用我们自己的节点索引系统。
/** Add costed link between two nodes
*
* If the nodes do not exist, they will be added.
*
*/
void addLink(
const std::string & srcname,
const std::string & dstname,
double cost = 1)
{
int u = findoradd(srcname);
int v = findoradd(dstname);
myG.find(u)->second.myLink.insert(
std::make_pair(v, cLink(cost)));
if (!myfDirected)
myG.find(v)->second.myLink.insert(
std::make_pair(u, cLink(cost)));
}
添加了一些 getter 和 setter 之后,我们就可以开始实现图形算法了!
要查看使用这些想法的完整实现,包括 Dijsktra,请查看 PathFinder。