数据结构知识点系列二
1、图
相关定义
图是由顶点的有穷非空集合和顶点之间边的集合组成,记为
G(V,E)
,其中
V
为顶点集合,
无向图中,顶点的度 TD(v) 为与顶点v相关的边的数目;有向图中,度=入度+出度:入度 ID(v) 为以v为头的边的数目;出度 OD(v) 为以v为尾的边的数目。
在无向图中,任意两个顶点都有路径,称为“连通图”。“连通分量”无向图中极大连通分量。
存储结构
邻接矩阵
入度:所在列的和。出度:所在行的和。
//1. 邻接矩阵创建图
typedef char VertexType;
typedef int EdgeType;
#define MaxVex 100
#define Inf 65535
typedef struct{
VertexType vexs[MaxVex];
EdgeType arc[MaxVex][MaxVex];
int numVertexes,numEdges;
}MGraph;
void CreateMGraph(MGraph* G){
int i,j,k,w;
printf("输入顶点数和边数:\n");
scanf("%d%d",&G->numVertexes,%G->numEdges);
for (i=0;i<G->numVertexes;i++){
scanf("%d",&G->vexs[i]);
}
for (i=0;i<G->numVertexes;i++)
for(j=0;j<G->numEdges;j++)
G->arc[i][j] = Inf;
for (k=0;k<G->numEdges;k++){
printf("输入边的下标i,j和权重w:\n");
scanf("%d%d%d",&i,&j,&w);
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j];
}
}
邻接表
以顶点为尾的相关边的顶点链表。无向图:n个表头结点,2e个表结点
typedef struct EdgeNode{
int adjvex;
EdgeType weight;
struct EdgeNode *next;
}EdgeNode;
typedef struct VertexNode{
VertexType data;
EdgeNode *firstedge;
}VertexNode,AdjList[MaxVex];
typedef struct{
AdjList adjList;
int numVertexes,numEdges;
}GraphAdjList;
void CreateALGraph(GraphAdjList* G){
int i,j,k;
EdgeNode *e;
//输入顶点数和边数
for (i=0;i<G->numVertexes;i++){
scanf("%d",&G->adjList[i].data);
G->adjList[i].firstedge = NULL;
}
for (k=0;k<G->numEdges;k++){
//输入下标i和j
e = (EdgeNode)malloc(sizeof(EdgeNode));
e->adjvex = j;
e->next = G->adjList[i].firstedge;
G->adjList[i].firstedge = e;
e = (EdgeNode)malloc(sizeof(EdgeNode));
e->adjvex = i;
e->next = G->adjList[j].firstedge;
G->adjList[j].firstedge = e;
}
}
十字链表for有向图;邻接多重表for无向图。
图的遍历
**深度优先遍历:**DFS使用栈。类似于树的前序遍历。
**广度优先遍历:**BFS使用队列。类似于树的层序遍历。
/深度优先遍历DFS
//邻接矩阵
void DFSTraverse(MGraph G){
int i;
int visited[MaxVex];
for (i=0;i<G.numVertexes;i++){
visited[i] = 0;
}
for (i=0;i<G.numVertexes;i++){
if (visited[i] == 0){
DFS1(G,i);
}
}
}
void DFS1(MGraph G,int i){
int j;
visited[i] = 1;
printf("%c",G.vexs[i]);
for (j=0;j<G.numVertexes;j++)
if (G.arc[i][j] ==1 && visited[j]==0)
DFS1(G,j);
}
//邻接表
void DFSTraverse(GraphAdjList G){
int i;
int visited[MaxVex];
for (i=0;i<G.numVertexes;i++){
visited[i] = 0;
}
for (i=0;i<G.numVertexes;i++){
if (visited[i] == 0){
DFS2(G,i);
}
}
}
void DFS2(GraphAdjList G,int i){
VertexNode* p = G.adjList[i].firstedge;
visited[i] = 1;
printf("%c",G.adjList[i].data);
while (p){
if (visited[p->adjvex]==0)
DFS2(G,p->adjvex);
p = p->next;
}
}
//广度优先遍历BFS
//邻接矩阵
void BFSTraverse(MGraph G){
int i,j;
queue<int> q;
int visited[MaxVex];
for (i=0;i<G.numVertexes;i++){
visited[i] = 0;
}
for (i=0;i<G.numVertexes;i++){
if (visited[i]==0){
visited[i] = 1;
printf("%c",G.vexs[i]);
q.push(i);
while (!q.empty()){
int k = q.front();
q.pop();
for (j=0;j<G.numVertexes;j++){
if (G.arc[i][j]==1 && visited[j]==0){
visited[j] = 1;
printf("%c",G.vexs[j]);
q.push(j);
}
}
}
}
}
}
//邻接表
void BFSTraverse2(GraphAdjList G){
int i,j;
queue<int> q;
EdgeNode* p;
int visited[MaxVex];
for (i=0;i<G.numVertexes;i++){
visited[i] = 0;
}
for (i=0;i<G.numVertexes;i++){
if (visited[i]==0){
visited[i] = 1;
printf("%c",G.adjList[i].data);
q.push(i);
while (!q.empty()){
int k = q.front();
q.pop();
p = G.adjList[i].firstedge;
while (p){
if (visited[p->adjvex]==0){
visited[p->adjvex] = 1;
printf("%c",G.adjList[p->adjvex].data);
p.push(p->adjvex);
}
p = p->next;
}
}
}
}
}
最小生成树
构造连通网的最小代价生成树。
Prim算法:假设
N={V,{E}}
是连通网,TE是N上最小生成树边的集合。算法从
U={u0},u0∈V,TE={}
开始。重复执行下述操作:在所有
u∈U,v∈{V−U}
的边
(u,v)∈E
中找到一条代价最小的边加入集合TE,同时将对应顶点并入
U
,直到
void Prim(MGraph G){
int min,i,j,k;
int adjvex[MaxVex],lowcost[MaxVex];
adjvex[0] = 0,lowcost[0] = 0;
for (i=1;i<G.numVertexes;i++){
lowcost[i] = G.arc[0][i];
adjvex[i] = 0;
}
for (i=1;i<G.numVertexes;i++){
min = Inf;
j=1,k=0;
while (j<G.numVertexes){
if (lowcost[j]!= 0 && lowcost[j] < min){
min = lowcost[j];
k = j;
}
j++;
}
printf("%d%d",adjvex[k],k);
lowcost[k] = 0;
for (j=1;j<G.numVertexes;j++)
if (lowcost[j]!=0 && G.arc[k][j]<lowcost[j]){
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
Kruskal算法:假设
N={V,{E}}
是连通网,则令最小生成树的初始状态为只有n个顶点而无边的非连通图
T={V,{}}
。重复执行下述操作:在
E
中选择代价最小的边,若这条边依附再得顶点落在T中不同的连通分量上,则将此边加入
typedef struct{
int begin,end,weight;
}Edge;
void Kruskal(MGraph G){
int i,n,m;
Edge edges[MaxEdge];
int parents[MaxVex];//定义一个数组判断边与边是否形成环路
//将邻接矩阵转为边集数组并按权重排序
for (i=0;i<G.numVertexes;i++)
parents[i] = 0;
for (i=0;i<G.numEdges;i++){
n = Find(parents,edges[i].begin);
m = Find(parents,edges[i].end);
if (n!=m){
parents[n] = m;
printf("(%d,%d)%d",edges[i].begin,edges[i].end,edges[i].weight);
}
}
}
int Find(int* parents,int f){
while (parents[f]>0)
f = parents[f];
return f;
}
最短路径
Dijkstra算法:基于已求出的最短路径求更远顶点的最短路径。
Floyd算法:动态规划方法。维护两个矩阵:D顶点到顶点的最短路径权值和矩阵;P对应顶点的最短路径的前驱。
//最短路径:Dijkstra算法(贪心)
typedef int Patharc[MaxVex];//最短路径下标的数组
typedef int ShortPathTable[MaxVex];//到各顶点最短路径的权重和
void Dijkstra(MGraph G,int s, Patharc* P,ShortPathTable* D){
int v,w,k,min;
int find[MaxVex];
for (v=0;v<G.numVertexes;v++){
find[v] = 0;
(*D)[v] = G.arc[s][v];
(*P)[v] = 0;
}
(*D)[s] = 0,find[s] = 1;
for (v=1;v<G.numVertexes;v++){
min = Inf;
for (w=0;w<G.numVertexes;w++){
if (find[w] == 0 && (*D)[w]<min){
min = (*D)[w];
k = w;
}
}
find[k] = 1;
for (w=0;w<G.numVertexes;w++){
if (find[w] == 0 && (min+G.arc[k][w])<(*D)[w]){
(*D)[w] = min+G.arc[k][w];
(*D)[w] = k;
}
}
}
}
//最短路径:Folyd算法(动态规划)
typedef int Pathmatrix[MaxVex][MaxVex];
typedef int ShortPathTable[MaxVex][MaxVex];
void Folyd(MGraph G,Pathmatrix* P,ShortPathTable* D){
int v,w,k;
for (v=0;v<G.numVertexes;v++){
for (w=0;w<G.numVertexes;w++){
(*P)[v][w] = w;
(*P)[v][w] = G.arc[v][w];
}
}
for (k=0;k<G.numVertexes;k++){
for (v=0;v<G.numVertexes;v++){
for (w=0;w<G.numVertexes;w++){
if ((*D)[v][w]>(*D)[v][k]+(*D)[k][w]){
(*D)[v][w] = (*D)[v][k]+(*D)[k][w];
(*P)[v][w] = (*P)[v][k];
}
}
}
}
}
拓扑排序
AOV网:在一个表示工程的有向图中,用顶点表示活动,用边表示活动之间的优先关系,所构成的网。
拓扑排序算法:从AOV网中选择一个入度为0的顶点输出,然后删除以该顶点为尾的弧,重复直到输出所有的顶点或不存在入度为0的顶点为止。
int ToplogicalSort(GraphAdjList G){
EdgeNode* e;
int i,k,gettop;
int top=count=0;
stack<int> s;
for (i=0;i<G.numVertexes;i++){
if (G.adjList[i].in == 0)
s.pust[i];
while (!s.empty()){
gettop = s.top();
s.pop();
printf("%c",G.adjList[gettop].data);
count++;
for (e=G.adjList[gettop].firstedge;e;e=e->next){
k = e->adjvex;
if (!(--G.adjList[k].in))
s.push(k);
}
}
if (count<G.numVertexes)
return 0;
return 1;
}
}
关键路径
AOE网=*AOV网+边的权值,用边表示活动,权值表示活动持续时间。没有入边的顶点为源点,没有出边的顶点为汇点。关键路径:从源点到汇点具有最大路径长度*的路径。