Опубликован: 05.01.2015 | Доступ: свободный | Студентов: 2178 / 0 | Длительность: 63:16:00
Лекция 3:

Элементарные структуры данных

Составные структуры данных

Массивы, связные списки и строки обеспечивают простые методы последовательной организации данных. Они создают первый уровень абстракции, который можно использовать для группировки объектов методами, пригодными для их эффективной обработки. Иерархию подобных абстракций можно использовать для построения более сложных структур. Возможны массивы массивов, массивы списков, массивы строк и т.д. В этом разделе мы рассмотрим примеры подобных структур.

Подобно тому, как одномерные массивы соответствуют векторам, двумерные массивы, с двумя индексами, соответствуют матрицам и широко используются в математических расчетах. Например, следующий код можно применить для перемножения матриц a и b с помещением результата в третью матрицу c.

for (i = 0; i < N; i++)
  for (j = 0; j < N; j++)
    c[i][j] = 0.0;
for (i = 0; i < N; i++)
  for (j = 0; j < N; j++)
    for (k = 0; k < N; k++)
      c[i][j] += a[i][k]*b[k][j];
        

Математические расчеты часто естественно выражаются с помощью многомерных массивов.

Помимо математических применений привычный способ структурирования информации состоит в использовании таблиц чисел, организованных в виде строк и столбцов. В таблице оценок можно выделить по одной строке для каждого студента и по одному столбцу для каждого предмета. Такую таблицу можно представить в виде двумерного массива с одним индексом для строк и еще одним - для столбцов. Если студентов 100, а предметов 10, можно объявить массив как grades[100][10], а затем ссылаться на оценку ;-го студента по j-му предмету следующим образом: grade[i][j]. Для вычисления средней оценки по предмету необходимо сложить элементы соответствующего столбца и разделить сумму на количество строк. Чтобы вычислить среднюю оценку определенного студента, нужно сложить элементы строки и разделить сумму на количество столбцов и т.п. Двумерные массивы широко используются в подобных приложениях. В программе часто удобно использовать и более двух измерений. Например, в таблице оценок можно использовать третий индекс для хранения всех оценок за все годы.

Двумерные массивы - это просто удобная запись, поскольку числа хранятся в памяти компьютера, которая, по сути, является одномерным массивом. Во многих языках программирования двумерные массивы хранятся построчно в одномерных массивах. Так, в массиве a[M][N] первые N позиций будут заняты первой строкой (элементы от a[0][0] до a[0][N-1]), следующие N позиций - второй строкой (элементы от a[1][0] до a[1][N-1]) и т.д. При организации хранения в порядке старшинства строк последняя строка кода перемножения матриц из предыдущего абзаца в точности эквивалентна выражению:

c[N*i+j] = a[N*i+k]*b[N*k+j]
        

Эту схему можно обобщить для массивов с большим количеством измерений. В языке C++ многомерные массивы можно реализовать более общим методом - в виде составных структур данных (массивов массивов). Это обеспечивает гибкость, например, возможны массивы массивов, которые имеют различный размер.

В программе 3.6 был представлен метод динамического выделения памяти массивам, который позволяет использовать программы для задач различных размеров без повторной компиляции. Хотелось бы иметь подобный метод и для многомерных массивов. Как выделять память многомерным массивам, размер которых неизвестен на этапе компиляции? Другими словами, необходима возможность обращаться в программе к элементу массива наподобие a[i][j], но не объявлять массив в виде (например) int a[M][N], поскольку значения Mи N неизвестны. При организации хранения элементов по строкам оператор вида

int* a = malloc(M*N*sizeof(int));
        

выделяет память под массив целых чисел размером MxN, но это решение приемлемо не для всех случаев. Например, если массив передается в функцию, во время компиляции можно не задавать только его первое измерение. В программе 3.16 приведено более эффективное решение для двумерных массивов, основанное на определении "массивов массивов".

Программа 3.17 демонстрирует использование аналогичной составной структуры - массива строк. Поскольку наше абстрактное понятие строки относится к массиву символов, то, на первый взгляд, массивы строк следовало бы представлять в виде массивов массивов. Однако конкретным представлением строки служит указатель на начало массива символов, поэтому массив строк может быть и массивом указателей. Как показано на рис. 3.12, перемещение строк можно выполнить простым перемещением указателей массива. В программе 3.17 используется библиотечная функция qsort. Реализация подобных функций рассматривается в главах 6-9 вообще и в "Быстрая сортировка" в частности. Этот пример содержит типичную ситуацию обработки строк: сначала символы считываются в большой одномерный массив, потом расставляются указатели на отдельные строки (ограниченные символами завершения), а затем осуществляется работа с указателями.

