AniUI Academy

A doubly linked list you can walk both ways

hardData Structures+350 XPPractice

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() and toArrayReverse() are always exact reverses of each other.
  • insertAt returns false for a negative index or one past the end, and true when it inserts.
  • removeAt returns undefined for an out-of-range index.
  • Emptying the list from either end leaves it usable again.
  • size() stays accurate after every operation.

Notes

  • pushFront, pushBack, popFront and popBack are O(1). insertAt and removeAt are 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.

Try it yourself
Loading playground...
5 tests · +350 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.