目录
一、创建图(邻接矩阵)且进行深度优先遍历和广度优先遍历
#include<iostream>
#include<queue>
#include<stack>
using namespace std;
const int MaxLen = 100;
class Map {
private:
int adj[MaxLen][MaxLen]; //邻接矩阵
int visited[MaxLen]; //访问数组
int vNum; //顶点数
int eNum; //边数
public:
//构造函数
Map(){
vNum=0;
eNum=0;
}
void createMap() {
cin>>vNum;
//eNum = e;
for (int i = 0; i < vNum; i++)
visited[i] = 0;
//输入邻接矩阵
for (int i = 0; i < vNum; i++)
for (int j = 0; j < vNum; j++)
cin>>adj[i][j];
// 另外一种输入邻接矩阵的方式
// cout << "请输入边:" << endl;
// for (int i = 0; i < e; i++) {
// int a, b;
// cin >> a >> b;
// adj[a][b] = 1;
// adj[b][a] = 1;
// }
}
void reset(){
for(int i=0;i<vNum;i++)
visited[i]=0;
}
//广度优先遍历BFS
void BFS(int vertex) {
queue<int> q;
q.emplace(vertex); //初始结点入队
visited[vertex] = 1;
int v;
while (!q.empty()) { //队不空一直循环
v = q.front(); //对头元素出栈
q.pop();
cout << v << " "; //打印当前结点
int i = 0;
while (i < vNum) { //与当前结点相邻且未被访问的结点入队
if (adj[v][i] == 1 && visited[i] == 0) {
q.emplace(i);
visited[i] = 1; //访问数组相应的置1
}
i++;
}
}
}
//递归深度优先遍历DFS1
void DFS1(int vertex) {
cout << vertex << " "; //打印第一个结点
visited[vertex] = 1; //相应位的访问数组置1
for (int i = 0; i < vNum; i++)
//依次递归遍历当前结点的未被访问的邻接点
if (adj[vertex][i] == 1 && visited[i] == 0)
DFS1(i);
}
//非递归深度优先遍历DFS2
void DFS2(int vertex) {
stack<int> s;
s.emplace(vertex); //当前结点入栈
visited[vertex] = 1; //相应位的访问数组置1
int v;
while (!s.empty()) { //栈不空则一直循环
v = s.top(); //当前结点出栈
s.pop();
cout << v << " "; //打印当前结点
//把当前结点的未被访问的邻接点依次入栈并把相应访问数组置1
for (int i = 0; i < vNum; i++) {
if (adj[v][i] != 0 && visited[i] == 0) {
s.emplace(i);
visited[i] = 1;
}
}
}
}
};
int main() {
int t;
cin>>t;
while(t--){
Map myMap;
myMap.createMap();
myMap.BFS(0);
cout<<endl;
myMap.reset();
myMap.DFS1(0);
cout<<endl;
myMap.reset();
myMap.DFS2(0);
cout<<endl;
}
return 0;
}
//-注意:这个代码样例测试是对的,但我在学校oj上已经无法提交了,所以无法判断正确性。
二、构建邻接表(尾插法)并遍历邻接表
#include<iostream>
#include<vector>
#include <cstring> ///memset
#include <queue>
#include <stack>
using namespace std;
class EdgeList{
private:
int vertex;
EdgeList* next;
public:
///创造一个新结点
EdgeList* Create(int vertex){
EdgeList* list=new EdgeList;
list->vertex=vertex;
list->next=nullptr;
return list;
}
///寻找并返回最后一个结点
EdgeList* getLast(EdgeList* list){
EdgeList* cur=list;
while(cur->next!=nullptr){
cur=cur->next;
}
return cur;
}
///插入函数
EdgeList* Attach(EdgeList* list,int vertex){
EdgeList* last=this->getLast(list);
EdgeList* insert=insert->Create(vertex);
last->next=insert;
return list;
}
int getVertex(EdgeList* list){
return list->vertex;
}
void Print(EdgeList* list){
EdgeList* cur=list;
while(cur){
cout<<cur->vertex<<" ";
cur=cur->next;
}
}
EdgeList* getNext(){
return this->next;
}
};
class Graph{
private:
vector<EdgeList*> Edgelist; ///邻接表
int* isvisited; ///访问数组
int vNum; ///顶点数
int eNum; ///边数
public:
///利用构造函数构造图
Graph(int v,int e){
vNum=v;
eNum=e;
///因为预留了vNum个位置,所以每一个Edgelist都要new一次
Edgelist.reserve(vNum);
isvisited=new int[vNum+1];
memset(isvisited,0,sizeof(isvisited[0])*(vNum+1));
cout<<"请输入顶点数:"<<endl;
for(int i=0;i<vNum;i++){
int vertex;
cin>>vertex;
Edgelist[i]=new EdgeList;
///依次构造图的结点,因为vertex是一个int类型的值嘛,不是一个结点
Edgelist[i]=Edgelist[i]->Create(vertex);
}
cout<<"请输入边:"<<endl;
for(int i=0;i<eNum;i++){
int start,end;
cin>>start>>end;
EdgeList* s1=this->Find(start);
EdgeList* s2=this->Find(end);
///我在你的最后一位
///你也在我的最后一位
s1->Attach(s1,end);
s2->Attach(s2,start);
}
}
///依照传入的值查找对应的结点
EdgeList* Find(int vertex){
for(int i=0;i<vNum;i++){
if(Edgelist[i]->getVertex(Edgelist[i])==vertex){
return Edgelist[i];
}
}
}
///打印邻接表函数
void Print(){
for(int i=0;i<vNum;i++){
///依次取某一条邻接表
EdgeList* list=Edgelist[i];
list->Print(list);
cout<<endl;
}
}
///重置访问数组
void reset(){
memset(isvisited,0,sizeof(isvisited[0])*(vNum+1));
}
///BFS广度优先遍历
void BFS(int vertex){
///依照传入的值查找对应的结点
EdgeList* cur=this->Find(vertex);
queue<int> queue;
queue.emplace(cur->getVertex(cur));
while(!queue.empty()){
int index=queue.front();
queue.pop();
isvisited[index]=1; ///标记为已访问
cout<<index<<" ";
cur=this->Find(index);
while(cur){ ///与当前结点相邻且未被访问的结点入队
index=cur->getVertex(cur);
if(isvisited[index]==0){
queue.emplace(index);
}
cur=cur->getNext();
}
}
}
///递归深度优先遍历
void DFS1(int vertex){
vector<int> node;
///找到当前(传入的)结点
EdgeList* cur=this->Find(vertex);
while(cur){
node.emplace_back(cur->getVertex(cur));
cur=cur->getNext();
}
///打印当前结点。所以这里的vertex就是类似于1、2、3、4、5的下标
cur=this->Find(node[0]);
int index=cur->getVertex(cur);
isvisited[index]=1;
cout<<index<<" ";
///一次堆当前结点的邻接点进行DFS
for(int i=1;i<node.size();i++){
cur=this->Find(node[i]);
index=cur->getVertex(cur);
if(isvisited[index]==0){
isvisited[index]=1;
this->DFS1(index);
}
}
///下面的isvisited在某些条件下要置0
///isvisited[index]=0;
}
///非递归深度优先遍历(DFS)
void DFS2(int vertex){
stack<int> stack;
EdgeList* cur=this->Find(vertex);
int index=cur->getVertex(cur);
stack.emplace(index);
isvisited[index]=1;
while(!stack.empty()){
index=stack.top();
stack.pop();
cout<<index<<" ";
cur=this->Find(index);
while(cur){ ///把当前顶点的邻接点依次入栈
index=cur->getVertex(cur);
if(isvisited[index]==0){
stack.emplace(index);
isvisited[index]=1;
}
cur=cur->getNext();
}
}
}
};
int main()
{
cout<<"请输入顶点数与边数:"<<endl;
int v,e;
cin>>v>>e;
Graph graph(v,e);
cout<<"邻接表为:"<<endl;
graph.Print();
cout<<"请输入开始要访问的结点:"<<endl;
int vertex;
cin>>vertex;
cout<<"广度优先遍历(BFS)序列为:"<<endl;
graph.BFS(vertex);
cout<<endl;
cout<<"递归深度优先遍历(DFS)序列为:"<<endl;
graph.reset();
graph.DFS1(vertex);
cout<<endl;
cout<<"非递归深度优先遍历(DFS)序列为:"<<endl;
graph.reset();
graph.DFS2(vertex);
cout<<endl;
return 0;
}
三、计算有向图和无向图的度
#include <iostream>
#include <cstring>
using namespace std;
class Node {
public:
///入度in,出度out,总度total
int in = 0, out = 0, total = 0;
///入度自增1,总度也自增1
void inadd() {
in++;
total++;
}
///出度自增1,总度也自增1
void outadd() {
out++;
total++;
}
};
class Map {
public:
int Vexnum; ///顶点数
char type; ///类型,判断是构建有向图还是无向图
Node *head; ///用于存储度的数组
string *Vex; ///顶点数组
int **Matrix; ///邻接矩阵
///查找并返回顶点c的下标
int index(string c) {
for (int i = 0; i < Vexnum; i++) {
if (c == Vex[i])
return i;
}
}
///根据传入的顶点个数n,和类型t构建图
Map(int n, char t) {
Vexnum = n;
type = t;
///new一堆空间
Vex = new string[Vexnum];
Matrix = new int *[Vexnum];
for (int i = 0; i < Vexnum; i++){
Matrix[i] = new int[Vexnum];
memset(Matrix[i],0,sizeof(Matrix[i][0])*Vexnum);
}
head = new Node[Vexnum];
///输入各个顶点
for (int i = 0; i < Vexnum; i++) {
cin >> Vex[i];
}
///将邻接矩阵置0,上面的memset也是同理
// for (int i = 0; i < Vexnum; i++){
// for (int j = 0; j < Vexnum; j++) {
// Matrix[i][j] = 0;
// }
// }
///输入k条边
int k;
cin >> k;
while (k--) {
string c1, c2;
///c1是弧尾(出),c2是弧头(入)
///一条弧从弧尾出发,指向弧头
cin >> c1 >> c2;
///由于结点时按顺序输入进来的,所以下标也是按顺序的
int num1 = index(c1), num2 = index(c2);
head[num1].outadd();
head[num2].inadd();
///D代表构建有向图,U代表构建无向图
if (type == 'U') {
Matrix[num1][num2] = 1;
Matrix[num2][num1] = 1;
} else if (type == 'D') {
Matrix[num1][num2] = 1;
}
}
}
~Map() {
delete[] Vex;
delete[] head;
for (int i = 0; i < Vexnum; i++)
delete[] Matrix[i];
delete[] Matrix;
}
void display() {
///输出邻接矩阵
for (int i = 0; i < Vexnum; i++){
for (int j = 0; j < Vexnum; j++) {
cout << Matrix[i][j];
if (j != Vexnum - 1)
cout << " ";
if (j == Vexnum - 1)
cout << endl;
}
}
///输出顶点和度
for (int i = 0; i < Vexnum; i++) {
cout << Vex[i];
///总度不为0,说明不是孤立点
if (head[i].total != 0) {
cout << ":";
if (type == 'D') {
cout << " " << head[i].out
<< " " << head[i].in
<< " " << head[i].total;
} else if (type == 'U') {
cout << " " << head[i].total;
}
}
cout << endl;
}
}
};
int main() {
int t;
cin >> t;
while (t--) {
char c;
int k;
cin >> c >> k;
Map m(k, c);
m.display();
}
return 0;
}
四、构建有向图并进行拓扑排序
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
class Graph{
private:
int** G; ///邻接矩阵
int* Indegree; ///入度
int vNum; ///定点数
vector<int> TopOrder; ///拓扑排序数组
public:
//构造函数
Graph(int v , int e){
vNum = v;
///new一堆东西
G = new int*[vNum+1];
Indegree = new int[vNum+1];
memset(Indegree,0,sizeof(Indegree[0])*(vNum+1));
for(int i = 0 ; i < vNum+1 ; i++){
G[i] = new int[vNum+1];
memset(G[i],0,sizeof(G[0][0])*(vNum+1));
}
cout<<"请输入边:"<<endl;
///生成有向图
for(int i = 0 ; i < e ; i++){
///v1是起始顶点,v2是终止顶点
int v1,v2;
cin>>v1>>v2;
G[v1][v2] = 1;
Indegree[v2]++; ///终止边入度加1
}
}
//拓扑排序
bool TopSort(){
queue<int> queue;
///入度为0的顶点加入队列里
///注意啊,这里的顶点默认是1、2、3、4之类的正整有序数,没有0
for(int i = 1 ; i < vNum+1 ; i++){
if(this->Indegree[i] == 0){
queue.push(i);
}
}
///队列不空一直循环,队列里面是入度为0的顶点
while(!queue.empty()){
int vertex = queue.front();
queue.pop(); ///顶点出队
TopOrder.push_back(vertex);///顶点加入拓扑序
///对新加入拓扑序的顶点指向的点进行处理
for(int i = 1 ; i < vNum+1 ; i++){
///如果入度大于0
if(this->G[vertex][i] != 0){
///把与vertex相关的边消去,即对应顶点入度减1
this->Indegree[i]--;
///顶点入度为0则入队
if(this->Indegree[i] == 0){
queue.push(i);
}
}
}
}
///判断拓扑序中是否有vNum个顶点 ,因为上面循环结束
///也有可能是存在环而结束的
if(this->TopOrder.size() != vNum){//没有nv个顶点
return false;///说明有向无环图
}else{
return true;///说明不是有向无环图
}
}
///打印拓扑序
void Print_TopOrder(){
for(int i = 0 ; i < TopOrder.size() ; i++){
cout<<TopOrder[i]<<" ";
}
}
};
int main(){
cout<<"请输入定点数与边数:"<<endl;
int v,e;
cin>>v>>e;
Graph graph(v,e);
if(graph.TopSort()){
cout<<"该图的拓扑序排序为:"<<endl;
graph.Print_TopOrder();
}
return 0;
}
五、有权图的迪杰斯特拉(Dijikstra)算法
#include <iostream>
#include <cstring>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
const int MAX = 65535;
int G[1001][1001]; ///邻接矩阵
int dist[1001] = {0}; ///距离数组
int path[1001] = {0}; ///路径数组,保存最短路径的顶点的集合
int visited[1001] = {0}; ///访问标记
int vNum,eNum;
///降序顶推,所以输出自己想想,输出由小到大
struct cmp{
bool operator()(int &a,int &b){
return dist[a]>dist[b];
}
};
void Create_Graph()
{
///初始化距离数组为正无穷
for(int i = 0 ; i < 1001 ; i++){
dist[i] = MAX;
}
///初始化路径数组为-1
memset(path,-1,sizeof(path[0])*(1001));
///初始化访问数组为-1
memset(visited,0,sizeof(visited[0])*(1001));
//memset(this->collected,0,sizeof(this->collected[0])*(nv+1));
///将邻接矩阵每一位都赋予一个最大值
for(int i = 0 ; i < 1001 ; i++){
for(int j = 0 ; j < 1001 ; j++){
G[i][j] = MAX;
}
}
///初始化图
cout<<"请输入顶点数与边数:"<<endl;
cin>>vNum>>eNum;
cout<<"请输入边与权重:"<<endl;
for(int i = 0 ; i < eNum ; i++){
int v1,v2,weight;
cin>>v1>>v2>>weight;
///无向有权图如此赋值:G[v1][v2] = G[v2][v1] = weight;
///更新邻接矩阵,将有权边的值由无穷更新为权值
G[v1][v2]=weight;
}
}
///迪杰斯特拉算法
bool Dijikstra(int vertex){
priority_queue<int,vector<int>,cmp> q;
///源顶点加入最小堆
q.push(vertex);
///初始化源顶点的dist为0
dist[vertex] = 0;
visited[vertex] = 1; ///对传入的vertex顶点做标记,说明访问过
///最小堆不为空,一直循环
while(!q.empty()){
///从最小堆中弹出最小元素
int start = q.top();
q.pop();
for(int i = 1 ; i < vNum+1 ; i++){
///负值圈问题,即迪杰斯特拉算法(Dijikstra算法)不能出现负值边,这个判断可有可无
if(G[start][i] < 0){
return false;
}
/// bfs遍历领接结点,这个 G[start][i] < MAX 判断是用来优化的
if (G[start][i] < MAX){
if(visited[i] == 0){
///不要想图,想象有一个长方形数组
/// 当前i到start的最小距离 大于 dist[start]+G[start][i]
///dist[start] 表示 start顶点(源顶点) 到 起始点 的距离
///G[start][i] 表示 start顶点(源顶点) 到 顶点i 的距离
if(dist[i] > dist[start] + G[start][i]){
dist[i] = dist[start] + G[start][i];
///因为你更新了,所以你有可能变成dist数组里面最小的值了,所以要push进去
q.push(i);
///path用来记录路径
///这个path的存储过程不易想明白,其实拿一个图模拟一下path的更新过程就可以明白
path[i] = start;
}
}
}
}
}
return true;
}
//打印start到end的最短路径
void Print(int start ,int end){
stack<int> stack;
stack.push(end);
cout<<start<<"到"<<end<<"的最短路径为:";
int j = end;
while(path[j] != -1){//路径上的元素一次入栈
j = path[j];
stack.push(j);
}
///打印路径
cout<<stack.top();
stack.pop();
while(!stack.empty()){
cout<<" -> "<<stack.top();
stack.pop();
}
cout<<endl<<"最短路径长度为:"<<dist[end]<<endl;
}
void Print_Dijikstra(int vertex){
for(int i = 1 ; i < vNum+1 ; i++){
if(i == vertex){
continue;
}
Print(vertex,i);
}
}
int main(){
Create_Graph();
cout<<"请输入一个起始点:"<<endl;
int vertex;
cin>>vertex;
if(Dijikstra(vertex)){
Print_Dijikstra(vertex);
}
return 0;
}
/**
请输入顶点数与边数:
6 8
请输入边与权重:
1 6 100
1 3 10
1 5 30
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
请输入一个起始点:
1
1到2的最短路径为:2
最短路径长度为:65535
1到3的最短路径为:1 -> 3
最短路径长度为:10
1到4的最短路径为:1 -> 5 -> 4
最短路径长度为:50
1到5的最短路径为:1 -> 5
最短路径长度为:30
1到6的最短路径为:1 -> 5 -> 4 -> 6
最短路径长度为:60
*/
六、无权图的迪杰斯特拉(Dijikstra)算法
#include <iostream>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;
class Graph{
private:
int** G; ///邻接矩阵
int* dist; ///距离数组
int* path; ///路径数组
int vNum; ///顶点数
int eNum; ///边数
public:
//构造函数
Graph(int v , int e){
vNum = v;
eNum = e;
///new一堆东西
G = new int*[v+1];
dist = new int[v+1];
path = new int[v+1];
memset(dist,-1,sizeof(dist[0])*(v+1));
memset(path,-1,sizeof(path[0])*(v+1));
for(int i = 0 ; i < vNum+1 ; i++){
G[i] = new int[vNum+1];
memset(G[i],0,sizeof(G[i][0])*(vNum+1));
}
cout<<"请输入边:"<<endl;
for(int i = 0 ; i < eNum ; i++){
int a,b;
cin>>a>>b;
this->G[a][b] = 1;
this->G[b][a] = 1;
}
}
///无权图的Dijikstra
void Unweighted(int vertex){
queue<int> queue; ///初始化队列
queue.push(vertex); ///初始结点入队
int cur_vertex; ///当前结点
dist[vertex] = 0; ///初始结点的距离为0
while(!queue.empty()){
cur_vertex = queue.front(); //队头结点出队
queue.pop();
///遍历cur_vertex的每个邻接点
for(int i = 1 ; i < vNum+1 ; i++){
if((G[cur_vertex][i] == 1)&& (dist[i] == -1)){
dist[i] = dist[cur_vertex]+1; ///当前结点的距离是cur_vertex的距离加1
path[i] = cur_vertex; ///把当前结点的上一个结点设为cur_vertex;
queue.push(i);
}
}
}
}
///打印无权图迪杰斯特拉路径
void Print_Unweighted(int vertex){
for(int i = 1 ; i < vNum+1 ; i++){
stack<int> stack;
stack.push(i);
cout<<vertex<<"到"<<i<<"的最短路径为:";
int j = i;
while(this->path[j] != -1){//路径上的元素一次入栈
j = path[j];
stack.push(j);
}
//打印路径
cout<<stack.top();
stack.pop();
while(!stack.empty()){
cout<<" -> "<<stack.top();
stack.pop();
}
cout<<endl;
}
}
};
int main()
{
cout<<"请输入顶点数与边数:"<<endl;
int v ,e;
cin>>v>>e;
Graph graph(v,e);
cout<<"请输入一个起始点:"<<endl;
int vertex;
cin>>vertex;
graph.Unweighted(vertex);
graph.Print_Unweighted(vertex);
return 0;
}
/**
请输入顶点数与边数:
6 8
请输入边:
1 3
1 6
1 5
2 3
3 4
4 5
5 6
4 6
请输入一个起始点:
1
1到1的最短路径为:1
1到2的最短路径为:1 -> 3 -> 2
1到3的最短路径为:1 -> 3
1到4的最短路径为:1 -> 3 -> 4
1到5的最短路径为:1 -> 5
1到6的最短路径为:1 -> 6
*/
七、弗洛伊德算法(Floyd算法)解决任意两点间的最短路径
#include <iostream>
#include <cstring>
#include <stack>
#include <queue>
using namespace std;
const int MAX = 65535;
class Graph{
private:
int** G; /// 邻接矩阵
int** dist; /// 距离数组
int** path; /// 路径数组
int vNum; /// 顶点数
public:
///构造函数
Graph(int nv, int ne){
vNum = nv;
///new一堆东西
G = new int*[nv+1];
dist = new int*[nv+1];
path = new int*[nv+1];
for(int i = 0 ; i < nv+1 ; i++){
G[i] = new int[nv+1];
dist[i] = new int[nv+1];
path[i] = new int[nv+1];
///将路径数组里的值全部赋值成-1
memset(path[i],-1,sizeof(path[0][0])*(nv+1));
///将邻接矩阵和距离数组里面的值全部赋值成最大值
for(int j = 0 ; j < nv+1 ; j++){
G[i][j] = dist[i][j] = MAX;
}
///对角线全部置为0
G[i][i] = dist[i][i] = 0;
}
cout<<"请输入边与权重:"<<endl;
for(int i = 0 ; i < ne ; i++){
int v1,v2,weight;
cin>>v1>>v2>>weight;
///根据算法要求,更新邻接矩阵,更新(初始化)距离数组
G[v1][v2] = G[v2][v1] = weight;
dist[v1][v2] = dist[v2][v1] = weight;
}
}
///Floyd算法(多源最短路径算法)
bool Floyd(){
for(int k = 1 ; k < vNum+1 ; k++){ ///k代表中间顶点
for(int i = 1 ; i < vNum+1 ; i++){ ///i代表起始顶点
for(int j = 1 ; j < vNum+1 ; j++){ ///j代表终点
if(dist[i][k] + dist[k][j] < dist[i][j]){
dist[i][j] = dist[i][k] + dist[k][j];
if(i == j && dist[i][j] < 0){ ///发现了负值圈
return false;
}
///path还是有些难理解,估计我回看的时候会忘记它的更新过程
path[i][j] = k;
}
}
}
}
return true;
}
/// 分治法寻找start到end最短路径的中间结点
void Find(queue<int> &q ,int start,int end){
int mid = path[start][end];
if(mid == -1){
return;
}
Find(q,start,mid);
q.push(mid);
Find(q,mid,end);
}
///打印start顶点到end顶点的路径
void Print_Path(int start,int end){
queue<int> queue;
queue.push(start);
Find(queue,start,end);
queue.push(end);
cout<<queue.front();
queue.pop();
while(!queue.empty()){
cout<<"->"<<queue.front();
queue.pop();
}
cout<<endl;
}
void Print_Floyd(){
int i,j,k;
for(int i = 1 ; i < vNum+1 ; i++){
for(int j = 1 ; j < vNum+1 ; j++){
cout<<path[i][j]<<" ";
}
cout<<endl;
}
cout<<" length path"<<endl;
for(i = 1 ; i < vNum+1 ; i++){
for(j = i+1 ; j < vNum+1 ; j++){
cout<<i<<"->"<<j<<" ";
cout<<dist[i][j]<<" ";
Print_Path(i,j);
}
cout<<endl;
}
}
};
int main(){
cout<<"请输入顶点数与边长数:"<<endl;
int v,e;
cin>>v>>e;
Graph graph(v,e);
if(graph.Floyd()){
cout<<"各个顶点的最短路径为:"<<endl;
graph.Print_Floyd();
}
return 0;
}
/**
请输入顶点数与边长数:
7 12
请输入边与权重:
1 2 12
1 6 16
1 7 14
2 6 7
6 7 9
5 7 8
5 6 2
2 3 10
3 6 6
3 5 5
3 4 3
4 5 4
各个顶点的最短路径为:
-1 -1 2 6 6 -1 -1
-1 -1 -1 3 6 -1 6
2 -1 -1 -1 -1 -1 5
6 3 -1 -1 -1 5 5
6 6 -1 -1 -1 -1 -1
-1 -1 -1 5 -1 -1 -1
-1 6 5 5 -1 -1 -1
length path
1->2 12 1->2
1->3 22 1->2->3
1->4 22 1->6->5->4
1->5 18 1->6->5
1->6 16 1->6
1->7 14 1->7
2->3 10 2->3
2->4 13 2->3->4
2->5 9 2->6->5
2->6 7 2->6
2->7 16 2->6->7
3->4 3 3->4
3->5 5 3->5
3->6 6 3->6
3->7 13 3->5->7
4->5 4 4->5
4->6 6 4->5->6
4->7 12 4->5->7
5->6 2 5->6
5->7 8 5->7
6->7 9 6->7
*/
八、克鲁斯卡尔算法(Kruskal算法)求最小生成树
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int MAX = 65535;
const int Min_Data = -32768;
///并查集
class UF{
private:
int* path; ///父亲结点
int size; ///容量
public:
///构造函数
UF(int size){
size = size;
path = new int[size+1];
memset(path , -1 ,sizeof(path[0])*(size+1));
}
///查找操作,采用路径压缩
int Find(int x){
if(path[x] < 0){
return x;
}else{
///首先查找x的父节点path[x],然后把根节点变成path[x],之后再返回根
return path[x] = Find(path[x]);
}
}
///并操作
void Union(int root1 ,int root2){
root1 = Find(root1);
root2 = Find(root2);
//集合里存的是集合个数的相反数,大集合并小集合
if(path[root1] < path[root2]){//集合1比集合2大
path[root1] += path[root2];
path[root2] = root1;
}else{//反之集合2比集合1大
path[root2] += path[root1];
path[root1] = root2;
}
}
///检查是否产生回路
bool CheckCircle(int root1, int root2){
root1 = Find(root1);
root2 = Find(root2);
if(root1 == root2){//如果产生回路
return false;
}else{//不产生回路
this->Union(root1,root2);
return true;
}
}
};
///边类
class Edge{
private:
int Start_Vertex; ///起始边
int End_Vertex; ///终止边
int Weight; ///边的权重
public:
//构造函数
void Set(int start_vertex,int end_vertex ,int weight){
Start_Vertex = start_vertex;
End_Vertex = end_vertex;
Weight = weight;
}
//打印边
void Print(){
cout<<Start_Vertex<<"\t"<<End_Vertex<<"\t"<<Weight<<endl;
}
int getWeight(){
return Weight;
}
int getStartVertex(){
return Start_Vertex;
}
int getEndVertex(){
return End_Vertex;
}
};
class MinHeap{
private:
vector<Edge> Edges; //边集数组
int capacity; //最大容量
int size; //当前规模
public:
//构造函数
MinHeap(int size){
capacity = size*2;
size = 0;
Edges.reserve(capacity);
Edge edge;
edge.Set(0,0,Min_Data);
Edges.push_back(edge);
}
//把Edge[n]为根的子堆调整为最小堆
void PreDown(int n){
Edge edge = this->Edges[n]; //保存下标为n的边
int parent,child;
for(parent = n ; parent*2 <= size ; parent = child){
child = parent*2;
if(child != size){
int wl = Edges[child].getWeight();//左孩子权重
int wr = Edges[child+1].getWeight();//右孩子权重
if(wr < wl){//右孩子权重小于左孩子权重
child++;//选择右孩子
}
}
///如果根节点权重小于子堆,那么找到合适位置,跳出循环
if(edge.getWeight() < Edges[child].getWeight()){
break;
}else{
Edges[parent] = Edges[child];
}
}
Edges[parent] = edge;
}
///构造最小堆
void Create(vector<Edge> &set){
for(int i = 0 ; i < set.size() ; i++){
Edges[++size] = set[i];
}
///从最后一个元素的父亲开始,把自己为根的子堆调整为最小堆
for(int i = size/2 ; i >= 1 ; i--){
PreDown(i);
}
}
//删除操作
Edge DeleteMin(){
if(this->IsEmpty()){
Edge edge;
edge.Set(0,0,Min_Data);
return edge;
}
Edge min_edge = Edges[1];
Edges[1] = Edges[this->size];
size--;
PreDown(1);
return min_edge;
}
///判断最小堆是否为空
bool IsEmpty(){
return this->size == 0;
}
void Print(){
cout<<"最小堆的元素有:"<<size<<endl;
for(int i = 1 ; i <= size ; i++){
cout<<Edges[i].getStartVertex()<<" "<<Edges[i].getEndVertex()<<" "<<Edges[i].getWeight()<<endl;
}
}
};
class Graph{
private:
int** G; ///邻接矩阵
int vNum; ///顶点数
int eNum; ///边数
vector<Edge> MST; ///最小生成树的边集合
vector<Edge> Edge_Set; ///边集合
public:
//构造函数
Graph(int v ,int e){
vNum = v;
eNum = e;
MST.reserve(vNum-1);
Edge_Set.reserve(e);
G = new int*[v+1];
for(int i = 0 ; i < v+1 ; i++){
G[i] = new int[v+1];
for(int j = 0 ; j < v+1 ; j++){
G[i][j] = MAX;
}
}
cout<<"请输入边与边长:"<<endl;
for(int i = 0 ; i < e ; i++){
int v1,v2,weight;
cin>>v1>>v2>>weight;
G[v1][v2] = G[v2][v1]= weight;
Edge edge;
edge.Set(v1,v2,weight);
Edge_Set.push_back(edge);
}
}
///Kruskal算法
int Kruskal(){
int sum_weight = 0;
UF uf(vNum); //构造并查集
int cnt = 0;
///构造边集合最小堆
MinHeap minheap(eNum);
minheap.Create(Edge_Set);
int edge_cnt = 0;
///最小生成树里未收录Nv-1条边且边集合里还有边则一直循环
while(edge_cnt != vNum-1 && !minheap.IsEmpty()){
Edge min_edge = minheap.DeleteMin();
int start= min_edge.getStartVertex();
int end = min_edge.getEndVertex();
if(uf.CheckCircle(start,end)){//不构成回路
//对应的边收录到最小生成树
MST.push_back(min_edge);
MST[edge_cnt++] = min_edge;
sum_weight += min_edge.getWeight();
}
}
if(edge_cnt < vNum-1){
sum_weight = -1;
}
return sum_weight;
}
void Print_Kruskal(){
cout<<"Kruskal算法构造的最小生成树的边集合为:"<<endl;
cout<<"源点\t终点\t权重"<<endl;
for(int i = 0 ; i < vNum-1 ; i++){
Edge edge = MST[i];
edge.Print();
}
}
};
int main(){
int v,e;
cout<<"请输入顶点数与边数:"<<endl;
cin>>v>>e;
Graph graph(v,e);
int min_weight = graph.Kruskal();
if(min_weight != -1){
cout<<"Kruskal算法生成的最小生成树的权重和为:"<<endl;
cout<<min_weight<<endl;
graph.Print_Kruskal();
}
return 0;
}
九、普里姆Prim算法构造最小生成树
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int MAX = 65535;
///边类
class Edge{
private:
int Start_Vertex; ///起始边
int End_Vertex; ///终止边
int Weight; ///边的权重
public:
///构造函数
Edge(int start_vertex,int end_vertex , int weight){
this->Start_Vertex = start_vertex;
this->End_Vertex = end_vertex;
this->Weight = weight;
}
///打印边
void Print(){
cout<<this->Start_Vertex<<"\t"<<this->End_Vertex<<"\t"<<this->Weight<<endl;
}
};
///图类
class Graph{
private:
int** G; ///邻接矩阵
int vNum; ///顶点数
int* dist; ///距离数组,各个顶点到最小生成树的距离
int* parent; ///父亲节点数组
vector<Edge> MST; ///最小生成树的边集合
public:
///构造函数
Graph(int v ,int e){
vNum = v;
MST.reserve(e);
G = new int*[v+1];
dist = new int[v+1];
parent = new int[v+1];
for(int i = 0 ; i < v+1 ; i++){
G[i] = new int[v+1];
dist[i] = MAX;
for(int j = 0 ; j < v+1 ; j++){
G[i][j] = MAX;
}
}
cout<<"请输入边与边长:"<<endl;
for(int i = 0 ; i < e ; i++){
int v1,v2,weight;
cin>>v1>>v2>>weight;
G[v1][v2] = G[v2][v1]= weight;
}
}
///寻找最小距离顶点函数
int FindMinDist(){
int MinDist = MAX;
int MinV = 0;
for(int i = 1 ; i < vNum+1 ; i++){ ///堆每个顶点进行遍历
/// 若V未被收录,且dist[V]更小
if(dist[i] != 0 && dist[i] < MinDist){
///更新最小距离与最小顶点
MinDist = dist[i];
MinV = i;
}
}
if(MinDist < MAX){
///最小距离小于正无穷则返回相应的顶点
return MinV;
}
///否则返回会-1的错误标志
return -1;
}
///普里姆(Prim)算法,已vertex为根节点的的最小生成树
int Prim(int vertex){
int cnt = 0; ///收录顶点的个数
int sum_weight = 0; ///最小生成树的权重和
for(int i = 1 ; i < vNum+1 ; i++){
parent[i] = vertex; ///暂时把父亲结点初始化为输出的初始顶点
dist[i] = G[vertex][i]; ///i顶点到最小生成树的距离为vertex与i之间边长
}
parent[vertex] = -1; ///把初始结点设置为最小生成树的根节点
dist[vertex] = 0; ///收录初始顶点,初始结点到最小生成树的距离为0
cnt++; ///收录的定点数加1
while(1){
int cur_vertex = FindMinDist(); ///寻找当前最小距离顶点
if(cur_vertex == -1){ ///未能找到最小的结点
///有两种可能,一是所有顶点已经全部被收录到最小生成树了
///而是所有节点都不连通
break;
}
sum_weight += dist[cur_vertex];
///把this->parent[cur_vertex]与cur_vertex构成边插入到最小生成树的边集合里
Edge edge(parent[cur_vertex],cur_vertex,dist[cur_vertex]);
this->MST.push_back(edge);
cnt++;
///置零说明已经收录到最小生成树里了,故cur_vertex顶点到最小生成树的距离为0
dist[cur_vertex] = 0;
for(int i = 1 ; i < vNum+1 ; i++){ ///对当前最小距离顶点的所有的邻接点进行遍历
///如果邻接点i不是根结点且跟cur_vertex直接相邻
if(dist[i] != 0 && G[cur_vertex][i] < MAX){
///如果cur_vertex与i之间的边小于i到最小生成树的把距离
if(G[cur_vertex][i] < dist[i]){
///更新相应距离数组与父亲结点
dist[i] = G[cur_vertex][i];
parent[i] = cur_vertex;
}
}
}
}
///上述循环结束有两种可能,一是所有顶点已经全部被收录到最小生成树了
///而是所有节点都不连通 ,故需要判断最小生成树的结点是否与顶点数相同
if(cnt != vNum){
sum_weight = -1;
}
return sum_weight;
}
///打印最小生成树的边集合
void Print_Prim(){
vector<Edge>:: iterator it;
cout<<"Prim算法构造的最小生成树的边集合为:"<<endl;
cout<<"源点\t终点\t权重"<<endl;
for(it = MST.begin() ; it != MST.end() ; it++){
it->Print();
}
}
};
int main(){
int v,e;
cout<<"请输入顶点数与边数:"<<endl;
cin>>v>>e;
Graph graph(v,e);
cout<<"请输一个初始顶点:"<<endl;
int vertex;
cin>>vertex;
int min_weight = graph.Prim(vertex);
cout<<min_weight;
if(min_weight != -1){
cout<<"Prim算法生成的最小生成树的权重和为:"<<endl;
cout<<min_weight<<endl;
graph.Print_Prim();
}
return 0;
}
我是花花,祝自己也祝您变强了~