Які конкретні правила використання зв'язаного списку замість масиву?


18

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

Це дуже абстрактно, і я хотів би конкретного пояснення того, чому слід використовувати зв’язаний список, а не масив. Я не дуже досвідчений у програмуванні, тому у мене не багато досвіду в реальному світі.


3
Чесно кажучи, я не думаю, що приклади означатимуть багато для вас, поки ви не матимете досвіду щось написати, а потім побачите ефект, який змінюють структури даних на ваш код. Спробуйте написати щось, що вас цікавить, і з’ясуйте, які структури даних та контейнери найкраще підходять, або подивіться якийсь існуючий код і подумайте, чому кожен контейнер був обраний, і який ефект змінив би його.
Марно

Я просто не думаю, що неможливо корисно повідомляти про компроміси та побічні ефекти вибору структури даних для (недосвідченої) ОП без набагато більш тривалого прикладу, ніж тут розумно. Приклади, наведені як відповіді, чудові і відповідають на запитання так, як їх задавали, але не (те, що я читав як), мається на увазі запит про фактичне розуміння.
Марно

Відповіді:


37

Ось щось середнє між прикладом і аналогією. У вас є кілька доручень, тому ви берете лист паперу і пишете:

  • банк
  • бакалії
  • скинути хімчистку

Тоді ви пам'ятаєте, що вам також потрібно купувати марки. Через географію вашого міста вам потрібно зробити це після банку. Ви можете скопіювати весь список на новий аркуш паперу:

  • банк
  • марок
  • бакалії
  • скинути хімчистку

або ви можете писати на тому, що у вас було:

  • банк ....... ШТАМПИ
  • бакалії
  • скинути хімчистку

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

Тоді твій мобільний телефон дзвонить, поки ти в банку "Ей, я отримав марки, більше не бери". Ви просто перекреслюєте STAMPS зі списку, ви не переписуєте зовсім нового без STAMPS у ньому.

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



@Dennis так, я знаю, завдяки семантиці рухатися. Однак це не стосується всіх мов, тому приклад стоїть.
Кейт Григорій

1
@KateGregory досить справедливий, але все-таки добре зазначити, що "базові" структури даних часто кращі, ніж круті структури даних, про які ми дізнаємося в школі (або деінде). Я не хочу, щоб нові міста використовували LL всюди, тому що це теоретично доцільно, хоча, можливо, є реальним вибором. Використання правильної структури даних є важливим, і іноді люди надмірно ускладнюють її. Трохи спорідненою є ця розмова Джонатана Блоу (творця "Braid") про структури даних .
Денніс

1
@Ray: Зв'язаний список може перевершити структуру дерева, якщо ви очікуєте, що там буде порядок 4 елементів, а порівняння порівняно дешеві. Я точно не знаю, де відбувається відсічка, де це не так. Також структура дерева вимагає сортування ваших даних, тоді як структура списку дозволяє підтримувати порядок вставки (або будь-який довільний порядок).
Девід Стоун

2
Я не думаю, що ця аналогія є дуже точною. Як люди, ми можемо (принаймні для такого короткого списку) знайти елемент в O (1). Але якщо ви хочете зробити випадкову вставку у зв'язаному списку, вам доведеться пройти половину елементів в середньому, що коштує вам O (n), тільки щоб знайти позицію, куди ви хочете вставити. Фактична вставка тоді коштує лише O (1), але з масивом ваша вартість також "лише" O (n). Тож причина пов’язана не лише з семантикою переміщення, але і з алгоритмічністю. Постійний фактор, прихований великим O, набагато більший для списку подобається, однак через неперервний доступ до пам'яті.
5gon12eder

18
  • Хеш- таблиці, які використовують ланцюжок для вирішення хеш-зіткнень, як правило, мають один пов'язаний список на відро для елементів у цьому відрі.
  • Прості алокатори пам'яті використовують вільний список невикористаних областей пам'яті, в основному пов'язаний список із вказівником списку всередині самої вільної пам'яті
  • У файловій системі FAT метадані великого файлу організовані як пов'язаний список записів FAT.

12

"Стек викликів" на мові C реалізований як пов'язаний список у бінарних API-кодах x86 (та більшості інших).

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

CALLІнструкція x86 закінчується implmenting пов'язаного списку , використовуючи «стек викликів». CALLІнструкція штовхає вміст регістра EIP%, адреса команди після в CALLна пам'ять стека. Пролог виклику-виклику висуває вміст регістра% EBP, найнижчу адресу локальних змінних у функціонуванні виклику, у пам'ять стека. Тоді викликаний пролог функції встановлює% EBP на базу стека поточної функції.

Це означає, що% EBP - вказівник на місце пам'яті, яке містить адресу функції виклику% EBP. Це не що інше, як пов'язаний список, частково реалізований в апаратному режимі через CALL.

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


3

Зв'язаний список можна використовувати для реалізації черги повідомлень.

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

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

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

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