C++圖論基本概念與存儲(chǔ)結(jié)構(gòu)
一、圖的基本概念
圖是由頂點(diǎn)集合及頂點(diǎn)間的關(guān)系組成的一種數(shù)據(jù)結(jié)構(gòu):G = (V, E);
- V是頂點(diǎn)集合
- E是頂點(diǎn)間關(guān)系的集合,也叫做邊的集合
- 圖中的結(jié)點(diǎn)稱為頂點(diǎn)。如果兩個(gè)頂點(diǎn) vi 和 vj 之間相關(guān)聯(lián),稱有一條邊記<vi, vj>
圖分為有向圖和無向圖:在有向圖中,<x, y>稱為頂點(diǎn)x到頂點(diǎn)y的一條邊,<x, y>和<y, x>是兩條不同的邊。而在無向圖中,<x, y>和<y, x>是同一條邊。

圖中邊的數(shù)量與頂點(diǎn)數(shù)量的關(guān)系,可以用稠密和稀疏形容。
完全圖:設(shè)一張圖有n個(gè)頂點(diǎn)。對(duì)于無向圖,若有n*(n-1)/2條邊,即任意兩點(diǎn)之間都有一條邊,則稱此圖為無向完全圖,也是最稠密的圖;對(duì)于有向圖,若有n*(n-1)條邊,即任意兩點(diǎn)之間都有兩條方向相反的邊,則稱此圖為有向完全圖。

頂點(diǎn)的度:頂點(diǎn)v的度是指與它相關(guān)聯(lián)的邊的條數(shù),記作deg(v)。在有向圖中,頂點(diǎn)v的度等于該頂點(diǎn)的入度與出度之和,入度是指以v為終點(diǎn)的有向邊的條數(shù),出度是指以v為起點(diǎn)的有向邊的條數(shù)。在無向圖中,頂點(diǎn)v的度等于出度等于入度。
路徑:在圖中,若從頂點(diǎn) vi 出發(fā)有一組邊可以使其到達(dá)頂點(diǎn) vj,則稱頂點(diǎn) vi 到 vj 的頂點(diǎn)序列為頂點(diǎn) vi 到 vj 的路徑。
權(quán)值:邊附帶的數(shù)據(jù)信息,比如:長度,價(jià)值,親密度

路徑長度:對(duì)于不帶權(quán)的圖,一條路徑的路徑長度是該路徑上的邊數(shù)量;對(duì)于帶權(quán)的圖,一條路徑的路徑長度是該路徑上的所有邊的權(quán)總和。
簡(jiǎn)單路徑與回路:若一條路徑上的各頂點(diǎn)都不重復(fù),則稱這樣的路徑為簡(jiǎn)單路徑。若路徑上的第一個(gè)頂點(diǎn)和最后一個(gè)頂點(diǎn)重合,則稱這樣的路徑為回路或環(huán)。

子圖:設(shè)圖G = {V, E}、G1 = {V1, E1},若V1屬于V且E1屬于E,則稱G1是子圖。即一個(gè)圖的子圖,所有的頂點(diǎn)和邊都在原圖中出現(xiàn)過。

連通圖:對(duì)于無向圖,若頂點(diǎn)v1到v2有路徑,則稱v1和v2是連通的。如果圖中任意兩個(gè)頂點(diǎn)都是連通的,則稱此圖為連通圖。
強(qiáng)連通圖:對(duì)于有向圖,如果任意兩頂點(diǎn)vi和vj之間,都存在一條vi到vj的路徑和vj到vi的路徑,則稱此圖為強(qiáng)連通圖。
生成樹:生成樹是連通圖(無向圖)的一個(gè)子圖,是一棵樹,包含原圖所有頂點(diǎn)且保持連通,有n個(gè)頂點(diǎn)的連通圖的生成樹有n個(gè)頂點(diǎn)和n-1條邊。

二、圖的存儲(chǔ)結(jié)構(gòu)
一個(gè)圖既有頂點(diǎn)也有邊,圖的存儲(chǔ)結(jié)構(gòu)中就需要保存它們。頂點(diǎn)保存比較簡(jiǎn)單,只需要一個(gè)數(shù)組即可,關(guān)系邊該怎么保存呢?
保存邊的方式,有領(lǐng)接矩陣和領(lǐng)接表兩種方式!
1. 領(lǐng)接矩陣
頂點(diǎn)與頂點(diǎn)之間是否連通,可以用0或1表示。領(lǐng)接矩陣就是一個(gè)二維數(shù)組,用矩陣來表示頂點(diǎn)之間的關(guān)系

