当前位置: 首页 > news >正文

LeetCode 2711.对角线上不同值的数量差:O(mn)时间O(1)空间 - 位运算优化 - C++/Go双百版本 - 三种方法(一步步优化)

【LetMeFly】2711.对角线上不同值的数量差:O(mn)时间O(1)空间 - 位运算优化 - C++/Go双百版本 - 三种方法(一步步优化)

力扣题目链接:https://leetcode.cn/problems/difference-of-number-of-distinct-values-on-diagonals/

给你一个下标从 0 开始、大小为 m x n 的二维矩阵 grid ,请你求解大小同样为 m x n 的答案矩阵 answer

矩阵 answer 中每个单元格 (r, c) 的值可以按下述方式进行计算:

  • topLeft[r][c] 为矩阵 grid 中单元格 (r, c) 左上角对角线上 不同值 的数量。
  • bottomRight[r][c] 为矩阵 grid 中单元格 (r, c) 右下角对角线上 不同值 的数量。

然后 answer[r][c] = |topLeft[r][c] - bottomRight[r][c]|

返回矩阵 answer

矩阵对角线 是从最顶行或最左列的某个单元格开始,向右下方向走到矩阵末尾的对角线。

如果单元格 (r1, c1) 和单元格 (r, c) 属于同一条对角线且 r1 < r ,则单元格 (r1, c1) 属于单元格 (r, c) 的左上对角线。类似地,可以定义右下对角线。

 

示例 1:

输入:grid = [[1,2,3],[3,1,5],[3,2,1]]
输出:[[1,1,0],[1,0,1],[0,1,1]]
解释:第 1 个图表示最初的矩阵 grid 。 
第 2 个图表示对单元格 (0,0) 计算,其中蓝色单元格是位于右下对角线的单元格。
第 3 个图表示对单元格 (1,2) 计算,其中红色单元格是位于左上对角线的单元格。
第 4 个图表示对单元格 (1,1) 计算,其中蓝色单元格是位于右下对角线的单元格,红色单元格是位于左上对角线的单元格。
- 单元格 (0,0) 的右下对角线包含 [1,1] ,而左上对角线包含 [] 。对应答案是 |1 - 0| = 1 。
- 单元格 (1,2) 的右下对角线包含 [] ,而左上对角线包含 [2] 。对应答案是 |0 - 1| = 1 。
- 单元格 (1,1) 的右下对角线包含 [1] ,而左上对角线包含 [1] 。对应答案是 |1 - 1| = 0 。
其他单元格的对应答案也可以按照这样的流程进行计算。

示例 2:

输入:grid = [[1]]
输出:[[0]]
解释:- 单元格 (0,0) 的右下对角线包含 [] ,左上对角线包含 [] 。对应答案是 |0 - 0| = 0 。

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n, grid[i][j] <= 50

挺不错的一道题。

解题方法一:暴力模拟

使用 O ( m n ) O(mn) O(mn)的时间复杂度枚举每个起点,对于每个起点:

使用两个哈希表(集合),分别统计这个起点左上角和右下角有多少个不同的元素。

怎么统计呢?从 k = 1 k=1 k=1开始,到 i + d i + d i+d j + d j + d j+d超出合法边界为止,直接将 g r i d [ i + d ] [ j + d ] grid[i + d][j + d] grid[i+d][j+d]插入到哈希表中。

知道了每个起点左上角和右下角分别有多少个不同的元素,就知道答案了。

  • 时间复杂度 O ( m n × min ⁡ ( m , n ) ) O(mn\times \min(m, n)) O(mn×min(m,n))
  • 空间复杂度 O ( min ⁡ ( m , n ) ) O(\min(m, n)) O(min(m,n))

AC代码

C++
/*
 * @Author: LetMeFly
 * @Date: 2025-03-25 16:52:02
 * @LastEditors: LetMeFly.xyz
 * @LastEditTime: 2025-03-25 17:42:27
 */
class Solution {
public:
    vector<vector<int>> differenceOfDistinctValues(vector<vector<int>>& grid) {
        vector<vector<int>> ans(grid.size(), vector<int>(grid[0].size()));
        for (int i = 0; i < grid.size(); i++) {
            for (int j = 0; j < grid[i].size(); j++) {
                unordered_set<int> l, r;
                for (int k = 1; i - k >= 0 && j - k >= 0; k++) {
                    l.insert(grid[i - k][j - k]);
                }
                for (int k = 1; i + k < grid.size() && j + k < grid[0].size(); k++) {
                    r.insert(grid[i + k][j + k]);
                }
                ans[i][j] = abs(int(l.size() - r.size()));
            }
        }
        return ans;
    }
};

