Сортировки
Еще одна часто встречающаяся задача при программировании — сортировка данных. Все существующие алгоритмы сортировки можно разделить на сортировки перестановкой, в которых на каждом шаге алгоритма меняется местами пара чисел; сортировки выбором, в которых на каждом шаге выбирается наименьший элемент и дописывается в отсортированный массив; и сортировки вставлением, в которых элементы массива рассматривают последовательно и каждый вставляют на подходящее место в отсортированном массиве. Самая простая сортировка перестановкой — пузырьковая, в которой более легкие элементы «всплывают» к началу массива. Сначала второй элемент сравнивается с первым и, если нужно, меняется с ним местами. Затем третий элемент сравнивается со вторым и только в том случае, когда они переставляются, сравнивается с первым, и т.д. Этот алгоритм также является и самой медленной сортировкой — в худшем случае для сортировки массива N чисел потребуется N2/2 сравнений и перестановок, а в среднем — N2/4.
; Процедура bubble_sort ; сортирует массив слов методом пузырьковой сортировки ; ввод: DS:DI = адрес массива ; DX = размер массива (в словах) bubble_sort proc near pusha cld cmp dx,1 jbe sort_exit ; выйти, если сортировать нечего dec dx sb_loop1: mov cx,dx ; установить длину цикла xor bx,bx ; BX будет флагом обмена mov si,di ; SI будет указателем на ; текущий элемент sn_loop2: lodsw ; прочитать следующее слово cmp ax,word ptr [si] jbe no_swap ; если элементы не ; в порядке, xchg ax,word ptr [si] ; поменять их местами mov word ptr [si-2],ax inc bx ; и установить флаг в 1, no_swap: loop sn_loop2 cmp bx,0 ; если сортировка не закончилась, jne sn_loop1 ; перейти к следующему элементу sort_exit: popa ret bubble_sort endp
Пузырьковая сортировка осуществляется так медленно потому, что сравнения выполняются лишь между соседними элементами. Чтобы получить более быстрый метод сортировки перестановкой, следует выполнять сравнение и перестановку элементов, отстоящих далеко друг от друга. На этой идее основан алгоритм, который называется «быстрая сортировка». Он работает следующим образом: делается предположение, что первый элемент является средним по отношению к остальным. На основе такого предположения все элементы разбиваются на две группы — больше и меньше предполагаемого среднего. Затем обе группы отдельно сортируются таким же методом. В худшем случае быстрая сортировка массива из N элементов требует N2 операций, но в среднем случае — только 2n*log2n сравнений и еще меньшее число перестановок.
; Процедура quick_sort ; сортирует массив слов методом быстрой сортировки ; ввод: DS:BX = адрес массива ; DX = число элементов массива quicksort proc near cmp dx,1 ; Если число элементов 1 или 0, jle qsort_done ; то сортировка уже закончилась xor di,di ; индекс для просмотра сверху (DI = 0) mov si,dx ; индекс для просмотра снизу (SI = DX) dec si ; SI = DX-1, так как элементы нумеруются с нуля, shl si,1 ; и умножить на 2, так как это массив слов mov ax,word ptr [bx] ; AX = элемент X1, объявленный средним step_2: ; просмотр массива снизу, пока не встретится ; элемент, меньший или равный Х1
cmp word ptr [bx][si],ax ; сравнить XDI и Х1
jle step_3 ; если XSI больше, sub si,2 ; перейти к следующему снизу элементу jmp short step_2 ; и продолжить просмотр step_3: ; просмотр массива сверху, пока не встретится ; элемент меньше Х1 или оба просмотра не придут ; в одну точку cmp si,di ; если просмотры встретились, je step_5 ; перейти к шагу 5, add di,2 ; иначе: перейти ; к следующему сверху элементу, cmp word ptr [bx][di],ax ; если он меньше Х1, jl step_3 ; продолжить шаг 3 steр_4: ; DI указывает на элемент, который не должен быть ; в верхней части, SI указывает на элемент, ; который не должен быть в нижней. Поменять их местами mov cx,word ptr [bx][di] ; CX = XDI
xchg cx,word ptr [bx][si] ; CX = XSI, XSI = XDI
mov word ptr [bx][di],cx ; XDI = CX jmp short step_2 step_5: ; Просмотры встретились. Все элементы в нижней ; группе больше X1, все элементы в верхней группе ; и текущий - меньше или равны Х1 Осталось ; поменять местами Х1 и текущий элемент: xchg ах,word ptr [bx][di] ; АХ = XDI, XDI = X1
mov word ptr [bx],ax ; X1 = AX ; теперь можно отсортировать каждую из полученных групп push dx push di push bx
mov dx,di ; длина массива X1...XDI-1
shr dx,1 ; в DX call quick_sort ; сортировка
pop bx pop di pop dx
add bx,di ; начало массива XDI+1...XN
add bx,2 ; в BX shr di,1 ; длина массива XDI+1...XN
inc di sub dx,di ; в DX call quicksort ; сортировка qsort_done: ret quicksort endp
Кроме того, что быстрая сортировка — самый известный пример алгоритма, использующего рекурсию, то есть вызывающего самого себя, это еще и самая быстрая из сортировок «на месте», то есть сортировка, использующая только ту память, в которой хранятся элементы сортируемого массива. Можно доказать, что сортировку нельзя выполнить быстрее, чем за n*log2n операций, ни в худшем, ни в среднем случаях, и быстрая сортировка достаточно хорошо приближается к этому пределу в среднем случае. Сортировки, достигающие теоретического предела, тоже существуют — это сортировки турнирным выбором и сортировки вставлением в сбалансированные деревья, но для их работы требуется резервирование дополнительной памяти, так что, например, работа со сбалансированными деревьями будет происходить медленно из-за дополнительных затрат на поддержку сложных структур данных в памяти.
Рассмотрим в качестве примера самый простой вариант сортировки вставлением, использующий линейный поиск и затрачивающий порядка n2/2 операций. Ее так же просто реализовать, как и пузырьковую сортировку, и она тоже имеет возможность выполняться «на месте». Кроме того, из-за высокой оптимальности кода этой процедуры она может оказываться даже быстрее рассмотренной нами «быстрой» сортировки на подходящих массивах.
; Процедура linear_selection_sort ; сортирует массив слов методом сортировки линейным выбором ; Ввод: DS:SI (и ES:SI) = адрес массива ; DX = число элементов в массиве
do_swap: lea bx,word ptr [di-2] mov ax, word ptr [bx] ; новое минимальное число dec cx ; если поиск минимального закончился, jcxz tail ; перейти к концу loop1: scasw ; сравнить минимальное в АХ ; со следующим элементом массива ja do_swap ; если найденный элемент ; еще меньше - выбрать ; его как минимальный loop loop1 ; продолжить сравнения ; с минимальным в АХ tail:. xchg ax,word ptr [si-2] ; обменять минимальный элемент mov word ptr [bx],ax ; с элементом, находящимся в начале ; массива linear_selection_sort proc near ; точка входа в процедуру mov bx,si ; BX содержит адрес ; минимального элемента lodsw ; пусть элемент, адрес ; которого был в SI, минимальный, mov di,si ; DI - адрес элемента, сравниваемого ; с минимальным dec dx ; надо проверить DX-1 элементов массива mov cx,dx jg loop1 ; переход на проверку, если DX > 1 ret linear_selection_sort endp
Содержание раздела