数据结构——图总结


写在前面

  1. 图的定义:G=(V,E),V为顶点集,E为边集。设图有n个顶点,V={v1,v2,v3,…,vn}
  2. 图的分类
    ①有向图:图中任意两个顶点之间的边都是有向边。若顶点M到顶点N的边有方向,称这条边为有向边,也称为弧,用偶序对 < M, N >表示,M表示弧尾,N表示弧头 。
    ②无向图:图中任意两个顶点之间的边都是无向边。若顶点M到顶点N的边没有方向,称这条边为无向边,用无序偶对(M,N)或(N,M)表示。

  3. ①对于有向图来说,与某个顶点相关联的弧的数目称为度(TD);以某个顶点v为弧尾的弧的数目定义为顶点v的出度(OD);以顶点v为弧头的弧的数目定义为顶点的入度(ID) 。度(TD) = 出度(OD) + 入度(ID)。
    ②对于无向图,假若顶点v和顶点w之间存在一条边,则称顶点v和顶点w互为邻接点,边(v,w)和顶点v和w相关联。顶点v的度是和v相关联的边的数目,记为TD(v);。


图的存储

一般来说,图的存储方式有两种:邻接矩阵和邻接表。这两种均可用于有向图和无向图,但各有优劣。
此外还有十字链表和邻接多重表。
十字链表有向图的一种链式存储结构,在十字链表中,对应于有向图中的每条弧有一个结点,对应于每个顶点也有一个结点。
邻接多重表无向图的另一种链式存储结构。在邻接多重表中,所有依附于同一顶点的边串联在同一链表中,由于每条边依附于两个顶点,则每个边顶点同时链接在两个链表中

邻接矩阵

//邻接矩阵存储结构定义
#define MaxVertexNum 100
typedef char VertexType;
typedef int EdgeType;
typedef struct{
	VertexType Vex[MaxVertexNum];
	EdgeType Edge[MaxVertexNum][MaxVertexNum];
	int vexnum,arcnum;
}MGraph; 

在简单应用中,可以直接用二维数组作为图的邻接矩阵(顶点信息等可省略)

邻接表

//邻接表存储结构定义
typedef struct ArcNode{ //边表结点 
	int adjvex;
	struct ArcNode *next;
}ArcNode;

typedef struct VNode{ //顶点表结点 
	VertexType data;
	ArcNode *first;
}VNode,AdjList[MaxVertexNum];

typedef struct{
	AdjList vertices;
	int vexnum,arcnum;
}ALGraph; 

图的邻接表表示不唯一,因为在每个顶点对应的单链表中,各边结点的链接次序可以是任意的,取决于建立邻接表的算法以及边的输入次序


图的遍历

图的遍历是指对图中的所有顶点按照一定的顺序进行访问,遍历的方法一般有两种:深度优先搜索(DFS)和广度优先搜索(BFS)。

深度优先搜索

//DFS
const int MAXV = 1000; //最大顶点数
const int INF = 1000000000; //设INF是一个很大的数

int n,G[MAXV][MAXV]; 
bool vis[MAXV] = {false};

void DFS(int u, int depth)
{
	vis[u] = true;
	for(int v = 0;v<n;v++)
	{
		if(vis[v]==false&&G[u][v]!=INF)
		{
			DFS(v,depth+1);
		}
	}
 }
void DFSTrave()
{
	for(int u=0;u<n;u++)
	{
		if(vis[u]==false)
		{
			DFS(u,1);
		}
	}
 } 

广度优先搜索

//BFS
bool inq[MAXV] = {false}; 
void BFS(int u)
{
	queue<int>q;
	q.push(u);
	inq[u] = true;
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		for(int v=0;v<n;v++)
		{
			if(inq[v]==false&&G[u][v]!=INF)
			{
				q.push(v);
				inq[v] = true;
			}
		 } 
	}
}
void BFSTrave()
{
	for(int u=0;u<n;u++)
	{
		if(inq[u]==false)
		{
			BFS(u);
		}
	}
}


图的应用

图的应用主要有,最小生成树,最短路径,拓扑排序和关键路径。

最小生成树

解决最小生成树问题有两种算法:prim算法(普里姆算法,时间复杂度O(V*V)适合顶点数目较少而边较多的稠密图)kruskal算法(克鲁斯卡尔算法,时间复杂度O(ElogE)适合边数目较少而顶点较多的稀疏图)

int map[505][505];
int visit[505];
int N ;//vertice number [0,N-1]
int prim(int path[],int dist[])//minimum spanning-tree
{
	for(int i=0;i<N;i++)
	{
		dist[i] = map[0][i];
	}
	memset(visit,0,sizeof(visit));
	visit[0] =  1;
	for(int i=1;i<N;i++)
	{
		int min = INT_MAX,pos=-1;
		for(int j=0;j<N;j++)
		{
			if(visit[j]==0&&dist[j]<min)
			{
				min = dist[j];
				pos = j;
			}
		}
		visit[pos] = 1;
		for(int j=0;j<N;j++)
		{
			if(visit[j]==0&&map[pos][j]<dist[j])
			{
				dist[j] = map[pos][j];
				path[j] = pos;
			 } 
		}
	}
	int sum = 0;
	for(int i=0;i<N;i++)
	{
		sum+=dist[i];
		if(dist[i]==INT_MAX)
		{
			return -1 ;//不存在最小生成树 
		}
	}
	return sum; 
}

最短路径

单源最短路径问题使用DIjkstra算法(迪杰斯特拉算法)。
全源最短路径问题使用Floyd算法(弗洛伊德算法)。

int map[505][505];
int visit[505];
int N ;//vertice number [0,N-1]
int s; // start point
int e; //end point 
void Dijkstra(int path[],int dist[])// shortest path
{
   for(int i=0;i<N;i++)
	{
		dist[i] = map[s][i];
	}
	memset(visit,0,sizeof(visit));
	visit[s] =  1;
	for(int i=1;i<N;i++)
	{
		int min = INT_MAX,pos=-1;
		for(int j=0;j<N;j++)
		{
			if(visit[j]==0&&dist[j]<min)
			{
				min = dist[j];
				pos = j;
			}
		}
		if(pos==-1)return;
		visit[pos] = 1;
		for(int j=0;j<N;j++)
		{
			if(visit[j]==0&&dist[pos]+map[pos][j]<dist[j])
			{
				dist[j] = dist[pos]+map[pos][j];
				path[j] = pos;
			 } 
		}
	}
} 

参考资料

  • 《数据结构考研复习指导》 王道论坛组编 电子工业出版社
  • 《算法笔记》 胡凡 曾磊主编 机械工业出版社

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值