图实现 bfs, dfs, dijkstra, prim

邓俊辉的数据结构,学习笔记,老师讲课真的真的好,好多地方还要细细打磨。

先把所有的代码贴出来

Vertex.h


using VStatus=enum{UNDISCOVERED,DISCOVERED,VISITED};//顶点状态
using EType=enum {UNDETERMINED,TREE,CROSS,FORWARD,BACKWAED};//边在遍历树中所属的类型
#define INT_MAX 2147483647
#define Rank int


template<typename Tv>
struct Vertex {
    Tv data;
    int inDegree;//入度
    int outDegree;//出度
    VStatus status;//状态
    int fTime; int dTime;
    Rank parent; int priority;
    Vertex(Tv const& d = (Tv)0) :
        data(d), inDegree(0), outDegree(0), status(UNDISCOVERED),
        dTime(-1), fTime(-1), parent(-1), priority(INT_MAX) {};
    
};

template<typename Te>
struct Edge
{
    Te data; int weight; EType type;//数据,权重,类型
    Edge(Te const&d,int w):data(d),weight(w),type(UNDETERMINED){}//构造
};

Graph.h

#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<string>
#include<iostream>
#include"Vertex.h"

using namespace std;

template<typename Tv,typename Te>
class GraphMatrix {
private:
    vector<Vertex<Tv>> V;//顶点集
    vector<vector<Edge<Te>*>> E;//边集,邻接矩阵

    void reset();

public:
    GraphMatrix() { n = 0; e = 0; }
    ~GraphMatrix() {
        for (Rank v = 0; v < n; v++) {
            for (Rank u = 0; u < n; u++)
                delete E[v][u];
        }
    }
    int n; //顶点总数
    int e; //边总数

    //顶点的基本操作
    Tv& vertex(Rank v) { return V[v].data; }//数据
    int inDegree(Rank v) { return V[v].inDegree; }//入度
    int outDegree(Rank v) { return V[v].outDegree; }//出度
    Rank firstNbr(Rank v) { return nextNbr(v, n); }//首个邻接顶点
    Rank nextNbr(Rank v, Rank u) {
        while ((-1 < u) && (!exists(v, --u)));
        return u;
    }
    VStatus& status(Rank v) { return V[v].status; }//状态
    int& dTime(Rank v) { return V[v].dTime; }
    int& fTime(Rank v) { return V[v].fTime; }
    Rank& parent(Rank v) { return V[v].parent; }//在遍历树中的父亲
    int& priority(Rank v) {return V[v].priority;}

    //顶点动态操作
    Rank insert(Tv const& vertex);
    Tv remove(Rank v);


    //边确认操作,看边是否存在
    bool exists(Rank v, Rank u) //边(v, u)是否存在
        { return (-1 < v) && (v < n)&&(-1<u) && (u < n) && E[v][u] != nullptr; }

    //边的基本操作
    EType& type(Rank v, Rank u) { return E[v][u]->type; }
    Te& edge(Rank v, Rank u) { return E[v][u]->data; }
    int& weight(Rank v, Rank u) { return E[v][u]->weight; }

    //边动态操作
    void insert(Te const& edge, int w, Rank v, Rank u) {
        if (E[v][u] != nullptr) return;
        E[v][u] = new Edge<Te>(edge, w);
        e++; V[v].outDegree++; V[u].inDegree++;
    }
    Te remove(Rank v, Rank u);
    
    //遍历
    void BFS(int v, int& clock);//广度优先搜索
    void bfs(int s);//多连通

    void DFS(int v, int& clock);//深度优先搜索
    void dfs(int s);//多连通

    //最短路径Dijkstra算法
    void dijkstra(Rank s);
    //最小生成树prim算法
    void prim(Rank s);
    //展示信息
    void getInfo();
};
template<typename Tv, typename Te>
Tv GraphMatrix<Tv, Te>::remove(Rank v) {
    for (Rank u = 0; u < n; u++) {//删除所有出边,以V[u]为起点
        if (exists(v, u))
        {
            delete E[v][u];
            V[u].inDegree--;
            e--;
        }
    }
    E.erase(E.begin() + v); n--;//删除第n行
    Tv vBak = vertex(v); V.erase(V.begin() + v);
    for (Rank u = 0; u < n; u++) {//删除第n列
        Edge<Te>* x = E[u][v];
        E[u].erase(E[u].begin() + v);
        if (x != nullptr) {
            delete x;
            V[u].outDegree--;
            e--;
        }
    }
    return vBak;
};

