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

力扣hot100刷题——11~20

文章目录

  • 11.滑动窗口最大值
    • 题目描述
    • 思路:滑动窗口+单调队列
    • code
  • 12.最小覆盖子串
    • 题目描述
    • 思路:双指针/滑动窗口+哈希
    • code Ⅰ
    • code Ⅱ
  • 13.最大子数组和
    • 题目描述
    • 思路:dp/贪心
    • code
  • 14.合并区间
    • 题目描述
    • 思路:贪心
    • code
  • 15.轮转数组
    • 题目描述
    • 思路:反转法
    • code
  • 16.除自身以外数组的乘积
    • 题目描述
    • 思路:前缀积+后缀积
    • code
  • 17.缺失的第一个正数
    • 题目描述
    • 思路:哈希
    • code
  • 18.矩阵置零
    • 题目描述
    • 思路:哈希
    • code
    • code(优化)
  • 19.螺旋矩阵
    • 题目描述
    • 思路:模拟/螺旋矩阵
    • code
  • 20.旋转图像
    • 题目描述
    • 思路一:水平翻转+转置
    • code

11.滑动窗口最大值

题目描述

239. 滑动窗口最大值

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       31 [3  -1  -3] 5  3  6  7       31  3 [-1  -3  5] 3  6  7       51  3  -1 [-3  5  3] 6  7       51  3  -1  -3 [5  3  6] 7       61  3  -1  -3  5 [3  6  7]      7

示例 2:

输入:nums = [1], k = 1
输出:[1]

思路:滑动窗口+单调队列

  1. 单调队列
    • 使用一个双端队列(Deque)来存储数组的索引。
    • 队列中的元素按从大到小的顺序排列,队头元素是当前窗口的最大值。
  2. 滑动窗口
    • 遍历数组,维护一个大小为 k 的滑动窗口。
    • 在遍历过程中,动态更新队列,确保队列中的元素始终在窗口内,并且按从大到小的顺序排列。
  3. 队列更新规则
    • 移除不在窗口内的元素:如果队头元素已经不在当前窗口内,则将其从队列中移除。
    • 移除小于当前元素的元素:从队尾开始,移除所有小于当前元素的元素,确保队列的单调性。
    • 将当前元素加入队列:将当前元素的索引加入队尾。
  4. 记录结果
    • 当窗口形成时(i >= k - 1),将队头元素(当前窗口的最大值)加入结果数组。

我认为本题真的很难,第一次不懂的话可以过段时间再看。

code

#include <stdio.h>
#include <stdlib.h>int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {if (nums == NULL || numsSize == 0 || k == 0) {*returnSize = 0;return NULL;}*returnSize = numsSize - k + 1; // 计算结果数组的大小int* result = (int*)malloc(sizeof(int) * (*returnSize)); // 分配结果数组int queue[numsSize]; // 使用数组模拟双端队列int head = 0, tail = -1; // 队列的头和尾for (int i = 0; i < numsSize; i++) {// 移除队列中不在当前窗口内的元素if (head <= tail && queue[head] < i - k + 1) {head++;}// 移除队列中所有小于当前元素的元素while (head <= tail && nums[queue[tail]] < nums[i]) {tail--;}// 将当前元素的索引加入队列queue[++tail] = i;// 如果窗口已经形成,将队列头部的元素加入结果数组if (i >= k - 1) {result[i - k + 1] = nums[queue[head]];}}return result; // 返回结果数组
}

12.最小覆盖子串

题目描述

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 ""

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。

示例 2:

输入:s = "a", t = "a"
输出:"a"
解释:整个字符串 s 是最小覆盖子串。

示例 3:

输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。

思路:双指针/滑动窗口+哈希

题目中的4个提示已经给出了本题思路:

  • Use two pointers to create a window of letters in s, which would have all the characters from t.
    使用两个指针在 s 中创建一个字母窗口,该窗口将包含 t 中的所有字符。
  • Expand the right pointer until all the characters of t are covered.
    展开右指针,直到覆盖 t 的所有字符。
  • Once all the characters are covered, move the left pointer and ensure that all the characters are still covered to minimize the subarray size.
    覆盖所有字符后,移动左指针并确保仍覆盖所有字符,以最小化子数组大小。
  • Continue expanding the right and left pointers until you reach the end of s.
    继续展开左右指针,直到到达 s 的末尾。

