《数据结构(C语言版)第二版》第六章-图(算法设计题)

习题1

分别以邻接矩阵和邻接表作为存储结构,实现以下图的基本操作:
①增加一个新顶点v, InsertVex(G, v);
②删除顶点v及其相关的边,DeleteVex(G,v);
③增加一条边<v, w>, InsertArc(G, v, w);
④删除一条边<v, w>, DeleteArc(G, v, w)。

以邻接矩阵作为存储结构(有向图)

#include <stdio.h>
#include <stdlib.h>

#define MaxInt 32767
#define MVNum 100

typedef char VerTexType;
typedef int ArcType;

typedef struct
{
	VerTexType vexs[MVNum];
	ArcType arcs[MVNum][MVNum];
	int vexnum;
	int arcnum;
}AMGraph;

void CreateDG(AMGraph& G);
int LocateVex(AMGraph& G, VerTexType v);
void printfAMGraph(AMGraph& G);
void InsertVex(AMGraph& G, VerTexType v);
void DeleteVex(AMGraph& G, VerTexType v);
void InsertArc(AMGraph& G, VerTexType v, VerTexType w);
void DeleteArc(AMGraph& G, VerTexType v, VerTexType w);


int main()
{
	AMGraph G = { {'\0'}, {0}, 0, 0 };

	CreateDG(G);
	printfAMGraph(G);

	printf("\n\n增加一个点'5'后:\n");
	InsertVex(G, '5');
	printfAMGraph(G);

	printf("\n\n删除一个点'4'后:\n");
	DeleteVex(G, '4');
	printfAMGraph(G);

	printf("\n\n增加一条弧<5,2>后:\n");
	InsertArc(G, '5', '2');
	printfAMGraph(G);

	printf("\n\n删除一条弧<5,2>后:\n");
	DeleteArc(G, '5', '2');
	printfAMGraph(G);

	return 0;
}

//采用邻接矩阵表示法创建有向图
void CreateDG(AMGraph& G)
{
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcType w = 0;

	printf("请输入有向图的总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入有向图的总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个顶点储存的元素:", i + 1);
		scanf_s(" %c", &G.vexs[i], sizeof(VerTexType));
	}

	//初始化邻接矩阵,将边的权值均置为0
	for (i = 0; i < G.vexnum; ++i)
	{
		for (j = 0; j < G.vexnum; ++j)
		{
			G.arcs[i][j] = 0;
		}
	}

	//构造邻接矩阵
	for (k = 0; k < G.arcnum; ++k)
	{
		printf("请输入第%d条边依附的两个顶点(用空格间隔,输入结束后按回车) : ", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);
		G.arcs[i][j] = 1;
	}
}

//在G的顶点表vexs中获取字符v的下标(数组G.vexs的下标从0开始)
int LocateVex(AMGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.vexs[i] != v); ++i)
	{
		;
	}

	return i;
}

//打印邻接矩阵表示法中的顶点表vexs和邻接矩阵arcs
void printfAMGraph(AMGraph& G)
{
	int i = 0;
	int j = 0;

	printf("各顶点为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c ", G.vexs[i]);
	}

	printf("\n邻接矩阵为:\n");
	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			if (G.arcs[i][j] == 32767)
			{
				printf("%d  ", G.arcs[i][j]);
			}
			else
			{
				printf("%d      ", G.arcs[i][j]);
			}

		}
		printf("\n");
	}
}

//增加一个新顶点v, InsertVex(G, v)
void InsertVex(AMGraph &G, VerTexType v)
{
	G.vexs[G.vexnum] = v;
	G.vexnum++;
}


//删除顶点v及其相关的边,DeleteVex(G, v)
void DeleteVex(AMGraph& G, VerTexType v)
{
	int k = 0;
	k = LocateVex(G, v);

	int i = 0;
	int j = 0;
	int m = k;

	//从第k+2个元素起,数组vexs中的每一个元素都向前移动一个
	for (i = k+1,m = k; i < G.vexnum; i++,m++)
	{
		G.vexs[m] = G.vexs[i];
	}
	
	//从第k+1行起,邻接矩阵中的每一行都向上移一行
	for (i = k + 1, m = k; i < G.vexnum; i++,m++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			G.arcs[m][j] = G.arcs[i][j];
		}
	}

	//从第k+1列起,邻接矩阵中的每一行都向前移一列
	for (j = k + 1, m = k; j < G.vexnum; j++, m++)
	{
		for (i = 0; i < G.vexnum; i++)
		{
			G.arcs[i][m] = G.arcs[i][j];
		}
	}

	//最后一行全部变为0
	for (i = 0; i < G.vexnum; i++)
	{
		G.arcs[G.vexnum - 1][i] = 0;
	}

	//最后一列全部变为0
	for (i = 0; i < G.vexnum; i++)
	{
		G.arcs[i][G.vexnum - 1] = 0;
	}

	G.vexnum--;
}


//增加一条边<v, w>, InsertArc(G, v, w); 
void InsertArc(AMGraph &G, VerTexType v, VerTexType w)
{
	int i = LocateVex(G, v);
	int j = LocateVex(G, w);

	G.arcs[i][j] = 1;
	G.arcnum++;
}



//删除一条边<v, w>, DeleteArc(G, v, w)。
void DeleteArc(AMGraph &G, VerTexType v, VerTexType w)
{
	int i = LocateVex(G, v);
	int j = LocateVex(G, w);

	G.arcs[i][j] = 0;
	G.arcnum--;
}

在这里插入图片描述
在这里插入图片描述

以邻接表作为存储结构(无向图)

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100

typedef int OtherInfo;   //假设在边结点中的数据域存储边的权值,且边的权值类型为整型
typedef char VerTexType;  //假设顶点的数据类型为字符型

typedef struct ArcNode
{
	int adjvex;
	struct ArcNode* nextarc;
	OtherInfo info;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode, AdjList[MVNum];  //表头结点

typedef struct
{
	AdjList vertices;  //存储所有顶点
	int vexnum;  //顶点总数
	int arcnum;  //总边数
}ALGraph;  //图的信息

void CreateUDG(ALGraph& G);
int LocateVex(ALGraph& G, VerTexType v);
void printALGraph(ALGraph& G);
void InsertVex(ALGraph& G, VerTexType v);
void DeleteVex(ALGraph& G, VerTexType v);
void InsertArc(ALGraph& G, VerTexType v, VerTexType w);
void DeleteArc(ALGraph& G, VerTexType v, VerTexType w);

int main()
{
	ALGraph G = { {'\0',NULL}, 0,0 };
	CreateUDG(G);
	printALGraph(G);

	printf("\n\n\n增加一个点'6'后:\n");
	InsertVex(G, '6');
	printALGraph(G);

	printf("\n\n\n删除一个点'4'后:\n");
	DeleteVex(G, '4');
	printALGraph(G);

	printf("\n\n\n增加一条边(6,2)后:\n");
	InsertArc(G, '6', '2');
	printALGraph(G);

	printf("\n\n\n删除一条边(6,2)后:\n");
	DeleteArc(G, '6', '2');
	printALGraph(G);

	return 0;
}

void CreateUDG(ALGraph& G)
{
	int i = 0;
	int k = 0;
	VerTexType v1 = 0;
	VerTexType v2 = 0;
	int j = 0;
	ArcNode* p1 = NULL;
	ArcNode* p2 = NULL;

	printf("请输入无向图的总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入无向图的总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &(G.vertices[i].data));
		G.vertices[i].firstarc = NULL;   //初始化
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边依附的两个顶点(用空格间隔,输入结束后按回车): ", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p1 = (ArcNode*)malloc(sizeof(ArcNode));
		p1->adjvex = j;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;  //将新结点*p1插入顶点Vi的边表头部

		p2 = (ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex = i;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;  //将与*p1对称的新的边结点*p2插入顶点Vj的边表头部
	}
}


//在G的顶点表vexs中获取字符v的下标(数组G.vexs的下标从0开始)
int LocateVex(ALGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.vertices[i].data != v); ++i)
	{
		;
	}

	return i;
}


//打印邻接表
void printALGraph(ALGraph& G)
{
	int i = 0;
	ArcNode* pMove = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1, G.vertices[i].data);

		pMove = G.vertices[i].firstarc;
		if (pMove)
		{
			printf("\n与第%d个顶点相邻接的每个顶点为:", i + 1);
			while (pMove)
			{
				printf("%c ",G.vertices[pMove->adjvex].data);
				pMove = pMove->nextarc;
			}
		}
		else
		{
			printf("\n没有与第%d个顶点相邻接的顶点。", i + 1);
		}
	}
}


//增加一个新顶点v, InsertVex(G, v)
void InsertVex(ALGraph&G, VerTexType v)
{
	G.vertices[G.vexnum].data = v;
	G.vertices[G.vexnum].firstarc = NULL;
	G.vexnum++;
}


