算法分析与设计实验2:实现克鲁斯卡尔算法和prim算法

实验原理

(一)克鲁斯卡尔算法:

     一种用于求解最小生成树问题的贪心算法,该算法的基本思想是按照边的权重从小到大排序,然后依次选择边,并加入生成树中,同时确保不会形成环路,直到生成树包含图中所有的顶点为止。

具体步骤

  1. 边的排序:将所有边按照权重从小到大排序。
  2. 初始化:创建一个空的生成树(可以是一个空的图结构),以及一个用于记录每个顶点所属集合(或称为连通分量)的数据结构(例如并查集)。
  3. 边的选择:依次选择排序后的边。对于每一条边,检查它的两个端点是否属于同一个集合(即是否已经在同一个连通分量中)。如果两个端点不属于同一个集合,则将该边加入生成树,并将这两个端点所属的集合合并(即执行并查集的合并操作)。如果两个端点属于同一个集合,则跳过该边(因为它会形成环路)。

4.终止条件:当生成树中包含图中的 V-1 条边(其中 V 是图中的顶点数)时,算法终止。此时,生成树即为最小生成树。

(二)prim 算法:

     一种用于求解最小生成树问题的贪心算法,该算法从一个初始顶点开始,逐步扩展生成树,每次选择权重最小且连接生成树外部顶点的边,直到生成树包含图中所有的顶点为止。

具体步骤

  1. 初始化:选择一个初始顶点,将其加入生成树。创建一个优先队列(或最小堆),用于存储所有连接生成树外部顶点的边及其权重。
  2. 边的选择:从优先队列中取出权重最小的边(及其连接的外部顶点)。将该边及其连接的外部顶点加入生成树。更新优先队列,将新加入的顶点所有连接外部顶点的边(及其权重)加入队列。

3.终止条件:当生成树中包含图中的 V 个顶点时,算法终止。此时,生成树即为最小生成树。

流程分析

(一)克鲁斯卡尔算法:

算法分析与设计实验2:实现克鲁斯卡尔算法和prim算法_第1张图片

(1)输入

一个包含所有边及其权重的图和图中的顶点数。

(2)流程

  1. 边的预处理

读取图中的所有边,并存储它们的权重,使用一种数据结构来存储这些边,以便后续排序。

  1. 边的排序

根据边的权重对所有边进行排序,确保权重最小的边排在前面。

  1. 初始化数据结构

创建一个空的生成树结构,用于存储最终的最小生成树,并使用并查集数据结构来跟踪图中的连通分量。

  1. 选择边并构建生成树

遍历排序后的边列表,对于每条边,检查其两个端点是否属于同一个连通分量。如果不属于同一个连通分量,则将该边添加到生成树中,并合并这两个连通分量。反之则跳过该边。

  1. 检查生成树是否完整

跟踪添加到生成树中的边数。当边数达到 V-1(其中 V 是图中的顶点数)时,生成树构建完成。

  1. 输出结果

输出生成树中的边及其权重。

(3)输出

一个包含 V-1 条边的最小生成树,这些边连接了图中的所有顶点,并且总权重最小。

(二)prim算法:

(1)

算法分析与设计实验2:实现克鲁斯卡尔算法和prim算法_第2张图片

输入

一个包含所有顶点及其相邻顶点和边权重的图以及图中的顶点数。

(2)流程

  1. 初始化

选择一个初始顶点,将其标记为已访问,并将其加入生成树。创建一个优先队列(最小堆),用于存储所有连接生成树外部顶点的边及其权重。初始化一个数组或集合来跟踪哪些顶点已经被访问过。

  1. 构建生成树

当生成树中的顶点数小于图中的总顶点数时,执行以下步骤:

从优先队列中取出权重最小的边及其连接的外部顶点。将该边及其连接的外部顶点加入生成树,并标记该顶点为已访问。更新优先队列,将新加入的顶点所有连接外部顶点的边及其权重加入队列。

  1. 检查生成树是否完整

跟踪添加到生成树中的顶点数,当顶点数达到图中的总顶点数时,生成树构建完成。

  1. 输出结果

输出生成树中的边及其权重。

(3)输出

一个包含 V-1 条边的最小生成树,这些边连接了图中的所有顶点,并且总权重最小。

程序实现

(一)克鲁斯卡尔算法:

#include 
#include 
#include 
#define MaxInt 32767
#define MVNum 100 

void Interrupt(void)
{
	while (1) 
		if (getchar() == '\n')
			break;
}

typedef struct 
{
	char vexs[MVNum];
	int arcs[MVNum][MVNum]; 
	int vexnum, arcnum;
}AMGraph;

struct
{
	char Head;
	char Tail;
	int lowcoat;
}Edge[MVNum];

void InitGraph(AMGraph& G)
{
	int i, j;
	for (i = 0; i < MVNum; i++)
		for (j = 0; j < MVNum; j++)
			G.arcs[i][j] = MaxInt;
}

