542. 01 Matrix(深搜/广搜/DP的题目)

Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell.

The distance between two adjacent cells is 1.

Example 1: 
Input:

0 0 0
0 1 0
0 0 0
Output:
0 0 0
0 1 0
0 0 0

Example 2: 
Input:

0 0 0
0 1 0
1 1 1
Output:
0 0 0
0 1 0
1 2 1

Note:

  1. The number of elements of the given matrix will not exceed 10,000.
  2. There are at least one 0 in the given matrix.
  3. The cells are adjacent in only four directions: up, down, left and right.
解答(摘自Solution):
Approach #1 Brute force [Time Limit Exceeded]

Intuition

Do what the question says.

Algorithm

  • Initialize dist[i][j]=INT_MAX for all {i,j} cells.
  • Iterate over the matrix.
  • If cell is 0dist[i][j]=0,
  • Else, for each 1 cell,
    • Iterate over the entire matrix
    • If the cell is 0, calculate its distance from current cell as abs(k-i)+abs(l-j).
    • If the distance is smaller than the current distance, update it.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (matrix[i][j] == 0)
                dist[i][j] = 0;
            else {
                for (int k = 0; k < rows; k++)
                    for (int l = 0; l < cols; l++)
                        if (matrix[k][l] == 0) {
                            int dist_01 = abs(k - i) + abs(l - j);
                            dist[i][j] = min(dist[i][j], abs(k - i) + abs(l - j));
                        }
            }
        }
    }
    return dist;
}

Complexity Analysis

  • Time complexity: O((r \cdot c)^2)O((rc)2). Iterating over the entire matrix for each 1 in the matrix.

  • Space complexity: O(r \cdot c)O(rc). No extra space required than the vector<vector<int> > dist


Approach #2 Using BFS [Accepted]

Intuition

A better brute force: Looking over the entire matrix appears wasteful and hence, we can use Breadth First Search(BFS) to limit the search to the nearest 0found for each 1. As soon as a 0 appears during the BFS, we know that the 0 is nearest, and hence, we move to the next 1.

Think again: But, in this approach, we will only be able to update the distance of one 1 using one BFS, which could in fact, result in slightly higher complexity than the Approach #1 brute force. But hey,this could be optimised if we start the BFS from 0s and thereby, updating the distances of all the 1s in the path.

Algorithm

  • For our BFS routine, we keep a queue, q to maintain the queue of cells to be examined next.
  • We start by adding all the cells with 0s to q.
  • Intially, distance for each 0 cell is 0 and distance for each 1 is INT_MAX, which is updated during the BFS.
  • Pop the cell from queue, and examine its neighbours. If the new calculated distance for neighbour {i,j} is smaller, we add {i,j} to q and update dist[i][j].

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX));
    queue<pair<int, int> > q;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            if (matrix[i][j] == 0) {
                dist[i][j] = 0;
                q.push({ i, j }); //Put all 0s in the queue.
            }

    int dir[4][2] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
    while (!q.empty()) {
        pair<int, int> curr = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int new_r = curr.first + dir[i][0], new_c = curr.second + dir[i][1];
            if (new_r >= 0 && new_c >= 0 && new_r < rows && new_c < cols) {
                if (dist[new_r][new_c] > dist[curr.first][curr.second] + 1) {
                    dist[new_r][new_c] = dist[curr.first][curr.second] + 1;
                    q.push({ new_r, new_c });
                }
            }
        }
    }
    return dist;
}

Complexity analysis

  • Time complexity: O(r \cdot c)O(rc).
  • Since, the new cells are added to the queue only if their current distance is greater than the calculated distance, cells are not likely to be added multiple times.

  • Space complexity: O(r \cdot c)O(rc). Additional O(r \cdot c)O(rc) for queue than in Approach #1


Approach #3 DP Approach [Accepted]

Intuition

The distance of a cell from 0 can be calculated if we know the nearest distance for all the neighbours, in which case the distance is minimum distance of any neightbour + 1. And, instantly, the word come to mind DP!!
For each 1, the minimum path to 0 can be in any direction. So, we need to check all the 4 direction. In one iteration from top to bottom, we can check left and top directions, and we need another iteration from bottom to top to check for right and bottom direction.

Algorithm

  • Iterate the matrix from top to bottom-left to right:
  • Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j-1],\text{dist}[i-1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j1],dist[i1][j])+1) i.e., minimum of the current dist and distance from top or left neighbour +1, that would have been already calculated previously in the current iteration.
  • Now, we need to do the back iteration in the similar manner: from bottom to top-right to left:
  • Update \text{dist}[i][j]=\min(\text{dist}[i][j],\min(\text{dist}[i][j+1],\text{dist}[i+1][j])+1)dist[i][j]=min(dist[i][j],min(dist[i][j+1],dist[i+1][j])+1) i.e. minimum of current dist and distances calculated from bottom and right neighbours, that would be already available in current iteration.

