「面试高频题」可拓展变形的「区间求和」经典题

8.23拼多多笔试真-护栏补强(C++/Py/Java /Js/Go) 拼多多笔试 8月23号真,拼多多笔试第二【护栏补强】,提供详细思路讲解以及Java、C++、Python、Go、JavaScript多语言代码实现。拼多多技术岗笔试真【护栏补强】多语言解。 阅读详情

image.png

题目描述
这是 LeetCode 上的 1893. 检查是否区域内所有整数都被覆盖 ,难度为 简单。

Tag : 「模拟」、「树状数组」、「线段树」

给你一个二维整数数组 ranges 和两个整数 left 和 right。每个 ranges[i]=[starti,endi]ranges[i] = [start_i, end_i]ranges[i]=[starti,endi] 表示一个从 startistart_istarti 到 endiend_iendi 的 闭区间 。

如果闭区间 [left,right][left, right][left,right] 内每个整数都被 ranges 中 至少一个 区间覆盖,那么请你返回 true,否则返回 false。

已知区间 ranges[i]=[starti,endi]ranges[i] = [start_i, end_i]ranges[i]=[starti,endi],如果整数 x 满足 starti<=x<=endistart_i <= x <= end_istarti<=x<=endi,那么我们称整数 x 被覆盖了。

示例 1:

输入:ranges = [[1,2],[3,4],[5,6]], left = 2, right = 5输出:true解释:2 到 5 的每个整数都被覆盖了:- 2 被第一个区间覆盖。- 3 和 4 被第二个区间覆盖。- 5 被第三个区间覆盖。复制代码
示例 2:

输入:ranges = [[1,10],[10,20]], left = 21, right = 21输出:false解释:21 没有被任何一个区间覆盖。复制代码
提示:

1<=ranges.length<=501 <= ranges.length <= 501<=ranges.length<=50
1<=starti<=endi<=501 <= start_i <= end_i <= 501<=starti<=endi<=50
1<=left<=right<=501 <= left <= right <= 501<=left<=right<=50
模拟
一个简单的想法是根据题意进行模拟,检查 [left,right][left, right][left,right] 中的每个整数,如果检查过程中发现某个整数没被 rangesrangesranges 中的闭区间所覆盖,那么直接返回 False,所有数值通过检查则返回 True。

代码:

class Solution {
public boolean isCovered(int[][] rs, int l, int r)
{
for (int i = l; i <= r; i++)
{
boolean ok = false;
for (int[] cur : rs)
{
int a = cur[0], b = cur[1];
if (a <= i && i <= b)
{
ok = true;
break;
}
}
if (!ok) return false;
}
return true;
}
}

时间复杂度:令 [left,right][left, right][left,right] 之间整数数量为 nnn,rangesrangesranges 长度为 mmm。整体复杂度为 O(n∗m)O(n * m)O(n∗m)
空间复杂度:O(1)O(1)O(1)
树状数组
针对此题,可以有一个很有意思的拓展,将本题难度提升到【中等】甚至是【困难】。

将查询 [left,right][left, right][left,right] 修改为「四元查询数组」querysquerysquerys,每个 querys[i]querys[i]querys[i] 包含四个指标 (a,b,l,r)(a,b,l,r)(a,b,l,r):代表询问 [l,r][l, r][l,r] 中的每个数是否在 rangerangerange 中 [a,b][a, b][a,b] 的闭区间所覆盖过。

如果进行这样的拓展的话,那么我们需要使用「持久化树状数组」或者「主席树」来配合「容斥原理」来做。

基本思想都是使用 range[0,b]range[0,b]range[0,b] 的计数情况减去 range[0,a−1]range[0, a-1]range[0,a−1] 的计数情况来得出 [a,b][a, b][a,b] 的计数情况。

回到本题,由于数据范围很小,只有 505050,我们可以使用「树状数组」进行求解:

void add(int x, int u):对于数值 xxx 出现次数进行 +u+u+u 操作;
int query(int x):查询某个满足 <=x<= x<=x 的数值的个数。
那么显然,如果我们需要查询一个数值 xxx 是否出现过,可以通过查询 cnt=query(x)−query(x−1)cnt = query(x) - query(x - 1)cnt=query(x)−query(x−1) 来得知。

代码:

class Solution
{
int n = 55;
int[] tr = new int[n];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int u)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += u;
}
int query(int x)
{
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) ans += tr[i];
return ans;
}
public boolean isCovered(int[][] rs, int l, int r)
{
for (int[] cur : rs)
{
int a = cur[0], b = cur[1];
for (int i = a; i <= b; i++)
{
add(i, 1);
}
}
for (int i = l; i <= r; i++)
{
int cnt = query(i) - query(i - 1);
if (cnt == 0)
return false;
}
return true;
}
}

时间复杂度:令 [left,right][left, right][left,right] 之间整数数量为 nnn,∑i=0range.legth−1ranges[i].length\sum_{i = 0}^{range.legth - 1}ranges[i].length∑i=0range.legth−1ranges[i].length 为 sumsumsum,常数 CCC 固定为 555555。建树复杂度为 O(sumlog⁡C)O(sum\log C)O(sumlogC),查询查询复杂度为 O(nlog⁡C)O(n\log{C})O(nlogC)。整体复杂度为 O(sumlog⁡C+nlog⁡C)O(sum\log{C} + n\log{C})O(sumlogC+nlogC)
空间复杂度:O©O©O©
树状数组(去重优化)
在朴素的「树状数组」解法中,我们无法直接查询 [l,r][l, r][l,r] 区间中被覆盖过的个数的根本原因是「某个值可能会被重复添加到树状数组中」。

因此,一种更加优秀的做法:在往树状数组中添数的时候进行去重,然后通过 cnt=query®−query(l−1)cnt = query® - query(l - 1)cnt=query®−query(l−1) 直接得出 [l,r][l, r][l,r] 范围内有多少个数被添加过。

这样的 Set 去重操作可以使得我们查询的复杂度从 O(nlog⁡C)O(n\log{C})O(nlogC) 下降到 O(log⁡C)O(\log{C})O(logC)。

由于数值范围很小,自然也能够使用数组来代替 Set 进行标记(见 P2)

代码:

class Solution
{
int n = 55;
int[] tr = new int[n];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int u)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += u; }
int query(int x) {
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) ans += tr[i];
return ans;
}
public boolean isCovered(int[][] rs, int l, int r)
{
Set set = new HashSet<>();
for (int[] cur : rs)
{
int a = cur[0], b = cur[1];
for (int i = a; i <= b; i++)
{
if (!set.contains(i))
{
add(i, 1);
set.add(i);
}
}
}
int tot = r - l + 1, cnt = query® - query(l - 1);
return tot == cnt;
}
}

class Solution {
int n = 55;
int[] tr = new int[n];
boolean[] vis = new boolean[n];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int u)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += u;
}
int query(int x)
{
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) ans += tr[i];
return ans;
}
public boolean isCovered(int[][] rs, int l, int r)
{
for (int[] cur : rs)
{
int a = cur[0], b = cur[1];
for (int i = a; i <= b; i++)
{
if (!vis[i]) {
add(i, 1);
vis[i] = true;
}
}
}
int tot = r - l + 1, cnt = query® - query(l - 1);
return tot == cnt;
}
}

时间复杂度:令 [left,right][left, right][left,right] 之间整数数量为 nnn,∑i=0range.legth−1ranges[i].length\sum_{i = 0}^{range.legth - 1}ranges[i].length∑i=0range.legth−1ranges[i].length 为 sumsumsum,常数 CCC 固定为 555555。建树复杂度为 O(sumlog⁡C)O(sum\log C)O(sumlogC),查询查询复杂度为 O(log⁡C)O(\log{C})O(logC)。整体复杂度为 O(sumlog⁡C+log⁡C)O(sum\log{C} + \log{C})O(sumlogC+logC)
空间复杂度:O(C+∑i=0range.legth−1ranges[i].length)O(C + \sum_{i = 0}^{range.legth - 1}ranges[i].length)O(C+∑i=0range.legth−1ranges[i].length)
线段树(不含“懒标记”)
更加进阶的做法是使用「线段树」来做,与「树状数组(优化)」解法一样,线段树配合持久化也可以用于求解「在线」问题。

与主要解决「单点修改 & 区间查询」的树状数组不同,线段树能够解决绝大多数「区间修改(区间修改/单点修改)& 区间查询」问题。