//删除顶点v及其相关的边,DeleteVex(G, v)
void DeleteVex(ALGraph& G, VerTexType v)
{
	int i = 0;
	int j = 0;
	int k = 0;
	int m = 0;
	ArcNode* p = NULL;
	int num = 0;

	k = LocateVex(G, v);

	for (i = 0; i < G.vexnum; i++)
	{
		p = G.vertices[i].firstarc;

		if (i != k && p)
		{
			if (p && p->adjvex == k)
			{
				G.vertices[i].firstarc = p->nextarc;
				free(p);
				p = NULL;
			}
			else
			{
				ArcNode* Pre = p;

				while (p && p->adjvex != k)
				{
					Pre = p;
					p = p->nextarc;
				}

				if (!p)
				{
					continue;
				}
				else
				{
					Pre->nextarc = p->nextarc;
					free(p);
					p = NULL;
				}
			}
		}

		if (i == k)
		{
			while (p)
			{
				ArcNode* temp = p;
				p = p->nextarc;
				free(temp);
				temp = NULL;
			}
		}
	}

	G.vexnum--;

	//从第k+2个元素起,数组vexs中的每一个元素都向前移动一个
	for (i = k; i < G.vexnum; i++)
	{
		G.vertices[i]= G.vertices[i+1];
	}

	// 清空数组vexs中最后一个顶点的信息
	G.vertices[G.vexnum].data = '\0';
	G.vertices[G.vexnum].firstarc = NULL;

	//更新所有边表中的顶点索引
	/*  删除顶点后,顶点数组中的顶点被向前移动了一位,这意味着比被删除顶点索引高的顶点的索引都减少了 1。
	否则,它们会仍然指向旧的索引位置及顶点值。*/
	for (i = 0; i < G.vexnum; i++)
	{
		p = G.vertices[i].firstarc;
		while (p)
		{
			if (p->adjvex > k)
				p->adjvex--; // 如果该边指向的顶点索引大于k,则减1
			p = p->nextarc;
		}
	}
}



//增加一条边(v, w), InsertArc(G, v, w); 
void InsertArc(ALGraph& G, VerTexType v, VerTexType w)
{
	int i = LocateVex(G, v);
	int j = LocateVex(G, w);

	ArcNode* p = (ArcNode*)malloc(sizeof(ArcNode));
	p->adjvex = j;
	p->nextarc = G.vertices[i].firstarc;
	G.vertices[i].firstarc = p;

	ArcNode* q = (ArcNode*)malloc(sizeof(ArcNode));
	q->adjvex = i;
	q->nextarc = G.vertices[j].firstarc;
	G.vertices[j].firstarc = q;
		
	G.arcnum++;
}



//删除一条边(v, w), DeleteArc(G, v, w)。
void DeleteArc(ALGraph& G, VerTexType v, VerTexType w)
{
	int i = LocateVex(G, v);
	int j = LocateVex(G, w);

	ArcNode *p1 = G.vertices[i].firstarc;
	ArcNode *p2 = G.vertices[j].firstarc;

	if (p1->adjvex == j)
	{
		G.vertices[i].firstarc = p1->nextarc;
		free(p1);
		p1 = NULL;
	}
	else
	{
		ArcNode* pPre1 = NULL;
		while (p1->adjvex != j)
		{
			pPre1 = p1;
			p1 = p1->nextarc;
		}

		pPre1->nextarc = p1->nextarc;
		free(p1);
		p1 = NULL;
	}

	if (p2->adjvex == i)
	{
		G.vertices[j].firstarc = p2->nextarc;
		free(p2);
		p2 = NULL;
	}
	else
	{
		ArcNode* pPre2 = NULL;
		while (p2->adjvex != i)
		{
			pPre2 = p2;
			p2 = p2->nextarc;
		}

		pPre2->nextarc = p2->nextarc;
		free(p2);
		p2 = NULL;
	}

	G.arcnum--;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

习题2

一个连通图采用邻接表作为存储结构,设计一个算法,实现从顶点v出发的深度优先遍历的非递归过程。

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100

bool visited[MVNum];
//标记某个顶点是否被访问过。数组中初值均为 "false",如果被访问过,则置其相应的分量为 "true"

typedef int OtherInfo;   //假设在边结点中的数据域存储边的权值,且边的权值类型为整型
typedef char VerTexType;  //假设顶点的数据类型为字符型

typedef struct ArcNode
{
	int adjvex;
	struct ArcNode* nextarc;
	OtherInfo info;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode, AdjList[MVNum];  //表头结点

typedef struct
{
	AdjList vertices;  //存储所有顶点
	int vexnum;  //顶点总数
	int arcnum;  //总边数
}ALGraph;  //图的信息


typedef struct stackNode
{
	int data; //存放顶点的位置数
	struct stackNode* next;
}stackNode, * LinkStack;


void CreateUDG(ALGraph& G);
int LocateVex(ALGraph& G, VerTexType v);
void printALGraph(ALGraph& G);
void InitStack(LinkStack& S);
void Push(LinkStack& S, int e);
void Pop(LinkStack& S, int& e);
int EmptyStack(LinkStack S);
void DFS_Nonrecursive(ALGraph G, int v);

int main()
{
	ALGraph G = { {'\0',NULL}, 0,0 };
	int i = 0;
	int j = 0;

	CreateUDG(G);
	printALGraph(G);

	printf("\n");

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n从第%d个顶点开始,采用邻接矩阵表示图的深度优先搜索遍历序列为:", i + 1);
		DFS_Nonrecursive(G, i + 1);

		//将visited数组初始化(重置visited数组)
		for (j = 0; j < G.vexnum; j++)
		{
			visited[j] = false;
		}
	}

	return 0;
}

void CreateUDG(ALGraph& G)
{
	int i = 0;
	int k = 0;
	VerTexType v1 = 0;
	VerTexType v2 = 0;
	int j = 0;
	ArcNode* p1 = NULL;
	ArcNode* p2 = NULL;

	printf("请输入无向图的总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入无向图的总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &(G.vertices[i].data));
		G.vertices[i].firstarc = NULL;   //初始化
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边依附的两个顶点(用空格间隔,输入结束后按回车): ", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p1 = (ArcNode*)malloc(sizeof(ArcNode));
		p1->adjvex = j;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;  //将新结点*p1插入顶点Vi的边表头部

		p2 = (ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex = i;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;  //将与*p1对称的新的边结点*p2插入顶点Vj的边表头部
	}
}


//在G的顶点表vexs中获取字符v的下标(数组G.vexs的下标从0开始)
int LocateVex(ALGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.vertices[i].data != v); ++i)
	{
		;
	}

	return i;
}