template<typename Tv, typename Te>
Rank GraphMatrix<Tv, Te>::insert(Tv const& vertex)
{
    //插入顶点,返回编号
    for (Rank u = 0; u < n; u++) {
        E[u].insert(E[u].end(), nullptr);
    }
    n++;
    E.insert(E.end(), vector<Edge<Te>*>(n, (Edge<Te>*)nullptr));
    V.insert(V.end(), Vertex<Tv>(vertex));
    return V.size();
};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::getInfo() {
    //    用于输出各边的状态
    map<int, string> vStatus;
    vStatus[UNDISCOVERED] = "UNDISCOVERED";
    vStatus[DISCOVERED] = "DISCOVERED";
    vStatus[VISITED] = "VISITED";

    map<int, string> eType;
    eType[UNDETERMINED] = "UNDETERMINED";
    eType[TREE] = "TREE";
    eType[CROSS] = "CROSS";
    eType[FORWARD] = "FORWARD";
    eType[BACKWAED] = "BACKWARD";

    cout << "一共有" << n << "个顶点," << e << "条边";
    cout << endl<<"点信息:" << endl;
    for (int i = 0; i < V.size(); i++) {
        cout << "数据:" << V[i].data << "  " << " 入度:" << V[i].inDegree
            << "出度:" << V[i].outDegree << endl;
        cout<<"dTime:"<<dTime(i)<<" fTime:"<<fTime(i) << " 父节点:"<<parent(i) << " 状态:" << vStatus[status(i)] <<" 距离:"<<priority(i) << endl;
    }
    cout << endl << "边信息:" << endl;
    for (int v = 0; v < n; v++) {
        for (int u = 0; u < n; u++) {
            if (E[v][u] != nullptr) {
                cout << "起点:" << v << " 终点:" << u << " 类型:" << eType[type(v, u)] << endl;
            }
        }
    }

    for (int v = 0; v < n; v++) {
        for (int u = 0; u < n; u++) {
            if (E[v][u] != nullptr)
                cout << E[v][u]->data << " ";
            else
                cout << 0 << " ";
        }
        cout << endl;
    }
    cout << endl;
};

template<typename Tv, typename Te>
Te GraphMatrix<Tv, Te>::remove(Rank v, Rank u) {
    //删除顶点v和u之间的联边
    Te eBak = edge(v, u);
    delete E[v][u]; E[v][u] = nullptr;
    e--; V[v].outDegree--; V[u].inDegree--;
    return eBak;
}

template<typename Tv,typename Te>
void GraphMatrix<Tv, Te>::BFS(int v, int& clock) {
    queue<Rank> Q;//存的是未遍历节点的编号
    status(v) = DISCOVERED;
    Q.push(v);
    dTime(v) = clock; clock = 0;
    while (!Q.empty()) {
        Rank v = Q.front();
        Q.pop();
        for (Rank u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
            //遍历所有的邻居
            if (UNDISCOVERED == status(u)) {
                status(u) = DISCOVERED;
                Q.push(u); dTime(u) = dTime(v) + 1;//发现该节点
                type(v, u) = TREE; parent(u) = v;
            }
            else
                type(v, u) = CROSS;
            status(v) = VISITED;
            fTime(v) = clock++;
            if (Q.empty())
                clock = dTime(v) + 1;
            else if (dTime(v) < dTime(Q.front()))
                clock = 0;
        }
    }

};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::bfs(int s) {
    reset(); int clock = 0; int v = s;
    do
        if (UNDISCOVERED == status(v))
            BFS(v, clock);
    while (s != (v = (++v % n)));

};

