【线段树】2276. 统计区间中的整数数目

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 【线段树】2276. 统计区间中的整数数目

算法可以发掘本质,如:

一,若干师傅和徒弟互有好感,有好感的师徒可以结对学习。师傅和徒弟都只能参加一个对子。如何让对子最多。

二,有无限多1X2和2X1的骨牌,某个棋盘若干格子坏了,如何在没有坏的格子放足够多骨牌。

三,某个单色图,1表示前前景,0表示后景色。每次操作可以将一个1,变成0。如何在最少得操作情况下,使得没有两个1相邻(四连通)。

四,若干路人,有些人是熟人,如何选出最多的人参加实验。为了避免熟人影响实验的效果,参加的人不能是熟人。

一二是二分图的最大匹配,三是二分图的最小点覆盖,四是二分图最大独立集。 而这三者是等效问题。

本文涉及知识点

线段树

LeetCode2276. 统计区间中的整数数目

给你区间的 空 集,请你设计并实现满足要求的数据结构

新增:添加一个区间到这个区间集合中。

统计:计算出现在 至少一个 区间中的整数个数。

实现 CountIntervals 类:

CountIntervals() 使用区间的空集初始化对象

void add(int left, int right) 添加区间 [left, right] 到区间集合之中。

int count() 返回出现在 至少一个 区间中的整数个数。

注意:区间 [left, right] 表示满足 left <= x <= right 的所有整数 x 。

示例 1:

输入

[“CountIntervals”, “add”, “add”, “count”, “add”, “count”]

[[], [2, 3], [7, 10], [], [5, 8], []]

输出

[null, null, null, 6, null, 8]

解释

CountIntervals countIntervals = new CountIntervals(); // 用一个区间空集初始化对象

countIntervals.add(2, 3); // 将 [2, 3] 添加到区间集合中

countIntervals.add(7, 10); // 将 [7, 10] 添加到区间集合中

countIntervals.count(); // 返回 6

// 整数 2 和 3 出现在区间 [2, 3] 中

// 整数 7、8、9、10 出现在区间 [7, 10] 中

countIntervals.add(5, 8); // 将 [5, 8] 添加到区间集合中

countIntervals.count(); // 返回 8

// 整数 2 和 3 出现在区间 [2, 3] 中

// 整数 5 和 6 出现在区间 [5, 8] 中

// 整数 7 和 8 出现在区间 [5, 8] 和区间 [7, 10] 中

// 整数 9 和 10 出现在区间 [7, 10] 中

提示:

1 <= left <= right <= 109

最多调用 add 和 count 方法 总计 105

调用 count 方法至少一次

线段树

由于本题无法离散化,所以用数组内存必定会超,只能用哈希映射或二叉树动态开点,二叉树的性能略好,就用二叉树。就算用二叉树也刚刚能过。

区间修改,查询全部。

由于只用到了查询全部,所以查询可以不实现。

save 记录 整个区间 在题目所在区间的数量。

叶子save 的值为1,表示在至少一个区间。0,表示不在任何区间。

template<class TSave=int, class TRecord =int >
class CMyTreeRangeLineTree : public CTreeRangeLineTree<TSave, TRecord>
{ 
public:
  using CTreeRangeLineTree<TSave, TRecord>::CTreeRangeLineTree;
protected:
  virtual void OnQuery(TSave& save) override
  {
  }
  virtual void OnUpdate(TSave& save, int iSaveLeft, int iSaveRight, const TRecord& update) override
  { 
    save = update*(iSaveRight-iSaveLeft+1);
  }
  virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) override
  {
    par = left + r;
  }
  virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override
  {
    old = newRecord;
  }
};

本题只会更新1,如果需要考虑取消,也就是更新为0。需要将懒标记的默认值设置成-1。

CountIntervals():m_treeLine(1,1’000’000’000,0,-1){

}

代码

