“贪婪法则”:
- 深搜
- 最短路径(迪杰斯特拉算法)
- 最小生成树(克鲁斯卡算法)
1.深搜:
void DFS(AdjMat g,int v)
{
int i=0;
visit[v]=1;
cout<<v<<" ";
for(;i<g.n;i++)
{
if(g.arc[v][i]<Max && !visit[i])//v能够到达i,并且i没被访问过
DFS(g,i);
}
}
2.最短路径(迪杰斯特拉算法):
bool minNodes[Maxsize];
int minIndex(Distance dist[],int n)
{
int i=0,min=Max,ind=-1;
for(;i<n;i++)
if(!minNodes[i] && dist[i].curLen<min)
ind=i;
return ind;
}
//最短路径(迪杰斯特拉算法):
void Dijkstra(AdjMat g,int v)//起点是v,到各个节点的距离
{
memset(minNodes,0,sizeof(minNodes));
Distance dist[Maxsize];
int cur,j;
int i=0;
minNodes[v]=1;//初始化
while(i<g.n)
{
dist[i].preLen=v;
dist[i].curLen=g.arc[v][i];i++;
}
for(i=1;i<g.n;i++)//还需要加入n-1个节点
{
cur=minIndex(dist,g.n);
minNodes[cur]=1;
for(j=0;j<g.n;j++)
if(!minNodes[j]&& dist[j].curLen>g.arc[cur][j]+dist[cur].curLen)
{
dist[j].curLen=g.arc[cur][j]+dist[cur].curLen;
dist[j].preLen=cur;
}
}
cout<<endl;
for(i=0;i<g.n;i++)
printf("\n%d结点的前趋为%d %d结点到达源点的最短路径长度为 %d\n",i,dist[i].preLen,i,dist[i].curLen);
}
3.最小生成树(克鲁斯卡算法):
bool cmp(edge x,edge y)
{
if(x.w==y.w)
return x.w>y.w;
return x.w<y.w;
}
int parents[]={0,1,2,3,4,5};
int Ancestor(int v)
{
while(v!=parents[v])
v=parents[v];
return v;
}
void unionOpt(int u,int v)
{
int a1=Ancestor(u),a2=Ancestor(v);
if(a1!=a2)
parents[a2]=a1;
}
void Krucal(edge edges[],int n,int vexNum)//n represents the num of edges
{
int i=0;
sort(edges,edges+n,cmp);//对边按权值排序
for(;i<n;i++)
if(Ancestor(edges[i].u)!=Ancestor(edges[i].v))
{
cout<<"新增路径:"<<edges[i].u<<" "<<edges[i].v<<endl;
unionOpt(edges[i].u,edges[i].v);
}
}
------》源代码*DFS+Dijkstra:
#include <iostream>
#include <stdlib.h>
using namespace std;
#define Maxsize 10
#define Max 100
//结构描述:
typedef struct
{
int n,e;
char vex[Maxsize];
int arc[Maxsize][Maxsize];
}AdjMat;//定义邻接矩阵
//存储邻接矩阵(初始化邻接矩阵):
void storeAdjM(int arcs[][5],AdjMat &g,int n,int e)
{
int i,j;
g.n=n;
g.e=e;
for(i=0;i<n;i++)
for(j=0;j<n;j++)
g.arc[i][j]=arcs[i][j];
}
//显示邻接矩阵:
void DispAdj(AdjMat g)
{
int i,j;
for(i=0;i<g.n;i++){
for(j=0;j<g.n;j++)
cout<<g.arc[i][j]<<" ";
cout<<endl;
}
}
//结构描述:
typedef struct
{
int preLen;//目前长度的前趋
int curLen;//当前到源点长度
}Distance;
//访问标志数组:
int visit[Maxsize];
//对图进行深度优先遍历:
void DFS(AdjMat g,int v)
{
int i=0;
visit[v]=1;
cout<<v<<" ";
for(;i<g.n;i++)
{
if(g.arc[v][i]<Max && !visit[i])//v能够到达i,并且i没被访问过
DFS(g,i);
}
}
//用于标识各点是否被访问过:
bool minNodes[Maxsize];
//找出暂时没有求出最短路径点到源点的长度中最小的值:
int minIndex(Distance dist[],int n)
{
int i=0,min=Max,ind=-1;
for(;i<n;i++)
if(!minNodes[i] && dist[i].curLen<min)
ind=i;
return ind;
}
//最短路径(迪杰斯特拉算法):
void Dijkstra(AdjMat g,int v)//起点是v,到各个节点的距离
{
memset(minNodes,0,sizeof(minNodes));
Distance dist[Maxsize];
int cur,j;
int i=0;
minNodes[v]=1;//初始化
while(i<g.n)
{
dist[i].preLen=v;
dist[i].curLen=g.arc[v][i];i++;
}
for(i=1;i<g.n;i++)//还需要加入n-1个节点
{
cur=minIndex(dist,g.n);
minNodes[cur]=1;
for(j=0;j<g.n;j++)
if(!minNodes[j]&& dist[j].curLen>g.arc[cur][j]+dist[cur].curLen)
{
dist[j].curLen=g.arc[cur][j]+dist[cur].curLen;
dist[j].preLen=cur;
}
}
cout<<endl;
for(i=0;i<g.n;i++)
printf("\n%d结点的前趋为%d %d结点到达源点的最短路径长度为 %d\n",i,dist[i].preLen,i,dist[i].curLen);
}
int main()
{
//图的存储与显示:
AdjMat g;
int arcs[5][5]={{0,3,5,9,2},{3,0,9,2,7},{4,9,0,1,6},{4,2,1,0,7},{2,5,8,7,0}};
storeAdjM(arcs,g,5,6);
DispAdj(g);
cout<<endl;
//深搜:
DFS(g,3);
//最短路径:
memset(visit,3,sizeof(visit));
Dijkstra(g,3);
}
------》源代码*Kruskal:
#include<iostream>
#include<algorithm>
#include<string.h>
#define Vex 10 //顶点个数的最大值
#define Edge 20 //边的个数的最大值
using namespace std;
//1.边的结构声明:
struct edge
{
int u, v, w; //边、顶点、权值
}edges[Vex]; //边的数组
//2.显示边:
void DispEdges(edge edges[])
{
int i=0;
while(i<6)
{
cout<<edges[i].u<<" "<<edges[i].v<<" "<<edges[i].w<<endl;
i++;
}
}
//3.搜索成本最小的边:
bool cmp(edge x,edge y)
{
if(x.w==y.w)
return x.w>y.w;
return x.w<y.w;
}
//4.为最小生成树做辅助:
int parents[]={0,1,2,3,4,5};
int Ancestor(int v)
{
while(v!=parents[v]) v=parents[v];
return v;
}
void unionOpt(int u,int v)
{
int a1=Ancestor(u),a2=Ancestor(v);
if(a1!=a2)
parents[a2]=a1;
}
//5.克鲁斯卡算法(最小成本生成树函数)->贪婪法则:
void Krucal(edge edges[],int n,int vexNum)//n represents the num of edges
{
int i=0;
sort(edges,edges+n,cmp);//对边按权值排序
for(;i<n;i++)
if(Ancestor(edges[i].u)!=Ancestor(edges[i].v))
{
cout<<"新增路径:"<<edges[i].u<<" "<<edges[i].v<<endl;
unionOpt(edges[i].u,edges[i].v);
}
}
int main()
{
int i=0;
int arcs[6][3]={{0,1,3},{0,4,2},{1,3,2},{1,2,9},{2,3,1},{3,4,7}};
while(i<6)
{
edges[i].u=arcs[i][0];
edges[i].v=arcs[i][1];
edges[i].w=arcs[i][2];
i++;
}
DispEdges(edges);
Krucal(edges,6,3);
}
图在数据结构中的应用主要体现在存储,搜索和遍历上,尤其是遍历,因为有众多路线,所以我们一般在遍历时遵行“贪婪法则”即最优化方案。
在图的应用中有两大经典问题即最小生成树和最短路径:
最小生成树:
- 普利姆算法(prim).
- 克鲁斯卡算法(Krucal).
最短路径:
迪杰斯特拉算法(Dijkstra).
*ps:
<algorithm>
是c++特有的STL模板的算法头文件,包含了一些特定的算法函数
包括 sort(), stable_sort(), partical_sort(), nth_element()等常用的算法函数。