template<typename Tv,typename Te>
void GraphMatrix<Tv, Te>::reset() {
    //所有的顶点、边的辅助信息复位
    for (Rank v = 0; v < n; v++) {
        //点信息复位
        status(v) = UNDISCOVERED;
        dTime(v) = -1; fTime(v) = -1;
        parent(v) = -1; priority(v) = INT_MAX;
        //边信息
        for (Rank u = 0; u < n; u++)
            if (exists(v, u))
                type(v, u) = UNDETERMINED;
    }

};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::DFS(int v, int& clock) {
    dTime(v) = ++clock; status(v) = DISCOVERED;
    for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
        switch (status(u))
        {
        case UNDISCOVERED:
            type(v, u) = TREE; parent(u) = v; DFS(u, clock); break;
        case DISCOVERED:
            type(v, u) = BACKWAED; break;
        default:
            type(v, u) = (dTime(v) < dTime(u) ? FORWARD : CROSS);
            break;
        }
    }
    status(v) = VISITED; fTime(v) = ++clock;
    
};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::dfs(int s) {
    reset(); int clock = 0; int v = s;
    do
        if (UNDISCOVERED == status(v))
            DFS(v, clock);
    while (s != (v = (++v % n)));

};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::dijkstra(Rank s) {
    reset(); priority(s) = 0;
    for (int i = 0; i < n; i++) {
        status(s) = VISITED;
        if (-1 < parent(s)) type(parent(s), s) = TREE;
        for (Rank j = firstNbr(s); -1 < j; j = nextNbr(s, j)) {
            //在所有s邻居里找最短的路径
            if (status(j) == UNDISCOVERED && priority(j) > priority(s) + weight(s, j))
            {
                priority(j) = priority(s) + weight(s, j);
                parent(j) = s;
            } 
        }
        //找出最短的路径,也适用于所有的找最小值
        for (int shortest = INT_MAX, j = 0; j < n; j++) {
            if (status(j) == UNDISCOVERED && shortest > priority(j)) {
                shortest = priority(j); s = j;
            }
        }
            
    }

};


template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::prim(Rank s) {
    reset(); priority(s) = 0;
    for (int i = 0; i < n; i++) {
        status(s) = VISITED;
        if (-1 < parent(s)) type(parent(s), s) = TREE;
        for (Rank j = firstNbr(s); -1 < j; j = nextNbr(s, j)) {
            //在所有s邻居里找最短的路径,到自己的最短路径,不一样的地方
            if (status(j) == UNDISCOVERED && priority(j) > weight(s, j))
            {
                priority(j) = weight(s, j);
                parent(j) = s;
            }
        }
        //找出最短的路径,也适用于所有的找最小值
        for (int shortest = INT_MAX, j = 0; j < n; j++) {
            if (status(j) == UNDISCOVERED && shortest > priority(j)) {
                shortest = priority(j); s = j;
            }
        }

    }

};

两个测试用例

void test() {
    //BFS DFS测试用例
    GraphMatrix<int, int> graph;
    for (int i = 0; i < 10; i++) {
        graph.insert(i);
    }
    for (int i = 1; i < 4; i++) {
        graph.insert(1, 0, 0, i);
        graph.insert(1, 0, i, 0);
    }
    int startPoint = 4;
    for (int i = 1; i < 4; i++) {
        for (int j = 0; j < 2; j++) {
            graph.insert(1, 0, startPoint, i);
            graph.insert(1, 0, i, startPoint);
            startPoint++;
        }
        graph.insert(1, 0, startPoint - 2, startPoint - 1);
        graph.insert(1, 0, startPoint - 1, startPoint - 2);
    }
    int a = 0;
    graph.getInfo();
    graph.dfs(0);
    graph.getInfo();
}

void test_di() {
    //迪杰斯特拉算法测试
    GraphMatrix<int, int> graph;
    for (int i = 0; i < 6; i++) {
        graph.insert(i);
    }
    vector<vector<int>> a({ {0,1,12},{0,3,1},
        {1,2,5},{1,3,9}, {1,4,4},
        {3,4,3},
        {2,4,13 },{2,5,15},
        {4,5,14} });
    for (int i = 0; i < a.size(); i++) {
        int j = a[i][0], k = a[i][1];
        int w = a[i][2];
        graph.insert(1, w, j, k);
        graph.insert(1, w, k, j);
    }
    graph.dijkstra(0);
    graph.getInfo();
}

图的实现

与老师给的代码不一样的地方是,我用的是c++模板库里的vector,用自己写的Vector析构的时候会出现空指针,不知道问题在哪。

