这道题还是蛮不错的,结合比较多的知识点,对我来说有几个新的点:
1, BFS的iterative方法。把所有的可以延伸的点都收集到一个queue里,从尾巴上加,头部poll,这让我想到了UBER实习第一面时,面试官说我自己实现了queue的结构,我现在想想确实在treelevel traversal 的题型中,也可以用queue来traversal,只要在每一层里,先知道queuesize,然后poll这么多次就正好把这一层的node给遍历完。对的!TODO TODO.
2. 这里非常聪明的把 x 和 y encode了,相当于matrix.width 进制,保证了一一对应,不出现重复情况。
3. floodfill的方法非常直观且容易理解。其实就是这道题要调用的一个方法,在matrix的四周进行实现。
4. 看主方法里的步骤,就可以对整体的思路有很直接的了解了:a,对四周进行floodfill;b,再对matrics进行全局扫瞄,对+和 o进行判断。
我自己的一些对这道题的描述:
// 还要说下整体思路: 对周围的4条边进行floodfill的操作,把所有的O都设置成#,一种区别与内陆湖和陆地这两种的颜色/符号,就相当于海洋的水。
// 这样的话 在完成了floodfill之后,就要把内陆湖给填满,然后把海水还是还原成水。所以这道题的本质是什么?就是题目中给出所有的水都只有以各种表示形式。题目的问题是,要把淡水,即内陆湖,和咸水,及海洋水给分离开。分离开之后,可以对内陆湖进行填充,然后海洋的话,还是还原成海水。
问题抽象: 通过位置特点把所有水分成两种,对其实一种加一个特殊标记即可。完成了区分后,把原有的标记进行ops这里是替换。最后把特殊标记后的点都复原回来。
题目的整体思路是这样,技术难点在于,怎么根据位置去 标记不同的水域,BFS就是这样解决的。同时要知道BFS有两种实现方式:递归和迭代/通过queue来维护。
用迭代的方式实现BFS的代码:
class Solution {
public void solve(char[][] board) {
if(board.length==0 || board[0].length==0) return;
int height=board.length;
int width=board[0].length;
for(int i=0; i<height; i++){
floodFill(board, i, 0);
floodFill(board, i, width-1);
}
for(int j=1; j<width-1; j++){
floodFill(board, 0, j);
floodFill(board, height-1, j);
}
for(int i=0; i<height; i++){
for(int j=0; j<width; j++){
if(board[i][j]=='O') board[i][j]='X';
else if(board[i][j]=='+') board[i][j]='O';
}
}
}
private void floodFill(char[][] board, int row, int col){
if(board[row][col]!='O') return;
// board[row][col]=='O';
board[row][col]='+';
LinkedList<Integer> pointCodeQueue = new LinkedList<>();
int pointCode = row*board[0].length+ col;
pointCodeQueue.add(pointCode);
while(!pointCodeQueue.isEmpty()){
int polledPointCode =pointCodeQueue.poll();
int pointRow= polledPointCode/board[0].length;
int pointCol= polledPointCode%board[0].length;
if(pointRow>0 && board[pointRow-1][pointCol]=='O'){
// >1 or >0 都可以,但是后者有一定重复。因为所有边界点都作为了起点。
// 等等,其实好像也不算重复,因为如果被改成*了,其实从这个起点开始的话就立马返回了。
board[pointRow-1][pointCol]='+';
pointCodeQueue.add(polledPointCode-board[0].length);
}
if(pointRow<board.length-1 && board[pointRow+1][pointCol]=='O'){
// pointRow<board.length-1 or -2 都可以,道理同上。
board[pointRow+1][pointCol]='+';
pointCodeQueue.add(polledPointCode+board[0].length);
}
if(pointCol>0 && board[pointRow][pointCol-1]=='O'){
board[pointRow][pointCol-1]='+';
pointCodeQueue.add(polledPointCode-1);
}
if(pointCol<board[0].length-1 && board[pointRow][pointCol+1]=='O'){
board[pointRow][pointCol+1]='+';
pointCodeQueue.add(polledPointCode+1);
}
}
}
}
用递归的方式实现BFS的代码:
// 用递归的方式实现flood fill 算法。
class Solution {
public void solve(char[][] board) {
if (board.length == 0 || board[0].length == 0)
return;
if (board.length < 2 || board[0].length < 2)
return;
int m = board.length, n = board[0].length;
//Any 'O' connected to a boundary can't be turned to 'X', so ...
//Start from first and last column, turn 'O' to '*'.
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O')
boundaryDFS(board, i, 0);
if (board[i][n-1] == 'O')
boundaryDFS(board, i, n-1);
}
//Start from first and last row, turn 'O' to '*'
for (int j = 0; j < n; j++) {
if (board[0][j] == 'O')
boundaryDFS(board, 0, j);
if (board[m-1][j] == 'O')
boundaryDFS(board, m-1, j);
}
//post-prcessing, turn 'O' to 'X', '*' back to 'O', keep 'X' intact.
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O')
board[i][j] = 'X';
else if (board[i][j] == '*')
board[i][j] = 'O';
}
}
}
//Use DFS algo to turn internal however boundary-connected 'O' to '*';
private void boundaryDFS(char[][] board, int i, int j) {
if (i < 0 || i > board.length - 1 || j <0 || j > board[0].length - 1)
return;
if (board[i][j] == 'O')
board[i][j] = '*';
if (i > 1 && board[i-1][j] == 'O')
boundaryDFS(board, i-1, j);
if (i < board.length - 2 && board[i+1][j] == 'O')
boundaryDFS(board, i+1, j);
if (j > 1 && board[i][j-1] == 'O')
boundaryDFS(board, i, j-1);
if (j < board[i].length - 2 && board[i][j+1] == 'O' )
boundaryDFS(board, i, j+1);
}
}
图片数据可以很大,所以递归的话很容易栈溢出 或者 超时。