think-cell test moving to linux for debugging.
This commit is contained in:
369
interviews/think-cell/IntervalMap.cpp
Normal file
369
interviews/think-cell/IntervalMap.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
/* Check .vimrc.local defs.
|
||||
VIM: let g:lcppflags="-std=c++11 -O2 -pthread"
|
||||
VIM: let g:wcppflags="/O2 /EHsc /DWIN32"
|
||||
VIM: let g:argv=""
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// interval_map<K,V> is a data structure that efficiently associates intervals of keys of type K with values of type V.
|
||||
// Your task is to implement the assign member function of this data structure, which is outlined below.
|
||||
|
||||
// interval_map<K, V> is implemented on top of std::map. In case you are not entirely sure which functions std::map provides,
|
||||
// what they do and which guarantees they provide, we have attached an excerpt of the C++1x draft standard at the end of this
|
||||
// file for your convenience.
|
||||
|
||||
// Each key-value-pair (k,v) in the m_map member means that the value v is associated to the interval from k (including) to
|
||||
// the next key (excluding) in m_map.
|
||||
// Example: the std::map (0,'A'), (3,'B'), (5,'A') represents the mapping
|
||||
// 0 -> 'A'
|
||||
// 1 -> 'A'
|
||||
// 2 -> 'A'
|
||||
// 3 -> 'B'
|
||||
// 4 -> 'B'
|
||||
// 5 -> 'A'
|
||||
// 6 -> 'A'
|
||||
// 7 -> 'A'
|
||||
// ... all the way to numeric_limits<key>::max()
|
||||
|
||||
// The representation in m_map must be canonical, that is, consecutive map entries must not have the same value:
|
||||
// ..., (0,'A'), (3,'A'), ... is not allowed.
|
||||
// Initially, the whole range of K is associated with a given initial value, passed to the constructor.
|
||||
template<class K, class V>
|
||||
class interval_map {
|
||||
friend void IntervalMapTest();
|
||||
|
||||
private:
|
||||
std::map<K,V> m_map;
|
||||
|
||||
public:
|
||||
// constructor associates whole range of K with val by inserting (K_min, val) into the map
|
||||
interval_map( V const& val) {
|
||||
m_map.insert(m_map.begin(),std::make_pair(std::numeric_limits<K>::min(),val));
|
||||
};
|
||||
|
||||
// Assign value val to interval [keyBegin, keyEnd).
|
||||
// Overwrite previous values in this interval. Do not change values outside 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.
|
||||
void assign( K const& keyBegin, K const& keyEnd, const V& val ) {
|
||||
//
|
||||
// If !( keyBegin < keyEnd ), this designates an empty interval, and
|
||||
// assign must do nothing.
|
||||
//
|
||||
if ( !(keyBegin < keyEnd ) )
|
||||
return;
|
||||
//
|
||||
// If there is a need to break the last interval with keyEnd do it.
|
||||
// Everything prior itEnd till itBegin will be removed.
|
||||
//
|
||||
auto ie = m_map.lower_bound( keyEnd );
|
||||
if ( ie != m_map.end()
|
||||
&& keyEnd < ie->first ) {
|
||||
auto hint = ie--;
|
||||
ie = m_map.insert(hint,std::make_pair(keyEnd,ie->second));
|
||||
}
|
||||
//
|
||||
// There should always be std::numeric_limits<K>::min()
|
||||
//
|
||||
auto it = --m_map.upper_bound( keyBegin );
|
||||
if ( !(it->second == val ) )
|
||||
{
|
||||
if ( it->first < keyBegin )
|
||||
it = m_map.insert(++it,std::make_pair(keyBegin,val));
|
||||
else
|
||||
it->second = val;
|
||||
}
|
||||
//
|
||||
// Find the range to be removed.
|
||||
//
|
||||
m_map.erase( ++it, ie );
|
||||
}
|
||||
|
||||
// look-up of the value associated with key
|
||||
V const& operator[]( K const& key ) const {
|
||||
return ( --m_map.upper_bound(key) )->second;
|
||||
}
|
||||
|
||||
private:
|
||||
//
|
||||
// Since this is a template class, I assume this method will be
|
||||
// instantiated only in IntervalMapTest. Hence, key and value are pretty
|
||||
// printable.
|
||||
//
|
||||
std::ostream& print( std::ostream& os ) const {
|
||||
for( auto e: m_map )
|
||||
os << '[' << e.first << ',' << e.second << ']';
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
// Key type K
|
||||
// - besides being copyable and assignable, is less-than comparable via operator< ;
|
||||
// - is bounded below, with the lowest value being std::numeric_limits<K>::min();
|
||||
// - does not implement any other operations, in particular no equality comparison or arithmetic operators.
|
||||
template<typename T>
|
||||
class test_key {
|
||||
T value;
|
||||
public:
|
||||
/*explicit*/ test_key( T v )
|
||||
: value(v)
|
||||
{}
|
||||
|
||||
bool operator < ( const test_key& op ) const {
|
||||
return value < op.value;
|
||||
}
|
||||
|
||||
std::ostream& print( std::ostream& os ) const {
|
||||
return os << value;
|
||||
}
|
||||
};
|
||||
namespace std {
|
||||
//
|
||||
// I hope this much of implementation of numeric_limits is enough for my
|
||||
// tests.
|
||||
//
|
||||
template <typename T>
|
||||
struct numeric_limits<::test_key<T> > : public numeric_limits<T> {
|
||||
typedef ::test_key<T> value_type;
|
||||
typedef numeric_limits<T> base_type;
|
||||
static value_type min() {
|
||||
return value_type( base_type::min() );
|
||||
}
|
||||
static value_type max() {
|
||||
return value_type( base_type::max() );
|
||||
}
|
||||
};
|
||||
}
|
||||
template <typename T>
|
||||
inline std::ostream& operator << ( std::ostream& os, test_key<T> t ) {
|
||||
return t.print(os);
|
||||
}
|
||||
template <typename T>
|
||||
void test_test_key() {
|
||||
|
||||
typedef test_key<T> test_type;
|
||||
test_type a(T(3.5));
|
||||
test_type b(T(5.5));
|
||||
|
||||
std::cout << "a<b before a=b: " << (a < b) << std::endl;
|
||||
|
||||
a = b;
|
||||
std::cout << "a<b after a=b: " << (a < b) << std::endl;
|
||||
|
||||
// (a == b);
|
||||
|
||||
std::cout << "min: " << std::numeric_limits<test_type>::min() << std::endl;
|
||||
std::cout << "max: " << std::numeric_limits<test_type>::max() << std::endl;
|
||||
}
|
||||
|
||||
// Value type V
|
||||
// - besides being copyable and assignable, is equality-comparable via operator== ;
|
||||
// - does not implement any other operations.
|
||||
template<typename T>
|
||||
class test_value {
|
||||
T value;
|
||||
public:
|
||||
/*explicit*/ test_value( T v )
|
||||
: value(v)
|
||||
{}
|
||||
|
||||
bool operator == ( const test_value& op ) const {
|
||||
return value == op.value;
|
||||
}
|
||||
|
||||
std::ostream& print( std::ostream& os ) const {
|
||||
return os << value;
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
void test_test_value() {
|
||||
test_value<T> a(T(3.5));
|
||||
test_value<T> b(T(5.5));
|
||||
|
||||
std::cout << "a==b before a=b: " << (a == b) << std::endl;
|
||||
|
||||
a = b;
|
||||
std::cout << "a==b after a=b: " << (a == b) << std::endl;
|
||||
|
||||
// (a < b);
|
||||
}
|
||||
template <typename T>
|
||||
inline std::ostream& operator << ( std::ostream& os, test_value<T> t ) {
|
||||
return t.print(os);
|
||||
}
|
||||
|
||||
// Provide a function IntervalMapTest() here that tests the functionality of the interval_map,
|
||||
// for example using a map of unsigned int intervals to char.
|
||||
// Many solutions we receive are incorrect. Consider using a randomized test to discover
|
||||
// the cases that your implementation does not handle correctly.
|
||||
void IntervalMapTest() {
|
||||
#if 0
|
||||
std::cout << "=============================================" << std::endl;
|
||||
std::cout << "Test test_key<int>" << std::endl;
|
||||
test_test_key<int>();
|
||||
|
||||
std::cout << "=============================================" << std::endl;
|
||||
std::cout << "Test test_key<double>" << std::endl;
|
||||
test_test_key<double>();
|
||||
|
||||
std::cout << "=============================================" << std::endl;
|
||||
std::cout << "Test test_value<int>" << std::endl;
|
||||
test_test_value<int>();
|
||||
|
||||
std::cout << "=============================================" << std::endl;
|
||||
std::cout << "Test test_value<double>" << std::endl;
|
||||
test_test_value<double>();
|
||||
#endif
|
||||
|
||||
typedef test_key<double> key;
|
||||
typedef test_value<double> val;
|
||||
|
||||
interval_map<key,val> im(0);
|
||||
|
||||
std::cout << "constrtuction: \n\t";
|
||||
im.print(std::cout);
|
||||
std::cout << std::endl;
|
||||
|
||||
|
||||
im.assign( 1000, 2000, 1 );
|
||||
std::cout << "constrtuction: \n\t";
|
||||
im.print(std::cout);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
IntervalMapTest();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The following paragraphs from the final draft of the C++1x ISO standard describe the available
|
||||
operations on a std::map container, their effects and their complexity.
|
||||
|
||||
23.2.1 General container requirements
|
||||
|
||||
<EFBFBD>1 Containers are objects that store other objects. They control allocation and deallocation of
|
||||
these objects through constructors, destructors, insert and erase operations.
|
||||
|
||||
<EFBFBD>6 begin() returns an iterator referring to the first element in the container. end() returns
|
||||
an iterator which is the past-the-end value for the container. If the container is empty,
|
||||
then begin() == end();
|
||||
|
||||
24.2.1 General Iterator Requirements
|
||||
|
||||
<EFBFBD>1 Iterators are a generalization of pointers that allow a C++ program to work with different
|
||||
data structures.
|
||||
|
||||
<EFBFBD>2 Since iterators are an abstraction of pointers, their semantics is a generalization of most
|
||||
of the semantics of pointers in C++. This ensures that every function template that takes
|
||||
iterators works as well with regular pointers.
|
||||
|
||||
<EFBFBD>5 Just as a regular pointer to an array guarantees that there is a pointer value pointing past
|
||||
the last element of the array, so for any iterator type there is an iterator value that points
|
||||
past the last element of a corresponding sequence. These values are called past-the-end values.
|
||||
Values of an iterator i for which the expression *i is defined are called dereferenceable.
|
||||
The library never assumes that past-the-end values are dereferenceable. Iterators can also have
|
||||
singular values that are not associated with any sequence. [ Example: After the declaration of
|
||||
an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular
|
||||
value of a pointer. <20>end example ] Results of most expressions are undefined for singular
|
||||
values; the only exceptions are destroying an iterator that holds a singular value, the
|
||||
assignment of a non-singular value to an iterator that holds a singular value, and, for
|
||||
iterators that satisfy the DefaultConstructible requirements, using a value-initialized
|
||||
iterator as the source of a copy or move operation.
|
||||
|
||||
<EFBFBD>10 An invalid iterator is an iterator that may be singular. (This definition applies to pointers,
|
||||
since pointers are iterators. The effect of dereferencing an iterator that has been invalidated
|
||||
is undefined.)
|
||||
|
||||
23.2.4 Associative containers
|
||||
|
||||
<EFBFBD>1 Associative containers provide fast retrieval of data based on keys. The library provides four
|
||||
basic kinds of associative containers: set, multiset, map and multimap.
|
||||
|
||||
<EFBFBD>4 An associative container supports unique keys if it may contain at most one element for each key.
|
||||
Otherwise, it supports equivalent keys. The set and map classes support unique keys; the multiset
|
||||
and multimap classes support equivalent keys.
|
||||
|
||||
<EFBFBD>5 For map and multimap the value type is equal to std::pair<const Key, T>. Keys in an associative
|
||||
container are immutable.
|
||||
|
||||
<EFBFBD>6 iterator of an associative container is of the bidirectional iterator category.
|
||||
(i.e., an iterator i can be incremented and decremented: ++i; --i;)
|
||||
|
||||
<EFBFBD>9 The insert member functions (see below) shall not affect the validity of iterators and references
|
||||
to the container, and the erase members shall invalidate only iterators and references to the erased
|
||||
elements.
|
||||
|
||||
<EFBFBD>10 The fundamental property of iterators of associative containers is that they iterate through the
|
||||
containers in the non-descending order of keys where non-descending is defined by the comparison
|
||||
that was used to construct them.
|
||||
|
||||
Associative container requirements (in addition to general container requirements):
|
||||
|
||||
std::pair<iterator, bool> insert(std::pair<const key_type, T> const& t)
|
||||
Effects: Inserts t if and only if there is no element in the container with key equivalent to the key of t.
|
||||
The bool component of the returned pair is true if and only if the insertion takes place, and the iterator
|
||||
component of the pair points to the element with key equivalent to the key of t.
|
||||
Complexity: logarithmic
|
||||
|
||||
iterator insert(const_iterator p, std::pair<const key_type, T> const& t)
|
||||
Effects: Inserts t if and only if there is no element with key equivalent to the key of t in containers with
|
||||
unique keys. Always returns the iterator pointing to the element with key equivalent to the key of t.
|
||||
Complexity: logarithmic in general, but amortized constant if t is inserted right before p.
|
||||
|
||||
size_type erase(key_type const& k)
|
||||
Effects: Erases all elements in the container with key equivalent to k. Returns the number of erased elements.
|
||||
Complexity: log(size of container) + number of elements with key k
|
||||
|
||||
iterator erase(const_iterator q)
|
||||
Effects: Erases the element pointed to by q. Returns an iterator pointing to the element immediately following
|
||||
q prior to the element being erased. If no such element exists, returns end().
|
||||
Complexity: Amortized constant
|
||||
|
||||
iterator erase(const_iterator q1, const_iterator q2)
|
||||
Effects: Erases all the elements in the left-inclusive and right-exclusive range [q1,q2). Returns q2.
|
||||
Complexity: Amortized O(N) where N has the value distance(q1, q2).
|
||||
|
||||
void clear()
|
||||
Effects: erase(begin(), end())
|
||||
Post-Condition: empty() returns true
|
||||
Complexity: linear in size().
|
||||
|
||||
iterator find(key_type const& k);
|
||||
Effects: Returns an iterator pointing to an element with the key equivalent to k, or end() if such an element is not found
|
||||
Complexity: logarithmic
|
||||
|
||||
size_type count(key_type const& k)
|
||||
Effects: Returns the number of elements with key equivalent to k
|
||||
Complexity: log(size of map) + number of elements with key equivalent to k
|
||||
|
||||
iterator lower_bound(key_type const& k)
|
||||
Effects: Returns an iterator pointing to the first element with key not less than k, or end() if such an element is not found.
|
||||
Complexity: logarithmic
|
||||
|
||||
iterator upper_bound(key_type const& k)
|
||||
Effects: Returns an iterator pointing to the first element with key greater than k, or end() if such an element is not found.
|
||||
Complexity: logarithmic
|
||||
|
||||
23.4.1 Class template map
|
||||
|
||||
<EFBFBD>1 A map is an associative container that supports unique keys (contains at most one of each key value) and provides
|
||||
for fast retrieval of values of another type T based on the keys. The map class supports bidirectional iterators.
|
||||
|
||||
23.4.1.2 map element access
|
||||
|
||||
T& operator[](const key_type& x);
|
||||
Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
|
||||
Returns: A reference to the mapped_type corresponding to x in *this.
|
||||
Complexity: logarithmic.
|
||||
|
||||
T& at(const key_type& x);
|
||||
const T& at(const key_type& x) const;
|
||||
Returns: A reference to the element whose key is equivalent to x.
|
||||
Throws: An exception object of type out_of_range if no such element is present.
|
||||
Complexity: logarithmic.
|
||||
*/
|
||||
Reference in New Issue
Block a user