出现了一个很愚蠢的bug,一直找不着顶点集E,结果是没加using namespace std; 原来模板库要用这个,受教了。废话不多说,贴上代码。

顶点和边的实现

Vstataus是顶点状态,undiscovered未访问顶点,discovered访问过该顶点,但他的所有邻居还没访问完,在递归中,正在访问,visited访问完了,包括他的所有邻居。

EType是边的状态,undetermined是未访问边,tree是该边是树的组成部分,bfs, dfs构成的搜索树,cross是这个边的两个点没有亲缘关系,谁也不是谁的父亲或曾曾父亲,forward是起始点是父亲,终点是孩子,backward起始点是孩子,终点是父亲。

比较重要的是知道这几个enum类型是啥意思。

using VStatus=enum{UNDISCOVERED,DISCOVERED,VISITED};//顶点状态
using EType=enum {UNDETERMINED,TREE,CROSS,FORWARD,BACKWAED};//边在遍历树中所属的类型
#define INT_MAX 2147483647
#define Rank int

template<typename Tv>
struct Vertex {
    Tv data;
    int inDegree;//入度
    int outDegree;//出度
    VStatus status;//状态
    int fTime; int dTime;//时间标签,dTime是开始访问的节点,fTime是结束访问的节点
    Rank parent; int priority;
    Vertex(Tv const& d = (Tv)0) :
        data(d), inDegree(0), outDegree(0), status(UNDISCOVERED),
        dTime(-1), fTime(-1), parent(-1), priority(INT_MAX) {};
};

template<typename Te>
struct Edge
{
    Te data; int weight; EType type;//数据,权重,类型
    Edge(Te const&d,int w):data(d),weight(w),type(UNDETERMINED){}//构造
};

图实现

接下来是图的操作,先把所有的方法贴上来,实现在后面。

template<typename Tv,typename Te>
class GraphMatrix {
private:
    vector<Vertex<Tv>> V;//顶点集
    vector<vector<Edge<Te>*>> E;//边集,邻接矩阵

    void reset();

public:
    GraphMatrix() { n = 0; e = 0; }
    ~GraphMatrix() {
        for (Rank v = 0; v < n; v++) {
            for (Rank u = 0; u < n; u++)
                delete E[v][u];
        }
    }
    int n; //顶点总数
    int e; //边总数

    //顶点的基本操作
    Tv& vertex(Rank v) { return V[v].data; }//数据
    int inDegree(Rank v) { return V[v].inDegree; }//入度
    int outDegree(Rank v) { return V[v].outDegree; }//出度
    Rank firstNbr(Rank v) { return nextNbr(v, n); }//首个邻接顶点
    Rank nextNbr(Rank v, Rank u) {
        while ((-1 < u) && (!exists(v, --u)));
        return u;
    }
    VStatus& status(Rank v) { return V[v].status; }//状态
    int& dTime(Rank v) { return V[v].dTime; }
    int& fTime(Rank v) { return V[v].fTime; }
    Rank& parent(Rank v) { return V[v].parent; }//在遍历树中的父亲
    int& priority(Rank v) {return V[v].priority;}

    //顶点动态操作
    Rank insert(Tv const& vertex);
    Tv remove(Rank v);


    //边确认操作,看边是否存在
    bool exists(Rank v, Rank u) //边(v, u)是否存在
        { return (-1 < v) && (v < n)&&(-1<u) && (u < n) && E[v][u] != nullptr; }

    //边的基本操作
    EType& type(Rank v, Rank u) { return E[v][u]->type; }
    Te& edge(Rank v, Rank u) { return E[v][u]->data; }
    int& weight(Rank v, Rank u) { return E[v][u]->weight; }

    //边动态操作
    void insert(Te const& edge, int w, Rank v, Rank u) {
        if (E[v][u] != nullptr) return;
        E[v][u] = new Edge<Te>(edge, w);
        e++; V[v].outDegree++; V[u].inDegree++;
    }
    Te remove(Rank v, Rank u);
    
    //遍历
    void BFS(int v, int& clock);//广度优先搜索
    void bfs(int s);//多连通

    void DFS(int v, int& clock);//深度优先搜索
    void dfs(int s);//多连通

