I need to use a Heap, so i've searched about the STL one, but it doesn't seem to work, i wrote some code to explain what i mean:
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
struct data
{
int indice;
int tamanho;
};
bool comparator2(const data* a, const data* b)
{
return (a->tamanho < b->tamanho);
}
int main()
{
std::vector<data*> mesas;
data x1, x2, x3, x4, x5;
x1.indice = 1;
x1.tamanho = 3;
x2.indice = 2;
x2.tamanho = 5;
x3.indice = 3;
x3.tamanho = 2;
x4.indice = 4;
x4.tamanho = 6;
x5.indice = 5;
x5.tamanho = 4;
mesas.push_back(&x1);
mesas.push_back(&x2);
mesas.push_back(&x3);
mesas.push_back(&x4);
mesas.push_back(&x5);
make_heap(mesas.begin(), mesas.end(), comparator2);
for(int i = 0 ; i < 5 ; i++)
{
data* mesa = mesas.front();
pop_heap(mesas.begin(),mesas.end());
mesas.pop_back();
printf("%d, %d\n", mesa->indice, mesa->tamanho);
}
return 0;
};
and this is what i get:
4, 6
2, 5
1, 3
3, 2
5, 4
So it's not working as a heap, as the maximum element on the vector is not being returned right.
Or am i doing something wrong?
You need to pass comparator2 to std::pop_heap since that is how you created the heap. Otherwise, it will use the default less than operator for pointers, which simply compares the pointer values.
MSN's answer is correct. However, either of a couple style guidelines can prevent this error:
Declare the comparator to work on references, not objects, as operator< would. Use a vector of objects, not pointers.
bool comparator2(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
}
You might really need the vector of pointers, in which case this doesn't apply.
Use std::priority_queue (from <queue>), which ties together pop_heap and pop_back for you, remembering your comparator. This requires a functor comparator:
struct comparator2 { bool operator()(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
} };
std::priority_queue<data, vector<data>, comparator2> mesas;
// or std::priority_queue<data, vector<data>, comparator2>
mesas.push(x1);
Most elegant way is to make this the default comparison for data:
struct data
{
int indice;
int tamanho;
friend bool operator<(const data& a, const data& b)
{
return (a.tamanho < b.tamanho);
}
};
std::priority_queue<data> mesas;
mesas.push(x1);
priority_queue can also take a prefilled unsorted container, which it will copy.
What about std::set
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include <set>
struct data
{
// Always put constructors on.
// When the constructor is finished the object is ready to be used.
data(int i,int t)
:indice(i)
,tamanho(t)
{}
int indice;
int tamanho;
// Add the comparator to the class.
// Then you know where to look for it.
bool operator<(data const& b) const
{
return (tamanho < b.tamanho);
}
};
int main()
{
std::set<data> mesas;
// Dont declare all your variables on the same line.
// One per line otherwise it is hard to read.
data x1(1,3);
data x2(2,5);
data x3(3,2);
data x4(4,6);
data x5(5,4);
mesas.insert(x1);
mesas.insert(x2);
mesas.insert(x3);
mesas.insert(x4);
mesas.insert(x5);
// You don't actually need the variables.
// You could have done it in place.
mesas.insert(data(6,100));
// Use iterator to loop over containers.
for(std::set<data>::iterator loop = mesas.begin(); loop != mesas.end(); ++loop)
{
printf("%d, %d\n", loop->indice, loop->tamanho);
}
return 0;
};
I had a similar problem and was able to solve it with something like this:
struct comparator2 { bool operator()(data const * const a, data const * const b)
{
return (a->tamanho < b->tamanho);
} };
std::priority_queue<data*, std::vector<data*>, comparator2> mesas;
Related
This question already has answers here:
Multi-Key Custom Sorting in C++
(4 answers)
Closed 3 years ago.
What should be the best approach to make multiple sortings in vector of pointers (std::vector<SomeObject*)? Should I make one sorting function for multiple data members and what would be the best way to do it? Or maybe I should make separate functions for each data member? Or maybe i should use some things from std::sort()?
Let's say i have some class Vehicle with some data members
class Vehicle{
private:
unsigned int _maxSpeed;
unsigned short _numberOfHorsePower;
unsigned short _numberOfGears;
unsigned short _numberOfWheels;
unsigned int _weight;
}
And I would like to have option to sort those objects by each data member e.g SortByMaxSpeedAsc(),SortByWeightAsc(),SortByWeightDesc().
sample sorting of one data member
void Vehicles::sortVehiclesByWeightAsc()
{
Vehicle* tmp;
size_t n = _VehiclesCollection.size();
int i, j, minIndex;
for (i = 0; i < n - 1; i++)
{
minIndex = i;
for (j = i + 1; j < n; j++)
{
if (_VehiclesCollection[j].weight < _VehiclesCollection[minIndex].weight)
{
minIndex = j;
}
}
if (minIndex != i)
{
tmp = _VehiclesCollection[i];
_VehiclesCollection[i] = _VehicleCollection[minIndex];
_VehiclesCollection[minIndex] = tmp;
}
}
}
Use std::sort and write a Compare functor for each of your cases. If you need to combine multiple fields, use std::tie.
Here's an example. You don't need to make the sorting functions static members like I've done here but it makes it possible to use std::tie with private members.
#include <algorithm>
#include <iostream>
#include <vector>
class Vehicle {
public:
Vehicle(unsigned maxSpeed, unsigned short numberOfHorsePower,
unsigned short numberOfGears, unsigned short numberOfWheels,
unsigned weight) :
maxSpeed_(maxSpeed),
numberOfHorsePower_(numberOfHorsePower), numberOfGears_(numberOfGears),
numberOfWheels_(numberOfWheels), weight_(weight) {}
// you can use the accessor functions with loose sort statements if you don't
// need to tie many columns together
inline unsigned get_maxSpeed() const { return maxSpeed_; }
inline unsigned short get_numberOfHorsePower() const {
return numberOfHorsePower_;
}
inline unsigned short get_numberOfGears() const { return numberOfGears_; }
inline unsigned short get_numberOfWheels() const { return numberOfWheels_; }
inline unsigned get_weight() const { return weight_; }
// sorting functions
template<typename It>
static void sort_on_speed(It first, It last) {
std::sort(first, last,
// a lambda receiving references to two Vehicle objects to compare
[](const Vehicle& a, const Vehicle& b) {
// return true if a is less than b (using the field you want)
return a.maxSpeed_ < b.maxSpeed_;
}
);
}
template<typename It>
static void sort_on_hp_plus_speed(It first, It last) {
std::sort(first, last,
// another lambda using std::tie to sort primarily on hp
// and secondary on speed
[](const Vehicle& a, const Vehicle& b) {
return std::tie(a.numberOfHorsePower_, a.maxSpeed_) <
std::tie(b.numberOfHorsePower_, b.maxSpeed_);
});
}
private:
unsigned int maxSpeed_;
unsigned short numberOfHorsePower_;
unsigned short numberOfGears_;
unsigned short numberOfWheels_;
unsigned int weight_;
};
int main() {
std::vector<Vehicle> cars = {
{1, 2, 3, 4, 5}, {2, 3, 4, 5, 1}, {3, 4, 5, 1, 2}
};
// sort on speed
Vehicle::sort_on_speed(cars.begin(), cars.end());
// sort on hp + speed
Vehicle::sort_on_hp_plus_speed(cars.begin(), cars.end());
}
boost.multiindex should be very useful.
I am trying to create a set of a structure but my code keeps getting errors, and I can't find anything online.
struct smove {
int src;
int dst;
};
int main()
{
smove moov;
moov.dst = 1;
moov.src = 2;
set<struct smove> moovs = {moov};
return 0;
}
Set's value type needs to be less-than comparable. That's how the container knows how elements relate to one another and in what order (including ensuring no duplicates).
Short story, make an operator< for smove.
The long story is, well, longer, because that operator is required to work in a certain way, but you can read up on that. For now, here's a simple example that uses std::tie to get a legal ordering quickly:
#include <set>
#include <tuple>
struct smove
{
int src;
int dst;
};
bool operator<(const smove& lhs, const smove& rhs)
{
return std::tie(lhs.src, lhs.dst) < std::tie(rhs.src, rhs.dst);
}
int main()
{
smove moov;
moov.dst = 1;
moov.src = 2;
std::set<smove> moovs = {moov};
}
I am making a Pentago game for someone, and I wanted to write a good code, so I decided to use operator overloading.
We have 2 classes; first one is Block class (which represents every single block of the board) and the second one is Set class (which represents a 3*3 table of blocks).
Now I want to use Set as a 2d array so I can use set[foo][foo].
Can you help me to make an operator like this?
A very simple solution is
struct MyClass {
int x[3][3];
int* operator[](int row) { return &(x[row][0]); }
};
i.e. returning an element* from operator[].
This allows using
myinstance[row][col]
There are (at least) two ways to go here.
The first is to make something like a set_row class, which is a proxy. So you'd have something like
class set
{
public:
set_row operator[](size_t row)
{
// Return a proxy object that just sees the correct row.
return set_row(internal_buffer_pointer[row]);
}
...
};
where set_row is something like
class set_row
{
public:
// Ctor takes a row
// Take a column, and return a reference to the correct column in the row.
element &operator[](size_t column);
};
From experience (on, ahem, VisualC++), this was slow, as it will need to construct a proxy object for each access.
The second is to forgo operator[], and use operator():
class set
{
public:
element &operator()(size_t row, size_t col);
...
};
It would be nice using operator[], but, unfortunately, you can't do that with it.
There is no operator[][]. If you want to provide those semantics you need to overload operator[] such that it returns another object that also overloads operator[].
Your case can be solved using a vector of vectors:
#include <vector>
#include <cstdint>
#include <iostream>
struct Block
{
int value = 0;
};
class Set
{
std::vector<std::vector<Block> > grid;
public:
Set(): grid(3, std::vector<Block>(3)) {} // 3 x 3
std::vector<Block>& operator[](std::size_t x) { return grid[x]; }
};
int main()
{
using std::size_t;
Set set;
set[1][1].value = 1;
for(size_t x = 0; x < 3; ++x)
{
for(size_t y = 0; y < 3; ++y)
{
std::cout << set[x][y].value << ' ';
}
std::cout << '\n';
}
}
Output:
0 0 0
0 1 0
0 0 0
This works because Set::operator[] returns reference to a std::vector and the std::vector overloads operator[] to return a reference to a Block.
There is no way to supply an operator[][] for a class.
However, if your Set supplies an operator[](), that operator can return a reference to something else that also has an operator[]().
For example;
class Row
{
public:
Block &operator[](int block_no) {return data[block_no];};
private:
std::vector<Block> data;
};
class Set
{
public:
Row &operator[](int row_no) {return row[row_no];};
private:
std::vector<Row> row;
};
int main()
{
Set s;
// assume s is set up appropriately
Block b = s[2][3]; // equivalent to s.operator[](2).operator[](3)
}
Obviously, it is also necessary to do relevant error checking, set up the contents of the classes correctly, etc.
Assuming the memory is contiguous, you can return a pointer to the first element of the row.
Working example
#include <iostream>
class MyType
{
public:
static const size_t rows = 3;
static const size_t columns = 3;
static const size_t size = rows * columns;
MyType()
{
for(size_t index = 0; index < 9; ++index)
{
data[index] = index;
}
}
int* operator[](size_t index)
{
return &data[rows * index];
}
private:
int data[size];
};
int main()
{
MyType instance;
std::cout << instance[2][1] << std::endl;
}
Quite often I have two variables foo1 and foo2 which are numeric types. They represent the bounds of something.
A user supplies values for them, but like a recalcitrant musician, not necessarily in the correct order!
So my code is littered with code like
if (foo2 < foo1){
std::swap(foo2, foo1);
}
Of course, this is an idiomatic sort with two elements not necessarily contiguous in memory. Which makes me wonder: is there a STL one-liner for this?
I suggest to take a step back and let the type system do the job for you: introduce a type like Bounds (or Interval) which takes care of the issue. Something like
template <typename T>
class Interval {
public:
Interval( T start, T end ) : m_start( start ), m_end( end ) {
if ( m_start > m_end ) {
std::swap( m_start, m_end );
}
}
const T &start() const { return m_start; }
const T &end() const { return m_end; }
private:
T m_start, m_end;
};
This not only centralizes the swap-to-sort code, it also helps asserting the correct order very early on so that you don't pass around two elements all the time, which means that you don't even need to check the order so often in the first place.
An alternative approach to avoid the issue is to express the boundaries as a pair of 'start value' and 'length' where the 'length' is an unsigned value.
No, but when you notice you wrote the same code twice it's time to write a function for it:
template<typename T, typename P = std::less<T>>
void swap_if(T& a, T& b, P p = P()) {
if (p(a, b)) {
using std::swap;
swap(a, b);
}
}
std::minmax returns pair of smallest and largest element. Which you can use with std::tie.
#include <algorithm>
#include <tuple>
#include <iostream>
int main()
{
int a = 7;
int b = 5;
std::tie(a, b) = std::minmax({a,b});
std::cout << a << " " << b; // output: 5 7
}
Note that this isn't the same as the if(a < b) std::swap(a,b); version. For example this doesn't work with move-only elements.
if the data type of your value that you're going to compare is not already in c++. You need to overload the comparison operators.
For example, if you want to compare foo1 and foo2
template <class T>
class Foo {
private:
int value; // value
public:
int GetValue() const {
return value;
}
};
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.GetValue() < rhs.GetValue());
}
If your value is some type of int, or double. Then you can use the std::list<>::sort member function.
For example:
std::list<int> integer_list;
int_list.push_back(1);
int_list.push_back(8);
int_list.push_back(9);
int_list.push_back(7);
int_list.sort();
for(std::list<int>::iterator list_iter = int_list.begin(); list_iter != int_list.end(); list_iter++)
{
std::cout<<*list_iter<<endl;
}
here is the C++ sample
int a[1000] = {3,1,5,4}
int b[1000] = {7,9,11,3}
how do i make it so if i sort array a, array b also following array a
example
a[1000] = {1,3,4,5}
b[1000] = {9,7,3,11}
is it possible using sort function
sort(a,a+4)
but also sort array b aswell ?
edit: what if there are 3 arrays ?
Instead of using two arrays, can you use an array of pairs and then sort THAT using a special comparison functor rather than the default less-than operator?
The simplest way is to rearrange your data into an array-of-structs instead of a pair of arrays so that each datum is contiguous; then, you can use an appropriate comparator. For example:
struct CompareFirst
{
bool operator() (const std::pair<int,int>& lhs, const std::pair<int,int>& rhs)
{
return lhs.first < rhs.first;
}
};
// c[i].first contains a[i], c[i].second contains b[i] for all i
std::pair<int, int> c[1000];
std::sort(c, c+1000, CompareFirst());
If you can't refactor your data like that, then you need to define a custom class that acts as a RandomAccessIterator:
struct ParallalArraySortHelper
{
ParallelArraySortHelper(int *first, int *second)
: a(first), b(second)
{
}
int& operator[] (int index) { return a[index]; }
int operator[] const (int index) { return a[index]; }
ParallelArraySortHelper operator += (int distance)
{
a += distance;
b += distance;
return *this;
}
// etc.
// Rest of the RandomAccessIterator requirements left as an exercise
int *a;
int *b;
};
...
int a[1000] = {...};
int b[1000] = {...};
std::sort(ParallalArraySortHelper(a, b), ParallelArraySortHelper(a+1000, b+1000));
Generate an array the same size as the original, containing the indexes into the array: {0, 1, 2, 3}. Now use a custom comparator functor that compares the elements in an associated array rather than the indexes themselves.
template<typename T>
class CompareIndices
{
public:
CompareIndices(const T * array) : m_AssociatedArray(array) {}
bool operator() (int left, int right) const
{
return std::less(m_AssociatedArray[left], m_AssociatedArray[right]);
}
private:
const T * m_AssociatedArray;
};
std::sort(i, i+4, CompareIndices(a));
Once you have a sorted list of indices, you can apply it to the original array a, or any other b array you want.