
AISD_EXAM.pptx
- Количество слайдов: 33
Обыкновенным графом называется пара G=(V, E), где V - конечное множество, E - множество неупорядоченных пар различных элементов из . Элементы множества V называются вершинами графа, элементы множества E - его ребрами. Пустой граф - граф, не содержащий ни одного ребра. Пустой граф с множеством вершин {1, 2, …, n} обозначается через On. Полный граф - граф, в котором каждые две вершины смежны. Полный граф с множеством вершин {1, 2, …, n} обозначается через Kn. Путь Pn - граф с множеством вершин {1, 2, …, n} и множеством ребер {(1, 2), (2, 3), …, (n-1, n)}. Цикл Cn - граф, который получается из графа Pn добавлением ребра (1, n). Граф называется взвешенным, если каждому ребру графа поставлено в соответствие некоторое число, называемое весом ребра. Связный граф — граф, содержащий ровно одну компоненту связности. Это означает, что между любой парой вершин этого графа существует как минимум один путь. Ориентированный граф – граф, ребрам которого присвоено направление Путь (или цикл) называют простым, если ребра в нём не повторяются; элементарным, если он простой и вершины в нём не повторяются.
Матрица смежности графа G с конечным числом вершин n (пронумерованных числами от 1 до n) — это квадратная матрица A размера n, в которой значение элемента aij равно числу рёбер из i-й вершины графа в j-ю вершину. Матрица инцидентности — одна из форм представления графа, в которой указываются связи между инцидентными элементами графа (ребро(дуга) и вершина). Столбцы матрицы соответствуют ребрам, строки — вершинам. Ненулевое значение в ячейке матрицы указывает связь между вершиной и ребром (их инцидентность). Список рёбер — это тип представления графа, подразумевающий, что каждое ребро представляется двумя числами — номерами вершин этого ребра. Ориентированный граф называется сильно-связным, если в нём существует ориентированный путь из любой вершины в любую другую, или, что эквивалентно, граф содержит ровно одну сильно связную компоненту. Ориентированный граф называется слабо-связным, если является связным неориентированный граф, полученный из него заменой ориентированных рёбер неориентированными. Если для графа G можно указать пару различных вершин, которые не соединяются цепью (простой цепью), то граф называется несвязным.
Алгоритм Дейкстры Он находит кратчайшее расстояние от одной вершины графа до другой. Ребра не должны иметь отрицательный вес. 1) Всем вершинам, за исключением первой, присваивается вес бесконечно большой. Первой – ноль 2) Все вершины невыделенные 3) Первая вершина объявляется текущей 4) Вес всех невыделенных вершин пересчитывается по формуле: Вес невыделенной вершины равен минимуму из старого веса этой вершины и суммы, равной, весу текущей вершины+весу ребра, которое соединяет текущую вершину с рассматриваемой невыделенной вершиной 5) Среди всех невыделенных вершин ищем вершину с минимальным весом. Найденная вершина становится текущей – она выделяется. 6) Если текущей вершиной становится конечная, то путь найден и его вес – вес конечной вершины. Завершение алгоритма. Если вершина не конечная, то переход в пункт 4.
Алгоритм Флойда-Уоршелла Алгоритм для нахождения кратчайших расстояний между всеми вершинами взвешенного ориентированного графа. Пусть вершины графа G=(V, E), |V|=n пронумерованы от 1 до n и введено обозначение dij для длины кратчайшего пути от i до j, который кроме самих вершин I, j проходит только через вершины 1, …, k. Очевидно, что dij — длина (вес) ребра (i, j), если таковое существует (в противном случае его длина может быть обозначена как ∞). Существует два варианта значения dij, k € (1, …, n): 1) Кратчайший путь между i, j не проходит через вершину k, тогда dij=dij 2) Существует более короткий путь между i, j, проходящий через k, тогда он сначала идёт от i до k, а потом от k до j. В этом случае, очевидно, dij=dik+dkj Таким образом, для нахождения значения функции достаточно выбрать минимум из двух обозначенных значений. Тогда рекуррентная формула для dij имеет вид: dij — длина ребра (i, j) dij=min(dij, dik+dkj) Алгоритм Флойда–Уоршелла последовательно вычисляет все значения dij, i, j для k от 1 до n. Полученные значения dij являются длинами кратчайших путей между вершинами i, j
Маршрут в графе — это чередующаяся последовательность вершин и рёбер v 0, e 1, v 1, …, ek, vk, в которой любые два соседних элемента инцидентны. Если v 0=vk, то маршрут замкнут, иначе открыт. Простой цикл — цикл, не проходящий дважды через одну вершину. Цикл (простой цикл) в орграфе — это простой путь длины не менее 1, который начинается и заканчивается в одной и той же вершине. Гамильтонов цикл — простой цикл в графе, содержащий все вершины графа ровно по одному разу. Эйлеров цикл — это цикл, который содержит все рёбра графа (вершины могут повторяться). Каркас минимального веса графа — ациклическое (не имеющее циклов) множество рёбер в связном, взвешенном и неориентированном графе, соединяющих между собой все вершины данного графа, при этом сумма весов всех рёбер в нём минимальна. Контур — замкнутый путь в орграфе. Клика — подмножество вершин графа, полностью соединённых друг с другом, то есть подграф, являющийся полным графом. Кликовое число — число (G) вершин в наибольшей клике. Правильная раска графа — раска, при которой каждый цветной класс является независимым множеством. Иначе говоря, в правильной раске любые две смежные вершины должны иметь разные цвета. Хроматическое число графа — минимальное количество цветов, требуемое для раски вершин графа, при которой любые вершины, соединенные ребром, раскрашены в разные цвета. Независимое множество вершин есть множество вершин графа G, такое, что любые две вершины в нем не смежны (никакая пара вершин не соединена ребром). Для графа G = (V, E) доминирующее множество вершин есть множество вершин S ⊆ E, выбранное так, что для каждой вершины xj, не входящей в S, существует дуга, идущая из некоторой вершины множества S в вершину xj.
Алгоритм Форда-Беллмана O(N*M) Пусть дан ориентированный взвешенный граф G с n вершинами и m рёбрами, и указана некоторая вершина V. Требуется найти длины кратчайших путей от вершины V до всех остальных вершин. struct edge { int a, b, cost; }; int n, m, v; vector<edge> e; const int INF = 100000; void solve() { vector<int> d (n, INF); d[v] = 0; for (int i=0; i<n-1; ++i) for (int j=0; j<m; ++j) if (d[e[j]. a] < INF) d[e[j]. b] = min (d[e[j]. b], d[e[j]. a] + e[j]. cost); // вывод d, например, на экран }
Алгоритм Прима Дан взвешенный неориентированный граф G с n вершинами и m рёбрами. Требуется найти такое поддерево этого графа, которое бы соединяло все его вершины, и при этом обладало наименьшим возможным весом (т. е. суммой весов рёбер). Поддерево — это набор рёбер, соединяющих все вершины, причём из любой вершины можно добраться до любой другой ровно одним простым путём. Сам алгоритм имеет очень простой вид. Искомый минимальный остов строится постепенно, добавлением в него рёбер по одному. Изначально остов полагается состоящим из единственной вершины (её можно выбрать произвольно). Затем выбирается ребро минимального веса, исходящее из этой вершины, и добавляется в минимальный остов. После этого остов содержит уже две вершины, и теперь ищется и добавляется ребро минимального веса, имеющее один конец в одной из двух выбранных вершин, а другой — наоборот, во всех остальных, кроме этих двух. И так далее, т. е. всякий раз ищется минимальное по весу ребро, один конец которого — уже взятая в остов вершина, а другой конец — ещё не взятая, и это ребро добавляется в остов (если таких рёбер несколько, можно взять любое). Этот процесс повторяется до тех пор, пока остов не станет содержать все вершины (или, что то же самое, n-1 ребро). В итоге будет построен остов, являющийся минимальным. Если граф был изначально не связен, то остов найден не будет (количество выбранных рёбер останется меньше n-1).
Топологическая сортировка — упорядочивание вершин безконтурного ориентированного графа согласно частичному порядку, заданному ребрами орграфа на множестве его вершин. Топологическая сортировка обходом в глубину int n; // число вершин vector<int> g[MAXN]; // граф bool used[MAXN]; vector<int> ans; void dfs (int v) { used[v] = true; for (size_t i=0; i<g[v]. size(); ++i) { int to = g[v][i]; if (!used[to]) dfs (to); } ans. push_back (v); } void top_sort() { for (int i=0; i<n; ++i) used[i] = false; ans. clear(); for (int i=0; i<n; ++i) if (!used[i]) dfs (i); reverse (ans. begin(), ans. end()); }
Алгоритм Крускала O (M log N + N 2) Дан взвешенный неориентированный граф. Требуется найти такое поддерево этого графа, которое бы соединяло все его вершины, и при этом обладало наименьшим весом (т. е. суммой весов рёбер) из всех возможных. Такое поддерево называется минимальным остовным деревом или простом минимальным остовом. Алгоритм Крускала изначально помещает каждую вершину в своё дерево, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым ребром. Перед началом выполнения алгоритма, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а ребро добавляется к ответу. По окончании перебора всех рёбер все вершины окажутся принадлежащими одному поддереву, и ответ найден. int m; vector < pair < int, pair<int, int> > > g (m); // вес - вершина 1 - вершина 2 int cost = 0; vector < pair<int, int> > res; sort (g. begin(), g. end()); vector<int> tree_id (n); for (int i=0; i<n; ++i) tree_id[i] = i; for (int i=0; i<m; ++i) { int a = g[i]. second. first, b = g[i]. second, l = g[i]. first; if (tree_id[a] != tree_id[b]) { cost += l; res. push_back (make_pair (a, b)); int old_id = tree_id[b], new_id = tree_id[a]; for (int j=0; j<n; ++j) if (tree_id[j] == old_id) tree_id[j] = new_id; } }
Переборный алгоритм для раски Рассмотрим алгоритм решения задачи о раске, похожий на описанный выше алгоритм для задачи о независимом множестве. Сходство заключается в том, что задача для данного графа сводится к той же задаче для двух других графов. Поэтому снова возникает дерево вариантов, обход которого позволяет найти решение. Но есть и одно существенное различие, состоящее в том, что теперь два новых графа не будут подграфами исходного графа. Выберем в данном графе G две несмежные вершины x и y и построим два новых графа: G 1, получающийся добавлением ребра (x, y) к графу G, и G 2, получающийся из G слиянием вершин x и y. Операция слияния состоит в удалении вершин x и y и добавлении новой вершины z и ребер, соединяющих ее с каждой вершиной, с которой была смежна хотя бы одна из вершин x, y. Если в правильной раске графа G вершины x и y имеют разные цвета, то она будет правильной и для графа G 1. Если же цвета вершин x и y в раске графа G одинаковы, то граф G 2 можно раскрасить в то же число цветов: новая вершина z окрашивается в тот цвет, в который окрашены вершины x и y, а все остальные вершины сохраняют те цвета, которые они имели в графе G. И наоборот, раска каждого из графов G 1, G 2, очевидно, дает раску графа G в то же число цветов. Поэтому χ(G)=min{χ(G 1), χ(G 2)}, что дает возможность рекурсивного нахождения раски графа в минимальное число цветов. Заметим, что граф G 1 имеет столько же вершин, сколько исходный граф, но у него больше ребер. Поэтому рекурсия в конечном счете приводит к полным графам, для которых задача о раске решается тривиально.
Задача о максимальном потоке заключается в нахождении такого потока по транспортной сети, что сумма потоков из истока, или, что то же самое, сумма потоков в сток максимальна. В теории графов транспортная сеть — ориентированный граф G=(V, E) , в котором каждое ребро (u, v)€E имеет неотрицательную пропускную способность c(u, v)>=0 и поток f(u, v). Разрез графа в задачах о потоке — такая пара множеств вершин (S, T), что 1) SUT=V, где V — множество вершин графа 2) S T=0 3) s€S, t€T, где s — исток, t — сток. Величиной разреза называется сумма пропускных способностей таких рёбер (i, j), что i€S, j€T. Теорема Форда—Фалкерсона — величина максимального потока равна величине минимального разреза.
Алгоритм Форда-Фалкерсона 1) Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью. 2) В остаточной сети находим любой путь из источника в сток. Если такого пути нет, останавливаемся. 3) Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: 1) На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью Cmin. 2) Для каждого ребра на найденном пути увеличиваем поток на Cmin, а в противоположном ему — уменьшаем на Cmin. 3) Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. 4) Возвращаемся на шаг 2. Важно, что алгоритм не конкретизирует, какой именно путь мы ищем на шаге 2 или как мы это делаем. По этой причине алгоритм гарантированно сходится только для целых пропускных способностей, но даже для них при больших значениях пропускных способностей он может работать очень долго. Если пропускные способности вещественны, алгоритм может работать бесконечно долго, не сходясь к оптимальному решению
Алгоритм Эдмондса-Карпа O(N*M^2) 1) Обнуляем все потоки. Остаточная сеть изначально совпадает с исходной сетью. 2) В остаточной сети находим кратчайший путь из источника в сток. Если такого пути нет, останавливаемся. 3) Пускаем через найденный путь (он называется увеличивающим путём или увеличивающей цепью) максимально возможный поток: 1) На найденном пути в остаточной сети ищем ребро с минимальной пропускной способностью Cmin. 2) Для каждого ребра на найденном пути увеличиваем поток на Cmin, а в противоположном ему — уменьшаем на Cmin. 3) Модифицируем остаточную сеть. Для всех рёбер на найденном пути, а также для противоположных им рёбер, вычисляем новую пропускную способность. Если она стала ненулевой, добавляем ребро к остаточной сети, а если обнулилась, стираем его. 4) Возвращаемся на шаг 2. Чтобы найти кратчайший путь в графе, используем поиск в ширину: 1) Создаём очередь вершин О. Вначале О состоит из единственной вершины s. 2) Отмечаем вершину s как посещённую, без предка, а все остальные как непосещённые. 3) Пока очередь непуста, выполняем следующие шаги: 1) Удаляем первую в очереди вершину u. 2) Для всех рёбер (u, v), исходящих из вершины u, таких что вершина v ещё не посещена, выполняем следующие шаги: 1) Отмечаем вершину v как посещённую, с предком u. 2) Добавляем вершину v в конец очереди 3) Если v=t, выходим из обоих циклов: мы нашли кратчайший путь. 4) Если очередь пуста, возвращаем ответ, что пути нет вообще и останавливаемся. 5) Если нет, идём от t к s, каждый раз переходя к предку. Возвращаем путь в обратном порядке.
Проталкивание предпотока O(N^4) Общая схема алгоритма такова. На каждом шаге будем рассматривать некоторый предпоток - т. е. функцию, которая по свойствам напоминает поток, но не обязательно удовлетворяет закону сохранения потока. На каждом шаге будем пытаться применить какую-либо из двух операций: проталкивание потока или поднятие вершины. Если на каком-то шаге станет невозможно применить какую-либо из двух операций, то мы нашли требуемый поток. Для каждой вершины определена её высота Hu, причём HS = N, HT = 0, и для любого остаточного ребра (u, v) имеем Hu <= Hv + 1. Для каждой вершины (кроме S) можно определить её избыток: Eu = FV, u. Вершина с положительным избытком называется переполненной. Операция проталкивания Push (u, v) применима, если вершина u переполнена, остаточная пропускная способность Cfu, v > 0 и Hu = Hv + 1. Операция проталкивания заключается в максимальном увеличении потока из u в v, ограниченном избытком Eu и остаточной пропускной способностью Cfu, v. Операция поднятия Lift (u) поднимает переполненную вершину u на максимально допустимую высоту. Т. е. Hu = 1 + min { Hv }, где (u, v) - остаточное ребро. Осталось только рассмотреть инициализацию потока. Нужно инициализировать только следующие значения: FS, v = CS, v, Fu, S = - Cu, S, остальные значения положить равными нулю.
Алгоритм «Поднять-и-в-начало» O(V^3) Помимо предпотока и высот, алгоритм "поднять в начало" хранит список вершин L и указатель на один из его элементов (в терминах C++ - итератор) it. • Инициализация: • Инициализировать потоки и высоты, как в алгоритме проталкивания предпотока. • Если никаких вершин, кроме источника и стока, нет, остановиться; задача решена. • Построить списки соседей всех вершин и установить итераторы на начала списков. • Записать в L список всех вершин, кроме источника и стока, в произвольном порядке. • it указывает на начало списка. • Пока it указывает на какую-то вершину списка: • Выполнить разрядку вершины, на которую указывает it. • Если вершина в ходе разрядки изменила высоту, переставить в начало списка её и итератор it (так что он по-прежнему будет указывать на неё). • Продвинуть итератор it на одну позицию вперёд. • Разрядка вершины u выполняется следующим образом: Шаг 1. Пока вершина u переполнена, выполнять шаги 2 -4. Шаг 2. Если current вышел за конец списка, поднять вершину u и вернуть current в начало списка. Шаг 3. Иначе, если допустимо проталкивание от u к current[u], выполнить его. Шаг 4. Иначе продвинуть current на 1 элемент вперёд. Для каждой вершины v – указатель current[v] на один из элементов списка соседей
Стандартная библиотека шаблонов (STL) (Standard Template Library) — набор согласованных обобщённых алгоритмов, контейнеров, средств доступа к их содержимому и различных вспомогательных функций в C++ Структура библиотеки В библиотеке выделяют пять основных компонентов: • Контейнер (container) — хранение набора объектов в памяти (vector, list, deque-последовательные, set, multiset, map, multimap-ассоциативные, stack, queue, priority_queueадаптеры, bitset, basic_string, valarray-псевдоконтейнеры). • Итератор (iterator) — обеспечение средств доступа к содержимому контейнера. • Алгоритм (algorithm) — определение вычислительной процедуры. • Адаптер (adaptor) — адаптация компонентов для обеспечения различного интерфейса. • Функциональный объект (functor) — сокрытие функции в объекте для использования другими компонентами. Разделение позволяет уменьшить количество компонентов. Например, вместо написания отдельной функции поиска элемента для каждого типа контейнера обеспечивается единственная версия, которая работает с каждым из них, пока соблюдаются основные требования.
Максимальное паросочетание — это такое паросочетание, которое не содержится ни в каком другом паросочетании этого графа. Наибольшее паросочетание — это такое паросочетание, которое содержит максимальное количество ребер. Сетевой график — граф, который отражает работы проекта, связи между ними, состояния проекта. Может строиться в 2 -х вариантах (а) вершины графа отображают состояния некоторого объекта (например, строительства), а дуги — работы, ведущиеся на этом объекте. (б) вершины графа отражают работы, а связи между ними - зависимости между работами. Критический путь — путь, имеющий наибольшую продолжительность от исходного события до завершающего. Задача о потоке минимальной стоимости состоит в нахождении самого дешёвого способа передачи определённого количества потока через транспортную сеть.
Алгоритм Куна O(N*M) int n, k; vector < vector<int> > g; vector<int> mt; vector<char> used; bool try_kuhn (int v) { if (used[v]) return false; used[v] = true; for (size_t i=0; i<g[v]. size(); ++i) { int to = g[v][i]; if (mt[to] == -1 || try_kuhn (mt[to])) { mt[to] = v; return true; } } return false; } int main() {. . . чтение графа. . . mt. assign (k, -1); for (int v=0; v<n; ++v) { used. assign (n, false); try_kuhn (v); } for (int i=0; i<k; ++i) if (mt[i] != -1) printf ("%d %dn", mt[i]+1, i+1); }
Пузырек O(N^2) #define SWAP(A, B) { int t = A; A = B; B = t; } void bubblesort(int *a, int n){ int i, j; for (i = n - 1; i > 0; i--) { for (j = 0; j < i; j++) { if (a[j] > a[j + 1]) SWAP( a[j], a[j + 1] ); } } }
Быстрая сортировка O(n logn) int n, a[n]; //n - количество элементов void qs(int* s_arr, int first, int last) { int i = first, j = last, x = s_arr[(first + last) / 2]; do { while (s_arr[i] < x) i++; while (s_arr[j] > x) j--; if(i <= j) { if (i < j) swap(s_arr + i, s_arr + j); i++; j--; } } while (i <= j); if (i < last) qs(s_arr, i, last); if (first < j) qs(s_arr, first, j); }
Волновой алгоритм На двумерной клетчатой карте (матрице), состоящей из «проходимых» и «непроходимых» клеток, обозначена клетка старта и клетка финиша. Цель алгоритма — проложить кратчайший путь от клетки старта к клетке финиша, если это, конечно, возможно. От старта во все направления распространяется волна, причем каждая пройденная волной клетка помечается как «пройденная» . Волна, в свою очередь, не может проходить через клетки, помеченные как «пройденные» или «непроходимые» . Волна движется, пока не достигнет точки финиша или пока не останется непройденных клеток. Если волна прошла все доступные клетки, но так и не достигла клетки финиша, значит, путь от старта до финиша проложить невозможно. После достижения волной финиша, от финиша прокладывается путь до старта и сохраняется в массиве.
Поиск в ширину (BFS) O(n+m) В результате поиска в ширину находится путь кратчайшей длины в невзвешенном графе, т. е. путь, содержащий наименьшее число рёбер. vector < vector<int> > g; // граф int n; // число вершин int s; // стартовая вершина (вершины везде нумеруются с нуля) queue<int> q; q. push (s); vector<bool> used (n); vector<int> d (n), p (n); used[s] = true; p[s] = -1; while (!q. empty()) { int v = q. front(); q. pop(); for (size_t i=0; i<g[v]. size(); ++i) { int to = g[v][i]; if (!used[to]) { used[to] = true; q. push (to); d[to] = d[v] + 1; p[to] = v; } } }
Поиск в глубину (DFS) O(n+m) В результате поиска в глубину находится лексикографически первый путь в графе. vector < vector<int> > g; // граф int n; // число вершин vector<int> color; // цвет вершины (0, 1, или 2) vector<int> time_in, time_out; // "времена" захода и выхода из вершины int dfs_timer = 0; // "таймер" для определения времён void dfs (int v) { time_in[v] = dfs_timer++; color[v] = 1; for (vector<int>: : iterator i=g[v]. begin(); i!=g[v]. end(); ++i) if (color[*i] == 0) dfs (*i); color[v] = 2; time_out[v] = dfs_timer++; }
Шейкерная сортировка void cocktail_sort(vector<int> &mas) { int l = 0, r = mas. size()-1; while (l<=r) { // всплывает самый "легкий" элемент for (int i = r ; i>l; i--) if (mas[i-1]>mas[i]) swap(mas[i-1], mas[i]); l++; // тонет самый "тяжелый элемент" for (int i=l; i<r; i++) if (mas[i]>mas[i+1]) swap(mas[i], mas[i+1]); r--; } }
Сортировка выбором template <typename T, typename C = less< typename T: : value_type> > void select_sort( T f, T l, C c = C() ) { if (f!= l) { while (f != l - 1) { T min = f; for (T i = f + 1; i != l; ++i) { if (c(*i, *min)) { typename T: : value_type tmp = *min; *min = *i; *i = tmp; } } ++f; } } }
Сортировка вставками #include <algorithm> template< typename Iterator > void insertion_sort( Iterator first, Iterator last ) { for( Iterator i = first + 1; i < last; ++i ) for( Iterator j = i; first < j && *j < *(j - 1); --j ) std: : iter_swap( j - 1, j ); }
Сортировка деревом #include <set> #include <algorithm> #include <iterator> template <typename Iterator> void binary_tree_sort(Iterator begin, Iterator end) { std: : multiset<typename std: : iterator_traits<Iterator>: : value_type> tree(begin, end); std: : copy(tree. begin(), tree. end(), begin); };
Пирамидальная сортировка Сортировка пирамидой использует сортирующее дерево. Сортирующее дерево — это такое двоичное дерево, у которого выполнены условия: -Каждый лист имеет глубину либо d, либо d-1, d — максимальная глубина дерева. -Значение в любой вершине больше, чем значения её потомков. Удобная структура данных для сортирующего дерева — такой массив Array, что Array[1] — элемент в корне, а потомки элемента Array[i] — Array[2 i] и Array[2 i+1]. Алгоритм сортировки будет состоять из двух основных шагов: 1. Выстраиваем элементы массива в виде сортирующего дерева: Array[i]>=Array[2 i] Array[i]>=Array[2 i+1] при 1 <= i< n/2. Этот шаг требует O(n) операций. 2. Будем удалять элементы из корня по одному за раз и перестраивать дерево. То есть на первом шаге обмениваем Array[1] и. Array[n], преобразовываем Array[1], Array[2], … , Array[n-1] в сортирующее дерево. Затем переставляем Array[1] и Array[n-1], преобразовываем. Array[1], Array[2], … , Array[n-2] в сортирующее дерево. Процесс продолжается до тех пор, пока в сортирующем дереве не останется один элемент. Тогда. Array[1], Array[2], … , Array[n] — упорядоченная последовательность. Этот шаг требует O(n logn) операций.
Естественное слияние Шаг 1. Исходный файл f разбивается на два вспомогательных файла f 1 и f 2. Распределение происходит следующим образом: поочередно считываются записи ai исходной последовательности (неупорядоченной) таким образом, что если значения ключей соседних записей удовлетворяют условию f(ai)<=f(ai+1), то они записываются в первый вспомогательный файл f 1. Как только встречаются f(ai)>f(ai+1), то записи ai+1 копируются во второй вспомогательный файл f 2. Процедура повторяется до тех пор, пока все записи исходной последовательности не будут распределены по файлам. Шаг 2. Вспомогательные файлы f 1 и f 2 сливаются в файл f, при этом серии образуют упорядоченные последовательности. Шаг 3. Полученный файл f вновь обрабатывается, как указано в шагах 1 и 2. Шаг 4. Повторяя шаги, сливаем упорядоченные серии до тех пор, пока не будет упорядочен целиком весь файл. Символ "`" обозначает признак конца серии. Признаками конца сортировки естественным слиянием являются следующие условия: количество серий равно 1 (определяется на фазе слияния). при однофазной сортировке второй по счету вспомогательный файл после распределения серий остался пустым.
Простое слияние Шаг 1. Исходный файл f разбивается на два вспомогательных файла f 1 и f 2. Шаг 2. Вспомогательные файлы f 1 и f 2 сливаются в файл f, при этом одиночные элементы образуют упорядоченные пары. Шаг 3. Полученный файл f вновь обрабатывается, как указано в шагах 1 и 2. При этом упорядоченные пары переходят в упорядоченные четверки. Шаг 4. Повторяя шаги, сливаем четверки в восьмерки и т. д. , каждый раз удваивая длину слитых последовательностей до тех пор, пока не будет упорядочен целиком весь файл. После выполнения i проходов получаем два файла, состоящих из серий длины 2 i. Окончание процесса происходит при выполнении условия 2 i>=n. Следовательно, процесс сортировки простым слиянием требует порядка O(log n) проходов по данным. Признаками конца сортировки простым слиянием являются следующие условия: • длина серии не меньше количества элементов в файле (определяется после фазы слияния); • количество серий равно 1 (определяется на фазе слияния). • при однофазной сортировке второй по счету вспомогательный файл после распределения серий остался пустым.
Двухпутевым слиянием называется сортировка, в которой данные распределяются на два вспомогательных файла. Многопутевым слиянием называется сортировка, в которой данные распределяются на N (N > 2) вспомогательных файлов. Общий алгоритм сортировки слиянием Сначала серии распределяются на два или более вспомогательных файлов. Данное распределение идет поочередно: первая серия записывается в первый вспомогательный файл, вторая – во второй и так далее до последнего вспомогательного файла. Затем опять запись серии начинается в первый вспомогательный файл. После распределения всех серий, они объединяются в более длинные упорядоченные отрезки, то есть из каждого вспомогательного файла берется по одной серии, которые сливаются. Если в каком-то файле серия заканчивается, то переход к следующей серии не осуществляется. В зависимости от вида сортировки сформированная более длинная упорядоченная серия записывается либо в исходный файл, либо в один из вспомогательных файлов. После того как все серии из всех вспомогательных файлов объединены в новые серии, потом опять начинается их распределение. И так до тех пор, пока все данные не будут отсортированы. Выделим основные характеристики сортировки слиянием: • количество фаз в реализации сортировки; • количество вспомогательных файлов, на которые распределяются серии. Фаза – это действия по однократной обработке всей последовательности элементов. Двухфазная сортировка – это сортировка, в которой отдельно реализуется две фазы: распределение и слияние. Однофазная сортировка – это сортировка, в которой объединены фазы распределения и слияния в одну.
Сортировка Шелла /* Пример из книги Герберта Шилдта */ void shell(char *items, int count) { register int i, j, gap, k; char x, a[5]; a[0]=9; a[1]=5; a[2]=3; a[3]=2; a[4]=1; for(k=0; k < 5; k++) { gap = a[k]; for(i=gap; i < count; ++i) { x = items[i]; for(j=i-gap; (x < items[j]) && (j >= 0); j=j-gap) items[j+gap] = items[j]; items[j+gap] = x; } } }
• • • Шарниром в теории графов называется вершина графа, при удалении которой количество компонент связности возрастает. Для обозначения этого понятия также используются термины «разделяющая вершина» и «точка сочленения» . Определения Вершина v графа G называется шарниром, если подграф G 1, полученный из графа G удалением вершины v и всех инцидентных ей рёбер, состоит из большего количества компонент связности, чем исходный граф G. Граф, содержащий два шарнира и три блока. С понятием шарнира также связано понятие двусвязности. Связный граф, не содержащий шарниров, называется двусвязным. Максимальный двусвязный подграфа называется компонентой двусвязности. Компоненты двусвязности иногда называют блоками. Рёберным аналогом шарнира является мост. Мостом называется такое ребро графа, в результате удаления которого количество компонент связности в графе возрастает.
AISD_EXAM.pptx