Efficient way to remove duplicates - c++

Following the answer in this thread "What's the most efficient way to erase duplicates and sort a vector?". I wrote the following code, but I got an error complaing no match for ‘operator<’ (operand types are ‘const connector’ and ‘const connector’) blahblah...
connector is a class I wrote myself, it basically is a line with two geometry points. uniqCntrs is a std::vector. It has 100% duplicates in it, which means each element has a duplicate, the size of uniqCntrs is quite big. What's wrong with my code, and how to deal with this situation?
std::set<connector> uniqCntrsSet;
for(unsigned int i = 0; i < uniqCntrs.size(); ++i )
{
uniqCntrsSet.insert(uniqCntrs[i]);
}
uniqCntrs.assign(uniqCntrsSet.begin(), uniqCntrsSet.end());
Edit:
I have no idea how to define < operator for my connector class. I mean it is physically meaningless to say one line is smaller than the other.

From cppreference:
std::set is an associative container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function Compare.
The second template argument of std::set, Compare, is defaulted to std::less which by defaults compares the objects with operator<. To fix the issue you can simply define operator< for your Key type (connector that is).

Actually the operator< is just used to efficiently order the map which is used by std::set. It does not need to make any sense. The only requirement is that the operator satisfy the standard mathematical definition of a strict weak ordering.
Look at this point example:
class Point
{
public:
Point(int x, int y) : x(x), y(y) {
}
public:
bool operator==(const Point &other) const {
return x==other.x && y==other.y;
}
bool operator!=(const Point &other) const {
return !operator==(other);
}
bool operator<(const Point &other) const {
if (x==other.x) {
return y<other.y;
} else {
return x<other.x;
}
}
private:
int x;
int y;
};

Related

trouble inserting a struct into a set C++

I am working on an A* pathfinding algorithm, but am having trouble with an error I receive when i insert a struct called node into a set. The error reads: "Error 1 error C2678: binary '<' : no operator found which takes a left-hand operand of type 'const node' (or there is no acceptable conversion)" but the error is found in another file 'XSTDDEF' which i haven't looked at. I am unsure of what it means.
struct node{
int f;
int g;
int h;
int x;
int y;
};
node coords[24] = { -1 };
std::set<node> open;
std::set<node> closed;
int main(int argc, char *argv[]){
coords[4].g = 0;
coords[4].h = heuristic(start, end, start.h);
coords[4].f = start.g + start.h;
coords[4].x = 4;
coords[4].y = 0;
open.insert(coords[4]);
As you may know if you've familiarized yourself with the documentation of std::set, it is an ordered container. Therefore there must be a way to compare the elements of the set so that they can be ordered. From the documentation, we know that the default comparison functor of std::set is std::less<T>.
Further, as you may know, std::less<T> does:
Unless specialized, invokes operator< on type T.
Since std::less isn't specialized for node, it uses operator<.
The error message tells you that an overload for operator< does not exist that would have const node (or anything that a node could be converted to) as the left operand.
The solution is to define such overload.
std::set is an ordered container so it needs operator< to compare and order elements, allowing fast search. If you don't need this you can use list or vector.
You need to create operator<or specialize std::less for your struct. Another solution could be to use std::array:
struct node : std::array<5,int> {
int &f() { return data()[0]; }
int &g() { return data()[1]; }
int &h() { return data()[2]; }
int &x() { return data()[3]; }
int &y() { return data()[4]; }
};
and you will inherit operator< from it. Another benefit - you can access underlying data as array, which would simplify serializing etc.

C++ STL Binary Search (lower_bound, upper_bound)

I have implemented a binary search like this:
typedef std::vector<Cell>::iterator CellVectorIterator;
typedef struct _Point {
char x,y;
} CoordinatePoint;
typedef struct _Cell {
...
CoordinatePoint coordinates;
} Cell;
struct CellEqualityByCoordinates
{
bool
operator()(const Cell& cell1, const Cell& cell2) const
{ return cell1.coordinates.x == cell2.coordinates.x && cell1.coordinates.y == cell2.coordinates.y; }
};
CellVectorIterator FindCellByCoordinates (CellVectorIterator first, CellVectorIterator last, const Cell &val)
{
return std::upper_bound(first, last, val, CellEqualityByCoordinates());
}
But it doesn't always find a value.
What's wrong with that?
Your comparison function will not work for a binary search. It is not supposed to determine equality, it is supposed to determine an order relation. Specifically, it should return true if the first argument would definitively come before the second in a sorted range. If the arguments should be considered equal, or the second would come before the first, it should return false. Your range also needs to be sorted by this same criteria in order for the binary search to work.
An example function that might work:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
if (cell1.coordinates.x < cell2.coordinates.x) return true;
if (cell2.coordinates.x < cell1.coordinates.x) return false;
return cell1.coordinates.y < cell2.coordinates.y;
}
A similar example that doubles as a lesson in short-circuit boolean evaluation would be something like:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return (cell1.coordinates.x < cell2.coordinates.x) ||
(!(cell2.coordinates.x < cell1.coordinates.x) &&
cell1.coordinates.y < cell2.coordinates.y);
}
Both exhibit a property called strict weak ordering. It is frequently required for various sorting and/or searches in standard library collections and search algorithms.
Yet another example utilizes a std::pair, which already has a proper std::less overload available that does the above, and thus makes this considerably less complicated:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return std::make_pair(cell1.coordinates.x, cell1.coordinates.y) <
std::make_pair(cell2.coordinates.x, cell2.coordinates.y);
}
A similar algorithm is available for tuples via std::tie.
Of course, all of this assumes you have an actual ordered sequence in the first place, ordered by the same comparison logic. (which we can only assume is true, as no evidence of such was posted).