具体代码思路:

  1. 初始化哈希表:
    • hasht[128]:记录 t 中每个字符的出现次数。
    • hashs[128]:记录当前窗口内字符的出现次数。
  2. 滑动窗口双指针:
    • leftright 指针分别表示窗口的左右边界。
    • right 向右扩展窗口,直到窗口包含 t 的所有字符。
    • 当窗口有效时,left 向右收缩以寻找更小的窗口。
  3. 计数器 count 的作用:
    • 统计当前窗口中恰好满足 t 字符需求的字符数量。
    • count == strlen(t) 时,说明窗口已覆盖 t 的所有字符。
  4. 更新最小窗口:
    • 每次窗口有效时,记录窗口的起始位置和长度。
    • 最终返回最小窗口对应的子串。

code Ⅰ

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>char* minWindow(char* s, char* t) {int m = strlen(s), n = strlen(t); // 获取 s 和 t 的长度char* ans = malloc(sizeof(char) * (m + 1)); // 分配结果数组if (m < n) return ""; // 如果 s 的长度小于 t,直接返回空字符串int hashs[130] = {0}; // 记录 s 中当前窗口的字符频率int hasht[130] = {0}; // 记录 t 中字符的频率// 统计 t 中字符的频率for (int i = 0; i < n; i++) {hasht[t[i]]++;}int l = 0, r = 0; // 滑动窗口的左右指针int indexl = 0, indexr = 0; // 最小窗口的左右索引int minlen = m + 1; // 最小窗口长度// 初始化窗口while (r < n) {hashs[s[r]]++; // 更新 s 中当前窗口的字符频率r++; // 右指针右移}// 滑动窗口while (r < m) {bool flag = true;// 检查当前窗口是否满足 t 中字符的频率要求for (int i = 0; i <= 128; i++) {if (hashs[i] < hasht[i]) {flag = false; // 如果不满足,设置 flag 为 falsebreak;}}if (flag) {// 如果满足条件,更新最小窗口if (r - l < minlen) {minlen = r - l;indexl = l;indexr = r;}// 移动左指针,缩小窗口hashs[s[l]]--;l++;} else {// 如果不满足条件,移动右指针,扩展窗口hashs[s[r]]++;r++;}}// 最后检查一次窗口bool flag = true;while (flag) {for (int i = 0; i <= 128; i++) {if (hashs[i] < hasht[i]) {flag = false; // 如果不满足,设置 flag 为 falsebreak;}}if (!flag) break; // 如果不满足条件,退出循环// 如果满足条件,更新最小窗口if (r - l < minlen) {minlen = r - l;indexl = l;indexr = m;}// 移动左指针,缩小窗口hashs[s[l]]--;l++;}// 如果最小窗口长度未更新,说明没有符合条件的子串if (minlen == m + 1) return "";// 将结果复制到 ans 中for (int i = indexl; i < indexr; i++) {ans[i - indexl] = s[i];}ans[minlen] = '\0'; // 添加字符串结束符return ans; // 返回结果
}

code Ⅱ

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>char* minWindow(char* s, char* t) {int m = strlen(s), n = strlen(t);if (m == 0 || n == 0 || m < n) return ""; // 如果 s 或 t 为空,或者 s 的长度小于 t,直接返回空字符串char* ans = malloc(sizeof(char) * (m + 1)); // 分配结果数组int hashs[128] = {0}; // 记录 s 中当前窗口的字符频率int hasht[128] = {0}; // 记录 t 中字符的频率// 统计 t 中字符的频率for (int i = 0; i < n; i++) {hasht[t[i]]++;}int l = 0, r = 0; // 滑动窗口的左右指针int minlen = m + 1; // 最小窗口长度int indexl = 0, indexr = 0; // 最小窗口的左右索引int count = 0; // 记录当前窗口中满足 t 中字符频率的字符数量while (r < m) {// 如果当前字符在 t 中出现过,则更新 hashs 和 countif (hasht[s[r]] > 0) {hashs[s[r]]++;if (hashs[s[r]] <= hasht[s[r]]) {count++;}}r++;// 如果当前窗口满足 t 中字符的频率要求while (count == n) {// 更新最小窗口if (r - l < minlen) {minlen = r - l;indexl = l;indexr = r;}// 移动左指针,缩小窗口if (hasht[s[l]] > 0) {hashs[s[l]]--;if (hashs[s[l]] < hasht[s[l]]) {count--;}}l++;}}// 如果最小窗口长度未更新,说明没有符合条件的子串if (minlen == m + 1) return "";// 将结果复制到 ans 中strncpy(ans, s + indexl, minlen);ans[minlen] = '\0'; // 添加字符串结束符return ans; // 返回结果
}

