[LeetCode] 419. Battleships in a Board 解题报告

Given an 2D board, count how many battleships are in it. The battleships are represented with 'X's, empty slots are represented with'.'s. You may assume the following rules:

  • You receive a valid board, made of only battleships or empty slots.
  • Battleships can only be placed horizontally or vertically. In other words, they can only be made of the shape 1xN (1 row, N columns) orNx1 (N rows, 1 column), where N can be of any size.
  • At least one horizontal or vertical cell separates between two battleships - there are no adjacent battleships.

Example:

X..X
...X
...X
In the above board there are 2 battleships.

Invalid Example:

...X
XXXX
...X
This is an invalid board that you will not receive - as battleships will always have a cell separating between them.

Follow up:

Could you do it in one-pass, using only O(1) extra memory and without modifying the value of the board?


这个题和前面某一个类似,求那个独立岛屿的数量。我看了以后,感觉和上一题417的思路特别像。(我看了网友的解答以后,发现自己的方法蠢酷了,又臭又长,不感兴趣的朋友,请直接跳到方法二)

方法一:

BFS搜索,和上面一题一样的,从左上角开始进行BFS搜索。使用循环和queue,boolean[][],搜索没有ship的格子“.”,然后把这个格子放到queue中,对应位置标记true,取出这个格子,继续循环,直到queue为空。

但是,一旦发现有ship的格子“X”,总和++,然后跳转到另一个函数,对这个格子四个方向的所有“X”格子进行搜索,这时候同样有另一个queue和循环,保证将这一个ship的所有格子都搜索到,并对应位置标记为true。(因为如果不一次性把当前ship对应的所有x找到,那么在后面的搜索中,可能会搜索到这个x,那就不知道这个x对应的ship到底有没有被搜索到。)

最后,返回总和即可。

由于BFS的时候,对于一个格子,搜索的是四个方向的,因此复杂度大约是O(4N),这个方法效率比较差,只打败了1.03%,代码的重用性也很差(我自己想的),不推荐。代码如下:

public class Solution {
	char[][] board;
	int nHeight;
	int nWidth;
	boolean[][] bExpl;
	int nTotal;
	Queue<int[]> queCommon;

	public int countBattleships(char[][] board) {
	    if (board.length == 0||board==null || board[0].length==0) {
			return 0;
		}
		this.board = board;
		nHeight = board.length;
		nWidth = board[0].length;
		bExpl = new boolean[nHeight][nWidth];
		queCommon = new LinkedList<int[]>();

        // init
		bExpl[0][0] = true;
		if (board[0][0] == '.') {
			queCommon.add(new int[] { 0, 0 });
		} else {
			nTotal++;
			findShip(new int[] { 0, 0 });
		}

        //explore the common point
		while (queCommon.size() > 0) {
			int[] nArrCur = queCommon.poll();
			// judge the current grid x or .
			explore(nArrCur, queCommon);
		}

		return nTotal;

	}

	private boolean isBeyond(int x, int y) {
		return x >= 0 && y >= 0 && x < nHeight && y < nWidth ? true : false;
	}

	private void explore(int[] n, Queue<int[]> q) {
		int x = n[0];
		int y = n[1];
		if (isBeyond(x, y - 1)) {
			tagNewPoint(x, y - 1, q);
		}
		if (isBeyond(x, y + 1)) {
			tagNewPoint(x, y + 1, q);
		}
		if (isBeyond(x - 1, y)) {
			tagNewPoint(x - 1, y, q);
		}
		if (isBeyond(x + 1, y)) {
			tagNewPoint(x + 1, y, q);
		}
	}

	private void exploreShip(int[] n, Queue<int[]> q) {
		int x = n[0];
		int y = n[1];
		if (isBeyond(x, y - 1)) {
			tagNewShip(x, y - 1, q);
		}
		if (isBeyond(x, y + 1)) {
			tagNewShip(x, y + 1, q);
		}
		if (isBeyond(x - 1, y)) {
			tagNewShip(x - 1, y, q);
		}
		if (isBeyond(x + 1, y)) {
			tagNewShip(x + 1, y, q);
		}
	}

	private void findShip(int[] n) {
		Queue<int[]> queShip = new LinkedList<int[]>();
		queShip.add(n);
		while (queShip.size() > 0) {
			int[] nArrCur = queShip.poll();
			// find all nearby ship
			exploreShip(nArrCur, queShip);
		}
	}

	private void tagNewPoint(int x, int y, Queue<int[]> q) {
		if (bExpl[x][y] == false) {
			bExpl[x][y] = true;
			int[] nn = { x, y };
			if (board[x][y] == '.') {
				q.add(nn);
			} else {
				nTotal++;
				//when meets the ship, and find the whole ship once
				findShip(nn);
			}
		}
	}

	private void tagNewShip(int x, int y, Queue<int[]> q) {
		if (bExpl[x][y] == false) {
			bExpl[x][y] = true;
			int[] nn = { x, y };
			if (board[x][y] == 'X') {
				q.add(nn);
			} else {
				queCommon.add(nn);
			}
		}
	}
}

方法二:

这个方法代码量也少,原理也简单,网友还是机制。

它利用了这个题目的特性,根据题意,任意两个ships都是隔开的;也就是说,不可能存在一个ship,四面相邻另一个ship;再换句话说,由于所有的ship不是横着就是竖着的,所有任意一个ship的左上角第一个格子的上面和左边必然相邻的不是ship,我们只用数一数这样的格子有几个,就知道有几个ship了。

另外,这个方法需要注意的是:在当前格子位于边缘的时候,左边和上面的格子index值为-1,如果这时候再直接取值的话,会出现数组超界的错误。

这个方法大概的复杂度应该是O(2N),代码如下:

public class Solution {
	public int countBattleships(char[][] board) {
		if (board == null) {
			return 0;
		}
		int nTotal = 0;
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[0].length; j++) {
				if (board[i][j] == 'X') {
					boolean bI = i == 0 ? true : false;
					boolean bJ = j == 0 ? true : false;
					if (!bI) {
						bI = board[i - 1][j] == '.' ? true : false;
					}
					if (!bJ) {
						bJ = board[i][j - 1] == '.' ? true : false;
					}
					if (bI && bJ) {
						nTotal++;
					}
				}
			}
		}
		return nTotal;
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值