本文涉及的基础知识点
题目
给你一个由非负整数 a1, a2, …, an 组成的数据流输入,请你将到目前为止看到的数字总结为不相交的区间列表。
实现 SummaryRanges 类:
SummaryRanges() 使用一个空数据流初始化对象。
void addNum(int val) 向数据流中加入整数 val 。
int[][] getIntervals() 以不相交区间 [starti, endi] 的列表形式返回对数据流中整数的总结。
示例:
输入:
[“SummaryRanges”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”, “addNum”, “getIntervals”] [[], [1], [], [3], [], [7], [], [2], [], [6], []]
输出:
[null, null, [[1, 1]], null, [[1, 1], [3, 3]], null, [[1, 1], [3, 3], [7, 7]], null, [[1, 3], [7, 7]], null, [[1, 3], [6, 7]]]
解释:
SummaryRanges summaryRanges = new SummaryRanges(); summaryRanges.addNum(1); // arr = [1] summaryRanges.getIntervals(); // 返回 [[1, 1]] summaryRanges.addNum(3); // arr = [1, 3] summaryRanges.getIntervals(); // 返回 [[1, 1], [3, 3]] summaryRanges.addNum(7); // arr = [1, 3, 7] summaryRanges.getIntervals(); // 返回 [[1, 1], [3, 3], [7, 7]] summaryRanges.addNum(2); // arr = [1, 2, 3, 7] summaryRanges.getIntervals(); // 返回 [[1, 3], [7, 7]] summaryRanges.addNum(6); // arr = [1, 2, 3, 6, 7] summaryRanges.getIntervals(); // 返回 [[1, 3], [6, 7]]
参数范围
0 <= val <= 104
最多调用 addNum 方法 3 * 104 次。
最多调用getIntervals 100次。
分析
用有序映射记录左端点和右端点。首尾各插入元素,避免判断非法迭代期。
addNum
情况处理
情况 | 处理 | |
一 | 已经存在包括value的区间 | 则什么都不干。 |
二 | 不存在和value相邻的区间 | 插入[value,value] |
三 | 左边有相邻的区间 | 删除左邻,插入新区间[左邻居左端点,value] |
四 | 右边有相邻的区间 | 删除右邻,插入新区间[value,右邻居右端点] |
五 | 左有都有相邻的区间 | 删除左右邻,插入新区间[左邻居左端点,右邻居右端点] |
情况二和五可以分成两种独立的情况。
情况判断
情况 | 判断方法 |
已经存在包括value的区间 | 存在区间左端点小于value,右端点大于等于value |
左邻 | 从右向左第一个左端点小于value, 右端点是否等于value-1 |
右邻 | 从左向右第一个左端点大于value, 左端点是否等于value+1 |
判断方法
判断右邻用: ij …upper_bound
判断左邻用ij前面的迭代期。
代码
核心代码
class SummaryRanges { public: SummaryRanges() { m_mLeftRight[-1000 * 1000] = -1000 * 1000; m_mLeftRight[1000*1000] = 1000 * 1000; } void addNum(int value) { const auto ij = m_mLeftRight.upper_bound(value); const auto it = std::prev(ij); if (it->second >= value) {//已经存在包括value的区间 return; } int left = value, right = value; if (it->second + 1 == value) { left = it->first; m_mLeftRight.erase(it); } if (value + 1 == ij->first) { right = ij->second; m_mLeftRight.erase(ij); } m_mLeftRight[left] = right; } vector<vector> getIntervals() { vector<vector> vRet; auto it = m_mLeftRight.begin(); for (++it; it != m_mLeftRight.end(); ++it) { vRet.emplace_back(vector{ it->first,it->second }); } vRet.pop_back(); return vRet; } std::map<int, int> m_mLeftRight; };
测试用例
template void Assert(const T& t1, const T& t2) { assert(t1 == t2); } template void Assert(const vector& v1, const vector& v2) { if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i < v1.size(); i++) { Assert(v1[i] ,v2[i]); } } int main() { SummaryRanges sr; vector<vector> res; res = sr.getIntervals(); Assert(res, vector<vector>{ }); sr.addNum(-1); res = sr.getIntervals(); Assert(res, vector<vector>{ {-1, -1} }); sr.addNum(-2); res = sr.getIntervals(); Assert(res, vector<vector>{ {-2, -1} }); sr.addNum(0); res = sr.getIntervals(); Assert(res, vector<vector>{ {-2, 0} }); sr.addNum(2); res = sr.getIntervals(); Assert(res, vector<vector>{ {-2, 0}, { 2,2 } });
sr.addNum(1); res = sr.getIntervals(); Assert(res, vector<vector<int>>{ {-2,2 } }); //CConsole::Out(res);
}
3月旧代码
class SummaryRanges { public: SummaryRanges() { } void addNum(int value) { if (m_setHas.count(value)) { return; } m_setHas.insert(value); auto itEnd = m_mEndBegin.find(value - 1); auto itBegin = m_mBeginEnd.find(value + 1); if ((m_mEndBegin.end() != itEnd) && (m_mBeginEnd.end() != itBegin)) { const int iOldBegin = itEnd->second; const int iOldEnd = itBegin->second; Erase(iOldBegin, value - 1); Erase(value + 1, iOldEnd); Insert(iOldBegin, iOldEnd); } else if (m_mEndBegin.end() != itEnd) { const int iOldBegin = itEnd->second; Erase(iOldBegin, value - 1); Insert(iOldBegin, value); } else if(m_mBeginEnd.end() != itBegin) { const int iOldEnd = itBegin->second; Erase(value + 1, iOldEnd); Insert(value, iOldEnd); } else { Insert(value, value); } } vector<vector> getIntervals() { vector<vector> vRet; for (const auto& it : m_mBeginEnd) { vRet.push_back({ it.first, it.second }); } return vRet; } protected: void Erase(int iBegin, int iEnd) { m_mBeginEnd.erase(iBegin); m_mEndBegin.erase(iEnd); } void Insert(int iBegin, int iEnd) { m_mBeginEnd[iBegin] = iEnd; m_mEndBegin[iEnd] = iBegin; } std::map<int, int> m_mBeginEnd; std::unordered_map<int, int> m_mEndBegin; std::unordered_set m_setHas; };
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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