13.最大子数组和

题目描述

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]
输出:1

示例 3:

输入:nums = [5,4,-1,7,8]
输出:23

思路:dp/贪心

  1. 定义状态

    • 定义 dp[i] 表示以 nums[i] 结尾的最大子数组和。
  2. 状态转移方程

    • 如果 dp[i-1] 是正数,则将其加入当前元素 nums[i],形成更大的子数组和。

    • 如果 dp[i-1] 是负数,则以当前元素 nums[i] 作为新的子数组起点。

    • 状态转移方程为:

      dp[i] = max(nums[i], dp[i-1] + nums[i])

  3. 初始化

    • dp[0] = nums[0],因为以第一个元素结尾的最大子数组和就是它本身。
  4. 遍历数组

    • 从第二个元素开始,依次计算 dp[i],并更新全局最大值 ans
  5. 返回结果

    • 返回全局最大值 ans

code

#include <stdio.h>
#include <stdlib.h>int maxSubArray(int* nums, int numsSize) {if (nums == NULL || numsSize == 0) return 0; // 如果数组为空,直接返回 0int dp[numsSize]; // 定义 dp 数组,dp[i] 表示以 nums[i] 结尾的最大子数组和dp[0] = nums[0]; // 初始化 dp[0]int ans = dp[0]; // 初始化全局最大值for (int i = 1; i < numsSize; i++) {// 状态转移方程:dp[i] = max(nums[i], dp[i-1] + nums[i])dp[i] = fmax(nums[i], dp[i - 1] + nums[i]);// 更新全局最大值ans = fmax(ans, dp[i]);}return ans; // 返回全局最大值
}

14.合并区间

题目描述

56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2:

输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

思路:贪心

本题比较简单,直接拷贝之前的文章

代码随想录——贪心

  • 按区间的起始位置排序。
  • 遍历区间,如果当前区间与前一个区间重叠,则合并它们;否则,将前一个区间加入结果列表。

个人感觉比较简单~~

code

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>// 比较函数,用于按区间的起始位置从小到大排序
int cmp(const void* a, const void* b) {int* intervala = *(int**)a;int* intervalb = *(int**)b;// 按起始位置从小到大排序if (intervala[0] - intervalb[0] < 0) return -1;return 1;
}int** merge(int** intervals, int intervalsSize, int* intervalsColSize, int* returnSize, int** returnColumnSizes) {// 按区间的起始位置排序qsort(intervals, intervalsSize, sizeof(int*), cmp);// 分配结果数组的内存int** res = malloc(sizeof(int*) * intervalsSize);// 初始化返回的区间数量为 0*returnSize = 0;// 初始化当前区间的起始和结束位置int start = intervals[0][0];int end = intervals[0][1];// 遍历所有区间for (int i = 1; i < intervalsSize; i++) {// 如果当前区间与前一个区间重叠if (intervals[i][0] <= end) {// 合并区间,更新结束位置end = fmax(end, intervals[i][1]);} else {// 如果不重叠,将前一个区间加入结果列表res[*returnSize] = malloc(sizeof(int) * 2);res[*returnSize][0] = start;res[*returnSize][1] = end;(*returnSize)++;// 更新当前区间的起始和结束位置start = intervals[i][0];end = intervals[i][1];}}// 将最后一个区间加入结果列表res[*returnSize] = malloc(sizeof(int) * 2);res[*returnSize][0] = start;res[*returnSize][1] = end;(*returnSize)++;// 分配列大小数组的内存*returnColumnSizes = malloc(sizeof(int) * (*returnSize));// 设置每个区间的列大小为 2for (int i = 0; i < *returnSize; i++) {(*returnColumnSizes)[i] = 2;}// 返回结果数组return res;
}

