最小生成树Prim算法 +索引优先队列

算法步骤 (实际应用中不用Prim算稀疏图)
算法步骤
邻接矩阵图

/*定义边*/
typedef struct ENode{
	int v1, v2;
	int Weight;
} * Edge;

/*邻接矩阵存储图*/
typedef struct MGNode
{
	int Nv, Ne;
	int V[MaxV][MaxV];
	string Data[MaxV];
} * MGraph;
/*图的初始化*/
MGraph CreateMGraph(int Nv)
{
	MGraph G = new MGNode();
	G->Nv = Nv;
	for (int i = 0; i < G->Nv; i++)
		for (int j = 0; j < G->Nv; j++)
			G->V[i][j] = INF;
	return G;
}
/*边的插入*/
void InsertEdge(MGraph G, Edge E) //无向图要插入两次
{
	G->V[E->v1][E->v2] = E->Weight;
	G->V[E->v2][E->v1] = E->Weight;
}
/*建造一个图*/
MGraph BuildGraph()
{
	int Nv;
	cin >> Nv;
	MGraph G = CreateMGraph(Nv);
	cin >> G->Ne;
	Edge E = new ENode;
	for (int i = 0; i < G->Ne; i++)
	{
		cin >> E->v1 >> E->v2 >> E->Weight;
		InsertEdge(G, E);
	}
	delete E;
	return G;
}

索引最小堆

typedef struct AssNode{
	int dist[MaxV];
	int reverse[MaxV];
} * AssArray;
//Index[i] = j;
//reverse[j] = i;
//reverse[Index[i]] = i;	//赋值

typedef struct IndexHNode{
	int *Index;		//堆真正存的数据是索引,但比较时用索引对应的dist[Index]
	AssArray Ass;
	int Size;
	int Capacity;
} * IndexHeap;
void SwapIndex(IndexHeap H, int i)	//修改反索引数组
{
	H->Ass->reverse[H->Index[i]] = i;
}

IndexHeap CreateHeap(int MaxSize)
{
	IndexHeap H = new IndexHNode;
	H->Size = 0; H->Capacity = MaxSize;
	H->Index = new int[MaxSize + 1](); H->Index[0] = 0;	//哨兵dist[index[0]] = 0
	H->Ass = new AssNode;
	return H;
}

/*插入顶点编号为v的顶点, 插入一个Index*/
void InsertIndex(IndexHeap H, int v) //从堆内访问dist必须通过dist[Index]
{
	int x = H->Ass->dist[v];
	int child;
	for ( child = ++H->Size; x < H->Ass->dist[H->Index[child / 2]]; child /= 2)	//目的是确定x位置
	{
		H->Index[child] = H->Index[child / 2];
		SwapIndex(H, child);
	}

	H->Index[child] = v;
	SwapIndex(H, child);
}
/*删除并返回最小边的索引*/
int DeleteIndex(IndexHeap H)
{
	int v = H->Index[1];
	int x = H->Ass->dist[H->Index[H->Size--]];
	int parent, child;
	for ( parent = 1; parent * 2 <= H->Size; parent = child)
	{
		child = parent * 2;
		if(H->Ass->dist[H->Index[child + 1]] < H->Ass->dist[H->Index[child]] && child != H->Size)
			child++;
		if(x > H->Ass->dist[H->Index[child]])
		{
			H->Index[parent] = H->Index[child];
			SwapIndex(H, parent);
		}
		else
			break;
	}
	H->Index[parent] = H->Index[H->Size + 1];
	SwapIndex(H, parent);
	return v;
}
/*修改dist[v]减少为x, 并调整堆*/
void Fix_up(IndexHeap H, int v, int x)
{
	H->Ass->dist[v] = x;
	int child;
	//切记!!!!!!!!!!!!!!!!!!!   这里使用reverse来访问堆!!!!而不是直接用v!!!(被坑傻过!!!)
	for (child = H->Ass->reverse[v]; x < H->Ass->dist[H->Index[child / 2]]; child /= 2)
	{
		H->Index[child] = H->Index[child / 2];
		SwapIndex(H, child);
	}
	H->Index[child] = v;
	SwapIndex(H, child);
}