    //最短路径Dijkstra算法
    void dijkstra(Rank s);
    //最小生成树prim算法
    void prim(Rank s);
    //展示信息
    void getInfo();
};
template<typename Tv, typename Te>
Tv GraphMatrix<Tv, Te>::remove(Rank v) {
    for (Rank u = 0; u < n; u++) {//删除所有出边,以V[u]为起点
        if (exists(v, u))
        {
            delete E[v][u];
            V[u].inDegree--;
            e--;
        }
    }
    E.erase(E.begin() + v); n--;//删除第n行
    Tv vBak = vertex(v); V.erase(V.begin() + v);
    for (Rank u = 0; u < n; u++) {//删除第n列
        Edge<Te>* x = E[u][v];
        E[u].erase(E[u].begin() + v);
        if (x != nullptr) {
            delete x;
            V[u].outDegree--;
            e--;
        }
    }
    return vBak;
};

template<typename Tv, typename Te>
Rank GraphMatrix<Tv, Te>::insert(Tv const& vertex)
{
    //插入顶点,返回编号
    for (Rank u = 0; u < n; u++) {
        E[u].insert(E[u].end(), nullptr);
    }
    n++;
    E.insert(E.end(), vector<Edge<Te>*>(n, (Edge<Te>*)nullptr));
    V.insert(V.end(), Vertex<Tv>(vertex));
    return V.size();
};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::getInfo() {
    //    用于输出各边的状态
    map<int, string> vStatus;
    vStatus[UNDISCOVERED] = "UNDISCOVERED";
    vStatus[DISCOVERED] = "DISCOVERED";
    vStatus[VISITED] = "VISITED";

    map<int, string> eType;
    eType[UNDETERMINED] = "UNDETERMINED";
    eType[TREE] = "TREE";
    eType[CROSS] = "CROSS";
    eType[FORWARD] = "FORWARD";
    eType[BACKWAED] = "BACKWARD";

    cout << "一共有" << n << "个顶点," << e << "条边";
    cout << endl<<"点信息:" << endl;
    for (int i = 0; i < V.size(); i++) {
        cout << "数据:" << V[i].data << "  " << " 入度:" << V[i].inDegree
            << "出度:" << V[i].outDegree << endl;
        cout<<"dTime:"<<dTime(i)<<" fTime:"<<fTime(i) << " 父节点:"<<parent(i) << " 状态:" << vStatus[status(i)] <<" 距离:"<<priority(i) << endl;
    }
    cout << endl << "边信息:" << endl;
    for (int v = 0; v < n; v++) {
        for (int u = 0; u < n; u++) {
            if (E[v][u] != nullptr) {
                cout << "起点:" << v << " 终点:" << u << " 类型:" << eType[type(v, u)] << endl;
            }
        }
    }

    for (int v = 0; v < n; v++) {
        for (int u = 0; u < n; u++) {
            if (E[v][u] != nullptr)
                cout << E[v][u]->data << " ";
            else
                cout << 0 << " ";
        }
        cout << endl;
    }
    cout << endl;
};

template<typename Tv, typename Te>
Te GraphMatrix<Tv, Te>::remove(Rank v, Rank u) {
    //删除顶点v和u之间的联边
    Te eBak = edge(v, u);
    delete E[v][u]; E[v][u] = nullptr;
    e--; V[v].outDegree--; V[u].inDegree--;
    return eBak;
}

说几个比较重要的点或者感悟

  1. 所有的小方法都写出来了,像边是否合法,是否存在,返回节点的下一个邻居,不从轮子造起,后面用的时候十分舒服

  1. 把点,边的属性用类的方法封装起来,一个&解决了很多问题,就不用去找Vertex的属性了

  1. exists(v , u)方法,判断边( v , u)是否存在

  1. v u 不能越界,越界了返回false

  1. E[v][u]是否指向空指针

  1. nextNbr(v , u), 返回V[v]的邻居V[u]的下一个邻居的下标,就像我们图上的边,下一条边,省去了很多的步骤。

  1. 插入一个点 insert( Vertex(d)),为d

  1. V在末尾添加一个元素,指向新建的节点

  1. E邻接向量的表示,E[0]~E[n-1]的末尾添加一个元素,也要添加一个长为e的E[n]

  1. n++

  1. 插入一条边

  1. E[v][u]=new Edge();

  1. V[v]出度++

  1. V[u]入度++

  1. e++(边的数量)

  1. 删除点和边的时候,顺序和添加的时候完全相反

  1. v和u一直表示边的起点下标和终点下标

