【线段树 有序映射】715. Range 模块

简介: 【线段树 有序映射】715. Range 模块

算法可以发掘本质,如:

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

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

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

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

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

本文涉及知识点

线段树 有序映射

线段树:就求一乐乎,难写。在超时的边缘。

LeetCode715. Range 模块

Range模块是跟踪数字范围的模块。设计一个数据结构来跟踪表示为 半开区间 的范围并查询它们。

半开区间 [left, right) 表示所有 left <= x < right 的实数 x 。

实现 RangeModule 类:

RangeModule() 初始化数据结构的对象。

void addRange(int left, int right) 添加 半开区间 [left, right),跟踪该区间中的每个实数。添加与当前跟踪的数字部分重叠的区间时,应当添加在区间 [left, right) 中尚未跟踪的任何数字到该区间中。

boolean queryRange(int left, int right) 只有在当前正在跟踪区间 [left, right) 中的每一个实数时,才返回 true ,否则返回 false 。

void removeRange(int left, int right) 停止跟踪 半开区间 [left, right) 中当前正在跟踪的每个实数。

示例 1:

输入

[“RangeModule”, “addRange”, “removeRange”, “queryRange”, “queryRange”, “queryRange”]

[[], [10, 20], [14, 16], [10, 14], [13, 15], [16, 17]]

输出

[null, null, null, true, false, true]

解释

RangeModule rangeModule = new RangeModule();

rangeModule.addRange(10, 20);

rangeModule.removeRange(14, 16);

rangeModule.queryRange(10, 14); 返回 true (区间 [10, 14) 中的每个数都正在被跟踪)

rangeModule.queryRange(13, 15); 返回 false(未跟踪区间 [13, 15) 中像 14, 14.03, 14.17 这样的数字)

rangeModule.queryRange(16, 17); 返回 true (尽管执行了删除操作,区间 [16, 17) 中的数字 16 仍然会被跟踪)

提示:

1 <= left < right <= 109

在单个测试用例中,对 addRange 、 queryRange 和 removeRange 的调用总数不超过 104 次

代码

template<class TSave, class TRecord >
class CRangUpdateLineTree 
{
protected:
  virtual void OnQuery(TSave& save, int iSaveLeft, int iSaveRight) = 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,node->m_iMinIndex,node->m_iMaxIndex);
      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;
  bool queryRange(int left, int right) {
    m_bHas = true;
    CTreeRangeLineTree<TSave, TRecord>::Query(left, right);
    return m_bHas;
  }
protected:
  bool m_bHas = true;
  virtual void OnQuery(TSave& save, int iSaveLeft, int iSaveRight) override
  {
    m_bHas &= (save == (iSaveRight - iSaveLeft + 1));
  }
  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 RangeModule {
public:
  RangeModule() :m_treeLine(1, 1'000'000'000, 0, -1) {
  }
  void addRange(int left, int right) {
    m_treeLine.Update(left, right-1, 1);
  }
  bool queryRange(int left, int right) {
    return m_treeLine.queryRange(left, right - 1);
  }
  void removeRange(int left, int right) {
    m_treeLine.Update(left, right-1, 0);
  }
  CMyTreeRangeLineTree<> m_treeLine;
};

有序映射

class RangeModule {
public:
  RangeModule() {
  }
  void addRange(int left, int right) {
    auto it1 = m_mLeftRight.lower_bound(left);
    auto it2 = m_mLeftRight.upper_bound(right);
    if (it1 != m_mLeftRight.begin())
    {
      auto tmp = it1;
      --tmp;
      if (tmp->second >= left)
      {
        left = tmp->first;
        --it1;
      }
    }
    if (it2 != m_mLeftRight.begin())
    {
      auto tmp = it2;
      --tmp;
      if (tmp->second > right)
      {
        right = tmp->second;
      }
    }
    m_mLeftRight.erase(it1, it2);
    m_mLeftRight[left] = right;
  }
  bool queryRange(int left, int right) {
    auto it1 = m_mLeftRight.upper_bound(left);
    if (it1 != m_mLeftRight.begin())
    {
      auto tmp = it1;
      tmp--;
      return tmp->second >= right;
    }
    return false;
  }
  void removeRange(int left, int right) {
    auto it1 = m_mLeftRight.lower_bound(left);
    auto it2 = m_mLeftRight.upper_bound(right);
    int iNewLeft = -1;
    if (it1 != m_mLeftRight.begin())
    {
      auto tmp = it1;
      --tmp;
      if (tmp->second >= left)
      {
        iNewLeft = tmp->first;
      }
    }
    int iNewRight = -1;
    if (it2 != m_mLeftRight.begin())
    {
      auto tmp = it2;
      --tmp;
      if (tmp->second > right)
      {
        iNewRight = tmp->second;
      }
    }
    m_mLeftRight.erase(it1, it2);
    if (-1 != iNewLeft)
    {
      m_mLeftRight[iNewLeft] = left;
    }
    if (-1 != iNewRight)
    {
      m_mLeftRight[right] = iNewRight;
    }
  }
  std::map<int, int> m_mLeftRight;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

相关文章
|
5月前
|
JavaScript
JS 【详解】双指针排序 -- 数组合并后递增排序
JS 【详解】双指针排序 -- 数组合并后递增排序
36 0
|
7月前
|
JavaScript 前端开发 搜索推荐
js将一组数据从大到小排序方法
js将一组数据从大到小排序方法
187 0
|
Web App开发 算法 JavaScript
JS中数组随机排序实现(原地算法sort/shuffle算法)
JS中数组随机排序实现(原地算法sort/shuffle算法)
311 1
JS中数组随机排序实现(原地算法sort/shuffle算法)
|
算法 JavaScript
js如何实现数组随机排序【附洗牌算法心得】
js如何实现数组随机排序【附洗牌算法心得】
js如何实现数组随机排序【附洗牌算法心得】
|
存储 数据可视化 数据挖掘
【Numpy】数组的创建和常用函数(排序、连接、属性、reshape、索引和切片)
【Numpy】数组的创建和常用函数(排序、连接、属性、reshape、索引和切片)
206 0
【Numpy】数组的创建和常用函数(排序、连接、属性、reshape、索引和切片)
|
数据建模 程序员 Go
数组 for-range 遍历|学习笔记
快速学习数组 for-range 遍历。
181 0
数组 for-range 遍历|学习笔记
LeetCode 5340. 统计有序矩阵中的负数 Count Negative Numbers in a Sorted Matrix
LeetCode 5340. 统计有序矩阵中的负数 Count Negative Numbers in a Sorted Matrix
CF960F. Pathwalks (树上二维LIS 线段树 动态开点树状数组)
CF960F. Pathwalks (树上二维LIS 线段树 动态开点树状数组)
107 0
20天刷题计划-532. 数组中的 k-diff 数对
给定一个整数数组和一个整数 k,你需要在数组里找到 不同的 k-diff 数对,并返回不同的 k-diff 数对 的数目。 这里将 k-diff 数对定义为一个整数对 (nums[i], nums[j]),并满足下述全部条件: 0 <= i < j < nums.length |nums[i] - nums[j]| == k 注意,|val| 表示 val 的绝对值。
|
索引 Python
数组中的 k-diff 数对
我们拿到本题,读取题意要求在一组整数数组中,求出差值为k的数对对数k-diff。在思考如何解答该题之前,需要明确如下几点细节: nums数组元素都是整数 索引位置i与位置j,不能相等 k-diff数对关系:nums[i] - nums[j] = k -> nums[i] = nums[j] + k -〉 nums[i] - k = nums[j] k-diff数对,存在相同数对情况,但结果只取1次
118 0