数据结构——Prim 算法与Dijkstra算法

Prim:(这里把权值理解成距离)假设,已经确定的点的集合为S,那么还未确定的点可以记为1-S,我们每次从还未确定的点集合1-S中,选择一个里离集合S中的点直接相连,且权值最小(贪心)的点加入S中,不妨被这个点为t,则S与1-S将发生变化:由于t变成了S中的点,那么1-S中与t相连的点的距离实际上变成了该点与S的距离。由于初始化时,已经有一个点已经标志,那么只需要循环N-1次就够了,而且只能是N-1次,否则可能会发生错误(视INF而定),这就是Prim算法。

Kruskal:这个算法相对于Prim来说就比较好理解多了,每次选择权值最小的(贪心)的边,然后看看该边的两个点是否与树矛盾(用并查集判断就行了),在加边的过程中记录有效点的数目,达到N个就结束,不用把每条边都考虑进去。

Dijkstra算法是解决**单源最短路径**问题的**贪心算法**
它先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径
    直到求出从源点到其他各个顶点的最短路径。

老师的代码

#include <stdio.h>
#include <malloc.h>

#define MAX_DISTANCE 10000

/**
 * The structure of a Net.
 */
typedef struct Net{
	int** weights;
	int numNodes;
} *NetPtr;

/**
 * Initialize a Net.
 */
NetPtr initNet(int paraSize, int** paraData) {
	int i, j;
	NetPtr resultPtr = (NetPtr)malloc(sizeof(Net));
	resultPtr -> numNodes = paraSize;

	//Two stage space allocation.
	resultPtr->weights = (int**)malloc(paraSize * sizeof(int*));
	for (i = 0; i < paraSize; i ++) {
		resultPtr -> weights[i] = (int*)malloc(paraSize * sizeof(int));
		for (j = 0; j < paraSize; j ++) {
			resultPtr -> weights[i][j] = paraData[i][j];
		}//Of for j
	}//Of for i
	
	return resultPtr;
}//Of initNet

/**
 * The Prim algorithm for spanning tree, or the Dijkstra algorithm for nearest path.
 * @param paraAlgorithm 0 for Dijkstra, 1 for Prim
 * @return The total cost of the tree.
 */
int dijkstraOrPrim(NetPtr paraPtr, int paraAlgorithm) {
	int i, j, minDistance, tempBestNode, resultCost;
	int source = 0;
	int numNodes = paraPtr->numNodes;
	int *distanceArray = (int*)malloc(numNodes * sizeof(int));
	int *parentArray = (int*)malloc(numNodes * sizeof(int));
	//Essentially boolean
	int *visitedArray = (int*)malloc(numNodes * sizeof(int)); 

	// Step 1. Initialize. Any node can be the source.
	for (i = 0; i < numNodes; i++) {
		distanceArray[i] = paraPtr->weights[source][i];
		parentArray[i] = source;
		visitedArray[i] = 0;
	} // Of for i
	distanceArray[source] = 0;
	parentArray[source] = -1;
	visitedArray[source] = 1;

	// Step 2. Main loops.
	tempBestNode = -1;
	for (i = 0; i < numNodes - 1; i++) {
		// Step 2.1 Find out the best next node.
		minDistance = MAX_DISTANCE;
		for (j = 0; j < numNodes; j++) {
			// This node is visited.
			if (visitedArray[j]) {
				continue;
			} // Of if

			if (minDistance > distanceArray[j]) {
				minDistance = distanceArray[j];
				tempBestNode = j;
			} // Of if
		} // Of for j

		visitedArray[tempBestNode] = 1;

		// Step 2.2 Prepare for the next round.
		for (j = 0; j < numNodes; j++) {
			// This node is visited.
			if (visitedArray[j]) {
				continue;
			} // Of if

			// This node cannot be reached.
			if (paraPtr->weights[tempBestNode][j] >= MAX_DISTANCE) {
				continue;
			} // Of if

			// Attention: the difference between Dijkstra and Prim algorithms.
			if (paraAlgorithm == 0) {
				if (distanceArray[j] > distanceArray[tempBestNode] + paraPtr->weights[tempBestNode][j]) {
					// Change the distance.
					distanceArray[j] = distanceArray[tempBestNode] + paraPtr->weights[tempBestNode][j];
					// Change the parent.
					parentArray[j] = tempBestNode;
				} // Of if
			} else {
				if (distanceArray[j] > paraPtr->weights[tempBestNode][j]) {
					// Change the distance.
					distanceArray[j] = paraPtr->weights[tempBestNode][j];
					// Change the parent.
					parentArray[j] = tempBestNode;
				} // Of if
			}//Of if
		} // Of for j
	} // Of for i

	printf("the parent of each node: ");
	for (i = 0; i < numNodes; i++) {
		printf("%d, ", parentArray[i]);
	} // Of for i

	if (paraAlgorithm == 0) {
		printf("From node 0, path length to all nodes are: ");
		for (i = 0; i < numNodes; i++) {
			printf("%d (%d), ", i, distanceArray[i]);
		} // Of for i
	} else {
		resultCost = 0;
		for (i = 0; i < numNodes; i++) {
			resultCost += distanceArray[i];
			printf("cost of node %d is %d, total = %d\r\n", i, distanceArray[i], resultCost);
		} // Of for i
		printf("Finally, the total cost is %d.\r\n ", resultCost);
	}//Of if

	// Step 3. Output for debug.
	printf("\r\n");

	return resultCost;
}// Of dijkstraOrPrim