每個(gè)結(jié)點(diǎn)可以用數(shù)組下標(biāo)代表。例如,頂點(diǎn)A的下標(biāo)是x,頂點(diǎn)B的下標(biāo)是y。領(lǐng)接矩陣中[x][y]代表從A到B的邊的權(quán)值,如果是無權(quán)圖就用01表示該邊是否存在即可,如果是有權(quán)圖則填入權(quán)值或默認(rèn)值(表示邊不存在,一般可以用INT_MAX代表)。
注意,對(duì)于無向圖,領(lǐng)接矩陣是左下右上對(duì)稱的,即[x][y]和[y][x]內(nèi)容一樣!有向圖則不是,[x][y]和[y][x]內(nèi)容不一定相同。
// 圖
namespace Matrix
{
// V頂點(diǎn)類型 W邊權(quán)值類型 MAX_W表示邊不存在的值 Direction表示圖是否有向
template<class V, class W, W MAX_W = INT_MAX, bool Direction = false>
class Graph
{
public:
Graph(const V* vertexs, size_t n)
{
_vertexs.reserve(n);
for (size_t i = 0; i < n; ++i)
{
_vertexs.push_back(vertexs[i]);
_vIndexMap[vertexs[i]] = i;
}
// MAX_W 作為不存在邊的標(biāo)識(shí)值
// 初始化時(shí)默認(rèn)沒有邊,邊需要一條一條手動(dòng)添加,用AddEdge函數(shù)
_matrix.resize(n);
for (auto& e : _matrix)
{
e.resize(n, MAX_W);
}
}
// 找到一個(gè)頂點(diǎn)的映射下標(biāo)
size_t GetVertexIndex(const V& v)
{
auto ret = _vIndexMap.find(v);
if (ret != _vIndexMap.end())
{
return ret->second;
}
else
{
throw invalid_argument("不存在的頂點(diǎn)");
return -1;
}
}
// 添加一條邊,src和dst代表兩端頂點(diǎn),w是權(quán)值
void AddEdge(const V& src, const V& dst, const W& w)
{
size_t srci = GetVertexIndex(src);
size_t dsti = GetVertexIndex(dst);
_matrix[srci][dsti] = w;
//如果是無向圖,則[dsti][srci]也需添加邊
if (Direction == false)
{
_matrix[dsti][srci] = w;
}
}
private:
map<V, size_t> _vIndexMap; // 頂點(diǎn)到下標(biāo)的映射
vector<V> _vertexs; // 頂點(diǎn)集合
vector<vector<W>> _matrix; // 領(lǐng)接矩陣 存儲(chǔ)邊
};
}領(lǐng)接矩陣適合存儲(chǔ)稠密圖,能O(1)判斷兩個(gè)頂點(diǎn)的關(guān)系,得到權(quán)值。但是如果要查找一個(gè)頂點(diǎn)連接的所有邊,效率是O(n)
2. 領(lǐng)接表
領(lǐng)接表是一個(gè)鏈表數(shù)組。數(shù)組表示頂點(diǎn)的集合,鏈表表示邊的關(guān)系。

領(lǐng)接表適合存儲(chǔ)稀疏圖,適合查找一個(gè)頂點(diǎn)連接出去的邊,但是相對(duì)不適合判斷兩個(gè)點(diǎn)是否有邊及其權(quán)值。
// 臨接表
namespace LinkTable
{
// 定義邊結(jié)構(gòu), W是權(quán)值類型
template<class W>
struct LinkEdge
{
int _srcIndex;
int _dstIndex;
W _w;
LinkEdge<W>* _next;
LinkEdge(const W& w)
: _srcIndex(-1)
, _dstIndex(-1)
, _w(w)
, _next(nullptr)
{ }
};
template<class V, class W, bool Direction = false>
class Graph
{
typedef LinkEdge<W> Edge;
public:
Graph(const V* vertexs, size_t n)
{
_vertexs.reserve(n);
for (size_t i = 0; i < n; ++i)
{
_vertexs.push_back(vertexs[i]);
_vIndexMap[vertexs[i]] = i;
}
_linkTable.resize(n, nullptr);
}
size_t GetVertexIndex(const V& v)
{
auto ret = _vIndexMap.find(v);
if (ret != _vIndexMap.end())
{
return ret->second;
}
else
{
throw invalid_argument("不存在的頂點(diǎn)");
return -1;
}
}
// 添加邊
void AddEdge(const V& src, const V& dst, const W& w)
{
size_t srcindex = GetVertexIndex(src);
size_t dstindex = GetVertexIndex(dst);
Edge* sd_edge = new Edge(w);
sd_edge->_srcIndex = srcindex;
sd_edge->_dstIndex = dstindex;
sd_edge->_next = _linkTable[srcindex];
_linkTable[srcindex] = sd_edge;
// 如果是無向圖,還要反過來添加一次
if (Direction == false)
{
Edge* ds_edge = new Edge(w);
ds_edge->_srcIndex = dstindex;
ds_edge->_dstIndex = srcindex;
ds_edge->_next = _linkTable[dstindex];
_linkTable[dstindex] = ds_edge;
}
}
private:
map<string, int> _vIndexMap; // 頂點(diǎn)到下標(biāo)的映射
vector<V> _vertexs; // 頂點(diǎn)集合
vector<Edge*> _linkTable; // 邊的集合的領(lǐng)接表
};
}以上就是C++圖論基本概念與存儲(chǔ)結(jié)構(gòu)的詳細(xì)內(nèi)容,更多關(guān)于C++圖的概念與存儲(chǔ)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++算法之在無序數(shù)組中選擇第k小個(gè)數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了C++算法之在無序數(shù)組中選擇第k小個(gè)數(shù)的實(shí)現(xiàn)方法,涉及C++數(shù)組的遍歷、判斷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-03-03
C/C++通過IP獲取局域網(wǎng)網(wǎng)卡MAC地址
這篇文章主要為大家詳細(xì)介紹了C++如何通過Win32API函數(shù)SendARP從IP地址獲取局域網(wǎng)內(nèi)網(wǎng)卡的MAC地址,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02
C++中utf8字符串和gbk字符串的轉(zhuǎn)換方法
文章介紹了C++中UTF-8字符串和GBK字符串之間的轉(zhuǎn)換,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-02-02
OpenCV利用霍夫變換實(shí)現(xiàn)交通車道線檢測(cè)
經(jīng)典霍夫變換用來檢測(cè)圖像中的直線,后來霍夫變換經(jīng)過擴(kuò)展可以進(jìn)行任意形狀物體的識(shí)別,例如圓和橢圓。本文就來利用霍夫變換實(shí)現(xiàn)交通車道線檢測(cè),需要的可以參考一下2022-09-09
C語言多功能動(dòng)態(tài)通訊錄實(shí)現(xiàn)示例
這篇文章主要為大家介紹了C語言多功能動(dòng)態(tài)通訊錄實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

