数据结构与C语言实现(十一)——图(下):最小生成树与Prim与Kruskal算法

1.Prim算法,来自于Dijkstra算法的变化,选取一个起始点,然后每次寻找距离这个点权值最小的点,收入树中(使它距离源点的距离为0),然后整个树相当于都是源点,而在V集合中(V集合指的是不在S集合中的点,也就是说还没被收录到树中的点)找一个权值最小的,然后收录之,依次循环,直到所有的点都被收录。

这是一种从点出发的算法,在Dijkstra算法上进行改进。

注意到,我们的初始化过程中首先将权值全部设置为极大值,便于之后选取最小权重,然后将自己到自己的距离设为0,将源点的邻接点设为权值。为什么要这么做呢?试想一下两个集合,一个S一个V,当你在S中放入源点的时候,那V中就要相应的出现其邻接点。

然后我们去在V中(既然是在V中,我们就必须设立一个collected数组作为判断点是否访问过的flag存在),选择一个权重最小的点,加进S中。

最后注意,我们每次改变把V中元素放入S的时候都要更新一下S中的值,去判断一下目前S中的源点(注意,这里的源点可不是一开始写的源点,整个树都是源点,)到邻接点距离和源点取道上一个点再到目标点的距离谁更小,谁小就保存在dist中。

