
- •Язык c# и структура простой программы
- •Что такое .Net Framework
- •Что такое с#
- •Создание первой программы на с#
- •Пространство имен System
- •С# как язык Объектно-Ориентированного Программирования (ооп)
- •Состав и назначение файлов проекта
- •Структура сборки
- •Элементы языка c#
- •Базовый синтаксис с#
- •Переменные
- •Числа без знака
- •Числа со знаком
- •Числа для финансистов
- •Текстовые символы
- •Логический тип данных
- •Литералы с плавающей точкой
- •Символьные литералы
- •Строковые литералы
- •Выражения и операторы с#
- •Инициализация переменных и оператор присваивания
- •Значение в левой части
- •Ввод данных с консоли
- •Математические операторы
- •Вычисление остатка при целочисленном делении
- •Унарные операторы
- •Унарное логическое отрицание
- •Составные операторы
- •Поразрядные операторы
- •Поразрядное логическое и
- •Поразрядное логическое или
- •Поразрядное логическое исключающее или
- •Унарная поразрядная операция дополнения
- •Поразрядный сдвиг
- •Пример использования поразрядных операторов
- •Логические операторы
- •Операторы отношения
- •Приоритеты операторов
- •Исполнители алгоритмов
- •Исполнитель Чертежник
- •Исполнитель Робот
- •Компьютер как формальный исполнитель алгоритмов
- •Блок-схема
- •Псевдокод
- •Необходимые свойства алгоритма
- •Понятие о временной сложности алгоритма
- •Примеры
- •Правила для определения сложности
- •Тернарный условный оператор
- •Применение логических операций
- •Конструкция выбора
- •Примеры применения оператора выбора
- •Объединение меток case
- •Пропущенный break
- •Пример программы: Простые числа
- •Пример обработки одномерного массива чисел
- •Использование генератора случайных данных
- •Использование оператора foreach
- •Типы задач обработки одномерных числовых массивов
- •Поиск элемента в массиве
- •Многомерные массивы
- •Базовые операции со строками
- •Методы типа string
- •Копирование и клонирование строк
- •Конкатенация строк
- •Извлечение подстроки
- •Вставка подстроки
- •Замена символов и строк
- •Удаление символов из строки
- •Удаление незначащих пробелов
- •Преобразование к верхнему и нижнему регистру
- •Выравнивание по левому и правому краю поля
- •Объединение массива строк
- •Разбор строки
- •Сравнение строк
- •Форматирование текстовых строк
- •Функции
- •Описание и использование функций
- •Возвращаемые значения
- •Передача параметров
- •Выходные параметры
- •Область действия переменных
- •Область действия переменных и управляющие конструкции
- •Рекурсия
- •Параметры функции Main()
- •Перегрузка функций
- •Перечислимый тип
- •Определение перечислимых типов
- •Структуры
- •Описание структур
- •Использование структур в качестве параметров функций
- •Использование структур в качестве возвращаемых значений функций
- •Функции структур
- •Файлы и потоки
- •Основные классы ввода и вывода
- •Классы для работы с потоками
- •Классы для работы с потоками текстовых символов
- •Работа с текстовыми файлами
- •Запись двоичных данных
- •Пример программы для работы с двоичным файлом
- •Просмотр содержимого каталога
- •Получение информации о каталоге
Поиск элемента в массиве
Задача поиска в массиве какого-либо элемента встречается достаточно часто и имеет несколько разновидностей. Самый элементарный случай заключается в поиске элемента с заданным значением, полагая, что такой элемент единственный. Обычно кроме факта, что такой элемент в массиве есть (или наоборот, что его там нет), пользователя интересует, где именно в массиве этот элемент находится, то есть значение его индекса.
Если элементов с заданным значением или отвечающих заданному условию несколько, то возникает задача поиска либо первого, либо последнего из них. Если массив, где производится поиск, является упорядоченным, то используются специальные алгоритмы, обеспечивающие быстрое нахождение заданного элемента.
Поиск элемента с заданным значением
Данная задача решается достаточно просто с использованием цикла, который прерывается после нахождения заданного элемента:
Ввод В – значения для поиска
Для i = 0 До N-1
Если A(i) = В, То
Выход из цикла
Все-Если
Все-Для-i
Если i < N, То
Вывод: «Элемент », В, « найден в позиции », i
Иначе
Вывод: «В данном массиве элемент », В, « не найден»
Все-Если
После ввода самого массива (что в алгоритме опущено) вводится искомое значение. Цикл прерывается сразу после совпадения искомого значения с элементом массива, что обеспечивает нахождение первого совпадающего элемента. После выхода из цикла значение параметра цикла сравнивается с размером массива: если они совпадают, значит, цикл не был прерван, то есть искомый элемент в массиве отсутствует.
Если необходимо найти не первый, а последний совпадающий элемент, то прерывать цикл поиска нельзя. Вместо этого необходимо запоминать индекс совпадающего элемента в дополнительной переменной, значение которой и определяет искомый результат:
Ввод В – значения для поиска
index = -1
Для i = 0 До N-1
Если A(i) = В, То
index = i
Все-Если
Все-Для-i
Если index > -1, То
Вывод: «Последний элемент », В, « найден в позиции », index
Иначе
Вывод: «В данном массиве элемент », В, « не найден»
Все-Если
В качестве начального значения индекса найденного элемента в алгоритме используется -1, что не может быть реальным индексом массива. После цикла значение индекса сравнивается с этой же константой, в случае его изменения искомый элемент найден.
Поиск элемента в упорядоченном массиве
Оба предыдущих алгоритма не предполагали, что элементы массива упорядочены по возрастанию или убыванию. Поэтому поиск осуществлялся по всему массиву. Такой метод поиска называется линейным и при больших размерах массива требует значительного времени.
Если же массив предварительно упорядочен каким-либо методом (такие методы будут рассмотрены ниже), то можно использовать гораздо более эффективным алгоритмом двоичного поиска.
При описании алгоритма используем следующие переменные:
size – размер массива
K – число, которое нужно найти
М – индекс найденного элемента
На каждом шаге этого циклического алгоритма рассматривается некоторая часть массива (в начале работы – весь массив). Пусть переменные lowerBound и upperBound содержат, соответственно, левую и правую границы отрезка массива, где находится нужный нам элемент. Мы начинаем с исследования среднего элемента отрезка. Если искомое значение меньше среднего элемента, мы переходим к поиску в левой половине отрезка, где все элементы меньше только что проверенного. Другими словами, значением upperBound становится (M – 1) и на следующей итерации мы работаем с половиной массива.
Таким образом, в результате каждой проверки мы вдвое сужаем область поиска. Так, если массив содержит шесть элементов, то после первой итерации область поиска – всего лишь три элемента, после второй остается всего лишь один элемент. Таким образом, если длина массива равна 6, нам достаточно трех итераций, чтобы найти нужное число.
Алгоритм двоичного поиска приведен ниже:
lowerBound = 0
upperBound = size
Пока Истина
M = (lowerBound + upperBound) / 2
Если K < A[M], то
upperBound = M – 1
Иначе Если K > A[M], то
lowerBound = M + 1
Иначе
Вывод «Элемент найден, его индекс: », M
Выход из цикла
Все если
Если lowerBound > upperBound, то
Сообщить «Элемент не найден»
Выход из цикла
Все если
Все-Пока
Поиск последовательности элементов, отвечающих заданному условию
Под последовательностью элементов массива обычно понимается несколько (два или боле) рядом расположенных элементов массива, обладающих каким-либо общим свойством. В задачах этого типа необходимо анализировать рядом расположенные элементы массива, что может быть выполнено двумя различными способами:
можно в цикле просматривать элементы массива до нахождения первого элемента, обладающего данным свойством, а при его наличии организовать второй цикл, проверяющий элементы до достижения конца последовательности или конца массива;
можно обойтись одним циклом, на каждой итерации которого проверять, принадлежит ли текущий элемент последовательности или нет, а при обнаружении конца последовательности выполнить требуемую ее обработку.
Второй способ является более предпочтительным, т.к. содержит один цикл вместо двух вложенных, что снижает временную сложность данного алгоритма. Рассмотрим использование этого способа для решения задачи нахождения первой последовательности нечетных элементов в целочисленном массиве.
Length = 0
Для i = 0 До N-1
Если A(i) - нечетный, То
Length = Length + 1
Иначе
Если Length > 1, То
Выход из цикла
Все-Если
Length = 0
Все-Если
Все-Для-i
Если Length > 1, То
Вывод: «Последовательность начинается в позиции », i – Length,
« и имеет длину », Length
Вывод: «Элементы последовательности:»
Для j = i – Length До i-1
Вывод A(j)
Все-Для-j
Иначе
Вывод: «В данном массиве последовательность не найдена»
Все-Если
Данный алгоритм использует переменную Length для подсчета длины последовательности. В цикле массиву его элементы проверяются на нечетность и при истинности этого условия длина последовательности увеличивается. Если же условие ложно, то проверяется длина найденной последовательности. Если она равна нулю (нечетных элементов не было) или единице (нечетные элементы чередуются с четными), то длина последовательности обнуляется и цикл продолжается. Если же длина больше единицы, то искомая последовательность найдена и цикл прерывается.
После завершения цикла проверяется длина найденной последовательности, и выдаются соответствующие сообщения. Если последовательность найдена, то печатаются ее элементы. При этом значение параметра цикла используется в качестве конечной границы цикла, а индекс начала последовательности определяется вычитанием из конечной границы длины последовательности.
Перестановка элементов в массиве
В данном типе задач элементы массива меняются местами по заданному в задании правилу. Одним из часто встречающихся случаев является перестановка пар элементов, отвечающих заданному условию. Рассмотрим случай, когда необходимо поменять местами пары рядом расположенных элементов, имеющих различные знаки:
Обмен = Нет
Для i = 0 До N-2
Если A(i) * A(i+1) < 0, То
Обмен = Да
Х = A(i)
A(i) = A(i+1)
A(i+1) = Х
Все-Если
Все-Для-i
Если Обмен = Да, То
Печать массива A(N)
Иначе
Печать «Нет таких пар элементов»
Все-Если
В другом случае необходимо поменять элементы массива, которые необходимо предварительно найти. Для этого при поиске элементов запоминаются их индексы, а затем совершается сам обмен аналогично предыдущему алгоритму. Рассмотрим случай, когда необходимо поменять максимальный и минимальный элементы массива:
Минимум = A(0)
ИндексМин = 0
Максимум = A(0)
ИндексМакс = 0
Для i = 0 До N-1
Если A(i) < Минимум, То
Минимум = A(i)
ИндексМин = i
Все-Если
Если A(i) > Максимум, То
Максимум = A(i)
ИндексМакс = i
Все-Если
Все-Для-i
Если Минимум = Максимум, То
Печать «Нет таких пар элементов»
Иначе
A(ИндексМакс) = Минимум
A(ИндексМин) = Максимум
Печать массива A(N)
Все-Если
Сдвиг элементов массива
Различают обычный и циклический сдвиг элементов массива. Обычный сдвиг используется, когда количество элементов массива изменяется, т.е. при удалении и вставке элементов массива. При циклическом сдвиге количество элементов не изменяется, все элементы смещаются либо вправо, либо влево, а элемент, «не умещающийся» в массиве, переносится либо в его начало (при сдвиге вправо), либо в конец (при сдвиге влево).
Алгоритм циклического сдвига влево выглядит следующим образом:
Х = A(0)
Для i = 0 До N-2
A(i) = A(i+1)
Все-Для-i
A(N-1) = Х
Для циклического сдвига вправо цикл необходимо организовать от конца к началу массива, т.е. в обратном направлении:
Х = A(N-1)
Для i = N-1 До 1 Шаг -1
A(i) = A(i-1)
Все-Для-i
A(0) = Х
Удаление и вставка элементов массива
Упорядочение элементов массива
Поиск одинаковых и различных элементов массива
Создание одного массива из другого
Слияние двух массивов в один
Обработка массива символов
Символьный тип char неплохо подходит для работы с массивами букв, если у кого-либо возникает такое желание. В качестве примера приведем пример программы, которая определяет, какой символ встречается наиболее часто во введенной пользователем последовательности, которая заканчивается точкой:
using System;
namespace SymArray
{
class Program
{
static void Main(string[] args)
{
char[] abc =
{'a','b','c','d','e','f','g','h','i','j','k','l','m',
'n','o','p','q','r','s','t','u','v','w','x','y','z'};
char bukva;
// Массив счетчиков для каждой буквы
int[] count = new int[abc.Length];
bukva = Console.ReadKey().KeyChar; // Считываем первый символ
while(bukva != '.')
{
// Ищем букву в алфавите
for (int i = 0; i < abc.Length; i++)
{
if (bukva == abc[i])
{
count[i]++; // Считаем вхождение каждой буквы
break;
}
}
// Считываем следующий символ
bukva = Console.ReadKey().KeyChar;
}
int max = 0, imax = -1;
// Цикл поиска максимума в массиве счетчиков
for (int i = 0; i < abc.Length; i++)
{
if (count[i] > max)
{
max = count[i];
imax = i; // Запоминаем позицию буквы, имеющей
// максимальное вхождение
}
}
Console.Write("\n Чаще других ({0} раз) ", max);
Console.WriteLine("встречается буква {0}", abc[imax]);
Console.ReadLine();
}
}
}
В данной программе объявляется массив символов char[ ] abc, в котором содержится все строчные буквы английского алфавита. Далее размер этого массива используется при размещении массива целочисленных счетчиков для каждой буквы этого алфавита:
int[] count = new int[abc.Length];
Ввод символа с консоли осуществляется с помощью метода ReadKey(). К сожалению, просто вызвать этот метод недостаточно, т.к. он возвращает не просто прочитанный с клавиатуры символ, а объект стандартного класса ConsoleKeyInfo. Это сделано разработчиками .NET для обеспечения работы со всеми клавишами, в том числе и функциональными. Нам эта возможность не нужна, достаточно получить код нажатого символа. Для этого нужно воспользоваться свойством KeyChar класса ConsoleKeyInfo:
char bukva = Console.ReadKey().KeyChar;
Для ввода всех обрабатываемых символов и завершения программы при вводе точки используется цикл while. Первый символ вводится отдельно перед началом цикла, что позволяет легко задать условие завершения цикла, все остальные вводятся внутри цикла перед его повторением. Такая конструкция достаточно часто используется для обработки последовательностей исходных данных заранее неизвестного размера:
bukva = Console.ReadKey().KeyChar; // Считываем первый символ
while(bukva != '.')
{
// Обработка введенного символа
// . . .
bukva = Console.ReadKey().KeyChar; // Следующий символ
}
Для поиска введенного символа в алфавите и определения наиболее часто встречающегося символа используются циклы for. При этом цикл поиска находится внутри цикла while, то есть используются вложенные циклы.
Результат работы этой программы может выглядеть так: