Лабораторная работа №2. Двумерные динамические массивы

Работа с двумерными динамическими массивами, различные способы обхода элементов.

Требования к заданию
ЛК1 Указатели

Теоретическая основа

Двумерные динамические массивы

Двумерный динамический массив — это массив указателей, каждый из которых указывает на одномерный массив. Это позволяет создавать матрицы с переменным количеством элементов в строках.

Основные принципы:

  • Сначала выделяется память для массива указателей (строки)
  • Затем для каждой строки выделяется память под элементы
  • Освобождение памяти происходит в обратном порядке

Задание 1. Квадратная матрица NxN с различными обходами

Требования

Создать динамический двумерный массив размером NxN элементов. Заполнить случайными целыми числами. Создать одномерный массив D. Переписать элементы матрицы в массив D в следующем порядке:

a) по правым диагоналям, начиная с правого верхнего элемента b) по левым диагоналям, начиная с левого верхнего элемента c) по спирали, начиная с центрального элемента d) по спирали, начиная с левого верхнего элемента

Реализация

#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>

using namespace std;

int **createMatrix(int n) {
  int **matrix = new int *[n];
  for (int i = 0; i < n; i++) {
    matrix[i] = new int[n];
  }
  return matrix;
}

void fillMatrix(int **matrix, int n) {
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      matrix[i][j] = rand() % 99 + 1;
    }
  }
}

