使用Visual Studio community 2022运行。
//虽然写起来还是比较慢的,但是,比起从头开始写快很多了。
//kruskal算法就一点点
//所以,我认为,在完成基本要求后,对于一段代码,如果可能的话,那就仔细探究这段代码的通用性,严格检查其错误,
//让这段代码变成可以快速复制到别的地方的代码。
#include <iostream>
#include <limits>
#include <memory>
#include <stdlib.h>
#include <string>
#include <vector>
using namespace std;
//我要建立一个堆结构,这个堆随本程序运行而建立,随本程序结束而消失,(或者把控制权给别的程序)。
// 这个堆不能进行复制,只能进行创建,修改,交权,删除。
// 我觉得,堆是给数字排序的,但是仅仅给数字排序没有意义。 需要给数字和某种数据类型建立联系。
//所以参与建立堆的数据类型是带有明确的可比较的数(比如正整数)的数据类型。
// 但是我想把程序写成泛型的。 因为只要带有课比较的数的数据类型都可以建立堆。
// 我不会这样写代码。 可能这需要c++ 的泛型技术或者c里面强制更改指针类型
// 我暂时不会那样写代码。我的代码要有通用性,至少做到可以把大段代码复制过去,改改就能用于新的类型。
//是不是在数据结构这门课上写通用代码不合适啊,有专门熟悉一门编程语言的课或者书。
//Task 是通用的数据类型
typedef struct Edge Task;
//Finger的设计目的是作为堆的元素,使堆能控制int类型以外的数据。
//因为Finger是个对象,而教材上给的只是个整形,所以要给Finger加许多操作。
struct Finger
{
Finger(int c1, Task* c2) :P(c1), t(c2) {}
Finger() = default;
Finger(const Finger& f) :P(f.P), t(f.t) {}
int P;
Task* t;
//埋下隐患1 这里只给Finger的P赋值优先级
Finger& operator=(const int& i)
{
P = i;
return *this;
}
Finger& operator=(const Finger& f) {
P = f.P;
t = f.t;
return *this;
}
};
//重载了Finger的六个关系运算符。
bool operator< (const Finger& h1, const Finger& h2)
{
return h1.P < h2.P;
}
bool operator<= (const Finger& h1, const Finger& h2)
{
return h1.P <= h2.P;
}
bool operator> (const Finger& h1, const Finger& h2)
{
return h1.P > h2.P;
}
bool operator>= (const Finger& h1, const Finger& h2)
{
return h1.P >= h2.P;
}
bool operator== (const Finger& h1, const Finger& h2)
{
return h1.P == h2.P;
}
bool operator!= (const Finger& h1, const Finger& h2)
{
return h1.P != h2.P;
}
//struct Edge是Kruskal算法需要使用的类型。Edge将被构造,Finger类型指向Edge,Finger类型是堆的元素。
//Edge最后被放入vector中,作为结果。
typedef int Vertex;
struct Edge {
Vertex v1;
Vertex v2;
int Weight;
Edge() = default;
Edge(const Edge& e1) {
v1 = e1.v1;
v2 = e1.v2;
Weight = e1.Weight;
}
};
Edge* NewEdge(Vertex v1,Vertex v2,int Weight)
{
//学号不检查,默认为可以为0或负数
Edge* e = new Edge;
e->v1 = v1;
e->v2 = v2;
e->Weight = Weight;
return e;
}
bool operator==(const Edge& e1, const Edge& e2)
{
if (e1.Weight != e1.Weight) return false;
if (e1.v1 == e2.v1 && e1.v2 == e2.v2|| e1.v1 == e2.v2 && e2.v1 == e1.v2)return true;
}
typedef struct HNode* Heap; /* 堆的类型定义 */
typedef Finger ElementType;/*Finger是堆的元素,该元素能指向其他类型*/
struct HNode {
ElementType* Data; /* 存储元素的数组 */
int Size; /* 堆中当前元素个数 */
int Capacity; /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */
#define MAXDATA 10000 /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */
#define MINDATA -10000
MinHeap CreateHeap(int MaxSize)
{ /* 创建容量为MaxSize的空的最小堆 */
MinHeap H = (MinHeap)malloc(sizeof(struct HNode));
H->Data = (ElementType*)malloc((MaxSize + 1) * sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Data[0] =ElementType(-10000,NULL); /* 定义"哨兵"为x小于堆中所有可能元素的值*/
// 我 我为这个操作重载了=
return H;
}
bool IsFull(MinHeap H)
{
return (H->Size == H->Capacity);
}
bool IsEmptyH(MinHeap H) {
return H->Size == 0;
}
bool Insert(MinHeap H, ElementType X)
{ /* 将元素X插入最小堆H,其中H->Data[0]已经定义为哨兵 */
int i;
if (IsFull(H)) {
printf("最小堆已满");
return false;
}
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
for (; H->Data[i / 2] > X; i /= 2)
H->Data[i] = H->Data[i / 2]; /* 上滤X */
H->Data[i] = X; /* 将X插入 */
return true;
}
ElementType DelRoot(MinHeap H)
{
//这个操作不管没有第0项的bug;
if (IsEmptyH(H)) {
printf("MinHeap is Empty\n");
return H->Data[0];
}
ElementType anwser = H->Data[1];
/* 用最xiao堆中最后一个元素从根结点开始向上过滤下层结点 */
ElementType X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
int Parent, Child;
for (Parent = 1; Parent * 2 <= H->Size; Parent = Child) {
Child = Parent * 2;
if ((Child != H->Size) && (H->Data[Child].P > H->Data[Child + 1].P))
Child++; /* Child指向左右子结点的较xiao者 */
if (X.P <= H->Data[Child].P) break; /* 找到了合适位置 */
else /* 下滤X */
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
return anwser;
}
//20210820我要做的是简单的并查集合,只有合并和查找操作。
//用数组实现,
//数组的元素应该是个结构啊,老师给的代码怎么没有呢
//看不懂老师给的代码
//她可能给的是那个最简表示法,不要数据,既然不要数据,
//那就不应该有ElementType
//我很难看懂老师的通用化操作,以及她为了教学而简化的各种细节,
// 实际上 复杂一点反而更容易懂
//我首先不要直接追求通用化,我首先应该最求把数据结构和算法写出来,
//直接用int写最好。
/*20210906写非最简版本
*/
//我要更改Element为我需要的类型Edge
//我弄错了,并查集要存的是顶点,而不是边。两个顶点都入了集,就回路了。
/*这个并查集的Node元素的设计似乎没必要。它的Element类型没什么运用。它的功能过于简单,如果要追究它的元素应该倒过来
* 让别的数据指向这个并查集的元素。
*/
struct Node
{
int Root = -1;
int Element = 0;
Node() = default;
Node(int i1, int i2) :Root(i1), Element(i2) {}
};
int GoToRoot(int X, const vector<Node>& s1)
{
//检查该元素是否在该集合内,因为是0~size-1与实体对应的集合,所以比较两个
//特殊数据就可以了
if (X > -1 && X < s1.size())
{
if (s1[X].Root < 0) return X;
else
return GoToRoot(s1[X].Root, s1);
}
else
return -1;
}
int FindElement(int X, const vector<Node>& s1)
{
//函数名不好,实际上是找到该元素的根的位置。
for (int i = 0; i != s1.size(); ++i)
if (s1[i].Element == X)
return GoToRoot(i, s1);
return -1;
}
void UnionRoot(vector<Node>& S, int Root1, int Root2)
{ /* 这里默认Root1和Root2是不同集合的根结点 */
/* 保证小集合并入大集合 */
if (S[Root2].Root < S[Root1].Root) { /* 如果集合2比较大 */
S[Root2].Root += S[Root1].Root; /* 集合1并入集合2 */
S[Root1].Root = Root2;
}
else { /* 如果集合1比较大 */
S[Root1].Root += S[Root2].Root; /* 集合2并入集合1 */
S[Root2].Root = Root1;
}
}
//bool Union(vector<Node>& S, int X1, int X2)
//{
// auto r1 = FindElement(X1, S);
// auto r2 = FindElement(X2, S);
// auto Same = r1 == r2;
// if (!Same)
// UnionRoot(S, r1, r2);
// return Same;
//}
bool Union2(vector<Node>& S, int X1, int X2)
{
auto r1 = GoToRoot(X1, S);
auto r2 = GoToRoot(X2, S);
auto Same = r1 == r2;
if (!Same)
UnionRoot(S, r1, r2);
return Same;
}
//用邻接表存储 you权有向图。
//所以要复习一遍邻接表的构造。
//如果你要用到邻接表,你又会发现,你好像只能用动态内存来存 出 或 入 的数组。
// 我的邻接链表是出度和入度都有的。
//当你要表示有权图的时候,是不是要把权重写道listnode里面呢?有没有别的办法?
//这样权重被存储了两次。出一次,入一次。
//更新:本次要用于kruskal算法,这个算法对无向图才有效,所以,要在buildgraph上建立有向图。
//如果要提高效率,那么不应该区分出度和入度,只用一个度就够了。否则就太浪费了。
//所以,在构建Edge时只用一半就行了。
struct ListNode
{
int vertex;
int weight;
struct ListNode* next;
};
struct Adjacency_List {
int vertex_N;
struct ListNode** point_me;
struct ListNode** point_other;
};
typedef int Vertex;
struct Adjacency_List* set_vertex_N(struct Adjacency_List* pa, int n)
{
//无检查,默认你输入的全部正确;
//你可以检查一下,whether pa所指向的Adjacency_List had been initialized or whether n more than 0;
//I just ignore it.
//这个函数就是分配两个数组,数组里存指针,指针是链表头,
pa->vertex_N = n;
pa->point_me = (struct ListNode**)malloc(sizeof(struct ListNode*) * n);
pa->point_other = (struct ListNode**)malloc(sizeof(struct ListNode*) * n);
//因为是初始化,所以,我要清理一下,让上述数组的每个元素都是NULL
for (int i = 0; i != n; ++i)
{
pa->point_me[i] = NULL;
pa->point_other[i] = NULL;
}
return pa;
}
struct Adjacency_List* insert_edge(struct Adjacency_List* const pa, int vertex1, int vertex2, int weight) {
//vertex1----> vertex2
struct ListNode* temp = (struct ListNode*)malloc(sizeof(struct ListNode));
temp->vertex = vertex1;
temp->next = pa->point_me[vertex2];
temp->weight = weight;
pa->point_me[vertex2] = temp;
temp = (struct ListNode*)malloc(sizeof(struct ListNode));
temp->vertex = vertex2;
temp->next = pa->point_other[vertex1];
temp->weight = weight;
pa->point_other[vertex1] = temp;
return pa;
}
void build_graph_AC(struct Adjacency_List* pa)
{
/*这是一个无向图版本,实际上它还是有图,只不过,正反各insert一次*/
int vertexN;
puts("输入顶点数");
scanf_s("%d", &vertexN);
set_vertex_N(pa, vertexN);
puts("输入vertex1 <----> vertex2, 权重,\n示例:0 2 100\n输入三个负数结束\n示例:-1 -1 -1");
int vertex1, vertex2, weight;
scanf_s("%d %d %d", &vertex1, &vertex2, &weight);
while (vertex1 >= 0) {
insert_edge(pa, vertex1, vertex2, weight);
insert_edge(pa, vertex2, vertex1, weight);
scanf_s("%d %d %d", &vertex1, &vertex2, &weight);
}
}
//struct TwoIntP {
// /*用于保存结果*/
// int* p1;
// int size1;
// int* p2;
// int size2;
//};
//void show_distance_or_path(int d[], int n) {
// for (int i = 0; i < n; ++i) {
// printf("%d ", d[i]);
// }
// printf("\n");
//}
vector<Edge> Kruskal(const struct Adjacency_List& g)
{
MinHeap H=CreateHeap(g.vertex_N*(g.vertex_N-1)/2);
for (int i = 0; i != g.vertex_N; ++i)
{
auto* po = g.point_other[i];
while (po)
{
auto v2 = po->vertex;
if (v2>i)/*避免Graph的来回指向的冗余*/
{
Edge* ep = NewEdge(i, v2, po->weight);
Insert(H, Finger(po->weight, ep));
}
po = po->next;
}
}
vector<Node> vn;
vn.resize(g.vertex_N);
vector<Edge> ve;
do
{
Edge e1=*(DelRoot(H).t);
if (GoToRoot(e1.v1, vn) != GoToRoot(e1.v2, vn))
{
ve.push_back(e1);
Union2(vn, e1.v1, e1.v2);
}
} while (ve.size() < static_cast<unsigned long long>(g.vertex_N) - 1 && !IsEmptyH(H));
if (ve.size() < g.vertex_N - 1)
{
cerr << "ERROR:这个图不连通!" << endl;
}
return ve;
}
int main()
{
struct Adjacency_List* pa = new Adjacency_List;
build_graph_AC(pa);
vector<Edge> ve = Kruskal(*pa);
for (const auto& c : ve)
{
cout << "(" << c.v1 << " , " << c.v2 << ")" << " ______路程: " << c.Weight << endl;
}
}