A doubly linked list you can walk both ways
A singly linked list can only be walked forwards, so removing a node means finding its predecessor first. Adding a prev pointer makes removal constant time, which is exactly why caches, playlists and carousels are built on doubly linked lists.
Implement a DoublyLinkedList class. It is constructed with no arguments and starts empty. pushBack(value) and pushFront(value) add a value at that end and each return the new size. popBack() and popFront() remove and return the value at that end, or return undefined when the list is empty. insertAt(index, value) inserts a value so it ends up at that zero-based index, returning true on success; a negative index or one greater than the current size inserts nothing and returns false, while an index equal to the size appends. removeAt(index) removes the node at that index and returns its value, or returns undefined when the index is out of range. size() returns how many values are held. toArray() returns the values front to back and toArrayReverse() returns them back to front, each following the pointers in that direction; both return [] when empty.
Because toArrayReverse() walks the prev pointers, both directions must stay consistent after every operation — this is where a half-updated pointer shows up.
Examples
const l = new DoublyLinkedList();
l.pushBack("b");
l.pushBack("c");
l.pushFront("a");
l.toArray(); // ["a", "b", "c"]
l.toArrayReverse(); // ["c", "b", "a"]const l = new DoublyLinkedList();
l.pushBack(1); l.pushBack(3);
l.insertAt(1, 2); // true
l.insertAt(9, 0); // false — out of range
l.toArray(); // [1, 2, 3]What it has to do
toArray()andtoArrayReverse()are always exact reverses of each other.insertAtreturnsfalsefor a negative index or one past the end, andtruewhen it inserts.removeAtreturnsundefinedfor an out-of-range index.- Emptying the list from either end leaves it usable again.
size()stays accurate after every operation.
Notes
pushFront,pushBack,popFrontandpopBackare O(1).insertAtandremoveAtare O(n) because they still have to walk to the index.- Given a node reference, removal is O(1) — unlink four pointers and the count. That is exactly what an LRU cache leans on to evict in constant time.
- The cases that break implementations are head === tail (one-element list) and the list going empty — handle both explicitly to keep head and tail in sync.
Stuck? The javascript course covers everything this challenge needs.
- 1.reads the same values in both directions
- 2.inserts in the middle and at the end, and rejects bad indices
- 3.removes from the middle and keeps both directions valid
- 4.is usable again after being drained
- 5.an empty list accepts an insert at index 0
These assertions run in your browser the moment you hit Run. Pass all of them to claim the XP.
Discussion
Sign in to postNo comments yet. Be the first to say something.