对于本题,由于数据范围只有 555555,因此我们可以使用与「树状数组(优化)」解法相同的思路,实现一个不包含“懒标记”的线段树来做(仅支持单点修改 & 区间查询)。

代码:

class Solution
{
// 代表 [l, r] 区间有 cnt 个数被覆盖
class Node
{
int l, r, cnt;
Node (int _l, int _r, int _cnt)
{
l = _l; r = _r; cnt = _cnt;
}
}
int N = 55;
Node[] tr = new Node[N * 4];
void pushup(int u)
{
tr[u].cnt = tr[u << 1].cnt + tr[u << 1 | 1].cnt;
}
void build(int u, int l, int r)
{
if (l == r) {
tr[u] = new Node(l, r, 0);
}
else
{
tr[u] = new Node(l, r, 0);
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
// 从 tr 数组的下标 u 开始,在数值 x 的位置进行标记
void update(int u, int x)
{
if (tr[u].l == x && tr[u].r == x)
{
tr[u].cnt = 1;
}
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) update(u << 1, x);
else update(u << 1 | 1, x);
pushup(u);
}
}
// 从 tr 数组的下标 u 开始,查询 [l,r] 范围内有多少个数值被标记
int query(int u, int l, int r)
{
if (l <= tr[u].l && tr[u].r <= r) return tr[u].cnt;
int mid = tr[u].l + tr[u].r >> 1;
int ans = 0;
if (l <= mid) ans += query(u << 1, l, r);
if (r > mid) ans += query(u << 1 | 1, l, r);
return ans;
}
public boolean isCovered(int[][] rs, int l, int r)
{
build(1, 1, N);
for (int[] cur : rs) {
int a = cur[0], b = cur[1];
for (int i = a; i <= b; i++)
{
update(1, i);
}
}
int tot = r - l + 1 , cnt = query(1, l, r);
return tot == cnt;
}
}

时间复杂度:令 [left,right][left, right][left,right] 之间整数数量为 nnn,∑i=0range.legth−1ranges[i].length\sum_{i = 0}^{range.legth - 1}ranges[i].length∑i=0range.legth−1ranges[i].length 为 sumsumsum,常数 CCC 固定为 555555。建树复杂度为 O(sumlog⁡C)O(sum\log C)O(sumlogC),查询查询复杂度为 O(log⁡C)O(\log{C})O(logC)。整体复杂度为 O(sumlog⁡C+log⁡C)O(sum\log{C} + \log{C})O(sumlogC+logC)
空间复杂度:O(C∗4)O(C * 4)O(C∗4)
最后
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

Freeswitch DTMF 按键无法接收问修复 freeswitch 在接收到 update后,会更新成新的 payload type,但是给的200响应,仍然是旧的,这时候就会导致手机侧认识需要发送旧的payload,所以发到 fs 后就会导致直接丢包,不去处理RTP包了。呼叫时需要设置通道变量 rtp_manual_rtp_bugs=ACCEPT_ANY_PAYLOAD ,这时候FS 就不会对包的payload进行判断,直接判定通过。应该修复 freeswitch 给响应时,应该根据收到的 payload 发送给手机,但是一直未找到正确的路径。 阅读详情

相关推荐

【Claude Code 源码解析教程】总体大纲

本教程旨在深入解析 Anthropic Claude Code 项目的源码架构实现细节。Claude Code 是一个基于 TypeScript + Bun 构建的 AI 编程助手 CLI 工具,代号 "Tengu",具有复杂的多模块架构丰富的功能特性。本教程基于Claude Code 的源码分析来生成,将帮助您全面掌握 Claude Code 的源码架构实现细节,为开发类似 AI 编程助手工具奠定坚实基础。

rengang66的博客 328

面试高频区间调度类问总结 interval scheduling

面试高频区间调度类问总结 interval scheduling

weixin_49376454的博客 939

前后端全部开源微信小程序商城(Java + uniapp)

减少重复造轮子,开源微信小程序商城(前后端开源:uniapp+Java)。快速搭建一个属于自己的微信小程序商城

面试必考字符串算法:3类高频+4套可直接复制代码,吃透轻松拿下前端算法手撕