Prim算法

int Prim(MGraph G, IndexHeap H)
{
	int MinTree = 0;
	for (int i = 1; i < G->Nv; i++)
		H->Ass->dist[i] = G->V[0][i];
	H->Ass->dist[0] = 0;			//表示0顶点已经是树中一部分

	for (int i = 1; i < G->Nv; i++)	//除0以外,所有顶点加入索引堆,表示还不是生成树内的顶点
		InsertIndex(H, i);

	while (H->Size != 0)	//队列中都是未加入生成树的顶点
	{

		int v = DeleteIndex(H);	//dist[v]最小,v加入生成树

		MinTree += H->Ass->dist[v];	//统计权重
		H->Ass->dist[v] = 0;	//标记该顶点已加入生成树

		for (int i = 0; i < G->Nv; i++)
			if(H->Ass->dist[i] != 0 && G->V[v][i] < INF)
				if(G->V[v][i] < H->Ass->dist[i])
					Fix_up(H, i, G->V[v][i]);	//找到最小dist后,在索引优先队列中更新dist
					
	}
	return MinTree;
}

int main()
{
	MGraph G = BuildGraph();
	IndexHeap H = CreateHeap(G->Nv);
	cout << Prim(G, H) << endl;

	system("pause");
	return 0;
}
input:
10 17
0 2 5
0 1 8
1 4 5
1 2 5
1 5 4
4 5 4
2 3 8
2 6 4
5 6 4
5 8 2
3 6 7
6 8 3
3 7 5
6 7 5
6 9 6
8 9 7
7 9 6
output:
38

生成树的具体边,可以加一个parent数组,然后每次找到一条边都插入一个新的图中,我就不写了。。。
完整代码

#include <iostream>
#include <climits>
#include <cstring>
using namespace std;
const int MaxV = 100;
const int INF = 10000;
/*定义边*/
typedef struct ENode{
	int v1, v2;
	int Weight;
} * Edge;

/*邻接矩阵存储图*/
typedef struct MGNode
{
	int Nv, Ne;
	int V[MaxV][MaxV];
	string Data[MaxV];
} * MGraph;
/*图的初始化*/
MGraph CreateMGraph(int Nv)
{
	MGraph G = new MGNode();
	G->Nv = Nv;
	for (int i = 0; i < G->Nv; i++)
		for (int j = 0; j < G->Nv; j++)
			G->V[i][j] = INF;
	return G;
}
/*边的插入*/
void InsertEdge(MGraph G, Edge E) //无向图要插入两次
{
	G->V[E->v1][E->v2] = E->Weight;
	G->V[E->v2][E->v1] = E->Weight;
}
/*建造一个图*/
MGraph BuildGraph()
{
	int Nv;
	cin >> Nv;
	MGraph G = CreateMGraph(Nv);
	cin >> G->Ne;
	Edge E = new ENode;
	for (int i = 0; i < G->Ne; i++)
	{
		cin >> E->v1 >> E->v2 >> E->Weight;
		InsertEdge(G, E);
	}
	delete E;
	return G;
}

typedef struct AssNode{
	int dist[MaxV];
	int reverse[MaxV];
} * AssArray;
//Index[i] = j;
//reverse[j] = i;
//reverse[Index[i]] = i;	//赋值

typedef struct IndexHNode{
	int *Index;		//堆真正存的数据是索引,但比较时用索引对应的dist[Index]
	AssArray Ass;
	int Size;
	int Capacity;
} * IndexHeap;
void SwapIndex(IndexHeap H, int i)	//修改反索引数组
{
	H->Ass->reverse[H->Index[i]] = i;
}

