DAG(有向无环图)、Dijkstra算法和Bellman-Ford算法都是图论中的重要概念和算法。它们各自有不同的原理、应用场景和特点。以下是它们之间的异同和应用。
1. DAG(有向无环图)
原理:
- DAG是一种有向图,其中不存在从某个顶点出发又能回到该顶点的路径(即没有环路)。
- DAG的一个重要特性是可以进行拓扑排序。
应用:
- 任务调度:表示任务之间的依赖关系。
- 数据处理流程:如编译器中的表达式树。
- 确定性模型:如版本控制系统中的提交历史。
2. Dijkstra算法
原理:
- Dijkstra算法用于计算从单个源点到所有其他顶点的最短路径。
- 适用于边权非负的图。
- 算法使用贪心策略,逐步扩展已知的最短路径。
应用:
- 地图导航系统:寻找最短路径。
- 网络路由协议:如OSPF(开放最短路径优先)。
- 交通优化:实时交通流量分析。
- Dijkstra算法求最短路径:
- 测试用例:
- 预期结果:
核心代码如下:
void Graph::dijkstra(int startId)
{//先把它从队列中删除,然后找到它的邻居顶点并更新节点的值直到队列为空
Pritree<Vertex> pritree;
initialForDij(pritree, startId);
while(pritree.getSize() != 0)
{
Vertex temp = pritree.popHead();//找到discovery最小的值
Vertex* vertexp = findVerAccId(temp.getVertexId());
Node* neighbor = vertexp->getHeadNode();//找到邻居节点
// cout<<temp.getVertexId()<<"-> ";
while(neighbor != NULL)//对每一个邻居节点进行循环中的操作
{
Vertex* tempNe = neighbor->getVertex();//与弹出来的顶点相邻的顶点
if(tempNe->getDiscovery() > temp.getDiscovery() + neighbor->getWeight())
{
tempNe->setDiscovery(temp.getDiscovery() + neighbor->getWeight());
tempNe->setParent(vertexp);//记住其父节点
pritree.delete_ele(*tempNe, equal);//从优先队列中删除
pritree.insert(*tempNe);//然后插入新的更新后的顶点
}
neighbor = neighbor->getNextNode();
}
}
//打印出最后的结果
Vertex* for_out_ver = this->headVertex;
cout<<"dijkstra:"<<endl;
while(for_out_ver != NULL)
{
for_out_ver = for_out_ver->getNextVertex();
if(for_out_ver != NULL)
cout<<(for_out_ver->getParent())->getVertexId()<<"->"<<for_out_ver->getVertexId()<<endl;
}
}
3. Bellman-Ford算法
原理:
- Bellman-Ford算法也用于计算从单个源点到所有其他顶点的最短路径。
- 可以处理边权为负的图,并能检测负权环。
- 通过动态规划的方式,逐步更新路径长度。
应用:
- 适用于存在负权边的图,如金融网络中的套利检测。
- 网络路由协议:如RIP(路由信息协议)。
- 解决最短路径问题的更一般化场景。
- Bellman-Ford算法求最短路径:
- 测试用例:
- 预期结果:
核心代码如下:
void Graph::bellmanFord(int startId)
{
//初始化
Vertex* vptr = this->headVertex;
while(vptr != NULL)
{
if(vptr->getVertexId() == startId)
vptr->setDiscovery(0);
else
vptr->setDiscovery(INT_MAX);
vptr->setParent(NULL);
vptr = vptr->getNextVertex();
}
//最外层迭代顶点数目减一次
for(int counter0 = 1; counter0 < this->vertexNumber; counter0++)
{
vptr = this->headVertex;
while(vptr != NULL)//遍历每一个顶点
{
Node* eptr = vptr->getHeadNode();
while(eptr != NULL)//遍历每一条边
{
Vertex* vtoptr = eptr->getVertex();
if( vtoptr->getDiscovery()> vptr->getDiscovery()+eptr->getWeight())
{
vtoptr->setDiscovery(vptr->getDiscovery()+eptr->getWeight());
vtoptr->setParent(vptr);
}
eptr = eptr->getNextNode();
}
vptr = vptr->getNextVertex();
}
}
Vertex* for_out_ver = this->headVertex;
cout<<"bellman-ford:"<<endl;
while(for_out_ver != NULL)
{
for_out_ver = for_out_ver->getNextVertex();
if(for_out_ver != NULL)
cout<<(for_out_ver->getParent())->getVertexId()<<"->"<<for_out_ver->getVertexId()<<endl;
}
}
4.异同点总结
特性 | DAG | Dijkstra算法 | Bellman-Ford算法 |
---|---|---|---|
图的类型 | 有向无环图 | 有向图(非负边权) | 有向图(可包含负边权) |
算法性质 | 拓扑排序 | 贪心算法 | 动态规划 |
负权边处理 | 不适用 | 不适用 | 支持 |
适用场景 | 任务调度、数据流程 | 最短路径查找 | 负权检测、一般最短路径查找 |
总结
- DAG用于表示无环的依赖关系,适合任务调度等场景。
- Dijkstra算法适合处理边权非负的最短路径问题,常用于实时导航。
- Bellman-Ford算法则适合处理更复杂的图形,尤其是需要处理负权边的情况,能够检测负权环