前言
之前软考的时候光看概念一直没搞懂(有点笨),导致考试的时候丢分了,因此特地去了解了并用JAVA代码实现.下面我将介绍使用栈和递归来实现深度优先遍历的方法.
一、什么是深度优先遍历?
深度优先遍历就是对每一个可能的分支路径深入到不能再深入为止,不能深入了就向上回溯,然后从其他分支再次开始深入到不能深入为止的原则,最后当所有路径都被访问过了,也就代表着遍历结束了(每个节点只能访问一次)
步骤:
1、从某个顶点(可以随机选取)开始,此时指针指向的是顶点
2、访问自己的一个未被访问过的邻接端点,将指针指向该邻接端点,如果没有未被访问过的邻接的端点则向上回溯至自己的直接父端点,当回溯至开始的顶点且顶点此时没有未访问的邻接端点时此时遍历结束.
3、重复步骤2
二、实现原理
1、利用递归思想,直至所有端点都被访问
2、利用栈先进后出原理存放当前遍历对象所有父端点,用于当前对象无未访问过的邻接端点时回溯
三、实现代码
代码如下(示例):
/**
* 深度优先遍历
*/
public class Test {
/**
* 端点数组
*/
private static final String[] ENDPOINT;
/**
* 边二维数组
*/
private static final Integer[][] SIDE_ARRAY;
/**
* 判断俩顶点是否存在边
*/
private static final Integer SIDE_FLAG = 1;
/**
* 未被访问的顶点map
*/
private static final Map<Integer, String> NOT_VISITED_ENDPOINT_MAP;
/**
* map table最大数
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
static {
ENDPOINT = new String[]{"A", "B", "C", "D", "E", "F", "G", "H"};
NOT_VISITED_ENDPOINT_MAP = new HashMap<>(calculateMapSize(ENDPOINT.length));
for (int i = 0; i < ENDPOINT.length; i++) {
NOT_VISITED_ENDPOINT_MAP.put(i, ENDPOINT[i]);
}
SIDE_ARRAY = new Integer[ENDPOINT.length][ENDPOINT.length];
for (int i = 0; i < ENDPOINT.length; i++) {
for (int j = 0; j < ENDPOINT.length; j++) {
SIDE_ARRAY[i][j] = 0;
}
}
// A-B有边
SIDE_ARRAY[0][1] = 1;
SIDE_ARRAY[1][0] = 1;
// A-D有边
SIDE_ARRAY[0][3] = 1;
SIDE_ARRAY[3][0] = 1;
// A-F有边
SIDE_ARRAY[0][5] = 1;
SIDE_ARRAY[5][0] = 1;
// B-E有边
SIDE_ARRAY[1][4] = 1;
SIDE_ARRAY[4][1] = 1;
// B-C有边
SIDE_ARRAY[1][2] = 1;
SIDE_ARRAY[2][1] = 1;
// C-D有边
SIDE_ARRAY[2][3] = 1;
SIDE_ARRAY[3][2] = 1;
// F-H有边
SIDE_ARRAY[5][7] = 1;
SIDE_ARRAY[7][5] = 1;
// F-G有边
SIDE_ARRAY[5][6] = 1;
SIDE_ARRAY[6][5] = 1;
}
public static void main(String[] args) {
//随机选择起点、终点
Random random = new Random();
int startEndPoint = random.nextInt(ENDPOINT.length - 1);
int endEndPoint = random.nextInt(ENDPOINT.length - 1);
String vertex = ENDPOINT[startEndPoint];
String target = ENDPOINT[endEndPoint];
System.out.println("顶点:" + vertex + ",目标顶点:" + target);
System.out.println("开始深度优先遍历搜索目标!");
// 栈用来回溯
Stack<Integer> endPointStack = new Stack<>();
NOT_VISITED_ENDPOINT_MAP.remove(startEndPoint);
search(startEndPoint, target, NOT_VISITED_ENDPOINT_MAP, endPointStack);
System.out.println("开始深度优先遍历搜索!");
for (int i = 0; i < ENDPOINT.length; i++) {
NOT_VISITED_ENDPOINT_MAP.put(i, ENDPOINT[i]);
}
endPointStack = new Stack<>();
NOT_VISITED_ENDPOINT_MAP.remove(startEndPoint);
search(startEndPoint, null, NOT_VISITED_ENDPOINT_MAP, endPointStack);
}
/**
* 递归方法
*/
private static void search(int index, String target, Map<Integer, String> notVisitedEndPointMap, Stack<Integer> endPointStack) {
int i = index;
Iterator<Integer> it = notVisitedEndPointMap.keySet().iterator();
while (it.hasNext()) {
int j = it.next();
int endPointSide = SIDE_ARRAY[i][j];
if (endPointSide == SIDE_FLAG) {
if (ENDPOINT[j].equals(target)) {
System.out.println("找到目标端点:" + ENDPOINT[j]);
return;
}
System.out.println("端点:" + ENDPOINT[j]);
it.remove();
endPointStack.push(i);
search(j, target, notVisitedEndPointMap, endPointStack);
return;
}
}
if (endPointStack.isEmpty()) {
return;
}
// 回溯父节点
int parentIndex = endPointStack.pop();
System.out.println("回溯端点:" + ENDPOINT[parentIndex]);
search(parentIndex, target, notVisitedEndPointMap, endPointStack);
}
/**
* 计算map最大不用扩容的siz大小
*
* @param cap
* @return
*/
private static int calculateMapSize(int cap) {
int maxTableSize = tableSizeFor(cap);
float size = (float) maxTableSize * 0.75f;
if (size < cap) {
maxTableSize = maxTableSize << 1;
}
if (maxTableSize > MAXIMUM_CAPACITY) {
maxTableSize = MAXIMUM_CAPACITY;
}
System.out.println("map容量为:" + maxTableSize);
return maxTableSize;
}
/**
* 获取大于等于cap的最小2次方数
*
* @param cap
* @return
*/
private static int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
}
总结
方法总比困难多,要相信自己.