Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

725. Split Linked List in Parts - Leetcode Solution

Code Implementation

# Definition for singly-linked list.
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def splitListToParts(self, head: ListNode, k: int):
        # Step 1: Find the total length
        length = 0
        curr = head
        while curr:
            length += 1
            curr = curr.next

        # Step 2: Determine the size of each part
        part_size = length // k
        extra = length % k

        # Step 3: Split the list
        res = []
        curr = head
        for i in range(k):
            part_head = curr
            size = part_size + (1 if i < extra else 0)
            for j in range(size - 1):
                if curr:
                    curr = curr.next
            if curr:
                next_part = curr.next
                curr.next = None
                curr = next_part
            res.append(part_head)
        return res
      
// Definition for singly-linked list.
struct ListNode {
    int val;
    ListNode *next;
    ListNode(int x) : val(x), next(nullptr) {}
};

class Solution {
public:
    vector<ListNode*> splitListToParts(ListNode* head, int k) {
        int length = 0;
        ListNode* curr = head;
        while (curr) {
            length++;
            curr = curr->next;
        }

        int part_size = length / k;
        int extra = length % k;

        vector<ListNode*> res;
        curr = head;
        for (int i = 0; i < k; ++i) {
            ListNode* part_head = curr;
            int size = part_size + (i < extra ? 1 : 0);
            for (int j = 0; j < size - 1; ++j) {
                if (curr) curr = curr->next;
            }
            if (curr) {
                ListNode* next_part = curr->next;
                curr->next = nullptr;
                curr = next_part;
            }
            res.push_back(part_head);
        }
        return res;
    }
};
      
/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode[] splitListToParts(ListNode head, int k) {
        int length = 0;
        ListNode curr = head;
        while (curr != null) {
            length++;
            curr = curr.next;
        }
        int part_size = length / k;
        int extra = length % k;
        ListNode[] res = new ListNode[k];
        curr = head;
        for (int i = 0; i < k; ++i) {
            ListNode part_head = curr;
            int size = part_size + (i < extra ? 1 : 0);
            for (int j = 0; j < size - 1; ++j) {
                if (curr != null) curr = curr.next;
            }
            if (curr != null) {
                ListNode next_part = curr.next;
                curr.next = null;
                curr = next_part;
            }
            res[i] = part_head;
        }
        return res;
    }
}
      
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} k
 * @return {ListNode[]}
 */
var splitListToParts = function(head, k) {
    // Step 1: Find total length
    let length = 0, curr = head;
    while (curr) {
        length++;
        curr = curr.next;
    }
    // Step 2: Determine size of each part
    let part_size = Math.floor(length / k);
    let extra = length % k;
    // Step 3: Split the list
    let res = [];
    curr = head;
    for (let i = 0; i < k; ++i) {
        let part_head = curr;
        let size = part_size + (i < extra ? 1 : 0);
        for (let j = 0; j < size - 1; ++j) {
            if (curr) curr = curr.next;
        }
        if (curr) {
            let next_part = curr.next;
            curr.next = null;
            curr = next_part;
        }
        res.push(part_head);
    }
    return res;
};
      

Problem Description

You are given the head of a singly linked list, head, and an integer k. Your task is to split the linked list into k consecutive parts as evenly as possible. If the number of nodes in the list is not divisible by k, then the first few parts should have an extra node.

  • No node should be reused or duplicated between parts; each node appears in exactly one part.
  • The order of nodes within each part must remain the same as in the original list.
  • If the list has fewer than k nodes, then some parts will be null.
  • The function should return an array (or list) of k linked list heads, each representing a part.

Thought Process

To solve this problem, we first need to determine how to split the linked list into k parts as evenly as possible. The main challenge is to ensure that the first few parts are longer if the list size is not perfectly divisible by k. We also need to ensure that we do not reuse or skip any nodes.

A brute-force approach might involve repeatedly traversing the list for each part, but this would be inefficient. Instead, we can make a single pass to count the total number of nodes, then calculate the ideal size for each part. With this information, we can split the list in a single pass, carefully managing pointers to avoid node reuse.

The key insight is to calculate the base size for each part and distribute any extra nodes to the earlier parts. This ensures the most even distribution possible.

Solution Approach

  • Step 1: Count the total number of nodes.
    Traverse the linked list from the head and count the total number of nodes. Let's call this length.
  • Step 2: Calculate part sizes.
    Each part should have at least length // k nodes. The first length % k parts will have one extra node each.
  • Step 3: Split the list.
    For each part:
    • Assign the current node as the head of the part.
    • Move forward part_size + 1 nodes if this part is one of the first extra parts, otherwise part_size nodes.
    • Break the link at the end of the part to separate it from the next part.
    • Repeat for all k parts.
  • Step 4: Handle shorter lists.
    If the list has fewer nodes than k, some parts will be null.

This approach ensures each node appears in exactly one part, and the parts are as equal as possible in size.

Example Walkthrough

Example: Let the input linked list be 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10, and k = 3.

  • Total nodes: 10
  • Base size per part: 10 // 3 = 3
  • Extra nodes to distribute: 10 % 3 = 1
  • Sizes of parts:
    • Part 1: 4 nodes (3 + 1 extra)
    • Part 2: 3 nodes
    • Part 3: 3 nodes
  • Splitting:
    • Part 1: 1 -> 2 -> 3 -> 4
    • Part 2: 5 -> 6 -> 7
    • Part 3: 8 -> 9 -> 10
  • Process:
    1. Start at node 1, move 3 more steps to node 4, break the link after node 4.
    2. Next, start at node 5, move 2 more steps to node 7, break the link after node 7.
    3. Finally, start at node 8, move 2 more steps to node 10, break the link after node 10.

The resulting array contains the heads of three linked lists as described above.

Time and Space Complexity

  • Brute-force Approach:
    If we traversed the list for each part, the time complexity would be O(k * n/k) = O(n), but with more overhead and repeated traversals.
  • Optimized Approach:
    The presented solution makes two passes over the list: one to count nodes, and one to split. Thus, total time complexity is O(n), where n is the number of nodes.
  • Space Complexity:
    The algorithm uses O(k) extra space to store the resulting parts. No additional data structures are used for the nodes themselves.

Summary

The key to splitting a linked list into k parts as evenly as possible is to first count the nodes, then distribute them so the first few parts get any extras. This ensures a fair split and respects the order of nodes. The solution is efficient, requiring only two passes and O(k) extra space for the result, making it both elegant and practical for large lists.