//打印邻接表
void printALGraph(ALGraph& G)
{
	int i = 0;
	ArcNode* pMove = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1, G.vertices[i].data);

		pMove = G.vertices[i].firstarc;
		if (pMove)
		{
			printf("\n与第%d个顶点相邻接的每个顶点在图中的位置(下标值)为:", i + 1);
			while (pMove)
			{
				printf("%d ", pMove->adjvex);
				pMove = pMove->nextarc;
			}
		}
		else
		{
			printf("\n没有与第%d个顶点相邻接的顶点。", i + 1);
		}
	}
}

//初始化栈
void InitStack(LinkStack& S)
{
	S = NULL;
}

//入栈
void Push(LinkStack& S, int e)
{
	struct stackNode* p = (struct stackNode*)malloc(sizeof(struct stackNode));
	if (!p)
	{
		printf("元素入栈时,内存分配失败。\n");
		return;
	}

	p->data = e;
	p->next = S;
	S = p;
}


//出栈
void Pop(LinkStack& S, int& e)
{
	if (!S)
	{
		printf("元素出栈时,栈为空。\n");
		return;
	}

	struct stackNode* p = S;
	e = p->data;
	S = p->next;
	free(p);
	p = NULL;
}


//判断栈空
int EmptyStack(LinkStack S)
{
	if (!S)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}


//使用非递归的方式,完成采用邻接表表示图的深度优先搜索遍历
void DFS_Nonrecursive(ALGraph G, int v)
{
	printf("%c ", G.vertices[v - 1].data);
	visited[v - 1] = true;

	ArcNode* p = NULL;
	int w = 0;
	LinkStack S = NULL;

	InitStack(S);
	Push(S, v);

	while (!EmptyStack(S))
	{
		Pop(S, v);  // 弹出栈顶元素

		// 指向该顶点的第一个邻接顶点
		p = G.vertices[v - 1].firstarc;

		while (p != NULL)
		{
			w = p->adjvex;
			if (!visited[w])
			{
				// p指向该被弹出顶点的第一个未被访问的邻接顶点
				printf("%c ", G.vertices[w].data);
				visited[w] = true;
				Push(S, w + 1);
				break;  //退出整个while(p)循环
			}
			p = p->nextarc;
		}
	}
}

在这里插入图片描述

习题3

设计一个算法,求图G中距离顶点v的最短路径长度最大的一个顶点,设v可达其余各个顶点。

#include <stdio.h>
#include <stdlib.h>

#define MaxInt 32767
#define MVNum 100

typedef char VerTexType;
typedef int ArcType;

ArcType D[MVNum][MVNum]; //记录顶点Vi和Vj之间的最短路径长度。
int Path[MVNum][MVNum]; //最短路径上顶点Vj的前一顶点的序号(下标)。
//这两个数组中的关于顶点的每个下标,与G.vexs中各顶点的下标是一致的

typedef struct
{
	VerTexType vexs[MVNum];
	ArcType arcs[MVNum][MVNum];
	int vexnum;
	int arcnum;
}AMGraph;

void CreateAMGraph(AMGraph& G);
int LocateVex(AMGraph G, VerTexType v);
void printAMGraph(AMGraph G);
void ShortestPath_Floyd(AMGraph G);
void find(AMGraph G, int i, int j);
void printPath(AMGraph G);


int main()
{
	AMGraph G = { {0},{0},0,0 };
	int i = 0;
	CreateAMGraph(G);
	printAMGraph(G);

	printf("\n");
	ShortestPath_Floyd(G);
	printPath(G);

	return 0;
}


//创建带权有向网的邻接矩阵
void CreateAMGraph(AMGraph& G)
{
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	ArcType w = 0;

	printf("请输入总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; i++)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &G.vexs[i]);

		for (j = 0; j < G.vexnum; j++)
		{
			if (i != j)
			{
				G.arcs[i][j] = MaxInt;
			}
			else
			{
				G.arcs[i][j] = 0;
				/* 在弗洛伊德算法中,每个顶点到其自身,(中间不存在弧),就已经是最短路径了。
				即使可以通过其它顶点中转再到达其本身,也是比其直接到其自身长的,
				所以把每个顶点与自身之间的权值均设为0.  */
			}
		}
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边的两个顶点:", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		printf("请输入第%d条边的权值:", k + 1);
		scanf_s(" %d", &w);

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		G.arcs[i][j] = w;
	}
}