template<class TSave, class TRecord >
class CRangUpdateLineTree 
{
protected:
  virtual void OnQuery(TSave& save) = 0;
  virtual void OnUpdate(TSave& save, int iSaveLeft,int iSaveRight, const TRecord& update) = 0;
  virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) = 0;
  virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) = 0;
};
template<class TSave, class TRecord >
class CTreeRangeLineTree : public CRangUpdateLineTree<TSave, TRecord>
{
protected:
  struct CTreeNode
  {
    int Cnt()const { return m_iMaxIndex - m_iMinIndex + 1; }
    int m_iMinIndex;
    int m_iMaxIndex;
    TRecord record;
    TSave data;
    CTreeNode* m_lChild = nullptr, * m_rChild = nullptr;
  };
  CTreeNode* m_root;
  TSave m_tDefault;
  TRecord m_tRecordDef;
public:
  CTreeRangeLineTree(int iMinIndex, int iMaxIndex, TSave tDefault,TRecord tRecordDef) {
    m_tDefault = tDefault;
    m_tRecordDef = tRecordDef;
    m_root = CreateNode(iMinIndex, iMaxIndex);
  }
  void Update(int iLeftIndex, int iRightIndex, TRecord value)
  {
    Update(m_root, iLeftIndex, iRightIndex, value);
  }
  TSave QueryAll() {
    return m_root->data;
  }
  void Query(int leftIndex, int leftRight) {
    Query(m_root, leftIndex, leftRight);
  }
protected:
  void Query(CTreeNode* node, int iQueryLeft, int iQueryRight) {
    if ((node->m_iMinIndex >= iQueryLeft) && (node->m_iMaxIndex <= iQueryRight)) {
      this->OnQuery(node->data);
      return;
    }
    if (1 == node->Cnt()) {//没有子节点
      return;
    }
    CreateChilds(node);
    Fresh(node);
    const int mid = node->m_iMinIndex + (node->m_iMaxIndex - node->m_iMinIndex) / 2;
    if (mid >= iQueryLeft) {
      Query(node->m_lChild, iQueryLeft, iQueryRight);
    }
    if (mid + 1 <= iQueryRight) {
      Query(node->m_rChild, iQueryLeft, iQueryRight);
    }
  }
  void Update(CTreeNode* node, int iOpeLeft, int iOpeRight, TRecord value)
  {
    const int& iSaveLeft = node->m_iMinIndex;
    const int& iSaveRight = node->m_iMaxIndex;
    if ((iOpeLeft <= iSaveLeft) && (iOpeRight >= iSaveRight))
    {
      this->OnUpdate(node->data, iSaveLeft, iSaveRight, value);
      this->OnUpdateRecord(node->record, value);
      return;
    }
    if (1 == node->Cnt()) {//没有子节点
      return;
    }
    CreateChilds(node);
    Fresh(node);
    const int mid = node->m_iMinIndex + (node->m_iMaxIndex - node->m_iMinIndex) / 2;
    if (mid >= iOpeLeft) {
      this->Update(node->m_lChild, iOpeLeft, iOpeRight, value);
    }
    if (mid + 1 <= iOpeRight) {
      this->Update(node->m_rChild, iOpeLeft, iOpeRight, value);
    }
    // 如果有后代,至少两个后代
    this->OnUpdateParent(node->data, node->m_lChild->data, node->m_rChild->data,node->m_iMinIndex,node->m_iMaxIndex);
  }
  void CreateChilds(CTreeNode* node) {
    if (nullptr != node->m_lChild) { return; }
    const int iSaveLeft = node->m_iMinIndex;
    const int iSaveRight = node->m_iMaxIndex;
    const int mid = iSaveLeft + (iSaveRight - iSaveLeft) / 2;
    node->m_lChild = CreateNode(iSaveLeft, mid);
    node->m_rChild = CreateNode(mid + 1, iSaveRight);
  }
  CTreeNode* CreateNode(int iMinIndex, int iMaxIndex) {
    CTreeNode* node = new CTreeNode;
    node->m_iMinIndex = iMinIndex;
    node->m_iMaxIndex = iMaxIndex;
    node->data = m_tDefault;
    node->record = m_tRecordDef;
    return node;
  }
  void Fresh(CTreeNode* node)
  {
    if (m_tRecordDef == node->record)
    {
      return;
    }
    CreateChilds(node);
    Update(node->m_lChild, node->m_lChild->m_iMinIndex, node->m_lChild->m_iMaxIndex, node->record);
    Update(node->m_rChild, node->m_rChild->m_iMinIndex, node->m_rChild->m_iMaxIndex, node->record);
    node->record = m_tRecordDef;
  }
};
template<class TSave=int, class TRecord =int >
class CMyTreeRangeLineTree : public CTreeRangeLineTree<TSave, TRecord>
{ 
public:
  using CTreeRangeLineTree<TSave, TRecord>::CTreeRangeLineTree;
protected:
  virtual void OnQuery(TSave& save) override
  {
  }
  virtual void OnUpdate(TSave& save, int iSaveLeft, int iSaveRight, const TRecord& update) override
  { 
    save = update*(iSaveRight-iSaveLeft+1);
  }
  virtual void OnUpdateParent(TSave& par, const TSave& left, const TSave& r, int iSaveLeft, int iSaveRight) override
  {
    par = left + r;
  }
  virtual void OnUpdateRecord(TRecord& old, const TRecord& newRecord) override
  {
    old = newRecord;
  }
};
class CountIntervals {
public:
  CountIntervals():m_treeLine(1,1'000'000'000,0,0){
  }
  void add(int left, int right) {
    m_treeLine.Update(left, right,1);
  }
  int count() {
    return m_treeLine.QueryAll();
  }
  CMyTreeRangeLineTree<> m_treeLine;
};

测试用例

