Связный список (Linked List) — это структура данных, состоящая из узлов (nodes), где каждый узел содержит данные и ссылку на следующий узел. В отличие от массивов, элементы связного списка не хранятся в contiguous memory, а связаны через указатели.
Каждый узел содержит:
value
— хранимые данные,next
— ссылка на следующий узел (или nil
, если это последний элемент).class Node
attr_accessor :value, :next
def initialize(value)
@value = value
@next = nil
end
end
Реализуем основные операции:
#append
)#prepend
)#find
)#delete
)class LinkedList
attr_reader :head
def initialize
@head = nil
end
# Добавление в конец (O(n))
def append(value)
new_node = Node.new(value)
if @head.nil?
@head = new_node
else
current = @head
current = current.next while current.next
current.next = new_node
end
end
# Добавление в начало (O(1))
def prepend(value)
new_node = Node.new(value)
new_node.next = @head
@head = new_node
end
# Поиск по значению (O(n))
def find(value)
current = @head
while current
return current if current.value == value
current = current.next
end
nil
end
# Удаление узла (O(n))
def delete(value)
return if @head.nil?
if @head.value == value
@head = @head.next
return
end
current = @head
while current.next
if current.next.value == value
current.next = current.next.next
return
end
current = current.next
end
end
# Вывод списка (для отладки)
def to_s
elements = []
current = @head
while current
elements << current.value
current = current.next
end
elements.join(" -> ")
end
end
list = LinkedList.new
list.append(10)
list.append(20)
list.prepend(5)
puts list.to_s #=> "5 -> 10 -> 20"
list.delete(10)
puts list.to_s #=> "5 -> 20"
append
работал за O(1).prev
, получится двунаправленный список (удобно для удаления элементов).Операция | Связный список | Массив |
---|---|---|
Вставка в начало | O(1) | O(n) |
Вставка в конец | O(n) (O(1) с tail) | O(1)* |
Удаление элемента | O(n) | O(n) |
Доступ по индексу | O(n) | O(1) |
*В Ruby массивы динамические, амортизированная сложность O(1).
Резюмируем: связный список эффективен для частых вставок/удалений в начале, но не подходит для задач с частым доступом по индексу. В Ruby он редко используется напрямую, но понимание его устройства важно для изучения структур данных.