void Dijkstra(int s)
{
	int MinDist = INIFITY;
	int min_i;
	int i;
	for (i = num_vertex; i >= 0; i--)//先粗略初始化,把距离权值设为无穷,把经过路径点设置为-1
	{
		dist[i] = INIFITY;
		path[i] = -1;
		collected[i] = 0;//收集到为1,未收集到为0
	}
	dist[s] = 0;//自己到自己的距离为0
	//将源点的邻接点距离设置为权值。
	Edge E = ver[s].ptr_edge;
	while (E != NULL)
	{
		dist[E->adjvex] = E->weight;//到邻节点的距离就是权值
		E = E->next;
	}

	while (1)
	{
		MinDist = INIFITY;
		for (i = 0; i < num_vertex; i++)//找到dist中最小的那个值
		{
			if(collected[i] == 0)//我要在未搜集的那些里面去寻找最小距离
			if (MinDist > dist[i])
			{
				MinDist = dist[i];
				min_i = i;//并且保存最小值的下标
			}	
		}
		if (MinDist == INIFITY)//如果MinDist未发生改变,说明所有点都被搜集到了
			break;
		collected[min_i] = 1;//最小距离点被访问到了,下面应该去找这个最小值的邻接点了

		Edge E = ver[min_i].ptr_edge;
		while (E != NULL)
		{
			if (!collected[E->adjvex])//如果有没被访问过的邻接点
				if ((dist[min_i] + E->weight) < dist[E->adjvex])//如果我走两段的距离比直接走要小,那么就取代它
				{
					dist[E->adjvex] = dist[min_i] + E->weight;
					path[E->adjvex] = min_i;//说明我是通过min_走到的邻接点
				}
			E = E->next;
		}
	}

2.Kruskal算法,是并查集的应用。每次都取最小的边,但是有要求,不能形成回路,一直搜集,直到搜集到顶点-1,说明搜集完毕。

问题来了,怎么判断不形成回路。要判断这个边的两个点,是否属于一个树(集合)内,如果是,那就是回路,如果不是,那就可以加入。


//邻接表

#include <stdio.h>
#include <stdlib.h>
#define	MAXSIZE 100
#define INIFITY 655535

typedef struct LNode_Vertex* Vertex;
typedef struct LNode_Edge* Edge;
typedef struct LNode_List* List;
typedef struct LNode_Queue* Queue;

struct LNode_Vertex
{
	int vex;
	Edge ptr_edge;
};


struct LNode_Edge
{
	int adjvex;
	int weight;
	Edge next;
};

struct TNode
{
	int parent[MAXSIZE];
	int data[MAXSIZE];
};
typedef struct TNode* Tree;

struct SetNode
{
	int parent;
	int data;
};
typedef struct SetNode* Set;
SetNode S[MAXSIZE];

LNode_Vertex ver[MAXSIZE];
int num_vertex, num_edge;
int visited[MAXSIZE];
int dist[MAXSIZE];
int path[MAXSIZE];
int collected[MAXSIZE];
Tree MST;
int delete_i;
void Create_Graph()
{
	printf("你要输入的点和边数\n");
	
	scanf("%d %d", &num_vertex, &num_edge);
	printf("下面请输入顶点\n");
	for (int i = 0; i < num_vertex; i++)
	{
		scanf("%d", &ver[i].vex);
	}
	printf("下面请输入顶点1,顶点2,权值\n");
	for (int i = 0; i < num_edge; i++)
	{
		int vex1, vex2, w;
		scanf("%d %d %d", &vex1, &vex2, &w);
		Edge E = (Edge)malloc(sizeof(struct LNode_Edge));//建立一个边节点
		if(ver[vex1].ptr_edge == NULL)
			ver[vex1].ptr_edge = E;
		else
		{
			Edge p = ver[vex1].ptr_edge;
			while (p->next != NULL)//直到找到顶点节点所引导的一系列节点的最后
			{
				p = p->next;//每次往后移动一位
			}
			p->next = E;//将最后一个的next指向E
		}
		E->weight = w;
		E->adjvex = vex2;
		E->next = NULL;
	}

}


void Print_Graph()
{
	for (int i = 0; i < num_vertex; i++)
	{
		printf("%d——》", ver[i].vex);
		if (ver[i].ptr_edge != NULL)
		{
			printf("[%d,%d]->", ver[i].ptr_edge->adjvex, ver[i].ptr_edge->weight);
			while (ver[i].ptr_edge->next != NULL)
			{
				printf("[%d,%d]->", ver[i].ptr_edge->next->adjvex, ver[i].ptr_edge->next->weight);
				ver[i].ptr_edge->next = ver[i].ptr_edge->next->next;
			}
		}
		putchar('\n');
	}
}
Edge Find_Min()
{
	int Min = ver[0].ptr_edge->weight;
	Edge E,Ans_E;
	for (int i = 0; i < num_vertex; i++)
	{
		E = ver[i].ptr_edge;
		while (E)
		{
			if (E->weight < Min)
			{
				Min = E->weight;
				Ans_E = E;
				delete_i = i;
			}			
			E = E->next;
		}
	}
	return Ans_E;
}

//int Find_Pre(Edge Ans_E)
//{
//	Edge E = ver[delete_i].ptr_edge;
//	Edge q = Ans_E;
//
//	if (E == q)
//	{
//		return ver[delete_i].vex;
//	}
//	else
//	{
//		while (E->next != q)
//		{
//			E = E->next;//找到应该被删除的前一项
//		}
//		return E->adjvex;
//	}
//
//}

void DeleteMin(Edge Ans_E)
{
	Edge E = ver[delete_i].ptr_edge;
	Edge q = Ans_E;
	//如果所引导的链表第一个就要被删除
	if (E == q)
	{
		if (E->next == NULL)
			ver[delete_i].ptr_edge = NULL;
		else
			ver[delete_i].ptr_edge = E->next;//指向下一个
	}	
	else 
	{
		while (E->next != q)
		{
			E = E->next;//找到应该被删除的前一项
		}
		if (q->next == NULL)
			E->next = NULL;
		else
			E->next = q->next;//指向下一个
	}

	free(q);//释放被删除链表的空间
}

//下面是并查集的相关操作
int Find_Set(int Item)//查找Item在哪个集合中
{
	int i = 0;
	while (S[i].data != Item) i++;
	while (S[i].parent != -1) i = S[i].parent;//S[i]的爸爸指向的是所在集合头的首元素的下标
	return i;
}

void Union_Set(SetNode S[], int x1, int x2)
{
	int root1, root2;
	root1 = Find_Set(x1);
	root2 = Find_Set(x2);
	S[root2].parent = root1;
}


void Kruskal()
{
	int i, k,flag = 0;
	Edge Find_E;
	int m, n;
	for (i = 0; i < num_vertex; i++)
	{
		MST->data[i] = INIFITY;
		MST->parent[i] = -1;
	}

	while (flag <= num_vertex -1)
	{
		Find_E = Find_Min();
		flag++;
		//这边要用并查集的相关操作
		m = delete_i;
		n = Find_E->adjvex;
		if (Find_Set(m) == Find_Set(n))//如果在同一个集合中,就不操作
			;
		else//不在一个集合中就合并
		{
			Union_Set(S,m,n);
		}
		DeleteMin(Find_E);
	}
}

int main()
{
	
	Create_Graph();
	Print_Graph();
	putchar('\n');

	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值