深度優先、廣度優先java實現

一 圖的基本概念及存儲結構
圖G是由頂點的有窮集合,以及頂點之間的關係組成,頂點的集合記爲V,頂點之間的關係構成邊的集合E
G=(V,E).
說一條邊從v1,連接到v2,那麼有v1Ev2(E是V上的一個關係)《=》<v1,v2>∈E.
圖有有向圖,無向圖之分,無向圖的一條邊相當於有向圖的中兩條邊,即如果無向圖的頂點v1和v2之間有一條邊
,那麼既有從v1連接到v2的邊,也有從v2連接到v1的邊,<v1,v2>∈E 並且<v2,v1>∈E,而有向圖是嚴格區分方向的。

一般圖的存儲有兩種方式
1)相鄰矩陣,用一個矩陣來保持邊的情況,<v1,v2>∈E則Matrix[v1][v2]=Weight.
2)鄰接表,需要保存一個順序存儲的頂點表和每個頂點上的邊的鏈接表。
這裏的實現採用第二種方案,另外圖又複雜圖,簡單圖之分,複雜圖可能在兩點之間同一個方向有多條邊,我們考慮的都是無環簡單圖,無環簡單圖是指頂點沒有自己指向自己的邊的簡單圖,即任取vi屬於V => <vi,vi>不屬於E並且沒有重複邊。

我們先給出圖的ADT:

package algorithms.graph;


public interface Graph {

//mark

public static interface Edge{
public int
getWeight();
}

int
vertexesNum();

int
edgeNum();
boolean
isEdge(Edge edge);
void
setEdge(
int
from,
int
to,
int
weight);
Edge firstEdge(
int
vertex);
Edge nextEdge(Edge edge);
int
toVertex(Edge edge);
int
fromVertex(Edge edge);
String getVertexLabel(
int
vertex);
void
assignLabels(String[] labels);
void
deepFirstTravel(GraphVisitor visitor);
void
breathFirstTravel(GraphVisitor visitor);



}

其中的方法大多數比較一目瞭然,
其中
1)Edge firstEdge(int vertex)返回指定節點的邊的鏈表裏存的第一條邊
2)
Edge nextEdge(Edge edge),順着邊鏈表返回下一條邊
3)fromVertex,toVertex很簡單返回該邊的起始頂點和終結頂點
4)
getVertexLabel返回該定點對應的標號,
assignLabels給所有頂點標上號

GraphVisitor是一個很簡單的接口:

package algorithms.graph;


public interface GraphVisitor {

void
visit(Graph g,
int
vertex);

}

OK,下面是該部分實現:

package algorithms.graph;

import
java.util.Arrays;



public class DefaultGraph implements Graph {


private static class
_Edge
implements
Edge{


private static final
_Edge NullEdge
=new
_Edge();

int
from;
int
to;
int
weight;

_Edge nextEdge;

private
_Edge()
{
weight
=
Integer.MAX_VALUE;
}

_Edge(
int
from,
int
to,
int
weight)
{

this.from=
from;
this.to=
to;
this.weight=
weight;
}
public int
getWeight()
{
return
weight;
}


}

private static class
_EdgeStaticQueue
{
_Edge first;
_Edge last;
}

private int
numVertexes;
private
String[] labels;
private int
numEdges;


private
_EdgeStaticQueue[] edgeQueues;

//
tag the specified vertex be visited or not

private boolean[] visitTags;


public DefaultGraph(int numVertexes) {
if(numVertexes<1
)
{
throw new
IllegalArgumentException();
}
this.numVertexes=
numVertexes;
this.visitTags=new boolean
[numVertexes];
this.labels=new
String[numVertexes];
for(int
i
=0;i<numVertexes;i++
)
{
labels[i]
=i+""
;


}
this.edgeQueues=new
_EdgeStaticQueue[numVertexes];
for(int
i
=0;i<numVertexes;i++
)
{
edgeQueues[i]
=new
_EdgeStaticQueue();
edgeQueues[i].first
=edgeQueues[i].last=
_Edge.NullEdge;

}
this.numEdges=0
;
}




@Override
public int edgeNum() {

return
numEdges;
}


@Override
public Edge firstEdge(int vertex) {
if(vertex>=numVertexes) throw new
IllegalArgumentException();
return
edgeQueues[vertex].first;

}


@Override
public boolean isEdge(Edge edge) {

return
(edge
!=
_Edge.NullEdge);
}


@Override
public Edge nextEdge(Edge edge) {

return
((_Edge)edge).nextEdge;
}


@Override
public int vertexesNum() {

return
numVertexes;
}


@Override
public int
fromVertex(Edge edge) {

return
((_Edge)edge).from;
}

@Override
public void
setEdge(
int
from,
int
to,
int
weight) {
//
we don't allow ring exist

if(from<0||from>=numVertexes||to<0||to>=numVertexes||weight<0||from==to)throw new IllegalArgumentException();
_Edge edge
=new
_Edge(from,to,weight);
edge.nextEdge
=
_Edge.NullEdge;
if(edgeQueues[from].first==
_Edge.NullEdge)
edgeQueues[from].first
=
edge;
else

edgeQueues[from].last.nextEdge
=
edge;
edgeQueues[from].last
=
edge;

}

@Override
public int
toVertex(Edge edge) {

return
((_Edge)edge).to;
}

@Override
public
String getVertexLabel(
int
vertex) {
return
labels[vertex];
}

@Override
public void
assignLabels(String[] labels) {
System.arraycopy(labels,
0, this.labels, 0
, labels.length);

}

//
to be continue


}