IndexHeap CreateHeap(int MaxSize)
{
	IndexHeap H = new IndexHNode;
	H->Size = 0; H->Capacity = MaxSize;
	H->Index = new int[MaxSize + 1](); H->Index[0] = 0;	//哨兵dist[index[0]] = 0
	H->Ass = new AssNode;
	return H;
}

/*插入顶点编号为v的顶点, 插入一个Index*/
void InsertIndex(IndexHeap H, int v) //从堆内访问dist必须通过dist[Index]
{
	int x = H->Ass->dist[v];
	int child;
	for ( child = ++H->Size; x < H->Ass->dist[H->Index[child / 2]]; child /= 2)	//目的是确定x位置
	{
		H->Index[child] = H->Index[child / 2];
		SwapIndex(H, child);
	}

	H->Index[child] = v;
	SwapIndex(H, child);
}
/*删除并返回最小边的索引*/
int DeleteIndex(IndexHeap H)
{
	int v = H->Index[1];
	int x = H->Ass->dist[H->Index[H->Size--]];
	int parent, child;
	for ( parent = 1; parent * 2 <= H->Size; parent = child)
	{
		child = parent * 2;
		if(H->Ass->dist[H->Index[child + 1]] < H->Ass->dist[H->Index[child]] && child != H->Size)
			child++;
		if(x > H->Ass->dist[H->Index[child]])
		{
			H->Index[parent] = H->Index[child];
			SwapIndex(H, parent);
		}
		else
			break;
	}
	H->Index[parent] = H->Index[H->Size + 1];
	SwapIndex(H, parent);
	return v;
}
/*修改dist[v]减少为x, 并调整堆*/
void Fix_up(IndexHeap H, int v, int x)
{
	H->Ass->dist[v] = x;
	int child;
	//切记!!!!!!!!!!!!!!!!!!!   这里使用reverse来访问堆!!!!而不是直接用v!!!(被坑傻过!!!)
	for (child = H->Ass->reverse[v]; x < H->Ass->dist[H->Index[child / 2]]; child /= 2)
	{
		H->Index[child] = H->Index[child / 2];
		SwapIndex(H, child);
	}
	H->Index[child] = v;
	SwapIndex(H, child);
}

int Prim(MGraph G, IndexHeap H)
{
	int MinTree = 0;
	for (int i = 1; i < G->Nv; i++)
		H->Ass->dist[i] = G->V[0][i];
	H->Ass->dist[0] = 0;			//表示0顶点已经是树中一部分

	for (int i = 1; i < G->Nv; i++)	//除0以外,所有顶点加入索引堆,表示还不是生成树内的顶点
		InsertIndex(H, i);

	while (H->Size != 0)	//队列中都是未加入生成树的顶点
	{

		int v = DeleteIndex(H);	//dist[v]最小,v加入生成树

		MinTree += H->Ass->dist[v];	//统计权重
		H->Ass->dist[v] = 0;	//标记该顶点已加入生成树

		for (int i = 0; i < G->Nv; i++)
			if(H->Ass->dist[i] != 0 && G->V[v][i] < INF)
				if(G->V[v][i] < H->Ass->dist[i])
					Fix_up(H, i, G->V[v][i]);	//找到最小dist后,在索引优先队列中更新dist

	}
	return MinTree;
}

int main()
{
	MGraph G = BuildGraph();
	IndexHeap H = CreateHeap(G->Nv);
	cout << Prim(G, H) << endl;

	system("pause");
	return 0;
}

下面是不采用优先队列的暴力算法

