数据结构>>>图的应用>>"贪婪法则"

“贪婪法则”:

  • 深搜
  • 最短路径(迪杰斯特拉算法)
  • 最小生成树(克鲁斯卡算法)

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);
}

图在数据结构中的应用主要体现在存储,搜索和遍历上,尤其是遍历,因为有众多路线,所以我们一般在遍历时遵行“贪婪法则”即最优化方案。
在图的应用中有两大经典问题即最小生成树和最短路径:

最小生成树:

  1. 普利姆算法(prim).
  2. 克鲁斯卡算法(Krucal).

最短路径:
迪杰斯特拉算法(Dijkstra).

*ps:
<algorithm>是c++特有的STL模板的算法头文件,包含了一些特定的算法函数
包括 sort(), stable_sort(), partical_sort(), nth_element()等常用的算法函数。

猜你喜欢

转载自blog.csdn.net/qq_43595030/article/details/90917091