Добавить в цитаты Настройки чтения

Страница 288 из 371



Особое внимание обратите на вывод, связанный с массивами u и v.

Динамические массивы

Во всех вышеприведенных примерах объявлялись статические массивы, поскольку нижняя граница равна нулю по определению, а верхняя всегда задавалась в этих примерах константой. Напомню, что в C# все массивы, независимо оттого, каким выражением описывается граница, рассматриваются как динамические, и память для них распределяется в "куче". Полагаю, что это отражение разумной точки зрения: ведь статические массивы, скорее исключение, а правилом является использование динамических массивов. В действительности реальные потребности в размере массива, скорее всего, выясняются в процессе работы в диалоге с пользователем.

Чисто синтаксически нет существенной разницы в объявлении статических и динамических массивов. Выражение, задающее границу изменения индексов, в динамическом случае содержит переменные. Единственное требование — значения переменных должны быть определены в момент объявления. Это ограничение в C# выполняется автоматически, поскольку хорошо известно, сколь требовательно C# контролирует инициализацию переменных.

Приведу пример, в котором описана работа с динамическим массивом-.

public void TestDynAr()

{

     //объявление динамического массива А1

    Console.WriteLine("Введите число элементов массива А1");

    int size = int.Parse(Console.ReadLine());

    int[] A1 = new int[size];

    Arrs.CreateOneDimAr(A1);

    Arrs.PrintAr1("A1",A1);

}//TestDynAr

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

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

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

Есть некоторые особенности в объявлении и инициализации таких массивов. Если при объявлении типа многомерных массивов для указания размерности использовались запятые, то для изрезанных массивов применяется более ясная символика — совокупности пар квадратных скобок; например, int [] [] задает массив, элементы которого — одномерные массивы элементов типа int.

Сложнее с созданием самих массивов и их инициализацией. Здесь нельзя вызвать конструктор new int [3] [5], поскольку он не задает изрезанный массив. Фактически нужно вызывать конструктор для каждого массива на самом нижнем уровне. В этом и состоит сложность объявления таких массивов. Начну с формального примера:

//массив массивов — формальный пример

//объявление и инициализация

int[] [] jagger = new int[3] []