Skip to content

Instantly share code, notes, and snippets.

@creature
Last active June 10, 2020 07:52
Show Gist options
  • Save creature/8520844 to your computer and use it in GitHub Desktop.
Save creature/8520844 to your computer and use it in GitHub Desktop.
A Ruby demo of a linked list, transformed into a doubly-linked list and some performance improvements.
class Node
attr_accessor :value
attr_accessor :next, :previous
def initialize(value)
@value = value
@next = nil
@previous = nil
end
end
class LinkedList
attr_reader :traversal_count, :length
def initialize
@head = nil
@tail = nil
@traversal_count = 0
@length = 0
end
def <<(value)
new_node = Node.new(value)
new_node.next = @head
@head.previous = new_node if @head
@head = new_node
@tail ||= @head
@length += 1
end
def [](index)
node = self.get_node_at(index)
node.value if node
end
def []=(index, value)
if index > self.length
raise RuntimeError
else
self.get_node_at(index).value = value
end
end
def get_node_at(index)
unless index.is_a? Fixnum
raise "Not a number!"
end
if index > self.length
nil
else
if index < @length / 2 # Asking for an older element, which is closer to the tail
jumps = index
node = @tail
method = :previous
else # It's closer to the head.
jumps = @length - index - 1
node = @head
method = :next
end
@traversal_count += jumps
jumps.times { node = node.__send__(method) }
node
end
end
def show
output = ""
node = @head
while node
output += "#{node.value}"
output += " -> " if node.next
node = node.next
@traversal_count += 1
end
output
end
def delete(value_to_delete)
if @head.value == value_to_delete
@head = @head.next
@length -= 1
else
previous_node = @head
current_node = @head.next
while current_node
if current_node.value == value_to_delete
previous_node.next = current_node.next
@length -= 1
else
previous_node = current_node
end
current_node = current_node.next
@traversal_count += 1
end
end
end
end
require 'test/unit'
class TestLinkedList < Test::Unit::TestCase
def setup
@list = LinkedList.new
@words = %w{foo bar baz rectangle america megaphone monday}
end
def teardown
puts " During this test we performed #{@list.traversal_count} traversals.\n"
end
def test_add_method
count = 0
@words.each do |w|
@list << w
count += 1
assert_equal @list.length, count
end
end
def test_array_indices
@words.each { |w| @list << w }
assert_nil @list[100]
for i in (0...@words.length)
assert_equal @words[i], @list[i]
end
end
def test_indices_assignment
@words.each { |w| @list << w}
assert_equal @words[3], @list[3]
@list[3] = "new word"
assert_equal "new word", @list[3]
for i in (0...@words.length)
unless i == 3
assert_equal @words[i], @list[i]
end
end
end
def test_bigger_lists
(0..1000).each { |i| @list << i}
(0..1000).each { |i| assert_equal i, @list[i] }
numbers = (0..1000).to_a.shuffle
numbers.each { |i| assert_equal i, @list[i] }
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment