Научитесь реализовывать одну из самых важных структур данных в Python.

Связанные списки, как и массивы, представляют собой структуры данных, которые используются для хранения множества элементов. Но в отличие от массивов связанные списки не хранятся в непрерывном месте в памяти. Вместо этого они используют узлы, которые могут храниться в любом месте памяти, но связаны друг с другом с помощью указателей. Каждый узел содержит ссылку на следующий узел в связанном списке.

Class node:
   def __init__(self,data) -> None:
       self.data = data
       self.next_node = None

Создание связанного списка

Чтобы создать связанный список, все, что нам нужно знать, это первый узел, поскольку каждый узел в связанном списке указывает на следующий, а последний узел указывает на ноль, указывая на конец связанного списка.

Class linked_list :
    def __init__(self,first_node = None) -> None:
          self. first_node = first_node
node1 = node('first')
node2 = node('second')
node3 = node('third')
linked_list1= linked_list(node1)
node1.next_node = node2
node2.next_node = node3

Вот как вы создаете простой связанный список, содержащий 3 элемента, не так ли? хорошо, может быть, это не так просто, как создание массива, но, как вы узнаете позже, это может быть очень полезно и стоит вашего времени.

Обход связанного списка

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

def read(self,index):
   current_node = self.first_node
   current_index = 0
   while current_index < index:
      current_node = current_node.next_node
      current_index += 1
      if current_node == None :
          return None
   return current_node

Функция чтения — это форма обхода, при которой мы возвращаем узел в заданном индексе. Эта форма обхода полезна в других операциях.

Поиск элемента в связанном списке

При проверке того, находится ли элемент в связанном списке, мы должны просмотреть каждый элемент в связанном списке и сравнить его со значением, которое мы ищем.

def search(self,data):
    current_node = self.first_node
    current_index = 0
    while current_node:
        if current_node.data == data:
            return current_index
        else:
            current_node = current_node.next_node
            current_index += 1
    return None

Вставка элементов в связанный список

Помните немного о том, что массивы хранятся в непрерывном месте? хорошо, это работает хорошо, пока нам не нужно вставить элемент в массив. Допустим, мы хотим вставить x в 3-й индекс массива из 20 элементов, нам нужно будет сдвинуть каждый элемент из 3-го элемента на один пробел вперед, что не очень быстро. Вставка в массив выполняется быстрее всего в конце и медленнее всего в начале, а для связанного списка дело обстоит наоборот. Фактически, для вставки в начало связанного списка требуется всего один шаг, что с точки зрения скорости настолько быстро, насколько это возможно.

Вставка в связанные списки выполняется путем изменения next_node нового узла на узел после заданного индекса и next_node предыдущего узла на новый узел.

def insert(self,value,index):
    previous_node = self.read(index-1) 
    next_node = previous_node.next_node
    current_node = node(value,next_node)
    previous_node.next_node = current_node

Для вставки в начале требуется только изменить first_node на новый узел и связать его с предыдущим first_node.

def insert_at_beginning(self,value):
    old_node = self.first_node
    self.first_node = node(value)
    self.first_node.next_node = old_node

Удаление элементов в связанном списке

Как и при вставке, удаление элемента выполняется путем изменения связей между узлами.

def delete(self,index):
    node_before = self.read(index-1)
    current_node = node_before.next_node
    node_after = current_node.next_node
    node_before.next_node = node_after

В приведенном выше примере мы пытаемся удалить узел с заданным индексом, получая узел до и после него, а не связывая их друг с другом.

Чтобы удалить в начале, все, что нам нужно сделать, это изменить first_node на его next_node.

def delete_at_beginning(self,index):
    self.first_node = self.first_node.next_node

Зачем использовать связанный список?

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

Важно научиться использовать преимущества уникальных сильных и слабых сторон каждой структуры данных при попытке решить проблемы или подготовиться к интервью.

Спасибо за прочтение. Я надеюсь, что это было полезно. Дайте мне знать в комментариях, если у вас есть какие-либо вопросы или комментарии, чтобы добавить. Я буду ждать вашего ответа.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.