Insert Greatest Common Divisors in Linked List
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
- Traverse the linked list and collect all node values into an array
arr - Create a dummy head node for the result list
- For each index
ifrom 0 to len(arr) - 2:
a. Create a node with valuearr[i]and append to result
b. Computegcd_val= GCD(arr[i], arr[i+1])
c. Create a node with valuegcd_valand append to result - Create a node with value
arr[len(arr) - 1](last element) and append to result - 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.nextimport 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:
-
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.
-
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
- Handle edge case: if head is null or has no next node, return head as-is
- Initialize two pointers:
prev= head,curr= head.next - While
curris not null:
a. Computegcd_val= GCD(prev.val, curr.val)
b. Create a new node with valuegcd_val
c. Set the new node's next pointer tocurr
d. Setprev.nextto the new node (this inserts it between prev and curr)
e. Moveprevtocurr(skip over the inserted node)
f. Movecurrtocurr.next - 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 headclass 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.