/**
 * Construct a sample net.
 * Revised from testGraphTranverse().
 */
NetPtr constructSampleNet() {
	int i, j;
	int myGraph[6][6] = { 
		{0, 6, 1, 5, 0, 0},
		{6, 0, 5, 0, 3, 0}, 
		{1, 5, 0, 5, 6, 4}, 
		{5, 0, 5, 0, 0, 2}, 
		{0, 3, 6, 0, 0, 6},
		{0, 0, 4, 2, 6, 0}};
	int** tempPtr;
	int numNodes = 6;
	printf("Preparing data\r\n");
		
	tempPtr = (int**)malloc(numNodes * sizeof(int*));
	for (i = 0; i < numNodes; i ++) {
		tempPtr[i] = (int*)malloc(numNodes * sizeof(int));
	}//Of for i
	 
	for (i = 0; i < numNodes; i ++) {
		for (j = 0; j < numNodes; j ++) {
			if (myGraph[i][j] == 0) {
				tempPtr[i][j] = MAX_DISTANCE;
			} else {
				tempPtr[i][j] = myGraph[i][j];
			}//Of if
		}//Of for j
	}//Of for i
 
	printf("Data ready\r\n");
	
	NetPtr resultNetPtr = initNet(numNodes, tempPtr);
	return resultNetPtr;
}//Of constructSampleNet

/**
 * Test the Prim algorithm.
 */
void testPrim() {
	NetPtr tempNetPtr = constructSampleNet();
	printf("=====Dijkstra algorithm=====\r\n");
	dijkstraOrPrim(tempNetPtr, 0);
	printf("=====Prim algorithm=====\r\n");
	dijkstraOrPrim(tempNetPtr, 1);
}//Of testPrim

/**
 * The entrance.
 */
int main(){
	testPrim();
	return 1;
}//Of main

运行结果

Preparing data
Data ready
=====Dijkstra algorithm=====
the parent of each node: -1, 0, 0, 0, 2, 2, From node 0, path length to all nodes are: 0 (0), 1 (6), 2 (1), 3 (5), 4 (7), 5 (5),
=====Prim algorithm=====
the parent of each node: -1, 2, 0, 5, 1, 2, cost of node 0 is 0, total = 0
cost of node 1 is 5, total = 5
cost of node 2 is 1, total = 6
cost of node 3 is 2, total = 8
cost of node 4 is 3, total = 11
cost of node 5 is 4, total = 15
Finally, the total cost is 15.

