克鲁斯卡尔算法-寻找最小生成树

前言:克鲁斯卡尔算法-寻找最小生成树的实现和学习笔记

什么是克鲁斯卡尔算法

跟普利姆算法一样都是基于MST性质来寻找最小生成树,但是它们之间的思路不一样,我今天学了这两个算法并且进行了实现,我个人会感觉克鲁斯卡尔算法会比较好理解,普利姆算法会比较绕

克鲁斯卡尔算法是通过找权值最小的边来实现寻找最小生成树的,而普利姆算法是通过找点来实现的,所以它们之间还是有些区别的

实现克鲁斯卡尔算法-寻找最小生成树

  • 因为克鲁斯卡尔算法是通过找边的方法来实现最小生成树的,所以这里就需要通过一个结构体来记录相关图中所有边的信息,一个边(两个顶点 一个权值),结构体如下
typedef struct _Edge{
	int iStart;
	int iEnd;
	int iWeight;
}Edge, *PEdge;
  • 接着我们还需要考虑如何找,那这个很简单,只需要对这个结构体中所有的边进行一次排序即可找到从小到大的边的权值了

  • 那还需要考虑的一个问题就是,从小到大找权值最小的边,但是这样找有可能会回路的现象,那这种是不行的,因为生成树的定义是如下

连通图中的生成树必须满足以下 2 个条件:
包含连通图中所有的顶点;
任意两顶点之间有且仅有一条通路;

  • 所以这里还需要防止顶点之间有回路的现象,那么这里的话我们通过一个结构体来进行记录,初始化的时候就是 VexSet[i] = i
typedef struct _vexSet{
	int iField;
}VexSet, *PVexSet;
  • 然后每次找到一个对应最小的权值边的时候,需要将VexSet中的各个值和该边的iEnd顶点进行判断,如果该顶点的iEnd和VexSet[1,2,3,4....n]中有相同的话,那么就将其VexSet[iEnd]修改为iStart

这个怎么理解呢?其实意思就是如果你在找边的最小权值的时候,那么这个边肯定是属于最小生成树中的边集合中的,那么肯定就要将该边归到最小生成树的边集合中,而当最后我们遍历的次数完成了之后,那么这个正好是一个连通图,并且也不存在回路的现象,因为每次找到最小权值的时候,我们就将其修改为最小生成树的集合,而如果下次找到的是条回路的话,那么肯定就会有矛盾,所以就会舍弃

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 32767
#define OK 1
#define ERROR 0
typedef int ElemType;
typedef int Status;
typedef char VexType;
typedef struct _Graph
{
	int vexNum;
	int arcNum;
	int** arcs;
	char* vexs;
}Graph, *PGraph;

Graph* initGraph(int vexNum)
{
	Graph* pGraph = NULL;
	if (pGraph == NULL)
	{
		pGraph = (Graph*)malloc(sizeof(Graph));
		memset(pGraph, 0, sizeof(Graph));
		if (pGraph == NULL)
			return NULL;
		pGraph->vexNum = vexNum;
		pGraph->vexs = (char*)malloc(sizeof(pGraph->vexNum));
		memset(pGraph->vexs, 0, sizeof(pGraph->vexNum));
		pGraph->arcNum = 0;
		pGraph->arcs = (int**)malloc(sizeof(int*)* pGraph->vexNum);
		memset(pGraph->arcs, sizeof(int*)* pGraph->vexNum, 0);
		for (int i = 0; i < pGraph->vexNum; i++)
		{
			pGraph->arcs[i] = (int*)malloc(sizeof(int)* pGraph->vexNum);
			memset(pGraph->arcs[i], 0, sizeof(int)*pGraph->vexNum);
		}
	}
	return pGraph;
}

Status createGraph(Graph** pGraph, char* vexs, int* arcs)
{
	if (*pGraph == NULL)
		return ERROR;
	for (int i = 0; i<(*pGraph)->vexNum; i++)
	{
		*((*pGraph)->vexs + i) = *(vexs + i);
		for (int j = 0; j<(*pGraph)->vexNum; j++)
		{
			(*pGraph)->arcs[i][j] = *(arcs + i*((*pGraph)->vexNum) + j);
			printf("%d ", (*pGraph)->arcs[i][j]);
			if ((*pGraph)->arcs[i][j] > 0 && (*pGraph)->arcs[i][j] != MAX)
				(*pGraph)->arcNum++;
		}
		printf("\n");
	}
	(*pGraph)->arcNum /= 2;
	return OK;
}

