Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Dopolnitelnye_voprosy.docx
Скачиваний:
7
Добавлен:
08.11.2019
Размер:
154.53 Кб
Скачать

Алгоритмы. Сведение алгоритмов. Сортировки и связанные с ними задачи.

Д.Кнут. Искусство программирования для ЭВМ. тт 1-3. Москва. Мир. 1996-1998

Т.Кормен, Ч.Лейзерсон, Р.Ривест. Алгоритмы. Построение и анализ. Москва. МЦНМО. 1999.

Препарата Ф., Шеймос М. Вычислительная геометрия. Москва. Мир. 1989

QuickSort.

Определение. Медианой множества А = {a1 ,…, aN } называется элемент с индексом (N+1)/2 в отсортированном по возрастанию множестве А.

Пусть, для определенности, мы сортируем массив вещественных чисел. Идея алгоритма заключается в следующем. Выберем некоторое число, желательно близкое, в каком-то смысле, к медиане сортируемого множества. Разобьем наше множество на две половины – в одной (левой половине) должны быть элементы меньше или равные выбранного элемента, а в другой (в правой половине) – больше или равные. Из построения этих подмножеств следует, что их расположение совпадает с их расположением в отсортированном множестве чисел (расположение – в смысле множеств), т.е. после сортировки элементы из этих подмножеств останутся на месте этих же подмножеств. Т.о., применив рекурсивно эту же процедуру для каждого из подмножеств, мы, в конечном итоге, получим отсортированное множество.

Для реализации этой идеи рассмотрим алгоритм, который не предполагает хранения медианы в отдельной ячейке памяти. В процессе работы алгоритма мы будем следить за тем, в какой ячейке памяти располагается элемент исходного множества, который мы выбрали в качестве медианы. Подобная реализация в реальности чуть более медленная, но доказательство работы алгоритма намного проще, чем в случае хранения медианы в отдельной ячейке.

В следующей реализации в комментариях показаны соотношения на значения элементов, которые выполняются после каждого шага алгоритма. Эти соотношения доказывают, что каждый раз массив разбивается на части, левая из которых не превосходит медианы, а правая – не меньше медианы. Здесь для простоты множество элементов { A s , A s+1 , … , A t } будем обозначать {s,t}. Медиану будем обозначать M.

QuickSort(A,p,q)

Если q-p < 1 то ВЫЙТИ

Вечный цикл

i=p; j=q; // пусть M=A j

//цикл 1:

Пока Ai < A j : i + +;

//{p,i-1}<=M<={j,q}, Ai>=M

поменять местами A i и A j ;//x -> Ai

//{p,i}<=M<={j,q}

j --;

//{p,i}<=M<={j+1,q}

Если i >= j то

//либо i==j то {p, j}<=M<={ j+1,q}

// либо i==j+1 то M== Aj+1 => {p, j}<=M<={ j+1,q}

{ QuickSort(A, p, j ); QuickSort(A, j+1, q );ВЫЙТИ }

//цикл 2:

Пока A j > Ai : j - -;

//{p,i}<=M<={j+1,q}, A j<=M

поменять местами A i и A j ;//x -> A j

//{p,i}<=M<={j,q}

i + +;

//{p,i-1}<=M<={j,q}

Если i >= j то

//либо i==j то M== Aj => {p, j}<=M<={ j+1,q}

// либо i==j+1 то {p, j}<=M<={ j+1,q}

{ QuickSort(A, p, j ); QuickSort(A, j+1, q );ВЫЙТИ }

Конец вечного цикла

В силу построения алгоритма j не может стать меньше 0 и не может быть больше или равным q, поэтому гарантируется, что мы не попадем в бесконечную рекурсию и границы рассмотрения массива корректны.

Отметим, что после первого цикла также имеем:

Если i >= j то

//либо i==j то {p, i}<=M<={ i+1,q}

// либо i==j+1 то M== Aj+1 => {p, i}<=M<={ i+1,q}

т.е. рекурсию можно было бы организовать в виде:

{ QuickSort(A, p, i ); QuickSort(A, i+1, q );ВЫЙТИ }

но в этом случае мы можем попасть в бесконечную рекурсию, т.к. в цикле i может дойти вплоть до q.

После второго цикла также имеем:

Если i >= j то

//либо i==j то {p, i-1}<=M<={ i,q}

// либо i==j+1 то {p, i-1}<=M<={ i,q}

т.е. рекурсию можно было бы организовать в виде:

{ QuickSort(A, p, i-1 ); QuickSort(A, i, q );ВЫЙТИ }

В этом случае i не может стать меньше 1 и не может быть больше q, поэтому такой вариант алгоритма также возможен.

--------------------------------------------------------------------------------

В более стандартной реализацией основной идеи данного алгоритма выбирается произвольный элемент x сортируемого множества в качестве среднего элемента и помещается в отдельную ячейку памяти. Далее, один шаг алгоритма заключается в следующем. Мы двигаемся слева направо, пока элементы множества меньше x. Затем мы движемся справа налево, пока элементы множества больше x. Если мы еще не встретились, то следует поменять местами элементы, на которых мы стоим при движении в каждую сторону и повторить шаг алгоритма. Если же мы встретились, то алгоритм вызывается для каждой из полученных половин множества.

QuickSort(A,p,q)

Если q-p < 1 то ВЫЙТИ

Вечный цикл

i=p; j=q; x=Ai

Пока A i < x : i + +;//{p,i-1}<=x, Ai >=x

Пока A j > x : j - -;//{j+1,q}>=x, Aj <=x

Если i < j то

поменять местами A i и A j ; //{p,i}<=x, {j,q}>=x

иначе

{

//либо i==j то Ai ==x => {p,j}<=x, {j+1,q}>=x

//либо i==j+1 то {p,j}<=x, {j+1,q}>=x

QuickSort(A, p, j ); QuickSort(A, j+1, q );ВЫЙТИ

}

i + +; j - -;//{p,i-1}<=x, {j+1,q}>=x

Конец вечного цикла

Замечание 1. При работе алгоритм индексы массива i и j никогда не выйдут за его границы p и q.

Замечание 2. В алгоритме никогда не произойдет вечной рекурсии, т.е. при рекурсивном вызове

p £ j < q

Замечание 3. Алгоритм гарантирует корректное разбиение массива, т.е. после разбиения массива выполняются соотношения

Ak £ x для всех k: p £ k £ j

Al ³ x для всех k: j+1 £ k £ qj

Тонкость алгоритма характеризует следующее наблюдение. Давайте попробуем ``обезопасить алгоритм’’ и включим обеспечение условия i £ j в циклы алгоритма Пока… . Т.е. приведем алгоритм к следующему виду:

QuickSort*(A,p,q)

Если q-p £ 1 то ВЫЙТИ

Вечный цикл

i=p; j=q; x=Ai

Пока A i < x и i < j : i + +;

Пока A j > x и i < j : j - -;

Если i < j то

поменять местами A i и A j ;

иначе

{ QuickSort(A, p, j ); QuickSort(A, j+1, q );ВЫЙТИ }

i + +; j - -;

Конец вечного цикла

Алгоритм QuickSort* оказывается неверным !!! Это легко увидеть на простейшем примере: {3,4,2,5}.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]