图的基类定义
由于使用模板基类template定义时,Visual Studio编译会出错(本人目前还没有找到很好的解决办法), 所以这里没有使用模板基类的方法,而是使用了typedef定义类型别名的方式,要修改类型的时候需要到.h头文件中修改T和E的定义。
基类存在的目的是:1.规定图需要实现的功能函数;2.用户使用图是可以通过Graph类型定义对象,从而可以忽略掉图的储存结构
#pragma once
#define DefaultVertex 30
typedef int E; //边的权值
typedef int T; //顶点类型
class Graph
{
protected:
int numVertex;
int numEdge;
int maxVertex;
public:
Graph(int sz = DefaultVertex):maxVertex(sz),numEdge(0),numVertex(0){}
bool isEmpty()const {
return (numEdge == 0) ? true : false;
}
bool isFull()const {
return (numEdge == maxVertex * (maxVertex - 1) / 2) ? true : false;
}
int numOfVertex() { return numVertex; }
int numOfEdge() { return numEdge; }
virtual T getValue(int i) = 0; //去顶点i的值,i不合理返回0
virtual E getWeight(int v1, int v2) = 0;
virtual int getFirstNeighbor(int v) = 0;
virtual int getNextNeighbor(int v, int w) = 0;
virtual bool insertVertex(const T vertex) = 0;
virtual bool insertEdge(int v1, int v2, E weight) = 0;
virtual bool removeVertex(int v) = 0;
virtual bool removeEdge(int v1, int v2) = 0;
};
};
图的邻接表实现
邻接表储存需要定义两个结构体,即顶点和边
这里重载了输入输出函数,实现了由键盘输入建树,但是在基类Graph中并没有实现输入输出的重载,所以导致使用基类定义的实例对象无法实现cin>>方式的输入和cout<<方式的输出,所以基类的存在有点失去了意义(基类存在的意义是使用户可以忽略掉图的储存结构,直接使用图的各种功能)
Graphlnk类声明
//Graphlnk.h
#pragma once
#include"Graph.h"
#include<iostream>
using namespace std;
struct Edge { //边节点的定义
int next; //边的邻接顶点的位置
E weight; //边上的权值
Edge* link; //下一条边链指针
bool operator!=(Edge& R)const {
return (next != R.next) ? true : false;
}
};
struct Vertex {
T data;
Edge* adj; //边链表的头指针,链接顶点的其中一条边A,顶点剩余的边B,C,D由B,C,D相互连接
};
class Graphlnk:public Graph
{
friend istream& operator>>(istream& in, Graphlnk& G);
friend ostream& operator<<(ostream& out, Graphlnk& G);
private:
Vertex* VertexList; //顶点表头指针
public:
int getPosVertex(T value) {
for (int i = 0; i < numVertex; i++) {
if (VertexList[i].data == value)
return i;
}
return -1; //没有找到对应顶点,返回-1
}
int getNumOfVertex() {return numVertex;}
int getNumOfEdge() { return numEdge; }
public:
Graphlnk(int sz=DefaultVertex);
~Graphlnk();
bool insertVertex(const T value);
bool removeVertex(int v);
bool insertEdge(int v1,int v2,E weight);
bool removeEdge(int v1,int v2);
int getFirstNeighbor(int v);
int getNextNeighbor(int v, int w);
T getValue(int v);
E getWeight(int v1, int v2);
};
函数定义
//Graphlnk.cpp
#include "Graphlnk.h"
Graphlnk::Graphlnk(int sz)
{
//创建一个空的邻接表
maxVertex = sz; numEdge = 0; numVertex = 0;
VertexList = new Vertex[sz];
for (int i = 0; i < sz; i++) {
VertexList[i].adj = NULL;
}
}
Graphlnk::~Graphlnk()
{
//删除各各个边链表中的节点
//删除顶点表
for (int i = 0; i < numVertex; i++) {
Edge* p = VertexList[i].adj;
while (p != NULL) {
VertexList[i].adj = p->link; //adj在这里充当临时地址
delete p;
p = VertexList[i].adj;
}
}
delete[] VertexList;
}
bool Graphlnk::insertVertex(const T value)
{
//插入顶点,只需要将新的顶点加入到顶点表中即可
if(numVertex==maxVertex)
return false;
VertexList[numVertex++].data = value;
return true;
}
bool Graphlnk::removeVertex(int v)
{
//删除顶点,需要完成的操作:
//1.删除与该顶点连接的所有边
//从顶点A的边链表中找出一条边<A,B>,接着在顶点表中找到对应顶点B,然后再在B的边链表中找到与A相连的边,进行删除;然后删除边<A,B>;边数减1;
//重复上述操作,直到删除完边链表
//2.从顶点表中删除顶点(实际操作:用顶点表中最后一个元素替换要删除的元素);顶点数量减1
if (numVertex == 1 || v < 0 || v >= numVertex) return false; //只剩一个顶点,或参数不合理,不进行删除
Edge* p, * s, * t;
int k;
while (VertexList[v].adj != NULL) {
p = VertexList[v].adj; k = p->next; //取邻接顶点k
s = VertexList[k].adj; t = NULL; //找对称存放的边节点
while (s != NULL && s->next != v) {
t = s; s = s->link; //从顶点B的边链表中寻找对称边
}
if (s != NULL) {
if (t == NULL) VertexList[k].adj = s->link; //寻找到的边在边链表第一位
t->link = s->link;
delete s; //删除对称边
}
VertexList[v].adj = p->link; delete p; //删除边<A,B>
numEdge--; //边数减1
}
return true;
}
bool Graphlnk::insertEdge(int v1, int v2, E weight)
{
//若此边存在,或者参数不合理,函数返回false
if (v1 >= 0 && v1 < numVertex && v2 >= 0 && v2 < numVertex) {
Edge* q, * p = VertexList[v1].adj; //v1对应的边链表指针
while (p != NULL && p->next != v2) {
p = p->link; //寻找邻接顶点v2
}
if (p != NULL) return false; //如果p不空,说明在边链表中找到了v2,不插入
p = new Edge; q = new Edge;
p->next = v2; p->weight = weight;
p->link = VertexList[v1].adj; VertexList[v1].adj = p; //链入v1的边链表中(头插法)
q->next = v1; q->weight = weight;
q->link = VertexList[v2].adj; VertexList[v2].adj = q; //链入v2的边链表中(链表的头插法)
numEdge++; //边数加1
return true;
}
return false;
}
bool Graphlnk::removeEdge(int v1, int v2)
{
if (v1 != -1 && v2 != -1) {
Edge* p = VertexList[v1].adj, * q = NULL, * s = p;
while (p != NULL && p->next != v2) {
q = p; p = p->link;
}
if (p != NULL) { //找到被删除节点
if (s == p) VertexList[v1].adj = p->link; //该节点是首节点
else q->link = p->link;
delete p;
}
else return false; //没有找到被删除节点
p = VertexList[v2].adj;
while (p != NULL && p->next != v1) {
q = p; p = p->link;
}
if (p != NULL) { //找到被删除节点
if (s == p) VertexList[v1].adj = p->link; //该节点是首节点
else q->link = p->link;
delete p;
return true;
}
else return false; //没有找到被删除节点
}
return false;
}
int Graphlnk::getFirstNeighbor(int v)
{
if (v != -1) {
Edge* p = VertexList[v].adj;
if (p != NULL) {
return p->next;
}
}
return -1;
}
int Graphlnk::getNextNeighbor(int v, int w)
{
if (v != -1) {
Edge* p = VertexList[v].adj;
while (p != NULL && p->next != w) { //在边链表中寻找w顶点
p = p->link;
}
if (p != NULL && p->link != NULL) { //找到了w,w后面还有顶点
return p->link->next; //返回下一个邻接顶点的位置
}
}
return -1;
}
T Graphlnk::getValue(int v)
{
return (v >= 0 && v < numVertex) ? VertexList[v].data : 0;
}
E Graphlnk::getWeight(int v1, int v2)
{
if (v1 != -1 && v2 != -1) {
Edge* p = VertexList[v1].adj;
while (p != NULL && p->next != v2) {
p = p->link; //寻找邻接顶点v2
}
if (p != NULL) //找到邻接顶点v2
{
return p->weight;
}
}
return 0;
}
istream& operator>>(istream& in, Graphlnk& G)
{
cout << "输入顶点数,边数:" << endl;
int numVertex, numEdge;
in >> numVertex >> numEdge;
cout << endl << "依次输入顶点:" << endl;
T vertex;
while (numVertex--) {
in >> vertex; G.insertVertex(vertex);
}
T v1, v2; E weight;
cout << endl << "依次输入边的信息(顶点,顶点,权值):" << endl;
while (numEdge--) {
in >> v1 >> v2 >> weight;
int p1 = G.getPosVertex(v1); int p2 = G.getPosVertex(v2);
G.insertEdge(p1, p2, weight);
}
cout << endl << "输入完成" << endl;
return in;
}
ostream& operator<<(ostream& out, Graphlnk& G)
{
int numVertex = G.numVertex;
int tag[DefaultVertex][DefaultVertex] = { 0 }; //建立一个标记数组,边(A,B)输出一次后,tag[A][B]标记为1
out << "顶点数:" << G.numVertex << ",边数:" << G.numEdge << endl;
out << "边的信息如下:" << endl;
int pos = 0;
while (pos < G.numVertex) {
Edge* p = G.VertexList[pos].adj;
while (p != NULL) { //遍历边链表
if (tag[pos][p->next] == 0) { //如果tag[A][B]==0,代表该边没有输出过
out << "(" << G.VertexList[pos].data << "," << G.VertexList[p->next].data << "," << p->weight <<")"<< endl; //输出(顶点,顶点,权值)
tag[p->next][pos] = 1; //输出后,将该边标记为改为1
}
p = p->link;
}
pos++; //遍历顶点表
}
out << endl;
return out;
}
图的邻接矩阵实现
Graphmtx类声明
//Graphlnk.h
#pragma once
#include"Graph.h"
#include<iostream>
using namespace std;
struct Edge { //边节点的定义
int next; //边的邻接顶点的位置
E weight; //边上的权值
Edge* link; //下一条边链指针
bool operator!=(Edge& R)const {
return (next != R.next) ? true : false;
}
};
struct Vertex {
T data;
Edge* adj; //边链表的头指针,链接顶点的其中一条边A,顶点剩余的边B,C,D由B,C,D相互连接
};
class Graphlnk:public Graph
{
friend istream& operator>>(istream& in, Graphlnk& G);
friend ostream& operator<<(ostream& out, Graphlnk& G);
private:
Vertex* VertexList; //顶点表头指针
public:
int getPosVertex(T value) {
for (int i = 0; i < numVertex; i++) {
if (VertexList[i].data == value)
return i;
}
return -1; //没有找到对应顶点,返回-1
}
int getNumOfVertex() {return numVertex;}
int getNumOfEdge() { return numEdge; }
public:
Graphlnk(int sz=DefaultVertex);
~Graphlnk();
bool insertVertex(const T value);
bool removeVertex(int v);
bool insertEdge(int v1,int v2,E weight);
bool removeEdge(int v1,int v2);
int getFirstNeighbor(int v);
int getNextNeighbor(int v, int w);
T getValue(int v);
E getWeight(int v1, int v2);
};
函数实现
#include "Graphmtx.h"
Graphmtx::Graphmtx(int sz)
{
maxVertex = sz; numVertex = 0; numEdge = 0; //初始化类变量
VertexList = new T[maxVertex]; //创建顶点表
Edge = (E**)new E * [maxVertex]; //创建邻接矩阵,二维数组
for (int i = 0; i < maxVertex; i++)
Edge[i] = new E[maxVertex];
for (int i = 0; i < maxVertex; i++) {
for (int j = 0; j < maxVertex; j++) {
Edge[i][j] = (i == j) ? 0 : MAX;
}
}
}
bool Graphmtx::insertVertex(const T vertex)
{
if(numVertex==maxVertex)
return false;
VertexList[numVertex++] = vertex;
return true;
}
bool Graphmtx::insertEdge(int v1, int v2, E weight)
{
if (v1 > -1 && v1<numVertex && v2>-1 && v2 < numVertex && Edge[v1][v2] == MAX) {
//输入的顶点位置有效,顶点之间原先不存在边,才进行插入边
Edge[v1][v2] = weight; Edge[v2][v1] = weight;
numEdge++;
return true;
}
return false;
}
bool Graphmtx::removeVertex(int v)
{
if(v<0||v>numVertex)
return false; //顶点不在图中,不删
if (numVertex == 1)
return false; //只剩一个顶点,不删
VertexList[v] = VertexList[numVertex-1]; //顶点表中删除v顶点
for (int i = 0; i < numVertex; i++) //减去与v相关联的边数
{
if (Edge[v][i] > 0 && Edge[v][i] < MAX)
numEdge--;
}
for (int i = 0; i < numVertex; i++) {
Edge[i][v] = Edge[i][numVertex - 1]; //用最后一列填补第v列
}
numVertex--; //顶点数减1,这时邻接矩有numVertex行,numVertex列
for (int j = 0; j < numVertex; j++) {
Edge[v][j] = Edge[v][numVertex];
}
return true;
}
bool Graphmtx::removeEdge(int v1, int v2)
{
if (v1!=v2&&v1 > -1 && v1<numVertex && v2>-1 && v2 < numVertex && Edge[v1][v2]>0 && Edge[v1][v2] < MAX) {
Edge[v1][v2] = Edge[v2][v1] = MAX;
numEdge--;
return true;
}
return false;
}
E Graphmtx::getWeight(int v1, int v2)
{
return Edge[v1][v2];
}
T Graphmtx::getValue(int i)
{
return VertexList[i];
}
int Graphmtx::getFirstNeighbor(int v)
{
if (v != -1) {
for (int col = 0; col < numVertex; col++) {
if (Edge[v][col] > 0 && Edge[v][col] < MAX)
return col;
}
}
return -1; //顶点没有后续顶点,返回-1
}
int Graphmtx::getNextNeighbor(int v, int w)
{
if (v != -1 && w != -1) {
for (int col = w+1; col < numVertex; col++) {
if (Edge[v][col] > 0 && Edge[v][col] < MAX)
return col;
}
}
return -1;
}
istream& operator>>(istream& in, Graphmtx& G)
{
cout << "输入顶点数和边数:" << endl;
T e1, e2;
E weight;
int n, m;
cin >> n >> m;
cout << endl << "依次输入顶点" << endl;
for (int i = 0; i < n; i++) {
in >> e1;
G.insertVertex(e1); //输入顶点,插入顶点表
}
cout << endl << "输入边的信息(顶点1,顶点2,权值):" << endl;
for (int i = 0; i < m; ) {
in >> e1 >> e2 >> weight;
int j = G.getPosVertex(e1); int k = G.getPosVertex(e2);
if (j == -1 || k == -1) {
cerr << "边两端信息有误,重新输入" << endl;
}
else {
G.insertEdge(j, k, weight); //输入边的信息
i++;
}
}
return in;
}
ostream& operator<<(ostream& out, Graphmtx& G)
{
cout << "顶点数:" << G.numVertex << ",边数:" << G.numEdge << endl;
for (int row = 0; row < G.numVertex; row++) {
for (int col = row + 1; col < G.numVertex; col++) {
int w = G.getWeight(row, col);
if (w > 0 && w < MAX) { //如果有边,输出
cout << "(" << G.getValue(row) << "," << G.getValue(col) <<","<< G.getWeight(row,col) << ")" << endl;
}
}
}
return out;
}
图的深度优先搜索(DFS)
DFS函数包括两部分,一是调用入口,需要传入图类的实例对象和变量的起始顶点,同时在该函数体内定义一个bool数组visited[],用于记录访问过的顶点
二是递归函数体,用于递归访问所有顶点,每访问一个顶点就输出该顶点的值
深度遍历的思路,从起点出发,访问起点的第一个后继顶点A,并将A标记为已访问;接着以A为起点,访问A的后继顶点X(没有访问标记的),X打上访问标记,然后访问X的后继顶点;按照这样的方式一直循环到访问完可以访问的顶点。
然后,再一直回退,访问A的位于X顶点之后的另外一个后继顶点,继续上述循环;
最后就访问完了所有顶点。因为是一直访问后继顶点的后继顶点,所以就做深度遍历。而广度优先遍历则是先访问完一个顶点A的所有后继顶点B,C,D,之后,再访问B的所有后继顶点E,F,G,再访问C的所有后继顶点,以此类推。
void DFS(Graphlnk& G, const T& v)
{
//从顶点v出发,对图进行深度优先搜索的主过程
int loc = G.getPosVertex(v);
int n = G.getNumOfVertex();
bool* visited = new bool[n];
for (int i = 0; i < n; i++) {
visited[i] = false;
}
DFS(G, loc, visited);
delete[] visited;
}
void DFS(Graphlnk& G, int v, bool visited[])
{
//从顶点v出发,访问所有可读入的未访问过的顶点
cout << G.getValue(v) << " ";
visited[v] = true;
int w = G.getFirstNeighbor(v);
while (w != -1) {
if (visited[w] == false) DFS(G, w, visited);
w = G.getNextNeighbor(v, w);
}
}
图的广度优先遍历(BFS)
广度优先搜索的思路:
从起点A开始,访问A的第一个后继顶点B,B打上访问标记,将B保存到队列中;访问A的第二个后继顶点C(如果C没有访问过,则进行访问;如果C被f访问过了,则跳过C进行下一步),C打上访问标记,将C保存到队列中;访问完A的所有后继顶点之后,从队列中退出一个元素B,取B的第一个后继顶点J,J打上访问标记,J进队;访问 B的第二个后继顶点K,K打标记,K进队;以此类推,直到队空(访问完连通简单图的所有顶点)
void BFS(Graphlnk& G, const T& v)
{
//广度优先搜索,从顶点v出发,横向搜索图
int w;
int loc = G.getPosVertex(v);
int n = G.getNumOfVertex();
bool* visited = new bool[n];
for (int i = 0; i < n; i++) {
visited[i] = false;
}
cout << G.getValue(loc)<< " ";
visited[loc] = true;
queue<int> Q;
Q.push(loc);
while (!Q.empty()) {
loc = Q.front(); Q.pop();
w = G.getFirstNeighbor(loc);
while (w != -1) {
if (visited[w] == false) {
cout << G.getValue(w) << " ";
visited[w] = true;
Q.push(w);
}
w = G.getNextNeighbor(loc, w);
}
}
delete[] visited;
}
Dijkstra算法求图的最短路径
void ShortPath(Graphlnk& G, T v, E dist[], int path[])
{
//参数依次为:图,最短路径起点,辅助数组(要传回主函数),路径数组(要传回主函数)
int n = G.getNumOfVertex();
bool* S = new bool[n]; //集合S,记录最短路径,为true则说明该点在最短路径集合内
E w, min;
for (int i = 0; i < n; i++) {
dist[i] = G.getWeight(v, i);//辅助数组初始化
S[i] = false; //集合初始化
if (i != v && dist[i] < MAX) {
path[i] = v; //如果从v到i有边,则把权值放到辅助数组中
}
else {
path[i] = -1;
}
}
S[v] = true; dist[v] = 0; //顶点v加入集合
for (int i = 0; i < n - 1; i++) { //边数为n-1(顶点数-1)
min = MAX;
int u = v;
for (int j = 0; j < n; j++) {
if (S[j] == false && dist[j] < min) {
u = j; min = dist[j]; //选不在S中具有最短路径的顶点u
}
}
S[u] = true; //将顶点u加入集合S
for (int k = 0; k < n; k++) {
w = G.getWeight(u, k); //获取新起点到各个剩余顶点k(不在S中的顶点)的路径长度
if (S[k] == false && w < MAX && dist[u] + w < dist[k]) { //比较新得出的路径长度与原长度的大小
dist[k] = dist[u] + w; //如果更小,则替换(辅助数组中)原来的路径大小
path[k] = u; //修改到k的最短路径
}
}
}
}
打印最短路径
void printShortPath(Graphlnk& G, int v, E dist[], int path[])
{
cout << "从顶点" << G.getValue(v) << "到其它各顶点的最短路径为:" << endl;
int n = G.getNumOfVertex();
int* d = new int[n]; //辅助数组,记录正序的路径
for (int i = 0; i < n; i++) {
if (i != v) {
int j = i; int k = 0;
while (j != v) {
d[k++] = j;
j = path[j]; //从v到i逆序的路径
}
cout << "顶点" << G.getValue(i) << "的最短路径为:" << G.getValue(v);
while (k > 0) {
cout << d[--k] << " ";
}
cout << "最短路径长度为:" << path[i] << endl;
}
}
delete[] d;
}
最小生成树
最小生成树的插入算法需要使用到最小堆和并查集
MinPanTree类声明
#pragma once
#include"MinHeap.h"
#include"UFSets.h"
#include"Graphlnk.h"
#include<iostream>
using namespace std;
/*//已在MinHeap.h文件中定义
typedef int E; //边的权值
typedef char T; //顶点类型
struct Node { //最小生成树边节点的定义
//MSTEdgeNode的定义
int head, tail; //两顶点位置
E key; //边上的权值
Node() :tail(-1), head(-1), key(0) {}
bool operator<=(Node& R) { return key <= R.key; } //最小堆排序时要用到
bool operator>(Node& R) { return key > R.key; } //最小堆排序时要用到
};*/
class MinSpanTree
{
friend ostream& operator<<(ostream& out, MinSpanTree& MST);
protected:
Node* edgeValue; //用边值数组表示树
int maxNode, numNode; //数组的最大元素个数和当前个数
public:
MinSpanTree(int sz = defaultSize - 1) :maxNode(sz), numNode(0) {
edgeValue = new Node[sz];
}
bool Insert(Node& item);
};
#include "MinSpanTree.h"
bool MinSpanTree::Insert(Node& item)
{
if(numNode>maxNode)
return false;
edgeValue[numNode++] = item;
return true;
}
ostream& operator<<(ostream& out, MinSpanTree& MST)
{
out << "最小生成树:" << endl;
for (int i = 0; i < MST.numNode; i++) {
out <<"("<< MST.edgeValue[i].head <<","<< MST.edgeValue[i].tail <<","<< MST.edgeValue[i].key << ")" << endl;
}
return out;
}
最小堆的定义
//MinHeap.h
#pragma once
#include<stdlib.h>
#include<string>//这个头文件一定要引入,否则无法使用string,而且从报错中无法直接发现这个问题
using namespace std;
typedef int E; //边的权值
typedef int T; //顶点类型
#define DefaultSize 100
struct Node {
//MSTEdgeNode的定义
int head, tail; //两顶点位置
E key; //边上的权值
Node():tail(-1),head(-1),key(0){}
bool operator<=(Node& R) { return key <= R.key; } //最小堆排序时要用到
bool operator>(Node& R) { return key > R.key; } //最小堆排序时要用到
};
class MinHeap
{
private:
Node* heap; //存放最小堆中元素的数组
int currentSize; //最小堆中当前元素数量
int maxHeapSize; //最小堆能容纳的最大数量
public:
MinHeap(int sz = DefaultSize); //构造函数
~MinHeap() { delete[] heap; } //析构函数
bool Insert(const Node& x); //将x插入到最小堆中
bool Remove(Node& x); //删除堆顶元素,并通过x返回
bool isEmpty() { return currentSize == 0 ? true : false; }
private:
void siftDown(int start, int m); //从start到m下滑调整成为最小堆
void siftUp(int start); //从start到0向上调整成为最小堆
};
//MinHeap.cpp
#include "MinHeap.h"
#include<iostream>
MinHeap::MinHeap(int sz)
{
//动态建立数组空间
maxHeapSize = ( DefaultSize < sz) ? sz : DefaultSize;
heap = new Node[maxHeapSize]; //创建储存空间
if (heap == NULL) {
cerr << "储存分配失败" << endl; exit(1);
}
currentSize = 0; //建立当前大小
}
bool MinHeap::Insert(const Node& x)
{
//将x插入到最小堆中
if (currentSize == DefaultSize) {
cerr << "Heap Full" << endl; return false; //堆满
}
heap[currentSize] = x; //插入
siftUp(currentSize); //向上调整
currentSize++; //堆计数加1
return true;
}
bool MinHeap::Remove(Node& x)
{
if (!currentSize) {
//cout << "Heap empty" << endl;
return false; //堆空
}
x = heap[0]; heap[0] = heap[currentSize - 1]; //返回并删除堆顶元素,用最后一个元素填补根节点
currentSize--; //最小堆计数减1
siftDown(0, currentSize - 1); //自上而下调整最小堆
return true;
}
void MinHeap::siftDown(int start, int m)
{
//从节点start开始到m为止,自上向下比较,如果父节点的值大于子女的值
//则关键码小的上浮,继续向下层比较,这样将一个集合局部调整为最小堆
int i = start, j = i * 2 + 1; //j是i的左子女
Node temp = heap[i]; //将start中的元素存放到临时空间中
while (j <= m) {
if (j<m && heap[j]>heap[j + 1]) {
j++; //如果右子树比左子树小,则将j指向右子树
}
if (temp <= heap[j]) break; //如果根节点比子女小,则不做调整
else {
heap[i] = heap[j]; //否则小的上移
i = j;
j = j * 2 + 1; //i,j下移
}
}
heap[i] = temp; //回放temp中暂存的元素
}
void MinHeap::siftUp(int start)
{
//从节点start开始向0调整,自下而上比较,如果子女的值小于父节点的值
//则交换,小的上移,这样将集合调整为最小堆
int j = start;
int i = (j - 1) / 2; //i是j的父节点
Node temp = heap[j];
while (j > 0) { //沿父节点路径向上直达根
if (heap[i] <= temp) break; //父节点的值小,不作调整
else {
heap[j] = heap[i]; //父节点的值大,用父节点的值替代子女节点的值
j = i; i = (i - 1) / 2; //i,j上移
}
}
heap[j] = temp; //回送
}
并查集的定义
//UFSets.h
#pragma once
#define defaultSize 100
class UFSets
{
private:
int* parent; //集合元素数组
int size; //集合元素数目
public:
UFSets(int sz = defaultSize);
~UFSets() { delete[] parent; }
//UFSets& operator=(UFSets& R); //集合赋值操作
void Union(int root1, int root2); //合并两个子集合,加权合并
int Find(int x); //查找子集合的根
};
//UFSets.cpp
#include "UFSets.h"
UFSets::UFSets(int sz)
{
size = sz;
parent = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = -1;
}
}
void UFSets::Union(int root1, int root2)
{
//使用节点个数探查方法求集合的合并
int r1 = Find(root1);
int r2 = Find(root2);
if (r1 != r2) { //合并两个子集合要求集合之间的根是不同的
if (r1 < r2) { //根r1中的元素数量更多,如-3<-2,r1中有3个元素,r2中有2个元素
parent[r1] += parent[r2];
parent[r2] = r1;
}
else { //根r2中的元素数量更多
parent[r2] += parent[r1];
parent[r1] = r2;
}
}
}
int UFSets::Find(int x)
{
while (parent[x] > 0) {
x = parent[x];
}
return x;
}