
- •Курс лекций по дисциплине “Основы программирования и алгоритмические языки” Бондарев в.М., Марченко ю.С. Введение
- •Основы алгоритмизации
- •1 Основные этапы решения задачи на эвм
- •1.1 Постановка задачи
- •1.2 Проектирование программы
- •1.3 Разработка алгоритма
- •1.4 Кодирование
- •1.5 Отладка и тестирование программы
- •2 Элементарные алгоритмические структуры
- •2.1 Последовательная алгоритмическая структура
- •2.2 Алгоритмическая структура выбора
- •2.3 Алгоритмическая структура повторения
- •2.4 Комбинация структур
- •2.5 Пошаговая детализация алгоритма
- •2.6 Разработка алгоритма вычисления Sin X
- •2.7 Разработка алгоритма подсчета простых чисел
- •3 Алгоритмы поиска
- •3.1 Поиск наибольшего среди вводимых чисел
- •3.2 Поиск наибольшего числа в массиве
- •3.3 Поиск заданного числа в массиве
- •3.4 Поиск с порогом
- •3.5 Двоичный поиск
- •4 Алгоритмы сортировки
- •4.1 Обменная сортировка
- •4.2 Сортировка слиянием
- •4.3 Сравнение двух алгоритмов сортировки
- •5 Рекурсивные алгоритмы
- •5.1 Простая рекурсия
- •5.2 Ханойские башни
- •5.3 Быстрая обменная сортировка
- •6.2 Простейшая программа
- •6.3 Составление простой программы
- •6.4 Программа сложение двух чисел
- •6.5 Организация повторений
- •6.6 Условный оператор
- •If (выражение) оператор [else оператор].
- •6.7 Оператор цикла for
- •7 Указатели и массивы
- •7.1 Указатели
- •7.2 Разыменование и разадресация
- •7.3 Операции new и delete
- •7.4 Массивы
- •7.5 Многомерные массивы
- •7.6 Связь между массивами и указателями
- •7.7 Массивы в динамической памяти
- •8 Строки и структуры
- •8.1 Встроенный тип char
- •8.2 Строки символов как массивы
- •8.3 Строковые библиотечные функции
- •8.4 Структуры
- •8.5 Объявление структур
- •8.6 Битовые поля
- •8.7 Объединения
- •9 Функции
- •9.1 Функция для сложения чисел
- •9.2 Ссылки
- •9.3 Выходные параметры функции
- •9.4 Ссылка — возвращаемое значение
- •9.5 Одномерные массивы как параметры
- •9.6 Двумерные массивы как параметры
- •10 Еще о функциях
- •10.1 Параметры по умолчанию
- •10.2 Произвольное число параметров
- •10.3 Неиспользуемые параметры
- •10.4 Перегруженные функции
- •10.5 Указатель на функцию
- •Void error(char* p) { /*тело ф-ции*/} ,
- •10.6 Спецификатор inline
- •Inline void error(char* p) { /*тело ф-ции*/}
- •10.7 Макросы
- •11 Ввод и вывод
- •11.1 Разновидности ввода и вывода
- •11.2 Открытие и закрытие потока
- •11.3 Ввод и вывод символов
- •11.4 Ввод и вывод строк
- •11.5 Ввод и вывод записей
- •11.6 Управление указателем файла
- •11.7 Состояние потока
- •11.8 Форматированный вывод
- •11.9 Форматированный ввод
- •11.10 Другие функции форматного ввода и вывода
- •12 Уточнение понятий языка
- •12.1 Объявление, определение, инициализация
- •12.2 Область видимости и время жизни
- •12.3 Типы
- •12.4 Производные типы
- •12.5 Числовые константы
- •12.6 Именные константы
- •12.7 Перечисление
- •12.8 Порядок вычисления выражений
- •13 Операции и операторы
- •13.1 Сводка операций
- •13.2 Сводка операторов
- •13.3 Оператор выражения
- •13.4 Оператор switch
- •13.5 Операторы break и continue
- •13.6 Оператор goto и метки
- •14 Классы
- •14.1 Определение класса
- •14.2 Инкапсуляция
- •14.3 Конструктор
- •14.4 Деструктор
- •14.5 Пример класса — список в динамической памяти
- •14.6 Указатель на себя
- •15 Производные классы
- •15.1 Простое наследование
- •15.2 Списки инициализации
- •15.3 Ключи доступа
- •15.4 Виртуальные функции
- •15.5 Реализация виртуальных функций
- •15.6 Полиморфизм
- •16 Еще о класcах
- •16.1 Статические элементы
- •16.2 Друзья класса
- •16.3 Перегрузка операций
- •16.4 Множественное наследование
- •17.1 Библиотека потоков
- •17.2 Предопределенные потоки
- •17.3 Операции помещения в поток и извлечения из потока
- •17.4 Форматирующие функции-элементы
- •17.5 Флаги форматирования
- •17.6 Манипуляторы
- •18 Еще о потоках
- •18.1 Ошибки потока
- •18.2 Опрос состояния потока
- •18.3 Файловый ввод-вывод с применением потоков
- •18.4 Конструкторы файловых потоков
- •18.5 Функции для открытия и закрытия файлов
- •18.6 Замена буфера потока
- •18.7 Текстовый и бинарный ввод-вывод
- •18.8 Бесформатный ввод и вывод
- •18.9. Часто применяемые функции потока
- •18.10 Форматирование в памяти
- •18.11 Дополнительные возможности ostrstream
- •19 Шаблоны
- •19.1 Шаблоны функций
- •19.2 Перегрузка и специализация шаблонов
- •19.3 Шаблоны классов
- •20 Директивы препроцесора
- •20.1 Директива #define
- •20.2 Директива #include
- •20.3 Условная компиляция
- •20.4 Директива #error
- •20.5 Директива #line
- •20.6 Директива #pragma
- •21.1 Адресация памяти
- •21.2 Модели памяти
- •21.3 Спецификация указателей
- •Int near* var_name;
- •Int* near var_name;
- •21.4 Макросы для указателей
- •21.5 Модификаторы переменных
- •21.6 Модификаторы функций
- •21.7 Соглашения о вызове
- •21.8 Встроенный код ассемблера
- •21.9 Псевдорегистры
- •Литература
- •Содержание
- •21.8 Встроенный код ассемблера ........................................................
- •21.9 Псевдорегистры ............................................................................
4 Алгоритмы сортировки
Эффективность двоичного поиска и общая обстановка в стране убедительно свидетельствуют в пользу порядка. В этом разделе мы рассмотрим несколько алгоритмов упорядочения массивов.
4.1 Обменная сортировка
Задан массив из n чисел, например:
50 40 10 60 30 20.
Пройдем вдоль массива, сравнивая на каждом шаге пару соседних чисел: 1-е со 2-м, 2-е с 3-м и т.д. Если левое число окажется больше правого, будем менять их местами. Получим:
40 10 50 30 20 60.
Полного порядка пока не получилось, но самое большое число в массиве попало на свое законное место. Проделаем то же самое еще раз:
10 40 30 20 50 60.
Теперь уже два числа в массиве, 50 и 60, занимают свои места. Нетрудно понять, что после (n-1)-го прохождения, а возможно и раньше, все числа в массиве станут на свои места, и он будет полностью упорядочен:
10 30 20 40 50 60.
10 20 30 40 50 60.
Запишем этот алгоритм.
Начало
Установить в 1 счетчик прохождений, Run = 1;
Пока Run < n -1 повторять
Начало
Пройти массив, сравнивая пары чисел;
Увеличить на 1 счетчик прохождений, Run = Run + 1;
Конец
Конец
Детализируем оператор "Пройти массив, сравнивая пары чисел", полагая, что числа хранятся в массиве M.
Начало
Установить в 1 счетчик элементов массива, count = 1;
Пока count <= (n - Run) повторять
Начало
Если M[count]>M[count+1], то M[count] <=> M[count+1];
count = count + 1;
Конец
Конец
Как видите, мы немного усовершенствовали алгоритм прохождения и теперь каждый новый проход на 1 короче предыдущего.
Остается подставить второй алгоритм в первый (будем называть их подчиненным и главным соответственно), чтобы получить законченный алгоритм обменной сортировки. Однако в практике программирования часто поступают иначе.
Подчиненный алгоритм (процедуру) снабжают заголовком, в котором указывают название алгоритма и перечисляют параметры — переменные, которые подчиненный алгоритм получает от главного или возвращает ему. В нашем примере починенному алгоритму можно дать следующий заголовок:
Процедура Прохождение (M, Run)
В том месте главного алгоритма, где должен располагаться подчиненный, помещают оператор процедуры — имя подчиненного алгоритма со списком значений или переменных, их называют аргументами процедуры. Смысл оператора процедуры в том, что значения аргументов передаются параметрам, и начинает выполняться подчиненный алгоритм. После того как подчиненный алгоритм завершится, главный алгоритм продолжится с оператора, следующего за оператором процедуры.
Окончательно алгоритм обменной сортировки примет вид:
Начало
Установить в 1 счетчик прохождений, Run = 1;
Пока Run < n-1 повторять
Начало
Прохождение (M, Run);
Увеличить на 1 счетчик прохождений, Run = Run + 1;
Конец
Конец
Процедура Прохождение (M, Run);
Начало
Установить в 1 счетчик элементов массива, count = 1;
Пока count <= (n - Run) повторять
Начало
Если M[count]>M[count+1], то M[count] <=> M[count+1];
count = count + 1;
Конец
Конец
4.2 Сортировка слиянием
Чтобы лучше понять смысл сортировки слиянием, отдельно рассмотрим алгоритм слияния.
З а д а ч а. Даны два упорядоченных массива A и B. Слить их в третий массив C, тоже упорядоченный.
Р е ш е н и е.
Сравним первые элементы сливаемых массивов, A[1] и B[1]. Меньший из них поместим в начало массива С и вычеркнем из исходного массива. Снова сравним два первых, не вычеркнутых элемента массивов A и B, меньший перепишем в C и вычеркнем и т.д., пока один из исходных массивов не исчерпается. Тогда перепишем в C остаток другого массива и слияние закончено.
Более точно все это можно записать в виде следующего алгоритма.
Начало
Обнулить счетчик вычеркнутых элементов массива А, СrossA = 0;
Сделать то же для массива В, СrossB = 0;
Обнулить счетчик элементов, записанных в массива C, WriteC = 0;
Пока в обоих массивах есть не вычеркнутые элементы повторять
Начало
ЕслиA[CrossA+1] < B[CrossB+1]то Начало
переписать в С из А, C[WriteC+1] = A[CrossA+1];
вычеркнуть элемент из А, CrossA = CrossA+1;
Конец иначе Начало
переписать в С из В, C[WriteC+1] = B[CrossB+1];
вычеркнуть элемент из В, CrossB = CrossB+1;
Конец
Увеличить счетчик записи в C, writeC = writeC+1;
Конец
Еслимассив А исчерпан, CrossA = nA,то
переписать в массив С остаток массива В;
Еслимассив В исчерпан, CrossВ = nВ,то
переписать в массив С остаток массива А;
Конец
Массив C будет полностью упорядочен, т.к. числа, которые попали туда раньше, меньше тех, что попали туда позже.
Слияние выполнено за один просмотр исходных массивов, поэтому затраченное время можно оценить функцией
,
где n — общее количество элементов в массивах А и В.
Вернемся к сортировке слиянием. Даже в неупорядоченном массиве можно обнаружить упорядоченные участки. Тривиальным будет утверждение, что любой массив состоит из упорядоченных участков единичной длины.
Разобьем сортируемый массив на две половины, А1 и В1, каждую из которых станем рассматривать как массив, состоящий из упорядоченных участков единичной длины.
Соединим их, применяя алгоритм слияния к упорядоченным участкам. Результаты слияния очередной пары участков будем заносить попеременно в два новых массива, А2 и В2. В новых массивах упорядоченные участки будут иметь длину, равную сумме длин исходных участков, т.е. 2.
Сольем теперь упорядоченные участки массивов А2 и В2, попеременно записывая результаты в освободившиеся массивы А1 и В1. Длина упорядоченных участков снова удвоится и станет равна 4.
Будем повторять слияние массивов, каждый раз меняя ролями пары (А1, В1) и (А2, В2), пока длина упорядоченного участка не сравняется с длиной исходного массива. В этот момент сортировку можно считать законченной.
Запишем сказанное в виде алгоритма.
Начало
Разбить сортируемый массив на две половины, А1 и В1;
Установить начальную длину упорядоченного участка, d = 1;
Пока d < n повторять Начало
Слить входные массивы, и получить два выходных массива;
Удвоить длину упорядоченных участков, d = d * 2;
Сделать выходные массивы входными и наоборот;
Конец
Конец
Алгоритм сортировки слиянием не разработан здесь до той степени, что обменная сортировка. Его полную детализацию и программирование вы должны выполнить самостоятельно.