简述DPT
上一节我们说了:要看一下PFS的更多应用,所以呢,这一节,我们就以优先级优先搜索(PFS)为基础,探讨一下图3大应用:
- 无向图:Dijkstra最优路径规划算法/Prime最小生成树算法;
- 有向图:Topology 拓扑排序
本节需一些DFS和PFS的基础知识~ 如果不知道相关原理的同学,对上面的话看不懂,可以看看上一节的内容从优先级看图的搜索
好了,话不多说,我们直接进入正题!
D-Dijkstra最优路径规划
Dijkstra最优路径规划,带权无向图的一种应用,算法可以得到从某点出发到图中任意节点的最短距离!
算法以PFS为基础进行实现! 因为对非连通图进行路径规划无意义,我们就不考虑非联通图的状况啦~
算法步骤如下:
- 1:访问当前节点
- 2:更新所有节点的距离(求和取小),并将距离作为优先级,值越小优先级越高
- 3:需找优先级最小的元素,进行访问标记
- 4:当遇到优先级最小的元素,也被访问,则说明所有节点访问完毕
template <typename T>
void Grap<T>::DUP(int s,int w)
{
V[0].priority=0;
if(G[s][w]==0)
G[s][w]=PMAX;//优先级置最大值
else if(V[w].priority>V[s].priority+G[s][w])
{
this->V[w].priority=V[s].priority+G[s][w];//更新优先级,到最小值
}
}
//单源最短路径问题
template <typename T>
void Grap<T>::dijkstra(int v)
{
int minind=0;//帮助最后越界的判断
int minpri=PMAX;
this->visited[v]=true;
(this->V[v]).vprint();
while(true)
{
minpri=PMAX;//复位最大值
for(int i1=nextV(v,0);i1<this->n;i1=nextV(v,i1+1))//更新所有邻接点的优先级
{
if(!this->visited[i1])
this->DUP(v,i1);//更新优先级
}
for(int i=0;i<this->n;i++)
{
if((!this->visited[i])&&(minpri>this->V[i].priority))// 1-10 值越小,优先级越高
{
minind=i;
minpri=this->V[i].priority;
}
}
if(this->visited[minind])
break;
else
{
this->visited[minind]=true;
(this->V[minind]).vprint();
v=minind;
}
}
}
P-Prime最小生成树
Prime算法主要用于生成最小生成树,算法可以找到连接图中所有点的最小路径!算法每次都遵照当前最优
的原则,属于贪心算法的一种!
当然,算法要应用于无向图,而且要求是连通性吆~
算法的程序结构与Dijkstra类似:从某一点出发,按照优先级顺序依次遍历途中各点! 与Dijkstra的区别就在于优先级更新算法啦!
算法流程如下:
- 1:访问当前节点
- 2:更新所有节点的距离(取小值),并将距离作为优先级,值越小优先级越高
- 3:查找优先级最小的元素,进行访问标记
- 4:当遇到优先级最小的元素,也已经被访问,则说明所有节点访问完毕
template <typename T>
void Grap<T>::PUP(int s,int w)
{
if(G[s][w]==0)
G[s][w]=PMAX;//优先级置最大值
else if(V[w].priority>G[s][w])
{
this->V[w].priority=G[s][w];//更新优先级,到最小值
}
}
//prime 最小生成树 (不考虑非联通图)
template <typename T>
void Grap<T>::prime(int v)
{
int minind=0;
int minpri=PMAX;
this->visited[v]=true;
(this->V[v]).vprint();
while(true)
{
minpri=PMAX;
for(int i1=nextV(v,0);i1<this->n;i1=nextV(v,i1+1))//更新所有邻接点的优先级
{
if(!this->visited[i1])
this->PUP(v,i1);
}
for(int i=0;i<this->n;i++)//需找最高优先级
{
if((!this->visited[i])&&(minpri>this->V[i].priority))// 1-10 值越小,优先级越高
{
minind=i;
minpri=this->V[i].priority;
}
}
if(this->visited[minind])
break;
else
{
this->visited[minind]=true;
(this->V[minind]).vprint();
v=minind;
}
}
}
T-Topology 拓扑排序
拓扑排序常用来判断一个有向图是否有环,也可用于寻找一些多对多关键点的先后顺序! 我们就从入度为0、出度为0两个角度来进行有向无环图的拓扑排序!
基于入度为0的拓扑排序
算法基于:能被拓扑排序的图一定存在入度为0 的点! 这是按照拓扑排序的定义,也是最常用的拓扑排序方法,一起看看步骤:
- 1:找到图中入度为0的点,入队
- 2:出队元素,然后去掉所有从该点出发的边,若入度减为0则继续入队;
- 3:循环2,直到队为空,循环结束,并判断总的访问元素与图中节点的大小是否相等!
使用队列存储排序的结果
代码代码:
template <typename T>
void Grap<T>::TsortQueue()
{
queue<int>q;
int temp;
int sum;
//init入度
for(int i=0;i<this->n;i++)
{
sum = 0;
this->visited[i]=false;
for(int j=0;j<this->n;j++)
{
if(this->G[j][i]>0)//出度
sum++;
}
this->V[i].priority=sum;
if(sum == 0)
{
q.push(i);
}
}
while(!q.empty())
{
temp=q.front();
V[temp].vprint();
q.pop();
for(int i=0;i<=this->n;i++)//遍历从temp出发的每一条边,入度--
{
if(G[temp][i]>0)
{
V[i].priority--;
if(V[i].priority==0)
q.push(i);
}
}
}
}
基于出度为0的DFS拓扑排序
算法基于:能被拓扑排序的图一定存在出度为0 的点!你可以证明一下哈哈~ 也用到了递归的一点属性:DFS函数返回时,节点的出度一定为0!
桥豆麻袋~
按照这个逻辑,是逆拓扑排序定义而为,所以得到的结果也不是真正拓扑排序的结果! 我们就需要一个栈结构,来把排序结果进行逆序输出~
步骤如下:
- 1:初始化图中各点的出度;
- 2:循环图中节点,从某点出发进行DFS;
- 3:在DFS函数尾部对节点入栈;
- 4:循环完成判断栈大小与图中节点个数的关系
注意:(排序是可以提前结束的!)
与DFS不同的是,节点有3种状态:未访问:0 已访问并入栈:2 已访问但未入栈:1 当DFS过程遇到状态为1的节点说明,遇到了前面调用过的点,即遇到回路!!!!!排序结束!!!
template <typename T>
void Grap<T>::TsortDFS(int v,stack<vertex<T> > * st)
{
this->Tvisited[v]=1;//路径状态
for(int num=nextV(v,0);num<this->n;num=nextV(v,num+1))
{
if(this->Tvisited[num]==0)
{
TsortDFS(num,st);
}
else if(this->Tvisited[num]==1)
{
cout<<"有环!!!!"<<endl;//有回路
return;
}
}
st->push(this->V[v]);
this->Tvisited[v]=2;//入栈状态
}
// 用优先级保存节点的入度,入度为0则进行输出
template <typename T>
void Grap<T>::Tsort(int s)
{
stack<vertex<T> > st;
int sum;
//init出度
for(int i=0;i<this->n;i++)
{
sum=0;
this->Tvisited[i]=0;
for(int j=0;j<this->n;j++)
{
if(this->G[i][j]>0)//出度
sum++;
}
this->V[i].priority=sum;
}
for(int j=0;j<this->n;j++)
{
if(this->Tvisited[j]==0)
TsortDFS(j,&st);
}
cout<<st.size()<<endl;
if(st.size() == this->n)
{
cout<<"拓扑排序成功: "<<endl;
while(!st.empty())
{
st.top().vprint();
st.pop();
}
}
else
{
cout<<"拓扑排序失败!"<<endl;
}
}
总结
接着上一节,我们看了D/P/T 3种图算法的实现,其中前两种均基于PFS实现,只是在更新优先级有所不同!
Topo排序使用基于DFS和Queue两种方式,希望以后能够灵活运用!
最后总结一下吧:
这2节基本覆盖了数据结构-图的基本知识,图的复习也就先到这吧, 我的想法肯定有很多不足,各位看官可以在下面多多提意见哈!(#^_^#)
上一节 从优先级看图的搜索