【資料結構週週練】029 判斷無向圖是否為一棵樹演算法原理詳解及程式碼分享
一、題目
設計一個演算法,判斷一個圖G是否為一棵樹,如果是,返回TRUE,否則,返回FALSE。
二、美麗的星座
星座真的好美好美。特別是當人類給它們賦予含義的那一刻,更美,彷彿有了靈魂一般。
是不是很美,是不是?你以為我是讓你過來看星星的嗎?你以為我是希望你以後能夠好好學習天文學知識的嗎?當然不僅僅是這樣啦!細心的你應該知道星星多麼像我們學的圖啊!!!
三、分析
敲黑板!!!
看題了嗨,學習完了我們可以一起暢遊星空哈,現在回來我們的重心,看下面這兩個星座,為了方便觀看,研究其特性,我們放小一點,它們不僅僅是一個星座,同樣它們也是一個圖,同樣它們也是一棵樹,也就是說這兩個圖都是一棵樹,那它們有什麼特點呢?
第一個圖有 7顆星星,相當於有三個頂點,星星之間有 6條連線,相當於有 6條邊;
第二個圖有11顆星星,相當於有11個頂點,星星之間有10條連線,相當於有10條邊。
這是樹的特性,有n個頂點,就會有n-1條邊,且每個頂點都有邊相連。即一個圖是一棵樹的條件是:有n個頂點,就會有n-1條邊,且每個頂點都有邊相連。
所以我們的演算法就在於判斷當我們遍歷完圖之後,我們是否訪問完所有的結點,並且,訪問的結點的條數是結點個數-1。
當然,如果我們採用鄰接表儲存的圖,那每一條邊都會訪問兩次,也就是說,我們訪問邊的次數是邊數的兩倍。
第一步,遍歷圖之前的操作
我們遍歷圖,因為我們要做判斷,判斷結點是否被訪問,被訪問說明圖中有環,就不是樹,所以我們需要定義一個數組來儲存結點訪問情況,並設定所有值為false,當訪問結點後,該結點對應的陣列位置的元素改為true。
for (int i = 0; i < G.vexnum; i++) //將所有的訪問狀態設定為false,即未訪問。
visited[i] = false;
並且我們要判斷訪問的邊以及結點數量,如果結點訪問的數量同圖中結點數量一致,並且訪問過的邊的次數是頂點個數減一的兩倍,說明是一棵樹,所以我們還需要設定兩個變數,用來儲存訪問結點次數以及訪問邊的次數。
int VNum = 0;//訪問的頂點的數量
int ENum = 0;//訪問的邊的數量
第二步,遍歷圖
遍歷圖我們採用遞迴演算法,為了方便,我們單獨寫一個遍歷演算法DFS,即深度優先遍歷圖(Depth-First-Search)。遍歷過程中,每遍歷一次,將一個結點的訪問改為true,頂點數目+1。
visited[v] = true;//做訪問標記
VNum++; //頂點計數+1
然後獲取當前結點,即v結點的第一個鄰接點,如果存在,說明有一條邊存在,即需要邊的數量+1,然後訪問v結點的第一個鄰接點,如果該鄰接點未訪問,則繼續遞迴訪問,如果訪問過了,則訪問下一個鄰接點。
int w = FirstNeighbor(G, v);
while (w!=-1)
{
ENum++;
if (!visited[w])
DFS(G, v, VNum, ENum, visited);
w = NextNeighbor(G, v, w);
}
第三步,做判斷
如果滿足遍歷的定點數與圖的頂點數相同並且訪問的邊數和等於頂點數-1的兩倍,則說明是樹,否則不是樹。
if (VNum == G.vexnum&& ENum == 2 * (G.vexnum - 1))
return true;
else
return false;
四、全部程式碼
剛才上面是分析,為了便於大家理解,我將分析對應的程式碼也寫上去了,為了方便大家整體分析,接下來我將所有的程式碼分享一下。
bool GraphIsTree(Graph &G) {
for (int i = 0; i < G.vexnum; i++)
{
visited[i] = false;
}
int VNum = 0;
int ENum = 0;
if (VNum == G.vexnum&& ENum == 2 * (G.vexnum - 1))
return true;
else
return false;
}
void DFS(Graph &G, int v, int &VNum, int &ENum, int visited[]) {
visited[v] = true;
VNum++;
int w = FirstNeighbor(G, v);
while (w!=-1)
{
ENum++;
if (!visited[w])
DFS(G, v, VNum, ENum, visited);
w = NextNeighbor(G, v, w);
}
}
大家有什麼問題在下面留言哦!!!