725. Split Linked List in Parts - Explanation
Description
Split the singly linked list into exactly k consecutive parts, preserving node order. Make their lengths differ by at most 1, with longer parts before shorter parts. Return an array containing the head of each part. Some parts can be empty. In examples and test inputs, arrays show list values from head to tail; each [] in the output represents an empty part.
Example 1:
Input: head = [1,2,3,4,5,6,7]
k = 3
Output: [[1,2,3],[4,5],[6,7]]Explanation: Seven nodes split into three parts have lengths 3, 2, and 2. The extra node goes in the first part, and all nodes stay in their original order.
Example 2:
Input: head = [8,9]
k = 4
Output: [[8],[9],[],[]]Explanation: There are more parts than nodes. Put one node in each of the first two parts and leave the final two parts empty.
Example 3:
Input: head = []
k = 2
Output: [[],[]]Explanation: The input list is empty, but the result must still contain exactly two parts. Both parts are empty.
Constraints:
- The list contains between
0and1000nodes. 0 <= Node.val <= 10001 <= k <= 50
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Linked List Traversal - Need to traverse the list to count nodes and split at specific positions
- Linked List Manipulation - Severing links by setting
nextpointers tonullto create separate parts - Division and Modulo Operations - Used to calculate base part size (
n / k) and distribute remainder (n % k)
1. Convert To Array
Intuition
To split the list into k parts as evenly as possible, we first need to know the total length. Converting the linked list to an array gives us random access, making it easy to determine where each part starts and ends. Each part gets n / k elements at minimum, and the first n % k parts get one extra element to distribute the remainder evenly.
Algorithm
- Traverse the linked list and store all nodes in an array.
- Calculate
base_len = n / k(minimum elements per part) andremainder = n % k(parts that get an extra element). - For each of the
kparts:- If there are remaining elements, set the part's head to
arr[start]. - Compute the tail index:
start + base_len - 1, plus 1 if this part gets an extra element. - Sever the link by setting
arr[tail].next = null. - Update
startfor the next part.
- If there are remaining elements, set the part's head to
- Return the array of part heads.
# 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: Optional[ListNode], k: int) -> List[Optional[ListNode]]:
arr, cur = [], head
while cur:
arr.append(cur)
cur = cur.next
N = len(arr)
base_len, remainder = N // k, N % k
res = [None] * k
start = 0
for i in range(k):
if start < N:
res[i] = arr[start]
tail = start + base_len - 1
if remainder:
tail += 1
remainder -= 1
arr[tail].next = None
start = tail + 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
2. Iteration
Intuition
We can avoid the extra array by working directly with the linked list. First, count the total nodes. Then traverse again, splitting the list into parts by tracking the current node and severing links at the appropriate positions. The first remainder parts each have one more node than the remaining parts.
Algorithm
- Count the total number of nodes in the list.
- Calculate
base_len = length / kandremainder = length % k. - Traverse the list to create
kparts:- Record the current node as the head of this part.
- Advance
base_len - 1 + (1 if remainder > 0 else 0)steps to reach the tail. - Save
curr.next, setcurr.next = nullto sever, then continue from the saved node. - Decrement
remainderafter each extra-length part.
- Return the array of part heads.
# 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: Optional[ListNode], k: int) -> List[Optional[ListNode]]:
length, curr = 0, head
while curr:
curr = curr.next
length += 1
base_len, remainder = length // k, length % k
curr, res = head, []
for i in range(k):
res.append(curr)
for j in range(base_len - 1 + (1 if remainder else 0)):
if not curr:
break
curr = curr.next
remainder -= 1 if remainder else 0
if curr:
curr.next, curr = None, curr.next
return resTime & Space Complexity
- Time complexity:
- Space complexity:
- extra space.
- space for the output array.
Common Pitfalls
Forgetting to Sever the Link Between Parts
After determining where each part ends, you must set the tail node's next pointer to null. Failing to do this leaves the parts still connected, causing the output to contain overlapping or incorrectly sized lists instead of independent segments.
Incorrect Distribution of Extra Nodes
When n % k != 0, the first remainder parts should each have one extra node. A common mistake is distributing extra nodes to the wrong parts (e.g., the last parts instead of the first) or failing to decrement the remainder counter properly, resulting in unevenly sized parts.
Not Handling Empty Parts When k > n
When there are more parts than nodes, some parts must be empty (null). Forgetting to account for this case can lead to index out of bounds errors or incorrect assignments when trying to set heads for parts that have no nodes.
Sign in to join the discussion