795. 区间子数组个数

这篇博客讨论了一个算法问题,即在给定整数数组中找到连续子数组,其最大元素位于特定范围内。提供的代码实现通过维护两个指针last1和last2来跟踪符合条件的子数组起始位置,从而计算出满足条件的子数组数量。该算法适用于解决数组和区间查询的问题。

给你一个整数数组 nums 和两个整数:left 及 right 。找出 nums 中连续、非空且其中最大元素在范围 [left, right] 内的子数组,并返回满足条件的子数组的个数。

生成的测试用例保证结果符合 32-bit 整数范围。

func numSubarrayBoundedMax(nums []int, left int, right int) (res int) {
    last2, last1 := -1, -1
    for i, x := range nums {
        if left <= x && x <= right {
            last1 = i
        } else if x > right {
            last2 = i
            last1 = -1
        }
        if last1 != -1 {
            res += last1 - last2
        }
    }
    return
}

你列出的这些关于数组的知识点整体上 **非常实用且贴近编程实践**,尤其是在 C 语言或算法竞赛中。我们来 **逐条详细解释、纠正细微错误、补充原理说明,并给出代码示例和最佳实践建议**。 --- ## ✅ 7.1 定义 n 长数组下标从 0 到 n-1;不允许定义以变量为长度的数组 ### ✔️ 解释: - 数组索引总是从 `0` 开始,长度为 `n` 的数组合法下标是:`0 ~ n-1` - 关于“不能用变量定义数组长度”——这在 **C90 标准下成立**,但在 **C99 及以后支持变长数组(VLA)** ### 🧪 示例: ```c int n = 10; int arr[n]; // C99 起合法!但不推荐用于大数组 ``` ### ⚠️ 注意事项: - VLA 在栈上分配,过大可能导致 **栈溢出** - 不支持初始化:`int arr[n] = {0};` ❌ 错误 - C++ 不支持 VLA(除非编译器扩展) ### ✅ 推荐做法: ```c // 动态分配(更安全) int *arr = malloc(n * sizeof(int)); ... free(arr); ``` > 🔔 总结:现代 C 支持 VLA,但**建议仅用于小规模临时数组** --- ## ✅ 7.2 较大数组需放 main 外(建议统一外置);会自动初始化为全零数组 ### ✔️ 正确! 全局数组(或静态局部数组)存储在 `.bss` 或 `.data` 段,**默认初始化为 0** ### 🧪 示例: ```c #include <stdio.h> int global_arr[100000]; // 全局 → 自动清零 int main() { printf("%d\n", global_arr[0]); // 输出 0 return 0; } ``` ### ❌ 局部大数组风险: ```c void bad() { int arr[100000]; // 放在栈上 → 可能栈溢出(常见于递归/嵌套调用) } ``` > 💡 建议:超过 `10^4` 级别的数组应声明为全局 --- ## ✅ 7.3 数组初始化需要注意初始化数值对后续输入数据的影响 ### ✔️ 正确!非常重要! 如果数组被部分初始化或未清空,旧值会影响逻辑判断。 ### 🧪 示例:频率统计错误 ```c int freq[26]; // freq 未初始化 → 垃圾值 freq[c - 'a']++; // ❌ 可能从非零开始计数 ``` ### ✅ 正确做法: ```c int freq[26] = {0}; // 静态初始化全 0 // 或 memset(freq, 0, sizeof(freq)); // 运行时清零 ``` 📌 特别注意:函数内局部数组不会自动清零! --- ## ✅ 7.4 数组前后元素比较时,循环下标可自 i=1 开始,比较 [i-1] 和 [i] 防越界 ### ✔️ 经典防越界技巧! 避免访问 `arr[-1]` 或 `arr[n]` ### 🧪 示例:检测相邻重复 ```c for (int i = 1; i < n; i++) { if (arr[i] == arr[i-1]) { printf("发现重复\n"); } } ``` ✅ 安全:只访问 `0 ~ n-1` ❌ 错误写法: ```c for (int i = 0; i < n; i++) { if (arr[i] == arr[i+1]) { ... } // 最后一次访问 arr[n] → 越界! } ``` --- ## ✅ 7.5 数组在最值求解时需要直接预设其为 [0] 而非自设初始值 ### ✔️ 极其重要! 不要假设最小值是 `0` 或最大值是 `100`,应该用第一个元素作为初值。 ### 🧪 错误示例: ```c int max = 0; for (int i = 0; i < n; i++) { if (arr[i] > max) max = arr[i]; } // 如果所有数都 < 0 → max 仍为 0 → 错误! ``` ### ✅ 正确写法: ```c int max = arr[0]; for (int i = 1; i < n; i++) { if (arr[i] > max) max = arr[i]; } ``` 📌 同理适用于最小值、最大差等场景 --- ## ✅ 7.6 数组在频率统计等问题中涉及到数组嵌套,注意含义解析 ### ✔️ 正确!典型如“桶套桶”、“映射链” ### 🧪 示例:统计每个数字出现次数 → 再统计频次的频次 ```c int freq[1001] = {0}; // freq[x] = x 出现的次数 int count[1001] = {0}; // count[k] = 有 k 次出现的数字有几个 // 输入处理 for (int i = 0; i < n; i++) { freq[arr[i]]++; } // 统计频次分布 for (int i = 1; i <= 1000; i++) { if (freq[i] > 0) { count[freq[i]]++; } } ``` 📌 关键:明确每一维的意义! --- ## ✅ 7.7 数组可用于记忆化 / 判断是否已操作 ### ✔️ 是动态规划与搜索优化的核心思想 ### 🧪 示例:记忆化递归(斐波那契) ```c int memo[1000] = {0}; int fib(int n) { if (n <= 1) return n; if (memo[n]) return memo[n]; return memo[n] = fib(n-1) + fib(n-2); } ``` ### 🧪 示例:标记访问(DFS) ```c int visited[1000] = {0}; void dfs(int u) { visited[u] = 1; for (each neighbor v) { if (!visited[v]) dfs(v); } } ``` --- ## ✅ 7.8 数组的前缀和 / 差项的应用 ### ✔️ 高频优化技巧! #### 前缀和(Prefix Sum): ```c int prefix[n+1]; prefix[0] = 0; for (int i = 1; i <= n; i++) { prefix[i] = prefix[i-1] + arr[i-1]; } // 查询区间 [l, r] 和:prefix[r+1] - prefix[l] ``` #### 差分(Difference Array): ```c int diff[n+1] = {0}; // 区间 [l, r] 加 d: diff[l] += d; diff[r+1] -= d; // 还原原数组: for (int i = 1; i < n; i++) { diff[i] += diff[i-1]; } ``` 📌 应用:频繁区间增减、子数组和统计 --- ## ✅ 7.9 二维数组定义时第一维可缺省,内存会自动分配 ### ✔️ 正确!适用于函数参数和初始化 ### 🧪 示例: ```c void print(int arr[][3], int rows) { // 第二维必须指定 for (int i = 0; i < rows; i++) for (int j = 0; j < 3; j++) printf("%d ", arr[i][j]); } int main() { int a[][3] = {{1,2,3}, {4,5,6}}; // 第一维缺省 → 编译器推断为 2 print(a, 2); } ``` > ⚠️ 第二维不可缺省!因为编译器需要知道每行多长才能计算偏移 --- ## ✅ 7.10 二维数组字符串列表,调用一维 A[i] 即调用 i 对应字符串 ### ✔️ 正确!常用于字符串表 ```c char days[7][10] = { "Monday", "Tuesday", ... }; printf("%s\n", days[0]); // 输出 Monday ``` > `days[i]` 是第 `i` 个字符串首地址(字符指针) --- ## ✅ 7.11 二维数组可用于矩阵 / 坐标 / 图案 / 字符串或状态列表等 ### ✔️ 典型应用场景: | 用途 | 示例 | |------|------| | 矩阵运算 | `int mat[3][3];` 表示 3x3 矩阵 | | 地图模拟 | `char grid[100][100];` 表示迷宫 | | 状态表 | `int state[10][2];` 表示有限状态机 | | 字符串集合 | `char names[50][20];` 存 50 个名字 | --- ## ✅ 7.12 任意维数组都可划归为一维数组,都可用指针代替 ### ✔️ 正确!内存本质是线性的 #### 三维转一维公式: ```c // arr[i][j][k] in [D1][D2][D3] int index = i * D2 * D3 + j * D3 + k; ``` #### 示例: ```c int flat[2*3*4]; int (*cube)[3][4] = (int (*)[3][4])flat; cube[1][2][3] = 100; // 实际访问 flat[1*12 + 2*4 + 3] = flat[23] ``` 📌 指针替代:`*(arr + i)` 等价于 `arr[i]` --- ## ✅ 7.13 `sizeof()` 返回数组总字节数;`strlen()` 遇 `\0` 记其前元素数 ### ✔️ 完全正确! 再强调一遍: ```c char s[] = "hello"; printf("sizeof = %zu\n", sizeof(s)); // 6(含 \0) printf("strlen = %zu\n", strlen(s)); // 5(不含 \0) ``` > `strlen` 是运行时函数,`sizeof` 是编译期操作符 --- ## ✅ 总结表格:关键对比 | 特性 | `sizeof(arr)` | `strlen(str)` | |------|----------------|----------------| | 类型 | 操作符 | 函数 | | 所属 | 语言内置 | `<string.h>` | | 返回 | 字节数 | 字符数(不含 `\0`) | | 是否包含 `\0` | 是 | 否 | | 是否依赖内容 | 否 | 是(找 `\0`) | | 对指针行为 | 返回指针大小 | 正常工作(若指向有效字符串) | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值