图——C++实现

基本概念

(graph)是用线连接在一起的顶点或节点的集合,即两个要素:顶点。每一条边连接个两个顶点,用(i,j)表示顶点为 i 和 j 的边。

​ 如果用图示来表示一个图,一般用圆圈表示顶点,线段表示边。有方向的边称为有向边,对应的图成为有向图,没有方向的边称为无向边,对应的图叫无向图。对于无向图,边(i, j)和(j,i)是一样的,称顶点 i 和 j 是邻接的,边(i,j)关联于顶点 i 和 j ;对于有向图,边(i,j)表示由顶点 i 指向顶点 j 的边,即称顶点 i 邻接至顶点 j ,顶点 i 邻接于顶点 j ,边(i,j)关联至顶点 j 而关联于顶点 i 。

​ 对于很多的实际问题,不同顶点之间的边的权值(长度、重量、成本、价值等实际意义)是不一样的,所以这样的图被称为加权图,反之边没有权值的图称为无权图。所以,图分为四种:加权有向图,加权无向图,无权有向图,无权无向图。

这里写图片描述

​ 在一个无向图中,与一个顶点相关联的边数成为该顶点的。而对于有向图,则用入度来表示关联至该顶点的边数,出度来表示关联于该顶点的边数。

​ 一个具有n个顶点和n(n-1)/2条边的无向图称为一个完全图,即每个顶点的度等于总顶点数减1。

图的描述

抽象数据类型

​ 定义抽象数据类型graph,有向图、无向图、加权图和无权图都可以根据此ADT实现。

numberOfVerticices():返回图的顶点数 
numberOfEdges:返回图的边数
exitsEdge(i, j):如果边(i,j)存在,则返回true,否则返回false
insertEdge(theEdge):插入边theEdge
eraseEdge(i, j):删除边(i,j)
degree(i):返回顶点 i 的度(无向图)
inDegree(i):返回顶点 i 的入度
outDegree(i):返回顶点 i 的出度
directed():当且仅当有向图,返回true
weighted():当且仅当加权图,返回true

无权图的描述

​ 对无向图的描述方法都是基于邻接的方式:邻接矩阵、邻接链表和邻接数组。

邻接矩阵

用图e的邻接矩阵表示图f的无权图,A(i,j)等于1表示边(i,j)存在,等于0则表示不存在。有向图无向图都可以用矩阵这样表示,但是对于无向图,矩阵具有对称性,即A(i,j)和A(j,i)一样,所以为了节省空间,可以用下三角(上三角)矩阵表示。

这里写图片描述

邻接链表

图g是图f对应的邻接链表,每一个和顶点 i 关联的顶点 j ,依次构成一条链表,节点的元素是顶点,节点的next指针指向下一个 j 构成的顶点,最后一个节点指向nullptr,7条链表用一个表List来维护。

这里写图片描述

邻接数组

图g是图f对应的邻接数组,可以看成一个列数不等的二位数组,也可以看成由一个元素为一维数组的表List。

这里写图片描述

加权图的描述

对于加权图,一般使用邻接矩阵或邻接链表描述。

邻接矩阵

和无权图的邻接矩阵描述相同道理,只不过A(I,j)的值不再是0或1,而是对应的加权图中的权值,而对于不存在的边,可以用一个固定的值表示,如-1。

邻接链表

和无权图的邻接链表描述也类似,每一个和顶点 i 关联的顶点 j 依次构成一条链表,不过需要改变的是节点元素包含两个域{顶点j,权值},节点next指向下一个节点,也用一个List维护这些链表。

类实现(C++)

使用邻接矩阵描述实现加权有向图,权值类型为T,无向图和无权图都可以由它派生出。

//加权图的边
template <typename T>
class weightedEdge {
public:
    weightedEdge() {}
    weightedEdge(int theV1, int theV2, T theW) : v1(theV1), v2(theV2), w(theW) {}
    ~weightedEdge() {}

    int vertex1() const { return v1; }
    int vertex2() const { return v2; }
    T weight() const { return w; }
    operator T() const { return w; }
private:
    int v1,
        v2;
    T w;
};
//邻接矩阵加权有向图
template <typename T>
class adjacencyWDigraph {
public:
    //构造函数和析构函数
    adjacencyWDigraph(int numberOfVertices = 0, T theNoEdge = 0);
    ~adjacencyWDigraph();

    int numberOfVertices() const { return n; }
    int numberOfEdges() const { return e; }
    bool directed() const { return true; }
    bool weighted() const { return true; }

    //判断边(i,j)是否存在
    bool existEdge(int i, int j) const;
    //插入、删除边
    void insertEdge(weightedEdge<T> *theEdge);
    void eraseEdge(int i, int j);

    //顶点的入度和出度
    int inDegree(int theVertex) const;
    int outDegree(int theVertex) const;

    //广度优先遍历和深度优先遍历
    void bfs(int v, int reach[], int lable);
    void dfs(int v, int reach[], int lable);

