Sorting static multi dimentional array with custom compare function - c++
There are examples of sorting vectors or dynamically allocated arrays but I couldn't find any help regarding static arrays. Let's say I have an array
int array[10][10];
and a compare function,
bool compare(const int (*a)[10], const int (*b)[10]);
When I call it like this,
std::sort(array, array + 10, compare);
I have compilation errors: error: cannot convert 'int*' to 'const int (*)[10]' in argument passing
I tried many ways, casting array to (void**) in sort function but then I have segmentation fault. My problem is using arrays as function parameters I guess but I couldn't figure out how to use this std::sort. Otherwise, I will have to write my own sort function.
When std::sort is called on a container of elements of type T, the comparison function needs to receive arguments of type T or const T&. In this case, you have a 2-dimensional array, so the type of elements is a 1-dimensional array int[10]. Since 1-dimensional arrays decay to pointers, compare can be:
bool compare(int a[10], int b[10]);
or equivalently:
bool compare(int *a, int *b);
This will fix the error you got, but your code still won't work: std::sort needs the container elements to be assignable (or movable in C++11), but arrays are not assignable.
You can use std::vector<std::vector<int> > instead as people have suggested. Note that your fear of performance problems is misguided: Even if sorting a two-dimensional array was possible, it would involve a lot of copying of one-dimensional arrays which would take a long time. Swapping vectors, on the other hand, is done by simply swapping pointers which is faster. In general, you should not make assumptions about performance if you haven't tested it first.
The comparison function doesn't get an iterator to the element passed but the dereferenced iterator, i.e., the value type. Thus, your comparison function would need to be declared as like the one below:
bool compare(int (&a0)[10], int (&a1)[10]);
You can verify that you can actually call it with array iterators:
compare(*(std::begin(array) + 0), *(std::begin(array) + 1));
However, this won't make it possible to sort you arrays: built-in arrays are not copy-assignable. The easiest way to sort statically sized arrays (where the outer dimension flexible) is to use std::array<T, N>:
std::array<int, 10> array[10];
std::sort(std::begin(array), std::end(array));
I say, if we're gonna use the STL and C++ .. lets write it in a modern style and really use the STL.
My attempt at the problem using modern c++11:
#include <vector>
#include <iostream>
#include <algorithm>
typedef std::vector<int> ArrayInt;
typedef std::vector< std::vector<int> > ArrayData;
bool compare(const ArrayInt& a, const ArrayInt& b) {
std::cout << &(a) << ' ' << &(b) << std::endl;
int sumA = std::accumulate(a.begin(), a.end(), 0);
int sumB = std::accumulate(b.begin(), b.end(), 0);
return sumA < sumB;
}
int main(int argc, char** argv) {
ArrayData array = {
{1,2,4,0,3,7,6,8,3,3},
{13,2,4,0,3,7,6,8,3,3},
{10,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{16,2,4,0,3,7,6,8,3,3},
{1,2,400,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{120,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3},
{1,2,4,0,3,7,6,8,3,3}
};
std::sort(array.begin(), array.end(), compare);
for (auto row : array) {
for (int num : row)
std::cout << num << ' ';
std::cout << std::endl;
}
}
It uses accumulate to sum each sub array, and sorts on the sum .. it's super inefficient because it has to sum the same row multiple times .. but it's just there to show off a custom compare function.
As an exercise, I wrote this version that uses async to distribute the summing part over any available cores to do the summing, before the sort. I'm sorry it's getting a bit off topic. I hope it's still useful to some people:
#include <vector>
#include <iostream>
#include <algorithm>
#include <future>
typedef std::vector<int> IntRow;
typedef std::pair<int, IntRow> DataRow;
typedef std::vector<DataRow> DataTable;
int main(int argc, char** argv) {
// Holds the sum of each row, plus the data itself
DataTable array = {
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {13,2,4,0,3,7,6,8,3,3}},
{0, {10,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {16,2,4,0,3,7,6,8,3,3}},
{0, {1,2,400,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {120,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}},
{0, {1,2,4,0,3,7,6,8,3,3}}
};
// Make use of multiple cores if it's efficient enough
// get the sum of each data row
std::vector<std::future<int>> sums(array.size());
auto next = sums.begin();
for (auto& row : array)
*next++ = std::async([](const IntRow& row) { return std::accumulate(row.begin(), row.end(), 0); }, row.second);
// Get the results
auto nextRow = array.begin();
for (auto& sum: sums)
(*nextRow++).first = sum.get();
// Sort it
std::sort(array.begin(), array.end(),
[](const DataRow& a, const DataRow& b) { return a.first < b.first; });
// Print it
for (auto row : array) {
for (int num : row.second)
std::cout << num << ' ';
std::cout << std::endl;
}
}
It needs to be compiled with pthread library or similar:
g++ -O6 sort.cpp --std=c++11 -g -lpthread
Related
How to retrieve an array from a pointer in c++
I'm having problems with a program that only accepts arrays. I'm having plenty of pointers to different arrays, but using *p seems to only give me the first element of the array. I want to return all the elements of the array. I know the length of the array, if that helps. #include <typeinfo> #include <iostream> int i[10]; int* k=i; cout<<typeid(i).name()<<'\n'; cout<<typeid(*k).name()<<'\n'; results in 'int [10]' and 'int' respectively. I want some way of returning k as 'int [10]'.
Your k is a pointer to int. It points to the first element of the array. If you want a pointer to the whole array then you need to declare it as such. #include <typeinfo> #include <iostream> int main() { int i[10]; int* k=i; int(*p)[10] = &i; std::cout<<typeid(i).name()<<'\n'; std::cout<<typeid(*k).name()<<'\n'; std::cout<<typeid(*p).name()<<'\n'; } Output: A10_i i A10_i However, as others have said, std::array is much less confusing to work with. It can do (almost) anything a c-array can do without its quirks. Certainly there is a solution to your actual problem that does not require to get the array from a pointer to a single integer.
Example to show you how much more convenient C++ array/vector is then "C" style arrays with pointers : #include <vector> #include <iostream> // with std::vector you can return arrays // without having to think about pointers and/or new // and your called cannot forget to call delete std::vector<int> make_array() { std::vector<int> values{ 1,2,3,4,5,6 }; return values; } // pass by reference if you want to modify values in a function void add_value(std::vector<int>& values, int value) { values.push_back(value); } // pass by const refence if you only need to use the values // and the array content should not be modified. void print(const std::vector<int>& values) { // use range based for loops if you can they will not go out of bounds. for (const int value : values) { std::cout << value << " "; } } int main() { auto values = make_array(); add_value(values, 1); print(values); std::cout << "\n"; std::cout << values.size(); // and a vector keeps track of its own size. return 0; }
C++ | Comparing two arrays using std::mismatch (or another STL alternative)
I am faced with the task of comparing two c++ arrays that are of int data type. I specifically cannot use any loops (for, while) of my own, and was encouraged to use an STL function. I found std::mismatch() and it seems to be what I want, but I am unable to get it working with a basic array. Here is my code: #include <iostream> // cout #include <algorithm> // std::mismatch #include <utility> // pair int main() { int a[10] = {1,3,5,7,9,11,13,15,17,19}; int b[10] = {2,4,6,8,10,12,14,16,18,20}; std::pair<int, int> result = std::mismatch(a, a + 9, b); std::cout<<result.first<<" "<<result.second<<std::endl; return 0; } I am getting the following error: error: conversion from 'std::pair' to non-scalar type 'std::pair' requested I am pretty new to C++, so I don't really know what this means.
std::mismatch returns a pair of iterators to the container, not a pair of ints. In this case, since you have an array, the iterator type is int*. The simple solution is to deduce the type when calling it instead: auto result = std::mismatch(a, a + 9, b); From c++17, you can name the individual elements of the pair as well: auto [i, j] = std::mismatch(a, a + 9, b);
std::mismatch() returns a std::pair of iterators. In your example, you are using iterators of type int* (an int[] array decays to an int* pointer to its 1st element). So you need to change your result variable from pair<int, int> to pair<int*, int*>. And then you need to dereference those iterators when printing their values to cout, eg: #include <iostream> // cout #include <algorithm> // std::mismatch #include <utility> // pair int main() { int a[10] = {1,3,5,7,9,11,13,15,17,19}; int b[10] = {2,4,6,8,10,12,14,16,18,20}; int *a_end = a + 10; std::pair<int*, int*> result = std::mismatch(a, a_end, b); if (result.first != a_end) std::cout << *(result.first) << " " << *(result.second) << std::endl; else std::cout << "no mismatch found" << std::endl; return 0; }
Is it possible to pass an array into a function as a parameter without creating a variable for that array?
So I made a function that takes arrays as parameters and I've tried calling the function by passing arrays that have not been defined as variables into said function (like {0,0,0,0}). However, I am given an error which says "too many initializer values." Say we have a function defined as: int func(int values[]) { int average = 0; for(int x = 0; x < values.size(); x++) { average += values[x]; } return average / values.size(); } And we want to call it without defining an array to pass in like this: func({1,6,7,2}); Is there any way to do something like this or would I have to define an array and pass it into the function that way?
You cannot do that using built-in arrays. The fact that Arrays are neither Assignable nor Copy-able. Also They are not classes so they don't have member functions like size() or they take Initializer-list. You can achieve that through using std::array if the size is constant or using std::vector if the size if dynamic. #include <array> int func(const std::array<int, 5>& values) { int average = 0; for (size_t x{}, sz{ values.size() }; x != sz ; ++x) average += values[x]; return average / values.size(); } int main() { auto ret{ func({ 1, 6, 7, 2 }) }; std::cout << ret << std::endl; } Also don't mix Unsigned with Signed in calculations like in your loop: for(int x = 0; x < values.size(); x++) // x is int while values.size() is unsigned int. int func(const std::array<int, 5>& values): pass by reference to avoid the copy especially if the size is big. Also pass by const as long as the function doesn't intend to change the parameter also another benefit of using const reference is you can pass literals instead of an object. N.B: I recommend to also to use range-based for because it is really relevant in your example as long as you want to iterate over all the elements and not intending to insert nor to delete elements: int average = 0; for (const auto& e : values) average += e; Another version of func as #M.M pointed out is to use std::accumalate to do the job for you: int func(const std::array<int, 5>& values) { return std::accumulate(values.begin(), values.end(), 0) / values.size(); }
Using a vector, yes: #include <vector> using namespace std; void f( const vector <int> & v ) { } int main() { f( {1,2,3,4} ); }
Arrays don't work like that. When you pass an array to a function, the address of the first element gets passed like a pointer, and inside the function there is no more information about the size of the array. (Before the compiler itself could infer the size because the array was declared in the scope, but a function can be called from any number of places) If you want to do something like that you would either have to use a container class, such as a vector, or you could pass a second argument into the function stating the size of the array. Another way is to have some sort of end point in your array, such as is the case with c-strings, for example a null value.
Modifying elements of an array through a function
I'm learning about pointers and I can't get this code to work. Here's what I have so far: void incrementArray(int* a[]) { for(auto& x : a) { ++x; } } int _tmain(int argc, _TCHAR* argv[]) { int array[] = {0,1,2,3,4,5,6,7,8,9}; for(auto x : array) { cout << x << '\n'; } incrementArray(&array); for(auto x : array) { cout << x << '\n'; } } I'm getting the following error: 'incrementArray' : cannot convert parameter 1 from 'int (*)[10]' to 'int *[]' What can I do to fix my code?
C-style arrays have funny syntax. To pass the array to a function, use int a[] This does not copy the array and changes to the array inside the function will modify the external array. You only need to call incrementArray(array); no & needed You could try using std::array class which follows more normal syntax.
you have a pointer as a parameter (a reference to an array), but you wish to modify the actual thing it's pointing to, so you gotta change *a, not a. You could use an array, vector, list, etc object that would have methods already associated to them that do most of the manipulation you could want
What you are trying to do will not work since the signature of a function taking int a[] as an argument does not contain the necessary length information needed to write a for-each loop (i.e. to instantiate the begin() and end() templates needed to use the for-each syntax). GCC's warning says this fairly clearly: Error:(14, 19) cannot build range expression with array function parameter 'a' since parameter with array type 'int *[]' is treated as pointer type 'int **' I thought this might be do-able with a template, but . . . EDIT: It can be done with templates, just took me a moment to wrap my head around the syntax. Here is your example in working condition: template <size_t N> void incArray(int (&a)[N]) { for(auto& x : a) { ++x; } } int main(int argc, const char * argv[]) { int array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; for (auto x : array) { cout << x << " "; } cout << endl; incArray(array); for (auto x : array) { cout << x << " "; } cout << endl; return 0; }
There are a couple approaches you could take to increment the elements of an array, all of which require knowing where to start and where to end. The simple way of doing what you want is to just pass the start and end address pointers, but you could also pass a start address with some offset. Since you are using a C-Style array, your int element has and address int*, so your std::begin(array) is an int* to the first element while std::end(array) points to the address of the location after your last allocated element. In your program, the std::end() address points to the memory location after your 10th allocated element. If you had an array with a size allocation (int other_arr[40]), std::end() will point to the first address after the allocation (std::end(other_arr) would be std::begin(other_arr)+41). C++ has recently introduced non-member std::begin() and std::end() in the <iterator> library, which returns a pointer to the respective element locations in your C-Array. #include <algorithm> // std::copy #include <iostream> // std::cout #include <iterator> // std::begin void increment_elements(int* begin, const int* end) { while (begin != end) { ++(*begin); ++begin; } } // An increment functor for std::transform int increase_element(int i) { return ++i; } int main() { int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; for (const int x : array) { std::cout << x << ' '; } std::cout << '\n'; increment_elements(std::begin(array), std::end(array)); // Another way to write your print statement above std::copy(std::begin(array), std::end(array), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; // Transform array elements through increase_element() // and print result to cout. std::transform(std::begin(array), std::end(array), std::ostream_iterator<int>(std::cout, " "), increase_element); std::cout << '\n'; } The generalized version of the increment_elements() function can be found in the <algorithm> library as the function std::transform() documented here. Since you are learning now, here are some habits that you can start to utilize: Do not use using namespace std; at the global level. By pulling everything in the standard library into the global namespace, you "pollute" it with functionality that can be called if a function call for it exists, since it doesn't require a std:: prefix qualification. Say you were to write a function that calculated the euclidean distance between two (x,y) points, double distance(Point* p1, Point* p2). You decide to use any of the STL containers, such as <vector>. The containers utilize the <iterator> library, which has its own std::distance(T*, T*) function to calculate the distance between two addresses in memory. By bringing std into the global namespace by using namespace std;, you now have 2 functions with the same signature in the same namespace, that do 2 completely different things. This is very bad yet easily avoidable. This general guideline is probably unnecessary for small projects, but I still recommend you just don't ever do it for any project. Ever. const or const T& your read only operations. When doing operations where you are pulling data for reading and you don't want to modify the data, initialize using const or const T&. const by itself is sufficient for primitive datatypes (int, float, double, char), but non-primitives will require const T& (T is the type). Values that are of type const T& (called const referenced) are read-only (const) and directly accessed (&).
C++ multi dimensional array function parameter
How can I pass a two or multi dimensional array as a parameter of a function without defining its size?? Here is my example code: void test(int *a) { a[0][0] = 100; } int main() { int a[2][2]; test(a); cout<<a[0][0]; }
You can use a template for static sizes template<int first, int second> void func(int(&array)[first][second]) { } Or a vector of vector for dynamic sizes void func(std::vector<std::vector<int>> array) { } However, what you most definitely cannot do is use an int**. An int[] will decay to an int* but an int[][] will decay to an int*[]. Think about it- else, how would the language differentiate between an array of pointers, and a multi-dimensional array of values? You really should never use primitive arrays anyway, they're begging for trouble with no safety and implicit conversions up the wazoo. Grab a nice, safe std::array (or boost::array if you're in C++03) for static arrays, or std::vector for dynamic arrays.
If you're working exclusively with statically-sized, stack-allocated arrays, then a function template will do exactly what you're asking for: #include <cstddef> #include <ostream> #include <iostream> template<std::size_t N, std::size_t M> void func(int (&arr)[N][M]) { std::cout << "int[" << N << "][" << M << "]\n"; for (std::size_t n = 0; n != N; ++n) for (std::size_t m = 0; m != M; ++m) std::cout << arr[n][m] << ' '; std::cout << '\n' << std::endl; } int main() { int i1[2][3] = { { 4, 5, 6 }, { 7, 8, 9 } }; int i2[4][2] = { { 1, 3 }, { 5, 7 }, { 9, 11 }, { 13, 15 } }; func(i1); func(i2); }
Passing the pointer to the array. For example, if you have a bidimensional int array you'll need to pass int** p, along with the dimensions of the array.
For built-in arrays, you have to specify the size of all dimensions but the last dimension or indexing won't work. If your goal is just to have a function that takes multi-dimensional arrays of any size, I'd consider boost::multi_array_ref (or boost::const_multi_array_ref) Update: Since passing by pointer appears to be the answer that's getting the most attention (although I think the multi_array_ref is good... unless boost isn't available or something) then here's an example that flattens the array and doesn't limit you by array dimensions (although you still need size information to make it useful) void f(int* array /* should probably pass in the size here - in this case, 4 */) { array[3] = 9; } int main() { int array[2][2] = { {1,2}, {3,4} }; // Note: The array is flattened here. If you truly need to remember the multi-dimensional nature, you need to pass in enough information to specify all the dimensions... maybe something like a vector<size_t> (that's what the multi_array_ref uses). I guess if you have a limited number of dimensions then a couple size_t will work for you test(&array[0][0]); std::cout << array[1][1] << std::endl; return 0; }
int a[][] Can be passed as: function name(int **arr) { //your code, you can then access it just like you would have accesses your array: arr[3][2] }