Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

информатика.Методы сортировок

.pdf
Скачиваний:
10
Добавлен:
04.06.2015
Размер:
2.25 Mб
Скачать

а) Проход1: б) Проход2:

Исходный массив:

РИСУНОК 5. Первые два прохода при сортировке массива. состоящего из пяти целых чисел, методом пузырька: а) первый проход; б) второй проход

На рис. 5, а показаны результаты первого прохода алгоритма

сортировки методом пузырька на примере массива, содержащего пять целых

чисел. Сначала сравниваются между собой элементы первой пары - числа 29 и 10. Они нарушают заданный порядок, поэтому их меняют местами. Затем сравниваются элементы второй пары - числа 29 и 14, поэтому их также меняют местами. После этого сравниваются элементы третьей пары ­ числа 29 и 37. Они не нарушают установленный порядок, поэтому остаются на своих местах. В заключение меняются местами элементы последней пары - числа 37 и 13.

Хотя после первого прохода массив остается неупорядоченным, наибольший элемент оказывается в конце массива, "всплывая", как пузырек на поверхность воды. Во время второго прохода нужно вернуться к началу массива и обработать его точно так же, как и в первый раз, останавливая обработку на предпоследнем элементе. Таким образом, при втором проходе просматриваются n-l элемент массива. После второго прохода второй наибольшийэлемент окажется на предпоследнемместе, как показано на рис. 5, б. Теперь, игнорируя два последних элемента, которые уже поставлены в нужном порядке, следует продолжить обработку массива, пока он весь не будет упорядочен.

Несмотря на то что алгоритм сортировки методом пузырька состоит из n-l прохода, в некоторых случаях удается обойтись меньшим количеством шагов. Таким образом, процесс можно прекратить, если в ходе проверки не выполнено ни одной перестановки, В приведенной ниже функции bubbleSort, написанной на языке С++, дЛЯ сигнализации о перестановке используется булева переменная sorted. Функция bubbleSort использует функцию swap, описаннуюранее.