#include <iostream>
#include <cstring>
#include <climits>
using namespace std;
const int MaxV = 100;
/*边的定义*/
typedef struct ENode
{
	int v1, v2;
	int Weight;
} * Edge;
/*邻接矩阵存储图*/
typedef struct MGNode
{
	int Nv, Ne;
	int F[MaxV][MaxV];
	string Data[MaxV];
} * MGraph;
/*图的初始化*/
MGraph CreateMGraph(int Nv)
{
	MGraph G = new MGNode();
	G->Nv = Nv;
	for (int i = 0; i < G->Nv; i++)
		for (int j = 0; j < G->Nv; j++)
			G->F[i][j] = INT_MAX;
	return G;
}
/*边的插入*/
void InsertEdge(MGraph G, Edge E) //无向图要插入两次
{
	G->F[E->v1][E->v2] = E->Weight;
	G->F[E->v2][E->v1] = E->Weight;
}
/*建造一个图*/
MGraph BuildGraph()
{
	int Nv;
	cin >> Nv;
	MGraph G = CreateMGraph(Nv);
	cin >> G->Ne;
	Edge E = new ENode;
	for (int i = 0; i < G->Ne; i++)
	{
		cin >> E->v1 >> E->v2 >> E->Weight;
		InsertEdge(G, E);
	}
	delete E;
	return G;
}

/*邻接表图存生成的MST*/
struct AdjVNode
{
	int AdjV;
	int Weight;
	AdjVNode *Next;
};
typedef struct VNode
{
	AdjVNode *EdgeFirst;
	string Data;
} AdjList[MaxV];
typedef struct LGNode
{
	int Nv, Ne;
	AdjList L;
} * LGraph;
/*MST树初始化*/
LGraph CreateLGraph(int Nv)
{
	LGraph MST = new LGNode();
	MST->Nv = Nv;
	return MST;
}
/*MST边的插入*/
void InsertEdgeMST(LGraph MST, Edge E)
{
	AdjVNode *V = new AdjVNode;
	V->AdjV = E->v2;
	V->Weight = E->Weight;
	V->Next = MST->L[E->v1].EdgeFirst;
	MST->L[E->v1].EdgeFirst = V;
	//无向图,插入两条边
	V = new AdjVNode;
	V->AdjV = E->v1;
	V->Weight = E->Weight;
	V->Next = MST->L[E->v2].EdgeFirst;
	MST->L[E->v2].EdgeFirst = V;
}



int FindMinDist(MGraph G, int dist[])
{
	int MinDist = INT_MAX;
	int MinV;
	for (int i = 0; i < G->Nv; i++)
		if(dist[i] != 0 && dist[i] < MinDist)
		{
			MinDist = dist[i];
			MinV = i;
		}
	if(MinDist < INT_MAX)
		return MinV;
	else
		return -1;
}
int Prim(MGraph G, LGraph MST)
{
	int dist[MaxV], TotalWeight = 0;
	int parent[MaxV], V, W;
	int VCount = 0;
	Edge E = new ENode;
	memset(parent, 0, sizeof(parent));
	for (int i = 1; i < G->Nv; i++)
		dist[i] = G->F[0][i];

	dist[0] = 0;
	VCount++;
	parent[0] = -1;
	while (1)
	{
		V = FindMinDist(G, dist);
		if(V == -1)
			break;

		E->v1 = parent[V];
		E->v2 = V;
		E->Weight = dist[V];
		InsertEdgeMST(MST, E);
		TotalWeight += dist[V];
		dist[V] = 0;
		VCount++;

		for (int i = 0; i < G->Nv; i++)
		{
			if(dist[i] != 0 && G->F[V][i] < INT_MAX)
				if(G->F[V][i] < dist[i])
				{
					dist[i] = G->F[V][i];
					parent[i] = V;
				}
		}

	}
	if(VCount < G->Nv)
		TotalWeight = -1;
	return TotalWeight;
}

int main()
{
	MGraph G = BuildGraph();
	LGraph MST = CreateLGraph(G->Nv);
	cout << Prim(G, MST) << endl;
	AdjVNode *V;
	for (int i = 0; i < MST->Nv; i++)
	{
		V = MST->L[i].EdgeFirst;
		cout << i << "->";
		while (V)
		{
			cout << V->AdjV << "->";
			V = V->Next;
		}
		cout << "NULL" << endl;

	}

	system("pause");
	return 0;
}

结果
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值