Using comparator for STL set - c++

Check the following code:
string toLowerCase(const string& str) {
string res(str);
int i;
for (i = 0; i < (int) res.size(); i++)
res[i] = (char) tolower(res[i]);
return res;
}
class LeagueComparator
{
public:
bool operator()(const string& s1, const string& s2)
{
return toLowerCase(s1) < toLowerCase(s2);
}
};
int main()
{
set<string, LeagueComparator> leagues;
set<string, LeagueComparator>::iterator iter;
leagues.insert("BLeague");
leagues.insert("aLeague"); // leagues = {"aLeague", "BLeague"}
leagues.insert("ALeague");
for (iter = leagues.begin(); iter != leagues.end(); iter++)
cout << *iter << endl;
return 0;
}
The output is:
aLeague
BLeague
which is shocking to me. I thought (and expecting) the output would be:
aLeague
ALeague
BLeague
Before the execution of leagues.insert("ALeague");, the leagues contains "aLeague" and "BLeague". My question is, while executing leagues.insert("ALeague"); why the machine treats "ALeague" == "aleague"? According to my understanding, there is no element "ALeague" in leagues. So "ALeague" should be inserted into leagues. The comparator should determine where to put "ALeague".
Thanks in advance.
PS: Please don't hit me for using C style cast. :P I'm too lazy to type static_cast.

Your comparator, thanks to the toLowerCase, says that "aLeague" == "ALeague". Since (according to your comparator) "aLeague" < "ALeague" == false and "ALeague" < "aLeague" == false, they must be equivalent. And inserting an equivalent element into a set doesn't do anything.

When you insert any value to a set, the object checks to see whether it already contains that value. Your LeagueComparator object compares ALeague with the other two values already in the set. It determines that the existing value aLeague is neither greater than nor less than the proposed new entry (ALeague), so they must be equal, and so it doesn't proceed with the insert. The set remains with just two elements. That's the whole point of providing a customer comparison object, so you can control how the set determines whether two elements match.

Given the comparator you provided, "ALeague" is indeed equivalent "aLeague".
Given two values, x and y, and a less-than comparator z:
If z(x, y) is true, then x is less than y
If z(y, x) is true, then y is less than x
If neither is true, then x is equivalent to y
If both are true, then you have a broken comparator.

Replace your LeagueComparator with
class LeagueComparator
{
public:
bool operator()(const string& s1, const string& s2)
{
return toLowerCase(s1) < toLowerCase(s2) ||
!(toLowerCase(s2) < toLowerCase(s1)) && s1 < s2;
}
};

Related

Forming the maximum number out of the available numbers?