CountIntervals countIntervals ; // 用一个区间空集初始化对象
  countIntervals.add(2, 3);  // 将 [2, 3] 添加到区间集合中
  countIntervals.add(7, 10); // 将 [7, 10] 添加到区间集合中
  auto res = countIntervals.count();    // 返回 6
                 // 整数 2 和 3 出现在区间 [2, 3] 中
                 // 整数 7、8、9、10 出现在区间 [7, 10] 中
  Assert(6, res);
  countIntervals.add(5, 8);  // 将 [5, 8] 添加到区间集合中
   res = countIntervals.count();    // 返回 8
                 // 整数 2 和 3 出现在区间 [2, 3] 中
                 // 整数 5 和 6 出现在区间 [5, 8] 中
                 // 整数 7 和 8 出现在区间 [5, 8] 和区间 [7, 10] 中
                 // 整数 9 和 10 出现在区间 [7, 10] 中
   Assert(8, res);

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。

https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程

https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

如无特殊说明,本算法用**C++**实现。

相关文章
|
4月前
|
人工智能 自然语言处理 算法
知识库是AI智能客服的核心根基
知识库是AI客服的“大脑”,决定问答准确率与服务价值。涵盖标准化梳理、NLP意图识别、相似问法智能匹配及人工闭环优化,支持模糊提问、多轮对话与动态进化。知识库越扎实,AI服务越精准高效。
|
3月前
|
存储 关系型数据库 API
向量数据库对比:Pinecone、Chroma、Weaviate 的架构与适用场景
本文对比Pinecone(生产级)、Chroma(轻量原型)和Weaviate(混合搜索)三大向量数据库,结合真实生产经验与可运行Python代码,详解其适用场景、部署要点与迁移策略,助你为RAG系统选对检索引擎。
412 3
向量数据库对比:Pinecone、Chroma、Weaviate 的架构与适用场景
|
12月前
|
弹性计算 人工智能 运维
云上十五年——弹性计算系列客户故事(一)
阿里云特别推出《云上十五年》客户故事专栏,分享与各行业伙伴的实践成果。
|
安全 网络安全 网络虚拟化
路由器与交换机:Access/Trunk,Wan/Lan
路由器与交换机:Access/Trunk,Wan/Lan
2135 0
|
SQL 开发框架 算法
【读书笔记】《Effective C#》50条建议笔记整理
对《Effective C#:改善C#代码的50个有效方法》一书整理的读书笔记。
28619 5
【读书笔记】《Effective C#》50条建议笔记整理
|
设计模式 消息中间件 缓存
DDD系列第五讲:聊聊如何避免写流水账代码
在过去一年里我们团队做了大量的老系统重构和迁移,其中有大量的代码属于流水账代码,通常能看到是开发在对外的API接口里直接写业务逻辑代码,或者在一个服务里大量的堆接口,导致业务逻辑实际无法收敛,接口复用性比较差。所以这讲主要想系统性的解释一下如何通过DDD的重构,将原有的流水账代码改造为逻辑清晰、职责分明的模块。
11689 3
DDD系列第五讲:聊聊如何避免写流水账代码
|
消息中间件 负载均衡 监控
RocketMQ 5.0 客户端负载均衡机制|学习笔记
快速学习 RocketMQ 5.0 客户端负载均衡机制
599 0
RocketMQ 5.0 客户端负载均衡机制|学习笔记
|
Linux C语言 C++
【Linux】基础IO(万字详解) —— 系统文件IO | 文件描述符fd | 重定向原理(上)
【Linux】基础IO(万字详解) —— 系统文件IO | 文件描述符fd | 重定向原理(上)
686 0
【Linux】基础IO(万字详解) —— 系统文件IO | 文件描述符fd | 重定向原理(上)
|
人工智能 运维 监控
【工业大脑】AICS
AICS在传统控制优化基础上,叠加海量实时数据计算及人工智能算法能 力。在全局优化层面,可根据当前原料消耗,产品质量,装置运行状态, 工况等使全局生产过程在最经济最有效的条件下运行;在装置控制层面, 可根据装置的操作限制,最大化装置处理量,减少波动,稳定运行,并支 持多装置系统组合控制。同时,AICS云边协同的远程部署和运维能力能 够有效解决现有APC领域所面临的控制效果不能长久持续的普遍问题。
 【工业大脑】AICS
|
消息中间件 Kafka Apache
超详细,Windows系统搭建Flink官方练习环境
如何快速的投入到Flink的学习当中,很多人在搭建环境过程中浪费了太多的时间。一套一劳永逸的本机Flink开发环境可以让我们快速的投入到Flink的学习中去,将精力用在Flink的原理,实战。这也对于工作和面试有着巨大帮助。 本文将利用Flink的官方练习环境,在本地Windows系统中快速的搭建Flink环境,并详细的记录整个搭建过程。
2671 0
超详细,Windows系统搭建Flink官方练习环境