\'oid buhh 1е/)()/'''(Пала Ту1)(; /11(;/~1 ккау1_1. j /1/ 11)

//--------------------------------------------------------­

20

falA.'ie.· //

// Упорядочиеает элементымассиеа в воэрастающсм

порядке.

 

// Предусловие:

масснв tllе~,41/"Г'а~\' состоит 1/3 /1 элементов.

// Постусловие:

массив fJle..:41/'r(~yупорядочен11()

во3.раст(,11-/1,' Т() ;

 

// ч исло /1 остается без изменения. 1/ Вызываемая функция: .S'}1lt11J.

//--------------------------------------------------------­

{

Ьооl хопеа -== false; // Если выполняетсн перестаковка.

// принимаетзначениекнзею»нп! ракк = 1: (ТJ{lSS <.. п} && 'копеи;

-+" +1J(1..чн)

r

l

//Инвариант:массивtll€i4,"rоР/J-z+I-/Jl.[s,\'..п-Г] упорядочен.

//(1 его раз..мер больше размера массива lhe;-l,',<{гу{О ..п-ракз]

зопег! :::::: true,- // Массивупорядочен

.

.for (;111 таех :::::: О; таех <:: п-разк; +' таех)

 

/

(

// J[нвариант: размер массива IhеL"1Гllf(l):j// . indi!x-lj ,// не превышает размера массива 11-1С)l,"jtOо}У'Тпаех.

ии пехнпаех =- index -+ /,' !f>(thе~4гга}!{ill{lе.\, :> f/l(!А4г,.а.\/{nеХfl-"1(lе~);])

{

// Ilереставляем элементЬ/

.\');1 J(;[j } ( t-hej'l rl"(I}'{,il'1d(?~Y}. 'h(!",/1 r}'(/)'[ пехлГпаех]') .,.

S()//4tcli :::::::: Приэнак персстановки

I} (:/ K()llel-( ()ператора if'

}/,1 f{()HC11 ()}7С!){1т()!){],!()//4

11Диагностическое утверждение: размер массива // theArr(lJ,'/().. п-раь:~;,s-Il- меньше !)t;13J\4(?/.)(/ J"1(1(~(:1~(---)a/~/

t heA /""ra.y / п-разз1

/1/ K{)fICl/ оператора .!()r

}//Z<'""()/lel.j функции bllhlJleS()rl

Анализ. Как указывалось выше, количество проходов при исполь.зовании метода пузырька не превышает n-l. При первом проходе выполняется n-l сравнением не больше n-l перестановок. При втором проходе выполняется n-2 сравнения и не больше n-2 перестановок. В общем, при l-M проходе выполняется n-l сравнений и не больше n-l перестановок. Следовательно, в худшем случае при сортировке методом пузырькабудет выполнено

(n-I)+(n-2)+ ...+1 == n*(n-I)/2

сравнений и столько же перестановок. Напомним, что при каждой перестановке выполняется три присваивания. Таким образом, общее количествоосновныхоперацийв худшем случае равно

2* n* (n-l) = 2 * n2 - 2*n.

Следовательно, в худшем случае сложность алгоритм сортировки

методом пузырькаравнао(n2).

Лучшим считается вариант, когда исходные данные уже упорядочены. В этом случае алгоритм сортировки методом пузырька сделает только один проход, выполнивn-l сравнений,и ни одной перестановки.

21

Сортировка методом вставок

Представьте себе колоду карт, из которой каждый раз вынимается и вставляется на указанное место одна карта. Такой способ упорядочения называется сортировкой методом вставок (insertion sort).

В данном случае массив делится на две части: упорядоченную и неупорядоченную, как показано на рис. 6.

Вначале весь массив неупорядочен. На каждом шаге метода вставок из неупорядоченной части извлекается первый элемент, который затем вставляется в нужное место упорядоченной части. Первый шаг тривиален: переместить элемент theArray{O} из неупорядоченной части в упорядоченную. Для этого даже не нужно переставлять элементы массива. Следовательно, этот шаг можно пропустить, считая, что элемент theArray[O} уже принадлежит упорядоченной части, анеупорядоченной частью массива является отрезок theArray[l ... n-l}. Тот факт, что элементы в упорядоченной части расположены в порядке возрастания, является инвариантом алгоритма. Поскольку на каждом шаге размер упорядоченной части увеличивается на единицу, а размер неупорядоченной части,

соответственно, на единицу уменьшается, в момент окончания алгоритма

весь массив окажется упорядоченным.

[I] ... =о

Упорядоченнаячасть

...[I]

Неупорядоченнаячасть

 

 

л--_

~_----~----_'" r_---

 

п-l

После i итераций

 

РИСУНОК 6. Сортировка методом вставок

разбивает массив на две части

На рис. 7 показаны результаты сортировки массива, состоящего из пяти целых чисел, методом вставок. В исходном положении упорядоченная часть массива состоит из единственного элемента theArray[O}, равного 29, а

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

Извлечем из неупорядоченной части массива ее первый элемент - число 1О - и вставим в соответствующее место упорядоченной части. Для этого понадобится сдвинуть элементы массива, чтобы освободить место для

вставляемого

числа.

Снова

извлечем

из

вновь

образованной

неупорядоченной части массива ее первый элемент -

число 14 - и вставим

в соответствующее место упорядоченной части и т.д.

22

Исходный

 

массив:

Скопировать 1О

 

 

Сдвинуть 29

 

Вставить 10;Скопировать 14

 

Сдвинуть 29

 

Вставить 14;Скопировать 37, оставить

 

37 на месте

 

Скопировать 13

 

Сдвинуть 37,29, 14

Упорядоченный

 

массив:

Вставить 13

Рисунок 7. Сортировка массива,

состоящего из пяти целых чисел, методо..и вставок

Рассмотрим функцию на языке С++, выполняющую сортировку

массива, состоящего из n элементов, методом вставок.

void il·1/j'erti()flS(),.{(DatcIT.J.pe thl::/l,"r{I)'/j, [п! 1'1)

//--------------------------------------------------------­

//Упорядочиваетэлементымассивав возраспшющемпорядке.

//Предусловие:массивthеАгrа)'состоитиз п элементоа.

//Постусловие:массивlhe.4rJf(lJ/ упорядочен по возрастанию:

//число п остаетсябез изменения.

/ / ---------------------------------------------------------­

/

I

// ипкопеа:::::: индекс первого элемента неупорядоченной части; //70С = индекс ячейки упорядоченной части. в которую

//производитсявставка;

//пехшет == следующий элемент псупорядоченпои части.

//13 исходном положении упорядоченная часть состоит

//единственногоэлементаthе-,L11;4ГС~1/i(~l,

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

//Ihе~4ГГ{IJ}[I..n-l/. В обще..н случае упорядоченной частью //массива является отрезок tJle~4 пау]')..ипзопеа-Г],

// а неупорядоченной отрезокthe./l1J']4(1)'[llJlsorle(/. n-IJ

/or (int ипзопеа ~ 1: ипзопеа «: п. --т---+-unsО,..lеd)

23

f l

Находим индекс нужной ячейки (1ос) в отрезке

//tll(!,,4гrо.у[О..un..зопеа] для вставки Э.7С..мента

//the/lr,.a}'[unsorledj. являющегося первым элементом

//пеупорядоченнойчасти; при необходимостивыполняем

//сдвиг элементов. освобождаяместо для вставки.

Еляа'Гурепехшет -= f}le~4"/Y1))/unso,.tedl~" int /ос = ипзопеа:

[о« (:(loc >- О) && (111е.Агrll):[юс-!J~> пехшет) :--lo(~

// (7()(114-~?(Je)\4 отрезок tlll!/lГГ(lJ'[!()('-lJ// вправо tllеАГ"СlуI1()(~./-== tJlcA J'*гау!1()с~-Г],

/гДиигностическоеутверждение: llчеiiка'//tI1еАГ1·(,))[I()с~.1 содержит элемент пехлИет

// Вставляем э.не./\'1е:i т пе.Хt 1t е111

J

11

Z~

-{.'

/'

//

n(JflCZf 1(ик...па J()r

Анализ. Внешний цикл в функции insertionSort выполняется n-l раз. Этот цикл содержит внутренний цикл, который выполняетсяне больше чем unsorted раз для значений переменной unsorted, изменяющихсяот 1 до n-l. Таким образом, в худшем случае алгоритм выполняет

1+ 2+ ... + (n-l) == n * (n-l )/2 сравнений. Кроме того, в худшем случае столько же раз внутренний цикл сдвигает элементы.

Во внешнем цикле перемещение элементов на каждой итерации выполняется дважды, т.е. в сумме 2 *(n-l) раз. Итак, подведем итог: в

наихудшем варианте выполняется.

n * (n-l) + 2 * (n-l) == n2 + n ­ 2

основных операций.

Следовательно, в худшем случае сложность алгоритма сортировки

~

методом вставок равна О(n"). Для небольших массивов, скажем, содержащих не более 25 элементов, алгоритм сортировки методом вставки предпочтительнее, поскольку он понятнее остальных алгоритмов. Однако для больших массивов этот метод совершенно неэффективен.

Сортировка слиянием

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

24

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

theArray{firat ... last}.

Алгоритм сортировки методом слияний является рекурсивным. Его эффективность не зависит от порядка следования элементов в исходном массиве. Допустим, что мы разделили массив пополам, рекурсивно упорядочили обе половины, а затем объединили их в одно целое, как показано на рис. 8. На рисунке показано, что части массива <1, 4, 8> и <2, 3> объединяются в массив <1, 2, 3, 4, 8>. В ходе слияния элементы,

стоящие в разных частях массива, попарно сравниваются друг с другом, и

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

theArray:

Исходный массив

' ---- _ ---- ' --- _ ------ ' -__....

L .. -- _ ---- ' -- _ ------ '

Делим массив пополам

 

 

' -------

7 ---

' ---------

' Упорядочиваемполовинымассива

а

 

 

 

 

 

 

 

 

Объединяем половины

 

 

 

а)] <2. поэтому копируем ] из левой

 

 

 

половины в массив theArray

 

 

 

б)4>2.по')'гому копируем 2 из

 

 

 

правой половины в массив тпсАггау

 

 

 

в)4>3,поэтому копируем 3 из

 

 

 

правой половины в массив theArray

 

 

 

г)Правая половина исчерпана.

 

 

 

остаток левой половины копируем

.---_--.----L-----,-_=-------

.-----

__тА--__'\=--,в массив tempArray

Временный массив тегпрАггау

Копируем содержимое

временного массива

назад в исходный массив

theArray:

РИСУНОК 8. Сортировка методом слияния с помощью вспомагательного массива

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

остается неясным, как выполняется сортировка на предыдущих этапах.

Сортировка слиянием выполняется рекурсивно. Ее псевдокод имеет следующий вид.

merge..чопитош theArrcly.·lte}n./:l}·l·(l.~'.

in ./zrsl: i11te~er, i71 Ган), il1Iep;er) ,// Упорндочиеает

отрезок

1/ tJ1с1'4}··ГСl..-V [fir.\'t.. Га..\'I),

25

//Z) сортируя первую половину массива:

//2) сортируя 61'n()p)'1() половину, массива:

//_1) объединяя две упорядоченные

//11'10J/OBU1-1 ы массива.

ij- (!lГSl

<~ I(Jst)

 

 

 

 

 

 

(

 

 

 

 

 

 

 

 

l

(fiг~,;'t +

 

 

 

 

 

 

1111(:! ==

las~),/2

 

 

 

 

// Определяем середину,

 

 

 

 

,// Сортируем отрезок /1 тt пеАз-па,'[!Z}'"5;t.. тilij

 

тerge.s'()rt(tI1C/4 кпау,

.fl1~,,'t,

тла) /~/ Сортируем (JI'1'11)(!3()K

th сАг}"'(1)'i(1-+- 1.. 1с's') тf?Г<-«еsоГ't (!пс.!,..,--(J) i,

П1i(I +

1.

[ая«)

 

 

 

 

 

///11(;./1 }'"I'"(J},[111'"8{.. П'1i{}J н

,// Объед1111>1 е./\;l упоря до чен1-[ ые

отрез!{11

1heA rr(l~~'"["т1(J-t- J.. /(1 S 1) n1elAf{e(the/11"·(I)J,

.llr.\'I,

ти],

/CI8~)

} // Конец оператора j!~

 

 

 

 

 

/'l Е(~.ЛиПг»t

> ~=

Га»1,

операции завериссны

 

 

Совершенно

ясно,

что

основные операции этого алгоритма

выполняются на этапе слияния, и все же, почему в результате возникает

упорядоченный массив? Рекурсивные вызовы продолжают разделять части

массива пополам, пока в них не останется только по одному элементу.

Очевидно, что массив, состоящий из одного элемента, является упорядоченным. Затем алгоритм объединяет фрагменты массива, пока не образуется один упорядоченный массив. Рекурсивные вызовы функции mergesort и результаты слияний проиллюстрированы на рис. 9 на

примере массива, состоящего из шести целых чисел.

Рекурсивные вызовы

I

I

I

I

\

/

I

I

/

 

 

\

/

I

 

 

~

/

 

 

 

<,

I

 

 

 

<, <,

I

 

 

 

..............

I

 

 

~

<,

<,

<,

Слияние

 

 

<,

 

 

 

 

<,

РИСУНОК 9. Сортировка методом СЛИЯНИЯ массива, состоящего из шести целых чисел

26

Ниже приведена функция на языке С++, реализующая алгоритм сортировки методом слияний. Для того чтобы упорядочить массив theArray, состоящий из n-l элементов, выполняется рекурсивный вызов mergesort

(theArray, О, n-l).

const int М'А): SlZE =

JW(IК(~li.Л1(IJlыt()t:!-/{·()Jllл-че(~гrl()()-.ЭJlе.Л1е11111()(~-)W(I(~С~ll(j(J;

void meT"cRe([)alaT))!Je

tI7e..:·4r""Cf))/·../, int .fir,st, i"t тла, int last)

,// Обьединяет два упорядоченных отрезка Ille!Jl·~J'{J)iflтos't..111i(i] и />' thеАr,.а~У[Jпid·+-1 .. Та.«] в один упорядоченный массив.

// Предусловие:jirst ~<~ == 111iJ <~::::::. Так: Оба подмассива

/1 ,1'1e./1rra.\;'[/i,4.\,t..пиа] и the./4J,,"clJJ[nlid+·j> .lаs~1.')fl0!)ядочеНbl

// по возрастанию.

1/Постусловие: отрезок t-}1е~'1гrа.1)[first../(15;1] упорядочен.

//Замечаниео реализации:функциявыполинетслияниедв}'х

//подмассивовво временныймассие. а ](l1}и!.1-1 копируетего

//содержимоев исходный ..массив fJ1е/lгr{~1/.

1/----------------------------------------------------

---------------------------------------------------­

f i

// Инициализируемлокальныеиндексы, выделяяподмассивы

;nt./iгstl ::::::.firs/, - // Началопервогоподмассива

йи Та.\:tl 111id,'

inl./iгst1 -= пиа » 1: /:>' Н{lчаJZО в/110!)О(?О подмассива

int !ClstJ =: 10s/,' // Конец второго подмассива

/~/Пока оба подмассиване пусты. копирус.н .меньшии элемент /гео временный массив

inl таех -== ..111;0.\"11; Следующая свободная ячейка

// массиваteml).l41Itr(~."

for (; (fil">c\'/1 <~== [ая!1) && (jiгst2 <:=:~ /(lsI2): ++'inc!ex)

I l

//Инвариант:0I11jJC301\' tеnl/JА,·га)'[fi,"stl..[паех-Г]

//упорядочен.

ij(theL'1174 {,IJ'l f irslZ} .< 111е~/lгrа.);ifiГSI2])

t·enlf)·~41·,·4c'.y/i}1(fe.x~.1

the..41"/"l.1)/{fiT".\<t 1..1:

.+ "+~fiУ.\' t 1:

 

1

 

I

 

el..ке

 

(

 

l

'11е~4ккауIfi1·5' t ~.I,.

temp.A r пау/}паех] =

..+-. '+:/1rs t 2,'

 

} // Конец оператораff) /! Конец оператораюп

Скопироватьподмассив. оставшиисянепустым

// Скопироватьпервыи подмассив. если необходимо

27

!()Г (; ,/iгstl <~ =- Газ:1,. +- 4~fi"$·11. -+- -+ таех)

//Инвариант:отрезок tеmjJ.4Г'lt+lIJ'l/;,-stl..таех-Г]

//упорядочен.

lеn1/JL4ггu}'[indех]о::::: lhеL1Г/'llJ'[jiгstlj:

//(/"1\оnиров{11nЬ в1110РОЙ подмассив. ее.'!и необходимо

'

( -..

2

 

1...,

+- '+-.

j"

'")

. d

'

./ ()I·

,:.I/I"sl

<~=

lQsf.:.,-

[Jtf.,'t..:;,

+-zn

С.Х)

//Инвариант:отрезокtеm/J/1Гlt+{1.}'Lii,.stl..таех-Г]

//упорядочен

(етрАrlt"(l)'[index] 11'zеL1'·ГО)i[/iг.\'12],·

// Копируемрезультатобратнов исходныймассив

[о» (index ==,!IГSI; таех -<-= [ах): -+ +indc.x)

1пеАпауйпаех] Iеnlp~'<11~/~(lJ'[in(fex~j,'

} // Конец функциитепг«

1,'о;t!111егgеsоrl(Гииа'ГуреIhe-<4rf·С~);'f], int.iirst. int I(ISI)

//--------------------------------------------------------------------------­

//Упорядочиваетэлементымассива в возрастающсмпорядке.

// Предусловие: отрезок l}lе/:1г·rс/у·[/ZI·SI".. Гаы]

массив,

/г Постуслояие: .t'1/1aCO(:116 lJlеАГI·С1J'[fiг.\·I.. lа,,\'tl упорядочен

//по возрастанию.

//Вызываемые функции: тепге.

~/--------------------------------------------------------------------------­

(

1

// Упорядочиваем каждую из частей массива

Гп: тй! == (fi1",,~)t

+

1(},\'I)/2;

// Индек...с среднего элемента

//' Упорядоч116 аем лев..,чо 1]().71()t) 111(1' 1/1CА /'/'а..\'{fi,·s[.. /'11 i('-I

П1егgе5;()rl(1hеАгга)',

.11,",S1,

пиа);

// Упорядочиваемправую полоеину thej4/·1"(!Y'[nlid--+-l .. 1{15;~1

lnel·f,;e.\'()1"/(lheArrcl.J',

тла: 1, I(/~""'[)_:

/;/ Объединяем две половины

 

теег« (tlle~4 ккау. .,firs1,

/11 id,

'а5;1):

}/~/ Конец оператора ~'f'

}// Конец функции nlеrкс>-\'()гl

Анализ. Поскольку основные операции в этом алгоритме выполняются на этапе слияния, начнем анализ с него. На каждом шаге происходит объединение подмассивов theArray [first. . .тла] и theArray /mid+ 1.. .last}. На рис.lО показан пример, в котором требуется выполнить максимальное количество сравнений. Если общее количество элементов объединяемых отрезков массива равно n., то при их слиянии потребуется выполнить n-l сравнений. (Например, на рис. 1О показан массив, состоящий из шести элементов, следовательно, выполняется пять сравнений.) Кроме того, после сравнений осуществляется копирование n элементов временного массива в исходный. Таким образом, на каждом шаге выполняется 3*n-l основныхопераций.

28

 

first

mid

last

l-t-h-еА-гг-а-у:-~Г--с-л-и-я-н-и-:е-п-о-л-о-в-и-н----'1:

 

 

 

 

 

а) 1<4"\ поэтому копируем 1 из подмассива

 

 

 

 

 

 

 

 

 

 

theArray[first..mid] в массив tempArray

 

 

 

 

 

б) 2<4~ поэтому копируем 2 из подмассива

 

 

 

 

 

theArray[first..mid] в массив tempArray

 

 

 

 

 

в) 8>4., поэтому копируем 4 из подмассива

 

 

 

 

 

theArray[mid+ l ..last] в массив tempArray

 

а

б

 

г) 8>5, поэтому копируем 5 из подмассива

 

 

 

 

 

theArray[mid+ l ..last] в массив tempArray

tempArray:

 

 

 

 

 

 

 

д) 8>6"\ поэтому копируем 6 из подмассива

 

 

 

 

 

 

 

 

 

 

theArray[mid+ l ..last] в массив tempArray

 

 

 

 

 

ж) ПодмассивtheArray[mid+ 1..last] исчерпан,

 

 

 

 

 

поэтому копируем 8 в массив tempArray

РИСУНОК 10. Наихудший случай на этапе слияния

в ФУНКЦИИ

mergesort

выполняются два рекурсивных вызова. Как

показано на рис.П, если исходный вызов функции mergesort принадлежит

нулевому уровню, то на первом уровне возникают два рекурсивных вызова.

Затем каждый из этих вызовов порождает еще два рекурсивных вызова второго уровня и т.д. Сколько уровней рекурсии возникнет? Попробуем их

подсчитать.

8

 

 

Уровень О

 

 

 

Уровень 1

4

 

 

 

 

Уровень О

 

 

 

Уровень О

1

1

Уровень О: вызов функции mergesort для 8 элементов

 

Уровень 1: вызов функции mergesort для 4 элементов

 

Уровень О: вызов функции mergesort для 2 элементов

 

Уровень О: вызов функции mergesort для 1 элемента

 

Рисунок 11. Уровни рекурсивных вызовов функции mergesort при сортировкимассива,

состоящего из восьми элементов

Каждый вызов функции mergesort делит массив пополам. На первом этапе исходный массив оказывается разделенным на две части. При

29