I am trying to solve the following question https://www.interviewbit.com/problems/largest-number/ : Given a list of non negative integers, arrange them such that they form the largest number.
For example:
Given [3, 30, 34, 5, 9], the largest formed number is 9534330.
Note: The result may be very large, so you need to return a string instead of an integer.
I have been able to solve it and implemented it, using comparison based sorting technique. That is, given two numbers X and Y, I compare two numbers XY (Y appended at the end of X) and YX (X appended at the end of Y). If XY is larger, then X should come before Y in output, else Y should come before. The following is the code:
string Solution::largestNumber(const vector<int> &A) {
// Do not write main() function.
// Do not read input, instead use the arguments to the function.
// Do not print the output, instead return values as specified
// Still have a doubt. Checkout www.interviewbit.com/pages/sample_codes/ for more details
vector<string> myvec;
for (int i = 0; i < A.size(); i++)
{
string s = to_string(A[i]);
myvec.push_back(s);
}
sort(myvec.begin(),myvec.end(),mycomp());
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
}
struct mycomp
{
inline bool operator() (const string &p1, const string &p2)
{
string s1 = p1.append(p2);
string s2 = p2.append(p1);
if (s1.compare(s2) < 0)
return false;
else
return true;
}
};
But, the problem is, I have to merge the two functions into a single one because I just have to implement the single function. I cannot define one more function since I have no control over the entire piece of code (look at the link's submission part). Therefore, my ask is, how can I use the comparator by defining it inside the function string Solution::largestNumber(const vector<int> &A). Thanks!
This is a perfect place for a lambda.
sort(myvec.begin(), myvec.end(), [](const string &p1, const string &p2) {
string s1(p1 + p2);
string s2(p2 + p1);
return s1.compare(s2) >= 0;
});
I changed your code to not call append() on the strings, since you accept them as references to const objects, and p1.append(p2) tries to modify p1, but that's not allowed on a const object. Further, avoid constructs like if(x) return true else return false; and instead just return x;
Also, this
string s = "";
auto it = myvec.begin();
while (it != myvec.end())
{
string p = *it;
s = s + p;
it++;
}
return s;
Can be condensed to:
string s;
for (auto const& e : myvec)
s += e;
return s;
(Assuming you have a c++11 compiler or later)

In which case a insert in a std::map can fail?

In my code I have those lines:
if(mymap.count(plan) == 0) {
std::vector<XYZ> v;
v.reserve(10);
mymap.emplace(plan, v);
std::cout << "Plan " << plan.normal << " # " << plan.O << " added";
}
//I inserted this code for debugging
std::map<Plan, std::vector<XYZ>>::const_iterator it = mymap.find(plan);
if(it == this->intersections.end())
std::cout << "not found";
How is it possible that I can read in the console plan added and just after not found ?
My map is declared as such:
std::map<Plan, std::vector<XYZ>, PlanComp> mymap;
At some point I thougt it comes from the comparator, but it respects irreflexivity, antisymmetry, transitivity, transitivity of equivalence (which is enough according to this blog) :
struct PlanComp {
bool operator()(const Plan& l, const Plan& n) const {
return (l.O.x != n.O.x) || (l.O.y != n.O.y) || (l.O.z != n.O.z)
|| (l.normal.x != n.normal.x) || (l.normal.y != n.normal.y) || (l.normal.z != n.normal.z);
}
};
struct XYZ {
double x;
double y;
double z;
};
struct Plan {
XYZ O;
XYZ plan;
};
Your comparator does not define a strict weak ordering (loosely speaking, "less than" semantics which define an order for your elements). Therefore your code exhibits undefined behaviour.
The simplest solution would be to use a lexicographical comparator - compare x first, then compare y only in the event of a tie, and so on. In C++11 that's even simpler; operator < for tuples already does this for you (and you can use std::tie to get the tuples). See the answers to Operator < and strict weak ordering for examples.

std map composite key

I have a problem with the operator<() method which is required for a std::map. I'm using a struct as composite key that looks as follows:
struct MyKey {
std::string string1;
std::string string2;
std::string string3;
unsigned int uint1;
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
}
}
As introduced I want to use a composite key with 4 values, but I don't know how to achieve this for the operator< method. I observed that only 1 value is stored at a time!
Can anybody tell me how the right condition looks like?
Thanks in advance!
The Standard library's associative containers such as std::map, std::set, std::multiset, std::multimap, std::bitset require that the ordering of elements must follow Strict Weak Ordering, which means your implementation of operator< must follow strict weak ordering. So one implementation could be this:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
if (mk1.string1 != mk2.string1 )
return mk1.string1 < mk2.string1;
else if ( mk1.string2 != mk2.string2)
return mk1.string2 < mk2.string2;
else if (mk1.string3 != mk2.string3)
return mk1.string3 < mk2.string3;
else
return mk1.uint1 < mk2.uint1;
}
Or you can implement it as:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
auto const & t1 = std::tie(mk1.string1, mk1.string2, mk1.string3, mk1.uint1);
auto const & t2 = std::tie(mk2.string1, mk2.string2, mk2.string3, mk2.uint1);
return t1 < t2;
}
In this solution, std::tie function creates two tuples t1 and t1 of the references of the arguments passed to it, and then compare t1 and t2 using overloaded operator< for std::tuple instead. The operator< for tuple compares the elements lexicographically — strict-weak ordering is achieved..
I think you have a problem in that the operator< doesn't necessarily implement strict weak ordering. There are too many combinations where A<B is false and B<A is also false, where A and B are MyKey objects. This is interpreted as A being equal to B.
The problem with your implementation is that it's not stable, consider...
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
...evaluating { "a", "a", "a", 1 } < { "a", "b", "a", 1 } = a<a && ... = false && ... = false
...but { "a", "b", "a", 1 } < { "a", "a", "a", 1 } = a<a && ... = false && ... = false
So, neither is reported as less than the other, despite them not being equal keys in the map.
A working solution: it's concise and efficient to do each necessary string comparisons only once...
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
int x;
return (x = mk1.string1.compare(mk2.string1)) ? x < 0 :
(x = mk1.string2.compare(mk2.string2)) ? x < 0 :
(x = mk1.string3.compare(mk2.string3)) ? x < 0 :
mk1.uint1 < mk2.uint1;
}