Программа 3.16. Выделение памяти под двумерный массив

Эта функция динамически выделяет память двумерному массиву как массиву массивов. Память сначала выделяется для массива указателей, а затем для каждой строки. С помощью этой функции оператор

int **a = malloc2d(M, N);
выделяет память массиву целых чисел размером M х N.
int **malloc2d(int r, int c)
  { int **t = new int*[r];
    for (int i = 0; i < r; i++)
      t[i] = new int[c]; return t;
  }
        

Программа 3.17. Сортировка массива строк

Эта программа иллюстрирует важную функцию обработки строк: расположение набора строк в упорядоченном виде. Строки считываются в буфер достаточного размера, чтобы вместить их все. Для каждой строки в массиве хранится указатель. Затем указатели переупорядочиваются так, чтобы указатель на самую младшую строку находился в первой позиции массива, указатель на следующую в алфавитном порядке строку - во второй позиции и т.д.

Библиотечная функция qsort, которая в действительности выполняет сортировку, принимает четыре аргумента: указатель на начало массива, количество объектов, размер каждого объекта и функцию сравнения. Независимость от типа сортируемых объектов достигается за счет слепого переупорядочения блоков данных, которые представляют объекты (в данном случае, указатели на строки), а также за счет использования функции сравнения, принимающей в качестве аргумента указатели на void. Программа выполняет обратное приведение этих блоков к типу указателей на указатели на символы для функции strcmp. Чтобы обратиться к первому символу строки для операции сравнения, выполняется разыменование трех указателей: один для получения индекса (который является указателем) элемента массива, один для получения указателя строки (с помощью индекса) и еще один для получения символа (с помощью указателя).

Мы будем использовать другой метод достижения независимости от типа для функций сортировки и поиска (см. "Абстрактные типы данных" и "Элементарные методы сортировки" ).

#include <iostream.h>
#include <stdlib.h>
#include <string.h>
int compare(const void *i, const void *j)
  { return strcmp(*(char **)i, *(char **)j); }
int main()
  { const int Nmax = 1000;
    const int Mmax = 10000;
    char* a[Nmax]; int N;
    char buf[Mmax]; int M = 0;
    for (N = 0; N < Nmax; N++)
      {
        a[N] = &buf[M];
        if (!(cin >> a[N])) break;
        M += strlen(a[N])+1;
      }
    qsort(a, N, sizeof(char*), compare);
    for (int i = 0; i < N; i++)
      cout << a[i] << endl;
  }
        

Нам уже встречалось другое применение массивов строк: массив argv, используемый для передачи строковых аргументов функции main в программах на C++. Система сохраняет в строковом буфере символы командной строки, введенные пользователем, и передает в процедуру main указатель на массив указателей строк в этом буфере. Для определения чисел, соответствующих некоторым аргументам, используются функции преобразования. Остальные аргументы используются непосредственно как строки.

Создавать составные структуры данных можно также исключительно с помощью ссылок. На рис. 3.13 показан пример мультисписка, узлы которого имеют несколько полей ссылок и принадлежат независимым связным спискам.

 Сортировка строк

Рис. 3.12. Сортировка строк

При обработке строк обычно используются указатели на буфер, содержащий строки (верхняя диаграмма), поскольку легче управлять указателями, чем строками, имеющими различную длину. Например, в результате сортировки указатели переупорядочиваются таким образом, что при последовательном обращении к ним возвращаются строки в алфавитном (лексикографическом) порядке.

 Мультисписок

Рис. 3.13. Мультисписок
Бактыгуль Асаинова
Бактыгуль Асаинова

Здравствуйте прошла курсы на тему Алгоритмы С++. Но не пришел сертификат и не доступен.Где и как можно его скаачат?

Александра Боброва
Александра Боброва

Я прошла все лекции на 100%.

Но в https://www.intuit.ru/intuituser/study/diplomas ничего нет.

Что делать? Как получить сертификат?