hot100 -- 矩阵

👂 Peter Pan - kidult. - 单曲 - 网易云音乐

👂 Bibliothèque(图书馆) - Jasing Rye - 单曲 - 网易云音乐

目录

🌼前言

🌼二分模板

🎂矩阵置零

[AC 标记数组](#AC 标记数组)

[AC 标记变量](#AC 标记变量)

🚩螺旋矩阵

[AC 模拟](#AC 模拟)

🌼旋转图像

[AC 转置 + 翻转](#AC 转置 + 翻转)

[AC 辅助数组](#AC 辅助数组)

[🍃搜索二维矩阵 II](#🍃搜索二维矩阵 II)

[AC 二分](#AC 二分)

[AC Z字形查找](#AC Z字形查找)


🌼前言

分享一个,24届,现在大四学长的经历

大二绩点专业第一,稳拿保研资格,大三翘课一年,全力冲刺工作实习,想着两手抓,结果错失保研资格,只能全力备战秋招....

最后,作为唯一一个本科生,和一堆研究生竞争,笔试全国第二,综合表现第一,逆风翻盘,成功入职大厂
想说下他笔试全国第二的秘诀之一,hot100 刷了七八遍,总题量 500 左右,笔试时随便一道 medium,hard,5 ~ 15分钟 AC

那么看来,我之前定的,大二结束前,++二刷 hot100 可能不太够++

那就大三再多刷两遍,无聊就刷刷
项目方面,他做了 webserver,6.824,另外还研究了 2 套源码,每套源码都写了十几篇 5000 字以上的博客记录

下个项目,我准备做 ++muduo++ 数据库项目,理由如下

  • 有个西邮的大二同学,打算和我一起做,但是他进度比我快一点
  • 手上有 2 个 muduo 讨论群,可以和不同进度的 uu 一起交流
  • 还有 1 套视频教程
  • 认识几个做了 6.824,Tiny KV,muduo,6.s081 的佬,没事厚着脸皮请教请教
    手头可选的项目:6.s081,6.824,Tiny KV,muduo,CMU 15445,rcore,ucore

🌼二分模板

二分,难点在于边界的处理,这里分享两个 yxc 的模板👇

2.1 二分与前缀和 - AcWing

++模板1 -- 整数二分++

视频 1:02分 ~ 1:14分 介绍模板1

cpp 复制代码
while (l < r) {
    int mid = (l + r + 1) >> 1; // 防止下取整死循环, 要 +1
    if (...) 
        l = m; // 记住 l = m
    else 
        r = m - 1;
}

++模板2 -- 整数二分++

cpp 复制代码
while (l < r) {
    int mid = (l + r) >> 1; 
    if (...)
        r = m; // 上面没 +1 就 r = m
    else
        l = m + 1;
}

🎂矩阵置零

73. 矩阵置零 - 力扣(LeetCode)

AC 标记数组

借助两个标记数组 r[], c[];r[3] = 1 表示第 3 行置 0;c[0] = 1 表示第 0 列置 0

注意:1)vector 要声明大小

时间O(mn) 空间O(m + n)

cpp 复制代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        // vector 要声明大小
        vector<int> r(m), c(n); // 行 / 列标记数组

        for (int i = 0; i < m; ++i)
            for (int j = 0; j < n; ++j)
                if (matrix[i][j] == 0)
                    r[i] = 1, c[j] = 1; // 标记

        for (int i = 0; i < m; ++i) 
            if (r[i] == 1)
                for (int j = 0; j < n; ++j)
                    matrix[i][j] = 0;
        for (int j = 0; j < n; ++j) 
            if (c[j] == 1)
                for (int i = 0; i < m; ++i) 
                    matrix[i][j] = 0;
                
    }
};

AC 标记变量

用原矩阵第 0 行,第 0 列代替原本的 r[] 和 c[](matrix[i][0] = 0 或 matrix[0][j] = 0 进行标记),此时 第 0 行,第 0 列是否包含 0 没办法记录

只需要借助两个变量 r, c 记录

++注意++:一开始我担心,遍历过程会破坏原本的第 0 行,第 0 列,并不会,因为某个位置 (i, j) 为 0,必然会使整行整列为 0,那么的 matrix[0][j] 和 matrix[i][0] = 0 的标记,包含在里面

行是竖着下去的,列是横着往右的😂

时间 O(mn),空间 O(1)

cpp 复制代码
class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        int r = 0, c = 0; // 原来的第 0 行,第 0 列是否包含 0

        // 遍历
        for (int i = 0; i < m; ++i) 
            for (int j = 0; j < n; ++j) {
                if (matrix[i][j] == 0 && i == 0)
                    r = 1; // 列
                if (matrix[i][j] == 0 && j == 0)
                    c = 1; // 行
                // 原矩阵第 0 行,第 0 列记录该行 / 列是否包含 0
                if (matrix[i][j] == 0)
                    matrix[0][j] = 0, matrix[i][0] = 0; // 标记
            }

        // 置零
        for (int i = 1; i < m; ++i) 
            if (matrix[i][0] == 0) // 第 i 行置 0
                for (int j = 0; j < n; ++j) 
                    matrix[i][j] = 0; 
        for (int j = 1; j < n; ++j) 
            if (matrix[0][j] == 0) // 第 j 列置 0
                for (int i = 0; i < m; ++i) 
                    matrix[i][j] = 0;
        if (r == 1) 
            for (int j = 0; j < n; ++j)
                matrix[0][j] = 0;
        if (c == 1)
            for (int i = 0; i < m; ++i)
                matrix[i][0] = 0;
    }
};

🚩螺旋矩阵

54. 螺旋矩阵 - 力扣(LeetCode)

AC 模拟

坑....vector,初始声明大小后,++不要用 push_back(),只会在后面追加++....否则就会内存溢出

一道++模拟题++
通过维护 up, down, right, left,四个边界值,边界值每次碰壁都会收缩

比如一开始走完上边,上边界++,达到收缩的目的

时间 O(m*n),空间 O(1)

cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<int> ans(m*n);

        // 边界值, 每次碰壁都会收缩
        int left = 0, right = n - 1, up = 0, down = m - 1;
        int i = 0, j = 0, cnt = 0;

        ans[cnt++] = matrix[i][j]; // 插入起点
        while (1) {
            while (cnt < m*n) { // 向右
                if (j < right)
                    j++;
                else break;
                ans[cnt++] = matrix[i][j];
            }
            up++; // 上边走完后,上边界收缩

            while (cnt < m*n) { // 向下
                if (i < down)
                    i++;
                else break;
                ans[cnt++] = matrix[i][j];
            }
            right--; // 右边走完,右边界收缩

            while (cnt < m*n) { // 向左
                if (j > left)
                    j--;
                else break;
                ans[cnt++] = matrix[i][j];
            }
            down--; // 下面走完,下边界收缩

            while (cnt < m*n) { // 向上
                if (i > up)
                    i--;
                else break;
                ans[cnt++] = matrix[i][j];
            }
            left++; // 左边走完,左边界收缩

            if (cnt == m*n) break;
        }

        return ans;
    }
};

🌼旋转图像

48. 旋转图像 - 力扣(LeetCode)

AC 转置 + 翻转

先矩阵转置(行列互换),再对称翻转

先矩阵转置(只需遍历对角线右侧)👇

(i, j),(j, i) 互换

再左右对称翻转,(i, j) 与 (i, n - j - 1) 互换,列的范围 < n/2 即可

时间 O(n^2),空间 O(1)

cpp 复制代码
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();

        // 矩阵转置
        for (int i = 0; i < n; ++i) 
            for (int j = i + 1; j < n; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;  
            }

        // 左右对称翻转
        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][n - j - 1];
                matrix[i][n - j - 1] = temp;
            }
    }
};