    //迭代器
    class iterator {
    public:
        iterator(T *theRow, T theNoEdge, int numberOfVertices) {
            row = theRow;
            noEdge = theNoEdge;
            n = numberOfVertices;
            currentVertex = 1;
        }

        ~iterator() {}

        int next() {
            for (int j = currentVertex; j <= n; ++j)
                if (row[j] != noEdge) {
                    currentVertex = j + 1;
                    return j;
                }

            currentVertex = n + 1;
            return 0;
        }

        int next(T &theWeight) {
            for (int j = currentVertex; j <= n; ++j)
                if (row[j] != noEdge) {
                    currentVertex = j + 1;
                    theWeight = row[j];
                    return j;
                }

            currentVertex = n + 1;
            return 0;
        }

    private:
        T *row;         //邻接矩阵的行
        T noEdge;
        int n;
        int currentVertex;
    };

    //生成迭代器
    iterator* makeIterator(int theVertex) {
        checkVertex(theVertex);
        return new iterator(a[theVertex], noEdge, n);
    }

private:
    int n;      //顶点数
    int e;      //边数
    int **a;    //邻接矩阵(二维数组)
    T noEdge;   //表示不存在的边

                //检查顶点是否存在
    void checkVertex(int theVertex) const;
};

template <typename T>
adjacencyWDigraph<T>::adjacencyWDigraph(int numberOfVertices = 0, T theNoEdge = 0) {
    if (numberOfVertices < 0) {
        cout << "number of vertices must be >= 0";
        exit(1);
    }

    n = numberOfVertices;
    e = 0;
    noEdge = theNoEdge;
    a = new T*[n + 1];
    for (int i = 0; i <= n; ++i)
        a[i] = new int[n + 1];

    //初始化邻接矩阵
    for (int i = 1; i <= n; ++i)
        fill(a[i], a[i] + n + 1, noEdge);
}

template <typename T>
adjacencyWDigraph<T>::~adjacencyWDigraph<T>() {
    for (int i = 0; i <= n; ++i)
        delete[] a[i];
    delete[] a;
    a = nullptr;
}

template <typename T>
bool adjacencyWDigraph<T>::existEdge(int i, int j) const {
    if (i < 1 || i > n || j < 1 || j > n || a[i][j] == noEdge)
        return false;
    else
        return true;
}

template <typename T>
void adjacencyWDigraph<T>::insertEdge(weightedEdge<T> *theEdge) {
    int v1 = theEdge->vertex1();
    int v2 = theEdge->vertex2();
    if (v1 < 1 || v1 > n || v2 < 1 || v2 > n || v1 == v2) {
        cout << "(" << v1 << "," << v2 << ") is not a permissble edge";
        exit(1);
    }

    if (a[v1][v2] == noEdge)
        ++e;
    a[v1][v2] = theEdge->weight();
}

template <typename T>
void adjacencyWDigraph<T>::eraseEdge(int i, int j) {
    if (i >= 1 && i <= n && j >= 1 && j <= n && a[i][j] != noEdge) {
        a[i][j] = noEdge;
        --e;
    }
}

template <typename T>
void adjacencyWDigraph<T>::checkVertex(int theVertex) const {
    if (theVertex < 1 || theVertex > n) {
        cout << "no vertex " << theVertex;
        exit(1);
    }
}

template <typename T>
int adjacencyWDigraph<T>::inDegree(int theVertex) const {
    checkVertex(theVertex);

    int sum = 0;
    for (int i = 1; i <= n; ++i)
        if (a[i][theVertex] != noEdge)
            ++sum;

    return sum;
}

template <typename T>
int adjacencyWDigraph<T>::outDegree(int theVertex) const {
    checkVertex(theVertex);

    int sum = 0;
    for (int j = 1; j <= n; ++j)
        if (a[theVertex][j] != noEdge)
            ++sum;

    return sum;
}

图的遍历

很多算法需要从一个已知的顶点开始,搜索所有可以到达的顶点。所谓顶点u是从顶点v可到达的,是指有一条顶点v到顶点u的路径。这种搜索有两种常见的方法:广度优先搜索(breadth first search,BFS)和深度优先搜索(depth first search,DFS)。一般来说,深度优先搜索方法效率更高,使用的也更多。

广度优先搜索

template <typename T>
void adjacencyWDigraph<T>::bfs(int v, int reach[], int lable) {
    queue<int> q;
    reach[v] = lable;

    q.push(v);
    while (!q.empty()) {
        //从队列中删除一个标记过的顶点
        int w = q.front();
        q.pop();

        iterator *iw = makeIterator(w);
        int u;
        while((u = iw->next()) != 0)
        //u是w的相邻顶点
            if (reach[u] == 0) {
                q.push(u);
                reach[u] = lable;
            }

        delete iw;
    }
}

深度优先搜索

template <typename T>
void adjacencyWDigraph<T>::dfs(int v, int reach[], int lable) {
    reach[v] = lable;
    iterator *iv = makeIterator(v);

    int u;
    while ((u = iv->next()) != 0)
    //u是v的相邻顶点
        if (reach[u] == 0)
            dfs(u, reach, lable);

    delete iv;
}
  • 10
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值