int LocateVex(AMGraph G, VerTexType v)
{
	int i = 0;

	for (i = 0; i < G.vexnum && (G.vexs[i] != v); i++)
	{
		;
	}

	return i;
}

void printAMGraph(AMGraph G)
{
	int i = 0;
	int j = 0;

	printf("各顶点值为:");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c ", G.vexs[i]);
	}

	printf("\n邻接矩阵为:\n");
	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			if (G.arcs[i][j] == MaxInt)
			{
				printf("%d   ", G.arcs[i][j]);
			}
			else if (G.arcs[i][j] > 9)
			{
				printf("%d      ", G.arcs[i][j]);
			}
			else
			{
				printf("%d       ", G.arcs[i][j]);
			}
		}

		printf("\n");
	}
}

//用Floyd算法求有向网G中各对顶点i和j之间的最短路径
void ShortestPath_Floyd(AMGraph G)
{
	int i = 0;
	int j = 0;
	int k = 0;

	for (i = 0; i < G.vexnum; i++)
	{
		for (j = 0; j < G.vexnum; j++)
		{
			D[i][j] = G.arcs[i][j];

			if (D[i][j] < MaxInt && D[i][j] != 0)
			{
				Path[i][j] = i;
			}
			else
			{
				Path[i][j] = -1;
			}
		}
	}

	for (k = 0; k < G.vexnum; k++)
	{
		for (i = 0; i < G.vexnum; i++)
		{
			for (j = 0; j < G.vexnum; j++)
			{
				if (D[i][k] + D[k][j] < D[i][j])
				{
					D[i][j] = D[i][k] + D[k][j];
					Path[i][j] = Path[k][j];
				}
			}
		}
	}
}

//递归打印G中从下标为i的顶点,到下标为j的顶点的最短路径
void find(AMGraph G, int i, int	j)
{
	if (i == j)
	{
		printf("%c ", G.vexs[i]);
		return;
	}
	else
	{
		find(G, i, Path[i][j]);  //不断取从序号为i的顶点到序号为j的顶点最短路径上,序号为j的顶点的直接前驱顶点的序号
		printf(" -> %c", G.vexs[j]); //并且打印该序号对应的顶点值
	}
}

void printPath(AMGraph G)
{
	int i = 0;
	int j = 0;
	int max = 0;
	int maxVex = 0;

	for (i = 0; i < G.vexnum; i++)
	{
		max = 0;
		maxVex = 0;

		for (j = 0; j < G.vexnum; j++)
		{
			if (j != i)
			{
				if (Path[i][j] == -1)
				{
					printf("\n从第%d个顶点%c到第%d个顶点%c不存在最短路径。", i + 1, G.vexs[i], j + 1, G.vexs[j]);
				}
				else
				{
					printf("\n从第%d个顶点%c到第%d个顶点%c的最短路径为:", i + 1, G.vexs[i], j + 1, G.vexs[j]);
					find(G, i, j);
					printf(" ,其长度为:%d", D[i][j]);


					if (D[i][j] != MaxInt && D[i][j] > max)
					{
						max = D[i][j];
						maxVex = j;
					}
				}
			}
		}

		if (max != 0)
		{
			printf("\n距离顶点%c的最短路径长度最大的一个顶点是:%c,其最短路径长度为:%d\n ", G.vexs[i], G.vexs[maxVex], max);
		}
	}
}

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

习题4 (存在时如何输出路径?)

试基于图的深度优先搜索策略写一算法,判别以邻接表方式存储的有向图中是否存在由顶点Vi到顶点Vj的路径(i≠j)。

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100

bool visited[MVNum];
//标记某个顶点是否被访问过。数组中初值均为 "false",如果被访问过,则置其相应的分量为 "true"

bool visited_findPath[MVNum];

typedef int OtherInfo;   //假设在边结点中的数据域存储边的权值,且边的权值类型为整型
typedef char VerTexType;  //假设顶点的数据类型为字符型

