20172303 2018-2019-1《程序設計與數據結構》深度優先遍歷
遍歷
- 圖的遍歷是指從圖中的某一頂點出發,按照必定的策略訪問圖中的每個頂點。固然,每一個頂點有且只能被訪問一次。
- 在圖的遍歷中,深度優先和廣度優先是最常使用的兩種遍歷方式。這兩種遍歷方式對無向圖和有向圖都是適用的,而且都是從指定的頂點開始遍歷的。
深度優先遍歷
簡單介紹
- 深度優先遍歷也叫深度優先搜索(Depth First Search)。
- 遍歷規則:不斷地沿着頂點的深度方向遍歷。頂點的深度方向是指它的鄰接點方向。具體點,給定一圖G=<V,E>,用visited[i]表示頂點i的訪問狀況,則初始狀況下全部的visited[i]都爲false。假設從頂點V0開始遍歷,則下一個遍歷的頂點是V0的第一個鄰接點Vi,接着遍歷Vi的第一個鄰接點Vj,直到全部的頂點都被訪問過。
- 所謂的第一個是指在某種存儲結構中(鄰接矩陣、鄰接表),全部鄰接點中存儲位置最近的,一般指的是下標最小的或元素最小的。
遍歷過程
- 在遍歷的過程當中有兩種狀況常常出現:
- 某個頂點的鄰接點都已被訪問過的狀況,此時需回溯已訪問過的頂點。
- 圖不連通,全部的已訪問過的頂點都已回溯完了,仍找不出未被訪問的頂點。此時需從下標0開始檢測visited[i],找到未被訪問的頂點i,從i開始新一輪的深度搜索。
- 舉個例子
- V0->V1->V3->V2->V4
- V1->V3->V0->V2->V4
- V2->V0->V1->V3->V4
- V3->V0->V1->V2->V4
- V4->V2->V0->V1->V3
代碼分析
- 深度優先遍歷——使用一個棧和一個無序列表來實現,棧用於管理遍歷,無序列表用於存儲遍歷結果
- 第一步:起始頂點進入棧。
- 第二步:從棧中取出起始頂點加入無序列表的末端,標記爲已訪問,讓與該頂點相連的頂點加入棧中。
- 第三步:重複第二步的操做,每次取出棧頂元素加入無序列表,把頂點標記爲已訪問,直至棧爲空。
- 鄰接矩陣實現的圖中的深度優先遍歷
public Iterator<T> iteratorDFS(int startIndex)
{
Integer x;
boolean found;
StackADT<Integer> traversalStack = new LinkedStack<Integer>();
UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();
boolean[] visited = new boolean[numVertices];// 標記判斷是否訪問,防止出現重複遍歷的狀況
if (!indexIsValid(startIndex)) {
throw new ElementNotFoundException("Graph");
// return resultList.iterator();
}
// numVertices用於記錄頂點的個數
for (int i = 0; i < numVertices; i++) {
visited[i] = false;
}
traversalStack.push(new Integer(startIndex));
resultList.addToRear(vertices[startIndex]);
visited[startIndex] = true;
// 開始循環,每次把棧頂元素添加到resultList中,將與首頂點鏈接的還未進入隊列的頂點加入棧
while (!traversalStack.isEmpty())
{
x = traversalStack.peek();
found = false;
//找到一個與x相鄰的沒有訪問過的頂點,並將其入棧
for (int i = 0; (i < numVertices) && !found; i++)
{
if (adjMatrix[x.intValue()][i] && !visited[i])
{
traversalStack.push(new Integer(i));
resultList.addToRear(vertices[i]);
visited[i] = true;
found = true;
}
}
if (!found && !traversalStack.isEmpty()) {
traversalStack.pop();
}
}
return new GraphIterator(resultList.iterator());
}
public Iterator iteratorBFS(int startIndex){
Integer x;
QueueADT<Integer> traversalQueue = new LinkedQueue<Integer>();
UnorderedListADT resultList = new ArrayUnorderedList();
// 若所給索引值無效,拋出錯誤
if (!indexIsValid(startIndex)){
throw new ElementNotFoundException("Graph");
}
// 設置每一個頂點是否被訪問,初始設置爲未訪問
boolean[] visited = new boolean[numVertices];
for (int i = 0;i < numVertices;i++){
visited[i] = false;
}
// 起始頂點進入隊列,並標記爲已訪問
traversalQueue.enqueue(startIndex);
visited[startIndex] = true;
// 開始循環,每次把隊列中的首頂點添加到resultList中,將與首頂點鏈接的還未進入隊列的頂點加入隊列,並標記爲已訪問
while (!traversalQueue.isEmpty()){
x = traversalQueue.dequeue();
resultList.addToRear(vertices.get(x).getElement());
for (int i = 0;i < numVertices;i++){
if (hasEdge(x,i) && !visited[i]){
traversalQueue.enqueue(i);
visited[i] = true;
a++;
}
}
}
return new GraphIterator(resultList.iterator());
}
// 判斷兩頂點之間是否有邊
public boolean hasEdge(int a,int b){
if (a == b){
return false;
}
VerticeNode vertex1 = vertices.get(a);
VerticeNode vertex2 = vertices.get(b);
while (vertex1 != null){
if (vertex1.getElement() == vertex2.getElement()){
return true;
}
vertex1 = vertex1.getNext();
}
return false;
}
一個例子
參考資料