解题方法二:前缀和优化

方法一中我们需要从每个起点开始往两个方向遍历,但其实很多数被统计了很多次。

怎么优化呢?前缀和呗。从第一行或第一列的共 m + n − 1 m + n - 1 m+n1个元素开始向右下角遍历,遍历过程中使用哈希表统计当前一共遍历到了多少个元素,并直接将结果写入 a n s [ i ] [ j ] ans[i][j] ans[i][j]中。

也就是说,当前 a n s [ i ] [ j ] ans[i][j] ans[i][j]代表 g r i d [ i ] [ j ] grid[i][j] grid[i][j]左上角的那条线一共有多少种不同的元素。

当从左上角遍历到右下角后,清空哈希表,再次向左上角折回,并在折回过程中统计右下角一共有多少种元素。

左上角元素的种类数减去右下角元素的种类数即为当前位置对应的答案。

  • 时间复杂度 O ( m n ) O(mn) O(mn)
  • 空间复杂度 O ( min ⁡ ( m , n ) ) O(\min(m, n)) O(min(m,n))

AC代码

C++
/*
 * @Author: LetMeFly
 * @Date: 2025-03-25 19:44:55
 * @LastEditors: LetMeFly.xyz
 * @LastEditTime: 2025-03-25 21:04:00
 */
class Solution {
public:
    vector<vector<int>> differenceOfDistinctValues(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<int>> ans(n, vector<int>(m));
        for (int k = 0; k < m + n - 1; k++) {
            int i = k < m ? 0 : k - m + 1, j = k < m ? k : 0;
            unordered_set<int> se;
            int d = 0;
            for (; i + d < n && j + d < m; d++) {
                ans[i + d][j + d] = se.size();
                se.insert(grid[i + d][j + d]);
            }
            se.clear();
            for (d--; d >= 0; d--) {
                ans[i + d][j + d] = abs(int(ans[i + d][j + d] - se.size()));
                se.insert(grid[i + d][j + d]);
            }
        }
        return ans;
    }
};

解题方法三:位运算优化

很容易注意到, g r i d [ i ] [ j ] grid[i][j] grid[i][j]的范围是 1 1 1 50 50 50,因此我们可以直接使用64位整数的每一位表示每个数值是否出现过

这样,加上将临时结果存放在答案数组中,我们就做到了 O ( 1 ) O(1) O(1)额外空间得出答案了。

  • 时间复杂度 O ( m n ) O(mn) O(mn)
  • 空间复杂度 O ( 1 ) O(1) O(1),力扣返回值不记入算法空间复杂度

AC代码

C++
/*
 * @Author: LetMeFly
 * @Date: 2025-03-25 21:06:53
 * @LastEditors: LetMeFly.xyz
 * @LastEditTime: 2025-03-25 21:17:48
 * @Description: AC,100.00%,100.00%
 */
typedef long long ll;


class Solution {
public:
    vector<vector<int>> differenceOfDistinctValues(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector<vector<int>> ans(n, vector<int>(m));
        for (int k = 0; k < m + n - 1; k++) {
            int i = k < m ? 0 : k - m + 1, j = k < m ? k : 0;
            ll se = 0;
            int d = 0;
            for (; i + d < n && j + d < m; d++) {
                ans[i + d][j + d] = __builtin_popcountll(se);
                se |= 1LL << grid[i + d][j + d];
            }
            se = 0;
            for (d--; d >= 0; d--) {
                ans[i + d][j + d] = abs(ans[i + d][j + d] - __builtin_popcountll(se));
                se |= 1LL << grid[i + d][j + d];
            }
        }
        return ans;
    }
};
  • 执行用时分布3ms,击败100.00%
  • 消耗内存分布28.95MB,击败100.00%
Python
'''
Author: LetMeFly
Date: 2025-03-25 21:18:16
LastEditors: LetMeFly.xyz
LastEditTime: 2025-03-25 21:23:09
'''
from typing import List

class Solution:
    def differenceOfDistinctValues(self, grid: List[List[int]]) -> List[List[int]]:
        n, m = len(grid), len(grid[0])
        ans = [[0] * m for _ in range(n)]
        for k in range(m + n - 1):
            i = 0 if k < m else k - m + 1
            j = k if k < m else 0
            se = d = 0
            while i + d < n and j + d < m:
                ans[i + d][j + d] = se.bit_count()  # python10才有
                se |= 1 << grid[i + d][j + d]
                d += 1
            se = 0
            d -= 1
            while d >= 0:
                ans[i + d][j + d] = abs(ans[i + d][j + d] - se.bit_count())
                se |= 1 << grid[i + d][j + d]
                d -= 1
        return ans
