數據結構學習(C++)——圖(無向圖上)
更新時間: 2007-05-25 11:24:49來源: 粵嵌教育瀏覽量:918
要是在紙上隨便畫畫,或者只是對圖做點示范性的說明,大多數人都會選擇無向圖。然而在計算機中,無向圖卻是按照有向圖的方法來儲存的——存兩條有向邊。實際上,當我們說到無向的時候,只是忽略方向——在紙上畫一條線,難不成那線“嗖”的就出現了,不是從一頭到另一頭畫出來的?
無向圖有幾個特有的概念,連通分量、關節點、小生成樹。下面將分別介紹,在此之前,先完成無向圖類的基本操作。
無向圖類
template
class Graph : public Network
{
public:
Graph() {}
Graph(dist maxdist) : Network (maxdist) {}
bool insertE(name v1, name v2, dist cost)
{
if (Network::insertE(v1, v2, cost))
return Network::insertE(v2, v1, cost);
return false;
}
};
僅僅是添加邊的時候,再添加一條反向邊,很簡單。
連通分量
這是無向圖特有的,有向圖可要復雜多了(強、單、弱連通),原因就是無向圖的邊怎么走都行,有向圖的邊好像掉下無底深淵就再也爬不上來了。有了DFS,求連通分量的算法就變得非常簡單了——對每個沒有訪問的頂點調用DFS就可以了。
void components()
{
visited = new bool[vNum()]; int i, j = 0;
for (i = 0; i < vNum(); i++) visited[i] = false;
cout << "Components:" << endl;
for (i = 0; i < vNum(); i++)
{
if (!visited[i]) { cout << '(' << ++j << ')'; DFS(i); cout << endl; }
}
delete []visited;
}
黑體的部分就是核心。
關節點
下定義是人們認識事物的一個方法,因為概念使得人們能夠區分事物——關于這個還有個的運動和相對的靜止的哲學觀點(河水總在流,但是長江還叫長江,記得那個的“不可能踏進同一條河里”嗎?)因此,能否有個準確的概念往往是一門學科發展程度的標志,而能否下一個準確的定義反映了一個人的思維能力。說這么多廢話,原因只有一個,我沒搞清楚什么叫“關節點”——參考書有限,不能仔細的考究了,如有誤解,還望指正。
嚴版是這么說的:如果刪除某個頂點,將圖的一個連通分量分割成兩個或兩個以上的連通分量,稱該頂點為關節點。——雖然沒有提到圖必須是無向的,但是提到了連通分量已經默認是無向圖了。
殷版是這么說的:在一個無向連通圖中,……(余下同嚴版)。
問題出來了,非連通圖是否可以討論含有關節點?我們是否可以說某個連通分量中含有關節點?遺憾的是,嚴版留下這個問題之后,在后面給出的算法是按照連通圖給的,這樣當圖非連通時結果就是錯的。殷版更是滑頭,只輸出重連通分量,不輸出關節點,自己雖然假定圖是連通的,同樣沒有連通判斷。
翻翻離散數學吧,結果沒找到什么“關節點”,只有“割點”,是這樣的:一個無向連通圖,如果刪除某個頂點后,變為非連通圖,該頂點稱為割點。權當“割點”就是“關節點”,那么算法至少也要先判斷是否連通吧?可是書上都直接當連通的了……
關于算法不再細說,書上都有。下面的示例,能輸出每個連通分量的“關節點”(是不是可以這樣叫,我也不清楚)。dfn儲存的是每個頂點的訪問序號,low是深度優先生成樹上每個非葉子頂點的子女通過回邊所能到達的頂點小的訪問序號。把指向雙親的邊也當成回邊并不影響判斷,因此不必特意區分,殷版顯式區分了,屬于畫蛇添足。這樣一來,if (low[n] >= dfn[i]) cout << getV(i);這個輸出關節點的判斷中的>=就顯得很尷尬了,因為只能等于不可能大于。還要注意的是,生成樹的根(DFS的起始點)是單獨判斷的。
void articul()
{
dfn = new int[vNum()]; low = new int[vNum()]; int i, j = 0, n;
for(i = 0; i < vNum(); i++) dfn[i] = low[i] = 0;//初始化
for (i = 0; i < vNum(); i++)
{
if (!dfn[i])
{
cout << '(' << ++j << ')'; dfn[i] = low[i] = count = 1;
if ((n = nextV(i)) != -1) articul(n); bool out = false;//訪問樹根
while ((n = nextV(i, n)) != -1)
{
if (dfn[n]) continue;
if (!out) { cout << getV(i); out = true; }//樹根有不只一個子女
articul(n);//訪問其他子女
}
cout << endl;
}
}
delete []dfn; delete []low;
}
private:
void articul(int i)
{
dfn[i] = low[i] = ++count;
for (int n = nextV(i); n != -1; n = nextV(i, n))
{
if (!dfn[n])
{
articul(n);
if (low[n] < low[i]) low[i] = low[n];
if (low[n] >= dfn[i]) cout << getV(i);//這里只可能=
}
else if (dfn[n] < low[i]) low[i] = dfn[n];//回邊判斷
}
}
int *dfn, *low, count;