bfs 广度优先搜索

图遍历算法的一种,从一个节点开始,第一次遍历所有和他相邻的边,第二次遍历和他相邻边的相邻边,就像石子投入湖泊,一层一层的涟漪。

思路:

  1. queue中取出一个节点

  1. 遍历他的所有邻居,

  1. 他是UNDISCOVER,入栈,标记为DISCOVERED,记录辅助信息

  1. 他是VISITED或者DISCOVERED,标记边为CROSS

  1. 对这个节点标记为DISCOVERED

  1. 再从queue中取出一个节点,循环

辅助信息复位

template<typename Tv,typename Te>
void GraphMatrix<Tv, Te>::reset() {
    //所有的顶点、边的辅助信息复位
    for (Rank v = 0; v < n; v++) {
        //点信息复位
        status(v) = UNDISCOVERED;
        dTime(v) = -1; fTime(v) = -1;
        parent(v) = -1; priority(v) = INT_MAX;
        //边信息
        for (Rank u = 0; u < n; u++)
            if (exists(v, u))
                type(v, u) = UNDETERMINED;
    }

};

bfs代码

template<typename Tv,typename Te>
void GraphMatrix<Tv, Te>::BFS(int v, int& clock) {
    queue<Rank> Q;//存的是未遍历节点的编号
    status(v) = DISCOVERED;
    Q.push(v);
    dTime(v) = clock; clock = 0;
    while (!Q.empty()) {
        Rank v = Q.front();
        Q.pop();
        for (Rank u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
            //遍历所有的邻居
            if (UNDISCOVERED == status(u)) {
                status(u) = DISCOVERED;
                Q.push(u); dTime(u) = dTime(v) + 1;//发现该节点
                type(v, u) = TREE; parent(u) = v;
            }
            else
                type(v, u) = CROSS;
            status(v) = VISITED;
            fTime(v) = clock++;
            if (Q.empty())
                clock = dTime(v) + 1;
            else if (dTime(v) < dTime(Q.front()))
                clock = 0;
        }
    }

};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::bfs(int s) {
    reset(); int clock = 0; int v = s;
    do
        if (UNDISCOVERED == status(v))
            BFS(v, clock);
    while (s != (v = (++v % n)));

};

这个时候,dTime和fTime就显现出来了,当AdTime<BdTime<BfTime<AfTime时候,我们就可以从O(1)的时间内判断B是A的子节点。边属性为Tree,说明他是遍历树的枝干,Cross, 说明不是枝干。

外面的一个bfs,找到所有未访问的点,因为图可能会有两个连通域。总的算法还是O(n2),外面的不可能遍历所有的点。

dfs 深度优先遍历

对一个点,找到他的下一个未被发现的邻居,将这个点设为discovered,然后再对这个未被发现的邻居,进行dfs,知道他的所有邻居都被发现了,将这个点设为visited,设置ftime,再返回遍历他的那个邻居。

思路:

  1. 一个节点,遍历他的所有邻居

  1. 邻居为UNDISCOVERED,记录辅助信息,边设为TREE,对这个邻居递归调用dfs

  1. 邻居为DISCOVERED,说明他已经遍历完了,且辈分比这个节点大,边设为BACKWAED,意为边是从辈分小的到辈分大的边

  1. 邻居为VISITED,这个地方有点模糊。

  1. 结束,记录辅助信息。

贴代码,dfs的。

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::DFS(int v, int& clock) {
    dTime(v) = ++clock; status(v) = DISCOVERED;
    for (int u = firstNbr(v); -1 < u; u = nextNbr(v, u)) {
        switch (status(u))
        {
        case UNDISCOVERED:
            type(v, u) = TREE; parent(u) = v; DFS(u, clock); break;
        case DISCOVERED:
            type(v, u) = BACKWAED; break;
        default:
            type(v, u) = (dTime(v) < dTime(u) ? FORWARD : CROSS);
            break;
        }
    }
    status(v) = VISITED; fTime(v) = ++clock;
    
};

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::dfs(int s) {
    reset(); int clock = 0; int v = s;
    do
        if (UNDISCOVERED == status(v))
            DFS(v, clock);
    while (s != (v = (++v % n)));

};

