间隔映射实现
interval map implementation
我在一家IT公司的C++职位上发送了一份申请。他们给我发了一份测试作业。
任务是实现间隔映射分配操作。我向他们发送了我的解决方案,但它没有通过第二个要求(正确的行为(。他们没有给出更多的反馈,只是说我的代码没有通过他们的所有测试。现在我想知道我能做错什么。当然,在发送解决方案之前,我做了一些测试,我能想到的每个测试都通过了。
现在我无法入睡,不知道我在哪里可以把它搞砸。
这是我的代码:
void assign (const K & keyBegin, const K & keyEnd, const V & val )
{
if (!(keyBegin < keyEnd))
return;
auto nextInterval = --m_map.upper_bound(keyEnd);
auto inserted1 = m_map.end();
auto inserted2 = m_map.end();
if (nextInterval->second == val)
++nextInterval;
else if (nextInterval->first < keyEnd)
{
const V & nextValue = nextInterval->second;
++nextInterval;
inserted1 = nextInterval = m_map.emplace_hint(nextInterval, keyEnd, nextValue);
}
try
{
auto prevInterval = nextInterval;
--prevInterval;
if (keyBegin < prevInterval->first)
prevInterval = --m_map.upper_bound(keyBegin);
if (!(prevInterval->second == val))
{
if (prevInterval->first < keyBegin)
{
++prevInterval;
inserted2 = prevInterval = m_map.emplace_hint(prevInterval, keyBegin, val);
}
else
{
auto beforePrev = prevInterval;
--beforePrev;
if (beforePrev != m_map.end() && beforePrev->second == val)
prevInterval = beforePrev;
else
{
auto hint = m_map.erase(prevInterval);
inserted2 = prevInterval = m_map.emplace_hint(hint, keyBegin, val);
}
}
}
m_map.erase(++prevInterval, nextInterval);
}
catch (...)
{
if (inserted1 != m_map.end())
m_map.erase(inserted1);
if (inserted2 != m_map.end())
m_map.erase(inserted2);
throw;
}
}
你能帮我找到一个错误吗?
通过递减地图的开头来获得 UB:
auto beforePrev = prevInterval;
--beforePrev;
演示
你下面的测试也很奇怪:
if (beforePrev != m_map.end()
之前Prev不能在你递减它时end()
。
似乎您可以将该块替换为
prevInterval->second = val;
if (prevInterval != m_map.begin() && !((--prevInterval)->second == val)){
++prevInterval;
}
演示
首先写测试代码:这是测试类型要求
class Value {
char a;
public:
Value(char _a){ a = _a; }
bool operator==(const Value& _val) const;
friend ostream& operator<<(ostream& os, const Value& val); // ONLY FOR TESTING, NOT RELATED TO SOLUTION
};
bool Value::operator==( const Value& _val ) const{
return ( a == _val.a ) ;
}
// operator<< is implemented ONLY FOR TESTING PURPOSE
ostream& operator<<(ostream& os, const Value& val)
{
os << val.a;
return os;
}
class Key {
int a;
public:
Key(int _a){ a = _a; }
Key(){ a = 0; }
bool operator<(const Key& _key) const;
friend ostream& operator<<(ostream& os, const Key& _key); // ONLY FOR TESTING, NOT RELATED TO SOLUTION
};
bool Key::operator<( const Key& _key ) const{
return ( a < _key.a ) ;
}
// operator<< is implemented ONLY FOR TESTING PURPOSE
ostream& operator<<(ostream& os, const Key& _key)
{
os << _key.a;
return os;
}
现在实施
void assign( K const& keyBegin, K const& keyEnd, V const& val ) {
K last = m_map.rbegin()->first;
K first = m_map.begin()->first;
if (!(keyBegin < keyEnd) ) return;
if ( keyBegin < first ) return;
if( last < keyEnd) return ;
for (auto it = m_map.begin(); it!=m_map.end(); ++it)
{
if((*it).first < keyBegin) continue;
else
{
(*it).second=val;
it++;
auto tmp=it;
while((*it).first < keyEnd){
it++;
}
m_map.erase(tmp, it);
break;
}
}
}
另一种解决方案
void assign( K const& keyBegin, K const& keyEnd, V const& val ) {
K last = m_map.rbegin()->first;
K first = m_map.begin()->first;
if (!(keyBegin < keyEnd) ) return;
if ( keyBegin < first ) return;
if( last < keyEnd) return ;
auto itr1 = m_map.find(keyBegin);
auto itr2 = m_map.find(keyEnd);
(*itr1).second=val;
itr1++;
m_map.erase(itr1,itr2);
cout << endl << endl;
}
上面来自 Vivek 的两个解决方案不起作用。这是我能想到的最简单和最短的实现:
if (not (keyBegin < keyEnd) )
return;
auto beginLowerBound = m_map.lower_bound(keyBegin);
auto endLowerBound = m_map.lower_bound(keyEnd);
std::optional<std::pair<K,V>> additionalElement;
if (endLowerBound == m_map.end() || keyEnd < endLowerBound->first)
additionalElement = std::pair(keyEnd, operator[]( keyEnd ));
else if (beginLowerBound == m_map.end() || keyBegin < beginLowerBound->first)
additionalElement = std::pair(keyEnd, operator[]( keyBegin ));
if (beginLowerBound != m_map.end())
m_map.erase(beginLowerBound, endLowerBound);
m_map.insert(std::pair(keyBegin, val));
if (additionalElement)
m_map.insert(additionalElement.value());
我刚刚接受了同样的采访。我的方法与上述所有方法都不同 - 但有效。我在测试场景上花了很多精力。不幸的是,我正在使用 [] 运算符来分配映射值。这需要使用默认构造函数 - 这显然是不允许的(虽然没有明确说明(。尽管他们建议使用 <int、char> 进行测试 - 当然两者都有默认构造函数。
if (!(keyBegin < keyEnd))
return;
auto upper_bound_end = m_map.upper_bound(keyEnd);
auto upper_bound_end2 = upper_bound_end;
upper_bound_end2--;
auto uper_bound_begin = m_map.upper_bound(keyBegin);
auto uper_bound_begin2 = uper_bound_begin;
uper_bound_begin2--;
bool flag = false;
bool flag2 = false;
if (!((uper_bound_begin2)->second == val))
flag = true;
V tmp;
if (!((upper_bound_end2)->second == val))
{
flag2 = true;
tmp = (upper_bound_end2)->second;
}
for (auto it = uper_bound_begin; it != upper_bound_end;)
{
it = m_map.erase(it);
}
if (flag == true)
m_map[keyBegin] = val;
if (flag2 == true)
m_map[keyEnd] = tmp;
这是我的解决方案,可以替换上面问题中的assign
函数。最初的问题是要求将其实现为类成员函数,但这里的问题省略了类部分。因此,您可以忽略template
和interval_map<K, V>
,以便能够匹配问题中的解决方案,就像本页中的其他答案一样。
在实现时,我将assign
函数想象为在条带中的现有颜色上绘制新颜色。此处的map
仅包含起点和颜色(即value
(中每个涂漆区域的条带。
template <typename K, typename V>
void interval_map<K, V>::assign(const K& keyBegin, const K& keyEnd, const V& val)
{
using iterator = typename std::map<K, V>::iterator;
if ( !(keyBegin < keyEnd) )
return;
// Handle keyEnd first to not impact the handling of keyBegin.
iterator it1 = m_map.upper_bound(keyEnd);
--it1; // it1 points to the greatest interval whose key <= keyEnd. It exists always.
V& old_val = it1->second;
++it1;
if ( old_val != val )
// If old_val from the greatest interval does not equal the desired val, we cut
// off the interval at keyEnd unless the interval already starts with keyEnd, in
// which case we do nothing.
// If old_val equals val we set up to erase up to that interval.
it1 = m_map.try_emplace(it1, keyEnd, std::move(old_val));
// Now it1 points to the first interval starting from keyEnd that has a value
// different than `val`, or m_map.end() if no such interval exits.
iterator it0 = m_map.lower_bound(keyBegin);
--it0; // it0 points to the greatest interval whose key < keyBegin. It exists always.
if ( it0->second != val )
{
// If the greatest interval has a value different than `val`, we create a new
// interval at keyBegin, or if there already exists an interval starting at
// keyBegin we change the value.
// If the greatest interval has `val` as the value we make it our interval to
// cover [keyBegin, keyEnd).
++it0;
it0 = m_map.insert_or_assign(it0, keyBegin, val);
}
// Now it0 points to the first interval that covers keyBegin and has the value `val`.
// Extend it0 until it1, that is, erase all iterators {it | it0 < it < it1}.
++it0;
m_map.erase(it0, it1);
}
仅供参考,我还编写了一个验证函数,可以在每次assign
后调用该函数来验证整个地图的有效性。希望这有助于了解地图的属性,以满足assign
操作。
template <typename K, typename V>
void interval_map<K, V>::_verify(const K& keyBegin, const K& keyEnd, const V& val) const
{
// 1. m_map should be non-empty.
assert( m_map.begin() != m_map.end() );
auto it = m_map.begin();
V prev_val = it->second;
while ( ++it != m_map.end() )
{
// 2. Adjacent intervals should have different values.
assert( prev_val != it->second );
prev_val = it->second;
}
auto it0 = --m_map.upper_bound(keyBegin);
auto it1 = --m_map.lower_bound(keyEnd);
// 3. There should be only one interval that covers [keyBegin, keyEnd).
assert( it0 == it1 );
// 4. The interval should have the disired value.
assert( val == it0->second );
}
试试这段代码:
#include <iostream>
#include <map>
using namespace std;
template < typename K, typename V >
class interval_map {
std::map < K, V > m_map;
public:
// constructor
interval_map(V
const & val) {
m_map.insert(m_map.begin(), std::make_pair(std::numeric_limits < K > ::lowest(), val));
}
// insert an interval and value
void insert(K
const & keyBegin, K
const & keyEnd, V
const & val) {
// check if the interval is valid
if (!(keyBegin < keyEnd))
return;
// find the entry for the interval start
auto lb = m_map.lower_bound(keyBegin);
if (lb != m_map.begin() && (--lb) -> second == val) {
++lb;
}
// remove all entries in the interval
auto up = m_map.upper_bound(keyEnd);
while (lb != up) {
lb = m_map.erase(lb);
}
// insert the new interval
m_map.insert(lb, std::make_pair(keyBegin, val));
m_map.insert(up, std::make_pair(keyEnd,
m_map.find(keyBegin) -> second));
}
};
下面的完整工作代码,在主((中进行测试(但雇主说模板类型的使用不正确(
#define _ITERATOR_DEBUG_LEVEL 2
#define _SECURE_SCL 1
#define _HAS_ITERATOR_DEBUGGING 1
#include <iostream>
#include <iomanip>
#include <cassert>
#include <map>
template<typename K, typename V>
class interval_map {
V m_valBegin;
std::map<K, V> m_map;
public:
// constructor associates whole range of K with val
interval_map(V const& val)
: m_valBegin(val)
{}
void assign(std::map<K, V> const& testMap) {
m_map = testMap;
}
// Assign value val to interval [keyBegin, keyEnd).
// Overwrite previous values in this interval.
// Conforming to the C++ Standard Library conventions, the interval
// includes keyBegin, but excludes keyEnd.
// If !( keyBegin < keyEnd ), this designates an empty interval,
// and assign must do nothing.
// look-up of the value associated with key
V const& operator[](K const& key) const {
auto it = m_map.upper_bound(key);
if (it == m_map.begin()) {
return m_valBegin;
}
else {
return (--it)->second;
}
}
void print() {
std::cout << 'n' << m_valBegin << 'n';
for (auto it = m_map.begin(); it != m_map.end(); ++it) {
std::cout << it->first << ", " << it->second << 'n';
}
}
void assign(K const& keyBegin, K const& keyEnd, V const& val) {
if (!(keyBegin < keyEnd)) return;
if (m_map.empty()) {
if (m_valBegin == val)
return;
m_map.insert({ keyBegin, val });
m_map.insert({ keyEnd, m_valBegin});
return;
}
auto startIt = m_map.lower_bound(keyBegin);
bool doErase = true;
if (startIt == m_map.end())
doErase = false;
auto endIt = m_map.upper_bound(keyEnd);
auto upperVal{ m_valBegin };
if (endIt == m_map.begin())
doErase = false;
else
upperVal = (--endIt)->second;
if (endIt != m_map.end())
endIt++;
if(doErase)
m_map.erase(startIt, endIt);
m_map.insert({ keyBegin, val });
m_map.insert({ keyEnd, upperVal });
// ensure canonical - further down
startIt = m_map.lower_bound(keyBegin);
assert(startIt->second == val);
// go back to prev interval (it can have value = val)
if (startIt != m_map.begin())
{
startIt--;
// then our inserted value is the first equal to val
if (startIt->second != val)
startIt++;
}
// skip first repeating value (first one should be left - others deleted)
if(!(startIt == m_map.begin() && val == m_valBegin))
startIt++;
while(startIt != m_map.end())
{
auto tmpKey = startIt->first;
if ((startIt++)->second == val)
m_map.erase(tmpKey);
else
break;
}
}
};
int main() {
interval_map<int, char> mymap{ 'a' };
//mymap.assign({ {10,'c'},{20,'a'},{30,'c'},{40,'i'},{50,'c'},{60,'i'} });
//mymap.assign(65, 68, 'z'); mymap.print();
while (1)
{
mymap.print();
int start, end;
char ch;
std::cout << "nn Enter start, end, char: ";
std::cin >> start >> end >> ch;
mymap.assign(start, end, ch);
}
}
- 哈希映射如何在 c++ 中实现?
- 自定义哈希表实现-将字符串映射到整数时出现内存错误
- 在不创建新节点的情况下实现带有映射的trie
- 间隔映射实现
- 为什么此代码在此 Trie 实现中使用映射 c++ 中的指针崩溃?
- 映射构造函数,同时在C++中实现计数器
- 如何在这个哈希映射中实现 AVL 树
- 我的 A* 实现无法正确跟踪"camefrom"映射
- C++通过映射并使用中的方法从基类实现派生类
- 具有良好性能的c++映射实现
- 双向映射是否有更有效的实现
- 高效实现 64 位和 32 位无符号整数之间的双向映射
- 在vf2_sub_graph_iso中使用属性映射实现等效性
- 实现将字符串映射到 C++ 中的数组的哈希表。我一直在"Debug Assertion Failed",我不知道为什么
- 在使用无序多映射实现的有向图中查找循环
- 如何用tbb实现一个将键映射到不同类型值的并发哈希表
- C++多索引映射实现
- 要使用 boost::object_pool 实现映射的自定义分配器,如何使用 boost::object_pool 分
- 不允许使用比较实现C++映射不完整类型
- 使用双链表实现映射