Лекции по C#(первая часть, версия 2.2)
.pdf
На практике оператор объявления одномерного массива может выглядеть заметно проще:
using System; class Массивы1
{
static void Main()
{
int[] m1 = new int[4], m2 = new int[] {2,4,6,8}, m3 = {1,3,5,7 };
for (short j = 0; j < 4;) m1[j] = ++j; int сумма = 0 ;
for (short i = 0 ; i <= 3; i++) сумма += m1[i] + m2[i] + m3[i]; Console.WriteLine("{0:d}", сумма);
}
}
На экране:
Оператор объявления массива, в общем случае, может состоять из трёх частей. Но уже и первая часть, например, int[] m1 представляет собой, в принципе, завершённый оператор. Его исполнение транслятором сводится к созданию ссылки с
именем m1 . Ссылка - это особого типа объект, предназначенный для размещения адреса. Ссылка должна соответствовать типу объекта, адрес которого она потенциально может содержать. Однако одно объявление ссылки значение конкретного адреса не обеспечивает.
Для инициализации (задания значения) ссылки в языке С# используется оператор new . Его действие сводится к запросу у исполняющей системы необходимого фрагмента динамической области памяти. В общем случае, область динамической памяти состоит из двух разделов: стека и кучи (heap). Место, где физически будет выделена память, определяется категорией объекта: ссылочные объекты размещаются в области heap, а значащие – в стеке.
Инициализирующий блок может содержать константы или константные выражения. Если размер массива определён в операции new, то количество инициализаторов должно точно соответствовать количеству элементов массива. При отсутствии инициализирующего блока, элементы массива получают значения по умолчанию (для числовых массивов – это нуль). Оператор new возвращает адрес выделенной области и затем этим адресом инициализируется ссылка. Аргументами оператора new является тип и количество элементов, которые необходимо разместить в динамической памяти. В языке С# нет необходимости заботиться об удалении объектов из динамической области после того, как обработка их завершена. Освобождением памяти занимается специальная программа – «сборщик мусора» -
Garbage Collector.
Как следует из примера, при наличии блока инициализаторов в операторе объявления массива может быть опущено, как количество элементов в операции new (массив m2), так и сама эта операция (массив m3). Типы констант в блоке инициализации либо должны соответствовать типу элементов массива, либо должны быть неявно приводимыми к этому типу (расширяющее неявное преобразование) .
51
Отдельного комментария требует переменная сумма. Как и следует из названия, эта переменная является сумматором. Чаще всего сумматор инициализируется нулем, что и сделано в примере. Следующий рисунок демонстрирует схему размещения массивов из примера Массивы1:
|
|
|
|
|
|
Динамическая область (heap) |
|||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Стек |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
0 |
|
1 |
|
2 |
|
3 |
|
|
||||||||
|
|
|
|
|
|
|
|
индексы |
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m1 |
|
адрес |
|
|
|
|
0 |
|
0 |
|
|
0 |
|
|
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
1 |
|
2 |
|
3 |
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m2 |
|
адрес |
|
|
|
|
2 |
|
4 |
|
|
6 |
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
0 |
|
1 |
|
2 |
|
3 |
|
|
|
||||
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m3 |
|
адрес |
|
|
|
1 |
|
3 |
|
|
5 |
|
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4.6.2 Оператор цикла foreach
Оператор foreach («для каждого») предназначен для работы с объектами, состоящими из некоторого множества элементов (их ещё называют коллекциями). Прежде всего, это относится к массивам. Синтаксис оператора foreach следующий
foreach ( Элемент in ИмяМассива ) оператор ;
При этом тип Элемента должен совпадать с типом массива. Так предыдущий пример с использованием оператора foreach выглядит несколько проще, хотя, несложно заметить, что foreach может быть использован только для чтения значений элементов массива, но не для их установки или изменения.
using System; class Массивы2
{
static void Main()
{
int[] m1 = new int[4], m2 = new int[] {2,4,6,8}, m3 = {1,3,5,7}; for (short j = 0; j < 4; ) m1[j] = ++j;
int сумма = 0 ;
foreach (int j in m1) сумма += j; foreach (int j in m2) сумма += j; foreach (int j in m3) сумма += j; Console.WriteLine("{0:d}", сумма);
}
}
На блок – схеме цикл foreach обычно указывают следующим образом :
52
…
для j из массива m1
сумма <- сумма +1
…
4.6.3 Простейшие приёмы работы с одномерными массивами
4.6.3.1 Инициализация массивов датчиком случайных чисел
Некоторые приёмы инициализации массивов были рассмотрены в предшествующем разделе. На практике (к примеру, на этапе отладки какого-либо алгоритма) зачастую бывает необходимо наполнить массив произвольными числовыми значениями. Для этого существует специальный класс Random (пространство имён System) – генератора псевдослучайных значений. Технология его использования включает следующие этапы :
1.Создание объекта класса Random;
2.Вызов необходимого метода для созданного в п.1 объекта. Наиболее используемые методы класса Random
Назначение
Возвращает очередное псевдослучайное целое число в диапазоне от 0 до 0x7FFFFFFF
Возвращает очередное псевдослучайное целое число в диапазоне от 0 до Max
Возвращает очередное псевдослучайное целое число в диапазоне от Min до Max
Возвращает очередное псевдослучайное вещественное число в диапазоне от 0,0 до 1,0
using System; class Массивы3
{
static void Main()
{
Random Gen = new Random(); int[] m1 = new int[10];
for (int i = 0; i < 10; i++) m1[i] = Gen.Next(100); int Cчётчик = 0 ;
foreach (int j in m1)
if (j % 2 == 0) Cчётчик++; Console.WriteLine("Массив случайных значений"); for (int i = 0; i < 10; i++ ) Console.WriteLine("m1[{0}] = {1:d}", i, m1[i]);
53
Console.WriteLine("Количество четных = {0}", Cчётчик);
}
}
На экране:
В данном примере первый раз демонстрируется вызов нестатического метода. Методом называется функция, являющаяся элементом класса. Функция WriteLine также является методом класса Console, но статическим. И её вызов технологически проще – необходимо через соединитель - точку указать имя класса и имя метода метод. Нестатические методы класса могут быть вызваны только в привязке к экземпляру (объекту) класса. Для этого приходиться объявлять объект Gen и выделать ему память (экземпляры класса, как и массивы являются ссылочными объектами). Разработка пользовательских методов (разумеется, в рамках соответствующих классов) – предмет будущих разговоров в рамках дисциплины «Современные языки программирования».
Листинг примера Массивы3 умышленно выполнен без структурирования. В качестве самостоятельного упражнения нарисуйте блок – схему этого примера.
4.6.3.2Экстремальные значения, статистические характеристики элементов массива
Предположим, что в массиве целых случайных значений, размер которого задаётся пользователем, требуется найти минимум, максимум, и такие статистические характеристики, как среднее значение, дисперсия, среднеквадратичное отклонение. Пример Массивы4 выполняет перечисленные действия.
using System; class Массивы4
{
static void Main()
{
string s;
Console.WriteLine("Задайте количество элементов массива"); s = Console.ReadLine();
int i = Convert.ToInt32(s); int[] mas = new int[i];
Random Gen = new Random();
for (int k = 0; k < mas.Length; k++) mas[k] = Gen.Next(1,10);
Console.WriteLine("Элементы массива\n");
foreach (int j in mas) Console.Write("{0,8}", j); int max = mas[0], min = mas [0];
54
foreach (int j in mas)
{
if (max < j) max = j; if (min > j) min = j;
}
Console.WriteLine("\n Максимум= {0}, Минимум ={1}", max, min);
float среднее=0, дисперсия=0; foreach (int j in mas) среднее+=j; среднее/=mas.Length;
Console.WriteLine("\nСреднее арифметическое= {0:f5}",среднее);
foreach (int j in mas) дисперсия+= (j-среднее)*(j-среднее); дисперсия/=mas.Length;
Console.WriteLine("Дисперсия = {0:f5}",дисперсия); Console.WriteLine("Среднеквадратичное отклонение= {0:f5}", Math.Sqrt(дисперсия));
}
}
На экране:
Впредшествующем примере впервые используется то обстоятельство, что любой массив является потомком класса System.Array. В соответствии с этим , в составе любого массива есть элементы указанного класса. Здесь используется свойство Length . Пока достаточно определиться, что это - элемент объекта, с помощью которого можно получить количество элементов данного массива. Иным способом могло быть использование значения переменной i, полученной с клавиатуры, но использование свойства Length является более универсальным решением.
Впримере Массивы4 используется также статический метод класса Math c именем Sqrt , который возвращает квадратный корень аргумента.
4.6.3.3 Методы сортировки одномерных массивов
Сортировкой принято называть процесс, позволяющий упорядочить множество подобных данных в возрастающем или убывающем порядке. При этом часть данных, определяющее место элемента в общем списке, называется ключом: при сравнении используется только значение ключа, а остальные данные, входящие в состав элемента, переносятся на новое место целиком. Ниже будут рассмотрены
55
простейшие методы сортировки числовых массивов, в которых данные и ключ – это одно и тоже.
Среди наиболее востребованных методов сортировки можно выделить следующие:
Отбора (еще известен, как метод минимакса);
Перестановки (или пузырьковой сортировки);
Вставки;
Шелла;
Компаранда.
Особенности каждого из перечисленных методов иллюстрирует соответствующий рисунок.
0 |
1 |
2 |
3 |
N-1 |
Массив |
|
|
|
|
|
|
|
Первый проход |
|
|
|
|
|
max1 |
Поиск |
|
|
|
Второй проход |
максимального |
|
|
|
|
|
|
|
max2 |
|
элемента |
|
|
|
|
|
|
|
|
… |
|
|
|
|
Последний проход |
maxN
Сортировка массива по убыванию методом минимакса.
56
Массив
Сравнение
соседних элементов: если их отношение не соответствует «направлению» сортировки, они меняются местами
Массив
Важно!
Если на очередном проходе выясняется, что не было произведено ни одной перестановки – значит массив уже отсортирован!
Массив
0 1 2 3 |
|
|
|
|
|
|
N-1 |
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
max1
Первый проход сравнения соседей:
максимальный «всплывает» и занимает место последнего элемента
0 1 2 3 |
|
|
|
|
|
|
N-1 |
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
max2
Второй проход сравнения соседей:
максимальный «всплывает» и занимает место предпоследнего
элемента
…
01 Последний (N-1-ый) проход: сравнение нулевого и первого элемента - массив окончательно отсортирован
Сортировка массива по возрастанию методом пузырька.
0 1 2 3 |
|
|
|
|
|
|
|
N-1 |
|
|
|||||||||||
Исходный массив |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Начало: |
|
нулевой |
элемент |
|||||||
|
|
|
|
|
|
|
|
|
|
|
переписывается в итоговый массив |
||||||||||
Итоговый массив |
|
|
|
|
|
|
|
|
|
Первый шаг: первый элемент |
|||||||||||
|
|
|
|
|
|
|
переписывается в итоговый массив |
||||||||||||||
? |
|
|
|
|
|
на место, соответствующее направ- |
|||||||||||||||
|
|
|
|
|
лению сортировки – либо до-, либо |
||||||||||||||||
Итоговый массив |
|
|
|
|
|
|
|
|
|
|
после нулевого элемента |
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
? |
|
|
|
Второй шаг: второй элемент |
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
переписывается в |
итоговый массив |
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
на место, соответствующее направ- |
||||||||
Итоговый массив |
|
|
|
|
|
|
|
лению сортировки |
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
…
Сортировка массива методом вставки.
57
0 1 2 3 |
|
N-1 |
Массив
Сравнение элементов: если их отношение не соответствует «направлению» сортировки, они меняются местами
Первый проход: сравнение элементов, отстоящих друг от друга на три позиции
0 1 2 3 |
|
N-1 |
Массив
Второй проход: сравнение элементов, отстоящих друг от друга на две позиции
|
|
|
|
|
|
|
|
|
|
0 |
1 |
2 3 |
|
|
|
|
|
|
|
N-1 |
|
|||||||||||
|
|
|
|
|
|
|
|
|
Массив |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||||||||
Последний проход: сравнение соседних элементов |
|
|
|
|
|
|||||||||||||||||||||||||||
|
Сортировка массива методом Шелла. |
|
|
|
|
|
||||||||||||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
0 1 2 |
3 |
|
|
|
|
|
|
|
|
|
N-1 |
|
|
|
|
|
|||||||||||||||
Исходный массив |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Шаг1: |
определяются |
|
компаранд |
|
|
|
|
|
||||||||||||||||||||||||
|
элементы, значения |
|
которых |
|||||||||||||||||||||||||||||
компаранд: |
элемент |
- |
|
|
|
|
|
|||||||||||||||||||||||||
|
|
|
|
больше компаранда |
|
|
|
|
|
|||||||||||||||||||||||
расположенный в середине (или |
|
|
|
|
|
|
||||||||||||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||||
почти в середине) массива. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
элементы, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
значения |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Шаг2: из исходного массива формируются два раздела в |
||||||||||||||||||||||||||||||||
которых |
||||||||||||||||||||||||||||||||
меньше |
первый переписываются элементы, значение которых |
|||||||||||||||||||||||||||||||
компаранда |
меньше компаранда, во второй - больше |
|
|
|
|
|
||||||||||||||||||||||||||
Для каждого из полученных разделов выполняются шаг1 и шаг2, пока количество элементов в получаемых разделах не станет равным единице.
Сортировка массива по возрастанию методом компаранда Пример Сортировка1 реализует сортировку методом минимакса.
using System; class Сортировка1
{
static void Main()
58
{
string s;
Console.WriteLine("Задайте количество элементов массива"); s = Console.ReadLine();
int k = Convert.ToInt32(s); int[] mas = new int[k]; Random Gen = new Random();
for (int i = 0; i < mas.Length; i++) mas[i] = Gen.Next(1,100); Console.WriteLine("Элементы массива");
foreach (int j in mas) Console.Write("{0,8}", j); int max , imax;
for (int i = 0; i < mas.Length - 1; i++)
{
max = mas[imax = i];
for ( int j = i + 1 ; j < mas.Length; j++) if (max < mas[j]) max = mas[imax = j];
mas[imax] = mas[i]; mas[i] = max;
}
Console.WriteLine("\nЭлементы массива после сортировки по убыванию"); foreach (int j in mas) Console.Write("{0,8}", j);
}
}
На экране:
Пример Сортировка2 реализует пузырьковую сортировку.
using System; class Сортировка2
{
static void Main()
{
string s;
Console.WriteLine("Задайте количество элементов массива"); s = Console.ReadLine();
int k = Convert.ToInt32(s); int[] mas = new int[k]; Random Gen = new Random();
for (int i = 0; i < mas.Length; i++) mas[i] = Gen.Next(1,100); Console.WriteLine("Элементы массива");
foreach (int j in mas) Console.Write("{0,8}", j); int r ;
bool flag = false;
for (int i = 0; i < mas.Length - 1; i++)
{
flag = false;
for ( int j = 0 ; j < mas.Length-i-1 ; j++)
59
if (mas[j] < mas[j + 1]) continue; else
{
r = mas[j];
mas[j] = mas[j + 1]; mas[j+1] = r;
flag = true;
}
if (flag == false) break;
}
Console.WriteLine("\nЭлементы массива после сортировки по возрастанию"); foreach (int j in mas) Console.Write("{0,8}", j);
}
}
Вывод на экран аналогичен.
4.6.4 Двумерные массивы
4.6.4.1 Объявление двумерных массивов
В общем случае массивы в программе на С# могут быть произвольной размерности. На практике используются массивы с размерностью не более 3-х, а чаще всего – двумерные массивы. При этом есть возможность использовать как прямоугольные (или квадратные) массивы, так и ломаные – со строками переменной длины.
Прямоугольные массивы объявляются в соответствии с одним из следующих вариантов синтаксиса:
тип[ , ] ИмяМассива = new тип [ КолСтр, КолСтолб ];
тип[ , ] ИмяМассива = { {Блок инициализаторов для строки0}, {Блок инициализаторов для строки1}, …};
Впервом случае размерности массива задаются явно, а исполняющая система после выделения памяти инициализирует значения элементов массива нулями. Во втором случае опущена операция new и конфигурация массива определяется по содержимому блока инициализаторов для конкретной строки. Правила использования констант в блоке инициализаторов аналогичны одномерным массивам. При этом количество элементов в блоке инициализаторов должно быть исчерпывающим.
По своей конструкции двумерный массив является массивом ссылок (количество элементов - это число строк), каждая из которых должна быть проинициализирована адресом соответствующего фрагмента памяти из кучи.
Вследующем примере демонстрируются варианты объявления прямоугольных двумерных целочисленных массивов.
using System; class Массивы5
{
static void Main()
{
60