Storing set of non-overlapping ranges and finding whether a value is present in any one of the ranges strictly

I have a set of ranges :
Range1 ---- (0-10)
Range2 ---- (15-25)
Range3 ---- (100-1000) and likewise.
I would like to have only the bounds stored since storing large ranges , it would be efficient.
Now I need to search for a number , say 14 . In this case, 14 is not present in any of the ranges whereas (say a number) 16 is present in one of the ranges.
I would need a function
bool search(ranges, searchvalue)
{
if searchvalues present in any of the ranges
return true;
else
return false;
}
How best can this be done ? This is strictly non-overlapping and the important criteria is that the search has to be most efficient.
One possibility is to represent ranges as a pair of values and define a suitable comparison function. The following should consider one range less than another if its bounds are smaller and there is no overlap. As a side effect, this comparison function doesn't let you store overlapping ranges in the set.
To look up an integer n, it can be treated as a range [n, n]
#include <set>
#include <iostream>
typedef std::pair<int, int> Range;
struct RangeCompare
{
//overlapping ranges are considered equivalent
bool operator()(const Range& lhv, const Range& rhv) const
{
return lhv.second < rhv.first;
}
};
bool in_range(const std::set<Range, RangeCompare>& ranges, int value)
{
return ranges.find(Range(value, value)) != ranges.end();
}
int main()
{
std::set<Range, RangeCompare> ranges;
ranges.insert(Range(0, 10));
ranges.insert(Range(15, 25));
ranges.insert(Range(100, 1000));
std::cout << in_range(ranges, 14) << ' ' << in_range(ranges, 16) << '\n';
}
The standard way to handle this is through so called interval trees. Basically, you augment an ordinary red-black tree with additional information so that each node x contains an interval x.int and the key of x is the low endpoint, x.int.low, of the interval. Each node x also contains a value x.max, which is the maximum value of any interval endpoint stored in the subtree rooted at x. Now you can determine x.max given interval x.int and the max values of node x’s children as follows:
x.max = max(x.int.high, x.left.max, x.right.max)
This implies that, with n intervals, insertion and deletion run in O(lg n) time. In fact, it is possible to update the max attributes after a rotation in O(1) time. Here is how to search for an element i in the interval tree T
INTERVAL-SEARCH(T, i)
x = T:root
while x is different from T.nil and i does not overlap x.int
if x.left is different from T.nil and x.left.max is greater than or equal to i.low
x = x.left
else
x = x.right
return x
The complexity of the search procedure is O(lg n) as well.
To see why, see CLRS Introduction to algorithms, chapter 14 (Augmenting Data Structures).
You could put something together based on std::map and std::map::upper_bound:
Assuming you have
std::map<int,int> ranges; // key is start of range, value is end of range
You could do the following:
bool search(const std::map<int,int>& ranges, int searchvalue)
{
auto p = ranges.upper_bound(searchvalue);
// p->first > searchvalue
if(p == ranges.begin())
return false;
--p; // p->first <= searchvalue
return searchvalue >= p->first && searchvalue <= p->second;
}
I'm using C++11, if you use C++03, you'll need to replace "auto" by the proper iterator type.
EDIT: replaced pseudo-code inrange() by explicit expression in return statement.
A good solution can be as the following. It is O(log(n)).
A critical condition is non overlapping ranges.
#include <set>
#include <iostream>
#include <assert.h>
template <typename T> struct z_range
{
T s , e ;
z_range ( T const & s,T const & e ) : s(s<=e?s:e), e(s<=e?e:s)
{
}
};
template <typename T> bool operator < (z_range<T> const & x , z_range<T> const & y )
{
if ( x.e<y.s)
return true ;
return false ;
}
int main(int , char *[])
{
std::set<z_range<int> > x;
x.insert(z_range<int>(20,10));
x.insert(z_range<int>(30,40));
x.insert(z_range<int>(5,9));
x.insert(z_range<int>(45,55));
if (x.find(z_range<int>(15,15)) != x.end() )
std::cout << "I have it" << std::endl ;
else
std::cout << "not exists" << std::endl ;
}
If you have ranges ri = [ai, bi]. You could sort all the ai and put them into an array and search for x having x >= ai and ai minimal using binary search.
After you found this element you have to check whether x <= bi.
This is suitable if you have big numbers. If, on the other hand, you have either a lot of memory or small numbers, you can think about putting those ranges into a bool array. This may be suitable if you have a lot of queries:
bool ar[];
ar[0..10] = true;
ar[15..25] = true;
// ...
bool check(int searchValues) {
return ar[searchValues];
}
Since the ranges are non-overlapping the only thing left to do is performing a search within the range that fit's the value. If the values are ordered within the ranges, searching is even simpler. Here is a summary of search algorithms.
With respect to C++ you also can use algorithms from STL or even functions provided by the containers, e. g. set::find.
So, this assumes the ranges are continous (i.e range [100,1000] contains all numbers between 100 and 1000):
#include <iostream>
#include <map>
#include <algorithm>
bool is_in_ranges(std::map<int, int> ranges, int value)
{
return
std::find_if(ranges.begin(), ranges.end(),
[&](std::pair<int,int> pair)
{
return value >= pair.first && value <= pair.second;
}
) != ranges.end();
}
int main()
{
std::map<int, int> ranges;
ranges[0] = 10;
ranges[15] = 25;
ranges[100] = 1000;
std::cout << is_in_ranges(ranges, 14) << '\n'; // 0
std::cout << is_in_ranges(ranges, 16) << '\n'; // 1
}
In C++03, you'd need a functor instead of a lambda function:
struct is_in {
is_in(int x) : value(x) {}
bool operator()(std::pair<int, int> pair)
{
return value >= pair.first && value <= pair.second;
}
private:
int value;
};
bool is_in_ranges(std::map<int, int> ranges, int value)
{
return
std::find_if(ranges.begin(), ranges.end(), is_in(value)) != ranges.end();
}

