Last active
June 10, 2020 07:52
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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