数据结构——图的代码总结(DFS、BFS、邻接、度、拓扑、Dijikstra、Floyd、Kruskal、Prim)

目录

一、创建图(邻接矩阵)且进行深度优先遍历和广度优先遍历

二、构建邻接表(尾插法)并遍历邻接表

三、计算有向图和无向图的度

四、构建有向图并进行拓扑排序

五、有权图的迪杰斯特拉(Dijikstra)算法

六、无权图的迪杰斯特拉(Dijikstra)算法

七、弗洛伊德算法(Floyd算法)解决任意两点间的最短路径

八、克鲁斯卡尔算法(Kruskal算法)求最小生成树

九、普里姆Prim算法构造最小生成树


一、创建图(邻接矩阵)且进行深度优先遍历和广度优先遍历

#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;
} 

我是花花,祝自己也祝您变强了~ 

  • 8
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吕飞雨的头发不能秃

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值