15.轮转数组

题目描述

189. 轮转数组

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

思路:反转法

  1. 反转整个数组
  2. 反转前 k 个元素
  3. 反转剩余 n - k 个元素

证明:

  1. 初始数组

    • 数组 nums 可以表示为:
      n u m s = [ a 0 , a 1 , . . . , a n − k − 1 , a n − k , a n − k + 1 , . . . , a n − 1 ] nums = [a_{0}, a_{1}, ..., a_{n-k-1}, a_{n-k}, a_{n-k+1}, ..., a_{n-1}] nums=[a0,a1,...,ank1,ank,ank+1,...,an1]
  2. 第一次反转(反转整个数组)

    • 反转后,数组变为:
      n u m s = [ a n − 1 , . . . , a n − k + 1 , a n − k , a n − k − 1 , . . . , a 0 ] nums = [a_{n-1}, ..., a_{n-k+1}, a_{n-k}, a_{n-k-1}, ..., a_{0}] nums=[an1,...,ank+1,ank,ank1,...,a0]
  3. 第二次反转(反转前 k 个元素)

    • 反转前 k 个元素后,数组变为:
      n u m s = [ a n − k , . . . , a n − 1 , a n − k − 1 , . . . , a 0 ] nums = [a_{n-k}, ..., a_{n-1}, a_{n-k-1}, ..., a_{0}] nums=[ank,...,an1,ank1,...,a0]
  4. 第三次反转(反转剩下的 n - k 个元素)

    • 反转剩下的 n - k 个元素后,数组变为:
      n u m s = [ a n − k , . . . , a n − 1 , a 0 , . . . , a n − k − 1 ] nums = [a_{n-k}, ..., a_{n-1}, a_{0}, ..., a_{n-k-1}] nums=[ank,...,an1,a0,...,ank1]

网上有很多种证明,我觉得这样是最直观的

code

#include <stdio.h>// 反转数组的辅助函数
void reverse(int* nums, int start, int end) {while (start < end) {int temp = nums[start];nums[start] = nums[end];nums[end] = temp;start++;end--;}
}void rotate(int* nums, int numsSize, int k) {k = k % numsSize; // 处理 k 大于数组长度的情况if (k == 0) return; // 如果 k 为 0,直接返回// 反转整个数组reverse(nums, 0, numsSize - 1);// 反转前 k 个元素reverse(nums, 0, k - 1);// 反转剩下的 n - k 个元素reverse(nums, k, numsSize - 1);
}

16.除自身以外数组的乘积

题目描述

238. 除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

请 **不要使用除法,**且在 O(n) 时间复杂度内完成此题。

示例 1:

输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

思路:前缀积+后缀积

  1. 前缀积
    • 从左到右遍历数组,计算每个位置左侧所有元素的乘积。
  2. 后缀积
    • 从右到左遍历数组,计算每个位置右侧所有元素的乘积。
  3. 结果计算
    • 对于每个位置 i,结果 ans[i] 等于前缀积 left[i] 乘以后缀积 right[i]

code

#include <stdio.h>
#include <stdlib.h>int* productExceptSelf(int* nums, int numsSize, int* returnSize) {// 分配结果数组的内存int* ans = malloc(sizeof(int) * numsSize);*returnSize = numsSize; // 设置返回数组的大小// 定义前缀积数组 left 和后缀积数组 rightint left[numsSize];  // 存储每个位置左侧所有元素的乘积int right[numsSize]; // 存储每个位置右侧所有元素的乘积// 计算前缀积left[0] = 1; // 第一个元素左侧没有元素,前缀积为 1for (int i = 1; i < numsSize; i++) {left[i] = left[i - 1] * nums[i - 1]; // 前缀积 = 前一个前缀积 * 前一个元素}// 计算后缀积right[numsSize - 1] = 1; // 最后一个元素右侧没有元素,后缀积为 1for (int i = numsSize - 2; i >= 0; i--) {right[i] = right[i + 1] * nums[i + 1]; // 后缀积 = 后一个后缀积 * 后一个元素}// 计算结果数组for (int i = 0; i < numsSize; i++) {ans[i] = left[i] * right[i]; // 结果 = 前缀积 * 后缀积}return ans; // 返回结果数组
}

