Для меня совершенно очевидно, что когда мы хотим удалить узел в связанном списке (будь то двойной или односвязный), и нам нужно искать этот узел, временная сложность для этой задачи O (n), поскольку мы должны пройти весь список в худшем случае, чтобы идентифицировать узел. Аналогично, это O (k), если мы хотим удалить k-й узел, и у нас уже нет ссылки на этот узел.
Обычно упоминается, что одним из преимуществ использования двусвязного списка над односвязным списком является то, что удаление - это O (1), когда у нас есть ссылка на узел, который мы хотим удалить. Т.е., если вы хотите удалить узел i, просто сделайте следующее:
i.prev.next = i.next и i.next.prev = i.prev
Говорят, что удаление - это O (1) в ТОЛЬКО связанном списке, только если у вас есть ссылка на узел перед тем, который вы хотите удалить. Тем не менее, я не думаю, что это обязательно так. Если вы хотите удалить узел i (и у вас есть ссылка на узел i), почему вы не можете просто скопировать данные из i.next и установить i.next = i.next.next? Это также будет O (1), как в случае двусвязного списка, что означает, что удаление не является более эффективным в двусвязном списке в ЛЮБОМ случае, если речь идет о Big-O. Конечно, эта идея не будет работать, если узел, который вы пытаетесь удалить, является последним узлом в связанном списке.
Меня действительно беспокоит то, что никто не помнит этого, сравнивая одиночные и двусвязные списки. Чего мне не хватает?
Чтобы уточнить : в случае односвязного случая я предлагаю перезаписать данные на узле, который вы хотите удалить , данными из следующего узла, а затем удаляя следующий узел. Это имеет тот же желаемый эффект, что и удаление узла i
, хотя это не то, что вы делаете как таковое.
EDIT
Что я выучил:
Так что, похоже, я в некоторой степени прав. Прежде всего, многие люди отметили, что мое решение не является полным, так как удаление последнего элемента является проблемой, поэтому мой алгоритм O (n) (по определению Big-O). В ответ я наивно предложил обойти это, отслеживая «второй-последний узел» в вашем списке - конечно, это вызывает проблемы, как только последний узел в вашем списке был удален в первый раз. Решение, которое было предложено и, похоже, работает, состоит в том, чтобы разграничить конец вашего списка чем-то вроде NullNode, и мне нравится этот подход.
Другими проблемами, которые были представлены, были ссылочная целостность и время, связанное с копированием самих данных со следующего узла (то есть, предположительно, может потребоваться дорогостоящее глубокое копирование). Если вы можете предположить, что у вас нет других объектов, использующих копируемый узел, и что задача копирования - это O (1) сама по себе, то, похоже, мое решение работает. Хотя на данный момент, может быть, стоит использовать двусвязный список:)