AC 辅助数组

假设原矩阵 (i ,j)

新的列就是原矩阵的行 i,新的行就是原矩阵的 n - j - 1

所以新 (i, j) = 原 (n - j - 1, i)

时间 O(n^2),空间 O(n^2)

cpp 复制代码
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        auto matrix_e = matrix; // 值拷贝

        for (int i = 0; i < n; ++i) 
            for (int j = 0; j < n; ++j) 
                 matrix_e[i][j] = matrix[n - j - 1][i];
        // 最后要值拷贝回原数组
        matrix = matrix_e;
    }
};

🍃搜索二维矩阵 II

240. 搜索二维矩阵 II - 力扣(LeetCode)

AC 二分

思路:遍历每一行,对该行二分

二分难点在于死循环,最好背背上面的模板,具体原因是,防止 L = R - 1 后进入死循环

时间 O(mlogn),空间 O(1)

cpp 复制代码
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();

        for (int i = 0; i < m; ++i) {
            int l = 0, r = n - 1; // 下标作为左右端点
            while (l < r) { // 二分查找每一行
                int mid = (l + r + 1) >> 1;
                if (matrix[i][mid] <= target)
                    l = mid;
                else if (matrix[i][mid] > target)
                    r = mid - 1;
            }
            // 退出 while 后 l == r
            if (matrix[i][l] == target)
                return true;
        }
        
        return false;
    }
};

AC Z字形查找

利用好这两个性质👇

  • 每行的元素从左到右升序排列
  • 每列的元素从上到下升序排列

我们将 target 可能的范围,划分到当前元素 (i, j) 往左往下的长方形中

从右上角开始

如果 target 大于当前元素,有两种选择,往左 或 往下

考虑到行 / 列是有序的,只能往下 i++

如果 target 小于当前元素,只能往左 j--

时间 O(m + n),空间 O(1)

cpp 复制代码
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();

        int i = 0, j = n - 1; // 右上角开始
        while (i >= 0 && i < m && j >= 0 && j < n) {
            if (target < matrix[i][j])
                j--; // 目标值更小,只能往左
            else if (target > matrix[i][j])
                i++; // 目标值更大,只能向下
            else 
                return true; // target == 
        }
        return false;
    }
};
相关推荐
达文汐2 天前
【困难】力扣算法题解析LeetCode332:重新安排行程
java·数据结构·经验分享·算法·leetcode·力扣
沉默-_-4 天前
备战蓝桥杯--栈
数据结构·算法·力扣·
苦藤新鸡7 天前
41.有序数组(二叉搜索树)转平衡二叉树
数据结构·力扣
苦藤新鸡9 天前
36.二叉树的中序遍历(递归)
力扣
苦藤新鸡9 天前
37.二叉树的最大深度
力扣
苦藤新鸡9 天前
39.判断对称二叉树
数据结构·力扣
Anastasiozzzz10 天前
力扣hot100 20.有效的括号 解析
java·算法·面试·力扣
苦藤新鸡10 天前
29.删除倒数第N个节点
数据结构·链表·力扣
苦藤新鸡11 天前
28.两数相加,进位制
数据结构·算法·链表·力扣
苦藤新鸡13 天前
20.右旋转图片
数据结构·算法·leetcode·力扣