17.缺失的第一个正数

题目描述

41. 缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

输入:nums = [1,2,0]
输出:3
解释:范围 [1,2] 中的数字都在数组中。

示例 2:

输入:nums = [3,4,-1,1]
输出:2
解释:1 在数组中,但 2 没有。

示例 3:

输入:nums = [7,8,9,11,12]
输出:1
解释:最小的正数 1 没有出现。

思路:哈希

本题如果抛弃时间和空间的限制是蛮简单的。

有两种思路:

1.开辟额外空间建立哈希表,记录1~numSize的数是否出现。空间复杂度O(N)

2.先对原数组排序,再寻找没有出现的最小正整整。时间复杂度O(NlogN)

虽然上面两种方法都可以解决,但是不能满足题目的要求。这里的思路是:原地哈希

具体步骤:

  1. 原地哈希
    • 将数组中的每个正整数放到它应该在的位置
    • 如果当前数在 1numsSize之间,并且它不在正确的位置上,将它交换到正确的位置。
  2. 查找缺失的正整数
    • 最后遍历数组,找到第一个正数的位置 ii + 1 就是缺失的最小正整数。
    • 如果所有位置都是负数,则缺失的最小正整数是 n + 1

code

#include <stdio.h>// 交换两个整数的值
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int firstMissingPositive(int* nums, int numsSize) {// 第一步:将数组中的每个正整数放到它应该在的位置for (int i = 0; i < numsSize; i++) {// 如果当前数在 1 到 numsSize 之间,并且它不在正确的位置上while (nums[i] > 0 && nums[i] <= numsSize && nums[nums[i] - 1] != nums[i]) {// 将它交换到正确的位置swap(&nums[i], &nums[nums[i] - 1]);}}// 第二步:遍历数组,找到第一个不满足 nums[i] == i + 1 的位置for (int i = 0; i < numsSize; i++) {if (nums[i] != i + 1) {return i + 1;}}// 如果所有位置都满足条件,返回 numsSize + 1return numsSize + 1;
}

18.矩阵置零

题目描述

73. 矩阵置零

给定一个 *m* x *n* 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。**

示例 1:

img

输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]

示例 2:

img

输入:matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
输出:[[0,0,0,0],[0,4,5,0],[0,3,1,0]]

思路:哈希

1.问题分析

  • 我们需要将矩阵中所有 0 所在的行和列都置为 0
  • 直接修改矩阵会导致后续的 0 被误判,因此需要先记录哪些行和列需要置零。

2.核心思想

  • 使用两个辅助数组 rowcol,分别记录哪些行和列需要置零。
  • 遍历矩阵,如果发现 matrix[i][j] == 0,则标记 row[i]col[j]true
  • 再次遍历矩阵,根据 rowcol 的标记,将对应的行和列置零。

3.优化

  • 可以使用矩阵的第一行和第一列来替代辅助数组,从而将空间复杂度优化为 O(1)

code

void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {// 定义两个辅助数组,用于记录哪些行和列需要置零bool col[200]; // 记录列是否需要置零bool row[200]; // 记录行是否需要置零memset(col, false, sizeof(col)); // 初始化 col 数组为 falsememset(row, false, sizeof(row)); // 初始化 row 数组为 false// 遍历矩阵,标记需要置零的行和列for (int i = 0; i < matrixSize; i++) {for (int j = 0; j < matrixColSize[i]; j++) {if (matrix[i][j] == 0) { // 如果当前元素为 0if (!col[j]) col[j] = true; // 标记列 j 需要置零if (!row[i]) row[i] = true; // 标记行 i 需要置零}}}// 遍历矩阵,将标记的行置零for (int i = 0; i < matrixSize; i++) {if (row[i]) { // 如果行 i 需要置零for (int j = 0; j < matrixColSize[i]; j++) {matrix[i][j] = 0; // 将行 i 的所有元素置零}}}// 遍历矩阵,将标记的列置零for (int j = 0; j < matrixColSize[0]; j++) {if (col[j]) { // 如果列 j 需要置零for (int i = 0; i < matrixSize; i++) {matrix[i][j] = 0; // 将列 j 的所有元素置零}}}
}

