1. 程式人生 > >程式設計師面試金典: 9.4樹與圖 4.2給定有向圖,設計一個演算法,找出兩個節點之間是否存在一條路徑。

程式設計師面試金典: 9.4樹與圖 4.2給定有向圖,設計一個演算法,找出兩個節點之間是否存在一條路徑。

#include <iostream>
#include <stdio.h>
#include <vector>
#include <queue>

using namespace std;

/*
問題:給定有向圖,設計一個演算法,找出兩個節點之間是否存在一條路徑。
分析:有向圖,無非是沒有頂點的樹,需要考慮頂點和邊。
      圖的兩種建立方式:鄰接矩陣,鄰接表。
	  找出兩個節點之間是否存在一條路徑。Dijstra:兩點之間最短路徑,Flord是任意兩點最短路徑。
	  還有連通圖的最短路徑。
	  圖的遍歷有:深度優先和廣度優先。


輸入:
5(結點個數,元素下標從1開始) 5(有向邊個數)
1(起始結點編號) 5(結束結點編號)
1 2(有向邊,從結點1指向節點2)
1 3
2 3
2 4
3 5

5 4
1 5
1 2 
1 3
2 3 
2 4 


輸出:
exist path
not exist path

未能解出

關鍵:
1 迪傑斯特拉演算法是求指定點到其他節點的單源最短路徑(但必須是無向圖),所以此題不能用dijkstra演算法
2 只需要從兩個節點中任意一個節點出發,繼續遍歷,訪問過的節點標記為已經訪問,即可。
  之所以沒想到是因為:考慮圖的遍歷的時候想到如果是非連通圖,認為就無法繼續向下遍歷了。就排除了圖的遍歷。
  實際上:圖遍歷過程中如果到一次遍歷結束後,包含了另外一個節點,說明兩者連通;否則,不連通。
  之所以考慮到dijkstra演算法是因為想到求指定點到其他節點單元最短路徑,但是沒考慮到迪傑斯特拉演算法需要無向圖,
  否則,有向圖連線中存在無法連通情況,單源最短路徑不一定能求出。

3 記住遍歷就兩種:廣度優先(佇列+結構體) ,深度優先(遞迴)
*/
const int MAXSIZE = 1000;




typedef struct Node
{
	Node(int value):_value(value){}
	int _value;
};


//圖的遍歷
bool isTwoNodeInPath(int beginNode , int endNode , vector<Node> vecNode[MAXSIZE + 1])
{
	int g_visit[MAXSIZE];
	memset(g_visit , 0 , sizeof(int) * MAXSIZE);
	//設定所有節點為
	Node node(beginNode);
	g_visit[beginNode] = 1;
	queue<Node> queueNode;
	queueNode.push(node);
	while(!queueNode.empty())
	{
		//彈出當前節點
		Node curNode = queueNode.front();
		queueNode.pop();
		int value = curNode._value;
		int size = vecNode[value].size();
		//遍歷所有連線的節點並壓入到佇列中
		for(int i = 0 ; i < size; i++)
		{
			Node tempNode = vecNode[value].at(i);
			//壓入之前,如果該結點已經被訪問,則不壓入
			if(g_visit[tempNode._value] == 1)
			{
				continue;
			}
			g_visit[tempNode._value] = 1;
			queueNode.push(tempNode);
		}
	}

	//佇列為空,遍歷結束,只需要檢查另一個節點如果已經訪問過,說明連通
	if( g_visit[endNode] == 1 )
	{
		return true;
	}
	else
	{
		return false;
	}

}



int main(int argc, char* argv[])
{
	int nodeNum ;
	int edgeNum;
	int beginNode;
	int endNode;
	int frontNode;
	int backNode;
	while(cin >> nodeNum >> edgeNum)
	{
		cin >> beginNode >> endNode;
		vector<Node> vecNode[MAXSIZE + 1];

		//到這裡N個節點對應的鄰接表都建立好了,每個節點的鄰接表實際上就是一個向量,每個節點對應於林劫鏢中向量下標
		//有向圖的鄰接表無需複製雙份,存在鄰接關係,只需要將被指向節點放入節點向量中
		for(int i = 0 ; i < edgeNum ; i++)
		{
			cin >> frontNode >> backNode;
			
			if(frontNode < 0 || backNode < 0)
			{
				continue;
			}
			Node node(backNode);
			vecNode[frontNode].push_back(node);
		}

		bool isPath = isTwoNodeInPath(beginNode , endNode , vecNode);
		if(isPath)
		{
			cout << "exist path" << endl;
		}
		else
		{
			cout << "no exist path" << endl;
		}
	}
	getchar();
	return 0;
}