C++ Map<>.find() overloading for different class

I try to use a map which is defined as :
map<Vertex,unsigned int> _addedVertices;
now when I use the find function to check if a vertex is already inside
I get an iterator to a wrong vertex with different information, so I have tried the following :
map<Vertex,unsigned int,cmpByVertexFields> _addedVertices;
which didn't help.
also I have the following overloaded functions inside the Vertex class.
bool operator<(const Vertex &otherV)const{
return(_x<otherV._x && _y<otherV._y && _z<otherV._z);
}
bool operator==(const Vertex &otherV)const{
return _x==otherV._x && _y==otherV._y && _z==otherV._z;
}
but nothing works.
Example:
I've inserted a vertex containing (0.2,0.1,0.4)
and next thing I use is the find function with (0.2,0.15,0.41)
the iterator I get is of the first vertex instead of map.end().
What did I forget to define?
Thanks
edit: cmpByVertexFields :
struct cmpByVertexFields {
bool operator()(const Vertex& a, const Vertex& b) const {
return a.getX()==b.getX() &&
a.getY()==b.getY() &&
a.getZ()==b.getZ();
}
};
This is your culprit
bool operator<(const Vertex &otherV)const{
return(_x<otherV._x && _y<otherV._y && _z<otherV._z);
}
This doesn't yield strict weak ordering.
You need something like this
bool operator<(const Vertex &otherV)const{
if(_x != otherV.x)
return _x < otherV.x;
if(_y != otherV.y)
return _y < otherV.y;
return _z < otherV.z;
}
Or, equivalently and more conveniently, compare them as tuples, using std::tie
bool operator<(const Vertex &otherV)const{
return std::tie(x_, y_, z_) < std::tie(OtherV.x_, OtherV.y_, OtherV.z_);
}
As Juan said in a comment, your operator < implementation is semantically incorrect. Since you’re talking about vertices, you actually need to implement a lexicographical comparison across _x, _y and _z.
The easiest way is to use the std::tuple built-in comparison:
bool operator<(const Vertex &otherV)const{
return std::tie(_x, _y, _z) < std::tie(otherV._x, otherV._y, otherV._z);
}
Using std::tie in this way is now the established way of implementing a lexicographical comparison across (member) variable (and you can actually use the same for the operator== implementation).
The comparison functor or less than operator< must implement a strict weak ordering. This is a requirement for an std::map. Yours does not. There is no obvious and natural way to order 3D vertices, but if you want to order lexicographically by x, y and then z coordinate, then the easiest way is to use std::tie (or boost::tie or std::tr1::tie if you do not have C++11 support):
bool operator<(const Vertex &otherV)const{
return std::tie(_x, _y, _z) < std::tie(otherV._x, otherV._y, otherV._z);
}
Note that this ordering is completely arbitrary: why would x take precedence over y? It is up to you to implement the ordering that suits the problem you are trying to solve. On the other hand, if you don't care about the actual ordering of the elements of the map, any strict weak ordering will do.