C++: std::sort using already destroyed object with custom predicate?

I'm having a very odd problem with some code using std::sort. If I replace std::sort by stable_sort the problem goes away.
class Entry
{
public:
Entry() : _date(0), _time(0), _size(0) {}
Entry(unsigned int d, unsigned int t, unsigned int s) : _date(d), _time(t), _size(s) {}
~Entry() {_size=0xfffffffe;}
unsigned int _date, _time, _size;
};
void initialise(std::vector<Entry> &vec)
vec.push_back(Entry(0x3f92, 0x9326, 0x1ae));
vec.push_back(Entry(0x3f92, 0x9326, 0x8a54));
//.... + a large number of other entries
}
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
if (e1._date < e2._date )
return true;
if (e1._time < e2._time )
return true;
return e1._size < e2._size;
}
int main (int argc, char * const argv[]) {
using namespace std;
vector<Entry> vec;
initialise(vec);
sort(vec.begin(), vec.end(), predicate);
vector<Entry>::iterator iter;
for (iter=vec.begin(); iter!=vec.end(); ++iter)
cout << iter->_date << ", " << iter->_time <<
", 0x" << hex << iter->_size << endl;
return 0;
}
The idea is that I sort the data first by date and time then by size. However, depending on the data in the vector, I will end up with 0xfffffffe in the size printed out at the end for the first object, indicating that a destroyed object has been accessed, or a seg fault during the sort.
(Xcode 3.2.4 - 64 bit intel target)
Any ideas anyone??
I suspect it has something to do with my predicate, but I can't see for the life of me what it is....!!
This page seems to refer to the same problem:
http://schneide.wordpress.com/2010/11/01/bug-hunting-fun-with-stdsort/
but the reason he gives (that the predicate needs to define a strict weak ordering) seems to be satisfied here...
Your predicate does not satisfy strict weak ordering criteria. Look at your function and ask yourself, what happens if e1's date comes after e2, but e1's time comes before e2?
I think what your predicate really should be is something like this:
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
return e1._date < e2._date ||
(e1._date == e2._date &&
(e1._time < e2._time ||
(e1._time == e2._time && e1._size < e2._size)));
}
What you wrote - if e1._date>e2._date, the first condition will be false, but the second may still be true and the function will still claim that e1<e2 which is probably not what you want.
Your code needs to be:
static bool predicate(const Entry &e1, const Entry &e2)
{
// Sort by date and time, then size
if (e1._date != e2._date )
return e1._data < e2._date;
if (e1._time != e2._time )
return e1._time < e2._time;
return e1._size < e2._size;
}
If e2's date is after e1, then your version treats goes on to compare the time and size. This is not what you want. This eventually confuses std::sort because if you swap e1 and e2 you will not get a consistent answer.