本文总结了前端面试高频出现的字符串算法,重点讲解了回文字符串的判断及变形解法。主要内容包括:1)JS字符串底层原理,如包装类转换、toString与JSON.stringify区别等;2)基础回文判断的API写法双指针优化解法;3)进阶"最多删除1字符判断回文"的完整实现;4)字符串反转的标准写法及注意事项;5)面试总结模板。文章提供了可直接运行的代码示例,针对常见面试陷阱给出提醒,帮助读者掌握字符串算法的核心考点最佳实践。

2303_79125167的博客 400

7.面试算法-链表之高频面试(二)

通过上面的这些目,我们能感受到链表的目真是不少,但是大部分常规目,都是从增删改查变换或者组合而来的。这些目大部分一看就知道该怎么做,但是要写出来甚至运行成功,难度还是很大的,所以,我们需要耐住寂寞,认真练习,只有练会了才可能在考场上应对自如,这就是所谓的思维能力了。在上面这些目中,需要特别强调的就是反转相关的几个问必须都要会,因为这几个问的考察频率非常高,而且对链表的能力要也不低,必须好好掌握。

weixin_36401695的博客 1335

LeetCode高频100解析与面试算法突破

算法作为计算机科学的核心基础,其本质是通过特定步骤解决复杂问的计算方法。从数据结构操作到动态规划,算法通过时间与空间复杂度的优化实现效率提升。在工程实践中,掌握滑动窗口、前缀经典算法模板能显著提高代码性能,特别是在处理大规模数据时。根据大厂面试统计,数组/字符串操作占比43%,动态规划出现频率达31%,这些高频型常出现在亚马逊、谷歌等企业的技术面试中。本文系统梳理了LeetCode高频100的解框架,包含标准化代码模板、边界条件处理技巧以及时间复杂度优化方法,帮助开发者快速构建算法思维体系,有效

weixin_34262482的博客 421

后端高频面试 Love 6 刷合集(第二轮)

文章目录前引后端面试博客合集(持续更新) 前引 目前笔者刷 已经刷过了第一轮 但是对于一些没有第一眼就有思路的目的话 就需要反复刷 而且第一轮主要是针对腾讯的高频面试去刷的 这一轮把所有后端的高频面试都需要加进去 第一轮刷链接:腾讯后端高频面试 Love 6 刷合集(第一轮) 这里也像第一轮一样的 把目前自己的刷总览贴一张图片出来吧 目前还是有700+了 但我觉得主要还是需要去反复刷那些 自己第一眼没有思路的目 笔者目前已经很久没有刷过新的了 都是在反复的做那些自己做过的

Love 6's Private Blog 1944

Java 面试高频手撕清单

Java面试中常考的手撕目合集

Electric Sheep 568

【算法】【前缀】核心原理、大厂高频考点、三语言实现、面试手撕技巧

前缀算法解析与面试应用 前缀通过预处理数组实现O(1)区间查询,核心是构建preSum数组(preSum[i] = preSum[i-1] + nums[i-1])。适用于静态数据高频求和场景,复杂度:预处理O(n),查询O(1)。典型应用包括子数组(如LeetCode 560)及二维矩阵求和(LeetCode 304)。大厂常考变形如将平均数问转化为为0的最长子数组。提供Python/Java/C++三语言实现模板,并强调面试需注意边界条件(如preSum[0]=0)动态数据改用线段树。

学习代码 1076

00 告别盲目刷,击破算法面试

《数据结构与算法面试宝典》专栏作者大壮分享其从技术架构师到"面霸"的成长历程。基于多年大厂面试经验,该课程提炼出300+高频面试,独创"一解多"与"一多解"方法论,覆盖栈、队列、二叉树等数据结构及动态规划等算法核心考点。课程提供Java/C++/Python三语实现、思维导图及动画图解,帮助职者在一个月内系统备战算法面试,掌握大厂P7+级别要的解能力。作者特别强调算法能力是职业发展的关键门槛,并分享独家代码模板面试技巧,助力程序员突破

周壮的博客 1027

LeetCode经典150:算法面试通关指南

算法是计算机科学的核心基础,其本质是通过特定步骤解决问的方法论。从数据结构到算法设计,理解时间复杂度空间复杂度等核心概念是每个程序员的必修课。在技术面试场景中,LeetCode库已成为评估候选人算法能力的事实标准,其中经典150浓缩了数组、链表、动态规划等高频考点。掌握双指针、滑动窗口等解范式,配合三遍刷管理系统,能显著提升面试通过率。无论是准备校招还是社招,系统性地攻克这些代表性问,都是构建算法思维体系的有效路径。

