区间图实现

问题描述 投票:0回答:8

我在一家 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;
    }
}

你能帮我找出错误吗?

c++ intervals
8个回答
5
投票

您通过递减地图的开头来获得 UB:

auto beforePrev = prevInterval;
--beforePrev;

演示

你下面的测试也很奇怪:

if (beforePrev != m_map.end()

beforePrev 不能是

end()
,因为你递减它。

看来你可以用

替换那个块
prevInterval->second = val;
if (prevInterval != m_map.begin() && !((--prevInterval)->second == val)){
    ++prevInterval;
} 

演示


4
投票

先写测试代码: 这是为了测试类型要求

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;

    }

2
投票

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());

0
投票
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;

0
投票

这是我的解决方案,可以替代上面问题中的

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 );
}

0
投票

试试这个代码:

#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));
    }

  };

0
投票

我刚刚接受了同样的采访。我的方法与上述方法不同 - 但有效。我花了很多精力来测试场景。不幸的是,我使用 [] 运算符来分配映射值。这需要使用默认构造函数——这显然是不允许的(虽然没有明确说明)。尽管他们建议使用 进行测试 - 当然两者都有默认构造函数。


-1
投票

下面的完整工作代码,在 main () 中进行测试 (但雇主说模板类型的使用不正确)

#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 << "\n\n Enter start, end, char: ";
        std::cin >> start >> end >> ch;
        
        mymap.assign(start, end, ch);
    }
    
}
© www.soinside.com 2019 - 2024. All rights reserved.