dijkstra

大名鼎鼎的最短路径算法,求带权图中,一个节点到另一个节点的最短距离。

假设A到D的最短路径是A-->B-->C-->D,那么A到C的最短路径一定是A-->B-->C,因为如果存在更短的路径使得A到C更短,那么我们可以找到另一条路径是A到D更短。

假设图中有A, B, C, D, E ,F六个点,求A到F最短路径,我们求出A到BCDE的最短距离,然后在BF, CF ,DF, EF(如果存在)选取一条最短的,两者相加即可。

思路:

  1. 遍历A的所有邻居,更新他们的权重为边AB, AC的权重(未遍历到的边权重设为正无穷)

  1. 选出权重最小的点,此时这个点的parent是A

  1. 遍历找到的所有邻居(假设他是B),当AB+BX<weight(X)时,说明有一条更短的路,更新他的权重

  1. 循环遍历n次

代码

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::dijkstra(Rank s) {
    reset(); priority(s) = 0;
    for (int i = 0; i < n; i++) {
        status(s) = VISITED;
        if (-1 < parent(s)) type(parent(s), s) = TREE;
        for (Rank j = firstNbr(s); -1 < j; j = nextNbr(s, j)) {
            //在所有s邻居里找最短的路径
            if (status(j) == UNDISCOVERED && priority(j) > priority(s) + weight(s, j))
            {
                priority(j) = priority(s) + weight(s, j);
                parent(j) = s;
            } 
        }
        //找出最短的路径,也适用于所有的找最小值
        for (int shortest = INT_MAX, j = 0; j < n; j++) {
            if (status(j) == UNDISCOVERED && shortest > priority(j)) {
                shortest = priority(j); s = j;
            }
        }
            
    }

};

测试

void test_di() {
    //迪杰斯特拉算法测试
    GraphMatrix<int, int> graph;
    for (int i = 0; i < 6; i++) {
        graph.insert(i);
    }
    vector<vector<int>> a({ {0,1,12},{0,3,1},
        {1,2,5},{1,3,9}, {1,4,4},
        {3,4,3},
        {2,4,13 },{2,5,15},
        {4,5,14} });
    for (int i = 0; i < a.size(); i++) {
        int j = a[i][0], k = a[i][1];
        int w = a[i][2];
        graph.insert(1, w, j, k);
        graph.insert(1, w, k, j);
    }
    graph.dijkstra(0);
    graph.getInfo();
}

prim

prim求的是最小生成树,相当于在一个带权图中,用最小的成本,生成一棵树,使得所有的节点都是连通的。思路和dijkstra几乎一样,就是判断条件不一样。

思路:

  1. 遍历A的所有邻居,更新他们的权重为边AB, AC的权重(未遍历到的边权重设为正无穷)

  1. 选出权重最小的点,此时这个点的parent是A

  1. 遍历找到的所有邻居(假设他是B),当BX<weight(X)时,说明有一条更短的路,更新他的权重

  1. 循环遍历n次

template<typename Tv, typename Te>
void GraphMatrix<Tv, Te>::prim(Rank s) {
    reset(); priority(s) = 0;
    for (int i = 0; i < n; i++) {
        status(s) = VISITED;
        if (-1 < parent(s)) type(parent(s), s) = TREE;
        for (Rank j = firstNbr(s); -1 < j; j = nextNbr(s, j)) {
            //在所有s邻居里找最短的路径,到自己的最短路径,不一样的地方
            if (status(j) == UNDISCOVERED && priority(j) > weight(s, j))
            {
                priority(j) = weight(s, j);
                parent(j) = s;
            }
        }
        //找出最短的路径,也适用于所有的找最小值
        for (int shortest = INT_MAX, j = 0; j < n; j++) {
            if (status(j) == UNDISCOVERED && shortest > priority(j)) {
                shortest = priority(j); s = j;
            }
        }

    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值