二 深度優先周遊
即從從某一點開始能繼續往前就往前不能則回退到某一個還有邊沒訪問的頂點,沿這條邊看該邊指向的點是否已訪問,如果沒有訪問,那麼從該指向的點繼續操作。

那麼什麼時候結束呢,這裏我們在圖的ADT實現里加上一個標誌數組。該數組記錄某一頂點是否已訪問,如果找不到不到能繼續往前訪問的未訪問點,則結束。

你可能會問,如果指定圖不是連通圖(既有2個以上的連通分量)呢?
OK,解決這個問題,我們可以讓每一個頂點都有機會從它開始周遊。
下面看deepFirstTravel的實現:


@Override
public void deepFirstTravel(GraphVisitor visitor) {
Arrays.fill(visitTags,
false);//
reset all visit tags

for(int i=0;i<numVertexes;i++)
{
if(!
visitTags[i])do_DFS(i,visitor);
}

}


private final void
do_DFS(
int
v, GraphVisitor visitor) {
//
first visit this vertex

visitor.visit(this, v);
visitTags[v]
=true
;

//
for each edge from this vertex, we do one time
//
and this for loop is very classical in all graph algorithms

for(Edge e=firstEdge(v);isEdge(e);e=nextEdge(e))
{
if(!
visitTags[toVertex(e)])
{
do_DFS(toVertex(e),visitor);
}
}


}


三 廣度優先周遊
廣度優先周遊從每個指定頂點開始,自頂向下一層一層的訪問。上一層所有頂點訪問完了才繼續下一層的訪問。可以把二叉樹的廣度優先周遊看成圖的廣度優先周遊的特例。
(二叉樹是連通的無迴路的有向圖,也是一棵根樹)
同樣,廣度優先也要藉助與一個隊列來存儲待訪問頂點

下面是breathFirstTravel的實現,爲了減小Java庫的影響,我自己實現一個很簡單的隊列。(你也可以使用
ArrayList,但是記住隊列的定義,只能在頭刪除,在尾插入):

private static class _IntQueue
{
private static class
_IntQueueNode
{
_IntQueueNode next;
int
value;
}
_IntQueueNode first;
_IntQueueNode last;

//
queue can only insert to the tail

void add(int i)
{
_IntQueueNode node
=new
_IntQueueNode();
node.value
=
i;
node.next
=null
;
if(first==null)first=
node;
else
last.next
=
node;
last
=
node;
}


boolean
isEmpty()
{
return
first
==null
;
}
//
queue can only remove from the head

int remove()
{
int
val
=
first.value;
if(first==
last)
first
=last=null
;
else

first
=first.next;
return
val;
}

}

@Override
public void breathFirstTravel(GraphVisitor visitor) {
Arrays.fill(visitTags,
false);//
reset all visit tags



for(int i=0;i<numVertexes;i++)
{
if(!
visitTags[i])
{

do_BFS(i,visitor);
}
}

}

private void
do_BFS(
int
v, GraphVisitor visitor) {
//
and BFS will use an queue to keep the unvisited vertexes
//
we can also just use java.util.ArrayList

_IntQueue queue=new _IntQueue();
queue.add(v);
while(!
queue.isEmpty())
{
int
fromV
=
queue.remove();
visitor.visit(
this
, fromV);
visitTags[fromV]
=true
;
for
(Edge e
=firstEdge(fromV);isEdge(e);e=
nextEdge(e))
{
if(!
visitTags[toVertex(e)])
{
queue.add(toVertex(e));
}
}
}
}


OK,最後我們測試一下:


public static void main(String[] args) {


DefaultGraph g
=new DefaultGraph(9);
g.setEdge(
0, 1, 0
);
g.setEdge(
0, 3, 0
);
g.setEdge(
1, 2, 0
);
g.setEdge(
4, 1, 0
);
g.setEdge(
2, 5, 0
);

g.setEdge(
3, 6, 0
);
g.setEdge(
7, 4, 0
);
g.setEdge(
5, 8, 0
);
g.setEdge(
6, 7, 0
);
g.setEdge(
8, 7, 0
);

//
now we visit it

GraphVisitor visitor=new GraphVisitor()
{
@Override
public void
visit(Graph g,
int
vertex) {
System.out.print(g.getVertexLabel(vertex)
+" "
);

}

};
System.out.println(
"DFS==============:"
);
g.deepFirstTravel(visitor);
System.out.println();
System.out.println(
"BFS==============:"
);
g.breathFirstTravel(visitor);
System.out.println();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章