Skip to main content

Insert Greatest Common Divisors in Linked List

MEDIUMProblemSolveExternal Links

Description

You are given the head of a linked list where each node contains an integer value.

Your task is to modify the linked list by inserting a new node between every pair of adjacent nodes. The value of each new node should be the greatest common divisor (GCD) of the two adjacent nodes it sits between.

The greatest common divisor of two numbers is the largest positive integer that evenly divides both numbers. For example, the GCD of 18 and 6 is 6, because 6 is the largest number that divides both 18 and 6 without leaving a remainder.

Return the linked list after all insertions are complete.

Examples

Example 1

Input: head = [18, 6, 10, 3]

Output: [18, 6, 6, 2, 10, 1, 3]

Explanation:

  • Between 18 and 6: GCD(18, 6) = 6. Insert a node with value 6.
  • Between 6 and 10: GCD(6, 10) = 2. Insert a node with value 2.
  • Between 10 and 3: GCD(10, 3) = 1. Insert a node with value 1.

The modified list is: 18 → 6 → 6 → 2 → 10 → 1 → 3 (bold nodes are the newly inserted ones).

Example 2

Input: head = [7]

Output: [7]

Explanation: The linked list has only one node. There are no pairs of adjacent nodes, so no insertions are needed. The list remains unchanged.

Example 3

Input: head = [12, 8, 6]

Output: [12, 4, 8, 2, 6]

Explanation:

  • Between 12 and 8: GCD(12, 8) = 4. The largest number dividing both 12 and 8 is 4.
  • Between 8 and 6: GCD(8, 6) = 2. The largest number dividing both 8 and 6 is 2.

The modified list is: 12 → 4 → 8 → 2 → 6.

Constraints

  • The number of nodes in the list is in the range [1, 5000]
  • 1 ≤ Node.val ≤ 1000

Editorial

Brute Force

Intuition

The simplest way to approach this problem is to separate the thinking into two phases: first understand the data, then build the result.

Imagine you have a chain of numbered beads. You want to add a new bead between every pair of neighbors, where the new bead's number is the GCD of its two neighbors. The most straightforward method: pick up all the beads, lay them out on a table (an array), figure out all the GCD values, then string them back together with the new beads included.

Concretely, we first traverse the linked list and collect all values into an array. Then we build a brand-new linked list: for each consecutive pair in the array, we create the original node followed by a GCD node, and finally append the last original node.

Step-by-Step Explanation

Let's trace through with head = [18, 6, 10, 3]:

Step 1: Traverse the linked list and collect values into an array.

  • Visit node 18 → arr = [18]
  • Visit node 6 → arr = [18, 6]
  • Visit node 10 → arr = [18, 6, 10]
  • Visit node 3 → arr = [18, 6, 10, 3]

Step 2: Process pair (18, 6).

  • Compute GCD(18, 6) = 6
  • Add to result list: 18 → 6

Step 3: Process pair (6, 10).

  • Compute GCD(6, 10) = 2
  • Add to result list: 18 → 6 → 6 → 2

Step 4: Process pair (10, 3).

  • Compute GCD(10, 3) = 1
  • Add to result list: 18 → 6 → 6 → 2 → 10 → 1

Step 5: Append the last original value.

  • Add to result list: 18 → 6 → 6 → 2 → 10 → 1 → 3

Step 6: Return the head of the new list.

Brute Force — Collect Values, Compute GCDs, Rebuild — Watch how we first collect all node values into an array, then rebuild the linked list with GCD nodes inserted between each original pair.

Algorithm

  1. Traverse the linked list and collect all node values into an array arr
  2. Create a dummy head node for the result list
  3. For each index i from 0 to len(arr) - 2:
    a. Create a node with value arr[i] and append to result
    b. Compute gcd_val = GCD(arr[i], arr[i+1])
    c. Create a node with value gcd_val and append to result
  4. Create a node with value arr[len(arr) - 1] (last element) and append to result
  5. Return the result list starting from the node after dummy

Code

#include <vector>
#include <algorithm>
using namespace std;