C++

vector<vector<int> > updateMatrix(vector<vector<int> >& matrix)
{
    int rows = matrix.size();
    if (rows == 0)
        return matrix;
    int cols = matrix[0].size();
    vector<vector<int> > dist(rows, vector<int>(cols, INT_MAX - 100000));

    //First pass: check for left and top
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (matrix[i][j] == 0)
                dist[i][j] = 0;
            else {
                if (i > 0)
                    dist[i][j] = min(dist[i][j], dist[i - 1][j] + 1);
                if (j > 0)
                    dist[i][j] = min(dist[i][j], dist[i][j - 1] + 1);
            }
        }
    }

    //Second pass: check for bottom and right
    for (int i = rows - 1; i >= 0; i--) {
        for (int j = cols - 1; j >= 0; j--) {
            if (i < rows - 1)
                dist[i][j] = min(dist[i][j], dist[i + 1][j] + 1);
            if (j < cols - 1)
                dist[i][j] = min(dist[i][j], dist[i][j + 1] + 1);
        }
    }

    return dist;
}

Complexity analysis

  • Time complexity: O(r \cdot c)O(rc). 2 passes of r \cdot crc each
  • Space complexity: O(r \cdot c)O(rc). No additional space required than dist vector<vector<int> >
注:
暴力法很好懂,DFS个人不感兴趣。。。DP的想法是:对于某一个位置,它到最近的0的距离=它的邻居中离这个0最近的那个+1(如果邻居有0那么直接赋0就好了)。注意第一趟要检查的是往上和往左,第二趟是下、右,不能混起来。。
其实我比较关心的是DFS怎么做(来自Discuss,Java版):
public class Solution {
    public int[][] updateMatrix(int[][] matrix) {
        if(matrix.length==0) return matrix;
        
        for(int i = 0; i<matrix.length; i++)
            for(int j = 0; j<matrix[0].length; j++)
                if(matrix[i][j]==1&&!hasNeiberZero(i, j,matrix)) 
                    matrix[i][j] = matrix.length+matrix[0].length+1;
        
        for(int i = 0; i<matrix.length; i++)
            for(int j = 0; j<matrix[0].length; j++)
                if(matrix[i][j]==1)
                    dfs(matrix, i, j, -1);
        
        return matrix;
    }
    private void dfs(int[][] matrix, int x, int y, int val){
        if(x<0||y<0||y>=matrix[0].length||x>=matrix.length||matrix[x][y]<=val)
            return;
        
        if(val>0) matrix[x][y] = val;
        
        dfs(matrix, x+1, y, matrix[x][y]+1);
        dfs(matrix, x-1, y, matrix[x][y]+1);
        dfs(matrix, x, y+1, matrix[x][y]+1);
        dfs(matrix, x, y-1, matrix[x][y]+1);
        
    }
    private boolean hasNeiberZero(int x, int y, int[][] matrix){
        if(x>0&&matrix[x-1][y]==0) return true;
        if(x<matrix.length-1&&matrix[x+1][y]==0) return true;
        if(y>0&&matrix[x][y-1]==0) return true;
        if(y<matrix[0].length-1&&matrix[x][y+1]==0) return true;
        
        return false;
    }
}
这个版本要清晰一点:
public class Solution {
    
    int currMin = 12000;    // The number of elements of the given matrix will not exceed 10,000, so we can use 12,000 (or anything >10,000) throughout the program as MAX value
    
    public int[][] updateMatrix(int[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) {
            return matrix;
        }
        
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                if (matrix[i][j] == 1) {
                    currMin = 12000;
                    int minDist = dfs(matrix, i, j, 0);
                    if (minDist < 12000)
                        matrix[i][j] = minDist;
                }
            }
        }
        
        return matrix;
    }
    
    
    private int dfs(int[][] matrix, int i, int j, int steps) {
        if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length ) {
            return 12000;
        }
        if (matrix[i][j] != 1 || steps > currMin) {
            return matrix[i][j];
        }
        matrix[i][j] ^= 12000;
        int down =    dfs(matrix, i+1, j, steps + 1);
        int up =  dfs(matrix, i-1, j, steps + 1);
        int left =  dfs(matrix, i, j-1, steps + 1);
        int right = dfs(matrix, i, j+1, steps + 1);
        matrix[i][j] ^= 12000;
        
        int minVal = Math.min(left, Math.min(right, Math.min(up, down))) + 1;
        currMin = minVal;
        
        return minVal;
    }
}
15 days ago reply quote 


