A : Prim算法
题目描述
使用 prim 算法实现最小生成树
输入输出格式
输入
第一行两个整数 n,e。n (1≤n≤200000) 代表图中点的个数,e (0≤m≤500000) 代表边的个数。
接下来 e 行,每行代表一条边:
i j w
表示顶点 i 和顶点 j 之间有一条权重为 w 的边
输出
最小生成树所有边的权重和
数据结构与算法描述
构造节点结构体包括权、元素大小、下一个节点。
struct vertexnode {
int weight;
int element;
vertexnode* next;
vertexnode(){}
vertexnode(const int& element, const int& weight) {
this->element = element;
this->weight = weight;
this->next = NULL;
}
};
在图类中,插入函数与邻接链表类似,终点节点包含了该边的权重,将该节点插入到邻接链表中。因为是无向图,所以反向再来一遍。
class graph {
private:
int numvertices;//点的个数
int numedges;//边的个数
vertexnode* list;//邻接链表
public:
graph(int n, int e) {
numvertices = n;
numedges = e;
list = new vertexnode[n + 1];
for (int i = 1; i <= n; i++) {
list[i].element = i;
list[i].next = NULL;
list[i].weight = 0;
}
}
void insert(int i, int j, int w);
void prim();
};
void graph::insert(int i, int j, int w) {
vertexnode* insertnode = new vertexnode(j,w);//创造一个终点的节点
vertexnode* nextnode = list[i].next;//与起点相连的点
if (nextnode == NULL) {//没有相连的点,直接连
list[i].next = insertnode;
}
else {
list[i].next = insertnode;
insertnode->next = nextnode;
}
vertexnode* insertnode1 = new vertexnode(i, w);//因为是无向图,所以反向再来一遍
vertexnode* nextnode1 = list[j].next;
if (nextnode1 == NULL) {
list[j].next = insertnode1;
}
else {
list[j].next = insertnode1;
insertnode1->next = nextnode1;
}
numedges++;
}
Prim函数,利用最小堆,从1点开始循环,循环次数为定点数减一,将与这个点的邻接点且没有到达过的压入最小堆,将已到达的点从堆中删除,权重和更新,在堆中删除该被加入的节点。最后循环结束输出权重和。
void graph::prim() {
minHeap mh(numvertices);
reach[1] = 1;
vertexnode* nextnode = list[1].next;//第一个点相连的点
long long sumw = 0;//权重和
for (int i = 2; i <= numvertices; i++) {//只要numvertices-1次
while (nextnode != NULL) {//将所有的邻接点压入最小堆
if (reach[nextnode->element] != 1) {
vertexnode node(nextnode->element, nextnode->weight);
mh.push(node);
}
nextnode = nextnode->next;
}
while (reach[mh.top().element] == 1) {//已经到达的点删除
mh.pop();
}
sumw += mh.top().weight;
reach[mh.top().element] = 1;
nextnode = list[mh.top().element].next;//要把与该点相连的点压入堆
mh.pop();
}
cout << sumw << endl;
}
完整代码(含注释)
#include<iostream>
using namespace std;
struct vertexnode {
int weight;
int element;
vertexnode* next;
vertexnode(){}
vertexnode(const int& element, const int& weight) {
this->element = element;
this->weight = weight;
this->next = NULL;
}
};
class minHeap {
private:
vertexnode* heap;//元素数组
int heapSize; //堆中的元素个数
int arrayLength;
public:
minHeap(int n) {
heapSize = 0;
heap = new vertexnode[n + 1];
arrayLength = n + 1;
}
~minHeap() { delete[] heap; }
bool empty() { return heapSize == 0; }//判断heapSize是否为0.
int Size() { return heapSize; }//返回heapSize的值
void push(vertexnode x);
vertexnode& top() { return heap[1]; }//输出根
void pop();
};
void minHeap::push(vertexnode x) {//插入
if (heapSize == arrayLength - 1) {//没有足够的空间
arrayLength *= 2;
vertexnode* newheap = new vertexnode[arrayLength];//数组长度加倍
for (int i = 1; i <= heapSize; i++) {
newheap[i] = heap[i];
}
delete[] heap;
heap = newheap;
}
int currentNode = ++heapSize;//从新的叶节点开始
while (currentNode != 1 && x.weight < heap[currentNode / 2].weight) {//当currentNode没到根节点,并且父节点比插入的大时,继续上升
heap[currentNode] = heap[currentNode / 2];//将该元素的父节点下移
currentNode /= 2;//移向父节点
}
heap[currentNode] = x;
}
void minHeap::pop() {//删除
vertexnode lastElement = heap[heapSize--];//最后一个元素
int currentNode = 1;//从当前被删掉的最小元素的位置开始
int child = 2;//currentNode的孩子
while (child <= heapSize) {
if (child<heapSize && heap[child].weight>heap[child + 1].weight)child++;//找到左右孩子中更小的
if (lastElement.weight <= heap[child].weight)break;//当孩子比要插入的大,则可以插入了,跳出循环
heap[currentNode] = heap[child];//将较小的孩子上移
currentNode = child;//currentNode向下移
child *= 2;
}
heap[currentNode] = lastElement;
}
int reach[2000000] = { 0 };
class graph {
private:
int numvertices;//点的个数
int numedges;//边的个数
vertexnode* list;//邻接链表
public:
graph(int n, int e) {
numvertices = n;
numedges = e;
list = new vertexnode[n + 1];
for (int i = 1; i <= n; i++) {
list[i].element = i;
list[i].next = NULL;
list[i].weight = 0;
}
}
void insert(int i, int j, int w);
void prim();
};
void graph::insert(int i, int j, int w) {
vertexnode* insertnode = new vertexnode(j,w);//创造一个终点的节点
vertexnode* nextnode = list[i].next;//与起点相连的点
if (nextnode == NULL) {//没有相连的点,直接连
list[i].next = insertnode;
}
else {
list[i].next = insertnode;
insertnode->next = nextnode;
}
vertexnode* insertnode1 = new vertexnode(i, w);//因为是无向图,所以反向再来一遍
vertexnode* nextnode1 = list[j].next;
if (nextnode1 == NULL) {
list[j].next = insertnode1;
}
else {
list[j].next = insertnode1;
insertnode1->next = nextnode1;
}
numedges++;
}
void graph::prim() {
minHeap mh(numvertices);
reach[1] = 1;
vertexnode* nextnode = list[1].next;//第一个点相连的点
long long sumw = 0;//权重和
for (int i = 2; i <= numvertices; i++) {//只要numvertices-1次
while (nextnode != NULL) {//将所有的邻接点压入最小堆
if (reach[nextnode->element] != 1) {
vertexnode node(nextnode->element, nextnode->weight);
mh.push(node);
}
nextnode = nextnode->next;
}
while (reach[mh.top().element] == 1) {//已经到达的点删除
mh.pop();
}
sumw += mh.top().weight;
reach[mh.top().element] = 1;
nextnode = list[mh.top().element].next;//要把与该点相连的点压入堆
mh.pop();
}
cout << sumw << endl;
}
int main() {
int n, e;
cin >> n >> e;
graph g(n, e);
for (int i = 0; i < e; i++) {
int y, j, w;
cin >> y >> j >> w;
g.insert(y, j, w);
}
g.prim();
return 0;
}
B : Kruskal算法
题目描述
使用 kruskal 算法实现最小生成树
输入输出格式
输入
第一行两个整数 n,e。n (1≤n≤200000) 代表图中点的个数,e (0≤m≤500000) 代表边的个数。
接下来 e 行,每行代表一条边:
i j w
表示顶点 i 和顶点 j 之间有一条权重为 w 的边
输出
最小生成树所有边的权重和
数据结构与算法描述
将输入的边的两个顶点和权重插入结构体数组中,将结构体数组压入最小堆,然后进行一个循环,当边数为零或者所加边数等于定点数减一。在循环中,取出堆顶的边,利用并查集,判断该边是否在同一个集合,如果不在则合并,如果在则不能加,避免成环。
for (int i = 1; i <= e; i++) {//结构体数组
cin >> edge[i].vertex1 >> edge[i].vertex2 >> edge[i].weight;
int t = 0;
}
int k = 0;
long long sumw=0;
minHeap heap(1);//最小堆
heap.initialize(edge, e);
while (e > 0 && k < n - 1) {
edgenode x = heap.top();
heap.pop();
e--;
int a = find(x.vertex1);//找该边两个顶点的根
int b = find(x.vertex2);
if (a != b) {//如果不在同一个集合,进行合并
unite(a, b);
k++;
sumw += x.weight;
}
}
cout << sumw << endl;
并查集find函数:第一个while循环找根,第二个while循环,将所有的点都连到根上,降低高度,节省找根的时间。Unite函数:将两个数合并,两个跟一个为另一个的父节点。
int find(int theelement) {
int root = theelement;
while (parent[root] != 0) {//找根
root = parent[root];
}
while (theelement != root) {//将所有的点都连到根上,降低高度
int t = parent[theelement];;
parent[theelement] = root;
theelement = t;
}
return root;
}
void unite(int a, int b) {//两个树合并
parent[b] = a;
}
完整代码(含注释)
#include<iostream>
using namespace std;
struct edgenode {
long long weight;
int vertex1;
int vertex2;
}edge[5000000];
class minHeap {
private:
edgenode* heap;//元素数组
int heapSize; //堆中的元素个数
public:
minHeap(int n) {
heapSize = 0;
heap = new edgenode[n + 1];
}
~minHeap() { delete[] heap; }
bool empty() { return heapSize == 0; }//判断heapSize是否为0.
int Size() { return heapSize; }//返回heapSize的值
edgenode top() { return heap[1]; }//输出根
void pop();
void initialize(edgenode* theheap, int theSize);
};
void minHeap::pop() {//删除
edgenode lastElement = heap[heapSize--];//最后一个元素
int currentNode = 1;//从当前被删掉的最小元素的位置开始
int child = 2;//currentNode的孩子
while (child <= heapSize) {
if (child<heapSize && heap[child].weight>heap[child + 1].weight)child++;//找到左右孩子中更小的
if (lastElement.weight <= heap[child].weight)break;//当孩子比要插入的大,则可以插入了,跳出循环
heap[currentNode] = heap[child];//将较小的孩子上移
currentNode = child;//currentNode向下移
child *= 2;
}
heap[currentNode] = lastElement;
}
void minHeap::initialize(edgenode* theHeap, int theSize) {//初始化
delete[] heap;
heap = new edgenode[theSize + 1];
for (int i = 1; i <= theSize; i++) {
heap[i] = theHeap[i];
}
heapSize = theSize;
for (int root = heapSize / 2; root >= 1; root--) {//从数组中最右一个有孩子的节点开始
edgenode rootElement = heap[root];//子树的根
int child = 2 * root;//rootElement的孩子位置
while (child <= heapSize) {
if (child<heapSize && heap[child].weight>heap[child + 1].weight)child++;//找到孩子里较小的
if (rootElement.weight <= heap[child].weight)break;//如果子树的根小于其孩子,则可以跳出循环
//如果孩子较小
heap[child / 2] = heap[child];//将孩子上移
child *= 2;
}
heap[child / 2] = rootElement;
}
}
int parent[2000000] = { 0 };
int find(int theelement) {
int root = theelement;
while (parent[root] != 0) {//找根
root = parent[root];
}
while (theelement != root) {//将所有的点都连到根上,降低高度
int t = parent[theelement];;
parent[theelement] = root;
theelement = t;
}
return root;
}
void unite(int a, int b) {//两个树合并
parent[b] = a;
}
int main() {
int n, e;
cin >> n >> e;
for (int i = 1; i <= e; i++) {//结构体数组
cin >> edge[i].vertex1 >> edge[i].vertex2 >> edge[i].weight;
int t = 0;
}
int k = 0;
long long sumw=0;
minHeap heap(1);//最小堆
heap.initialize(edge, e);
while (e > 0 && k < n - 1) {
edgenode x = heap.top();
heap.pop();
e--;
int a = find(x.vertex1);//找该边两个顶点的根
int b = find(x.vertex2);
if (a != b) {//如果不在同一个集合,进行合并
unite(a, b);
k++;
sumw += x.weight;
}
}
cout << sumw << endl;
return 0;
}
如能打赏,不胜感激[叩谢]。