这是我面试某无人驾驶企业时遇到的一个面试题
直入正题
面试题如下:
这里有个二维的地图,其中横轴纵轴上连续相连的"1"表示一个栅栏,请写一个计算图中栅栏个数的算法。
解决这道算法之前,我们先聊一下什么是递归函数?
编程语言中,函数Func(Type a,……)直接或间接调用函数本身,则该函数称为递归函数。递归函数不能定义为内联函数。
在数学上,关于递归函数的定义如下:对于某一函数f(x),其定义域是集合A,那么若对于A集合中的某一个值X0,其函数值f(x0)由f(f(x0))决定,那么就称f(x)为递归函数。
这里引入一个迷宫问题
如何从A点走到B点?
我们首先可以把地图转化为一个二维数组,把墙壁设为"1",空地设为"0",路径用"2"来表示,那么连续的"2"则为一条通路
地图如下:
1 1 1 1 1 1 1
1 0 0 0 0 0 1
1 0 1 0 0 0 1
1 0 1 0 0 0 1
1 1 1 0 0 0 1
1 0 0 0 0 0 1
1 0 0 0 0 0 1
1 1 1 1 1 1 1
通路如下:
1 1 1 1 1 1 1
1 2 2 2 0 0 1
1 0 1 2 0 0 1
1 0 1 2 0 0 1
1 1 1 2 0 0 1
1 0 0 2 0 0 1
1 0 0 2 2 2 1
1 1 1 1 1 1 1
简化后问题就清晰了,走迷宫的每一步走法都只有上下左右四种,我们按照这种走法走,如果走错就原路返回换一种走法,运用递归思想,很容易就能找到从起点A到终点B的通路,我们来看一下如何实现的:
/**
* 这里我们设置 (1,1)为起点A, (6,5)为终点B
* @author floyd
*
*/
public class Maze {
public static void main(String[] args) {
// create 2d map
int[][] map = new int[8][7];
// "1" 表示墙
for (int i = 0; i < 7; i++) {
map[0][i] = 1;
map[7][i] = 1;
}
for (int i = 0; i < 8; i++) {
map[i][0] = 1;
map[i][6] = 1;
}
// 设置挡板墙
map[4][1] = 1;
map[4][2] = 1;
map[2][2] = 1;
map[3][2] = 1;
// print map
System.out.println("地图的情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
// 迷宫走法
setWay(map, 1,1);
System.out.println("通路的情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 7; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
}
public static boolean setWay(int[][] map, int i, int j) {
if(map[6][5] == 2) { // 通路已经找到ok
return true;
} else {
if(map[i][j] == 0) { //如果当前这个点还没有走过
//按照策略 下->右->上->左 走
map[i][j] = 2; // 假定该点是可以走通.
if(setWay(map, i+1, j)) {//向下走
return true;
} else if (setWay(map, i, j+1)) { //向右走
return true;
} else if (setWay(map, i-1, j)) { //向上
return true;
} else if (setWay(map, i, j-1)){ // 向左走
return true;
} else {
//说明该点是走不通,是死路
map[i][j] = 3;
return false;
}
} else { // 如果map[i][j] != 0 , 可能是 1, 2, 3
return false;
}
}
}
}
回到算法题,我们先自己画个图,理解下题目
地图的情况
0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 0 1 1 0 0
0 0 0 1 1 0 0 0
0 1 1 0 0 0 0 0
0 1 0 0 1 1 0 0
0 1 1 0 0 0 1 0
0 0 0 0 0 0 0 0
如上图所示,横轴纵轴上连续相连的"1"表示一个栅栏,所以图中应为5个栅栏,我们要怎么用算法求解出栅栏个数呢?
运用上边提到的迷宫问题的思想,我们转变一下思维,我们(0, 0)开始从左至右,上至下的规则遍历整个二维数组;每当发现"1"的时候就停下,然后对当前遍历到的"1"除外的下方、右方、左方相连的各个"1"做一个标记为"2",如下图所示:
栅栏标注情况
0 0 1 0 0 0 0 0
0 0 2 0 0 0 0 0
0 0 0 0 1 2 0 0
0 0 0 2 2 0 0 0
0 1 2 0 0 0 0 0
0 2 0 0 1 2 0 0
0 2 2 0 0 0 1 0
0 0 0 0 0 0 0 0
由上图所示,"1"的个数已为栅栏个数。
下面我们来实现一下这个算法:
public class Palisade {
public static void main(String[] args) {
// create 2d map
int[][] map = new int[8][8];
// 1 表示栅栏
// 设置栅栏
map[0][2] = 1;
map[1][2] = 1;
map[2][4] = 1;
map[2][5] = 1;
map[3][4] = 1;
map[3][3] = 1;
map[4][1] = 1;
map[4][2] = 1;
map[5][1] = 1;
map[5][4] = 1;
map[5][5] = 1;
map[6][1] = 1;
map[6][2] = 1;
map[6][6] = 1;
// print map
System.out.println("地图的情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
int num = getPalisadeNum(map);
// print new map
System.out.println("栅栏标注情况");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(map[i][j] + " ");
}
System.out.println();
}
System.out.println("栅栏个数为:" + num);
}
// 统计栅栏个数
public static int getPalisadeNum(int[][] map) {
for (int i = 0; i < map[0].length; i++) {
for (int j = 0; j < map.length; j++) {
if (map[i][j] == 1) mark(map, i, j);
}
}
return sumPalisade(map);
}
public static void mark(int[][] map, int i, int j) {
// 入口坐标记录
int x = i, y = j;
markPalisade(map, i, j, x, y);
}
// 标注栅栏
public static void markPalisade(int[][] map, int i, int j, int x, int y) {
// 向右标注
if (j < map[0].length && map[i][j + 1] == 1) {
map[i][j + 1] = 2;
markPalisade(map, i, j + 1, x, y);
}
// 向下标注
if (i < map.length && map[i + 1][j] == 1) {
map[i + 1][j] = 2;
markPalisade(map, i + 1, j, x, y);
}
// 向左标注
if (j > 0 && map[i][j - 1] == 1) {
// 这里要注意不能把入口坐标也标记为"2"
map[i][j - 1] = (x == i && y == j - 1) ? 1 : 2;
markPalisade(map, i, j - 1, x, y);
}
}
// 栅栏计数
public static int sumPalisade(int[][] map) {
int sum = 0;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (map[i][j] == 1)
sum++;
}
}
return sum;
}
}
/* 输出如下:
地图的情况
0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 0 1 1 0 0
0 0 0 1 1 0 0 0
0 1 1 0 0 0 0 0
0 1 0 0 1 1 0 0
0 1 1 0 0 0 1 0
0 0 0 0 0 0 0 0
栅栏标注情况
0 0 1 0 0 0 0 0
0 0 2 0 0 0 0 0
0 0 0 0 1 2 0 0
0 0 0 2 2 0 0 0
0 1 2 0 0 0 0 0
0 2 0 0 1 2 0 0
0 2 2 0 0 0 1 0
0 0 0 0 0 0 0 0
栅栏个数为:5
*/
递归真好用~
本文写自一个睡不着觉的夜晚~