另一个版本的DFS:
class Solution {
public:
    vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
        for(int row=0;row<matrix.size();row++){
            for(int col=0;col<matrix[0].size();col++){
                if(matrix[row][col]==1&&no_adjacent_zero(matrix,row,col))
                    matrix[row][col]=10000;
            }
        }
        for(int row=0;row<matrix.size();row++){
            for(int col=0;col<matrix[0].size();col++){
                dfs(matrix,row,col);
            }
        }
        return matrix;
    }
    bool no_adjacent_zero(vector<vector<int>>& matrix,int row,int col){
        int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
        for(int k=0;k<4;k++){
            int new_row=row+next[k][0],new_col=col+next[k][1];
            if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
                continue;
            if(matrix[new_row][new_col]==0)
                return false;
        }
        return true;
    }
    void dfs(vector<vector<int>>& matrix,int row,int col){
        int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
        for(int k=0;k<4;k++){
            int new_row=row+next[k][0],new_col=col+next[k][1];
            if(new_row<0||new_row>=matrix.size()||new_col<0||new_col>=matrix[0].size())
                continue;
            if(matrix[new_row][new_col]>matrix[row][col]+1){
                matrix[new_row][new_col]=matrix[row][col]+1;
                dfs(matrix,new_row,new_col);
            }
        }
    }
};
所以这里有一个重点就是周围没有0的点要用一个比较大的值覆盖。上一个DFS的解法是只要有1就赋一个大的值。。




