算法小白的进阶之路(力扣9~12)

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝 ✨✨ 欢迎订阅本专栏 ✨✨

前言

本栏目将记录博主暑假从0开始刷力扣的算法题,每一条题目我都会把知识点抽丝剥茧地进行分析,以便大家更好的理解和学习,话不多说,肝!

|----|---------|------|
| 序号 | 标题 | 力扣序号 |
| 9 | 重塑矩阵 | 566 |
| 10 | 区间加法 | 598 |
| 11 | 三个数最大乘积 | 628 |
| 12 | 错误的集合 | 645 |

1.重塑矩阵

题目:

在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原始数据。

给你一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 rc ,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。

如果具有给定参数的 reshape 操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

示例 1:

复制代码
输入:mat = [[1,2],[3,4]], r = 1, c = 4
输出:[[1,2,3,4]]

示例 2:

复制代码
输入:mat = [[1,2],[3,4]], r = 2, c = 4
输出:[[1,2],[3,4]]

解题思路:

  1. 检查维度兼容性 :首先,需要确保原始矩阵nums的元素总数(即M * N)等于目标矩阵res的元素总数(即r * c)。如果两者不等,说明无法按照要求重塑矩阵,因此直接返回原始矩阵。

  2. 创建目标矩阵 :如果维度兼容,则创建一个新的二维数组res,其大小为r x c,用于存储重塑后的矩阵。

  3. 遍历并填充目标矩阵 :通过两层嵌套循环遍历原始矩阵nums的每个元素。外层循环遍历原始矩阵的行(i从0到M-1),内层循环遍历列(j从0到N-1)。

  4. 更新目标矩阵的索引 :在遍历过程中,使用两个变量rowcol来跟踪目标矩阵res的当前填充位置。每当col达到c(即目标矩阵的列数),说明已经填满了一行,此时将row增加1以移到下一行,并将col重置为0。这样,res[row][col]就能正确地指向目标矩阵的下一个填充位置。

  5. 填充元素 :在每次内层循环中,将原始矩阵nums的当前元素nums[i][j]赋值给目标矩阵res的对应位置res[row][col],然后递增col以指向下一个列位置。

  6. 返回结果 :遍历完成后,返回填充好的目标矩阵res

java 复制代码
int M = nums.length; // 获取原始矩阵的行数  
int N = nums[0].length; // 假设原始矩阵不是空矩阵,获取其列数  
if (M * N != r * c) { // 检查维度兼容性  
    return nums; // 如果维度不兼容,返回原始矩阵  
}  
int[][] res = new int[r][c]; // 创建一个新的二维数组,大小为r x c,用于存储重塑后的矩阵  
int row = 0, col = 0; // 初始化目标矩阵的填充位置索引  
for (int i = 0; i < M; i++) { // 外层循环遍历原始矩阵的行  
    for (int j = 0; j < N; j++) { // 内层循环遍历列  
        if (col == c) { // 如果已经填满了一行  
            row += 1; // 移到下一行  
            col = 0; // 重置列索引  
        }  
        res[row][col] = nums[i][j]; // 将原始矩阵的元素填充到目标矩阵的对应位置  
        col += 1; // 移到下一列  
    }  
}  
return res; // 返回重塑后的矩阵

2.区间加法

题目:

给你一个 m x n 的矩阵 M和一个操作数组 op 。矩阵初始化时所有的单元格都为 0ops[i] = [ai, bi] 意味着当所有的 0 <= x < ai0 <= y < bi 时, M[x][y] 应该加 1。

执行完所有操作后 ,计算并返回 矩阵中最大整数的个数

示例 :

复制代码
输入: m = 3, n = 3,ops = [[2,2],[3,3]]
输出: 4
解释: M 中最大的整数是 2, 而且 M 中有4个值为2的元素。因此返回 4。

解题思路:

这道题其实不关心最大的值,而是关心次数,讲过小知识:在前端的知识里面,左上角为画面的默认值,所以重叠最多次。

这个问题实际上并不需要真正去模拟矩阵的加法操作或者构建一个真实的矩阵。关键在于理解操作数组 ops 对矩阵 M 的影响。每个操作 [ai, bi] 实际上是在说:"在矩阵的左上角 ai x bi 的子矩阵内的所有元素都应该加 1"。但是,由于所有元素初始都是 0,并且每个操作都是对整个子矩阵的加 1 操作,因此最终矩阵 M 的最大值将仅由操作数组 ops 中定义的最小 ai 和最小 bi 决定。

代码(Java)

java 复制代码
class Solution {  
    public int maxCount(int m, int n, int[][] ops) {  
        // 遍历操作数组  
        for (int[] op : ops) {  
            // 更新 m 为当前 m 和操作数组中的 ai 的最小值  
            m = Math.min(m, op[0]);  
            // 更新 n 为当前 n 和操作数组中的 bi 的最小值  
            n = Math.min(n, op[1]);  
        }  
        // 返回最终矩阵中最大整数的个数,即 m * n  
        return m * n;  
    }  
}

假设我们有以下输入:

  • 矩阵的初始大小(虽然在这个问题中实际上并不重要,因为我们只关心操作数组):m = 5, n = 5(即一个 5x5 的矩阵,但所有元素初始都是 0)。
  • 操作数组 ops[[2, 3], [4, 2], [1, 5]]