void CreateGraph(AMGraph& G)
{
	int i;
	char a;
	printf("请输入顶点数和边数:");
	scanf("%d %d", &G.vexnum, &G.arcnum);
	Interrupt(); 
	printf("请输入顶点名称(连续输入):");
	for (i = 0; i < G.vexnum; i++)
	{
		scanf("%c", &a);
		G.vexs[i] = a;
	}
	
	char b, c;
	int w, j, k; 
	for (i = 0; i < G.arcnum; i++)
	{
		printf("请输入边的两个顶点和权值w:");
		scanf("%c %c %d", &b, &c, &w); 
		Interrupt();
		Edge[i].Head = b;
		Edge[i].Tail = c;
		Edge[i].lowcoat = w;
		for (j = 0; j < G.vexnum; j++)
		{
			if (G.vexs[j] == b)
				break;
		}
		for (k = 0; k < G.vexnum; k++)
		{
			if (G.vexs[k] == c)
				break;
		}
		G.arcs[j][k] = G.arcs[k][j] = w;
	}
}

void InputGraph(AMGraph G)
{
	int i, j; 
	printf("邻接矩阵为:\n   ");
	for (i = 0; i < G.vexnum; i++)
		printf("%c  ", G.vexs[i]);
	printf("\n");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c  ", G.vexs[i]);
		for (j = 0; j < G.vexnum; j++) 
			printf("%d  ", G.arcs[i][j]);
		printf("\n");
	}
}

int Vexset[MVNum];
void Sort(AMGraph G)
{
	int i, j;
	for (i = G.arcnum - 1; i >= 0; i--)
	{
		for (j = 0; j < i; j++) 
		{
			if (Edge[j].lowcoat > Edge[j + 1].lowcoat) 
			{
				Edge[G.arcnum] = Edge[j];
				Edge[j] = Edge[j + 1];
				Edge[j + 1] = Edge[G.arcnum];
			}
		}
	}
}
int LocateVex(AMGraph G, char b)
{
	int j;
	for (j = 0; j < G.vexnum; j++)
	{
		if (G.vexs[j] == b)
			break;
	}
	return j;
}
void MiniSpanTree_Kruskal(AMGraph G) 
{
	int i, j;
	Sort(G);
	for (i = 0; i < G.vexnum; i++) 
		Vexset[i] = i;
	int v1, v2,
		vs1, vs2;
	for (i = 0; i < G.arcnum; i++)
	{
		v1 = LocateVex(G, Edge[i].Head);
		v2 = LocateVex(G, Edge[i].Tail);
		vs1 = Vexset[v1];
		vs2 = Vexset[v2];
		if (vs1 != vs2)
		{
			printf("%c->%c  ", Edge[i].Head, Edge[i].Tail);
			for (j = 0; j < G.vexnum; j++)
				if (Vexset[j] == vs2)
					Vexset[j] = vs1;
		}
	}
}

int main()
{
	AMGraph G;
	InitGraph(G); 
	CreateGraph(G);
	InputGraph(G);
	MiniSpanTree_Kruskal(G);
	return 0;
}

(二)prim算法:

#define _CRT_SECURE_NO_WARNINGS
#pragma warning (disable:4996)
#include 
#include 
#include 
#define MaxInt 32767
#define MVNum 100 

void Interrupt(void)
{
	while (1)
		if (getchar() == '\n')
			break;
}

typedef struct
{
	char vexs[MVNum];
	int arcs[MVNum][MVNum];
	int vexnum, arcnum;
}AMGraph;

void InitGraph(AMGraph& G)
{
	int i, j;
	for (i = 0; i < MVNum; i++)
		for (j = 0; j < MVNum; j++)
			G.arcs[i][j] = MaxInt;
}

void CreateGraph(AMGraph& G)
{
	int m,
		n,
		i;
	char a;
	printf("请输入顶点数和边数:");
	scanf("%d %d", &m, &n);
	Interrupt();
	G.vexnum = m;
	G.arcnum = n;
	printf("请输入顶点名称(连续输入):");
	for (i = 0; i < m; i++)
	{
		scanf("%c", &a);
		G.vexs[i] = a;
	}
	Interrupt();
	char b, c;
	int w, j, k;
	for (i = 0; i < n; i++)
	{
		printf("请输入边的两个顶点和权值w:");
		scanf("%c %c %d", &b, &c, &w);
		Interrupt();
		for (j = 0; j < n; j++)
		{
			if (G.vexs[j] == b)
				break;
		}
		for (k = 0; k < n; k++)
		{
			if (G.vexs[k] == c)
				break;
		}
		G.arcs[j][k] = G.arcs[k][j] = w;
	}
}

