@yanglt7
2018-10-21T15:49:06.000000Z
字数 15161
阅读 772
数据结构和算法
对于图的定义,需要明确几个注意的地方:
无向边
有向边
简单图
无向完全图
有向完全图
稀疏图和稠密图
有些图的边或弧带有与它相关的数字,这种与图的边或弧相关的数叫做权(Weight),带权的图通常称为网(Network)。
子图
邻接点、度
路径
连通图
连通图的生成树
对称矩阵:n阶矩阵的元满足a[i][j]=a[j][i](0<=i,j<=n)。即从矩阵的左上角到右下角的主对角线为轴,右上角的元与左下角相对应的元全都是相等的。
作用:
| 顶点数组: | V0 | V1 | V2 | V3 |
|---|
| ~ | V0 | V1 | V2 | V3 |
|---|---|---|---|---|
| V0 | 0 | 1 | 1 | 1 |
| V1 | 1 | 0 | 1 | 0 |
| V2 | 1 | 1 | 0 | 1 |
| V3 | 1 | 0 | 1 | 0 |
// 邻接矩阵(无向图)#include <stdio.h>#define n 4#define M 4#define N 4int main(){int i=0, j=0, k=0;int c;int Vertex[n];int a[M][N];printf("请输入顶点: ,以-1作为结束标志!\n");scanf("%d", &c);while( c != -1 ){Vertex[++i] = c;Vertex[i] = '\0';scanf("%d", &c);}printf("请输入顶点之间边的关系: 1表示两个顶点之间有边,0表示两个顶点之间无边。\n");for( i=0; i < M; i++ ){for( j=0; j < N; j++ ){printf("%d 和 %d 之间的关系为: ", i, j);scanf("%d", &a[i][j]);printf("%d\n", a[i][j]);}}printf("邻接矩阵(无向图)为: \n");for( i=0; i < M; i++ ){for( j=0; j < N; j++ ){printf("%d\t", a[i][j]);k++;}if( k%4 == 0 ){printf("\n");}}return 0;}
| 顶点数组: | V0 | V1 | V2 | V3 |
|---|
| ~ | V0 | V1 | V2 | V3 |
|---|---|---|---|---|
| V0 | 0 | ∞ | ∞ | 18 |
| V1 | 8 | ∞ | 2 | ∞ |
| V2 | 4 | ∞ | 0 | ∞ |
| V3 | ∞ | ∞ | ∞ | 0 |
| 下标 | data | first |
|---|---|---|
| 0 | V0 | |
| 1 | V1 | |
| 2 | V2 | |
| 3 | V3 |
// 邻接表(无向图)#include<stdio.h>#include<stdlib.h>#define MAX_VERTEX_NUM 100typedef struct {int adjvex; //该边的另一个顶点的位置struct ArcNode *nextarc; //指向下一条边} ArcNode;typedef struct{int data; // 顶点的值ArcNode *firstarc; // 指向第一条依附该顶点的边的指针} VNode, AdjList[MAX_VERTEX_NUM];typedef struct{AdjList vertices; //顶点数组int vexnum, arcnum;} ALGraph;//定位函数int LocateVex(ALGraph G, int v){int i=0;for( i=0; i < G.vexnum; i++ ){if( v == G.vertices[i].data )return i;}}void CreateUDG( ALGraph G ){ArcNode *p,*q;int i, j, k, v1, v2;printf("分别输入顶点个数和边的数目:\n");scanf("%d%d", &G.vexnum, &G.arcnum);printf("分别输入各个顶点值:\n");for( i=0; i < G.vexnum; i++ ){scanf("%d", &G.vertices[i].data);G.vertices[i].firstarc = NULL; //初始化}printf("分别输入各条边的两个顶点:\n");for( k=0; k < G.arcnum; k++ ){scanf("%d%d", &v1, &v2);//定位i = LocateVex(G,v1);j = LocateVex(G,v2);//申请一个结点p = (ArcNode*)malloc(sizeof(ArcNode));//赋值p->adjvex = j;p->nextarc = NULL;//连接结点p->nextarc = G.vertices[i].firstarc;G.vertices[i].firstarc = p;q = (ArcNode*)malloc(sizeof(ArcNode));q->adjvex = i;q->nextarc = NULL;q->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = q;}}//输出邻接表void PrintUDG(ALGraph G){int i, j;for( i=0; i < G.vexnum; i++ ){printf("%d:", i);ArcNode *p;p = G.vertices[i].firstarc;while( p != NULL ){printf("->%d", p->adjvex);p = p->nextarc;}printf("\n");}}int main(){ALGraph G;CreateUDG(G);PrintUDG(G);return 0;}
| 下标 | data | first |
|---|---|---|
| 0 | V0 | |
| 1 | V1 | |
| 2 | V2 | |
| 3 | V3 |
| 下标 | data | first |
|---|---|---|
| 0 | V0 | |
| 1 | V1 | |
| 2 | V2 | |
| 3 | V3 |
| data | firstIn | firstOut |
|---|
| iVex | iLink | jVex | jLink |
|---|
| 下标 | data | first |
|---|---|---|
| 0 | V0 | |
| 1 | V1 | |
| 2 | V2 | |
| 3 | V3 |
| 顶点数组: | V0 | V1 | V2 | V3 |
|---|
| 边数组 | begin | end | weight |
|---|---|---|---|
| edges[0] | 0 | 3 | 5 |
| edges[1] | 1 | 0 | 4 |
| edges[2] | 1 | 2 | 3 |
| edges[3] | 2 | 0 | 8 |
// 边集数组#include <stdio.h>#define VertexNum 4#define EdgesNum 4int main(){int i=0, j=0, k=0, n=0, m=0;int c;int Vertex[VertexNum];int Begin[EdgesNum];int End[EdgesNum];int Weight[EdgesNum];// 存储顶点信息printf("请输入顶点: ,以-1作为结束标志!\n");scanf("%d", &c);while( c != -1 ){Vertex[++i] = c;Vertex[i] = '\0';scanf("%d", &c);}// 存储边的begin数值for( j=0; j < EdgesNum; j++ ){printf("请输入第 %d 条边的起点下标begin:\n", j);scanf("%d", &Begin[j]);}// 存储边的end数值for( k=0; k < EdgesNum; k++ ){printf("请输入第 %d 条边的终点下标end:\n", k);scanf("%d", &End[k]);}// 存储边的weight数值for( n=0; n < EdgesNum; n++ ){printf("请输入第 %d 条边的权值weight:\n", n);scanf("%d", &Weight[n]);}// 打印边集数组printf("边集数组信息为: 起点下标 终点下标 权值\n");for( m=0; m < EdgesNum; m++ ){printf("Edges[%d]\t%d\t %d\t %d\n", m, Begin[m], End[m], Weight[m]);}return 0;}
题目要求
回溯法:一般和递归可以很好的搭配使用,还有深度优先搜索。
// 马踏棋盘算法#include <stdio.h>#include <time.h>#define X 8#define Y 8int chess[X][Y];//找到基于(x,y)位置的下一个可走的位置int nextxy(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+1<=X-1 && *y-2>=0 && chess[*x+1][*y-2]==0 ){*x += 1;*y -= 2;return 1;}break;case 3:if( *x+1<=X-1 && *y+2<=Y-1 && chess[*x+1][*y+2]==0 ){*x += 1;*y += 2;return 1;}break;case 4:if( *x-2>=0 && *y-1>=0 && chess[*x-2][*y-1]==0 ){*x -= 2;*y -= 1;return 1;}break;case 5:if( *x-2>=0 && *y+1<=Y-1 && chess[*x-2][*y+1]==0 ){*x -= 2;*y += 1;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 print(){int i, j;for( i=0; i < X; i++ ){for( j=0; j < Y; j++ ){printf("%2d\t", chess[i][j]);}printf("\n");}printf("\n");}//深度优先遍历棋盘//(x,y)为位置坐标//tag是标记变量,每走一步,tag+1int TravelChessBoard(int x, int y, int tag){int x1=x, y1=y, flag=0, count=0;chess[x][y] = tag;if( X*Y == tag ){//打印棋盘print();return 1;}//找到马的下一个可走的坐标(x1,y1),如果找到flag=1,否则为0flag = nextxy(&x1, &y1, count);while( 0==flag && count<7 ){count ++;flag = nextxy(&x1, &y1, count);}while( flag ){if( TravelChessBoard(x1, y1, tag+1) ){return 1;}//找到马的下一个可走的坐标(x1,y1),如果找到flag=1,否则为0x1 = x;y1 = y;count++;flag = nextxy(&x1, &y1, count);while( 0==flag && count<7 ){count ++;flag = nextxy(&x1, &y1, count);}}if( 0 == flag ){chess[x][y] = 0;}return 0;}int main(){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("抱歉,马踏棋盘失败了!\n");}finish = clock();printf("\n本次计算一共耗时: %f秒\n\n", (double)(finish-start)/CLOCKS_PER_SEC);return 0;}
| ~ | V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 |
|---|---|---|---|---|---|---|---|---|---|
| V0 | 0 | 10 | ∞ | ∞ | ∞ | 11 | ∞ | ∞ | ∞ |
| V1 | 10 | 0 | 18 | ∞ | ∞ | ∞ | 16 | ∞ | 12 |
| V2 | ∞ | 18 | 0 | 22 | ∞ | ∞ | ∞ | ∞ | 8 |
| V3 | ∞ | ∞ | 22 | 0 | 20 | ∞ | ∞ | 16 | 21 |
| V4 | ∞ | ∞ | ∞ | 20 | 0 | 26 | ∞ | 7 | ∞ |
| V5 | 11 | ∞ | ∞ | ∞ | 26 | 0 | 17 | ∞ | ∞ |
| V6 | ∞ | 16 | ∞ | ∞ | ∞ | 17 | 0 | 19 | ∞ |
| V7 | ∞ | ∞ | ∞ | 16 | 7 | ∞ | 19 | 0 | ∞ |
| V8 | ∞ | 12 | 8 | 21 | ∞ | ∞ | ∞ | ∞ | 0 |
// Prim算法生成最小生成树void MiniSpanTree_Prim(MGraph G){int min, i, j, k;int adjvex[MAXVEX]; // 保存相关顶点下标int lowcost[MAXVEX]; // 保存相关顶点间边的权值lowcost[0] = 0; // V0作为最小生成树的根开始遍历,权值为0adjvex[0] = 0; // V0第一个加入// 初始化操作for( i=0; i < G.numVertexes; i++ ){lowcost[i] = G.arc[0][i]; // 将邻接矩阵第0行所有权值先加入数组adjvex[i] = 0; // 初始化全部先为V0的下标}// 真正构造最小生成树的过程for( i=0; i < G.numVertexes; i++ ){min = INFINITY; // 初始化最小权值为65535等不可能数值j = 1;k = 0;// 遍历全部顶点while( j < G.numVertexes ){// 找出lowcost数组已存储的最小权值if( lowcost[j] != 0 && lowcost[j] < min ){min = lowcost[j];k = j; // 将发现的最小权值的下标存入k,以待使用}j++;}// 打印当前顶点边中权值最小的边printf("(%d, %d)", adjvex[k], k);lowcost[k] = 0; // 将当前顶点的权值设置为0,表示此顶点已经完成任务,进行下一个顶点的遍历// 邻接矩阵k行逐个遍历全部顶点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;}}}}
| ~ | begin | end | weight |
|---|---|---|---|
| edges[0] | 4 | 7 | 7 |
| edges[1] | 2 | 8 | 8 |
| edges[2] | 0 | 1 | 10 |
| edges[3] | 0 | 5 | 11 |
| edges[4] | 1 | 8 | 12 |
| edges[5] | 3 | 7 | 16 |
| edges[6] | 1 | 6 | 16 |
| edges[7] | 5 | 6 | 17 |
| edges[8] | 1 | 2 | 18 |
| edges[9] | 6 | 7 | 19 |
| edges[10] | 3 | 4 | 20 |
| edges[11] | 3 | 8 | 21 |
| edges[12] | 2 | 3 | 22 |
| edges[13] | 3 | 6 | 24 |
// Kruskal算法生成最小生成树int Find(int *parent, int f){while( parent[f] > 0 ){f = parent[f];}return f;}void MiniSpanTree_Kruskal(MGraph G){int i, n, m;Edge edges[MAGEDGE]; // 定义边集数组int parent[MAXVEX]; // 定义parent数组用来判断边与边是否形成环路for( i=0; i < G.numVertexes; i++ ){parent[i] = 0;}for( i=0; i < G.numEdges; i++ ){n = Find(parent, edges[i].begin);m = Find(parent, edges[i].end);if( n!= m ) // 如果n==m,不满足!{parent[n] = m; // 将此边的结尾顶点放入下标为起点的parent数组中,表示此顶点已经在生成树集合中printf("(%d, %d) %d ", edges[i].begin, edges[i].end, edges[i].weight);}}}
| ~ | V0 | V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 |
|---|---|---|---|---|---|---|---|---|---|
| V0 | 0 | 1 | 5 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
| V1 | 1 | 0 | 3 | 7 | 5 | ∞ | ∞ | ∞ | ∞ |
| V2 | 5 | 3 | 0 | ∞ | 1 | 7 | ∞ | ∞ | ∞ |
| V3 | ∞ | 7 | ∞ | 0 | 2 | ∞ | 3 | ∞ | ∞ |
| V4 | ∞ | 5 | 1 | 2 | 0 | 3 | 6 | 9 | ∞ |
| V5 | ∞ | ∞ | 7 | ∞ | 3 | 0 | ∞ | 5 | ∞ |
| V6 | ∞ | ∞ | ∞ | 3 | 6 | ∞ | 0 | 2 | 7 |
| V7 | ∞ | ∞ | ∞ | ∞ | 9 | 5 | 2 | 0 | 4 |
| V8 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | 7 | 4 | 0 |
D: 0 1 4 7 5 8 10 12 16
P: 0 0 1 4 2 4 3 6 7
final: 1 1 1 1 1 1 1 1 1
// dijkstra算法求最短路径#define MAXVEX 9#define INFINITY 65535typedef int Patharc[MAXVEX]; // 用于存储最短路径下标的数组typedef int ShortPathTable[MAXVEX]; // 用于存储到各点最短路径的权值和void ShortestPath_Dijkstra(MGragh G, int V0, Patharc *P, ShortPathTable *D){int v, w, k, min;int final[MAXVEX]; // final[w] = 1 表示已经求得顶点V0到Vw的最短路径// 初始化数据for( v=0; v < G.numVertexes; v++ ){final[v] = 0; // 全部顶点初始化为未找到最短路径(*D)[V] = G.arc[V0][v]; // 将与V0点有连线的顶点加上权值(*P)[V] = 0; // 初始化路径数组P为0}(*D)[V0] = 0; // V0至V0的路径为0final[V0] = 1; // V0至V0不需要路径// 开始主循环,每次求得V0到某个v顶点的最短路径for( v=1; v < G.numVertexes; v++ ){min = INFINITY;for( w=0; w < G.numVertexes; w++ ){if( !final[w] && (*D)[w] < min ){k = w;min = (*D)[w];}}final[k] = 1; // 将目前找到的最近的顶点置1for( w=0; w < G.numVertexes; w++ ){// 如果经过v顶点的路径比现在这条路径的长度短的话,更新if( !final[w] && (min+G.arc[k][w] < (*D)[w]) ){(*D)[w] = min + G.arc[k][w]; // 修改当前路径长度(*p)[w] = k; // 存放前驱顶点}}}}
// 弗洛伊德算法求最短路径#define MAXVEX 9#define INFINITY 65535typedef int Pathmatirx[MAXVEX][MAXVEX];typedef int ShortPathTable[MAXVEX][MAXVEX];void ShortestPath_Floyd(MGraph G, Pathmatirx *P, ShortPathTable *D){int v, w,k;// 初始化D和Pfor( v=0; v < G.numVertexes; v++ ){for( w=0; w < G.numVertexes; w++ ){(*D)[v][w] = G.matirx[v][w];(*P)[v][w] = w;}}// 优美的弗洛伊德算法for( k=0; k < G.numVertexes; k++ ){for( v=0; v < G.numVertexes; v++ ){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];}}}}
| 下标 | in | data | first |
|---|---|---|---|
| 0 | 0 | C1 | |
| 1 | 2 | C2 | |
| 2 | 2 | C3 | |
| 3 | 2 | C4 | |
| 4 | 1 | C5 | |
| 5 | 1 | C6 | |
| 6 | 3 | C7 | |
| 7 | 1 | C8 | |
| 8 | 1 | C9 | |
| 9 | 2 | C10 | |
| 10 | 1 | C11 | |
| 11 | 1 | C12 | |
| 12 | 0 | C13 | |
| 13 | 1 | C14 | |
| 14 | 1 | C15 |
// 拓扑排序算法// 边表结点声明typedef struct EdgeNode{int adjvex;struct EdgeNode *next;} EdgeNode;typedef struct VertexNode{int in; // 顶点入度int data;EdgeNode *firstedge;} VertexNode, AdjList[MAXVEX];typedef struct{AdjList adjList;int numVertexes, numEdges;} GraphAdjList, *GraphAdjList;// 拓扑排序算法// 若GL无回路,则输出拓扑排序序列并返回OK,否则返回ERRORStatus TopologicalSort(GraphAdjList GL){EdgeNode *e;int i, k, gettop;int top = 0; // 用于栈指针下标索引int count = 0; // 用于统计输出顶点的个数int *stack; // 用于存储入度为0的顶点stack = (int *)malloc(GL->numVertexes * sizeof(int));for( i=0; i < GL->numVertexes; i++ ){if( 0 == GL->adjList[i].in ){stack[++top] = i; // 将入度为0的顶点下标入栈}}while( 0 != top ){gettop = stack[top--]; // 出栈printf("%d -> ", GL->adjList[gettop].data);count++;for( e=GL->adjList[gettop].firstedge; e; e=e->next ){k = e->adjvex;// 注意:下边这个if条件是分析整个程序的要点// 将k号顶点邻接点的入度为-1,因为它的前驱已经消除// 接着判断-1后入度是否为0,如果为0则也入栈if( !(--GL->adjList[k].in) ){stack[++top] = k;}}}if( count < GL->numVertexes ) //如果count小于顶点数,说明存在环{return ERROR;}else{return OK;}}
| 顶点 | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 |
|---|---|---|---|---|---|---|---|---|---|
| etv | 0 | 6 | 4 | 5 | 7 | 7 | 14 | 12 | 16 |
| ltv | 0 | 6 | 6 | 6 | 7 | 8 | 14 | 12 | 16 |
| 弧 | a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 | a9 | a10 | a11 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| ete | 0 | 0 | 0 | 6 | 4 | 5 | 7 | 7 | 7 | 14 | 12 |
| lte | 0 | 0 | 2 | 6 | 6 | 6 | 7 | 7 | 8 | 14 | 12 |
| 下标 | in | data | first |
|---|---|---|---|
| 0 | 0 | C1 | |
| 1 | 1 | C2 | |
| 2 | 1 | C3 | |
| 3 | 1 | C4 | |
| 4 | 2 | C5 | |
| 5 | 1 | C6 | |
| 6 | 1 | C7 | |
| 7 | 2 | C8 | |
| 8 | 2 | C9 |
// 关键路径// 边表结点声明typedef struct EdgeNode{int adjvex;struct EdgeNode *next;} EdgeNode;// 顶点表结点声明typedef struct VertexNode{int in; // 顶点入度int data;EdgeNode *firstedge;} VertexNode, AdjList[MAXVEX];typedef struct{AdjList adjList;int numVertexes, numEdges;} GraphAdjList, *GraphAdjList;int *etv, *ltv;int *stack2; // 用于存储拓扑序列的栈int top2; // 用于stack2的栈顶指针// 拓扑排序算法// 若GL无回路,则输出拓扑排序序列并返回OK,否则返回ERRORStatus TopologicalSort(GraphAdjList GL){EdgeNode *e;int i, k, gettop;int top = 0; // 用于栈指针下标索引int count = 0; // 用于统计输出顶点的个数int *stack; // 用于存储入度为0的顶点stack = (int *)malloc(GL->numVertexes * sizeof(int));for( i=0; i < GL->numVertexes; i++ ){if( 0 == GL->adjList[i].in ){stack[++top] = i; // 将度为0的顶点下标入栈}}// 初始化etv都为0top2 = 0;etv = (int *)malloc(GL->numVertexes * sizeof(int));for( i=0; i < GL->numVertexes; i++ ){etv[i] = 0;}stack2 = (int *)malloc(GL->numVertexes * sizeof(int));while( 0 != top ){gettop = stack[top--]; // 出栈printf("%d -> ", GL->adjList[gettop].data);count++;for( e=GL->adjList[gettop].firstedge; e; e=e->next ){k = e->adjvex;// 注意:下边这个if条件是分析整个程序的要点// 将k号顶点邻接点的入度为-1,因为它的前驱已经消除// 接着判断-1后入度是否为0,如果为0则也入栈if( !(--GL->adjList[k].in) ){stack[++top] = k;}if( (etv[gettop] + e->weight) > etv[k] ){etv[k] = etv[gettop] + e->weight;}}}if( count < GL->numVertexes ) //如果count小于顶点数,说明存在环{return ERROR;}else{return OK;}}// 关键路径,GL为有向图,输出GL的各项关键活动void CriticalPath(GraphAdjList GL){EdgeNode *e;int i, gettop, k, j;int ete, lte;// 调用改进后的拓扑排序,求出etv和stack2的值TopologicalSort(GL);// 初始化ltv都为汇点的时间ltv = (int *)malloc(GL->numVertexes * sizeof(int));for( i=0; i < GL->numVertexes; i++ ){ltv[i] = etv[GL->numVertexes - 1];}// 从汇点倒过来逐个计算ltvwhile( 0 != top2 ){gettop = stack2[top2--]; // 注意,第一个出栈是汇点for( e=GL->adjList[gettop].firstedge; e; e=e->next ){k = e->adjvex;if( ltv[k] - e->weight < ltv[gettop] ){ltv[gettop] = ltv[k] - e->weight;}}}// 通过etv和ltv求ete和ltefor( j=0; j < Gl->numVertexes; j++ ){for( e=GL->adjList[j].firstedge; e; e=e->next ){k = e->adjvex;ete = etv[j];lte = ltv[k] - e->weight;if( ete == lte ){printf("<v%d, v%d> length: %d, ", GL->adjList[j].data, GL->adjList[k].data, e->weight);}}}}