Status DFS(Graph* pGraph, int* iVisitedArray, int visitedIndex)
{
	if (pGraph == NULL)
		return ERROR;
	iVisitedArray[visitedIndex] = 1;
	printf("%c ", pGraph->vexs[visitedIndex]);
	for (int i = 0; i<pGraph->vexNum; i++)
	{
		if (pGraph->arcs[visitedIndex][i] > 0 && !iVisitedArray[i] && pGraph->arcs[visitedIndex][i] != MAX)
			DFS(pGraph, iVisitedArray, i);
	}
	return OK;
}

typedef struct _Edge{
	int iStart;
	int iEnd;
	int iWeight;
}Edge, *PEdge;

typedef struct _vexSet{
	int iField;
}VexSet, *PVexSet;

// 初始化initEdge,后面用于生成最小生成树所使用
Edge* initEdge(Graph* pGraph)
{
	int index = 0;
	Edge* pEdge = (Edge*)malloc(sizeof(Edge)*pGraph->arcNum);
	memset(pEdge, 0, sizeof(Edge)*pGraph->arcNum);
	if (pEdge == NULL)
		return NULL;
	for (int i = 0; i < pGraph->vexNum; i++)
	{
		for (int j = i + 1; j < pGraph->vexNum; j++)
		{
			if (pGraph->arcs[i][j] < MAX)
			{
				pEdge[index].iStart = i;
				pEdge[index].iEnd = j;
				pEdge[index].iWeight = pGraph->arcs[i][j];
				index++;
			}
		}
	}
	return pEdge;
}

// vexSet之间的初始化,用于之后判断其中的连通关系
VexSet* initVexSet()
{
	VexSet* vexSet = (VexSet*)malloc(sizeof(VexSet)*6);
	for (int i = 0; i < 6; i++)
		vexSet[i].iField = i;
	return vexSet;
}

// Edge之间的权值排序
void bubbleSort(Graph* pGraph, Edge* pEdge)
{
	Edge temp;
	if (pEdge == NULL)
		return;
	for (int i = 0; i<pGraph->arcNum - 1; i++)
	{
		for (int j = 0; j < pGraph->arcNum - i - 1; j++)
		{
			if (pEdge[j].iWeight > pEdge[j+1].iWeight)
			{
				temp = pEdge[j];
				pEdge[j] = pEdge[j + 1];
				pEdge[j + 1] = temp;
			}
		}
	}
	// 测试排序结果
	/*
	for (int i = 0; i<pGraph->arcNum; i++)
		printf("v%d -- v%d -- weight: %d\n", pEdge[i].iStart, pEdge[i].iEnd, pEdge[i].iWeight);
	printf("\n");
	*/
}

void getKruskal(Graph* pGraph)
{

	VexSet* vexSet = NULL;
	Edge* pEdge = NULL;
	// 初始化Edge,这里的Edge保存的是当前图中每条边和相关两个顶点和之间的权值的关系
	pEdge = initEdge(pGraph);
	// 连通分量标识符
	vexSet = initVexSet();
	if (pEdge == NULL)
		return;
	bubbleSort(pGraph, pEdge);
	// 下面开始进行克鲁斯卡尔算法生成最小生成树
	for (int i = 0; i < pGraph->vexNum; i++)
	{
		int iStart = pEdge[i].iStart;
		int iEnd = pEdge[i].iEnd;
		if (vexSet[iStart].iField != vexSet[iEnd].iField)
		{
			printf("%c -- %c -- weight: %d\n", pGraph->vexs[iStart], pGraph->vexs[iEnd], pEdge[i].iWeight);
			for (int j = 0; j<pGraph->vexNum; j++)
			{
				if (vexSet[j].iField == vexSet[iEnd].iField)
					vexSet[j].iField = vexSet[iStart].iField;
			}
		}
	}
}

int main()
{
	int initArcs[6][6] =
	{
		0, 6, 1, 5, MAX, MAX,
		6, 0, 5, MAX, 3, MAX,
		1, 5, 0, 5, 6, 4,
		5, MAX, 5, 0, MAX, 2,
		MAX, 3, 6, MAX, 0, 6,
		MAX, MAX, 4, 2, 6, 0
	};
	char initVexs[7] = "123456";
	Graph* pGraph = initGraph(6);
	createGraph(&pGraph, initVexs, (int*)initArcs);
	getKruskal(pGraph);
	//printf("\n");
	return 0;
}

posted @ 2022-04-10 17:01  zpchcbd  阅读(201)  评论(2)    收藏  举报