code(优化)

void setZeroes(int** matrix, int matrixSize, int* matrixColSize) {bool firstRowHasZero = false; // 记录第一行是否需要置零bool firstColHasZero = false; // 记录第一列是否需要置零// 检查第一行是否有 0for (int j = 0; j < matrixColSize[0]; j++) {if (matrix[0][j] == 0) {firstRowHasZero = true;break;}}// 检查第一列是否有 0for (int i = 0; i < matrixSize; i++) {if (matrix[i][0] == 0) {firstColHasZero = true;break;}}// 遍历矩阵,标记需要置零的行和列for (int i = 1; i < matrixSize; i++) {for (int j = 1; j < matrixColSize[i]; j++) {if (matrix[i][j] == 0) {matrix[i][0] = 0; // 标记第 i 行需要置零matrix[0][j] = 0; // 标记第 j 列需要置零}}}// 根据标记置零for (int i = 1; i < matrixSize; i++) {for (int j = 1; j < matrixColSize[i]; j++) {if (matrix[i][0] == 0 || matrix[0][j] == 0) {matrix[i][j] = 0;}}}// 处理第一行if (firstRowHasZero) {for (int j = 0; j < matrixColSize[0]; j++) {matrix[0][j] = 0;}}// 处理第一列if (firstColHasZero) {for (int i = 0; i < matrixSize; i++) {matrix[i][0] = 0;}}
}

19.螺旋矩阵

题目描述

54. 螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例 1:

img

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

img

输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]

思路:模拟/螺旋矩阵

  1. 边界初始化

    • topbottom 分别表示当前遍历的上下边界。
    • leftright 分别表示当前遍历的左右边界。
  2. 螺旋遍历核心逻辑

    • 从左到右遍历上边界:将上边界的元素加入结果数组,然后上边界下移。
    • 从上到下遍历右边界:将右边界的元素加入结果数组,然后右边界左移。
    • 从右到左遍历下边界:将下边界的元素加入结果数组,然后下边界上移。
    • 从下到上遍历左边界:将左边界的元素加入结果数组,然后左边界右移。

    注:每次遍历完一个边界后修改边界值

  3. 终止条件

    • 当上边界超过下边界或左边界超过右边界时,遍历结束。
  4. 边界条件处理

    • 如果矩阵为空,直接返回 NULL 并设置 returnSize0

code

/*** Note: The returned array must be malloced, assume caller calls free().*/
int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) {if (matrixSize == 0 || matrixColSize[0] == 0) {*returnSize = 0;return NULL;}int m = matrixSize;          // 矩阵的行数int n = matrixColSize[0];    // 矩阵的列数int* ans = malloc(sizeof(int) * m * n); // 结果数组*returnSize = 0;             // 初始化返回数组的大小int top = 0, bottom = m - 1; // 定义上下边界int left = 0, right = n - 1; // 定义左右边界while (top <= bottom && left <= right) {// 从左到右遍历上边界for (int j = left; j <= right; j++) {ans[(*returnSize)++] = matrix[top][j];}top++; // 上边界下移// 从上到下遍历右边界for (int i = top; i <= bottom; i++) {ans[(*returnSize)++] = matrix[i][right];}right--; // 右边界左移// 如果上边界超过下边界,说明已经遍历完if (top > bottom) break;// 从右到左遍历下边界for (int j = right; j >= left; j--) {ans[(*returnSize)++] = matrix[bottom][j];}bottom--; // 下边界上移// 如果左边界超过右边界,说明已经遍历完if (left > right) break;// 从下到上遍历左边界for (int i = bottom; i >= top; i--) {ans[(*returnSize)++] = matrix[i][left];}left++; // 左边界右移}return ans;
}

20.旋转图像

题目描述

48. 旋转图像

给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

你必须在** 原地** 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。

示例 1:

img

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[[7,4,1],[8,5,2],[9,6,3]]

示例 2:

img

