数据结构之图(C++)--邻接矩阵表示(一)

来源:互联网 发布:select算法 编辑:程序博客网 时间:2024/04/30 15:27

数据结构之图(C++)–邻接矩阵表示(一)


基本概念

简单地说,图是一个用线或边链接爱一起的顶点或节点的集合。严格地说,图是有限集V和E的有序对,即G=(V,E),其中V的元素称为顶点(也称节点或点),E的元素称为边(也叫弧或线)。每一条边链接两个不同的顶点,而且用元组(i,j)来表示,其中i和j是边所连接的两个顶点。

  1. 图的术语:顶点,边,邻接,关联,度,回路,路径,连通构件,生成树
  2. 图的类型:无向图,有向图和加权图
  3. 图的常用描述方式:邻接矩阵,矩阵邻接表和邻接链表
  4. 图的搜索方式:广度优先搜索和深度优先搜索
  5. 相关算法:拓扑排序,二分覆盖,最短路径,最小生成树,旅行推销员等等

图的相关定义

  • 如果图的所有边都是无向边(不带箭头的边),那么该图叫做无向图。如果图的所有边都是有向边,那么该图叫做有向图。
    有向图和无向图

  • 一个图不能有重复的边。在有向图中,任意两个顶点 i 和 j 之间,从顶点 i 到顶点 j 最多有一条边。在无向图中,任意两个顶点之间,最多只能有一条边。而且一个图不能包含自连边(即“顶点”自己与自己连线)

  • 当我们给每条边赋予一个表示成本的值,我们称之为权(例如,高速公路上,从A地到B地要走300公里,A地到C地要走500公里,这里的300和500可以看成是AB和AC边的权)。这是的图我们成为加权有向图或加权五向图。

  • 度:一个顶点i相关联的边数称为该顶点的度。

  • 有向图中的入度和出度

    • 入度: 关联至该顶点的边数。
    • 出度:关联于该顶点的边数
      例如上面的有向图,顶点3的入度为2, 出度为2。顶点5的入度为2,出度为1.

图的相关操作

抽象类graph

