#include<iostream>
using namespace std;
#define DEFAULT 111
#define VERTEX_DEFAULT 66 //默认最大顶点数
#define ADJ_DEFAULT 111 //其实多余的,跟定点数一样即可
#define WEIGHT_INF 11111
//typedef char vertex;
//typedef int edge;
struct Vertex //顶点结构体
{
int v_no; //顶点的序号,习惯性约定,表示其在图中的位置
char v_type; //顶点的类型 此处设置为字符型
Vertex(int no, char type) :v_no(no), v_type(type){} //顶点的结构体构造函数
Vertex(){}
~Vertex(){} //顶点的析构函数
};
struct Edge //边结构体
{
int from; //边尾
int to; //边头
int weight; //边的权值
Edge(int f = 0, int t = 0, int w = WEIGHT_INF) :from(f), to(t), weight(w){} //边的结构体构造函数
~Edge(){} //边的析构函数
};
class Union_find //等价类,用于实现kruskal算法
{
private: //采用静态循环链表思想
//静态链表,这种链表用数组来描述,主要为了解决没有指针或者不用指针的情况下具备链表插入删除操作便捷的特性。
int sz;
int *root; //标识顶点集合的顶点索引
int *next; //指示链表中的下一个顶点
int *length; //指示链表中的顶点数目
public:
Union_find(int size = DEFAULT) :sz(size)
{
if (size < 1)
{
cout << "数组大小为空或者size不合法!" << endl;
exit(1);
}
if ((root = new int[sz]) == NULL)
{
cout << "root空间申请失败!" << endl;
exit(1);
}
if ((next = new int[sz]) == NULL)
{
cout << "next空间申请失败!" << endl;
exit(1);
}
if ((length = new int[sz]) == NULL)
{
cout << "length空间申请失败!" << endl;
exit(1);
}
}
~Union_find()
{
delete root;
delete next;
delete length;
}
void Initialize(); //初始化静态链表成员
int Find(int v); //判断两个是否在同一个集合中,查找一个给定结点v的根结点的过程称为FIND
void Unoin(int v, int u); //合并两个连通分量
friend class Graph;
};
void Union_find::Initialize()
{
for (int i = 0; i < sz; i++)
{
root[i] = i;
next[i] = i;
length[i] = 1;
}
}
int Union_find::Find(int v)
{
if (v < 0 || v >= sz)
{
cout << "v不是图的顶点,查找失败!" << endl;
return -1;
}
return root[v];
}
void Union_find::Unoin(int v, int u)
{
if (Find(v) == -1 || Find(u) == -1)
{
cout << "此边不是图的边,合并两个连通分量失败!" << endl;
return;
}
if (Find(v) == Find(u)) //如果在外面先判定了,此选择语句可省略 判定两顶点是不是在同一个连通分量中
{
cout << "边的两顶点在一个连通分量中,不用合并!" << endl;
return;
}
int rt;
if (length[root[v]] < length[root[u]])
{
rt = root[v]; //把顶点少的连通分量当多的连通分量的子树
length[root[u]] += length[rt];
root[rt] = root[u];
for (int j = next[rt]; j != rt; j = next[j]) //next是循环的静态链表
{
root[j] = root[u];
}
swap(next[rt], next[u]);
}
else
{
rt = root[u];
length[v] += length[rt];
root[rt] = root[v];
for (int i = next[rt]; i != rt; i = next[i])
{
root[i] = root[v];
}
swap(next[rt], next[v]);
}
}
class Heap //用优先级队列来实现kruskal算法的第一步
{
private:
Edge *heap; //堆节点数组
int maxsize;
int curr;
public:
Heap(int max = DEFAULT) :maxsize(max)
{
curr = 0;
if (max <= 0)
{
cout << "这个max非法!" << endl;
}
if ((heap = new Edge[max]) == NULL)
{
cout << "内存空间申请失败!" << endl;
}
}
~Heap() {}
void Build_min_heap(Edge *eg, int n); //用数组初始化堆
void Insert(Edge &p); //往堆中插入一个节点
void Sift_up(int pos); //从pos位置向上调整,直到heap[pos]到指定位置
void Sift_down(int pos); //从pos位置开始向下调整堆,直至其为最小堆
int Lchild(int pos); //获取左孩子在heap数组中的位置
int Rchild(int pos); //获取右孩子在heap数组中的位置
void Print(); //遍历堆
Edge* Remove_min(); //删除堆顶元素,并返回堆顶节点的地址
void Swap(Edge &node1, Edge &node2); //交换两个节点
friend class Graph;
};
void Heap::Build_min_heap(Edge *eg, int n)
{
if (n<1 || n>maxsize)
{
cout << "这个n非法!" << endl;
exit(1);
}
while (curr < n)
{
heap[curr] = eg[curr];
curr++;
}
if (curr == 1)
{
cout << "堆中只有一个元素,不用调整!" << endl;
exit(1);
}
for (int i = n / 2 - 1; i >= 0; i--)
{
Sift_down(i);
}
}
void Heap::Insert(Edge &p) //往堆中插入一个节点
{
if (curr == maxsize)
{
cout << "堆元素数组已满,无法插入!";
return;
}
heap[curr++] = p;
if (curr > 1)
{
Sift_up(curr - 1);
}
else
{
cout << "为堆的根,不用调整!" << endl;
}
}
void Heap::Swap(Edge &node1, Edge &node2) //交换两个节点
{
Edge temp;
temp = node1;
node1 = node2;
node2 = temp;
}
inline int Heap::Lchild(int pos) //获取左孩子在heap数组中的位置
{
return 2 * pos + 1;
}
inline int Heap::Rchild(int pos) //获取右孩子在heap数组中的位置
{
return 2 * pos + 2;
}
void Heap::Sift_up(int pos) //从pos位置向上调整,直到heap[pos]到指定位置
{
if (pos == 0)
{
cout << "不用调整,此为堆的第一个节点!" << endl;
return;
}
while (pos > 0)
{
if (heap[pos].weight < heap[(pos - 1) / 2].weight)
{
Swap(heap[pos], heap[(pos - 1) / 2]);
pos = (pos - 1) / 2;
}
else
{
return;
}
}
}
void Heap::Sift_down(int pos) //从pos位置开始向下调整堆,直至其为最小堆
{
int r, l;
int index;
while (pos <= (curr - 2) / 2 && pos >= 0)
{
r = Rchild(pos);
l = Lchild(pos);
index = l;
if (r < curr && heap[r].weight < heap[l].weight)
{
index = r;
}
if (heap[index].weight < heap[pos].weight)
{
Swap(heap[index], heap[pos]);
pos = index;
}
else
{
return;
}
}
}
void Heap::Print() //遍历堆
{
for (int i = 0; i < curr; i++)
{
cout << heap[i].weight << " ";
}
}
Edge* Heap::Remove_min() //删除堆顶元素,并返回堆顶节点的地址
{
if (curr == 0)
{
cout << "堆已空,删除失败!" << endl;
exit(1);
}
Edge *temp = new Edge; //必须用new node初始化,因为要往temp指向的地址里写入数据
(*temp) = heap[0]; //把堆顶元素赋给temp指向的节点空间
heap[0] = heap[--curr];
if (curr > 1)
{
Sift_down(0);
}
return temp;
}
class Graph //图类 图的抽象数据模型(ADT)
{
private:
int **adj; //adjacent(邻接矩阵)
int v_num; //图中顶点最大数目
int v_curr; //当前图中顶点的个数
int e_num; //图中边的最大数目 可以不要,直接用v_num*v_num代替
int e_curr; //当前图中的边数
bool flag; //判断图是有向图还是无向图 0---为无向图 1---为有向图
Vertex *vertex; //顶点数组
Edge *edge; //边数组
public:
Graph(int v_n = VERTEX_DEFAULT, bool f = 0) :flag(f), v_num(v_n)
{
v_curr = 0;
e_curr = 0;
if (v_n < 0)
{
cout << "顶点或者边的最大数目不合法!" << endl;
exit(1);
}
e_num = v_n*(v_n - 1); //n个顶点的最大边数为(n*(n-1)/2)
adj = new int*[v_n]; //开辟行
for (int i = 0; i < v_n; i++)
{
adj[i] = new int[v_n];
}
if (adj == NULL)
{
cout << "邻接矩阵空间申请失败!";
exit(1);
}
if ((vertex = new Vertex[v_n]) == NULL)
{
cout << "顶点数组空间申请失败!";
exit(1);
}
if ((edge = new Edge[e_num]) == NULL)
{
cout << "边数组空间申请失败!";
exit(1);
}
}
~Graph()
{
for (int i = 0; i < v_num; ++i)
{
delete adj[i];//由里至外,进行释放内存。 ---------------我认为是delete [] p[i];!!!!!后来查到的帖子证实
adj[i] = NULL;//不要忘记,释放空间后p[i]不会自动指向NULL值,还将守在原处,只是释放内存而已,仅此而已。
}
delete[] adj;
adj = NULL;
delete[] vertex;
delete[] edge;
}
int Vertices_num(); //返回图的顶点个数
int Edges_num(); //返回图的边数
void Initi