- •Структуры и алгоритмы обработки данных
- •Алгоритмы. Основные определения
- •От задачи к программе
- •Написание программы.
- •Типы данных, структуры данных и абстрактные типы данных
- •Указатели и курсоры
- •Время выполнения программ
- •Измерение времени выполнения программы
- •Степень (порядок) роста
- •Вычисление времени выполнения программ
- •Линейные абстрактные типы данных атд «Список»
- •Реализация списков посредством массивов
- •Реализация списков с помощью указателей
- •Атд «Стек»
- •Атд «Очередь»
- •Реализация очередей с помощью циклических массивов
- •Нелинейные абстрактные типы данных Деревья
- •Порядок узлов
- •Прямой, обратный и симметричный обходы дерева
- •Помеченные деревья и деревья выражений
- •Представление деревьев с помощью массивов
- •Специальные виды множеств атд “Дерево двоичного поиска”
- •Атд "Словарь", основанный
- •Представление ориентированных графов
- •Атд для ориентированных графов
- •Задача нахождения кратчайшего пути
- •Int **c, // массив стоимостей
- •Int *d) // массив кратчайших
- •Остовные деревья минимальной стоимости
- •Обход графов
- •Поиск в ширину
- •Поиск в глубину
- •Сортировка
- •Простые схемы сортировки Метод «пузырька»
- •Сортировка вставками
- •Быстрая сортировка
- •Пирамидальная сортировка
- •«Карманная» сортировка
- •Порядковые статистики
- •Методы разработки алгоритмов. Типы алгоритмов
- •Алгоритмы «разделяй и властвуй» (метод декомпозиции)
- •Баланс подзадач
- •Динамическое программирование
- •Перемножение нескольких матриц
- •Шаг 1: строение оптимальной расстановки скобок
- •Шаг 2: рекуррентное соотношение
- •Шаг 3: вычисление оптимальной стоимости
- •Void MatrixChainOrder(int n, // кол-во матриц
- •Int p[], // размеры матриц
- •Int **s) // оптимальное k
- •Шаг 4: построение оптимального решения
- •Int **s, // таблица, полученная
- •Int I, // индексы
- •Когда применимо динамическое программирование?
- •Оптимальность для подзадач
- •Перекрывающиеся подзадачи
- •«Жадные» алгоритмы
- •"Жадные" алгоритмы как эвристики
- •Когда применим жадный алгоритм?
- •Принцип жадного выбора
- •Оптимальность для подзадач
- •Поиск с возвратом
- •Функции выигрыша
- •Метод ветвей и границ
- •Структуры данных и алгоритмы для внешней памяти
- •Внешняя сортировка
- •Хранение данных в файлах
- •Простая организация данных
- •Хешированные файлы
- •Индексированные файлы
- •Содержание
- •Глава I. Линейные абстрактные типы данных 31
- •Глава II. Сортировка 108
Сортировка вставками
Алгоритм сортировки вставками: добавляем элементы один за другим к отсортированной части массива. Именно таким способом обычно сортируют карты: держа в левой руке уже упорядоченные карты и взяв правой рукой очередную карту, мы вставляем ее в нужное место, сравнивая с имеющимися. Запишем этот алгоритм в виде процедуры.
Пусть необходимо отсортировать массив А из n элементов. Последовательность сортируется на месте без дополнительной памяти. Помимо массива мы используем только 1 дополнительную ячейку памяти.
После выполнения процедуры массив упорядочен по возрастанию.
insertionSort(element_type a[], int n)
{
element_type key;
int i, j;
for(j=1; j < n; j++)
{
key=a[j];
// берем элемент а[j] и сдвигаем идущие
// перед ним и большие его по величине
// элементы вправо, освобождая место
// для взятого элемента
for( i = j-1; i>=0 && a[i]>key ; i-- )
a[i+1]=a[i];
a[i+1]=key;
// элемент помещается
// на освобожденное место
} // for j
}
Временная сложность алгоритма сортировки вставками О(n2)
Быстрая сортировка
Быстрая сортировка Хоара – алгоритм с временем выполнения О(n log n) в среднем и О(n2) в худшем случае.
Основан на принципе «разделяй и властвуй».
Сортировка участка а[p],…,a[r] происходит следующим образом
-
элементы массива а представляются так, чтобы любой из элементов a[p],…,a[q] был не больше любого из элементов a[q+1],…,a[r]. Эту операцию будем называть разделением.
-
процедура сортировки рекурсивно вызывается для массивов a[p],…,a[q] и a[q+1],…,a[r].
Void Quicksort( int *a, int p, int r)
{
int q;
if ( p < r)
{
q = Partition(a, p, r);
Quicksort(a, p, q);
Quicksort(a, q+1, r);
}
}
Для сортировки всего массива необходимо выполнить процедуру Quicksort(a, 0 , n-1).
Разбиение массива
int Partition(int *a, int p, int r)
{
int x, i, j;
x = a[p]; i = p-1; j = r+1;
while(1)
{
do
j--;
while(a[j] <= x);
do
i++;
while(a[i]>=x);
if(i<j)
{
temp=a[i]; a[i]=a[j]; a[j]=temp;
}
else
return j;
}
}
В виду сложности дальнейших алгоритмов, разбирать их не будем, ознакомимся с их основными характеристиками.
Пирамидальная сортировка
Свое название алгоритм получил вследствие того, что применяемое здесь частично упорядоченное сортирующее дерево имеет вид «пирамиды».
Время выполнения этого алгоритма в худшем случае такое как и в среднем, и имеет порядок О(n log n).
Хотя этот алгоритм и имеет время выполнения порядка О(n log n) в самом худшем случае, в среднем его время несколько хуже, чем в быстрой сортировке, хотя и имеет тот же порядок.
На практике этот алгоритм полезен тогда, когда надо не сортировать все n элементов списка, а только отобрать k наименьших элементов, и при этом k значительно меньше n.
«Карманная» сортировка
Часто можно получить время сортировки меньшее, чем О(n log n), но необходима дополнительная информация о ключах. Например, что они не повторяются (уникальный ключ).
Предположим, что значения ключей есть целые числа от 0 до n-1.
Пусть
а[] – массив, подлежащий сортировке,
b[] – отсортированный массив той же величины, тогда можно организовать поочередное помещение в массив b записей в порядке возрастания значений ключей следующим образом:
for(i=0; i<n; i++)
b[a[i].key]=a[i];
Этот цикл требует времени порядка О(n) и работает корректно только тогда, когда значения всех ключей различны и являются целыми числами из интервала от 0 до n-1. Это пример простой «карманной» сортировки. «Карманами» для записи являются элементы массива b.