Упорядкування та пошук в списках

Впорядкований список передбачає певну послідовність слідування елементів – алфавіт, зростання значень і т.п.

Пошук – типова операція для списків. У невпорядкованому списку для її виконання необхідно переглянути весь список, у зв’язку з чим в середньому на пошук буде витрачатися N/2 порівнянь.

Для впорядкованого списку час на здійснення пошуку можна суттєво скоротити.

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

Інший спосіб передбачає виконання бінарного (чи логарифмічного) пошуку. При виконанні бінарного пошуку спочатку здійснюється порівняння з елементом всередині списку (N+1)/2, якщо цей елемент не є шуканим, то, в залежності від того, чи є він більшим, чи меншим, необхідно переглянути елементи, що йдуть до нього, чи після нього. Елементи переглядаються також, починаючи з елементу, що є всередині списку.

Використання бінарного пошуку дозволяє скоротити час на пошук у логарифмічній залежності. Наприклад, пошук повним перебиранням елементів при N=128 передбачає в середньому 64 порівнянь, а бінарний пошук – у будь-якому разі не більше 8 порівнянь.

Недоліком впорядкованих структур даних є необхідність витрат на їх впорядкування. Якщо пошук здійснюється рідше, ніж внесення нових елементів, то витрати ресурсів на впорядкування можуть бути невиправданими. Проте у більшості випадків внесення нових елементів відбувається рідше, ніж їх пошук.

7.5. Похідні структури даних: черга, стек, дек

Черга – різновид лінійного списку, у якій нові дані розміщуються слідом за попередніми в порядку надходження.

В залежності від того, яким чином можна отримати доступ до даних розрізняють:

– черга FIFO (First In – First Out) – доступ лише до першого елементу (аналогія з чергою до магазину);

– двобічна черга (дек) – доступ до елементів з початку і з кінця черги (аналогія з чергою до магазину, в якій товару на всіх не вистачить);

– стек – лінійний список, я якому операції додавання та вилучення виконують лише з однієї сторони (на верхівці стеку).

На відміну від масивів та традиційних лінійних списків, доступ до елементів яких спеціальним чином не обмежується, основним призначенням черги є тимчасове зберігання елементів, які заносяться та вилучаються з черги в спеціальній послідовності.

Основні операції:

– поставити в чергу;

– вилучити з черги.

Також черга може повертати свою довжину (кількість елементів), або відповідати на запит, чи є наступний елемент.

При роботі з чергами можливі наступні помилки:

– переповнення – спроба поставити елемент до черги, коли всю відведену для неї пам’ять використано;

– незаповнення – спроба вилучити елемент з пустої черги.

 

Черга FIFO

Черга FIFO – традиційна реалізація черги, яка, як правило, створюється з метою обробки певних даних, коли процес обробки може бути тривалішим за надходження даних.

Для черги FIFO операції виконуються наступним чином:

· поставити в чергу – елемент заноситься до кінця черги і стає її останнім елементом;

· вилучити з черги – черга повертає перший елемент і виділяє його з себе, першим стає елемент, який був другим.

Найкращим варіантом реалізації черги FIFO є використання однобічно зв’язаного списку.

 

Блокуюча черга

Черги часто використовуються у ситуації, коли доступ до неї мають два потоки – один на додавання елементів, інший – на вилучення. Крім того, в реальній ситуації обсяг доступної пам’яті, як правило, обмежений, а черги використовуються для «буферів» - тимчасового сховища певних елементів у обмеженому обсягу пам’яті.

В такій ситуації створюється певна модифікація черги, яка має назву блокуючої.

Блокуюча черга відрізняється від звичайної черги тим, що у разі свого заповнення вона призупиняє потік, який додає елементи, до тих пір, доки з черги не буде вилучено елемент і у ній не з’явиться місце.

Крім власне операції блокування блокуючи черга має бути «безпечною для потоків» і передбачати таку реалізацію, в якій з нею одночасно можуть працювати декілька потоків.

 

Визначення стеку

Стек – різновид лінійного списку, у якому операції внесення та вилучення елементів відбуваються з однієї сторони (на верхівці стеку).

Стек також називають чергою LIFO (Last In – First Out).

Стек можна розглядати як певну аналогію до стопки тарілок, з якої можна взяти верхню, і на яку можна покласти верхню тарілку (інша назва стеку - "магазин", за аналогією з принципом роботи магазину в автоматичній зброї).

Стек дуже поширений у різних реалізаціях мов програмування та мікропроцесорах ЕОМ.

 

Основні операції зі стеками

Основні операції зі стеками, такі ж, як і з чергами: внесення елементу і вилучення елементу зі стеку. У специфічній до стеку термінології термінології ці операції мають назву:

· заштовхнути елемент("push"): елемент додається в стек та розміщується в його верхівці. Розмір стеку збільшується на одиницю. При перевищенні розміра стека граничної величини, відбувається переповнення стека (stack overflow)

· виштовхнути елемент("pop"): отримує елемент з верхівки стеку. При цьому він видаляється зі стеку і його місце в верхівці стеку займає наступний за ним відповідно до правила LIFO, а розмір стеку зменшується на одиницю. При намаганні "виштовхнути" елемент з вже пустого стеку, відбувається ситуація "незаповнення" стеку (stack underflow)

Кожна з цих операцій зі стеком виконується за фіксований час O(1) і не залежить від розміру стеку.

Додаткові операції (присутні не у всіх реалізаціях стеку):

· isEmpty: перевірка наявності елементів в стеку; результат: істина (true), коли в стеку немає елементів.

· isFull: перевірка заповненості стека. Результат: істина, коли додавання нового елементу неможливе

· clear: звільнити стек (видалити усі елементи).

· top: отримати верхній елемент (без виштовхування).

· size: отримати розмір (кількість елементів) стека.

· swap: поміняти два верхніх елементи місцями.