图的深度和广度优先遍历算法
图的遍历算法设计需要考虑三个问题:
(1)图的特点是没有首尾之分,所以算法的参数要指定访问的第一个结点;
(2)对图的遍历路径有可能构成一个回路,从而造成死循环,所以算法设计要考虑遍历路径可能出现的死循环问题;
(3)一个结点可能和若干个结点都是邻接结点,要使一个结点的所有邻接结点按照某种次序被访问。
1 图的深度优先遍历算法
连通图的深度优先遍历递归算法为:
(1)访问结点v并标记结点v为已访问;
(2)查找结点v的第一个邻接结点w;
(3)若结点v的邻接结点w存在,则继续执行,否则算法结束;
(4)若结点w尚未被访问则深度优先搜索递归访问结点w;
(5)查找结点v的w邻接结点的下一个邻接结点w,转到步骤(3)。
2 图的广度优先遍历算法
连通图的广度优先遍历算法为:
(1)访问初始结点v并标记结点v为已访问;
(2)结点v入队列;
(3)当队列非空时则继续执行,否则算法结束;
(4)出队列取得队头结点u;
(5)查找结点u的第一个邻接结点w;
(6)若结点u的邻接结点w不存在,则转到步骤(3),否则循环执行,
(6.1)若结点w尚未被访问,则访问结点w,并标记结点w为已访问;
(6.2)结点w入队列;
(6.3)查找结点u的w邻接结点后的下一个邻接结点w,转到步骤(6)。
3 非连通图的遍历
对于连通图,从图的任意一个结点开始深度或广度优先遍历,一定可以访问图中的所有结点。但对于非连通图,从图的任意一个结点开始深度或广度优先遍历,并不能访问图中的所有结点。对于非连通图,从图的任意一个结点开始深度或广度优先遍历只能访问和初始结点连通的所有结点。
8.4.2 图的深度和广度优先遍历成员函数设计
1 访问结点
2 深度和广度优先遍历成员函数设计
3 测试程序
package cn.ls.graph;
/**
*
*邻接矩阵图类
*/
public class AdjMWGraph {
static final int maxWeight = 10000;
private SeqList vertices; // 存储结点的顺序表
private int[][] edge; // 存储边的二维数组
private int numOfEdges; // 边的个数
public AdjMWGraph(int maxV) { // 构造函数,maxV为结点个数
vertices = new SeqList(maxV);
edge = new int[maxV][maxV];
for (int i = 0; i < maxV; i++) {
for (int j = 0; j < maxV; j++) {
if (i == j)
edge[i][j] = 0;
else
edge[i][j] = maxWeight;
}
}
numOfEdges = 0;
}
public int getNumOfVertices() { // 返回结点个数
return vertices.size;
}
public int getNumOfEdges() { // 返回边的个数
return numOfEdges;
}
public Object getValue(int v) throws Exception {
// 返回结点v的数据元素
return vertices.getData(v);
}
public int getWeight(int v1, int v2) throws Exception {
// 返回边<v1,v2>的权值
if (v1 < 0 || v1 >= vertices.size || v2 < 0 || v2 >= vertices.size)
throw new Exception("参数v1或v2越界出错!");
return edge[v1][v2];
}
public void insertVertex(Object vertex) throws Exception {
// 插入结点
vertices.insert(vertices.size, vertex);
}
public void insertEdge(int v1, int v2, int weight) throws Exception {
// 插入边<v1,v2>,权值为weight
if (v1 < 0 || v1 >= vertices.size || v2 < 0 || v2 >= vertices.size)
throw new Exception("参数v1或v2越界出错!");
edge[v1][v2] = weight; // 置边的权值
numOfEdges++; // 边的个数加1
}
public void deleteEdge(int v1, int v2) throws Exception {
// 删除边<v1,v2>
if (v1 < 0 || v1 > vertices.size || v2 < 0 || v2 > vertices.size)
throw new Exception("参数v1或v2越界出错!");
if (edge[v1][v2] == maxWeight || v1 == v2)
throw new Exception("该边不存在!");
edge[v1][v2] = maxWeight; // 置边的权值为无穷大
numOfEdges--; // 边的个数减1
}
public int getFirstNeighbor(int v) throws Exception {
// 取结点v的第一个邻接结点。若存在返回该结点的下标序号,否则返回-1
if (v < 0 || v >= vertices.size)
throw new Exception("参数v越界出错!");
for (int col = 0; col < vertices.size; col++)
if (edge[v][col] > 0 && edge[v][col] < maxWeight)
return col;
return -1;
}
public int getNextNeighbor(int v1, int v2) throws Exception {
// 取结点v1的邻接结点v2后的邻接结点
// 若存在返回该结点的下标序号,否则返回-1
if (v1 < 0 || v1 >= vertices.size || v2 < 0 || v2 >= vertices.size)
throw new Exception("参数v1或v2越界出错!");
for (int col = v2 + 1; col < vertices.size; col++)
if (edge[v1][col] > 0 && edge[v1][col] < maxWeight)
return col;
return -1;
}
/**
* 深度优先遍历
* @param v
* @param visited
* @param vs
* @throws Exception
*/
private void depthFirstSearch(int v, boolean[] visited, Visit vs) throws Exception {
// 连通图以v为初始结点序号、访问操作为vs的深度优先遍历
// 数组visited标记了相应结点是否已访问过,0表示未访问,1表示已访问
vs.print(getValue(v)); // 访问该结点
visited[v] = true; // 置已访问标记
int w = getFirstNeighbor(v); // 取第一个邻接结点
while (w != -1) { // 当邻接结点存在时循环
if (!visited[w]) // 如果没有访问过
depthFirstSearch(w, visited, vs); // 以w为初始结点递归遍历
w = getNextNeighbor(v, w); // 取下一个邻接结点
}
}
/**
* 广度优先遍历
* @param v
* @param visited
* @param vs
* @throws Exception
*/
private void broadFirstSearch(int v, boolean[] visited, Visit vs) throws Exception {
// 连通图以v为初始结点序号、访问操作为vs的广度优先遍历
// 数组visited标记了相应结点是否已访问过,0表示未访问,1表示已访问
int u, w;
SeqQueue queue = new SeqQueue(); // 创建顺序队列queue
vs.print(getValue(v)); // 访问结点v
visited[v] = true; // 置已访问标记
queue.append(new Integer(v)); // 结点v入队列
while (!queue.isEmpty()) { // 队列非空时循环
u = ((Integer) queue.delete()).intValue(); // 出队列
w = getFirstNeighbor(u); // 取结点u的第一个邻接结点
while (w != -1) { // 当邻接结点存在时循环
if (!visited[w]) { // 若该结点没有访问过
vs.print(getValue(w)); // 访问结点w
visited[w] = true; // 置已访问标记
queue.append(new Integer(w)); // 结点w入队列
}
// 取结点u的邻接结点w的下一个邻接结点
w = getNextNeighbor(u, w);
}
}
}
/**
* 非连通图的深度优先遍历
* @param vs
* @throws Exception
*/
public void depthFirstSearch(Visit vs) throws Exception {
boolean[] visited = new boolean[getNumOfVertices()];
for (int i = 0; i < getNumOfVertices(); i++)
visited[i] = false; // 置所有结点均未访问过
for (int i = 0; i < getNumOfVertices(); i++)
// 对每个结点循环
if (!visited[i]) // 如果该结点未访问
depthFirstSearch(i, visited, vs);// 以结点i为初始结点深度优先遍历
}
/**
* 非连通图的广度优先遍历
* @param vs
* @throws Exception
*/
public void broadFirstSearch(Visit vs) throws Exception {
boolean[] visited = new boolean[getNumOfVertices()];
for (int i = 0; i < getNumOfVertices(); i++)
visited[i] = false; // 置所有结点均未访问过
for (int i = 0; i < getNumOfVertices(); i++)
// 对每个结点循环
if (!visited[i]) // 如果该结点未访问过
broadFirstSearch(i, visited, vs);// 以结点i为初始结点广度优先遍历
}
}
package cn.ls.graph;
/**
*
*边信息行列权值类.
*/
public class RowColWeight{
int row; //行下标
int col; //列下标
int weight; //权值
public RowColWeight(int r, int c, int w){
row = r;
col = c;
weight = w;
}
/**
* 创建矩阵图
* @param g 矩阵图类
* @param v 结点集合
* @param n 结点的个数
* @param rc 边信息集合
* @param e 边的个数
* @throws Exception
*/
public static void createGraph(AdjMWGraph g, Object[] v, int n, RowColWeight[] rc, int e) throws Exception{
for(int i = 0; i < n; i ++)
g.insertVertex(v[i]);
for(int k = 0; k < e; k ++)
g.insertEdge(rc[k].row, rc[k].col, rc[k].weight);
}
}
package cn.ls.graph;
/**
*
*测试矩阵类.
*/
public class Exam8_1 {
public static void main(String[] args) {
int n = 5, e = 5;
AdjMWGraph g = new AdjMWGraph(n);
Character[] a = { new Character('A'), new Character('B'), new Character('C'), new Character('D'), new Character('E') };
RowColWeight[] rcw = { new RowColWeight(0, 1, 10), new RowColWeight(0, 4, 20), new RowColWeight(1, 3, 30), new RowColWeight(2, 1, 40), new RowColWeight(3, 2, 50) };
try {
RowColWeight.createGraph(g, a, n, rcw, e);
System.out.println("结点个数为:" + g.getNumOfVertices());
System.out.println("边的个数为:" + g.getNumOfEdges());
g.deleteEdge(0, 4);
System.out.println();
System.out.println("结点个数为:" + g.getNumOfVertices());
System.out.println("边的个数为:" + g.getNumOfEdges());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
package cn.ls.graph;
/**
*
*图的遍历测试类.
*/
public class Exam8_2 {
public static void createGraph(AdjMWGraph g, Object[] v, int n, RowColWeight[] rc, int e) throws Exception {
for (int i = 0; i < n; i++)
g.insertVertex(v[i]);
for (int k = 0; k < e; k++)
g.insertEdge(rc[k].row, rc[k].col, rc[k].weight);
}
public static void main(String[] args) {
final int maxVertices = 100;
Visit vs = new Visit();
AdjMWGraph g = new AdjMWGraph(maxVertices);
Character[] a = { new Character('A'), new Character('B'), new Character('C'),
new Character('D'), new Character('E') };
RowColWeight[] rcw = { new RowColWeight(0, 1, 10), new RowColWeight(0, 4, 20),
new RowColWeight(1, 3, 30), new RowColWeight(2, 1, 40), new RowColWeight(3, 2, 50) };
int n = 5, e = 5;
try {
createGraph(g, a, n, rcw, e);
System.out.print("深度优先搜索序列为:");
g.depthFirstSearch(vs);
System.out.println();
System.out.print("广度优先搜索序列为:");
g.broadFirstSearch(vs);
System.out.println();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
package cn.ls.graph;
public interface List {
public void insert(int i, Object obj) throws Exception;
public Object delete(int i) throws Exception;
public Object getData(int i) throws Exception;
public int size();
public boolean isEmpty();
// public void next();
// public void prev();
// public void
}
package cn.ls.graph;
public class SeqList implements List {
final int defaultSize = 10;
int maxSize;
int size;
Object[] listArray;
SeqList() {
initiate(defaultSize);
}
SeqList(int size) {
initiate(size);
}
public void initiate(int sz) {
maxSize = sz;
size = 0;
listArray = new Object[sz];
}
public void insert(int i, Object obj) throws Exception {
if (size == maxSize) {
throw new Exception("顺序表已满无法插入!");
}
if (i > size) {
throw new Exception("参数错误!");
}
for (int j = size; j > i; j--) {
listArray[j] = listArray[j - 1];
}
listArray[i] = obj;
size++;
}
public Object delete(int i) throws Exception {
if (size == 0) {
throw new Exception("顺序表已空无法删除!");
}
if (i > size - 1) {
throw new Exception("参数错误!");
}
Object it = listArray[i];
for (int j = i; j < size - 1; j++) {
listArray[j] = listArray[j + 1];
}
size--;
return it;
}
public Object getData(int i) throws Exception {
if (i < 0 || i >= size) {
throw new Exception("参数错误!");
}
return listArray[i];
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public int MoreDataDelete(SeqList L, Object x) throws Exception {
int i, j;
int tag = 0;
for (i = 0; i < L.size; i++) {
if (x.equals(L.getData(i))) {
L.delete(i);
i--;
tag = 1;
}
}
return tag;
}
}
package cn.ls.graph;
public class SeqQueue {
static final int defaultSize = 100;
int front;
int rear;
int count;
int maxSize;
Object[] data;
SeqQueue() {
initiate(defaultSize);
}
SeqQueue(int sz) {
initiate(defaultSize);
}
private void initiate(int sz) {
maxSize = sz;
front = rear = 0;
count = 0;
data = new Object[sz];
}
public void append(Object obj) throws Exception {
if (count > 0 && front == rear) {
throw new Exception("队列已满!");
}
data[rear] = obj;
rear = (rear + 1) % maxSize;
count++;
}
public Object delete() throws Exception {
if (count == 0) {
throw new Exception("队列已空!");
}
Object temp = data[front];
front = (front + 1) % maxSize;
count--;
return temp;
}
public Object getFront() throws Exception {
if (count == 0) {
throw new Exception("队列已空!");
}
return data[front];
}
public boolean isEmpty() {
return count == 0;
}
}
package cn.ls.graph;
public class Visit{
public void print(Object item){
System.out.print(item + " ");
}
}
结果为:
深度优先搜索序列为:A B D C E
广度优先搜索序列为:A B E D C