图(Graph)的定义:由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合
无向边:若顶点Vi到Vj之间的边没有方向,则称这条边为无向边
有向边:若顶点Vi到Vj之间的边有方向,则称这条边为有向边,也称为弧
简单图:图中不存在顶点到其自身的边,且同一条边不重复出现
无向完全图:无向图中任意两个顶点之间都存在边
有向完全图:有向图中任意两个顶点间存在方向相反的弧
稀疏图:边或者弧数小于n*log(n)
稠密图:与上述条件相反
图的顶点与边之间的关系:
无向图:对于无向图G(V,E),如果边(V1,V2)∈E,则称顶点V1和V2互为邻接点,顶点V的度是和V相关联的边的数目,记为TD(V)
有向图:对于无向图G(V,E),如果弧<V1,V2>∈E,则称顶点V1邻接到顶点V2,或者顶点V2邻接自顶点V1,以顶点V为头的弧的数目称为V的入度,记为ID(V),以V为尾的弧的数目称为V的出度,记为OD(V),因此顶点V的度为TD(V) = ID(V)+OD(V)
路径:图中一个顶点到另一个顶点的遍历,长度是路径上的边或弧的数目
简单环:除第一个和最后一个顶点外,其余顶点不重复出现
连通图:无向图中任意两个顶点都有路径连通
连通分量:无向图中的极大连通子图
强连通图:有向图中任意两个顶点都存在路径
如果一个有向图恰有一个顶点入度为0,其余顶点入度均为1,则这是一颗有向树
图的存储结构:
内存物理位置是线性的,图的元素关系是平面的,故无法以数据元素在内存中的位置来表示元素间的关系
邻接矩阵:
用两个数组表示图,一个一维数组存储图中顶点信息,一个二维数组(无向图中为对称矩阵)存储图中边或弧的信息
网:每条边上带有权的图
邻接表:用于边数及顶点相对较少的图
图中顶点用一个一维数组存储,图中每个顶点的所有邻接点构成一个线性表
十字链表:与邻接表类似,增加弧头(或弧尾)的数据以及入度(或出度)的next指针
链接多重表:与邻接表类似,边表结构变为可表示边,next指针指向下一个与该顶点有关的边
边集数组:两个一维数组构成,一个存储顶点信息,一个存储边的信息,边数组每个数据元素由一条边的起点下标、终点下标和权组成
各存储结构的实现形式:
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
/*邻接矩阵定义*/
typedef struct _Graph
{
unsigned int line[MAXSIZE][MAXSIZE];
char value[MAXSIZE];
int n, e; //记录图中顶点数以及边或弧的数量
}Graph;
/*邻接表定义(对于有向图来说,一个表只能求入度或出度的一个)*/
typedef struct _GraphNode
{
unsigned int linkvalue;
struct _GraphNode *next;
}GraphNode;
typedef struct _GraphBox
{
char value;
GraphNode *first;
}GraphBox;
typedef struct _GraphTable
{
GraphBox table[MAXSIZE];
int n, e; //记录图中顶点数以及边或弧的数量
}GraphTable;
/*十字链表定义(有向图专用)*/
typedef struct _GraphTenNode
{
unsigned int tailvalue; //表示这条弧的尾(数组下标)
unsigned int headvalue; //表示这条弧的头
struct _GraphTenNode *headnext; //表示该顶点入度的下一个
struct _GraphTenNode *tailnext; //表示该顶点出度的下一个
}GraphTenNode;
typedef struct _GraphTenBox
{
char value;
GraphTenNode *headfirst; //表示该顶点入度的第一个
GraphTenNode *tailfirst; //表示该顶点出度的第一个
}GraphTenBox;
typedef struct _GraphTenTable
{
GraphTenBox table[MAXSIZE];
int n, e;
}GraphTenTable;
/*邻接多重表定义(对边的操作较为轻松)(无向图专用)*/
typedef struct _GraphCoNode
{
unsigned int ivex; //边的第一个顶点
struct _GraphCoNode *ilink; //上述顶点的下一个邻接边
unsigned int jvex; //边的第二个顶点
struct _GraphCoNode *jlink; //上述顶点的下一个邻接边
}GraphCoNode;
typedef struct _GraphCoBox
{
char value;
GraphCoNode *first; //此顶点第一个邻接边
}GraphCoBox;
typedef struct _GraphCoTable
{
GraphCoBox table[MAXSIZE];
int n, e;
}GraphCoTable;
/*边集数组定义*/
typedef struct _ArrayGraphNode
{
unsigned int begin;
unsigned int end;
unsigned int weight;
}ArrayGraphNode;
typedef struct _ArrayGraph
{
char value[MAXSIZE];
ArrayGraphNode edegs[2 * MAXSIZE];
int n, e;
}
图的遍历:
深度优先遍历:也称为深度优先搜索,简称为DFS,类似于树的前序遍历,需要使用递归
/*邻接矩阵的深度遍历实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 8
typedef struct _Array
{
char vex[MAXSIZE];
unsigned int linevalue[MAXSIZE][MAXSIZE];
}Array;
int n = 1;
int flag[MAXSIZE];
void InitArray(Array *a)
{
int i, j, k;
char c;
for(i=0; i<MAXSIZE; i++)
{
j = 65 + i;
(*a).vex[i] = (char) j;
}
for(i=0; i<MAXSIZE; i++)
{
printf("请输入第%d行的数值:\n", i);
fflush(stdin);
for(j=0; j<MAXSIZE; j++)
{
scanf("%c", &c);
k = (int) c;
k = k - 48;
(*a).linevalue[i][j] = k;
}
}
}
void printArray(Array a)
{
int i, j;
printf("顶点依次表示为:\n");
for(i=0; i<MAXSIZE; i++)
{
printf("%c", a.vex[i]);
}
printf("\n");
printf("表示边的矩阵:\n");
for(i=0; i<MAXSIZE; i++)
{
for(j=0; j<MAXSIZE; j++)
{
printf("%d ", a.linevalue[i][j]);
}
printf("\n");
}
}
void DepTra(Array a, int i)
{
int j;
for(j=0; j<MAXSIZE; j++)
{
if(a.linevalue[i][j] == 1 && flag[j] == 0)
{
if(7 > n)
{
printf("%c->", a.vex[j]);
}
else
{
printf("%c", a.vex[j]);
}
n++;
flag[j] = 1;
DepTra(a, j);
}
}
}
int main(void)
{
Array a;
int i, x;
InitArray(&a);
printArray(a);
for(i=0; i<MAXSIZE; i++)
{
flag[i] = 0;
}
printf("请输入开始遍历的行数:\n");
scanf("%d", &x);
printf("遍历结果为:\n");
printf("%c->", a.vex[x]);
flag[x] = 1;
DepTra(a, x);
return 0;
}
/*邻接表的深度遍历实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 8
typedef struct _GraphLink
{
unsigned int value;
struct _GraphLink *next;
}GraphLink;
typedef struct _GraphNode
{
char vex;
GraphLink *first;
}GraphNode;
typedef struct _GraphList
{
GraphNode table[MAXSIZE];
//int n, e;
}GraphList;
int flag[MAXSIZE];
int n = 1;
void InitGraph(GraphList *a)
{
unsigned int i, j;
char k;
GraphLink *temp, *target;
for(i=0; i<MAXSIZE; i++)
{
flag[i] = 0; //深度遍历的顶点标记
j = i + 65;
(*a).table[i].vex = (char) j; //顶点赋值
(*a).table[i].first = NULL;
printf("请输入和顶点%c有关的顶点的下标,以#结束:\n", (*a).table[i].vex);
fflush(stdin);
scanf("%c", &k);
while('#' != k)
{
temp = (GraphLink *)malloc(sizeof(GraphLink));
temp->value = k - 48;
if((*a).table[i].first == NULL)
{
(*a).table[i].first = temp;
temp->next = NULL;
target = temp;
}
else
{
target->next = temp;
temp->next = NULL;
target = temp;
}
scanf("%c", &k);
}
}
/*
gra.value[MAXSIZE][MAXSIZE] = {
{0,1,0,0,0,0,0,1},
{1,0,0,1,0,1,0,1},
{0,0,0,1,0,0,0,0},
{0,1,1,0,0,1,0,1},
{0,0,0,0,0,1,0,0},
{0,1,0,1,1,0,0,1},
{0,0,0,0,0,0,0,1},
{1,1,0,1,0,1,1,0}};
*/
}
void PrintGraph(GraphList a)
{
int i, j;
GraphLink *target;
int b[MAXSIZE][MAXSIZE];
printf("\n此图的关系矩阵为:\n");
for(i=0; i<MAXSIZE; i++)
{
target = a.table[i].first;
printf("%c ", a.table[i].vex);
for(j=0; j<MAXSIZE; j++)
{
b[i][j] = 0;
}
while(target != NULL)
{
j = (*target).value;
b[i][j] = 1;
target = target->next;
}
for(j=0; j<MAXSIZE; j++)
{
printf("%d ", b[i][j]);
}
printf("\n");
}
}
void TraverseGraph(GraphList a, int x)
{
int i;
GraphLink *target;
target = a.table[x].first;
while(target != NULL)
{
i = (*target).value;
if(1 != flag[i])
{
if(7 > n)
{
printf("%c->", a.table[i].vex);
}
else
{
printf("%c\n", a.table[i].vex);
}
flag[i] = 1;
n++;
TraverseGraph(a, i);
}
target = target->next;
}
}
int main(void)
{
int x;
GraphList a;
InitGraph(&a);
PrintGraph(a);
printf("\n请输入开始遍历的行数:\n");
scanf("%d", &x);
printf("\n遍历结果为:\n");
printf("%c->", a.table[x].vex);
flag[x] = 1;
TraverseGraph(a, x);
return 0;
}
马踏棋盘(图的深度优先遍历+回溯法):
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define X 6
#define Y 6
int chess[X][Y];
//马的路径寻找过程
int NextTry(int *x, int *y, int count)
{
switch(count)
{
case 0:
if(*x+2 <= X-1 && *y-1 >= 0 && chess[*x+2][*y-1] == 0)
{
*x += 2;
*y -= 1;
return 1;
}
break;
case 1:
if(*x+2 <= X-1 && *y+1 <= Y-1 && chess[*x+2][*y+1] == 0)
{
*x += 2;
*y += 1;
return 1;
}
break;
case 2:
if(*x-2 >= 0 && *y-1 >= 0 && chess[*x-2][*y-1] == 0)
{
*x -= 2;
*y -= 1;
return 1;
}
break;
case 3:
if(*x-2 >= 0 && *y+1 <= Y-1 && chess[*x-2][*y+1] == 0)
{
*x -= 2;
*y += 1;
return 1;
}
break;
case 4:
if(*x+1 <= X-1 && *y-2 >= 0 && chess[*x+1][*y-2] == 0)
{
*x += 1;
*y -= 2;
return 1;
}
break;
case 5:
if(*x+1 <= X-1 && *y+2 <= Y-1 && chess[*x+1][*y+2] == 0)
{
*x += 1;
*y += 2;
return 1;
}
break;
case 6:
if(*x-1 >= 0 && *y-2 >= 0 && chess[*x-1][*y-2] == 0)
{
*x -= 1;
*y -= 2;
return 1;
}
break;
case 7:
if(*x-1 >= 0 && *y+2 <= Y-1 && chess[*x-1][*y+2] == 0)
{
*x -= 1;
*y += 2;
return 1;
}
break;
default:
break;
}
return 0;
}
void PrintChess()
{
int i, j;
for(i=0; i<X; i++)
{
for(j=0; j<Y; j++)
{
printf("%2d ",chess[i][j]);
}
printf("\n");
}
printf("\n");
}
//深度优先遍历棋盘
//(x,y)为位置坐标
//tag是标记变量,每走一步,tag+1
int TravelChessBoard(int x, int y, int tag)
{
int x1 = x, y1 = y, flag = 0, count = 0;
chess[x][y] = tag;
if(X*Y == tag)
{
PrintChess();
return 1;
}
//找到马的下一个可走的坐标(x1,y1),如果找到flag=1,否则为0
flag = NextTry(&x1, &y1, count);
while( 0 == flag && count < 7 )
{
count++;
flag = NextTry(&x1, &y1, count);
}
while( flag )
{
if( TravelChessBoard(x1, y1, tag+1) )
{
return 1;
}
//继续找,找马的下一个可走坐标
x1 = x;
y1 = y;
count++;
flag = NextTry(&x1, &y1, count);
while( 0 == flag && count < 7 )
{
count++;
flag = NextTry(&x1, &y1, count);
}
}
if( 0 == flag )
{
chess[x][y] = 0;
}
return 0;
}
int main(void)
{
int i, j;
clock_t start, finish;
start = clock();
for(i=0; i<X; i++)
{
for(j=0; j<Y; j++)
{
chess[i][j] = 0;
}
}
if( !TravelChessBoard(2, 0, 1) )
{
printf("Travel failed!\n");
}
finish = clock();
printf("\nThe run time: %f s\n", (double)(finish-start)/CLOCKS_PER_SEC);
return 0;
}
广度优先遍历:用队列实现
/*邻接矩阵的广度遍历实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 8
typedef struct _GraphArray
{
char vex[MAXSIZE];
unsigned int value[MAXSIZE][MAXSIZE];
}GraphArray;
typedef struct _QNode
{
char data;
struct _QNode *next;
}QNode;
typedef struct
{
QNode *front, *rear;
}LinkQueue;
int flag[MAXSIZE];
int n = 1;
void Initgraph(GraphArray *a)
{
int i, j, k;
char c;
for(i=0; i<MAXSIZE; i++)
{
flag[i] = 0;
k = i + 65;
(*a).vex[i] = (char) k;
printf("请输入第%d行的数据,以#结束:\n", i+1);
fflush(stdin);
scanf("%c", &c);
for(j=0; j<MAXSIZE && '#' != c; j++)
{
(*a).value[i][j] = c - 48;
scanf("%c", &c);
}
}
}
void InitQueue(LinkQueue *q)
{
q->front = q->rear = (QNode *)malloc(sizeof(QNode));
if( !q->front )
{
exit(0);
}
q->front->next = NULL;
}
void InsertQueue(LinkQueue *q, char c)
{
QNode *temp;
temp = (QNode *)malloc(sizeof(QNode));
if( !temp )
{
exit(0);
}
temp->data = c;
temp->next = q->rear->next;
q->rear->next = temp;
q->rear = temp;
}
void DeleteQueue(LinkQueue *q, char *c)
{
QNode *temp;
if(q->front == q->rear)
{
return;
}
temp = q->front->next;
*c = temp->data;
q->front->next = temp->next;
if(q->rear == temp)
{
q->rear = q->front;
}
free(temp);
}
void TraverseWidth(GraphArray a, LinkQueue *q, int x)
{
int j;
char c;
for(j=0; j<MAXSIZE; j++)
{
if(1 == a.value[x][j] && flag[j] == 0)
{
//j所代表顶点入队列操作
c = a.vex[j];
InsertQueue(q, c);
flag[j] = 1;
}
}
//第一个入队列的顶点出队列
DeleteQueue(q, &c);
//记录第一个出队列的顶点下标,为下次递归准备
for(j=0; j<MAXSIZE; j++)
{
if(c == a.vex[j])
{
x = j;
}
}
if(7 > n)
{
printf("%c->", c);
}
else
{
printf("%c", c);
return;
}
n++;
TraverseWidth(a, q, x);
}
int main(void)
{
GraphArray a;
LinkQueue q;
int x;
InitQueue(&q);
Initgraph(&a);
printf("\n请输入开始遍历的行数:\n");
scanf("%d", &x);
printf("\n此图的广度遍历结果为:\n");
printf("%c->", a.vex[x]);
flag[x] = 1;
TraverseWidth(a, &q, x);
return 0;
}
/*邻接表的广度遍历实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 8
typedef struct _GraphLink
{
unsigned int value;
struct _GraphLink *next;
}GraphLink;
typedef struct _GraphNode
{
char vex;
GraphLink *first;
}GraphNode;
typedef struct _GraphList
{
GraphNode table[MAXSIZE];
}GraphList;
typedef struct _QNode
{
char data;
struct _QNode *next;
}QNode;
typedef struct
{
QNode *front;
QNode *rear;
}Queue;
int flag[MAXSIZE];
int n = 0;
void InitQueue(Queue *q)
{
q->front = q->rear = (QNode *)malloc(sizeof(QNode));
if( !q->front )
{
exit(0);
}
q->front->next = NULL;
}
void InsertQueue(Queue *q, char c)
{
QNode *temp;
temp = (QNode *)malloc(sizeof(QNode));
if( !temp )
{
exit(0);
}
temp->data = c;
temp->next = q->rear->next;
q->rear->next = temp;
q->rear = temp;
}
void DeleteQueue(Queue *q, char *c)
{
QNode *temp;
temp = q->front->next;
*c = temp->data;
q->front->next = temp->next;
if(q->rear == temp)
{
q->rear = q->front;
}
free(temp);
}
void InitGraph(GraphList *l)
{
GraphLink *temp, *target;
int i, j;
char c;
for(i=0; i<MAXSIZE; i++)
{
(*l).table[i].first = NULL;
flag[i] = 0;
j = i + 65;
(*l).table[i].vex = (char) j;
printf("请输入与顶点%c有关的顶点下标,以#结束:\n", (*l).table[i].vex);
fflush(stdin);
scanf("%c", &c);
while(c != '#')
{
temp = (GraphLink *)malloc(sizeof(GraphLink));
if( !temp )
{
exit(0);
}
temp->value = c - 48;
if((*l).table[i].first == NULL)
{
(*l).table[i].first = temp;
temp->next = NULL;
target = temp;
}
else
{
target->next = temp;
temp->next = NULL;
target = temp;
}
scanf("%c", &c);
}
}
}
void TraverseGraph(GraphList l, Queue *q, int i)
{
GraphLink *target;
int j;
char c;
target = l.table[i].first;
//输入顶点的出队列操作
if( flag[i] == 0 )
{
DeleteQueue(q, &c);
printf("%c->", c);
flag[i] = 1;
n++;
}
//遍历该顶点的邻接表
//将符合条件的顶点入队列
while(target != NULL)
{
j = target->value;
if(flag[j] == 0)
{
c = l.table[j].vex;
InsertQueue(q, c);
flag[j] = 1;
}
target = target->next;
}
//出队列的第一个顶点并记录值
DeleteQueue(q, &c);
if(7 > n)
{
printf("%c->", c);
}
else
{
printf("%c", c);
return;
}
for(j=0; j<MAXSIZE; j++)
{
if(c == l.table[j].vex)
{
i = j;
}
}
//递归
n++;
TraverseGraph(l, q, i);
}
int main(void)
{
GraphList l;
Queue q;
int i, j, x;
x = 1;
InitQueue(&q);
InitGraph(&l);
while(x)
{
printf("\n请输入开始遍历的顶点下标:\n");
scanf("%d", &i);
InsertQueue(&q, l.table[i].vex);
printf("\n此图的广度遍历结果为:\n");
TraverseGraph(l, &q, i);
n = 0;
for(j=0; j<MAXSIZE; j++)
{
flag[j] = 0;
}
printf("\n\n要重新遍历吗?(1/0)\n");
scanf("%d", &x);
}
return 0;
}
最小生成树:
普利姆算法(prime):邻接矩阵实现
/*Prim算法实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 9
typedef struct _Array
{
char vex[MAXSIZE];
unsigned int weight[MAXSIZE][MAXSIZE];
}Array;
typedef struct _Adjust
{
unsigned int value[MAXSIZE];
}Adjust;
int n = 1;
void InitArray(Array *a)
{
FILE *fp;
int i, j;
i = 0;
if((fp=fopen("c:\\value.txt","r")) == NULL)
{
printf("Can not open the file!\n");
exit(1);
}
while( !feof(fp) )
{
for(j=0; j<MAXSIZE; j++)
{
fscanf(fp, "%d,", &(*a).weight[i][j]);
}
(*a).vex[i] = (char) (i+65);
i++;
}
fclose(fp);
/*
for(i=0; i<MAXSIZE; i++)
{
(*a).vex[i] = (char)(i+65);
for(j=0; j<MAXSIZE; j++)
{
if(i == j)
{
(*a).weight[i][j] = 0;
}
else
{
(*a).weight[i][j] = 1000;
}
}
printf("是否有与顶点%c相连的点?(1/0)\n", (*a).vex[i]);
scanf("%d", &k);
while(1 == k)
{
printf("\n请输入与顶点%c相连的点的下标:\n", (*a).vex[i]);
scanf("%d", &j);
printf("\n请输入与该点的边的权值:\n");
scanf("%d", &k);
(*a).weight[i][j] = k;
printf("\n还有与顶点%c相连的点吗?(1/0)\n", (*a).vex[i]);
scanf("%d", &k);
}
printf("\n");
}
*/
}
void Print(Array a)
{
int i, j;
printf("\n此图的权值矩阵为:\n");
for(i=0; i<MAXSIZE; i++)
{
printf("%c ", a.vex[i]);
for(j=0; j<MAXSIZE; j++)
{
if(a.weight[i][j] == 1000)
{
printf(" * ");
}
else
{
printf("%2d ", a.weight[i][j]);
}
}
printf("\n");
}
}
void Prim(Array *a, Adjust *b, int x)
{
int i, j, k, y, min;
min = 1000;
/*用权值数组与原矩阵除开始行外每一行作比较,记录较小值*/
if(n > 1)
{
for(j=0; j<MAXSIZE; j++)
{
if(0 != (*b).value[j] && (*b).value[j] > (*a).weight[x][j])
{
(*b).value[j] = (*a).weight[x][j];
y = j;
}
}
}
/*找到此时权值数组里的最小值并用min记录*/
for(j=0; j<MAXSIZE; j++)
{
if(0 != (*b).value[j] && (*b).value[j] < min)
{
min = (*b).value[j];
y = j;
}
}
(*b).value[y] = 0;
/*判断选中的边的开头顶点*/
for(j=0; j<MAXSIZE; j++)
{
for(k=0; k<MAXSIZE; k++)
{
if((*b).value[j] == 0 && min == (*a).weight[j][k] && j != y)
{
i = j;
}
}
}
if(n < MAXSIZE-1)
{
printf("%c->%d->%c ", (*a).vex[i], min, (*a).vex[y]);
}
else
{
printf("%c->%d->%c", (*a).vex[i], min, (*a).vex[y]);
return;
}
n++;
Prim(a, b, y);
}
int main(void)
{
Array a;
Adjust b;
int x, j;
InitArray(&a);
Print(a);
printf("请输入开始计算的行数:\n");
scanf("%d", &x);
for(j=0; j<MAXSIZE; j++)
{
b.value[j] = a.weight[x][j];
}
Prim(&a, &b, x);
return 0;
}
克鲁斯卡尔算法(Kruskal):边集数组实现
/*克鲁斯卡尔算法实现*/
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 14
typedef struct _LineNode
{
unsigned int begin;
unsigned int end;
unsigned int weight;
}LineNode;
typedef struct _Array
{
LineNode edges[MAXSIZE];
}Array;
char vex[9];
void InitArray(Array *a)
{
FILE *fp;
int i = 0;
if((fp=fopen("c:\\edges.txt", "r")) == NULL)
{
printf("Can not open file!\n");
exit(1);
}
while( !feof(fp) )
{
fscanf(fp, "%d,%d,%d", &(*a).edges[i].begin, &(*a).edges[i].end, &(*a).edges[i].weight);
i++;
}
fclose(fp);
for(i=0; i<9; i++)
{
vex[i] = (char) (i+65);
}
}
void Print(Array a)
{
int i, j, k;
printf("此图的边集数组为:\n");
for(i=0; i<MAXSIZE; i++)
{
j = a.edges[i].begin;
k = a.edges[i].end;
printf("[%c,%c,%d]\n", vex[j], vex[k], a.edges[i].weight);
}
printf("\n");
}
int Find(int *record, int f)
{
while( record[f] > 0 )
{
f = record[f];
}
return f;
}
void Kruskal(Array *a)
{
int i, m, n;
int x, y;
int record[9];
for(i=0; i<9; i++)
{
record[i] = 0;
}
printf("此图的最小生成树为:\n");
for(i=0; i<MAXSIZE; i++)
{
m = Find(record,(*a).edges[i].begin);
n = Find(record,(*a).edges[i].end);
if( m != n )
{
x = (*a).edges[i].begin;
y = (*a).edges[i].end;
printf("(%c, %c) %d\n", vex[x], vex[y], (*a).edges[i].weight);
record[m] = n;
}
}
printf("\n");
}
int main(void)
{
Array a;
InitArray(&a);
Print(a);
Kruskal(&a);
return 0;
}
最短路径:
网图:两顶点经过的边上权值之和最少路径(花费最少)
非网图:两顶点经过边最少的路径(中转最少)
迪杰斯特拉算法(Dijkstra):
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 9
typedef struct _Array
{
char vex[MAXSIZE];
unsigned int weight[MAXSIZE][MAXSIZE];
}Array;
int flag[MAXSIZE];
int D[MAXSIZE];
int P[MAXSIZE];
void InitArray(Array *a)
{
FILE *fp;
int i, j;
i = 0;
if((fp=fopen("c://Dijkstra.txt", "r")) == NULL)
{
printf("Can not open file!\n");
exit(1);
}
while( !feof(fp) )
{
for(j=0; j<MAXSIZE; j++)
{
fscanf(fp, "%d,", &(*a).weight[i][j]);
}
i++;
}
fclose(fp);
for(j=0; j<MAXSIZE; j++)
{
(*a).vex[j] = (char) (j+65);
flag[j] = 0;
D[j] = 0;
P[j] = 0;
}
}
void Print(Array a)
{
int i, j;
printf("此图的权值矩阵为:\n");
for(i=0; i<MAXSIZE; i++)
{
printf("%c ", a.vex[i]);
for(j=0; j<MAXSIZE; j++)
{
if(1000 == a.weight[i][j])
{
printf("* ");
}
else
{
printf("%d ", a.weight[i][j]);
}
}
printf("\n");
}
}
void Dijkstra(Array *a, int x, int y)
{
int i, j, k, min;
flag[x] = 1;
for(i=0; i<MAXSIZE; i++)
{
D[i] = (*a).weight[x][i];
}
for(j=1; j<MAXSIZE; j++)
{
min = 1000;
for(i=0; i<MAXSIZE; i++)
{
if(!flag[i] && D[i] < min)
{
min = D[i];
k = i;
}
}
flag[k] = 1;
for(i=0; i<MAXSIZE; i++)
{
if(!flag[i] && min+(*a).weight[k][i] < D[i])
{
D[i] = min + (*a).weight[k][i];
P[i] = k;
}
}
}
printf("%c点到%c点的最小距离为%d",(*a).vex[x], (*a).vex[y], D[y]);
}
int main(void)
{
Array a;
int x, y;
InitArray(&a);
Print(a);
printf("\n请输入起点下标与终点下标,以空格隔开:\n");
scanf("%d %d", &x, &y);
Dijkstra(&a, x, y);
return 0;
}
弗洛伊德算法(Floyd):
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 9
typedef struct _GraphNode
{
unsigned int value[MAXSIZE][MAXSIZE];
}GraphNode;
char vex[MAXSIZE];
void InitGraph(GraphNode *g)
{
FILE *fp;
int i = 0, j;
if((fp=fopen("c://Dijkstra.txt", "r")) == NULL)
{
printf("Can't open the file");
exit(1);
}
while( !feof(fp) )
{
for(j=0; j<MAXSIZE; j++)
{
fscanf(fp, "%d,", &(*g).value[i][j]);
//(*h).value[i][j] = (*g).value[i][j];
}
i++;
}
fclose(fp);
for(j=0; j<MAXSIZE; j++)
{
vex[j] = (char) (j+65);
}
}
void PrintGraph(GraphNode g)
{
int i, j;
printf("此图的权值矩阵为:\n");
for(i=0; i<MAXSIZE; i++)
{
printf("%c ", vex[i]);
for(j=0; j<MAXSIZE; j++)
{
if(1000 == g.value[i][j])
{
printf("* ");
}
else
{
printf("%d ", g.value[i][j]);
}
}
printf("\n");
}
}
void Floyd(GraphNode *g, int x, int y)
{
GraphNode D, P;
int i, j, k, v;
//D数组(最短距离记录数组)和P数组(存放前驱顶点)初始化
for(i=0; i<MAXSIZE; i++)
{
for(j=0; j<MAXSIZE; j++)
{
D.value[i][j] = (*g).value[i][j];
P.value[i][j] = j;
}
}
for(k=0; k<MAXSIZE; k++)
{
for(i=0; i<MAXSIZE; i++)
{
for(j=0; j<MAXSIZE; j++)
{
if( D.value[i][j] > D.value[i][k] + D.value[k][j] )
{
D.value[i][j] = D.value[i][k] + D.value[k][j]; //对值进行比较记录
P.value[i][j] = P.value[i][k]; //存放其前驱结点的数字
}
}
}
}
printf("\n从%c点到%c点的最短距离为: %d\n", vex[x], vex[y], D.value[x][y]);
printf("\n路径从终点到起点依次为:\n");
v = P.value[x][y];
printf("%c->", vex[x]);
while( v != y )
{
printf("%c->", vex[v]);
v = P.value[v][y];
}
printf("%c\n", vex[y]);
}
int main()
{
GraphNode g;
int x, y;
InitGraph(&g);
PrintGraph(g);
printf("\n请输入起点和终点下标:\n");
scanf("%d %d", &x, &y);
Floyd(&g, x, y);
return 0;
}
拓扑排序:
DAG图:一个无环的有向图
活动:工程或者流程当中的若干个小阶段
在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称为AOV网
拓扑序列:在G=(V, E)是一个具有n个顶点的有向图,V中的顶点序列V1,V2,...,Vn满足若从顶点Vi到Vj有一条路径,则在顶点序列中顶点Vi必在顶点Vj之前,则我们称这样的顶点序列为一个拓扑序列
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 15
typedef struct _TableNode
{
unsigned int num;
struct _TableNode *next;
}TableNode;
typedef struct _ArrayNode
{
int data;
unsigned int in;
TableNode *first;
}ArrayNode;
typedef struct
{
ArrayNode Gra[MAXSIZE];
}ArrayList;
void InitList(ArrayList *g)
{
FILE *fp;
TableNode *temp, *target;
int i = 0, j, k;
if( (fp = fopen("c://course.txt", "r")) == NULL )
{
printf("Can not open the file!\n");
exit(1);
}
while( !feof(fp) )
{
fscanf(fp, "%d,%d,%d", &(*g).Gra[i].in, &(*g).Gra[i].data, &j);
(*g).Gra[i].first = NULL;
target = (*g).Gra[i].first;
while( j != 0 )
{
temp = (TableNode *)malloc(sizeof(TableNode));
if( !temp )
{
printf("分配动态内存失败!\n");
exit(1);
}
printf("请输入C%d的下一个顶点下标:\n", (*g).Gra[i].data);
scanf("%d", &k);
temp->num = k;
temp->next = NULL;
if((*g).Gra[i].first == NULL)
{
(*g).Gra[i].first = temp;
}
else
{
target->next = temp;
}
target = temp;
j--;
}
i++;
}
fclose(fp);
}
void Print(ArrayList g)
{
TableNode *target;
int i;
printf("\n此图的邻接表为:\n");
for(i=0; i<MAXSIZE; i++)
{
target = g.Gra[i].first;
printf("%d,C%d ", g.Gra[i].in, g.Gra[i].data);
while(target != NULL)
{
if(target->next == NULL)
{
printf("%d", target->num);
}
else
{
printf("%d->", target->num);
}
target = target->next;
}
printf("\n");
}
}
void TopoSort(ArrayList *g)
{
TableNode *target;
int top = 0, count = 0;
int i, value, n;
int *stack;
stack = (int *)malloc(MAXSIZE * sizeof(int));
for(i=0; i<MAXSIZE; i++)
{
if( 0 == (*g).Gra[i].in )
{
stack[++top] = i;
}
}
while( 0 != top ) //因此判断条件,不能使用stack[0]这个元素存储数据
{
value = stack[top--];
printf("C%d->", (*g).Gra[value].data);
count++;
target = (*g).Gra[value].first;
while(target != NULL)
{
n = target->num - 1;
(*g).Gra[n].in--;
if( (*g).Gra[n].in == 0 )
{
stack[++top] = n;
}
target = target->next;
}
}
if( count < MAXSIZE )
{
printf("此图有回环!\n");
}
else
{
printf("拓扑结束\n");
}
}
int main(void)
{
ArrayList g;
InitList(&g);
Print(g);
TopoSort(&g);
return 0;
}
关键路径:
AOE网:在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间
etv:事件最早发生时间(从前往后算)
ltv:事件最晚发生时间(从后往前算)
ete:活动最早发生时间(从前往后算)
lte:活动最晚发生时间(从后往前算)
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 9
typedef struct _LinkNode
{
int num;
int weight;
struct _LinkNode *next;
}LinkNode;
typedef struct _ArrayNode
{
int in;
int data;
LinkNode *first;
}ArrayNode;
typedef struct _ArrayList
{
ArrayNode vex[MAXSIZE];
}ArrayList;
int *etv, *ltv;
int *stack2;
int top2 = 0;
void InitList(ArrayList *g)
{
FILE *fp;
LinkNode *temp, *target;
int i = 0;
int j;
if( (fp=fopen("c://CriPath.txt", "r"))==NULL )
{
printf("Can not open the file!\n");
exit(1);
}
while(!feof(fp))
{
fscanf(fp, "%d,%d,%d", &g->vex[i].data, &g->vex[i].in, &j);
g->vex[i].first = NULL;
target = g->vex[i].first;
while(j)
{
temp = (LinkNode *)malloc(sizeof(LinkNode));
printf("请输入与顶点%d相连的点的下标以及它们之间的边的值:\n", g->vex[i].data);
scanf("%d %d", &temp->num, &temp->weight);
temp->next = NULL;
if(g->vex[i].first == NULL)
{
g->vex[i].first = temp;
}
else
{
target->next = temp;
}
target = temp;
j--;
}
i++;
}
}
void PrintList(ArrayList g)
{
LinkNode *target;
int i;
printf("\n此图的邻接表形式为:\n");
for(i=0; i<MAXSIZE; i++)
{
printf("%d %d ", g.vex[i].data, g.vex[i].in);
target = g.vex[i].first;
while(target)
{
printf("->(%d,%d)", target->num, target->weight);
target = target->next;
}
printf("\n");
}
}
void TopoSort(ArrayList *g)
{
LinkNode *target;
int *stack;
int top = 0, count = 0;
int i, j, value;
stack = (int *)malloc(MAXSIZE * sizeof(int));
for(i=0; i<MAXSIZE; i++)
{
if(g->vex[i].in == 0)
{
stack[++top] = i;
}
}
etv = (int *)malloc(MAXSIZE * sizeof(int));
stack2 = (int *)malloc(MAXSIZE * sizeof(int));
for(i=0; i<MAXSIZE; i++)
{
etv[i] = 0;
}
while(top != 0)
{
value = stack[top--];
stack2[++top2] = value;
count++;
target = g->vex[value].first;
while(target)
{
j = target->num - 1;
if((--g->vex[j].in) == 0)
{
stack[++top] = j;
}
if(etv[value] + target->weight > etv[j])
{
etv[j] = etv[value] + target->weight;
}
target = target->next;
}
}
if(count < MAXSIZE)
{
printf("\n存在回环!\n");
}
else
{
printf("\n排序完毕,不存在回环\n");
}
}
void CriPath(ArrayList *g)
{
LinkNode *target;
int i, j, value;
int ete, lte;
TopoSort(g);
//初始化ltv
ltv = (int *)malloc(MAXSIZE * sizeof(int));
for(i=0; i<MAXSIZE; i++)
{
ltv[i] = etv[MAXSIZE-1];
}
//从汇点倒过来逐个计算ltv
while(top2 != 0)
{
value = stack2[top2--];
for(target=g->vex[value].first; target != NULL; target = target->next)
{
j = target->num - 1;
if((ltv[j] - target->weight) < ltv[value])
{
ltv[value] = ltv[j] - target->weight;
}
}
}
//通过etv和ltv求ete和lte
for(i=0; i<MAXSIZE; i++)
{
for(target=g->vex[i].first; target != NULL; target = target->next)
{
j = target->num - 1;
ete = etv[i];
lte = ltv[j] - target->weight;
if(ete == lte)
{
printf("(%d,%d) length:%d , ", g->vex[i].data, g->vex[j].data, target->weight);
}
printf("\n");
}
}
}
int main()
{
ArrayList g;
InitList(&g);
PrintList(g);
CriPath(&g);
return 0;
}