medium

Sort List

Sort a linked list in ascending order.

1. Define the problem

Sort List

Given the head of a singly linked list , return the list after sorting it in ascending order. Use merge sort : find the middle node with fast and slow pointers , sort each half recursively, then merge the two sorted halves by relinking nodes instead of writing into an array.

Constraints

  • The number of nodes in the list is in the range [0, 5 × 104]
  • -105 ≤ Node.val ≤ 105

Example

Inputhead = [4, 2, 1, 3]
Output[1, 2, 3, 4]

Explanation Splitting the list in half at the middle node and merging the sorted halves back together produces [1, 2, 3, 4].

2. Know the words first

In plain terms

Linked list
A chain of items where each item points to the next one, like a scavenger hunt where each clue tells you where to find the next.
3. Visualize the solution

Cut at the middle, sort each half, merge by relinking

Cut at the middle, sort each half, merge by relinking
Statussplit

Find the middle node and cut the list into two halves: [4, 2] and [1, 3].

What happens in this step

list: 4 → 2 → 1 → 3

fast/slow pointers: slow advances to the node holding 1, with prev trailing at the node holding 2 — prev.next is cut to null there.
  left  half: 4 → 2
  right half: 1 → 3

Each half still has more than one node, so both recurse further before merging.
Step 1 of 5

Steps to visualize

  1. Use fast and slow pointers to find the middle node, then cut the list into two halves there.
  2. Recursively sort each half the same way.
  3. Merge the two sorted halves by relinking nodes — the same two pointer walk used to merge two sorted arrays, just following next pointers instead of array indices.
  4. The recursion bottoms out at a single node or an empty list, which are already sorted.
4. Walk through the code

Walk through the code

Same walkthrough, now with the code. Press Next to move one step and watch which lines run.

Cut at the middle, sort each half, merge by relinking
Statussplit

Find the middle node and cut the list into two halves: [4, 2] and [1, 3].

What happens in this step

list: 4 → 2 → 1 → 3

fast/slow pointers: slow advances to the node holding 1, with prev trailing at the node holding 2 — prev.next is cut to null there.
  left  half: 4 → 2
  right half: 1 → 3

Each half still has more than one node, so both recurse further before merging.
Step 1 of 5
5. Solution

Solution

solution.tsTypeScript
function sortList(values) {
  function buildList(arr) {
    const dummy = { val: 0, next: null };
    let curr = dummy;
    for (const v of arr) {
      curr.next = { val: v, next: null };
      curr = curr.next;
    }
    return dummy.next;
  }

  function toArray(head) {
    const result = [];
    let node = head;
    while (node) {
      result.push(node.val);
      node = node.next;
    }
    return result;
  }

  function getMiddle(head) {
    let slow = head;
    let fast = head;
    let prev = null;
    while (fast && fast.next) {
      prev = slow;
      slow = slow.next;
      fast = fast.next.next;
    }
    if (prev) prev.next = null; // cut the list into two halves
    return slow;
  }

  function mergeLists(a, b) {
    const dummy = { val: 0, next: null };
    let tail = dummy;
    while (a && b) {
      if (a.val <= b.val) {
        tail.next = a;
        a = a.next;
      } else {
        tail.next = b;
        b = b.next;
      }
      tail = tail.next;
    }
    tail.next = a || b;
    return dummy.next;
  }

  function sort(head) {
    if (!head || !head.next) return head;

    const middle = getMiddle(head);
    const left = sort(head);
    const right = sort(middle);

    return mergeLists(left, right);
  }

  return toArray(sort(buildList(values)));
}
Time
O(n log n)
Space
O(log n)
6. Test cases

Test cases

InputExpectedCovers
head = [4, 2, 1, 3][1, 2, 3, 4]example from the docstring
head = [][]empty list
head = [5][5]smallest valid input, a single node
head = [1, 2, 3][1, 2, 3]already sorted list
head = [3, 2, 1][1, 2, 3]fully descending list
head = [2, 2, 1][1, 2, 2]repeated values
head = [-1, -3, 2][-3, -1, 2]mix of negative and non-negative values
head = [2, 1][1, 2]boundary case, exactly two nodes

Keep reading

TopicDescription
Merge SortSplit the list in half, sort each half, then merge them back together in order, giving reliable O(n log n) time.
Merge k Sorted ListsMerge a set of sorted linked lists into a single sorted linked list.
Merge Two Sorted ArraysMerge two sorted arrays into one sorted array.
Sort an Array Using Merge SortSort an array of integers using the merge sort algorithm.
Count InversionsCount out-of-order pairs in an array using a merge-sort-based inversion count.
Merge IntervalsMerge every pair of overlapping intervals into one combined interval.