Press any key to continue

我的代码

#include "stdio.h"    
#include "stdlib.h"   

#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXEDGE 20
#define MAXVEX 20
#define GRAPH_INFINITY 65535

typedef int Status;	/* Status是函数的类型,其值是函数结果状态代码,如OK等 */

typedef struct
{
	int arc[MAXVEX][MAXVEX];
	int numVertexes, numEdges;
}MGraph;

void CreateMGraph(MGraph *G)/* 构件图 */
{
	int i, j;

	/* printf("请输入边数和顶点数:"); */
	G->numEdges=15;
	G->numVertexes=9;

	for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
	{
		for ( j = 0; j < G->numVertexes; j++)
		{
			if (i==j)
				G->arc[i][j]=0;
			else
				G->arc[i][j] = G->arc[j][i] = GRAPH_INFINITY;
		}
	}

	G->arc[0][1]=10;
	G->arc[0][5]=11; 
	G->arc[1][2]=18; 
	G->arc[1][8]=12; 
	G->arc[1][6]=16; 
	G->arc[2][8]=8; 
	G->arc[2][3]=22; 
	G->arc[3][8]=21; 
	G->arc[3][6]=24; 
	G->arc[3][7]=16;
	G->arc[3][4]=20;
	G->arc[4][7]=7; 
	G->arc[4][5]=26; 
	G->arc[5][6]=17; 
	G->arc[6][7]=19; 

	for(i = 0; i < G->numVertexes; i++)
	{
		for(j = i; j < G->numVertexes; j++)
		{
			G->arc[j][i] =G->arc[i][j];
		}
	}

}

/* Prim算法生成最小生成树  */
void MiniSpanTree_Prim(MGraph G)
{
	int min, i, j, k;
	int adjvex[MAXVEX];		/* 保存相关顶点下标 */
	int lowcost[MAXVEX];	/* 保存相关顶点间边的权值 */
	lowcost[0] = 0;/* 初始化第一个权值为0,即v0加入生成树 */
			/* lowcost的值为0,在这里就是此下标的顶点已经加入生成树 */
	adjvex[0] = 0;			/* 初始化第一个顶点下标为0 */
	for(i = 1; i < G.numVertexes; i++)	/* 循环除下标为0外的全部顶点 */
	{
		lowcost[i] = G.arc[0][i];	/* 将v0顶点与之有边的权值存入数组 */
		adjvex[i] = 0;					/* 初始化都为v0的下标 */
	}
	for(i = 1; i < G.numVertexes; i++)
	{
		min = GRAPH_INFINITY;	/* 初始化最小权值为∞, */
						/* 通常设置为不可能的大数字如32767、65535等 */
		j = 1;k = 0;
		while(j < G.numVertexes)	/* 循环全部顶点 */
		{
			if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值小于min */
			{	
				min = lowcost[j];	/* 则让当前权值成为最小值 */
				k = j;			/* 将当前最小值的下标存入k */
			}
			j++;
		}
		printf("(%d, %d)\n", adjvex[k], k);/* 打印当前顶点边中权值最小的边 */
		lowcost[k] = 0;/* 将当前顶点的权值设置为0,表示此顶点已经完成任务 */
		for(j = 1; j < G.numVertexes; j++)	/* 循环所有顶点 */
		{
			if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]) 
			{/* 如果下标为k顶点各边权值小于此前这些顶点未被加入生成树权值 */
				lowcost[j] = G.arc[k][j];/* 将较小的权值存入lowcost相应位置 */
				adjvex[j] = k;				/* 将下标为k的顶点存入adjvex */
			}
		}
	}
}

int main(void)
{
	MGraph G;
	CreateMGraph(&G);
	MiniSpanTree_Prim(G);
  
	return 0;
 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值