weixin_30394981的博客 297

Linux服务器RocketMQRocketMQ Dashboard安装部署

rockermq安装

qq_39984729的博客 216

大数据 之 Snappy

【代码】大数据 之 Snappy。

zhixingheyi_tian的博客 165

商超智能运营如何落地?从系统架构到实战避坑的完整技术路径

3. **多端交互展示层**:需覆盖顾客使用的**小程序、APP及H5公众号**,以及员工使用的管理后台。答:在应用层引入**适配器模式**。在项目启动时,应强制要供应商或自研团队产出**部署文档**(含环境变量清单)**二次开发文档**(含核心流程时序图),确保后续维护不受限于个人。- **多租户插件**:MyBatis Plus的`TenantLineInnerInterceptor`可实现SQL层面的自动拼接`store_id`条件,防止开发者因SQL编写疏漏导致的数据越权。

weixin_56812938的博客 366

【手搓 Agent 第2.3关】搭建 Agent 进阶能力:工具注册中心架构重构

本篇优化了之前编写 Agent 时的杂乱硬编码,使用工具注册中心统一调度,促使后期增添工具更加容易。顺便优化了 RAG 知识库的懒加载,使 Agent 启动更快。

2502_92964924的博客 325

Java深入解析篇二十之JavaStream API详解

Stream(流)是引入的数据处理抽象,位于包。它表示从数据源产生的元素序列,并支持对其进行函数式、聚合式操作。不是集合:流不存储数据,只描述对数据的计算;不是 IO 流:与无关;惰性管道:中间操作只是登记,终端操作才触发实际计算。// 命令式写法(对照) // List<String> r = new ArrayList<>();

萧瑟余晖的博客 342

电商推返模式的技术架构与系统落地实践

成功的电商推返模式系统,本质是。

weixin_42804741的博客 172

深度解析:一个 Java 对象究竟占用多少字节?

System.out.println("========== 空对象 EmptyObj 内存布局 ==========");System.out.println("========== 基础类型对象 User 内存布局 ==========");System.out.println("========== 含引用对象 Order 内存布局 ==========");对象头12字节 + long(8B) + 引用(4B) = 24B,刚好是8的整数倍,无对齐填充,完美匹配计算规则。

这就是程序猿的博客 388

AI智慧商城小程序源码开发实战:从架构设计到上线全流程指南

本文基于真实开发经验,系统梳理 AI 智慧商城小程序从架构设计到上线的完整流程。技术栈采用 Spring Boot + MyBatis Plus + MySQL 构建后端,uniapp 实现多端用户端,Vue + Element UI 搭建管理后台。核心围绕四大 AI 能力展开:多路召回+重排序的智能推荐引擎、TTS+数字人驱动的商品讲解、基于 Function Calling 的智能客服与售前引导,以及文案生成、营销配置等智能运营工具。

weixin_60938478的博客 178

MYBATIS=PLUS批量插入数据提效

**原因**:如果开启事务,`saveBatch` 在执行完 `flush` 后,Spring 并不会立即提交事务,而是等待方法结束才 `commit`。- **优化**:如果业务允许部分失败(或无需强事务),**去掉**该方法上的 `@Transactional`,让 `saveBatch` 内部自动提交;* **优化建议**:如果业务允许,**去掉方法上的 `@Transactional`**,让 `saveBatch` 利用自动提交模式,或者使用编程式事务来精确控制边界。

datalover的专栏 368

SpringBoot4 云端咖啡站 阶段三:接口质量与工程化

本阶段让接口更健壮可观测:09 章用 Jakarta Validation(@NotNull/@Size/@Pattern + @Valid)在边界拦非法参数,再以 @RestControllerAdvice 全局异常处理器收口,统一返回 ApiError,区分业务/系统异常并理清回滚关系;10 章用 @Aspect 把日志、耗时等横切逻辑抽成独立切面(@Order 排序),借自定义 @AuditLog 注解精准埋点,并点明 JDK 代理与 CGLIB 底层差异。

桃李不言下自成蹊 368
上一篇: 什么人更适合Java编程?这5类人最适合
下一篇: 怎么高效学习Java进阶技能 java进阶反射的应用:动态代理
Netty711
博客等级 码龄4年 6粉丝 267原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值