What is a Doubly Linked List?
The main difference between doubly and a singly linked list is that in doubly linked list, a node not only has a pointer for the next node, but also the one that precedes it. This makes the list traversable in either direction.
head tail
nil <-- [node|value] <-> [node|value] <-> [node|value] --> nil
Since the basic structure are the same between single and doubly linked list, they share majority of the methods such as insert
, remove
, cat
, etc…
Although they share same methods, the implementation will change slightly since doubly linked list can link to the preceding node as well. Here are couple methods where implementation will change:
insert(item)
pop_back
pop_front
remove(item)
cat(list)
- catenate two lists together
def insert item
new_node = Node.new(item)
if @head == nil
@head = new_node
@tail = new_node
else
new_node.prev = @tail
@tail.next = new_node
@tail = @tail.next
end
@length += 1
end
def pop_back
return nil unless @tail
ret = nil
if @head == @tail
ret = @head.data
@head = nil
@tail = nil
else
ret = @tail.data
@tail = @tail.prev
@tail.next = nil
end
@length -= 1
ret
end
def pop_front
return nil unless @head
ret = nil
if @head == @tail
ret = @head.data
@head = nil
@tail = nil
else
ret = @head.data
@head = @head.next
@head.prev = nil
end
@length -= 1
ret
end
def remove item
return nil unless head
return pop_front if @head.data == item
return pop_back if @tail.data == item
curr = @head
while curr
if curr.data == item
curr.prev.next = curr.next
curr.next.prev = curr.prev
curr.prev = nil
curr.next = nil
@length -= 1
break
end
curr = curr.next
end
end
def cat list
if @head == nil
@head = list.head
@tail = list.tail
else
list.head.prev = @tail
@tail.next = list.head
@tail = list.tail
end
@length += list.length
list = nil
end
Since doubly linked list can traverse backward, we can utilize this to implement more useful methods:
find_last(&predicate)
- get the last element that satisfies a given predicatereverse_each
- loops over the list backward yielding one element at a timedisplay(reverse=false)
- print the contents in reverse order ifreverse
istrue
def find_last &predicate
return nil unless block_given?
curr = @tail
while curr
return curr if predicate.call(curr)
curr = curr.prev
end
end
def reverse_each
return nil unless block_given?
curr = @tail
while curr
yield curr
curr = curr.prev
end
end
def display rev=false
return nil unless @length > 0
if rev
reverse_each {|item| puts item.data }
else
each {|item| puts item.data }
end
end