C++ std::set Find function overloading == operator

I am using sets. I use a custom struct as the key. I am inserting a value and trying to find the inserted value. But it never seems to find the element.
I have overridden both the == operator and the < operator.
Here is the code of the structure:
struct distance_t
{
public:
int id;
double distance;
bool operator<(const distance_t& rhs) const
{
if(distance < rhs.distance)
return true;
else
return false;
}
bool operator==( const distance_t& rhs)
{
if(id == rhs.id)
return true;
else
return false;
}
};
And this is the code of main
int main()
{
set<distance_t> currentSet;
distance_t insertDistance;
insertDistance.id =1;
insertDistance.distance = 0.5;
currentSet.insert(insertDistance);
distance_t findDistance;
findDistance.id = 1;
assert(currentSet.find(findDistance) != currentSet.end());
}
It always fails in the assert statement. What am I doing wrong?
Edit -Ok now I understand that it does not use the == operator at all. Here is what I want. I need the data structure to be ordered by distance. But I should be able to remove it using the id. Is there any clean way or already existing datastructure to do this?
It fails because your less-than comparison uses distance_t::distance, which you are not setting in findDistance:
distance_t findDistance;
findDistance.id = 1;
std::set does not use operator== for anything. It only uses operator<. So you would have to change it's logic to use distance_t::id.
If you want to search by id without changing the set's ordering, you can use std::find:
set<distance_t>::iterator it = std::find(currentSet.begin(),
currentSet.end(),
findDistance);
This will use your operator==. Bear in mind that this has linear time complexity.
Because operator== is not invoked at all. Comparing elements is like:
!(a < b) && !(b < a)
In other words, it uses operator<.
As you haven't assigned a value to findDistance.distance the result of the less then comparison is undefined.
Note that your definitions of the equality and less then comparison operators is dangerous, because it is easy to define instances of distance_t where their result is inconsistent. One example is two instances with the same distance but different id's.

std::map::find()

I have a simple struct which i'm using as a key in a std::map
struct PpointKey{
unsigned int xp,yp; //pixel coordinates
unsigned int side;
PpointKey(unsigned xp,unsigned yp,unsigned side=5):xp(xp),yp(yp),side(side)
{}
bool operator==(const PpointKey& other) const{
const unsigned int x = other.xp;
const unsigned int y = other.yp;
return ((x>=xp && x<=xp+side) && (y>=yp && y<=yp+side));
}
bool operator<(const PpointKey& other) const{
const unsigned int x = other.xp;
const unsigned int y = other.yp;
const unsigned other_distance_2 = x*x + y*y;
const unsigned this_distance_2 = this->xp*this->xp + this->yp * this->yp;
return this_distance_2 < other_distance_2;
}
};
What I would like to achieve is to use the find() to access the map with a key that has its xp,yp attributes within a side distance. In other words, if I have an (x,y) tuple, I would like to find inside the map the first PpointKey that fulfils the condition inside the operator== function
return ((x>=xp && x<=xp+side) && (y>=yp && y<=yp+side));
Is this possible using find? I'm getting map.end(), so I would like to check wheter the find () function uses the operator==. Maybe the search algorithm would be better?
Thanks in advance.
The find function of map does not use the operator==.
However you can use std::find, passing in the begin() and end() iterator of map. It will simply iterate through the sequence one at a time and yield the first object that matches (complexity is linear).
The issue you encounter is due to the fact that you have abused operator overload. The problem here is that the common definition of operator== is:
T operator==(T lhs, T rhs)
{
return !(lhs < rhs) && !(rhs < lhs);
}
And this is not the case with your definition, thus you cannot substitute one for the other.
It would be best if you used traditional functions with expressive names rather than operator overloading, it would be less misleading. Note that map and std::find allow you to pass suitable predicate objects, you don't need to overload the operators to use them.