现在,我们逐步执行代码:

  1. 初始化

    • m = 5
    • n = 5
    • ops = [[2, 3], [4, 2], [1, 5]]
  2. 开始遍历操作数组

    第一步(处理第一个操作 [2, 3]

    • m = Math.min(5, 2); -> m = 2(因为 2 是当前 mop[0] 的最小值)。
    • n = Math.min(5, 3); -> n = 3(因为 3 是当前 nop[1] 的最小值)。

    此时

    • m = 2
    • n = 3

    第二步(处理第二个操作 [4, 2]

    • m = Math.min(2, 4); -> m = 2(因为 2 仍然是当前 mop[0] 的最小值)。
    • n = Math.min(3, 2); -> n = 2(因为 2 是当前 nop[1] 的最小值)。

    此时

    • m = 2
    • n = 2

    第三步(处理第三个操作 [1, 5]

    • m = Math.min(2, 1); -> m = 1(因为 1 是当前 mop[0] 的最小值)。
    • n = Math.min(2, 5); -> n = 2(因为 2 仍然是当前 nop[1] 的最小值)。

    此时

    • m = 1
    • n = 2
  3. 遍历结束,返回结果

    • 执行 return m * n; -> return 1 * 2; -> return 2;

3.三个数最大乘积

题目:

给你一个整型数组 nums ,在数组中找出由三个数组成的最大乘积,并输出这个乘积。

示例 :

复制代码
输入:nums = [1,2,3]
输出:6

解题思路:

如果数组中全是非负数,则排序后最大的三个数相乘即为最大乘积;如果全是非正数,则最大的三个数相乘同样也为最大乘积。

如果数组中有正数有负数,则最大乘积既可能是三个最大正数的乘积,也可能是两个最小负数(即绝对值最大)与最大正数的乘积。

代码(Java):

java 复制代码
class Solution {
    public int maximumProduct(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        return Math.max(nums[0] * nums[1] * nums[n - 1], nums[n - 3] * nums[n - 2] * nums[n - 1]);
    }
}
  1. 数组中最小的两个数(nums[0]和nums[1])乘以最大的数(nums[n-1]), 这种情况适用于数组中有负数且负数的绝对值很大的情况,因为负数乘以负数会得到正数,可能使得乘积变得更大。

  2. 数组中最大的三个数(nums[n-3]、nums[n-2]和nums[n-1])相乘。 这是最常见的情况,因为当数组中没有负数或者负数的绝对值不大时,直接取最大的三个数相乘即可得到最大乘积。


4.错误的集合

题目:

集合 s 包含从 1n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复

给定一个数组 nums 代表了集合 S 发生错误后的结果。

请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

示例 :

复制代码
输入:nums = [1,2,2,4]
输出:[2,3]

解题思路:

使用「哈希表」统计每个元素出现次数,然后在 [1,n] 查询每个元素的出现次数。

在「哈希表」中出现 2 次的为重复元素,未在「哈希表」中出现的元素为缺失元素。

由于这里数的范围确定为 [1,n],我们可以使用数组来充当「哈希表」,以减少「哈希表」的哈希函数执行和冲突扩容的时间开销。

代码(Java)

java 复制代码
class Solution {
    public int[] findErrorNums(int[] nums) {
        int n = nums.length;
        int[] cnts = new int[n + 1];
        for (int x : nums) cnts[x]++;
        int[] ans = new int[2];
        for (int i = 1; i <= n; i++) {
            if (cnts[i] == 0) ans[1] = i;
            if (cnts[i] == 2) ans[0] = i;
        }
        return ans;
    }
}
java 复制代码
int[] nums = {1, 2, 3, 3, 5};  
int[] cnts = new int[6]; // 长度为6,对应数字1到5  
  
// 遍历nums数组  
for (int x : nums) {  
    cnts[x]++; // 对应索引位置上的计数加1  
}  
  
// 遍历结束后,cnts数组的内容如下:  
// cnts[0] = 0(没有数字对应索引0)  
// cnts[1] = 1(数字1出现1次)  
// cnts[2] = 1(数字2出现1次)  
// cnts[3] = 2(数字3出现2次,即重复了)  
// cnts[4] = 0(数字4没有出现,即缺失了)  
// cnts[5] = 1(数字5出现1次)

❤️❤️❤️小郑是普通学生水平,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

相关推荐
荒古前16 分钟前
龟兔赛跑 PTA
c语言·算法
Colinnian19 分钟前
Codeforces Round 994 (Div. 2)-D题
算法·动态规划
用户00993831430125 分钟前
代码随想录算法训练营第十三天 | 二叉树part01
数据结构·算法
shinelord明29 分钟前
【再谈设计模式】享元模式~对象共享的优化妙手
开发语言·数据结构·算法·设计模式·软件工程
დ旧言~35 分钟前
专题八:背包问题
算法·leetcode·动态规划·推荐算法
_WndProc1 小时前
C++ 日志输出
开发语言·c++·算法
努力学习编程的伍大侠1 小时前
基础排序算法
数据结构·c++·算法
XiaoLeisj2 小时前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