I wrote this routine to order items, keep only unique items, where it takes in an array of type T, and the size of the array. It returns the new size of the array after processing.
template <class T>
int reduce(T array[], int size) {
T *begin = array;
T *end = array + size;
sort(begin, end);
T *end_new = unique(begin, end);
return end_new - array;
}
My question is I was expecting it to sort const char *data like
{"aa", "bb", "bc", "ca", "bc", "aa", "cc", "cd", "ca", "bb"};
into //aa bb bc ca cc cd
However it does it the opposite way, : "cd cc ca bc bb aa"
Why does it do that? Does it not use the standard string comparisons? If I wanted to, how could I alter it so it would order const char * alphabetically? thanks.
sort() uses operator< per default, which would just compare the addresses in your case.
If you want to sort C-strings, you have to pass a comparator to sort(). To do this generically you can let the user pass a comparator, use specialization on a comparator function or a combination of these:
template<class T> bool my_comp(T a, T b) {
return a < b;
}
template<> bool my_comp<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) < 0;
}
template<class T, class Comp>
int reduce(T array[], size_t size, Comp comp = my_comp<T>) {
// ...
std::sort(begin, end, comp);
// ...
}
std::sort uses < by default, < on chars has nothing to do with their lexicographic ordering. You can provide an additional parameter to sort to tell it how to compare, or you can use an array of std::string or similar instead of char.
const char*'s < operator performs pointer comparison, not string data comparison. Either use std::string for your string data, or specialize reduce so that it calls sort with a special comparator based on strcmp. I'm guessing you got the output you did because your compiler decided to reverse-alphabetize all of its string constants in memory.
unique also isn't doing anything -- it only works in the first place because your compiler pooled all of the strings in memory at compile time, so that all of the "bb" strings would use the same memory. If you read the exact same strings from a file, your array wouldn't change. The solutions to this problem are the same.
Related
Iam beginner in C++, and I want to use insert function of unordered multiset pointer below to add new element:
struct Customer {
size_t operator()(const char& c) const;
};
unordered_multiset<char, Customer>* ms
can any one help?
void populate_multiset(const string& s, unordered_multiset<char, CustomHasher>* ms)
Given this function accepts a string and your unordered_multiset accepts a char, You can only insert a char
for(size_t i = 0; i<s.size(); i++) {
ms->insert(s[i]); // insert each individual char
}
Or use the iterators to insert a range of char
ms->insert(s.begin(), s.end());
Also, since the standard library already provides a way to hash a char. You can simply declare
unordered_multiset<char> ms;
However, if you do want to provide a custom hash function, you can. And the syntax is exactly like what you have in your question.
And a far more common way to pass in a container to a function is through reference. e.g.
void populate_multiset(const string& s, unordered_multiset<char, CustomHasher>& ms)
Then, you can use . instead of -> to do the exact same thing.
Below is the code and
std::string str[5] = {"Tejas","Mejas","Rajas","Pojas","Ljas"};
std::sort(str,str+5);
size_t test = bin_search("Ljas",str,5);
Here is the generic function for binary search
template<class T>
size_t bin_search(T x, T* array, int n)
{
size_t begin = 0, end = n;
// Invariant: This function will eventually return a value in the range [begin, end]
while (begin != end) {
size_t mid = (begin + end) / 2;
if (array[mid] < x) {
begin = mid + 1;
} else {
end = mid;
}
}
return begin; // Or return end, because begin == end
}
And the Error is
main.cpp|12|error: no matching function for call to 'bin_search(const char [5], std::string [5], int)'|
There is a problem with only the std::string array, but the int array works really fine.
Does it work with string arrays or is there anything missing in the logic?
As the error message tried to tell you, "Ljas" is not std::string, it's const char[5]. Then the template argument deduction failed since the type T could not be deduced (as const char* or std::string).
You could explicitly cast it to std::string to make template argument deduction work well:
size_t test = bin_search(std::string("Ljas"),str,5);
or explicitly specify the template argument to avoid template argument deduction:
size_t test = bin_search<std::string>("Ljas",str,5);
template<class T>
size_t bin_search(T x, T* array, int n)
Expects that you recieve a T and a pointer to T. When the compiler deducts the types in
size_t test = bin_search("Ljas",str,5);
x is deduced as a const char[5] as all string literals have the type const char[N]. array is deduced std::strign[5]. Since a cont char[] and a std::string[] are not the same type the no function will be generated. You need to make "Ljas" a string like
size_t test = bin_search(std::string("Ljas"),str,5);
Also note that the collection passed to a binary search needs to be sorted. If the data is not sorted then you cannot reason what half the element should be in.
size_t test = bin_search(std::string("Ljas"), str, 5); maybe?
You algorithm won't work even if you fix your compile error because a binary search requires a sorted array.
It isn't necessary to write your own binary search algorithm, the STL already has one: http://en.cppreference.com/w/cpp/algorithm/binary_search
Your first argument should be a std::string, not a string literal: bin_search(std::string("Ljas"),str,5);
Here is my problem:
I have a struct:
struct point
{
int x;
int y;
};
and then I have an array:
for (int i = 0;i < n;i++)
{
arr[i].x=rand() % n + 1;
}
I defined the quicksort function as follows:
void quicksort(int *a, int left, int right);
and I want to sort the point by X coordinate, so I call the quicksort:
quicksort(arr.x, 0, n-1);
And this is the error message:
error: request for member 'x' in 'arr', which is of non-class type 'point [(((unsigned int)(((int)n) + -0x000000001)) + 1)]'
Sorry if the question is too stupid or badly formulated, the truth is I'm a newbie and I'm really willing to learn as much as possible and I'd be very thankful for your help!
If you always want to sort by x, then you can hard-code it into the sort function, and just pass a pointer to the array to sort:
void quicksort(point * arr, int left, int right) {
// test points with
// if (arr[i].x < arr[j].x) {/* i sorts before j */}
}
quicksort(arr, 0, n-1);
To specify a class member to sort by, you need a pointer-to-member, not a pointer; something like:
void quicksort(point * arr, int point::*member, int left, int right){
// test points with
// if (arr[i].*member < arr[j].*member) {/* i sorts before j */}
}
quicksort(arr, &point::x, 0, n-1);
More generically, you could follow the example of std::sort and accept any comparison functor:
template <typename RandIter, typename Compare>
void quicksort(RandIter begin, RandIter end, Compare compare) {
// test points with
// if (compare(*it1, *it2)) {/* *it1 sorts before *it2 */}
}
quicksort(arr, arr+n,
[](point const &lhs, point const &rhs) {return lhs.x < rhs.x;});
And of course, unless you're learning how to implement a sorting algorithm, just use std::sort.
quicksort(arr,0,n-1);
then within quicksort, try to compare the arr[i].x
There are a few problems with your code.
1. quicksort accepts int* but you try to pass int value x
2. You try to pass int but you actually call an undefined variable arr.x
What you need to do is either call in the form of &arr[i].x, but to accomplish what you want, you probably want to pass the entire struct as a pointer.
You need to pass arr as the parameter, as that is the array to be sorted. arr.x is meaningless. You are not passing the string "arr.x" as a parameter which can somehow be interpreted as meaning sort on the x field - when the compiler sees this, it is looking for an x element of arr, which doesn't exist, as the error message suggests - only the elements of arr (e.g. arr[0]) have x elements (accessed as arr[0].x).
Assuming this is for academic purposes (why else would you declare your own sorting algorithm instead of using one of the ones already implemented with a custom comparator?), you can do this a few ways:
Array
std::array<point, 10> myArray; // declares an array of size 10 for points
template<size_t N>
void quicksort(std::array<point, N>& arr, ...)
{
// implement sort operating on arr
}
Vector
std::vector<point> myVector; // declares a dynamic array/vector of points
void quicksort(std::vector<point>& arr, ...)
{
// implement sort operating on arr
}
If for some god-awful reason, you want to keep it in C:
Legacy
const size_t SIZE = 10;
point arr[SIZE]; // declare an array of 10 points
void quicksort(point* p, const size_t n, ...)
{
// implement sort operating on elements in p passing in SIZE for n
}
I'd rather defined the function as:
void quicksort(void *a,int left,int right, size_t size, int (*fp)(void*,void*));
size is the size of one element of array and fp is a compare function which returns true if the two arguments are equal. Now you can pass the call the function as:
quicksort(arr,0,n-1,sizeof(arr)/sizeof(arr[0]), compare);
where function compare is something like:
int compare(void* a, void* b) { return *((int*)a) >= *((int*)b); }
Rest of the implementation of function is trivial I think.
(almost) never try to fool the system by passing a pointer to a member when you really want to pass a pointer to an object. Do as Grijesh suggested. Passing a member can lead to horrible side effects. For example, quicksort is going to sort all the integers together, regardless of which of them are X's and which are Y's. In milder cases you may get wrong compare criteria, and often hard to debug effects such as incorrect pointer optimization. Just be honest with the compiler and pass the object pointer if you need to pass an object pointer. There are very very very few exceptions, mostly to do with low-level system programming where the "other side' of the function call won't be able to handle the object.
I am writing my own vector class, Vector, with the data members: T* array, size_t vector_size and size_t capacity. I am trying to create a sort() method:
template <class T>
void Vector<T>::sort(bool ascending)
{
std::sort(array,array+vector_size);
if(ascending==false)
std::reverse(array,array+vector_size);
}
It works fine when the elements in the array are of type int, char etc. But when I try to sort a vector consisting of Vector elements it won't compile.
From what I have read I need to define the <operator in some way, but I really don't know how to do that...
I have tried:
template <class T>
bool Vector<T>::operator<(Vector<T> & source) const
{
return (vector_size < source.vector_size);
}
My main look like this:
int main() {
Vector<int> v1(5,1);
Vector<int> v2(7,2);
Vector<int> v3(3,3);
Vector<Vector<int>> v4;
v4 = {v1,v2,v3};
v4.sort(1);
return 0;
}
This is one of the errors I get:
/usr/include/c++/4.6/bits/stl_algo.h:2212:4: error: no match for ‘operator<’ in ‘* __first < __pivot’
You provided a comparison method with the wrong signature. You need to accept a const reference or a value, but not a (modifiable) reference to your type, while the former should be preferred unless it's a primitive type. So the signature of your comparison method should look like this:
template <class T>
bool Vector<T>::operator<(const Vector<T> & source) const
{
return (vector_size < source.vector_size);
}
This is because std::sort (and a lot of other methods) are designed to not modify the contents. This is guaranteed if they take a value (but this will be slow for large types) or a const reference.
Note that you defined the comparison method to compare the size of the vectors, not their contents. All your vectors are of equal length. So they are treated to be equal by std::sort. So std::sort wouldn't change v4... If you intend to compare the contents in a way similar to string comparison (the first entry counts first, if equal then take the next and so on...), use this:
template <class T>
bool Vector<T>::operator<(const Vector<T> & source) const
{
for(int i = 0; i < size && i < source.size; ++i) {
if(*this[i] < source[i])
return true;
else if(source[i] < *this[i])
return false;
}
// You have to decide what to do if the length isn't equal.
// But if the vectors are really equal than return false:
if(size == source.size)
return false;
}
Your forgot a const!
template <class T>
bool Vector<T>::operator<(const Vector<T> & source) const // <- here
{
return (vector_size < source.vector_size);
}
One thing you'd need is to use const in the parameter to your operator, otherwise it can't match anything that is read-only (which would be the common case).
Keep in mind though that sorting vectors-of-vectors would copy entire vectors every time swaps occur. This will not be particularly efficient. If the vectors were stored separately and you had something like vector-of-pointer-to-vector, at least the sorting would be faster.
Be sure to read the definition of "strict weak ordering", too. It is very important for the ordering to be consistent with itself, or standard algorithms like std::sort() can badly misbehave (to the point of corrupting memory in some implementations).
I'm trying to sort a concurrent_vector type, where hits_object is:
struct hits_object{
unsigned long int hash;
int position;
};
Here is the code I'm using:
concurrent_vector<hits_object*> hits;
for(i=0;...){
hits_object *obj=(hits_object*)malloc(sizeof(hits_object));
obj->position=i;
obj->hash=_prevHash[tid];
hits[i]=obj;
}
Now I have filled up a concurrent_vector<hits_object*> called hits.
But I want to sort this concurrent_vector on position property!!!
Here is an example of what's inside a typical hits object:
0 1106579628979812621
4237 1978650773053442200
512 3993899825106178560
4749 739461489314544830
1024 1629056397321528633
5261 593672691728388007
1536 5320457688954994196
5773 9017584181485751685
2048 4321435111178287982
6285 7119721556722067586
2560 7464213275487369093
6797 5363778283295017380
3072 255404511111217936
7309 5944699400741478979
3584 1069999863423687408
7821 3050974832468442286
4096 5230358938835592022
8333 5235649807131532071
I want to sort this based on the first column ("position" of type int). The second column is "hash" of type unsigned long int.
Now I've tried to do the following:
std::sort(hits.begin(),hits.end(),compareByPosition);
where compareByPosition is defined as:
int compareByPosition(const void *elem1,const void *elem2 )
{
return ((hits_object*)elem1)->position > ((hits_object*)elem2)->position? 1 : -1;
}
but I keep getting segmentation faults when I put in the line std::sort(hits.begin(),hits.end(),compareByPosition);
Please help!
Your compare function needs to return a boolean 0 or 1, not an integer 1 or -1, and it should have a strongly-typed signature:
bool compareByPosition(const hits_object *elem1, const hits_object *elem2 )
{
return elem1->position < elem2->position;
}
The error you were seeing are due to std::sort interpreting everything non-zero returned from the comp function as true, meaning that the left-hand side is less than the right-hand side.
NOTE : This answer has been heavily edited as the result of conversations with sbi and Mike Seymour.
int (*)(void*, void*) is the comparator for C qsort() function. In C++ std::sort() the prototype to the comparator is:
bool cmp(const hits_object* lhs, const hits_object* rhs)
{
return lhs->position < rhs->position;
}
std::sort(hits.begin(), hits.end(), &cmp);
On the other hand, you can use std::pair struct, which by default compares its first fields:
typedef std::pair<int position, unsigned long int hash> hits_object;
// ...
std::sort(hits.begin(), hits.end());
Without knowing what concurrent_vector is, I can't be sure what's causing the segmentation fault. Assuming it's similar to std::vector, you need to populate it with hits.push_back(obj) rather than hits[i] = j; you cannot use [] to access elements beyond the end of a vector, or to access an empty vector at all.
The comparison function should be equivalent to a < b, returning a boolean value; it's not a C-style comparison function returning negative, positive, or zero. Also, since sort is a template, there's no need for C-style void * arguments; everything is strongly typed:
bool compareByPosition(hits_object const * elem1, hits_object const * elem2) {
return elem1->position < elem2->position;
}
Also, you usually don't want to use new (and certainly never malloc) to create objects to store in a vector; the simplest and safest container would be vector<hits_object> (and a comparator that takes references, rather than pointers, as arguments). If you really must store pointers (because the objects are expensive to copy and not movable, or because you need polymorphism - neither of which apply to your example), either use smart pointers such as std::unique_ptr, or make sure you delete them once you're done with them.
The third argument you pass to std::sort() must have a signature similar to, and the semantics of, operator<():
bool is_smaller_position(const hits_object* lhs, const hits_object* rhs)
{
return lhs->position < rhs->position;
}
When you store pointers in a vector, you cannot overload operator<(), because smaller-than is fixed for all built-in types.
On a sidenote: Do not use malloc() in C++, use new instead. Also, I wonder why you are not using objects, rather than pointers. Finally, if concurrent_vector is anything like std::vector, you need to explicitly make it expand to accommodate new objects. This is what your code would then look like:
concurrent_vector<hits_object*> hits;
for(i=0;...){
hits_object obj;
obj.position=i;
obj.hash=_prevHash[tid];
hits.push_back(obj);
}
This doesn't look right:
for(i=0;...){
hits_object *obj=(hits_object*)malloc(sizeof(hits_object));
obj->position=i;
obj->hash=_prevHash[tid];
hits[i]=obj;
}
here you already are sorting the array based on 'i' because you set position to i as well as it becomes the index of hits!
also why using malloc, you should use new(/delete) instead. You could then create a simple constructor for the structure to initialize the hits_object
e.g.
struct hits_object
{
int position;
unsigned int hash;
hits_object( int p, unsigned int h ) : position(p), hash(h) {;}
};
then later write instead
hits_object* obj = new hits_object( i, _prevHash[tid] );
or even
hits.push_back( new hits_object( i, _prevHash[tid] ) );
Finally, your compare function should use the same data type as vector for its arguments
bool cmp( hits_object* p1, hits_object* p2 )
{
return p1->position < p2->position;
}
You can add a Lambda instead of a function to std::sort.
struct test
{
int x;
};
std::vector<test> tests;
std::sort(tests.begin(), tests.end(),
[](const test* a, const test* b)
{
return a->x < b->x;
});