Skip to content

sf-chorus-frogs-2016/data-structures-linked-list-challenge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Linked Lists

Linked Lists are one approach to producing a dynamically sized collection of elements. In a Linked List, a collection of elements is represented as a chain of Nodes.

The most basic Node has two things:

  • A value (also known as an element)
  • A pointer to another Node

Linked List

In other words, Linked Lists are lists that are composed of many Nodes each referencing the next.

Why is this important?

Linked Lists are useful when you need a dynamically sized collection. Unlike a fixed-array, you can expand a Linked List by simple pointing its last Node to a new Node.

Linked Lists are also efficient when inserting nodes in the middle of a collection. We'll see why that is as we build out our LinkedList implementation.

Restrictions

In the following releases do not use Ruby's built in data structures, including Arrays, Hashes or Sets in your implementation. The only structures you should use are objects from classes you define. You may also use the FixedArray class if you see a use.

The elements (values) you store in your list may be any kind of object, because they do not affect the implementation.

Release 0: Implement Node

Since Linked Lists are made up of Nodes, let's start by creating a Node class.

Implement and write RSpec tests for the Node class, supporting the following interface:

Interface

  • Node#new(element): Instantiate a new node containing element
  • Node#insert_after(other_node): Insert other_node after this node. In other words, other_node becomes the node that this instance points to.
  • Node#remove_after(): Remove the node that this node points to (aka the node "after" this node)

Release 1: Implement LinkedList

Now that you've implemented and tested Node let's build up our LinkedList class.

Implement and write RSpec tests for the LinkedList class, supporting the following interface.

Interface

  • LinkedList#new: Instantiate a new linked list
  • LinkedList#insert_first(element): Insert an element at the front of the list
  • LinkedList#remove_first: Remove the element at the front of the list or nil if absent
  • LinkedList#insert_last(element): Insert an element at the back of the list LinkedList#remove_last: Remove the element at the back of the list or nil if absent

Release 2: More methods

Now you have a basic LinkedList class implemented. Let's expand it to make it more useful as a collection.

Implement and write RSpec tests for the LinkedList class, expanding to support the following interface.

Interface

  • LinkedList#get(index): Get the element in the list at index
  • LinkedList#set(index, element): Set the element in the list at index LinkedList#size: Return the size of the list

Release 3: Complexity

By now you have the following methods on your LinkedList class:

  • LinkedList#new
  • LinkedList#insert_first(element)
  • LinkedList#remove_first(element)
  • LinkedList#insert_last(element)
  • LinkedList#remove_last(element)
  • LinkedList#get(index)
  • LinkedList#set(index, element)
  • LinkedList#size

For each of these methods, determine the big-O complexity of the method. Create a file complexity.md and write the big-O for each method, explaining why. How do these methods compare to similar methods in your Resizable Array class?

For example, LinkedList#new is O(1) — whether our list ends up containing 0 elemnets or 1000, #new will always take the same amount of time.

After you have figured out the big-O for each method, answer the following question in complexity.md:

  • Why is inserting a value in the middle of the collection faster with a LinkedList than it is with an Array?

Release Interlude: Harder, Better, Faster, Stronger

After the last release, you should have a sense of which methods in your Linked List implementation are fast and which are slow.

Let's revisit our implementation and speed it up.

Release 4: Remove... faster

In this release, ensure that remove_first and remove_last run in constant (O(1)) time. Remember, that means the remove_* methods should run just as fast whether it's a list of 2 nodes or 1000 nodes.

Think about what state the list needs to keep track of to accomplish this. What design trade offs must you make to achieve this result?

Since you've already written tests for these methods, ensure that they still run. You're using your tests as a safety net in this refactor.

Release 5: Revisit size

Change the implementation of you class to ensure that the size method runs in constant time as well. What did you have to do to make

Release 6: Reflect on Refactoring

What changes did you need to make to complete releases 4 and 5? What are the downsides to the change you made? Add your thoughts to complexity.md.

Releases

No releases published

Packages

No packages published

Languages