C++图、树、最小堆、并查集

图的基类定义

由于使用模板基类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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值