输入:matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出:[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

思路一:水平翻转+转置

证明略。。。(后面再补)

code

//主对角线翻转+水平翻转
void swap(int *a,int *b){int temp=*a;*a=*b;*b=temp;
}
void rotate(int** matrix, int matrixSize, int* matrixColSize) {//水平翻转for(int i=0;i<matrixSize/2;i++){for(int j=0;j<matrixSize;j++){swap(&matrix[i][j],&matrix[matrixSize-1-i][j]);}}//主对角线翻转for(int i=0;i<matrixSize;i++){for(int j=i+1;j<matrixSize;j++){swap(&matrix[i][j],&matrix[j][i]);}}}

相关文章:

力扣hot100刷题——11~20

文章目录 11.滑动窗口最大值题目描述思路&#xff1a;滑动窗口单调队列code 12.最小覆盖子串题目描述思路&#xff1a;双指针/滑动窗口哈希code Ⅰcode Ⅱ 13.最大子数组和题目描述思路&#xff1a;dp/贪心code 14.合并区间题目描述思路&#xff1a;贪心code 15.轮转数组题目描…...

R语言Stan贝叶斯空间条件自回归CAR模型分析死亡率多维度数据可视化

全文链接&#xff1a;https://tecdat.cn/?p40424 在空间数据分析领域&#xff0c;准确的模型和有效的工具对于研究人员至关重要。本文为区域数据的贝叶斯模型分析提供了一套完整的工作流程&#xff0c;基于Stan这一先进的贝叶斯建模平台构建&#xff0c;帮助客户为空间分析带来…...

速通HTML

目录 HTML基础 1.快捷键 2.标签 HTML进阶 1.列表 a.无序列表 b.有序列表 c.定义列表 2.表格 a.内容 b.合并单元格 3.表单 a.input标签 b.单选框 c.上传文件 4.下拉菜单 5.文本域标签 6.label标签 7.按钮标签 8.无语义的布局标签div与span 9.字符实体 HTML…...

安装 Milvus Java SDK

本主题介绍如何为 Milvus 安装 Milvus Java SDK。 当前版本的 Milvus 支持 Python、Node.js、GO 和 Java SDK。 要求 Java&#xff08;8 或更高版本&#xff09;Apache Maven 或 Gradle/Grails 安装 Milvus Java SDK 运行以下命令安装 Milvus Java SDK。 Apache Maven &…...

云手机如何进行经纬度修改

云手机如何进行经纬度修改 云手机修改经纬度的方法因不同服务商和操作方式有所差异&#xff0c;以下是综合多个来源的常用方法及注意事项&#xff1a; 通过ADB命令注入GPS数据&#xff08;适用于技术用户&#xff09; 1.连接云手机 使用ADB工具连接云手机服务器&#xff0c;…...

牛客周赛 Round 82(思维、差分、树状数组、大根堆、前后缀、递归)

文章目录 牛客周赛 Round 82&#xff08;思维、差分、树状数组、大根堆、前后缀、递归&#xff09;A. 夹心饼干B. C. 食堂大作战&#xff08;思维&#xff09;D. 小苯的排列计数(差分、树状数组)E. 和和&#xff08;大根堆&#xff0c;前缀和&#xff09;F. 怎么写线性SPJ &…...

MQTT实现智能家居------2、写MQTT程序的思路

举个最简单的例子&#xff1a; 手机------服务器-------家具 我们这里只看手机和家具的客户端&#xff1a; 手机&#xff1a;1&#xff09;需要连接服务器 2&#xff09;需要发布指令给服务器到家里的家具 3&#xff09;接受来自于家里家具的异常状况 4&#xff09;保持心…...

大模型面试问题准备

1. BERT的多头注意力为什么需要多头&#xff1f; 为了捕捉不同子空间的语义信息&#xff0c;每个头关注不同的方面&#xff0c;增强模型的表达能力 2. 什么是softmax上下溢出问题&#xff1f; 问题描述&#xff1a; 上溢出&#xff1a;ye^x中&#xff0c;如果x取非常大的正数…...

C语言:二维数组在内存中是怎么存储的

目录 1. 二维数组的定义&#xff1a; 2. 行主序存储&#xff1a; 具体内存排列&#xff1a; 3. 如何通过指针访问数据&#xff1a; 4. 总结&#xff1a; 在 C 语言中&#xff0c;二维数组是按 行主序&#xff08;row-major order&#xff09; 存储的。也就是说&#xff0c…...

AI时代前端开发技能变革与ScriptEcho:拥抱AI,提升效率

在飞速发展的科技浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着各个行业&#xff0c;前端开发领域也不例外。曾经被认为是核心竞争力的传统前端技能&#xff0c;例如精通HTML、CSS和JavaScript&#xff0c;其价值正在发生微妙的变化。 得益于…...

计算机毕业设计SpringBoot+Vue.js美容院管理系统(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

【LeetCodehHot100_0x01】

LeetCodeHot100_0x01 1. 两数之和 解题思路&#xff1a; 暴力枚举法、哈希法 【暴力枚举】 class Solution {public int[] twoSum(int[] nums, int target) {int n nums.length;for(int i0;i<n;i) {for(int ji1;j<n;j) {if(nums[i] nums[j] target) {return new in…...

Qt::MouseButtons解析

一 问题 今天想自定定义一个QMouseEvent变量,变量的的初始化参数有Qt::MouseButtons,这是个啥?查看类型为QFlags<Qt::MouseButton>。 二 Qt::MouseButton Qt::MouseButton 是 Qt 框架中定义的一个枚举类型(enum),用于表示鼠标事件中的物理按钮。它是 Qt 事件处理…...

跨域问题解释及前后端解决方案(SpringBoot)

一、问题引出 有时,控制台出现如下问题。 二、为什么会有跨域 2.1浏览器同源策略 浏览器的同源策略 &#xff08; Same-origin policy &#xff09;是一种重要的安全机制&#xff0c;用于限制一个源&#xff08; origin &#xff09;的文档或 脚本如何与另一个源的资源进行…...

4-知识图谱的抽取与构建-4_2实体识别与分类

&#x1f31f; 知识图谱的实体识别与分类&#x1f525; &#x1f50d; 什么是实体识别与分类&#xff1f; 实体识别&#xff08;Entity Recognition&#xff09;是从文本中提取出具体的事物&#xff0c;如人名、地名、组织名等。分类&#xff08;Entity Classification&#x…...

腾讯云大模型知识引擎×DeepSeek赋能文旅

腾讯云大模型知识引擎DeepSeek赋能文旅 ——以合肥文旅为例的技术革新与实践路径 一、技术底座&#xff1a;知识引擎与DeepSeek的融合逻辑 腾讯云大模型知识引擎与DeepSeek模型的结合&#xff0c;本质上是**“知识库检索增强生成&#xff08;RAG&#xff09;实时联网能力”**…...

TMDS视频编解码算法

因为使用的是DDR进行传输&#xff0c;即双倍频率采样&#xff0c;故时钟只用是并行数据数据的5倍&#xff0c;而不是10倍。 TMDS算法流程&#xff1a; 视频编码TMDS算法流程实现&#xff1a; timescale 1 ps / 1ps //DVI编码通常用于视频传输&#xff0c;将并行数据转换为适合…...

C++程序员内功修炼——Linux C/C++编程技术汇总

在软件开发的宏大版图中&#xff0c;C 语言宛如一座巍峨的高山&#xff0c;吸引着无数开发者攀登探索。而 Linux 操作系统&#xff0c;以其开源、稳定、高效的特性&#xff0c;成为了众多开发者钟爱的开发平台。将 C 与 Linux 相结合&#xff0c;就如同为开发者配备了一把无坚不…...

【数据结构】链表中快指针和慢指针

目录 一、找出并返回链表的中间结点 二、输出链表中倒数第k个结点 三、判断链表中是否有环 四、两个单链表相交 一、找出并返回链表的中间结点 给你单链表的头结点 head ,请你找出并返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。 要求&#xff1a;只遍历…...

6_zookeeper集群配置

配置 一、配置myid文件 # 进入解压好的文件夹下面 touch myid vim myid # master节点写0&#xff0c;slave1节点写1&#xff0c;slave2节点写2二、配置zoo.cfg文件 1.在master节点编辑zookeeper配置文件 # 进入解压好的文件夹下面 cd conf/ cp zoo_sample.cfg zoo.cfg vim …...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...