template <typename T>class graph{public:    virtual ~graph() { }    // ADT 方法    virtual int numberOfVertices() const = 0;       // 返回图的顶点数目    virtual int numberOfEdges() const = 0;          // 返回图的边数    virtual bool existsEge(int, int) const = 0;         // 如果边(i, j)存在,返回true    virtual void insertEdge(edge<T>*) = 0;          // 插入边    virtual void eraseEdge(int, int) = 0;               // 删除边    virtual int degree(int) const = 0;                  // 返回顶点的i的度    virtual int inDegree(int) const = 0;                // 返回顶点i的入度    virtual int outDegree(int) const = 0;           // 返回顶点i的出度    virtual bool directed() const = 0;              // 如果是有向图返回true    virtual bool weighted() const = 0;              // 如果是加权图返回true    virtual vertexIterator<T>* interator(int) = 0;      // 访问指定顶点的相邻顶点};

上述的相关操作将在下一节讲解

邻接矩阵

一个n顶点图G=(V,E)的邻接矩阵是一个n×n的矩阵,其中每个元素是0或者1.

  • 无向图中元素定义:A(i,j) = 1 (如果(i,j)∈E或(j,i)∈E) 或者 A(i,j)= 0
  • 无向图的邻接矩阵是对称的。
    例如下面表示的就是无限图
    矩阵

  • 有向图元素定义:A(i,j)= 1 (如果(i,j)∈E)或者 A(i,j)= 0

代码实现

// val值默认为1bool Graph::setDirectGraph(int row, int col, int val){    if (row < 0 || row > m_iCapacity || col < 0 || col > m_iCapacity) {        return false;    }    m_pMatrix[row * m_iCapacity + col] = val;    return true;}bool Graph::setUndiretGraph(int row, int col, int val){    if (row < 0 || row >= m_iCapacity || col < 0 || col >= m_iCapacity) {        return false;    }    m_pMatrix[row * m_iCapacity + col] = val;    m_pMatrix[col * m_iCapacity + row] = val;    return true;}

图的顶点类

类中只包含了构造函数用于设置顶点的信息,可以自行扩展,例如加入权值

// Node.h#ifndef NODE_H__#define NODE_H__class Node{public:    Node(char data = 0);    char m_cData;               bool m_bIsVisited;      //用于标记是否遍历过};Node::Node(char data){    m_cData = data;    m_bIsVisited = false;}#endif  //NODE_H__

图的边类

对于边来说我们一边保存边的两端的顶点,和边的权值

// Edge.h#ifndef EDGE_H__#define EDGE_H__class Edge{public:    Edge(int nodeIndexA = 0, int nodeIndexB = 0, int weightValue = 0);    int m_iNodeIndexA;              // 顶点A的索引    int m_iNodeIndexB;              // 顶点B的索引    int m_iWeightValue;             // 边的权值    bool m_bSelected;               // 用于标记是否遍历过};Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue){    m_iNodeIndexA = nodeIndexA;    m_iNodeIndexB = nodeIndexB;    m_iWeightValue = weightValue;    m_bSelected = false;}#endif  //EDGE_H__

图的Graph类

#include <vector>#include "Node.h"#include "Edge.h"using namespace std;class Graph{public:    Graph(int capacity);    ~Graph();    bool addNode(Node* pNode);      // 添加顶点    void resetNode();                           // 重置顶点(遍历一次会导致m_bIsVisited=true,需要重置将其设为false)    bool setDirectGraph(int row, int col, int val = 1);     // 设置有向图    bool setUndiretGraph(int row, int col, int val = 1);        // 设置无向图    void depthTraverse(int nodeIndex);          // 深度优先搜索    void breadthTraverse(int nodeIndex);        // 广度优先搜索    void printMatrix();         //打印邻接矩阵    //void primTree(int nodeIndex);     //普利姆生成树    //void kruskalTree();                           //克鲁斯卡尔算法生成树private:    bool getValueFromMatrix(int row, int col, int& val);    void breadthTraverseImpl(vector<int> preVec);private:    int m_iCapacity;                // 图的最大容量    int m_iNodeCount;           // 顶点的个数    int* m_pMatrix;                 // 邻接矩阵的指针    Edge* m_pEdge;              // 边的指针    Node* m_pNodeArray;     // 数组中顶点的指针};#endif  //GRAPH_H__
构造函数和析构函数
Graph::Graph(int capacity){    m_iCapacity = capacity;    m_iNodeCount = 0;    m_pMatrix = new int[m_iCapacity * m_iCapacity];     // 建立一个m_iCapacity*m_iCapacity大小的数组    m_pNodeArray = new Node[m_iCapacity];    memset(m_pMatrix, 0, m_iCapacity * m_iCapacity * sizeof(int));    m_pEdge = new Edge[m_iCapacity - 1];}Graph::~Graph(){    delete[] m_pMatrix;    delete[] m_pNodeArray;    delete[] m_pEdge;}
深度优先搜索

深度优先搜索有点类似二叉搜索树中的前序遍历

如图,利用深度优先搜素遍历:A-B-C-D-E-F-G-H
深度优先搜索

代码利用递归实现

void Graph::depthTraverse(int nodeIndex){    // 每次递归输出该节点信息    int value = 0;    cout << m_pNodeArray[nodeIndex].m_cData << " ";    // 将节点设置成true    m_pNodeArray[nodeIndex].m_bIsVisited = true;    //     for (int i = 0; i < m_iCapacity; i++) {        getValueFromMatrix(nodeIndex, i, value);        if (value == 1) {            if (m_pNodeArray[i].m_bIsVisited == true) {                continue;            }            else {                depthTraverse(i);            }        }        else {            continue;        }    }}
广度优先搜索

从一个顶点开始,搜索所有可以到达顶点的方法叫做广度优先搜索。

如图,利用广度优先搜索遍历:A-B-F-C-E-F-G-H-D
深度优先搜索

代码实现

void Graph::breadthTraverse(int nodeIndex){    cout << m_pNodeArray[nodeIndex].m_cData << " ";    m_pNodeArray[nodeIndex].m_bIsVisited = true;    vector<int> curVec;    curVec.push_back(nodeIndex);    breadthTraverseImpl(curVec);}bool Graph::getValueFromMatrix(int row, int col, int& val){    if (row < 0 || row >= m_iCapacity || col < 0 || col >= m_iCapacity) {        return false;    }    val = m_pMatrix[row * m_iCapacity + col];    return true;}void Graph::breadthTraverseImpl(vector<int> preVec){    int value = 0;    vector<int> curVec;    for (unsigned j = 0; j < preVec.size(); j++) {        for (int k = 0; k < m_iCapacity; k++) {            getValueFromMatrix(preVec[j], k, value);            if (value != 0) {                if (m_pNodeArray[k].m_bIsVisited == true) {                    continue;                }                else {                    cout << m_pNodeArray[k].m_cData << " ";                    m_pNodeArray[k].m_bIsVisited = true;                    curVec.push_back(k);                }            }        }    }    if (curVec.size() == 0) {        return;    }    else {        breadthTraverseImpl(curVec);    }}
其他代码
bool Graph::addNode(Node* pNode){    if (pNode == NULL) {        return false;    }    m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;    m_iNodeCount++;    return false;}void Graph::resetNode(){    for (int i = 0; i < m_iCapacity; i++) {        m_pNodeArray[i].m_bIsVisited = false;    }}void Graph::printMatrix(){    for (int i = 0; i < m_iCapacity; i++) {        for (int j = 0; j < m_iCapacity; j++) {            cout << m_pMatrix[i*m_iCapacity + j] << " ";        }        cout << endl;    }}bool Graph::getValueFromMatrix(int row, int col, int& val){    if (row < 0 || row >= m_iCapacity || col < 0 || col >= m_iCapacity) {        return false;    }    val = m_pMatrix[row * m_iCapacity + col];    return true;}
0 0
原创粉丝点击