Чому видалення зазвичай реалізувати набагато складніше, ніж вставлення в багато структур даних?


33

Чи можете ви придумати якусь конкретну причину, чому видалення зазвичай реалізувати значно складніше, ніж вставлення для багатьох (більшості?) Структур даних?

Короткий приклад: пов'язані списки. Вставлення є тривіальним, але вилучення має кілька особливих випадків, які значно ускладнюють його. Самобалансуючі двійкові пошукові дерева, такі як AVL та Червоно-чорні, - класичні приклади реалізації болісного видалення.

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


4
Як щодо pop, extract-min?
coredump

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

1
Як я вважаю, coredump на які натякає, стеки повинні бути принаймні такими ж простими, як і додати (для масиву, що підтримується масивом, popping - це лише зменшення покажчика [1], тоді як для натискання може знадобитися ціла копія масиву, якщо ви натиснете на максимальний розмір масив). Також є деякі випадки використання, коли передбачається, що вставки будуть частими, а видалення рідше, але це була б дуже магічна структура даних, де кількість видалень перевищує вставки. [1] Ви, мабуть, також повинні обнулити тепер невидиме посилання на спливний об'єкт, щоб уникнути витоку пам'яті, які я пам’ятаю, бо підручник Ліскова не зробив
Foon

43
"Офіціант, ви можете, будь ласка, додати більше майо до цього сендвіча?" - Звичайно, немає проблем, сер. "Чи можете ви також зняти всю гірчицю?" "А-а ......"
кобальтдук

3
Чому віднімання складніше, ніж додавання? Ділення (або проста факторизація) складніше, ніж множення? Коріння складніше, ніж експоненція?
mu занадто короткий

Відповіді:


69

Це більше, ніж просто стан душі; Є фізичні (тобто цифрові) причини, чому видалення важче.

Коли ви видаляєте, ви залишаєте дірку, де раніше щось було. Технічний термін для отриманої ентропії - "фрагментація". У зв’язаному списку це вимагає, щоб ви "закріпили" видалений вузол і розмістили пам'ять, яку він використовує. У бінарних деревах це викликає незбалансованість дерева. У системах пам’яті це призводить до того, що пам’ять на деякий час залишається невикористаною, якщо знову виділені блоки більше, ніж блоки, залишені при видаленні.

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


3
Фрагментація не є проблемою, коли вступають вказівники та непрямість, як для структури в пам'яті, так і для діаграм. У пам'яті не має значення, де існують окремі вузли через непрямість. Для списків видалення внутрішнього вузла (у якому ви маєте дірку на діаграмі) передбачає дещо менше операцій, ніж вставка (1 призначення вказівника та 1 вільний проти 1 розподілу та 2 призначення вказівника). Для дерев вставлення вузла може врівноважити дерево так само, як і видалення. Саме крайні випадки викликають труднощі, на які брито посилається, де фрагментація не має значення.
outis

12
Я не погоджуюся з тим, що вставки та вилучення відрізняються передбачуваністю. "Виправлення навколо" вузла списку - це саме те, що відбувається в зворотному випадку, якщо замість цього буде вставлено той самий вузол. У будь-якій точці немає невизначеності в будь-якому напрямку, і в будь-якому контейнері без внутрішньої структури його елементів (наприклад, врівноважене бінарне дерево, масив із суворим співвідношенням між зміщеннями елементів) взагалі немає "дірки". Тому, боюся, я не знаю, про що ви тут говорите.
sqykly

2
Дуже цікаво, але я б сказав, що аргументи пропущені. Ви можете організувати структури даних навколо простого / швидкого видалення без проблем. Це просто рідше, ймовірно, менш корисне.
luk32

@sqykly Я думаю, що список був поганим прикладом вибору, тому що середня вставка та середнє відношення однаково важкі. Один випадок виділяє пам'ять, куди другий перерозподіляється. Один відкриває отвір, де інший запечатує дірку. Тому не всі випадки видалення складніші, ніж додати.
ydobonebi

36

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

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

Крім того, яка користь використовується для послідовного видалення кожного елемента? Чому б просто не викликати якусь функцію, яка очищає її все відразу (можливо, просто створивши нову)? Також структури даних є найбільш корисними, коли вони насправді щось містять. Тож настільки часто видалення, скільки вставок, на практиці не дуже поширений.

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


4
Я можу собі уявити один випадок використання. Структура даних, яка готується до початкового введення, а потім до індивідуального споживання. Звичайно, це рідко і не дуже цікаво алгоритмічно, тому що, як ви вже говорили, така операція не може домінувати над вставкою асимптотично. Можливо, є якась надія, що пакетна вставка може мати амортизовану вартість досить непоганою, а також швидкою та простою для видалення, тому це матиме складні, проте практичні пакетні вставки та прості та швидкі індивідуальні видалення. Звичайно, дуже незвична практична потреба.
luk32

1
Уммм, я думаю, що прикладом може бути зворотний упорядкований вектор. Ви можете додати партію kелементів досить швидко: зворотне введення сортування та об'єднання з існуючим вектором - O(k log k + n). Тоді у вас є структура з досить складною вставкою, але споживання верхніх uелементів є тривіальним і швидким. Просто візьміть останнє uі перемістіть кінець вектора. Хоча, якщо комусь колись потрібне таке, я буду проклятий. Я сподіваюся, що це хоча б посилює ваш аргумент.
luk32