void printMatrix(int **matrix, int n) {
  cout << "Матрица " << n << "x" << n << ":" << endl;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      cout << setw(3) << matrix[i][j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

void printArray(int *arr, int size, const string &title) {
  cout << title << ": ";
  for (int i = 0; i < size; i++) {
    cout << arr[i];
    if (i < size - 1)
      cout << " ";
  }
  cout << endl << endl;
}

// a) Правые диагонали (начиная с правого верхнего угла)
void rightDiagonals(int **matrix, int n, int *result) {
  int index = 0;

  for (int count = 0; count < n; count++) {
    for (int i = count; i < n; i++) {
      int j = n - 1 - i + count;
      result[index++] = matrix[i][j];
    }
  }
}

// b) Левые диагонали
void leftDiagonals(int **matrix, int n, int *result) {
  int index = 0;

  for (int count = 0; count < n; count++) {
    for (int i = 0; i < n - count; i++) {
      int j = count + i;
      result[index++] = matrix[i][j];
    }
  }
}

// c) Спираль от центра
void spiralFromCenter(int **matrix, int n, int *result) {
  int index = 0;

  int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
  int direction = 0;

  int x = n / 2 - (n + 1) % 2, y = n / 2 - (n + 1) % 2;
  int step = 1;

  while (index < n * n) {
    for (int i = 0; i < 2; i++) {
      for (int j = 0; j < step; j++) {
        if (x >= n || y >= n || x < 0 || y < 0)
          break;

        result[index++] = matrix[x][y];
        x += directions[direction][0];
        y += directions[direction][1];
      }
      direction = (direction + 1) % 4;
    }
    step++;
  }
}

// d) Спираль от левого верхнего угла
void spiralFromTopLeft(int **matrix, int n, int *result) {
  int index = 0;

  int top = 0, bottom = n - 1;
  int left = 0, right = n - 1;

  while (top <= bottom && left <= right) {
    // Move RIGHT along top row
    for (int j = left; j <= right; j++) {
      result[index++] = matrix[top][j];
    }
    top++;

    // Move DOWN along right column
    for (int i = top; i <= bottom; i++) {
      result[index++] = matrix[i][right];
    }
    right--;

    // Move LEFT along bottom row
    if (top <= bottom && left <= right) {
      for (int j = right; j >= left; j--) {
        result[index++] = matrix[bottom][j];
      }
      bottom--;
    }

    // Move UP along left column
    if (left <= right && top <= bottom) {
      for (int i = bottom; i >= top; i--) {
        result[index++] = matrix[i][left];
      }
      left++;
    }
  }
}

void deleteMatrix(int **matrix, int n) {
  for (int i = 0; i < n; i++)
    delete[] matrix[i];
  delete[] matrix;
}

Задание 2. Матрица с произвольным количеством элементов в строках

Требования

Создать двумерный динамический массив с произвольным количеством элементов в каждой строке. Заполнить его и распечатать построчно.

Реализация

// Функция генерации случайного массива
int *genRandArray(int size, int maxValue) {
  int *arr = new int[size + 1];
  if (!arr) {
    cout << "Ошибка выделения памяти" << endl;
    return nullptr;
  }

  arr[0] = size;
  for (int i = 1; i <= size; i++) {
    arr[i] = rand() % maxValue + 1;
  }

  return arr;
}

// Функция генерации двумерного массива
int **genRandMatrix(int size, int maxValue) {
  // Выделяем память для массива указателей
  int **matrix = new int *[size];
  if (!matrix) {
    cout << "Ошибка выделения памяти для матрицы" << endl;
    return nullptr;
  }

  // Создаем каждую строку с произвольным размером
  for (int i = 0; i < size; i++) {
    int rowSize = rand() % 8 + 2; // Размер строки от 2 до 9
    matrix[i] = genRandArray(rowSize, maxValue);

    if (!matrix[i]) {
      // Если не удалось выделить память для строки, очищаем уже выделенные
      for (int j = 0; j < i; j++) {
        delete[] matrix[j];
      }
      delete[] matrix;
      return nullptr;
    }
  }

  return matrix;
}

void printJaggedMatrix(int **matrix, int n) {
  cout << "Матрица:" << endl;
  for (int i = 0; i < n; i++) {
    for (int j = 1; j <= matrix[i][0]; j++) {
      cout << setw(3) << matrix[i][j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

Полный код программы

#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>

using namespace std;

// Функция генерации случайного массива
int *genRandArray(int size, int maxValue) {
  int *arr = new int[size + 1];
  if (!arr) {
    cout << "Ошибка выделения памяти" << endl;
    return nullptr;
  }

  arr[0] = size;
  for (int i = 1; i <= size; i++) {
    arr[i] = rand() % maxValue + 1;
  }

  return arr;
}

// Функция генерации двумерного массива
int **genRandMatrix(int size, int maxValue) {
  // Выделяем память для массива указателей
  int **matrix = new int *[size];
  if (!matrix) {
    cout << "Ошибка выделения памяти для матрицы" << endl;
    return nullptr;
  }

  // Создаем каждую строку с произвольным размером
  for (int i = 0; i < size; i++) {
    int rowSize = rand() % 8 + 2; // Размер строки от 2 до 9
    matrix[i] = genRandArray(rowSize, maxValue);

    if (!matrix[i]) {
      // Если не удалось выделить память для строки, очищаем уже выделенные
      for (int j = 0; j < i; j++) {
        delete[] matrix[j];
      }
      delete[] matrix;
      return nullptr;
    }
  }

  return matrix;
}

int **createMatrix(int n) {
  int **matrix = new int *[n];
  for (int i = 0; i < n; i++) {
    matrix[i] = new int[n];
  }
  return matrix;
}

void fillMatrix(int **matrix, int n) {
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      matrix[i][j] = rand() % 99 + 1;
    }
  }
}

void printMatrix(int **matrix, int n) {
  cout << "Матрица " << n << "x" << n << ":" << endl;
  for (int i = 0; i < n; i++) {
    for (int j = 0; j < n; j++) {
      cout << setw(3) << matrix[i][j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

void printJaggedMatrix(int **matrix, int n) {
  cout << "Матрица:" << endl;
  for (int i = 0; i < n; i++) {
    for (int j = 1; j <= matrix[i][0]; j++) {
      cout << setw(3) << matrix[i][j] << " ";
    }
    cout << endl;
  }
  cout << endl;
}

void printArray(int *arr, int size, const string &title) {
  cout << title << ": ";
  for (int i = 0; i < size; i++) {
    cout << arr[i];
    if (i < size - 1)
      cout << " ";
  }
  cout << endl << endl;
}

// a) Правые диагонали (начиная с правого верхнего угла)
void rightDiagonals(int **matrix, int n, int *result) {
  int index = 0;

  for (int count = 0; count < n; count++) {
    for (int i = count; i < n; i++) {
      int j = n - 1 - i + count;
      result[index++] = matrix[i][j];
    }
  }
}

// b) Левые диагонали
void leftDiagonals(int **matrix, int n, int *result) {
  int index = 0;

  for (int count = 0; count < n; count++) {
    for (int i = 0; i < n - count; i++) {
      int j = count + i;
      result[index++] = matrix[i][j];
    }
  }
}

// c) Спираль от центра
void spiralFromCenter(int **matrix, int n, int *result) {
  int index = 0;

  int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
  int direction = 0;

  int x = n / 2 - (n + 1) % 2, y = n / 2 - (n + 1) % 2;
  int step = 1;

  while (index < n * n) {
    for (int i = 0; i < 2; i++) {
      for (int j = 0; j < step; j++) {
        if (x >= n || y >= n || x < 0 || y < 0)
          break;

        result[index++] = matrix[x][y];
        x += directions[direction][0];
        y += directions[direction][1];
      }
      direction = (direction + 1) % 4;
    }
    step++;
  }
}

// d) Спираль от левого верхнего угла
void spiralFromTopLeft(int **matrix, int n, int *result) {
  int index = 0;

  int top = 0, bottom = n - 1;
  int left = 0, right = n - 1;

  while (top <= bottom && left <= right) {
    // Move RIGHT along top row
    for (int j = left; j <= right; j++) {
      result[index++] = matrix[top][j];
    }
    top++;

    // Move DOWN along right column
    for (int i = top; i <= bottom; i++) {
      result[index++] = matrix[i][right];
    }
    right--;

    // Move LEFT along bottom row
    if (top <= bottom && left <= right) {
      for (int j = right; j >= left; j--) {
        result[index++] = matrix[bottom][j];
      }
      bottom--;
    }

    // Move UP along left column
    if (left <= right && top <= bottom) {
      for (int i = bottom; i >= top; i--) {
        result[index++] = matrix[i][left];
      }
      left++;
    }
  }
}

void deleteMatrix(int **matrix, int n) {
  for (int i = 0; i < n; i++)
    delete[] matrix[i];
  delete[] matrix;
}

int main() {
  srand(time(0));

  cout << "=== Обходы матрицы ===" << endl;

  int n;
  cout << "Введите размер матрицы N: ";
  cin >> n;

  int **matrix = createMatrix(n);
  fillMatrix(matrix, n);
  printMatrix(matrix, n);

  int size = n * n;
  int *result = new int[size];

  rightDiagonals(matrix, n, result);
  printArray(result, size, "a) Правые диагонали");

  leftDiagonals(matrix, n, result);
  printArray(result, size, "b) Левые диагонали");

  spiralFromCenter(matrix, n, result);
  printArray(result, size, "c) Спираль от центра");

  spiralFromTopLeft(matrix, n, result);
  printArray(result, size, "d) Спираль от левого верхнего угла");

  delete[] result;
  deleteMatrix(matrix, n);

  cout << "\n=== Двумерный массив, строки произвольной длины ===" << endl;

  int rows;
  cout << "Введите количество строк: ";
  cin >> rows;

  matrix = genRandMatrix(rows, 100);
  printJaggedMatrix(matrix, rows);
  deleteMatrix(matrix, rows);

  return 0;
}

Объяснение алгоритмов

Правые диагонали

Обход осуществляется по диагоналям, начиная с элементов в правой части матрицы. Алгоритм использует формулу j = n - 1 - i + count для вычисления позиции столбца, обеспечивая движение по диагоналям справа налево и сверху вниз.

Левые диагонали

Обход по диагоналям, начиная с левого верхнего угла. Использует формулу j = count + i для движения по диагоналям слева направо и сверху вниз.

Спираль от центра

Алгоритм начинает с центрального элемента (вычисленного как n/2 - (n+1)%2) и движется по спирали наружу. Использует массив направлений {{0, 1}, {1, 0}, {0, -1}, {-1, 0}} (вправо, вниз, влево, вверх) и увеличивает количество шагов после каждых двух поворотов.

Спираль от левого верхнего угла

Классический алгоритм обхода по спирали с использованием четырех границ (top, bottom, left, right). Движется по периметру: вправо → вниз → влево → вверх, постепенно сужая границы после каждого прохода.