class Solution {
public:
    ListNode* insertGreatestCommonDivisors(ListNode* head) {
        // Step 1: Collect all values into an array
        vector<int> arr;
        ListNode* curr = head;
        while (curr) {
            arr.push_back(curr->val);
            curr = curr->next;
        }
        
        // Edge case: single node
        if (arr.size() <= 1) return head;
        
        // Step 2: Build new list with GCD nodes
        ListNode dummy(0);
        ListNode* tail = &dummy;
        
        for (int i = 0; i < (int)arr.size() - 1; i++) {
            tail->next = new ListNode(arr[i]);
            tail = tail->next;
            
            int gcdVal = __gcd(arr[i], arr[i + 1]);
            tail->next = new ListNode(gcdVal);
            tail = tail->next;
        }
        
        // Append last original node
        tail->next = new ListNode(arr.back());
        
        return dummy.next;
    }
};
from math import gcd
from typing import Optional

class Solution:
    def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # Step 1: Collect all values into an array
        arr = []
        curr = head
        while curr:
            arr.append(curr.val)
            curr = curr.next
        
        # Edge case: single node
        if len(arr) <= 1:
            return head
        
        # Step 2: Build new list with GCD nodes
        dummy = ListNode(0)
        tail = dummy
        
        for i in range(len(arr) - 1):
            tail.next = ListNode(arr[i])
            tail = tail.next
            
            gcd_val = gcd(arr[i], arr[i + 1])
            tail.next = ListNode(gcd_val)
            tail = tail.next
        
        # Append last original node
        tail.next = ListNode(arr[-1])
        
        return dummy.next
import java.util.ArrayList;
import java.util.List;

class Solution {
    public ListNode insertGreatestCommonDivisors(ListNode head) {
        // Step 1: Collect all values into an array
        List<Integer> arr = new ArrayList<>();
        ListNode curr = head;
        while (curr != null) {
            arr.add(curr.val);
            curr = curr.next;
        }
        
        // Edge case: single node
        if (arr.size() <= 1) return head;
        
        // Step 2: Build new list with GCD nodes
        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;
        
        for (int i = 0; i < arr.size() - 1; i++) {
            tail.next = new ListNode(arr.get(i));
            tail = tail.next;
            
            int gcdVal = gcd(arr.get(i), arr.get(i + 1));
            tail.next = new ListNode(gcdVal);
            tail = tail.next;
        }
        
        // Append last original node
        tail.next = new ListNode(arr.get(arr.size() - 1));
        
        return dummy.next;
    }
    
    private int gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
}

Complexity Analysis

Time Complexity: O(n × log M)

Where n is the number of nodes and M is the maximum node value. We traverse the list once to collect values (O(n)), then iterate through pairs to compute GCDs (n-1 times). Each GCD computation using the Euclidean algorithm takes O(log M). The list construction is O(n). Total: O(n × log M).

Space Complexity: O(n)

We store all node values in an auxiliary array of size n. Additionally, we create an entirely new linked list with (2n - 1) nodes. The array alone uses O(n) extra space beyond the required output.

Why This Approach Is Not Efficient

The brute force works correctly, but it wastes space in two ways:

  1. Unnecessary array storage: We collect all values into an array, but we only ever need two adjacent values at a time. There is no need to store the entire list in memory.

  2. Complete list reconstruction: We build an entirely new linked list from scratch, discarding the original nodes. This creates duplicate nodes for the original values when we could simply modify the existing list in place.

The key insight is that linked list insertion is naturally an O(1) operation — we just need to adjust pointers. We can traverse the original list and insert GCD nodes on the fly without any intermediate data structure.

Optimal Approach - In-Place Two-Pointer Insertion

Intuition

Instead of collecting values and rebuilding, we can modify the linked list in place as we walk through it.

Picture yourself walking along a chain of linked beads. You hold one bead in your left hand (prev) and the next bead in your right hand (curr). You compute the GCD of these two beads' values, create a new bead, and physically clip it into the chain between them. Then you slide your left hand to where your right hand was, and your right hand grabs the next bead. Repeat until there are no more beads to grab.

The crucial detail: after inserting a new node between prev and curr, we must skip over the newly inserted node when moving forward. We advance prev directly to curr (not to prev.next, which now points to the GCD node). This ensures we only process original adjacent pairs, never accidentally computing GCD between an original node and an inserted node.

Step-by-Step Explanation

Let's trace through with head = [18, 6, 10, 3]:

Step 1: Initialize pointers.

  • prev = node(18), curr = node(6)

Step 2: Process pair (18, 6).

  • Compute GCD(18, 6): 18 = 6×3 + 0, so GCD = 6
  • Create new node with value 6
  • Set prev.next → newNode(6) → curr
  • List is now: 18 → 6 → 6 → 10 → 3

