Java&C++题解与拓展——leetcode661.图片平滑器【vector学习与使用】

每日一题做题记录,参考官方和三叶的题解

题目要求

在这里插入图片描述

思路一:模拟

直接根据题目模拟。

Java

class Solution {
    public int[][] imageSmoother(int[][] img) {
        int m = img.length, n = img[0].length;
        int[][] res = new int[m][n];
        int[][] dirs = new int[][]{{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,0}, {0,1}, {1,-1}, {1,0}, {1,1}};
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                int sum = 0, cnt = 0;
                for(int[] d : dirs) {
                    int nx = i + d[0], ny = j + d[1];
                    if(nx < 0 || nx >= m || ny < 0 || ny >= n)
                        continue;
                    sum += img[nx][ny];
                    cnt++;
                }
                res[i][j] = sum / cnt;
            }
        }
        return res;
    }
}
  • 时间复杂度: O ( m ∗ n ∗ C ) O(m*n*C) O(mnC) m m m为边数, n n n为节点数, C C C为灰度单位所包含的单元格数量,本题固定为9
  • 空间复杂度: O ( m ∗ n ) O(m*n) O(mn)

C++

class Solution {
public:
    vector<vector<int>> imageSmoother(vector<vector<int>>& img) {
        int m = img.size(), n = img[0].size();
        vector<vector<int>> res(m, vector<int>(n));
        int dirs[9][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,0}, {0,1}, {1,-1}, {1,0}, {1,1}};
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                int sum = 0, cnt = 0;
                for(int k = 0; k < 9; k++) {
                    int nx = i + dirs[k][0], ny = j + dirs[k][1];
                    if(nx < 0 || nx >= m || ny < 0 || ny >= n)
                        continue;
                    sum += img[nx][ny];
                    cnt++;
                }
                res[i][j] = sum / cnt;
            }
        }
        return res;
    }
};
  • 时间复杂度: O ( m ∗ n ∗ C ) O(m*n*C) O(mnC)
  • 空间复杂度: O ( m ∗ n ) O(m*n) O(mn)

vector

  • 学习参考链接
  • 简介
    • 一个封装了动态大小数组的顺序容器。
    • 严格线性顺序排序;
    • 动态数组快速访问操作;
    • 使用内存分配器对象动态处理其存储需求。
  • 代码中的res:大小为 m m m的容器,每个元素又都是一个大小为 n n n的容器。

一个没什么必要的空间优化思路

用时间换空间,不定义dirs数组,而用两个for循环替代:

for (int x = i - 1; x <= i + 1; x++) {
	for (int y = j - 1; y <= j + 1; y++) {
		......
	}
}

此时时间复杂度为 O ( m ∗ n ∗ C 2 ) O(m*n*C^2) O(mnC2)

思路二:前缀和

每次遍历周围的八个很明显会有大量重复,所以采用前缀和,提前计算每个元素 i m g ( i , j ) img(i,j) img(i,j)左上矩阵之和,即从 [ 0 ] [ 0 ] [0][0] [0][0] [ i ] [ j ] [i][j] [i][j]所有值相加,即图示部分。
在这里插入图片描述

计算时则用右下元素的前缀减去多余值即可,即 p r e f i x ( r , d ) − p r e f i x ( r , u ) − p r e f i x ( l , d ) + p r e f i x ( l , u ) prefix(r,d)-prefix(r,u)-prefix(l,d)+prefix(l,u) prefix(r,d)prefix(r,u)prefix(l,d)+prefix(l,u)
在这里插入图片描述

Java

class Solution {
    public int[][] imageSmoother(int[][] img) {
        int m = img.length, n = img[0].length;
        int[][] prefix = new int[m + 10][n + 10];
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
                prefix[i][j] = prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1] + img[i - 1][j - 1];
        int[][] res = new int[m][n];
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                //防止越界
                int left = Math.max(0, i - 1), up = Math.max(0, j - 1);
                int right = Math.min(m - 1, i + 1), down = Math.min(n - 1, j + 1);
                int cnt = (right - left + 1) * (down - up + 1);
                //右下角-左多余-上多余+左上角
                int sum = prefix[right + 1][down + 1] - prefix[left][down + 1] - prefix[right + 1][up] + prefix[left][up];
                res[i][j] = sum / cnt;
            }
        }
        return res;
    }
}
  • 时间复杂度: O ( m ∗ n ) O(m * n) O(mn)
  • 空间复杂度: O ( m ∗ n ) O(m * n) O(mn)

C++

class Solution {
public:
    vector<vector<int>> imageSmoother(vector<vector<int>>& img) {
        int m = img.size(), n = img[0].size();
        vector<vector<int>> prefix(m + 10, vector<int>(n + 10));
        for(int i = 1; i <= m; i++)
            for(int j = 1; j <= n; j++)
                prefix[i][j] = prefix[i - 1][j] + prefix[i][j - 1] - prefix[i - 1][j - 1] + img[i - 1][j - 1];
        vector<vector<int>> res(m, vector<int>(n));
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                //防止越界
                int left = max(0, i - 1), up = max(0, j - 1);
                int right = min(m - 1, i + 1), down = min(n - 1, j + 1);
                int cnt = (right - left + 1) * (down - up + 1);
                //右下角-左多余-上多余+左上角
                int sum = prefix[right + 1][down + 1] - prefix[left][down + 1] - prefix[right + 1][up] + prefix[left][up];
                res[i][j] = sum / cnt;
            }
        }
        return res;
    }
};
  • 时间复杂度: O ( m ∗ n ) O(m * n) O(mn)
  • 空间复杂度: O ( m ∗ n ) O(m * n) O(mn)

总结

本题实现很简单,对于法二,能够想到要用前缀和,但具体思路还是要经过一番推敲。
同时也要注意C++容器的使用。
如果了解深度学习的话,这道题其实是个平均池化,其padding 为 1,stride 为 1,核为 3×3。


欢迎指正与讨论!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值