Выбрать главу

Многомерные массивы

Уже объяснялось, что разделение массивов на одномерные и многомерные носит исторический характер. Никакой принципиальной разницы между ними нет. Одномерные массивы — это частный случай многомерных. Можно говорить и по-другому: многомерные массивы являются естественным обобщением одномерных. Одномерные массивы позволяют задавать такие математические структуры как векторы, двумерные — матрицы, трехмерные — кубы данных, массивы большей размерности — многомерные кубы данных. Замечу, что при работе с базами данных многомерные кубы, так называемые кубы OLAP, встречаются сплошь и рядом.

В чем особенность объявления многомерного массива? Как в типе указать размерность массива? Это делается достаточно просто, за счет использования запятых. Вот как выглядит объявление многомерного массива в общем случае:

<тип>[, …,] <объявители>;

Число запятых, увеличенное на единицу, и задает размерность массива. Что касается объявителей, то все, что сказано для одномерных массивов, справедливо и для многомерных. Можно лишь отметить, что хотя явная инициализация с использованием многомерных константных массивов возможна, но применяется редко из-за громоздкости такой структуры. Проще инициализацию реализовать программно, но иногда она все же применяется. Вот пример:

public void TestMultiArr()

{

    int[,]matrix = {{1,2}, {3,4} };

    Arrs.PrintAr2("matrix", matrix);

}//TestMultiArr

Давайте рассмотрим классическую задачу умножения прямоугольных матриц. Нам понадобится три динамических массива для представления матриц и три процедуры, одна из которых будет заполнять исходные матрицы случайными числами, другая — выполнять умножение матриц, третья — печатать сами матрицы. Вот тестовый пример:

public void TestMultiMatr()

{

    int n1, m1, n2, m2,n3, m3;

    Arrs.GetSizes("MatrA",out n1,out m1);

    Arrs.GetSizes("MatrB",out n2,out m2);

    Arrs.GetSizes("MatrC",out n3,out m3);

    int[,]MatrA = new int[n1,m1], MatrB = new int[n2,m2];

    int[,]MatrC = new int[n3,m3];

    Arrs.CreateTwoDimAr(MatrA);Arrs.CreateTwoDimAr(MatrB);

    Arrs.MultMatr(MatrA, MatrB, MatrC);

    Arrs.PrintAr2("MatrA",MatrA); Arrs.PrintAr2("MatrB",MatrB);

    Arrs.PrintAr2("MatrC", MatrC);

}//TestMultiMatr

Три матрицы — MatrA, MatrB и MatrC — имеют произвольные размеры, выясняемые в диалоге с пользователем, и использование для их описания динамических массивов представляется совершенно естественным. Метод CreateTwoDimAr заполняет случайными числами элементы матрицы, переданной ему в качестве аргумента, метод PrintAr2 выводит матрицу на печать. Я не буду приводить их код, похожий на код их одномерных аналогов.

Метод MuitMatr выполняет умножение прямоугольных матриц. Это классическая задача из набора задач, решаемых на первом курсе. Вот текст этого метода:

public void MuitMatr(int[,]A, int[,]B, int[,]C)

{

    if (A.GetLength(1)!= В.GetLength(0))

       Console.WriteLine("MuitMatr: ошибка размерности!");

    else

       for (int i = 0; i < A.GetLength(0); i + +)

            for (int j = 0; j < В.GetLength(1); j++)

           {

               int s=0;

               for (int k = 0; k < A.GetLength(1); k++)

                   s+= A[i,k]*B[k,j];

               С [i, j] = s;

           }

}//MuitMatr

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

Обратите внимание, как выглядят результаты консольного вывода на данном этапе работы (рис. 11.2).

Рис. 11.2. Умножение матриц

Массивы массивов

Еще одним видом массивов C# являются массивы массивов, называемые также изрезанными массивами (jagged arrays). Такой массив массивов можно рассматривать как одномерный массив, элементы которого являются массивами, элементы которых, в свою очередь, снова могут быть массивами, и так может продолжаться до некоторого уровня вложенности.

В каких ситуациях может возникать необходимость в таких структурах данных? Эти массивы могут применяться для представления деревьев, у которых узлы могут иметь произвольное число потомков. Таковым может быть, например, генеалогическое дерево. Вершины первого уровня — Fathers, представляющие отцов, могут задаваться одномерным массивом, так что Fathers [i] — это i-й отец. Вершины второго уровня представляются массивом массивов — children, так что children [i] — это массив детей i-го отца, a children [i] [j] — это j-й ребенок i-ro отца. Для представления внуков понадобится третий уровень, так что Grandchildren [i] [j] [k] будет представлять k-го внука j-ro ребенка i-ro отца.