Step 3: Advance pointers.

  • prev moves to curr (node 6, the original one)
  • curr moves to curr.next (node 10)
  • prev = node(6), curr = node(10)

Step 4: Process pair (6, 10).

  • Compute GCD(6, 10): 10 = 6×1 + 4, 6 = 4×1 + 2, 4 = 2×2 + 0, so GCD = 2
  • Create new node with value 2
  • Set prev.next → newNode(2) → curr
  • List is now: 18 → 6 → 6 → 2 → 10 → 3

Step 5: Advance pointers.

  • prev = node(10), curr = node(3)

Step 6: Process pair (10, 3).

  • Compute GCD(10, 3): 10 = 3×3 + 1, 3 = 1×3 + 0, so GCD = 1
  • Create new node with value 1
  • Set prev.next → newNode(1) → curr
  • List is now: 18 → 6 → 6 → 2 → 10 → 1 → 3

Step 7: Advance pointers.

  • prev = node(3), curr = null
  • curr is null, so we stop.

Step 8: Return head → 18 → 6 → 6 → 2 → 10 → 1 → 3.

In-Place Two-Pointer Insertion of GCD Nodes — Watch how two pointers traverse the original list, inserting GCD nodes between each adjacent pair without any auxiliary data structure.

Algorithm

  1. Handle edge case: if head is null or has no next node, return head as-is
  2. Initialize two pointers: prev = head, curr = head.next
  3. While curr is not null:
    a. Compute gcd_val = GCD(prev.val, curr.val)
    b. Create a new node with value gcd_val
    c. Set the new node's next pointer to curr
    d. Set prev.next to the new node (this inserts it between prev and curr)
    e. Move prev to curr (skip over the inserted node)
    f. Move curr to curr.next
  4. Return head

Code

class Solution {
public:
    ListNode* insertGreatestCommonDivisors(ListNode* head) {
        // Edge case: empty list or single node
        if (!head || !head->next) return head;
        
        ListNode* prev = head;
        ListNode* curr = head->next;
        
        while (curr) {
            // Compute GCD of adjacent pair
            int gcdVal = __gcd(prev->val, curr->val);
            
            // Create new node and insert between prev and curr
            ListNode* newNode = new ListNode(gcdVal, curr);
            prev->next = newNode;
            
            // Advance pointers (skip over newly inserted node)
            prev = curr;
            curr = curr->next;
        }
        
        return head;
    }
};
from math import gcd
from typing import Optional

class Solution:
    def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]:
        # Edge case: empty list or single node
        if not head or not head.next:
            return head
        
        prev = head
        curr = head.next
        
        while curr:
            # Compute GCD of adjacent pair
            gcd_val = gcd(prev.val, curr.val)
            
            # Create new node and insert between prev and curr
            new_node = ListNode(gcd_val, curr)
            prev.next = new_node
            
            # Advance pointers (skip over newly inserted node)
            prev = curr
            curr = curr.next
        
        return head
class Solution {
    public ListNode insertGreatestCommonDivisors(ListNode head) {
        // Edge case: empty list or single node
        if (head == null || head.next == null) return head;
        
        ListNode prev = head;
        ListNode curr = head.next;
        
        while (curr != null) {
            // Compute GCD of adjacent pair
            int gcdVal = gcd(prev.val, curr.val);
            
            // Create new node and insert between prev and curr
            ListNode newNode = new ListNode(gcdVal, curr);
            prev.next = newNode;
            
            // Advance pointers (skip over newly inserted node)
            prev = curr;
            curr = curr.next;
        }
        
        return head;
    }
    
    private int gcd(int a, int b) {
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
}

Complexity Analysis

Time Complexity: O(n × log M)

Where n is the number of nodes in the original list and M is the maximum node value (up to 1000). We visit each of the n nodes exactly once. For each adjacent pair (n-1 pairs), we compute the GCD, which takes O(log M) using the Euclidean algorithm. Node creation and pointer manipulation are O(1) each. Total: O(n × log M).

Space Complexity: O(1) (excluding output)

We use only two pointer variables (prev and curr) regardless of the input size. The newly created GCD nodes are part of the required output, not auxiliary space. If we count the output, it would be O(n) for the n-1 new nodes, but by convention we exclude the output from space analysis. This is a significant improvement over the brute force, which used O(n) extra space for the array.