Java
/*
 * @Author: LetMeFly
 * @Date: 2025-03-25 21:23:51
 * @LastEditors: LetMeFly.xyz
 * @LastEditTime: 2025-03-25 21:28:30
 */
class Solution {
    public int[][] differenceOfDistinctValues(int[][] grid) {
        int n = grid.length, m = grid[0].length;
        int[][] ans = new int[n][m];
        for (int k = 0; k < m + n - 1; k++) {
            int i = k < m ? 0 : k - m + 1;
            int j = k < m ? k : 0;
            long se = 0;
            int d = 0;
            for (; i + d < n && j + d < m; d++) {
                ans[i + d][j + d] = Long.bitCount(se);
                se |= 1L << grid[i + d][j + d];
            }
            se = 0;
            for (d--; d >= 0; d--) {
                ans[i + d][j + d] = Math.abs(ans[i + d][j + d] - Long.bitCount(se));
                se |= 1L << grid[i + d][j + d];
            }
        }
        return ans;
    }
}
Go
/*
 * @Author: LetMeFly
 * @Date: 2025-03-25 21:29:42
 * @LastEditors: LetMeFly.xyz
 * @LastEditTime: 2025-03-25 21:37:43
 */
package main

import "math/bits"

func abs2711(a int) int {
    if a < 0 {
        return -a
    }
    return a
}

func differenceOfDistinctValues(grid [][]int) [][]int {
    n, m := len(grid), len(grid[0])
    ans := make([][]int, n)
    for i := range ans {
        ans[i] = make([]int, m)
    }
    for k := 0; k < m + n - 1; k++ {
        var i, j int
        if k < m {
            i, j = 0, k
        } else {
            i, j = k - m + 1, 0
        }
        se := uint64(0)
        d := 0
        for ; i + d < n && j + d < m; d++ {
            ans[i + d][j + d] = bits.OnesCount64(se)
            se |= uint64(1) << grid[i + d][j + d]
        }
        se = 0
        for d--; d >= 0; d-- {
            ans[i + d][j + d] = abs2711(ans[i + d][j + d] - bits.OnesCount64(se))
            se |= uint64(1) << grid[i + d][j + d]
        }
    }
    return ans
}
  • 执行用时分布0ms,击败100.00%
  • 消耗内存分布8.31MB,击败100.00%

End

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

相关文章:

  • Axios核心原理
  • Python与文件——保存文件
  • 微软Copilot与向量数据库:智能化办公的技术架构与实现路径
  • 李宏毅机器学习笔记(1)—机器学习基本概念+深度学习基本概念
  • 软件工程面试题(六)
  • 解决linux centos ubuntu等无法启动谷歌chrome浏览器问题
  • HarmonyOS NEXT——【鸿蒙相册图片以及文件上传Picker封装】
  • Stereolabs ZED Box Mini:NVIDIA Orin™驱动,双GMSL2输入,智能机器视觉AI新选择”
  • android studio调试aosp手机userdebug版本无法查看局部变量和参数问题如何解决?
  • 【从零实现Json-Rpc框架】- 项目实现 -抽象消息类实现篇
  • Uni-app入门到精通:subPackages节点为小程序的分包加载配置
  • 实现金蝶与钉钉无缝对接以高效管理银行账号信息
  • 人脸识别新规落地,“多模态认证”将成常态
  • 每日一题之日期统计
  • 为什么大模型在 OCR 任务上表现不佳?
  • 包络解调在故障诊断中的应用-广义检波解调案例
  • 架构师面试(二十二):TCP 协议
  • PDF处理控件Spire.PDF系列教程:使用 JavaScript 在 React 中将 PDF 转换为 HTML
  • java八股文之企业场景
  • rv1106抓h264流
  • 男子恶意遗弃幼子获刑,最高法发布涉未成年人家庭保护典型案例
  • 30平米的无障碍酒吧里,我们将偏见折叠又摊开
  • 4月企业新发放贷款利率处于历史低位
  • 美凯龙:董事兼总经理车建兴被立案调查并留置
  • 佩斯科夫:若普京认为必要,将公布土耳其谈判俄方代表人选
  • 威尼斯建筑双年展总策划:山的另一边有什么在等着我们