一、环境说明
- 本文是 leetcode 542题 :01 矩阵,使用c语言实现
- 方法一使用广度优先遍历实现。
- 方法二使用动态规划实现。
- 测试环境:Visual Studio 2019
二、题目说明:
三、代码展示
方法1 广度优先遍历
const int dx[4] = { 1,-1,0,0 };
const int dy[4] = { 0,0,1,-1 };
typedef struct queue {
int x;
int y;
}queue;
int** updateMatrix(int** mat, int matSize, int* matColSize, int* returnSize, int** returnColumnSizes) {
int m = matSize, n = matColSize[0];
*returnSize = m;
*returnColumnSizes = (int*)calloc(m, sizeof(int));
for (int i = 0; i < m; i++) {
returnColumnSizes[0][i] = n;
}
int** ans = (int**)calloc(m, sizeof(int*));
int front = 0, rear = 0;
queue* q = (queue*)calloc(m * n, sizeof(queue));
for (int i = 0; i < m; i++) {
ans[i] = (int*)calloc(n, sizeof(int));
for (int j = 0; j < n; j++) {
if (mat[i][j]) {
ans[i][j] = INT_MAX;
}
else {
q[rear].x = i;
q[rear++].y = j;
}
}
}
while (front != rear) {
int x = q[front].x;
int y = q[front++].y;
for (int i = 0; i < 4; i++) {
int new_x = x + dx[i], new_y = y + dy[i];
if (new_x >= 0 && new_x < m && new_y >= 0 && new_y < n) {
if (ans[new_x][new_y] > ans[x][y] + 1) {
ans[new_x][new_y] = ans[x][y] + 1;
q[rear].x = new_x;
q[rear++].y = new_y;
}
}
}
}
return ans;
}
方法2 动态规划
#define Min(a,b) a<b?a:b
int** updateMatrix(int** mat, int matSize, int* matColSize, int* returnSize, int** returnColumnSizes) {
int m = matSize, n = matColSize[0];
*returnSize = m;
*returnColumnSizes = (int*)calloc(m, sizeof(int));
for (int i = 0; i < m; i++) {
returnColumnSizes[0][i] = n;
}
int** ans = (int**)calloc(m, sizeof(int*));
for (int i = 0; i < m; i++) {
ans[i] = (int*)calloc(n, sizeof(int));
for (int j = 0; j < n; j++) {
if (mat[i][j]) {
ans[i][j] = INT_MAX-100000;
}
}
}
for(int i = 0; i<m;i++){
for(int j = 0;j<n;j++){
if(i > 0){
ans[i][j]=Min(ans[i][j],ans[i-1][j]+1);
}
if(j > 0){
ans[i][j]=Min(ans[i][j],ans[i][j-1]+1);
}
}
}
for(int i = m-1;i>=0;i--){
for(int j = n-1;j>=0;j--){
if(i<m-1){
ans[i][j]=Min(ans[i][j],ans[i+1][j]+1);
}
if(j<n-1){
ans[i][j]=Min(ans[i][j],ans[i][j+1]+1);
}
}
}
return ans;
}
四、思路分析
广度优先遍历思路分析
- 这道题类似图的遍历,树有从根结点出发的单源遍历,图则有从多个结点出发的多源遍历。其实二者本质上是一样的,只不过图的遍历于没有从第一个结点出发,而是直接从多个结点出发,图的多源遍历相当于树的遍历第二步。
- 用一个队列,记录待遍历结点的坐标。
- 创建ans矩阵,大小等于mat,将所有0到0的距离设为0,将所有1到0的距离设为INT_MAX(int的最大值)
- 将所有的0,看做一个超级0,这个超级0到每个0的距离为1。将所有0的坐标入队,等待遍历。
- 出队队首元素,对它遍历,遍历到的下一个坐标,如果它目前到0的最近距离<(队首元素到0的距离+1),则更新下一个坐标到0的最近距离为二者最小值,同时下一个坐标入队。
- 注意设置边界。
- 细节看代码注释
动态规划思路分析
- 从左上往右下遍历一躺,再从右下往左上遍历一趟,更新每个点到0的最小距离。
- 注意设置边界
- 看注释!
五、AC
广度优先遍历
动态规划
六、复杂度分析
广度优先遍历复杂度
- 时间复杂度:O(mn),m是行数,n是列数。O(mn)是遍历所有元素的时间开销
- 空间复杂度:O(mn)队列的开销。
动态规划复杂度
- 时间复杂度:O(mn),m是行数,n是列数。O(mn)是遍历所有元素的时间开销
- 空间复杂度:O(1)除若干变量使用的常量空间,没有使用额外的线性空间。ps:其实返回矩阵规模很大,空间复杂度是O(mn)。但是算法层面,可以不考虑答案大小对空间复杂度的影响。