请优化代码,以下代码无法释放焦点 ,不能变换拖动目标 public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: // 手指压下屏幕 mode = MODE.DRAG; // 寻找被点击的图片 CustomBitmap clickedBitmap = null; for (CustomBitmap bitmap : _bitmaps) { float[] values = new float[9]; bitmap.matrix.getValues(values); float globalX = values[Matrix.MTRANS_X]; float globalY = values[Matrix.MTRANS_Y]; float width = values[Matrix.MSCALE_X] * bitmap.getBitmap().getWidth(); float height = values[Matrix.MSCALE_Y] * bitmap.getBitmap().getWidth(); Rect rect = new Rect((int) globalX, (int) globalY, (int) (globalX + width), (int) (globalY + height)); if (rect.contains((int) event.getX(), (int) event.getY())) { clickedBitmap = bitmap; break; } } // 切换操作对象 if (clickedBitmap != null) { _bitmaps.remove(clickedBitmap); _bitmaps.add(clickedBitmap); currentMatrix.set(clickedBitmap.matrix);// 记录ImageView当前的移动位置 clickedBitmap.matrix.set(currentMatrix); clickedBitmap.startPoint.set(event.getX(), event.getY()); _curCustomBitmap = clickedBitmap; } postInvalidate(); break; case MotionEvent.ACTION_POINTER_DOWN: // 当屏幕上还有触点(手指),再有一个手指压下屏幕 mode = MODE.ZOOM; _curCustomBitmap.oldRotation = rotation(event); _curCustomBitmap.startDis = distance(event); if (_curCustomBitmap.startDis > 10f) { _curCustomBitmap.midPoint = mid(event); currentMatrix.set(_curCustomBitmap.matrix);// 记录ImageView当前的缩放倍数 } break; case MotionEvent.ACTION_MOVE: // 手指在屏幕移动,该事件会不断地触发 if (mode == MODE.DRAG) { float dx = event.getX() - _curCustomBitmap.startPoint.x;// 得到在x轴的移动距离 float dy = event.getY() - _curCustomBitmap.startPoint.y;// 得到在y轴的移动距离 _curCustomBitmap.matrix.set(currentMatrix);// 在没有进行移动之前的位置基础上进行移动 _curCustomBitmap.matrix.postTranslate(dx, dy); } else if (mode == MODE.ZOOM) {// 缩放与旋转 float endDis = distance(event);// 结束距离 _curCustomBitmap.rotation = rotation(event) - _curCustomBitmap.oldRotation; if (endDis > 10f) { float scale = endDis / _curCustomBitmap.startDis;// 得到缩放倍数 _curCustomBitmap.matrix.set(currentMatrix); _curCustomBitmap.matrix.postScale(scale, scale, _curCustomBitmap.midPoint.x, _curCustomBitmap.midPoint.y); _curCustomBitmap.matrix.postRotate(_curCustomBitmap.rotation, _curCustomBitmap.midPoint.x, _curCustomBitmap.midPoint.y); } } break; case MotionEvent.ACTION_UP: // 手指离开屏 break; case MotionEvent.ACTION_POINTER_UP: // 有手指离开屏幕,但屏幕还有触点(手指) mode = MODE.NONE; break; } invalidate(); return true; }
最新发布
06-06
以下是对代码的优化建议: 1. 将对位图的操作封装成一个方法,以提高可读性和可维护性。 2. 在ACTION_DOWN事件中,可以使用MotionEvent的findPointerIndex()和getX()、getY()方法来获取点击位置,避免使用循环查找位图。 3. 在ACTION_MOVE事件中,可以使用Matrix的postTranslate()和postScale()方法来进行位图的移动和缩放,避免重复创建Matrix对象。 4. 在ACTION_POINTER_DOWN事件中,可以使用MotionEvent的getX()、getY()方法来获取缩放中心点的坐标,避免重复计算。 5. 在ACTION_POINTER_DOWN事件中,可以将记录位图旋转角度和缩放倍数的代码提取出来,避免与缩放和旋转的代码耦合。 6. 在ACTION_POINTER_UP事件中,可以将模式设置为MODE.NONE,避免在其他事件中误操作。 7. 在代码中添加注释,以提高可读性和可维护性。 下面是优化后的代码: ``` public boolean onTouchEvent(MotionEvent event) { switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: // 手指压下屏幕 mode = MODE.DRAG; // 查找被点击的图片 int index = event.getActionIndex(); float x = event.getX(index); float y = event.getY(index); CustomBitmap clickedBitmap = findClickedBitmap(x, y); if (clickedBitmap != null) { // 切换操作对象 _bitmaps.remove(clickedBitmap); _bitmaps.add(clickedBitmap); // 记录ImageView当前的移动位置 currentMatrix.set(clickedBitmap.matrix); clickedBitmap.matrix.set(currentMatrix); clickedBitmap.startPoint.set(x, y); _curCustomBitmap = clickedBitmap; } postInvalidate(); break; case MotionEvent.ACTION_POINTER_DOWN: // 当屏幕上还有触点(手指),再有一个手指压下屏幕 mode = MODE.ZOOM; // 记录位图的旋转角度和缩放倍数 _curCustomBitmap.oldRotation = rotation(event); _curCustomBitmap.startDis = distance(event); if (_curCustomBitmap.startDis > 10f) { // 获取缩放中心点的坐标 float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); _curCustomBitmap.midPoint.set((x1 + x2) / 2, (y1 + y2) / 2); // 记录ImageView当前的缩放倍数 currentMatrix.set(_curCustomBitmap.matrix); } break; case MotionEvent.ACTION_MOVE: // 手指在屏幕移动,该事件会不断地触发 if (mode == MODE.DRAG) { // 移动图片 float dx = event.getX() - _curCustomBitmap.startPoint.x; float dy = event.getY() - _curCustomBitmap.startPoint.y; _curCustomBitmap.matrix.set(currentMatrix); _curCustomBitmap.matrix.postTranslate(dx, dy); } else if (mode == MODE.ZOOM) { // 缩放和旋转图片 float endDis = distance(event); float rotation = rotation(event) - _curCustomBitmap.oldRotation; if (endDis > 10f) { float scale = endDis / _curCustomBitmap.startDis; _curCustomBitmap.matrix.set(currentMatrix); _curCustomBitmap.matrix.postScale(scale, scale, _curCustomBitmap.midPoint.x, _curCustomBitmap.midPoint.y); _curCustomBitmap.matrix.postRotate(rotation, _curCustomBitmap.midPoint.x, _curCustomBitmap.midPoint.y); } } break; case MotionEvent.ACTION_POINTER_UP: // 有手指离开屏幕,但屏幕还有触点(手指) mode = MODE.NONE; break; } invalidate(); return true; } // 查找被点击的图片 private CustomBitmap findClickedBitmap(float x, float y) { for (CustomBitmap bitmap : _bitmaps) { float[] values = new float[9]; bitmap.matrix.getValues(values); float globalX = values[Matrix.MTRANS_X]; float globalY = values[Matrix.MTRANS_Y]; float width = values[Matrix.MSCALE_X] * bitmap.getBitmap().getWidth(); float height = values[Matrix.MSCALE_Y] * bitmap.getBitmap().getHeight(); RectF rect = new RectF(globalX, globalY, globalX + width, globalY + height); if (rect.contains(x, y)) { return bitmap; } } return null; } // 计算两点之间的距离 private float distance(MotionEvent event) { float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); } // 计算两点之间的旋转角度 private float rotation(MotionEvent event) { float x1 = event.getX(0); float y1 = event.getY(0); float x2 = event.getX(1); float y2 = event.getY(1); double radians = Math.atan2(y2 - y1, x2 - x1); return (float) Math.toDegrees(radians); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值