j : Indx; v : T;
label 1;
begin
if lo >= hi then goto 1;
if (lo = hi - 1) and (A[lo] > A[hi]) then
begin
swap ( A[lo], A[hi] );
goto 1
end;
k := lo + 1; j := hi;
v := A[lo]; {?!}
while ( k < j ) do
if A[k] < v then k := k + 1
else
begin
if A[j] < v then swap ( A[k], A[j] );
j := j - 1
end;
{ k = j }
if A[k] >= v then k := k - 1;
{ A[k] є останнім від початку елементом, }
{ значення якого менше v }
swap ( A[lo], A[k] ); { A[k] = v }
quicksort ( A , lo, k - 1 );
quicksort ( A , k + 1, hi );
1: end
Якщо за виконання кожного виклику після циклу while значення змінної k приблизно рівне (lo+hi)/2, то складність швидкого сортування масиву з n елементів можна оцінити як O(nlogn). Середня кількість порівнянь елементів усіх можливих числових послідовностей довжини n також має оцінку O(nlogn); доведення є, наприклад, у книзі [АХУ]. Емпіричні дослідження свідчать, що швидке сортування вимагає в середньому елементарних дій приблизно вдвічі менше, ніж сортування деревом, і вчетверо менше, ніж сортування злиттям. Але якщо початковий масив близький до відсортованого, то його сортування за наведеним алгоритмом вимагає вже O(n2) порівнянь. У такому випадку відокремлюючим елементом не можна вибирати значення A[lo].
Існує багато способів вибору іншого відокремлюючого значення. Наприклад, можна вибрати значення елемента з випадковим номером серед номерів lo, … , hi, або середнє із значень A[lo], A[hi], та A[(lo+hi)div2]. У такому разі перед оператором процедури
v := A[lo]; {?!}
треба задати обмін значень A[lo] та відповідного елемента, чиє значення вибирається відокремлюючим.
Задачі
8. Переробити процедуру Merges так, щоб на кроці з непарним номером перший масив без копіювання зливався в другий, а на кроці з парним номером, навпаки, другий без копіювання зливався в перший.
9.* Написати нерекурсивний варіант сортування деревом (підр.17.4.2).
10. За масивом із N рядків визначається додатковий індексовий масив: попаpно pізні значення його елементів належать діапазону 1..N і є індексами в масиві рядків. Написати процедуру
а) сортування індексового масиву таким чином, щоб рядки, на які посилаються його послідовні елементи, були лексикогpафічно впоpядковані;
б) друкування рядків масиву за зpостанням у лексикографічному порядку (кожний рядок друкується один pаз незалежно від кількості повтоpень у масиві).
11. Написати процедуру обчислення N (N ? 1000) найменших значень числової послідовності довільної довжини за умови, що
а)* незалежно від кількості повтоpень усі числа враховуються один pаз кожне;
б) число враховується стільки pазів, скільки воно зустрілося в послідовності (але не більше ніж N);
в) числа враховуються один pаз кожне, але ведеться додатковий облік кількостей їх повтоpень.
12. Запрограмувати злиття
а) трьох б) п'яти в) тисячі
упорядкованих послідовностей в одну.
13.* Запрограмувати сортування елементів зв'язаного списку. Складність алгоритму повинна бути O(nlogn).
14. Запрограмувати сортування послідовностей із
а) чотирьох елементів так, щоб виконувалося не більше п'яти порівнянь;
б) п'яти елементів так, щоб виконувалося не більше восьми порівнянь;
в) п'яти елементів так, щоб виконувалося не більше семи порівнянь.
5. Відсортуй, і багато чого побачиш
Вам треба огородити сад із деревами, витративши на огорожу якомога менше матеріалу. Вважатимемо, що дерев не менше трьох, і вони задаються координатами на площині. Розв'язання полягає в побудові так званої опуклої оболонки навколо множини точок із заданими координатами. На площині вона являє собою опуклий багатокутник, що містить усі задані точки і не містить інших опуклих багатокутників, які також "накривають" усі точки.
Якщо намалювати план саду, то побудова оболонки стане очевидною. Треба відшукати якусь "крайню" початкову точку і рухатися від неї, малюючи опуклий багатокутник. Як результат, ми одержимо послідовність вершин – заданих точок, на які "натягнуто" багатокутник. Точки, що потрапили на сторону багатокутника, але не є вершинами, можна відкинути.
Наприклад, за множини точок із координатами (1,1), (2,2), (3,3), (5,-1), (2,-1), (3,0) ми одержимо багатокутник із вершинами (1,1), (3,3), (5,-1), (2,-1). Точка (3,0) опинилася всередині його, точка (2,2) – на стороні.
Комп'ютер не бачить плану саду, тому доведеться пояснити йому докладніше, як одержати потрібну оболонку. Один із способів оснований на тім, що спочатку точки сортуються за зростанням, наприклад, координати Y. Тепер це не просто множина, а певним чином упорядкована послідовність. Врахування цього дозволяє створити простий та прозорий алгоритм побудови оболонки.
За початкову точку можна взяти точку із найменшою координатою Y. Таких точок може бути кілька – виберемо з них ту, координата X якої найменша. Далі ми будуємо дві послідовності точок, починаючи обхід множини як зліва, так і справа. Назвемо їх лівою та правою. Друга точка додається до обох. Далі після обробки кожної точки будується оболонка тієї множини точок, які вже розглянуто.
Нехай A та B – остання й передостання точки послідовності, побудованої при обході зліва, C – нова точка. Якщо при переході з відрізка BA на відрізок AC ми робимо лівий поворот або рухаємося по прямій, то точку A можна вилучити з послідовності. Так само треба вилучити й точку B, якщо перед нею в послідовності є точка Z, така що, ZBC – лівий поворот або відрізок прямої. І так далі. Нарешті, або дві останні точки послідовності разом із новою утворюють правий поворот, або всі, крім першої, вилучено.
Визначення, чи утворюють точки BAC лівий поворіт, здійснюється за знаком векторного добутку , як у прикладі 7.5 з