运用递归思想解决某厂算法面试题

这是我面试某无人驾驶企业时遇到的一个面试题

直入正题
面试题如下:
  这里有个二维的地图,其中横轴纵轴上连续相连的"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
*/

递归真好用~
本文写自一个睡不着觉的夜晚~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值