2

I have a matrix of the form pMat[M][N] (where M and N are variable and, hence, input from the user). I want to sort the elements of the 2-D array using the inbuilt std::sort function.

For example, consider the following array:

5 9 6
8 1 3
7 2 4

It should be output as:

1 2 3
4 5 6
7 8 9

The following is the code that I wrote for the purpose:

#include <iostream>
#include <algorithm>

int main() {
    int M, N, **pMat;
    std::cin >> M >> N;
    pMat = new int* [M];
    for (int i=0; i<M; i++){
        pMat[i] = new int[N];
    }
    for (int i=0; i<M; i++){
        for (int j=0; j<N; j++){
            std::cin >> pMat[i][j];
        }
    }
    std::sort(&pMat[0][0], (&pMat[0][0])+(M*N));
    for (int i=0; i<M; i++){
        for (int j=0; j<N; j++){
            std::cout << pMat[i][j] << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}

I wrote the above code based on the following understanding:

  1. Arrays in C++ are stored in contiguous memory locations
  2. std::sort(add1,add2) sorts all the elements present in the memory locations [add1,add2)

The above code doesn't give the correct output. For example, when provided with the following input:

4 3
13 2 1
4 5 6
7 8 9
10 11 12

it displays the following output:

0 0 0 
5 6 13 
7 8 9 
10 11 12

How can I go about writing the code? And where is my understanding wrong?

(For information, I looked at the following answer: Sort a 2D array in C++ using built in functions(or any other method)? , but it does not answer my query)

Python_user
  • 1,378
  • 3
  • 12
  • 25
  • 4
    This won't work since you're working with an array of _pointers_, not a contiguous array. Since the array is always rectangular, you could instead use a contiguous array: `int* pMat = new int[N*M]` and `std::sort(pMat, pMat + N*M)`. Note that that changes accessing from `pMat[x][y]` to `pMat[(x*M)+y]` (or similar) – alter_igel Sep 21 '18 at 04:46
  • 3
    Your array isn't really 2D. You want to load it in a single vector and sort it like that (then print N elements per line or whatever, it doesn't matter) – n.caillou Sep 21 '18 at 04:46
  • @alterigel Ah yes! Thanks a lot! Sorry for the trouble! – Python_user Sep 21 '18 at 04:48
  • @n.caillou Yes! It escaped my mind... Thanks! – Python_user Sep 21 '18 at 04:49
  • 3
    If you created your 2d array [like this](https://stackoverflow.com/questions/21943621/how-to-create-a-contiguous-2d-array-in-c/21944048#21944048), your sort function would have worked with a couple of tweaks. The trick is that the data is contiguous. Wait -- it probably would have worked correctly now that I look closely at how you called `sort`. – PaulMcKenzie Sep 21 '18 at 04:51
  • 1
    This is probably the worst possible method of representing a 2D array in c++, and the worst possible data structure to store data that needs to be sorted in any language. Is this some kind of assignment? – n. m. could be an AI Sep 21 '18 at 05:15
  • @Python_user -- [Here is a working example](https://www.ideone.com/u1klwR). – PaulMcKenzie Sep 21 '18 at 05:20
  • @Python_user I agree with n.m. If you know your 2d array needs to be allocated in this fashion, and the number of rows and columns are fixed (not a ragged array), then at the very least, go for contiguous creation. I know that a lot of textbooks and sites shows creating a 2d array dynamically like how you attempted, but this technique not only fragments the memory (you are calling the allocate / deallocate functions `M + 1` times instead of just twice), it is definitely slower in terms of execution. Imagine if `M` were 10,000 instead of 4. – PaulMcKenzie Sep 21 '18 at 05:29

2 Answers2

5

One way to do this is to create one big array of contiguous (sortable) memory and then access that array as an array of sub-arrays through a second array of pointers.

The second array simply contains a list of pointers, each one pointing to a different sub-array within the larger one.

Something like this:

int M, N;

std::cin >> M >> N;

// one big array of actual data
// (an array of contiguous sub-arrays)
std::vector<int> v(M * N);

// array of pointers to sub-arrays within the actual data
std::vector<int*> pMat;

// point the pointers at the actual data
// each pointer pointing to the relevant sub-array
for(int i = 0; i < M; i++)
    pMat.push_back(v.data() + (i * N));

// get the input, changing the actual data
// through the pointers
for(int i = 0; i < M; i++)
    for(int j = 0; j < N; j++)
        std::cin >> pMat[i][j];

// sort the actual data
std::sort(std::begin(v), std::end(v));

// look at the data through the sub-array pointers
for(int i = 0; i < M; i++)
{
    for(int j = 0; j < N; j++)
        std::cout << pMat[i][j] << " ";
    std::cout << '\n';
}

return 0;

Note: I used std::vector to manage my arrays but it will also work with built in arrays created with new[] and delete[] (not recommended).

EDIT: To add.

Alternatively (much better) you can create a class that will store the data in a big contiguous block and access the different sub-arrays using mathematical offsets like this:

template<typename T>
class two_dee_array
{
public:
    two_dee_array(std::size_t M, std::size_t N): v(M * N), stride(N) {}

    T& operator()(std::size_t M, std::size_t N)
        { return v[(M * stride) + N]; }

    T const& operator()(std::size_t M, std::size_t N) const
        { return v[(M * stride) + N]; }

    std::size_t col_size() const { return stride; }
    std::size_t row_size() const { return v.size() / stride; }

    auto begin() { return std::begin(v); }
    auto end() { return std::end(v); }

    auto begin() const { return std::begin(v); }
    auto end() const { return std::end(v); }

    auto cbegin() const { return std::cbegin(v); }
    auto cend() const { return std::cend(v); }

private:
    std::vector<int> v;
    std::size_t stride;
};

int main()
{
    int M, N;

    std::cin >> M >> N;

    two_dee_array<int> v(M, N);

    for(int i = 0; i < M; i++)
        for(int j = 0; j < N; j++)
            std::cin >> v(i, j);

    std::sort(std::begin(v), std::end(v));

    for(int i = 0; i < M; i++)
    {
        for(int j = 0; j < N; j++)
            std::cout << v(i, j) << " ";
        std::cout << '\n';
    }
}
Galik
  • 47,303
  • 4
  • 80
  • 117
  • And it's fairly simple to provide `row_range row(std::size_t); col_range col(std::size_t)` with [boost's stride and slice adaptors](https://www.boost.org/doc/libs/1_67_0/libs/range/doc/html/range/reference/adaptors/reference.html) – Caleth Sep 21 '18 at 08:32
0

As @alter_igel said. You problem is having non continuous array. Here corrected.

#include <iostream>
#include <algorithm>

int main() {

    int M, N, *pMat;
    std::cin >> M >> N;

    pMat = new int[M*N];

    for (int i=0; i<M; i++){
        for (int j=0; j<N; j++){
            std::cin >> pMat[i*N+j];
        }
    }
        std::cout << std::endl;

    std::sort(pMat, pMat+(M*N));

    for (int i=0; i<M; i++){
        for (int j=0; j<N; j++){
            std::cout << pMat[i*N+j] << " ";
        }
        std::cout << std::endl;
    }
    return 0;
}
kelalaka
  • 5,064
  • 5
  • 27
  • 44