Чи не хочете ви оптимізувати середній показник використання, а не те, що ви найбільше робите?
Шив

Проста чергова робоча черга FIFO зазвичай намагається бути порожньою більшу частину часу. Добре розроблена черга буде добре оптимізована (тобто O (1)) як для вставок, так і для видалення (і дуже хороша також підтримуватиме швидкі паралельні операції, але це вже інше питання).
Кевін

6

Це не важче.

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

Це передбачає, що в обох випадках у вас є вузол для вставки / видалення. (А у випадку вставки, у вас також є вузол, який потрібно вставити раніше, тож певним чином вставлення можна вважати дещо складнішим.) Якщо ви намагаєтесь видалити, маючи не вузол для видалення, а корисний навантаження вузла, то, звичайно, вам доведеться спочатку здійснити пошук у списку корисного навантаження, але це не є недоліком видалення, чи не так?

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

Коротше кажучи, немає жодної причини, чому одному слід бути важче, ніж іншому, і якщо ви виявите, що це так, то насправді можливо, що ви стали жертвою (дуже людської) тенденції знайти природніше думати. конструктивно, ніж субтрактивно, це означає, що ви можете здійснювати видалення таким чином, який є складнішим, ніж це має бути. Але це питання людини. З математичної точки зору, питання не виникає.


1
Я повинен не погодитися. Алгоритм видалення AVL складніший за вставку. Для певних видалень вузлів, можливо, доведеться перебалансувати все дерево, що, як правило, робиться рекурсивно, але також може бути виконано не рекурсивно. Не потрібно робити це для вставки. Мені невідомо вдосконалення алгоритму, де такого збалансування цілого дерева можна уникнути у всіх випадках.
Денніс

@Dennis: Можливо, дерева AVL дотримуються винятку, а не правила.
outis

@outis IIRC, усі врівноважені дерева пошуку мають більш складні процедури видалення (ніж вставки).
Рафаель

Що щодо закритих хеш-таблиць? Вставлення є (відносно) простим, видалення принаймні складніше для концептуалізації, оскільки вам доведеться виправити все "те, що повинно було знаходитись в індексі X, в даний час знаходиться в індексі Y, і ми повинні піти знайти його і повернути назад" питань.
Кевін

3

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

Що стосується когнітивної складності, то в питанні є відповідь: кращі випадки. У видаленні їх може бути більше, ніж вставки (це ще не встановлено в загальному випадку). Однак принаймні деяких з цих крайових випадків можна уникнути в певних конструкціях (наприклад, мати дозорний вузол у пов'язаному списку).


2
"Більшість структур даних не потребують пошуку для вставки." -- як от? Насправді я б висловив протилежне твердження. (Ви "знаходите" позицію вставки, яка настільки ж дорога, як і пошук того ж елемента знову пізніше.)
Рафаель,

@Raphael: Цю відповідь слід читати в контексті пов'язаної таблиці складності операцій, яка не включає операцію пошуку як частину видалення. Відповідаючи на ваше запитання, я класифікував структуру за загальною назвою. З масивів, списків, дерев, хеш-таблиць, стеків, черг, купи та наборів, дерев та наборів потрібен пошук для вставки; інші використовують індекс, не пов’язаний із елементом (для базових стеків, черг і купи, виставляється лише 1 індекс, а знаходження не підтримується) або обчислити його з елемента. Графіки можуть йти в будь-який бік, залежно від способу їх використання.
outis

... Спроби можна вважати деревами; однак, якщо класифікація як їх власна структура, то чи є "знахідка" під час вставки, це більше питання, тому я не включаю її. Зверніть увагу, що список структур даних не враховує інтерфейс та реалізацію. Також те, як ви рахуєте, багато в чому залежить від того, як ви класифікуєтесь. Я побачу, чи можу я придумати більш об’єктивне твердження.
outis

Я визнаю, що мав на увазі інтерфейс словника / набору (як звичайний у CS). У будь-якому випадку ця таблиця вводить в оману і (iirc) навіть неправильно в декількох місцях - Вікіпедія, яма дезінформації CS. : /
Рафаель

0

На додаток до всіх згаданих питань є цілісність референтних даних. Для більшості правильно побудованих структур даних, таких як бази даних у SQL, дуже важливою є референтна цілісність Oracle.
Щоб переконатися, що ви випадково не знищили його безліч різних придуманих речей.
Наприклад, каскад при видаленні, який не просто видаляє те, що ви намагаєтесь видалити, але також запускає очищення пов'язаних даних.
Це очищає базу даних від непотрібних даних, а також зберігає цілісність даних недоторканими.
Наприклад, у вас є таблиці з батьками та види, як відповідні записи у другій таблиці.
Де головний стіл батьків. Якщо у вас немає посиленої референтної цілісності, ви можете видалити будь-які записи з будь-якої таблиці, і пізніше ви не знатимете, як отримати повну сімейну інформацію, оскільки у вас є дані в дочірній таблиці і нічого в батьківській таблиці.
Ось чому перевірка референтності на цілісність не дозволить видалити запис з батьківської таблиці, поки записи з дочірньої таблиці не будуть очищені.
І саме тому в більшості джерел даних важче видалити дані.


Я думаю, що питання ставилося про структури пам'яті, такі як пов'язані списки, хеш-таблиці тощо, а не бази даних, але референтна цілісність є головною проблемою навіть у структурах пам'яті.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.