2012-12-27 3 views
12

Я не понимаю, почему удаление в конце одного связанного списка идет в O (1) раз, как говорит wikipedia article.Почему удаление в одном связанном списке O (1)?

Один связанный список состоит из узлов. Узел содержит какие-то данные и ссылку на следующий узел. Ссылка на последний узел в связанном списке равна NULL.

-------------- --------------   -------------- 
| data | ref | -> | data | ref | -> ... -> | data | ref | 
-------------- --------------   -------------- 

Я действительно могу удалить последний узел в O (1). Но в этом случае вы не устанавливаете ссылку на новый последний, предыдущий, на null, поскольку он все еще содержит ссылку на удаленный последний узел. Поэтому мне было интересно, не игнорируют ли они это в анализе времени выполнения? Или вам нужно, чтобы вам не приходилось менять это, поскольку ссылка, ну, просто указывает на ничего, и такое считается нулевым?

Потому что, если это не будет пренебрегать, я бы сказал, что удаление O (n). Так как вам нужно перебирать весь список, чтобы перейти к новому последнему узлу и установить его ссылку также на null. Только в двойном связанном списке было бы действительно O (1).

-edit- Возможно, эта точка зрения дает еще несколько разъяснений. Но я вижу «удаление узла» как успешное удаление самого узла и установление предыдущей ссылки на нуль.

+2

Я вижу две ссылки на O (1) в этой статье в Википедии, но ни одна из них не заявляет, что удаление последнего узла в односвязном списке является операцией O (1). Предположительно, вы уже удерживаете указатель на предыдущий узел при попытке удаления, требуемый для удаления любого узла, кроме первого. –

ответ

23

Я не уверен, что вижу в статье в Википедии, где говорится, что можно удалить последнюю запись односвязного списка в O (1) времени, но эта информация в большинстве случаев неверна. Учитывая любой отдельный узел в связанном списке, всегда можно удалить узел после него в O (1) раз, переназначив список вокруг этого нового узла. Следовательно, если вы указали указатель на предпоследний узел в связанном списке, вы можете удалить последний элемент списка в O (1) раз.

Однако, если у вас не было никаких дополнительных указателей в списке, кроме указателя на голову, то вы не сможете удалить последний элемент списка без сканирования в конец списка, для чего потребуется Θ (n) времени, как вы заметили. Вы абсолютно правы, что просто удаление последнего узла без предварительного изменения указателей на него было бы очень плохой идеей, так как если бы вы это сделали, существующий список содержал бы указатель на освобожденный объект.

В общем случае, стоимость вставки или удаления в односвязном списке равна O (1), если у вас есть указатель на узел прямо перед тем, который вы хотите вставить или удалить. Однако вам может потребоваться дополнительная работа (до Θ (n)), чтобы найти этот узел.

Надеюсь, это поможет!

+0

Это должен быть одобренный ответ. Гораздо лучшее объяснение. –

6

Добавление/удаление ЛЮБОЙ узла на ЛЮБОЙ месте представляет собой О (1). Код просто играет с фиксированной стоимостью (несколько указателей указателей и malloc/frees) для добавления/удаления узла. Эта арифметическая стоимость фиксируется для любого конкретного случая.

Однако стоимость достижения (индексации) нужного узла равна O (n).

Статья представляет собой просто перечисление добавления/удаления в нескольких подкатегориях (добавление в середине, начале и конце), чтобы показать, что стоимость добавления в середине отличается от добавления в начале/конце (но соответствующие затраты по-прежнему фиксированы).

+0

Я все еще не очень уверен, правильно ли понимаю. Вы видите удаление, просто удалив узел O (1), и не устанавливая ссылку предыдущего узла на нуль, O (n), «стоимость достижения желаемого узла»? Я хочу, чтобы удаление было удалено, удалив сам узел и установив ссылку на предыдущий узел на нуль. –

+0

Я не понимаю, почему вы одобрили этот ответ, это мало объясняет. Если я удаляю узел для удаления и его указатель на следующий узел, у меня остается разбитый список. Я, возможно, разделил свой список пополам. Как это правильное удаление? Если он не принимает delete для удаления следующего узла. Мне нужно объяснение, которое входит в специфику. Индексирование - это O (n), но если удаление требует индексации, оно должно быть частью прецедента. То есть, если я вызываю метод Delete в единственно связанном списке, я должен ожидать O (n). –

0

Например, вы можете иметь указатель на элемент перед последним («второй от конца») и при удалении: 1. Удалить * следующий элемент «второй от конца». 2. Установите этот «второй с конца» * рядом с NULL

+0

За исключением того, что если вы сохраняете указатель на «второй с конца», теперь вам нужно обновить указанный указатель. – James

0

O (1) просто означает «постоянная стоимость». Это не означает 1 операцию. Это означает, что операции «не более C» с C фиксируются независимо от изменения других параметров (например, размера списка). На самом деле, в иногда запутанном мире больших-Oh: O (1) == O (22).

В отличие от удаления всего списка, существует стоимость O (n), поскольку стоимость изменяется с размером (n) списка.

+1

Удаление всего списка - O (1). Опорожнение всего списка - O (n). –

0

Если вы указали стоимость фиксации оборванного узла, вы все равно можете сделать это в O (1) с использованием узла-дозорника для конца (также описанного на этой странице).

Ваш «пустой» список начинается с одного дозорного

Head -> [Sentinel] 

Добавить некоторые вещи

Head -> 1 -> 2 -> 3 -> [Sentinel] 

Теперь удалите хвост (3), пометив узел, который был 3 недействительными, а затем удаление ссылки на старый дозорный сигнал и освобождение памяти для него:

Head -> 1 -> 2 -> 3 -> [Sentinel] 
Head -> 1 -> 2 -> [Sentinel] -> [Sentinel] 
Head -> 1 -> 2 -> [Sentinel] 
+0

Мне это нравится. Это создало бы временную сложность O (1), в то же время создавая космическую торговлю, хотя она будет поддерживать пространственную сложность O (n). Очевидно, что, как вы сказали, время от времени вам нужно будет выполнить операцию O (n) для очистки списка или вы можете запустить проблемы с памятью. –

0

Для дальнейшего использования, я должен сказать: после некоторых исследований я обнаружил, что ни один из аргументов, представленных в ответ на этот вопрос, не имеет отношения к делу. Ответ заключается в том, что мы просто решаем, чтобы верхняя часть стека была главой связанного списка (а не хвоста). Это введет небольшое изменение в рутину push, но тогда поп и толчок оба останутся o (1).

+0

@morgul Пожалуйста, прочтите сообщения, которые вы просматриваете. Это * * ответ. – Rob