void InputGraph(AMGraph G)
{
	int i, j;
	printf("邻接矩阵为:\n   ");
	for (i = 0; i < G.vexnum; i++)
		printf("%c  ", G.vexs[i]);
	printf("\n");
	for (i = 0; i < G.vexnum; i++)
	{
		printf("%c  ", G.vexs[i]);
		for (j = 0; j < G.vexnum; j++) 
			printf("%d  ", G.arcs[i][j]);
		printf("\n");
	}
}

struct
{
	char adjvex;
	int lowcost;
}closedge[MVNum];

int Min(AMGraph G)
{
	int i,
		a,
		min = MaxInt;
	for (i = 0; i < G.vexnum; i++)
	{
		if (closedge[i].lowcost == 0)
			continue;
		if (min > closedge[i].lowcost)
		{
			min = closedge[i].lowcost;
			a = i;
		}
	}
	return a;
}

void MiniSpanTree_Prim(AMGraph G, char u)
{
	int i, j,
		k;
	for (j = 0; j < G.vexnum; j++)
	{
		if (G.vexs[j] == u)
			break;
	}
	k = j;
	for (j = 0; j < G.vexnum; j++)
	{
		if (j != k)
		{
			closedge[j].adjvex = u;
			closedge[j].lowcost = G.arcs[k][j];
		}
	}
	closedge[k].lowcost = 0;
	char u0, v0;
	for (i = 0; i < G.vexnum; i++)
	{
		k = Min(G);
		u0 = closedge[k].adjvex;
		v0 = G.vexs[k];
		printf("%c->%c  ", u0, v0);
		closedge[k].lowcost = 0; 
		for (j = 0; j < G.vexnum; j++)
		{
			if (G.arcs[k][j] < closedge[j].lowcost)
			{
				closedge[j].adjvex = G.vexs[k];
				closedge[j].lowcost = G.arcs[k][j];
			}
		}
	}
}

int main()
{
	AMGraph G;
	InitGraph(G);
	CreateGraph(G);
	InputGraph(G);
	MiniSpanTree_Prim(G, 0);
	return 0;
}

调试分析 

    (一)克鲁斯卡尔算法调试分析:

    1. 使用边的排序调试,在排序后,可以打印出排序后的边列表,检查排序结果是否符合预期,是否按照权重从小到大进行了排序。
    2. 通过单元测试来验证并查集的基本操作是否正确。同时,在算法执行过程中,可以打印出并查集的状态,检查合并操作是否按预期进行,保证算法够高效地检测环路和合并连通分量。
    3. 经过边的选择调试,在每次选择边时,可以打印出当前并查集的状态和所选边的信息,检查是否选择了正确的边,保证正确地判断了两个端点是否属于同一个连通分量。
    4. 执行生成树的完整性调试,检验生成树是否包含了所有顶点,并且边数是否为 V-1。在算法执行完毕后,可以检查生成树中的顶点数和边数,确保它们符合预期。

    (二)prim算法调试分析:

    1. 通过单元测试来验证优先队列的基本操作是否正确。同时,在算法执行过程中,可以打印出优先队列的状态,检查是否按预期选择了权重最小的边,保证优先队列正确实现,能够高效地选择权重最小的边。
    2. 经过生成树的构建调试,在每次选择外部顶点时,可以打印出当前生成树的状态和所选顶点的信息,检查是否选择了正确的顶点,保证正确地选择了外部顶点,并将其与生成树连接。
    3. 通过访问状态的跟踪调试,置一个数组或集合来跟踪访问状态,并在每次选择顶点时更新该数组或集合。在算法执行过程中,可以打印出访问状态的信息,检查是否按预期进行了更新,保证正确地跟踪了哪些顶点已经被访问过,以避免重复选择边。
    4. 执行生成树的完整性调试,与克鲁斯卡尔算法相同,检验生成树是否包含了所有顶点,并且边数是否为 V-1。在算法执行完毕后,可以检查生成树中的顶点数和边数,确保它们符合预期。

    测试结果

    (一)克鲁斯卡尔算法:

    算法分析与设计实验2:实现克鲁斯卡尔算法和prim算法_第3张图片

         输入一个有向图:5个顶点为abcde,其中a->b,权重为2;a->c,权重3;b->d权重为3;a->e,权重为4;e->d,权重为5,e->c,权重为6,;d->c,权重为4。经过克鲁斯卡尔算法的执行,成功输出相对应的邻接矩阵,并成功输出最小生成树为a->c,b->d,a->e,d->c。

    (二)prim算法: 

    算法分析与设计实验2:实现克鲁斯卡尔算法和prim算法_第4张图片

         输入一个有向图:4个顶点为abcd,其中a->b,权重为2;a->c,权重2;a->d权重为3;b->d,权重为4;c->d,权重为5。经过prim算法的执行,成功输出相对应的邻接矩阵,并成功输出最小生成树为a->c,a->d,b->d。

    你可能感兴趣的:(算法,笔记,经验分享)