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

数据结构之图(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值默认为1

bool 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
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值