Опубликован: 12.02.2014 | Доступ: свободный | Студентов: 924 / 239 | Длительность: 11:22:00
Специальности: Программист
Лекция 7:

Списки. Предикаты высших порядков

< Лекция 6 || Лекция 7: 1234 || Лекция 8 >

7.4. Алгоритмы сортировки списка

Ниже реализуются три хорошо известных способа сортировки списка — сортировка вставками, быстрая сортировка и сортировка слиянием. Списки упорядочиваются по возрастанию элементов. Первые два алгоритма имеют сложность O(n^2), последний алгоритм — сложность O(n\log n).

Сортировка вставками заключается в постепенном формировании упорядоченного списка из элементов исходного списка. Изначально формируемый список полагается равным пустому списку. На каждом шаге алгоритма берется очередной элемент списка (его голова) и вставляется в формируемый список так, чтобы он оставался упорядоченным.

class predicates
    insertionSort: (A* List) -> A* SortedList.
    sort: (A* List, A* SortedList) -> A* SortedList.
    insert: (A, A* SortedList) -> A* SortedList.
clauses
    insertionSort(L) = sort(L, []).

    sort([H | T], L) = sort(T, insert(H, L)).
    sort([], L) = L.

    insert(A, [B | L]) = [B | insert(A, L)]:-
        B < A,
        !.
    insert(A, L) = [A | L].

    run():-
        R = upperBound(integer),
        L = [math::random(R) || _ = std::fromTo(1, 20)],
        write(L, "\n", insertionSort(L)),
        _ = readLine().
Пример 7.5. Сортировка вставками

Предикат upperBound/1 возвращает элемент, являющийся верхней границей числового домена.

Алгоритм быстрой сортировки постепенно формирует упорядоченный суффикс S списка, который изначально полагается пустым. На каждом шаге алгоритма берется очередной элемент списка — его голова X, и хвост списка разбивается на список L1 элементов, меньших головы, и на список L2 остальных элементов. Далее алгоритм рекурсивно применяется к списку L2 и списку S, получается упорядоченный список S1. После этого алгоритм применяется к списку L1 и списку [X | S1] (заметим, что последний список упорядочен и что он является суффиксом исходного списка). В результате получается упорядоченный список.

class predicates
    quickSort: (A* List) -> A* SortedList.
    sort: (A* List, A* SortedSuffix) -> A* SortedList.
    split: (A H, A*, A* LessThanH [out], A* GreaterThanH [out]).
clauses
    quickSort(L) = sort(L, []).

    sort([H | T], L) = sort(L1, [H | sort(L2, L)]):-
        split(H, T, L1, L2).
    sort([], L) = L.

    split(H, [A | L], [A | L1], L2):-
        A < H,
        !,
        split(H, L, L1, L2).
    split(H, [A | L], L1, [A | L2]):-
        split(H, L, L1, L2).
    split(_, [], [], []).

    run():-
        L = [math::random(20) || _ = std::fromTo(1, 20)],
        write(L, "\n", quickSort(L)),
        _ = readLine().
Пример 7.6. Быстрая сортировка

Предикат split можно заменить предикатом filter/4 класса list, определив предикат sort/2 следующим образом:

sort([H | T], L) = sort(L1, [H | sort(L2, L)]):-
    list::filter(L, {(X):- X < H}, L1, L2),
    !.
sort(_, L) = L.

Нетрудно заметить, что если исходный список упорядочен, то разбиение хвоста на два списка относительно его головы, не приносит эффекта (см. листинг 7.6). Этого недостатка лишен алгоритм слияния.

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

class predicates
    mergeSort: (A* List) -> A* SortedList.
    split: (A*, A* List1 [out], A* List2 [out]).
    merge: (A* SortedList1, A* SortedList2) -> A* SortedList.
clauses
    mergeSort([]) = []:- !.
    mergeSort([A]) = [A]:- !.
    mergeSort(L) = merge(mergeSort(L1), mergeSort(L2)):-
        split(L, L1, L2).

    split([A, B | L], [A | L1], [B | L2]):- !,
        split(L, L1, L2).
    split(L, L, []).

    merge([], L) = L:- !.
    merge(L, []) = L:- !.
    merge([A | L1], [B | L2]) = [A | merge(L1, [B | L2])]:-
        A < B,
        !.
    merge(L1, [B | L2]) = [B | merge(L1, L2)].

    run():-
        L = [math::random(20) || _ = std::fromTo(1, 20)],
        write(L, "\n", mergeSort(L)),
        _ = readLine().
Пример 7.7. Сортировка слиянием

Упражнение 3. Напишите такой вариант сортировки

  • вставками;
  • быстрой;
  • слиянием,

чтобы одновременно с сортировкой из списка удалялись дубликаты элементов.

< Лекция 6 || Лекция 7: 1234 || Лекция 8 >
Жаныл Айкын
Жаныл Айкын
Rustam Inatov
Rustam Inatov

Доброго времени суток, подскажите пожалуйста, visual prolog examples, pie, vip7.5 - это все, где я могу скачать? (в смысле) может быть на сайте есть какой-то архив? Увы я не нашел его.

Подскажите, пожалуйста.

С уважением, Рустам.