typedef struct ArcNode
{
	int adjvex;
	struct ArcNode* nextarc;
	OtherInfo info;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode, AdjList[MVNum];  //表头结点

typedef struct
{
	AdjList vertices;  //存储所有顶点
	int vexnum;  //顶点总数
	int arcnum;  //总边数
}ALGraph;  //图的信息

void CreateUDG(ALGraph& G);
int LocateVex(ALGraph& G, VerTexType v);
void printALGraph(ALGraph& G);
void DFS_AL(ALGraph G, int v);
void DFSTraverse(ALGraph G);
int findPath_DFS(ALGraph G, int i, int j);

int main()
{
	ALGraph G = { {'\0',NULL}, 0,0 };
	int i = 0;
	int j = 0;
	int k = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	char choice = '\0';

	CreateUDG(G);
	printALGraph(G);

	printf("\n");

	DFSTraverse(G);

	while (1)
	{
		printf("\n\n请输入要判断是否存在路径的两个顶点值:");
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);
		k = findPath_DFS(G, i, j);

		if (k == 1)
		{
			printf("\n顶点%c与%c之间存在路径。", v1,v2);
		}
		else
		{
			printf("\n顶点%c与%c之间不存在路径。", v1, v2);
		}


		//将visited_findPath数组初始化(重置visited_findPath数组)
		for (j = 0; j < G.vexnum; j++)
		{
			visited_findPath[j] = false;
		}

		printf("\n是否继续?(y/n): ");
		scanf_s(" %c", &choice);  //这里的"%c"前面必须要有空格,否则输入y也会退出主函数
		getchar(); // 清除输入缓冲区中的换行符

		if (choice != 'y' && choice != 'Y')
			break;
	}

	return 0;
}

void CreateUDG(ALGraph& G)
{
	int i = 0;
	int k = 0;
	VerTexType v1 = 0;
	VerTexType v2 = 0;
	int j = 0;
	ArcNode* p1 = NULL;
	ArcNode* p2 = NULL;

	printf("请输入无向图的总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入无向图的总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &(G.vertices[i].data));
		G.vertices[i].firstarc = NULL;   //初始化
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边依附的两个顶点(用空格间隔,输入结束后按回车): ", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p1 = (ArcNode*)malloc(sizeof(ArcNode));
		p1->adjvex = j;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;  //将新结点*p1插入顶点Vi的边表头部

		p2 = (ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex = i;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;  //将与*p1对称的新的边结点*p2插入顶点Vj的边表头部
	}
}


//在G的顶点表vexs中获取字符v的下标(数组G.vexs的下标从0开始)
int LocateVex(ALGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.vertices[i].data != v); ++i)
	{
		;
	}

	return i;
}


//打印邻接表
void printALGraph(ALGraph& G)
{
	int i = 0;
	ArcNode* pMove = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1, G.vertices[i].data);

		pMove = G.vertices[i].firstarc;
		if (pMove)
		{
			printf("\n与第%d个顶点相邻接的每个顶点在图中的位置(下标值)为:", i + 1);
			while (pMove)
			{
				printf("%d ", pMove->adjvex);
				pMove = pMove->nextarc;
			}
		}
		else
		{
			printf("\n没有与第%d个顶点相邻接的顶点。", i + 1);
		}
	}
}


//算法6.6 采用邻接表表示图的深度优先搜索遍历
void DFS_AL(ALGraph G, int v)
{
	printf("%c ", G.vertices[v - 1].data);

	visited[v - 1] = true;

	ArcNode* p = G.vertices[v - 1].firstarc;
	int w = 0;

	while (p != NULL)
	{
		w = p->adjvex;

		if (!visited[w])
		{
			DFS_AL(G, w + 1);
		}

		p = p->nextarc;
	}
}


//算法6.4 深度优先搜索遍历非连通图
void DFSTraverse(ALGraph G)
{
	int v = 0;

	for (v = 0; v < G.vexnum; ++v)
	{
		visited[v] = false;
	}

	printf("\n非连通图G的深度优先搜索遍历序列为:");
	for (v = 0; v < G.vexnum; ++v)
	{
		if (!visited[v])
		{
			DFS_AL(G, v + 1);
		}
	}
}


int findPath_DFS(ALGraph G, int i, int j)
{
	ArcNode* p = NULL;
	int m = 0;

	if (i == j)
	{
		//printf(" %c ", G.vertices[j].data);
		return 1;
	}
	else
	{
		//printf("%c -> ", G.vertices[i].data);
		visited_findPath[i] = 1;


		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			m = p->adjvex;

			if (!visited_findPath[m] && findPath_DFS(G, m, j))
			{
				return 1;
			}
		}

		//由于p为空跳出for循环时
		if (!p)
		{
			return 0;
		}
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int findPath_DFS(ALGraph G, int i, int j)
{
	ArcNode* p = NULL;
	int m = 0;

	printf(" \n\nG.vertices[i].data =  G.vertices[%d].data = %c", i, G.vertices[i].data);
	printf(" \nG.vertices[j].data =  G.vertices[%d].data = %c\n", j, G.vertices[j].data);


	if (i == j)
	{
		printf("\n【1】%c \n", G.vertices[j].data);
		return 1;
	}
	else
	{
		printf("\n【2】%c -> \n", G.vertices[i].data);
		visited_findPath[i] = 1;
		printf("\n【3】visited_findPath[i] =  visited_findPath[%d] = %d\n", i, visited_findPath[i]);


		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			printf(" \n【4】G.vertices[p->adjvex].data =  G.vertices[%d].data = %c\n", p->adjvex, G.vertices[p->adjvex].data);
			m = p->adjvex;
			printf("\n【5】m = %d\n", m);

			printf("\n【6】visited_findPath[m] =  visited_findPath[%d] = %d\n", m, visited_findPath[m]);

			if (!visited_findPath[m] && findPath_DFS(G, m, j))
			{
				return 1;
			}
		}

		//由于p为空跳出for循环时
		if (!p)
		{
			return 0;
		}
	}
}

