- •Тема 2. ЕТАПИ РОЗВИТКУ ТЕОРІЇ АЛГОРИТМІВ ТА ЇЇ ЗАСНОВНИКИ
- •Тема 3. МОДЕЛІ ОБЧИСЛЕНЬ
- •Тема 4. ПОНЯТТЯ СТРУКТУР ДАНИХ
- •Тема 5 СТРУКТУРНІСТЬ ДАНИХ І ТЕХНОЛОГІЯ ПРОГРАМУВАННЯ
- •Тема 6. ІНФОРМАЦІЙНА МОДЕЛЬ
- •Тема 7. ПОКАЖЧИКИ ТА ОПЕРАЦІЇ НАД НИМИ
- •Тема 8. ФІЗИЧНА СТРУКТУРА ПОКАЖЧИКА
- •Тема 9. ПРЕДСТАВЛЕННЯ ПОКАЖЧИКІВ У МОВАХ ПРОГРАМУВАННЯ
- •Тема 10. ВИДІЛЕННЯ ТА ЗВІЛЬНЕННЯ ДИНАМІЧНОЇ ПАМ'ЯТІ
- •Тема 11. ПРИКЛАДИ РОБОТИ З ДИНАМІЧНИМИ ЗМІННИМИ
- •Тема 12. ЗАГАЛЬНА ХАРАКТЕРИСТИКА СПИСКОВИХ СТРУКТУР ДАНИХ
- •Тема 13. ЗВ’ЯЗНЕ ПРЕДСТАВЛЕННЯ ДАНИХ В ПАМ'ЯТІ КОМП’ЮТЕРА
- •Тема 14. СТЕКИ
- •Тема 15. МАШИННЕ ПРЕДСТАВЛЕННЯ СТЕКА І РЕАЛІЗАЦІЯ ОПЕРАЦІЙ
- •Тема 16. ЧЕРГИ
- •Тема 17. МАШИННЕ ПРЕДСТАВЛЕННЯ ЧЕРГИ. ЧЕРГИ З ПРІОРИТЕТАМИ. ДЕКИ.
- •Тема 18. ЛІНІЙНІ СПИСКИ
- •Тема 19: ДВОНАПРЯМЛЕНІ ЛІНІЙНІ СПИСКИ
- •Тема 20. ДЕРЕВА. СТВОРЕННЯ ТА ОБХІД БІНАРНОГО ДЕРЕВА
- •Тема 21: ЕЛЕМЕНТИ ТА ВЛАСТИВОСТІ БІНАРНОГО ДЕРЕВА
- •Тема 22. ОПЕРАЦІЇ З ВУЗЛАМИ ДЕРЕВА
- •Тема 23: АЛГОРИТМИ ВИЗНАЧЕННЯ ВЛАСТИВОСТЕЙ БІНАРНОГО ДЕРЕВА
- •Тема 24. ПОНЯТТЯ ГРАФА ТА ЙОГО ЗОБРАЖЕННЯ В ПАМ'ЯТІ КОМП'ЮТЕРА
- •Тема 26. ОБХІД ГРАФУ: ПОШУК ВГЛИБИНУ
- •Тема 27. ОБХІД ГРАФУ: ПОШУК УШИР
- •Тема 28. КЛАСИЧНІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
- •Тема 29. ШВИДКІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
- •Тема 30. КЛАСИЧНІ АЛГОРИТМИ ПОШУКУ ДАНИХ ЗА ЗАДАНИМИ КРИТЕРІЯМИ
- •Тема 31. КЛАСИФІКАЦІЯ КРИТЕРІЇВ ПОШУКУ ДАНИХ У МАСИВАХ
- •Тема 32. КРИПТОГРАФІЧНІ ЗАСОБИ ЗАХИСТУ ІНФОРМАЦІЇ
- •Тема 33. ПРОБЛЕМИ І ПЕРСПЕКТИВИ КРИПОТГРАФІЧНИХ СИСТЕМ
- •Тема 34. АЛГОРИТМИ ШИФРУВАННЯ
- •Тема 36. ПОКАЗНИКИ СКЛАДНОСТІ АЛГОРИТМІВ
Тема 28. КЛАСИЧНІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
Однією з найбільш поширених операцій обробки однорідних даних, організованих у вигляді масивів, є їх упорядкування, або сортування. Впорядкування масиву – це зміна порядку розташування його елементів за певним критерієм. Наприклад, числовий масив можна впорядкувати за зростанням значень його елементів або за їх спаданням, а масив рядків можна відсортувати в алфавітному порядку. Найчастіше сортування масиву здійснюється з метою полегшення подальшого пошуку.
Відомо багато методів сортування масиву, що відрізняються швидкодією й обсягом оперативної пам'яті, яка при цьому використовується. Серед цих методів можна вирізнити методи внутрішнього та зовнішнього сортування. Методи внутрішнього сортування не передбачають використання допоміжних масивів. Ці методи застосовують до масивів, що повністю розташовані в оперативній пам'яті. Методи зовнішнього сортування застосовують до великих масивів даних, які зберігаються на зовнішніх носіях.
Методи внутрішнього сортування прийнято поділяти на дві групи: елементарні (прямі) та удосконалені методи.
Найбільш відомими елементарними методами сортування масиву є:
Сортування вставкою (включення);
Сортування вибором;
Сортування обміном (метод «бульбашки»).
З удосконалених методів сортування найчастіше використовуються такі:
Швидке сортування або метод Хоара;
Сортування включенням зі спадним приростом, або метод Шелла;
Сортування за допомогою дерева, або пірамідальне сортування;
Сортування методом злиття.
Сортування методом вставки
На кожному кроці цього методу масив розділений на дві частини: ліву, вже відсортовану, та праву, ще не відсортовану. Перший елемент правої частини вставляється до лівої частини так, щоб ліва частина залишалась відсортованою. У результаті відсортована частина збільшується на один елемент, а інша – на один елемент зменшується. Отже, на кожному кроці алгоритму сортування методом вставки слід виконати дві операції: пошук позиції для вставки елемента та власне його вставку із подальшим зсувом на одну позицію вправо від елементів відсортованої частини. Цей зсув «затре» перший елемент невпорядкованої частини масиву останнім елементом відсортованої. Спочатку відсортованою частиною масиву вважається перший елемент, а решта елементів відноситься до невпорядкованої частини. Поки ця частина не стане порожньою виконуються наступні дії:
1.Перший елемент невпорядкованої частини зберігається в допоміжній змінній.
2.Визначається позиція вставки збереженого елемента у масив. Для цього слід дотримуватись наступних вказівок:
Вважати перший елемент масиву поточним.
Доки елемент вставки більше за поточний, збільшувати індекс поточного елементу.
3.Збережений на кроці 1 елемент вставляється на знайдену позицію, а решта елементів відсортованої частини зсуваються на одну позицію вправо.
4.Початок невпорядкованої частини пересувається вправо на одну позицію.
Фрагмент програмного коду реалізації алгоритму вставки має вигляд: program metod_vstavku;
101
var n, i, j, k : integer; {кількість та індекси елементів}
a: array[1..10] of integer; {масив, який слід відсортувати}
tmp: integer |
{допоміжна змінна} |
begin |
|
……………… {Введення вхідних даних}
for i:=2 to n do {i - початок невідсортованої частини}
begin |
|
tmp:=a[i]; |
{визначається елемент для вставки} |
j:=1; |
{цикл пошуку позиції вставки} |
while tmp>a[j] do j:=j+1;
for k:=i-1 downto j do a[k+1]:=a[k]; {зсув елементів вправо} {вставка елементу}
Удосконалені методи сортування масиву використовують значно меншу кількість
операцій порівняння та перестановок елементів.
Сортування включенням зі спадним приростом, або метод Шелла
В алгоритмі Шелла спочатку порівнюються відділені, а потім – близько розташовані елементи даних. Змінна gap (зазор) містить інтервал, який розділяє елементи даних, які порівнюються між собою. Спочатку gap дорівнює половині кількості елементів. В процесі сортування gap зменшується на кожному кроці в два рази, доки не почне виконуватися порівняння сусідніх елементів, як у методі «бульбашки». Алгоритм сортування Шелла для N елементів складається з п’яти кроків:
1.Встановлення значення gap := N div 2.
2.Для кожного i-го елементу (значення i змінюється від 1 до N-1) порівнюються елемент i з елементом i+gap. При необхідності здійснюється обмін.
3.Крок 2 повторюється доки для елементу gap є хоча б одна пара, яка потребує обміну.
4.Значення gap зменшується в два рази: gap := gap div 2.
5.Повторення кроків 1-4 доки значення gap не стане рівним нулю.
Фрагмент програмного коду сортування за зростанням методом Шелла масиву з N елементів має наступний вигляд:
gap:=n div 2; repeat repeat
gapDone:=true;
for i:=1 to (n-gap) do begin
if a[i]>a[i+gap] then begin temp:=a[i]; a[i]:=a[i+gap]; a[i+gap]:=temp; gapDone:=false; end;
end;
until gapDone; gap:=gap div 2; until gap=0;
Рис.2 Результати
сортування масиву методом Шелла
Результати роботи програми сортування масиву методом Шелла зображені на рис.2.
103
Тема 29. ШВИДКІ АЛГОРИТМИ СОРТУВАННЯ ОДНОРІДНИХ ДАНИХ
Автор методу Ч.Хоар назвав свій алгоритм швидким сортуванням, оскільки для більшості масивів цей метод потребує приблизно (n/6)log n обмінів елементів і nlog n порівнянь, тобто набагато менше, ніж будь-який з елементарних методів. Метод функціонує за принципом «Розділяй і володарюй»: елементи масиву діляться на дві частини, і кожна з них сортується окремо. Для цього обирають деякий елемент x, який називають розділовим. Мета полягає у розташуванні всіх менших за x елементів зліва від x, а всіх більших за x елементів – справа від x. Поділивши масив, слід повторити процедуру сортування для кожної частини, потім – для частин цих частин і т.д., доки кожна з частин масиву не міститиме лише один елемент. У деяких модифікаціях методу Хоара розташування та значення розділового елемента можуть змінюватися під час розподілу елементів.
Алгоритм методу Хоара є рекурсивним і для його реалізації доцільно застосувати рекурсивну процедуру. Параметрами цієї процедури будуть змінні left і right, що позначатимуть ліву та праву частини масиву. Розділовим елементом вважатиметься середній за номером елемент частини масиву, значення якого зберігатиметься в звінній x. Рекурсивний виклик процедури для поділу лівого під масиву на дві частини здійснюється в тому разі, якщо ліва межа під масиву менша за індекс його середнього елементу, а для поділу правого під масиву – якщо права межа більша за індекс середнього елемента. Для поділу елементів на дві частини застосовуються два цикли while у середині циклу repeat – until. На кожній ітерації зовнішнього циклу здійснюватиметься один обмін елементами між лівою та правою частинами. Внутріші цикли (здійснюють перегляд елементів зліва та справа) призначеня для знаходження чергової пари елементів, що мають бути переставлені. Переставленим може бути й сам розділовий елемент, при цому він може втратити властивість розділовості. Цей факт не впливає на коректність роботи програми:
program Yrok13_1;
//Швидке сортування - метод Хоара {$APPTYPE CONSOLE}
uses SysUtils;
var i,n: integer; //індекс, кількість елементів масиву a: array[1..10] of integer; //вихідний масив
{Рекурсивна процедурв сортування} procedure quicksort(left, right: integer);
var i,j,k,x,tmp: integer; //розділовий елемент та лопоміжні змінні begin
i:=left; //ліва границя підмасиву j:=right; //права границя підмасиву
x:=a[(left+right) div 2]; // значення бар"єрного елемента writeln('left=',left,' right=',right,' x=',x);
repeat //розділити масив на підмасиви
while a[i]<x do i:=i+1; //визначити індекси елементів, while a[j]>x do j:=j-1; //що обмінюються місцями
if i<=j then |
|
begin |
// обмін елементів місцями |
tmp:=a[i]; |
|
a[i]:=a[j]; |
|
a[j]:=tmp; |
|
104
i:=i+1; j:=j-1;
for k:=1 to n do write(a[k],' '); writeln;
end; until i>j;
if left<j then quicksort(left, j); //впорядкування лівого підмасиву
if i<right then quicksort(i, right); //впорядкування правого підмасиву end;
{головна програма} begin
randomize;
writeln('quicksort');
write('Enter number of the components (<=10)'); readln(n);
writeln('Generated array: '); |
|
for i:=1 to n do |
|
begin |
|
a[i]:=random(50); |
|
write(a[i],' '); |
|
end; |
|
writeln; |
|
writeln('sort process: '); |
|
quicksort(1,n); |
|
writeln('sorted array:' ); |
|
for i:=1 to n do write(a[i],' '); |
|
writeln; |
|
readln; |
Рис.1.Результат сортування методом Хоара |
end. |
|
Сортування методом злиття
У запропонованому Дж. фон Нейманом методі сортування злиттям , як і в методі Хоара, реалізовано метод «розділяй і володарюй». Масив ділиться навпіл, до кожної половини застосовується рекурсивно та сама процедура сортування злиттям, а відсортовані частини з’єднуються в один впорядкованний масив (рис.2).
|
|
|
1 |
|
|
2 |
|
|
3 |
|
3 |
|
4 |
|
|
5 |
|
|
7 |
|
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
|
7 |
|
9 |
|
|
|
1 |
|
3 |
|
4 |
|
5 |
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
|
|
9 |
|
|
|
|
2 |
|
7 |
|
|
|
|
4 |
|
|
5 |
|
|
1 |
|
3 |
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
|
3 |
7 |
|
2 |
5 |
|
|
4 |
|
|
1 |
|
|
3 |
|
|
|
|
|
Рис.2. Схема сортування методом злиття Отже.ю базовою операцією методу є злиття двох упорядкова них масивів в один.
Ефективний спосіб виконання цієї операції полягає в тому, що елементи масивів порівнюються і за результатами порівняння в новий масив записується елемент або з пе ршого, або з другого
105
масивую Один з масивів під час злиття може закінчитися раніше. В такому випадку елементи іншого масиву, які ще не були опрацьовані, додаються до нового масиву.
Час злиття упорядкованих масивів лінійно залежить від їх сумарної довжини. Враховуючи цей факт, неважко буде довести, що повне сортування методом злиття, як і сортування методом Хоара, потребує виконання О(nlog n) базових операцій над елементами масиву розмірності n.
В наступній програмі задача злиття двох масивів реалізується за допомогою процедури. Оскільки процедура злиття має бути застосована для різних під масивів, то зручно було б передавати масив до процедури як параметр.
program Yrok13_2;
//Сортування методом злиття (фон Неймана) {$APPTYPE CONSOLE}
uses SysUtils;
type Arr=array[1..10] of integer; //тип масиву
var i,n: integer; |
//індекс, кілкість елементів масиву |
a,g,e,f: Arr; |
//масив а, що сортується, g,e,f - допоміжні масиви |
//Злиття упорядкованих масивів a і b procedure Merge(a,b: Arr; n,m: integer; var c: Arr); var i,j: integer;
begin i:=1; j:=1;
while (i<=n) and (j<=m) do //формування вихідних масивів if a[i]<b[j] //якщо елемент масиву а менший
then begin |
|
c[i+j-1]:=a[i]; |
|
i:=i+1; |
//перейти до наступного елементу масиву а |
end |
|
else begin |
//якщо елемент масиву b менший |
c[i+j-1]:=b[j]; |
|
j:=j+1; |
//перейти до наступного елементу масиву b |
end; |
|
for i:=i to n do c[i+j-1]:=a[i]; //якщо масив а не вичерпано for j:=j to n do c[i+j-1]:=b[j]; //якщо масив b не вичерпано end;
//Копіювання частини масиву а до масиву b
procedure CopyArr(a: Arr; x,y: integer; var b: Arr; z: integer); var i: integer;
begin
for i:=x to y do b[i-x+z]:=a[i]; end;
//Виведення масиву а, що має довжину n procedure OutPut(a: Arr; n: integer);
var i: integer; begin
for i:=1 to n do write(a[i],' '); writeln;
end;
106
//Сортування частини м асиву від i-го до j-го елементу procedure Sort(i,j: integer);
var t: integer; begin
if i<j then //якщо сортується більше ніж один елемент begin
t:=i+(j-i) div 2; //середина частини, що сортується
if i<j-1 then |
//якщо сортується більше ніж два елементи |
begin |
|
Sort(i,t); |
//сортува ння лівої половини |
Sort(t+1,j); |
//сорту вання правої половини |
end; |
|
CopyArr(a,i,t,g,1); //коп іювання відсортованих частин CopyArr(a,t+1,j,e,1); //д о резервних масивів g та e Merge(g,e,t-i+1,j-t,f); //злиття відсортованих частин CopyArr(f,1,j-i+1,a,i);
write('The sorted [',i,',',j,']part ');
OutPut(f,j-i+1); |
//виведення проміжних результатів |
end; |
|
end; |
|
//головна програма |
|
begin |
|
randomize; writeln('Merge sort');
write('Enter number of the components (<=10) '); readln(n); for i:=1 to n do a[i]:=random(50);
writeln('Generated array: '); OutPut(a,n);
Sort(1,n); writeln('Sorted array: '); OutPut(a,n);
readln;
end.
Рис. 3.Результат сортування методом злитт я
107