Транспониране на матрица в C++

Пиша програма за транспониране на дадена матрица, използвайки разпределена памет. Функцията работи перфектно с квадратна матрица NxN (редове==колове), но се срива с матрица MxN (редове !=колове). Моля помогнете

void transpose(int **matrix, int *row, int *col)
{
    // dynamically allocate an array
    int **result;
    result = new int *[*col]; //creates a new array of pointers to int objects
    // check for error
    if (result == NULL)
    {
        cout << "Error allocating array";
        exit(1);
    }
    for (int count = 0; count < *col; count++)
    {
        *(result + count) = new int[*row];
    }

    // transposing
    for (int i = 0; i<*row; i++)
    {
       for (int j = i+1; j<*col; j++)
       {
        int temp = *(*(matrix + i) + j);
        *(*(matrix + i) + j) = *(*(matrix + j) + i);
        *(*(matrix + j) + i) = temp;
       }
    }

    for (int i = 0; i<*row; i++)
    {
       for (int j = 0; j<*col; j++)
       {
          *(*(result + i) + j) = *(*(matrix + i) + j);
          cout << *(*(result + i) + j) << "\t";
       }
       cout << endl;
    }
}

person Casper    schedule 13.02.2013    source източник
comment
new хвърля изключение при повреда. Използвайте new(nothrow), ако искате да върне null при повреда (въпреки че е необичайно да искате това).   -  person Peter Wood    schedule 13.02.2013


Отговори (2)


Линиите:

for (int i = 0; i<*row; i++)
{
   for (int j = i+1; j<*col; j++)
   {
    int temp = *(*(matrix + i) + j);
    *(*(matrix + i) + j) = *(*(matrix + j) + i);
    *(*(matrix + j) + i) = temp;
   }
}

са проблемът. Проблемът е, че матрицата се индексира от i, след това j, а не от j, след това i, както правите във втория и третия ред в цикъла while. Представете си, че матрицата е матрица 2x3, след което се опитвате да изпълните matrix[2][3] = matrix[3][2], но matrix[3][2] не съществува.

Най-добре е просто да инициализирате резултата директно в този цикъл:

for (int i = 0; i<*row; i++)
   for (int j = 0; j<*col; j++)
     result[j][i] = matrix[i][j];

След това можете да изведете, както е показано по-долу, или да изтриете матрицата и да присвоите отново матрицата, за да бъде резултат, както желаете. Цялата ми функция за транспониране се превърна в следния код (ред и колона не трябва да са указатели към int, преминавайки по стойност, е съвсем добре. Също така достъпът до матрици трябва да използва индекси на масив, тъй като е по-хубав стил):

void transpose(int **matrix, int row, int col)
{
  // dynamically allocate an array
  int **result;
  result = new int *[col]; //creates a new array of pointers to int objects
  for (int i = 0; i < col; i++)
    result[i] = new int[row];

  // transposing
  for (int i = 0; i<row; i++)
   for (int j = 0; j<col; j++)
     result[j][i] = matrix[i][j];

  //output resulting matrix
  for (int i = 0; i<col; i++) {
   for (int j = 0; j<row; j++)
    cout << result[i][j] << "\t";
   cout << endl;
  }
}
person pippin1289    schedule 13.02.2013
comment
беше от изходните ви отчети? защото трябва да се уверите, че тези цикли индексират резултата по подобен начин, вместо *ред по *кол те трябва да са *кол по *ред - person pippin1289; 13.02.2013
comment
Добавих цялата си функция за транспониране, която тествах - person pippin1289; 13.02.2013
comment
@pippin1289 не забравяйте да изтриете разпределената памет - person borisbn; 13.02.2013
comment
о, това е друг въпрос, щях да попитам дали трябва да освободя този резултатен масив, в моя учебник, когато използват функция за връщане и използват разпределен масив от памет, не виждам да извикват delete[] array, но това, което мисля, е всеки път използваме ключова дума new за разпределен масив от памет, трябва да го освободим, нали? - person Casper; 13.02.2013
comment
Разбира се, ще трябва да го изтриете. Не бях сигурен какво всъщност искаш от резултата. Можете да го върнете, ако искате да го използвате, или можете да изтриете матрицата и да присвоите отново резултата. И в двата случая, когато изтривате матрица, трябва да преминете през първия масив, извиквайки delete[] за всеки подмасив, след което да завършите с delete[] на цялата матрица. - person pippin1289; 13.02.2013
comment
за горната функция резултатът е просто да задържа масива и след това да го отпечата точно както правят кодовете, имам и други функции, които връщат указатели и в тези функции също създадох нов масив с динамична памет. Така че трябва ли да ги освобождавам и в двата вида функции void и **int? - person Casper; 14.02.2013
comment
Ако връщате указател, не можете да освободите паметта във функцията или ще върнете памет за боклук или нула. Ако не върнете никаква стойност, определено трябва да освободите паметта във функцията. Просто внимавайте с функциите, връщащи новоразпределената памет, за да сте сигурни, че ще ги изтриете, когато вече не се използват. - person pippin1289; 14.02.2013

Опитвате се да транспонирате матрицата "на място":

((матрица + i) + j) = ((матрица + j) + i);

не трябва да правиш това Ако броят на колоните е по-голям от броя на редовете, разпределени за matrix, ще четете и пишете в неразпределената памет.

IMHO, би било по-добре да съхранявате цялата матрица в непрекъсната памет. Не на различни парчета. По този начин кодът ще изглежда така:

void transpose( int *matrix, int row, int col )
{
    for ( int i = 0; i < row; i++ )
    {
       for ( int j = i + 1; j < col; j++ )
       {
           int temp = matrix[ i * col + j ];
           matrix[ i * col + j ] = matrix[ j * col + i ];
           matrix[ j * col + i ] = temp;
       }
    }
}

Единственият минус на това разпределение е, че не можете да адресирате елемент като matrix[ i ][ j ], а само matrix[ i + col + j ]. Плюсовете са: 1) лесна за разпределяне/освобождаване на памет (само matrix = new int[ col * row ] и delete [] matrix) 2) малко по-бърз достъп до елементи (заради непрекъснатото им местоположение)

В крайна сметка мисля, че това би било най-добрият начин да погледнете std::vector. Ако искате, мога да ви покажа как ще изглежда функцията ви с вектор

person borisbn    schedule 13.02.2013
comment
да, благодаря за вашия принос, казаха ми много пъти, че използването на вектор е много по-добро, но за този проблем се изисква да използвам тази концепция :( - person Casper; 13.02.2013