习题5(存在时如何输出路径?)

采用邻接表存储结构,编写一个算法,判别无向图中任意给定的两个顶点之间是否存在一条长度为k 的简单路径。

#include <stdio.h>
#include <stdlib.h>

#define MVNum 100

bool visited[MVNum];
//标记某个顶点是否被访问过。数组中初值均为 "false",如果被访问过,则置其相应的分量为 "true"

bool visited_findPath[MVNum];
bool visited_PathLen[MVNum];
int path[MVNum];


typedef int OtherInfo;   //假设在边结点中的数据域存储边的权值,且边的权值类型为整型
typedef char VerTexType;  //假设顶点的数据类型为字符型

typedef struct ArcNode
{
	int adjvex;
	struct ArcNode* nextarc;
	OtherInfo info;
}ArcNode;  //边结点

typedef struct VNode
{
	VerTexType data;
	ArcNode* firstarc;
}VNode, AdjList[MVNum];  //表头结点

typedef struct
{
	AdjList vertices;  //存储所有顶点
	int vexnum;  //顶点总数
	int arcnum;  //总边数
}ALGraph;  //图的信息

void CreateUDG(ALGraph& G);
int LocateVex(ALGraph& G, VerTexType v);
void printALGraph(ALGraph& G);
void DFS_AL(ALGraph G, int v);
void DFSTraverse(ALGraph G);
int findPath_DFS(ALGraph G, int i, int j);
int findPathLen(ALGraph G, int i, int j, int k);

int main()
{
	ALGraph G = { {'\0',NULL}, 0,0 };
	int i = 0;
	int j = 0;
	int k = 0;
	int k1 = 0;
	VerTexType v1 = '\0';
	VerTexType v2 = '\0';
	int len = 0;
	int k2 = 0;
	char choice = '\0';

	CreateUDG(G);
	printALGraph(G);

	printf("\n");

	DFSTraverse(G);

	while (1)
	{
		printf("\n\n请输入要判断是否存在路径的两个顶点值:");
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));
		i = LocateVex(G, v1);
		j = LocateVex(G, v2);
		k1 = findPath_DFS(G, i, j);

		if (k1 == 1)
		{
			printf("顶点%c与%c之间存在路径。", v1, v2);
		}
		else
		{
			printf("顶点%c与%c之间不存在路径。", v1, v2);
		}

		//将visited_findPath数组初始化(重置visited_findPath数组)
		for (k = 0; k < G.vexnum; k++)
		{
			visited_findPath[k] = false;
		}

		if (k1 == 1)
		{
			printf("\n请输入要查找的长度len :");
			scanf_s(" %d", &len);
			k2 = findPathLen(G, i, j, len);

			if (k2 == 1)
			{
				printf("顶点%c与%c之间存在长度为%d的路径。", v1, v2, len);
			}
			else
			{
				printf("顶点%c与%c之间不存在长度为%d的路径。", v1, v2, len);
			}


			//将visited_PathLen数组初始化(重置visited_PathLen数组)
			for (k = 0; k < G.vexnum; k++)
			{
				visited_PathLen[k] = false;
			}
		}

		printf("\n是否继续?(y/n): ");
		scanf_s(" %c", &choice);  //这里的"%c"前面必须要有空格,否则输入y也会退出主函数
		getchar(); // 清除输入缓冲区中的换行符

		if (choice != 'y' && choice != 'Y')
			break;
	}

	return 0;
}

void CreateUDG(ALGraph& G)
{
	int i = 0;
	int k = 0;
	VerTexType v1 = 0;
	VerTexType v2 = 0;
	int j = 0;
	ArcNode* p1 = NULL;
	ArcNode* p2 = NULL;

	printf("请输入无向图的总顶点数:");
	scanf_s(" %d", &G.vexnum);

	printf("请输入无向图的总边数:");
	scanf_s(" %d", &G.arcnum);

	for (i = 0; i < G.vexnum; ++i)
	{
		printf("请输入第%d个顶点的值:", i + 1);
		scanf_s(" %c", &(G.vertices[i].data));
		G.vertices[i].firstarc = NULL;   //初始化
	}

	for (k = 0; k < G.arcnum; k++)
	{
		printf("请输入第%d条边依附的两个顶点(用空格间隔,输入结束后按回车): ", k + 1);
		scanf_s(" %c %c", &v1, sizeof(VerTexType), &v2, sizeof(VerTexType));

		i = LocateVex(G, v1);
		j = LocateVex(G, v2);

		p1 = (ArcNode*)malloc(sizeof(ArcNode));
		p1->adjvex = j;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;  //将新结点*p1插入顶点Vi的边表头部

		p2 = (ArcNode*)malloc(sizeof(ArcNode));
		p2->adjvex = i;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;  //将与*p1对称的新的边结点*p2插入顶点Vj的边表头部
	}
}


//在G的顶点表vexs中获取字符v的下标(数组G.vexs的下标从0开始)
int LocateVex(ALGraph& G, VerTexType v)
{
	int i = 0;
	for (i = 0; i < G.vexnum && (G.vertices[i].data != v); ++i)
	{
		;
	}

	return i;
}


//打印邻接表
void printALGraph(ALGraph& G)
{
	int i = 0;
	ArcNode* pMove = NULL;

	for (i = 0; i < G.vexnum; i++)
	{
		printf("\n第%d个顶点为:%c", i + 1, G.vertices[i].data);

		pMove = G.vertices[i].firstarc;
		if (pMove)
		{
			printf("\n与第%d个顶点相邻接的每个顶点在图中的位置(下标值)为:", i + 1);
			while (pMove)
			{
				printf("%d ", pMove->adjvex);
				pMove = pMove->nextarc;
			}
		}
		else
		{
			printf("\n没有与第%d个顶点相邻接的顶点。", i + 1);
		}
	}
}


//算法6.6 采用邻接表表示图的深度优先搜索遍历
void DFS_AL(ALGraph G, int v)
{
	printf("%c ", G.vertices[v - 1].data);

	visited[v - 1] = true;

	ArcNode* p = G.vertices[v - 1].firstarc;
	int w = 0;

	while (p != NULL)
	{
		w = p->adjvex;

		if (!visited[w])
		{
			DFS_AL(G, w + 1);
		}

		p = p->nextarc;
	}
}


//算法6.4 深度优先搜索遍历非连通图
void DFSTraverse(ALGraph G)
{
	int v = 0;

	for (v = 0; v < G.vexnum; ++v)
	{
		visited[v] = false;
	}

	printf("\n非连通图G的深度优先搜索遍历序列为:");
	for (v = 0; v < G.vexnum; ++v)
	{
		if (!visited[v])
		{
			DFS_AL(G, v + 1);
		}
	}
}


int findPath_DFS(ALGraph G, int i, int j)
{
	ArcNode* p = NULL;
	int m = 0;

	if (i == j)
	{
		//printf(" %c ", G.vertices[j].data);
		return 1;
	}
	else
	{
		//printf("%c -> ", G.vertices[i].data);
		visited_findPath[i] = 1;


		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			m = p->adjvex;

			if (!visited_findPath[m] && findPath_DFS(G, m, j))
			{
				return 1;
			}
		}

		//由于p为空跳出for循环时
		if (!p)
		{
			return 0;
		}
	}
}



int findPathLen(ALGraph G, int i, int j, int k)
{
	ArcNode* p = NULL;
	int m = 0;

	if (i == j && k == 1)
	{
		return 1;
	}
	else
	{
		visited_PathLen[i] = 1;

		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			m = p->adjvex;

			if (!visited_PathLen[m] && findPathLen(G, m, j, k - 1))
			{
				return 1;
			}
		}

		visited_PathLen[i] = 0;

		//由于p为空跳出for循环时
		if (!p)
		{
			return 0;
		}
	}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

int findPathLen(ALGraph G, int i, int j, int k)
{
	ArcNode* p = NULL;
	int m = 0;


	printf(" \n\nG.vertices[i].data =  G.vertices[%d].data = %c", i, G.vertices[i].data);
	printf(" \nG.vertices[j].data =  G.vertices[%d].data = %c\n", j, G.vertices[j].data);


	if (i == j && k == 1)
	{
		printf("\n【1】%c \n", G.vertices[j].data);
		return 1;
	}
	else
	{
		printf("\n【2】%c -> \n", G.vertices[i].data);
		visited_PathLen[i] = 1;
		printf("\n【3】visited_PathLen[i] =  visited_PathLen[%d] = %d\n", i, visited_PathLen[i]);


		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			printf("\n【4】G.vertices[p->adjvex].data =  G.vertices[%d].data = %c\n", p->adjvex, G.vertices[p->adjvex].data);
			m = p->adjvex;
			printf("\n【5】m = %d\n", m);

			printf("\n【6】visited_PathLen[m] =  visited_PathLen[%d] = %d\n", m, visited_PathLen[m]);
			if (!visited_PathLen[m] && findPathLen(G, m, j, k - 1))
			{
				return 1;
			}
		}

		printf("\n【7】visited_PathLen[i] =  visited_PathLen[%d] = %d\n", i, visited_PathLen[i]);
		visited_PathLen[i] = 0;
		printf("\n【8】visited_PathLen[i] =  visited_PathLen[%d] = %d\n", i, visited_PathLen[i]);
	}


	//由于p为空跳出for循环时
	if (!p)
	{
		return 0;
	}
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值