3819. Rotate Non Negative Elements
Problem Description
You are given an integer array nums and an integer k.
Your task is to rotate only the non-negative elements of the array to the left by k positions, in a cyclic manner. This means that when an element goes past the beginning of the sequence of non-negative elements, it wraps around to the end.
There are some important rules to follow:
- All negative elements must stay in their original positions and must not move at all.
- After rotating the non-negative elements, you place them back into the array in their new order, filling only the positions that originally contained non-negative values and skipping all positions that contain negative values.
In other words, imagine you pull out all the non-negative numbers (keeping their relative order), rotate just that collected sequence to the left by k steps cyclically, and then drop them back into the array one by one into the slots that previously held non-negative numbers. The slots that held negative numbers remain untouched.
Return the resulting array after performing this operation.
Example walkthrough of the idea:
- Suppose
nums = [3, -1, 5, -2, 7]andk = 1. - The non-negative elements are
[3, 5, 7]. - Rotating
[3, 5, 7]to the left by1gives[5, 7, 3]. - Now place them back into the non-negative positions (indices
0,2, and4), keeping the negative elements (indices1and3) in place. - The result is
[5, -1, 7, -2, 3].
How We Pick the Algorithm
Why Simulation / Basic DSA?
This problem maps to Simulation / Basic DSA through a short path in the full flowchart.
Extracting non-negative elements, rotating them cyclically, and placing them back into their original positions is a direct simulation.
Open in FlowchartIntuition
The key observation is that the negative elements act like fixed anchors — they never move. Only the non-negative elements participate in the rotation. This naturally suggests separating the two groups: deal with the non-negative elements on their own, and leave the negative ones exactly where they are.
So the first idea is to extract all the non-negative elements into a separate list t, preserving their original relative order. Now the problem reduces to a simple, classic task: rotate this list t to the left by k positions cyclically.
For a left rotation by k, the element originally at index i should move to a new index. If we shift left by k, the element at position i ends up at position i - k. Since the rotation is cyclic, we take this index modulo m (where m is the number of non-negative elements). Because i - k can be negative, we use ((i - k) % m + m) % m to safely wrap it into the valid range [0, m - 1]. We store the rotated result in an array d.
Finally, we need to put these rotated values back. We walk through the original nums array one position at a time. Whenever we hit a position that originally held a non-negative value, we fill it with the next value from d (using a pointer j that advances each time). When we hit a negative value, we skip it and leave it untouched. This guarantees the negatives stay in place while the non-negatives are placed back in their new rotated order, exactly as the problem requires.
Solution Approach
Solution 1: Simulation
We simulate the process directly by separating the non-negative elements, rotating them, and putting them back.
Step 1: Extract the non-negative elements.
We iterate through nums and collect every element that is >= 0 into a new list t, keeping their original relative order.
t = [x for x in nums if x >= 0]
m = len(t)
Here m is the number of non-negative elements, which is also the length of the cycle we rotate within.
Step 2: Rotate the elements into a result list d.
We create an array d of size m. For each element t[i], a left rotation by k means it should land at index i - k. Because the rotation is cyclic and i - k may be negative, we normalize the index with ((i - k) % m + m) % m to guarantee it falls within [0, m - 1].
d = [0] * m
for i, x in enumerate(t):
d[((i - k) % m + m) % m] = x
After this loop, d holds the non-negative elements in their final rotated order.
Step 3: Place the rotated elements back into nums.
We scan through the original nums with a pointer j initialized to 0. Whenever we encounter a position that originally held a non-negative value, we overwrite it with d[j] and advance j. Negative values are skipped entirely, so they remain in their original positions.
j = 0
for i, x in enumerate(nums):
if x >= 0:
nums[i] = d[j]
j += 1
return nums
Data structures and patterns used:
- Auxiliary arrays (
tandd) to isolate and reorder only the non-negative elements. - Modular arithmetic (
((i - k) % m + m) % m) to handle the cyclic left rotation safely, even whenkis larger thanmor the index becomes negative. - A two-pass scan with a pointer to map the rotated values back onto the correct slots while preserving the negative anchors.
Complexity Analysis:
- Time complexity:
O(n), wherenis the length ofnums. We make a constant number of passes over the array. - Space complexity:
O(n), for the auxiliary arraystanddthat store the non-negative elements.
Example Walkthrough
Let's trace through the solution approach with a small example.
Input: nums = [4, -3, 0, 8, -5, 2] and k = 2
Step 1: Extract the non-negative elements.
We scan through nums and collect every element >= 0, keeping their original order:
| Index | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| Value | 4 | -3 | 0 | 8 | -5 | 2 |
| Non-negative? | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ |
So t = [4, 0, 8, 2] and m = 4.
Step 2: Rotate the elements into a result list d.
We create d = [0, 0, 0, 0]. For each t[i], we compute its new index using ((i - k) % m + m) % m with k = 2 and m = 4:
i | t[i] | (i - k) | ((i - k) % m + m) % m | Placement |
|---|---|---|---|---|
| 0 | 4 | -2 | ((-2 % 4) + 4) % 4 = 2 | d[2] = 4 |
| 1 | 0 | -1 | ((-1 % 4) + 4) % 4 = 3 | d[3] = 0 |
| 2 | 8 | 0 | ((0 % 4) + 4) % 4 = 0 | d[0] = 8 |
| 3 | 2 | 1 | ((1 % 4) + 4) % 4 = 1 | d[1] = 2 |
After the loop, d = [8, 2, 4, 0].
This matches a left rotation of [4, 0, 8, 2] by 2: dropping the first two elements 4, 0 to the back gives [8, 2, 4, 0]. ✅
Step 3: Place the rotated elements back into nums.
We scan the original nums with pointer j = 0. Non-negative slots get filled from d; negative slots are skipped:
| Index | Original | Non-negative? | Action | j after | Array state |
|---|---|---|---|---|---|
| 0 | 4 | ✅ | nums[0] = d[0] = 8 | 1 | [8, -3, 0, 8, -5, 2] |
| 1 | -3 | ❌ | skip | 1 | [8, -3, 0, 8, -5, 2] |
| 2 | 0 | ✅ | nums[2] = d[1] = 2 | 2 | [8, -3, 2, 8, -5, 2] |
| 3 | 8 | ✅ | nums[3] = d[2] = 4 | 3 | [8, -3, 2, 4, -5, 2] |
| 4 | -5 | ❌ | skip | 3 | [8, -3, 2, 4, -5, 2] |
| 5 | 2 | ✅ | nums[5] = d[3] = 0 | 4 | [8, -3, 2, 4, -5, 0] |
Result: [8, -3, 2, 4, -5, 0]
Notice that the negative anchors -3 (index 1) and -5 (index 4) never moved, while the non-negative sequence [4, 0, 8, 2] was rotated left by 2 to [8, 2, 4, 0] and dropped back into the non-negative slots in order.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def rotateElements(self, nums: List[int], k: int) -> List[int]:
6 # Extract all non-negative elements while preserving their order
7 non_negatives = [num for num in nums if num >= 0]
8 count = len(non_negatives)
9
10 # Prepare an array to hold the rotated non-negative elements
11 rotated = [0] * count
12
13 # Rotate each non-negative element to the right by k positions
14 # ((i - k) % count + count) % count ensures a valid index even for large k
15 for i, num in enumerate(non_negatives):
16 rotated[((i - k) % count + count) % count] = num
17
18 # Write the rotated values back into their original non-negative slots,
19 # leaving negative numbers untouched in place
20 pointer = 0
21 for i, num in enumerate(nums):
22 if num >= 0:
23 nums[i] = rotated[pointer]
24 pointer += 1
25
26 return nums
271class Solution {
2 public int[] rotateElements(int[] nums, int k) {
3 // Count how many non-negative elements exist.
4 // These are the elements that will participate in the rotation.
5 int count = 0;
6 for (int value : nums) {
7 if (value >= 0) {
8 count++;
9 }
10 }
11
12 // Extract all non-negative elements into a separate array,
13 // preserving their original order.
14 int[] extracted = new int[count];
15 int writeIndex = 0;
16 for (int value : nums) {
17 if (value >= 0) {
18 extracted[writeIndex++] = value;
19 }
20 }
21
22 // Rotate the extracted elements by k positions.
23 // Element originally at index i moves to index (i - k) mod count.
24 // The ((i - k) % count + count) % count expression guarantees
25 // a non-negative index even when (i - k) is negative.
26 int[] rotated = new int[count];
27 for (int i = 0; i < count; i++) {
28 rotated[((i - k) % count + count) % count] = extracted[i];
29 }
30
31 // Write the rotated values back into the non-negative slots of nums,
32 // leaving the negative elements untouched in their original positions.
33 int readIndex = 0;
34 for (int i = 0; i < nums.length; i++) {
35 if (nums[i] >= 0) {
36 nums[i] = rotated[readIndex++];
37 }
38 }
39
40 return nums;
41 }
42}
431class Solution {
2public:
3 vector<int> rotateElements(vector<int>& nums, int k) {
4 // Step 1: Collect all non-negative elements while preserving their order.
5 // Negative values act as fixed "anchors" and will not participate in rotation.
6 vector<int> nonNegatives;
7 for (int value : nums) {
8 if (value >= 0) {
9 nonNegatives.push_back(value);
10 }
11 }
12
13 int count = static_cast<int>(nonNegatives.size());
14
15 // Edge case: if there is nothing to rotate, return the original array unchanged.
16 if (count == 0) {
17 return nums;
18 }
19
20 // Step 2: Compute the rotated arrangement of the non-negative elements.
21 // Each element originally at index i moves to index (i - k) under a left rotation by k.
22 // The expression ((i - k) % count + count) % count guarantees a valid non-negative index,
23 // correctly handling negative results and values of k larger than count.
24 vector<int> rotated(count);
25 for (int i = 0; i < count; ++i) {
26 int targetIndex = ((i - k) % count + count) % count;
27 rotated[targetIndex] = nonNegatives[i];
28 }
29
30 // Step 3: Write the rotated values back into the original array,
31 // skipping over the negative anchors so they stay in place.
32 int rotatedIndex = 0;
33 for (int i = 0; i < static_cast<int>(nums.size()); ++i) {
34 if (nums[i] >= 0) {
35 nums[i] = rotated[rotatedIndex++];
36 }
37 }
38
39 return nums;
40 }
41};
421/**
2 * Rotates the non-negative elements of the array to the right by k positions,
3 * while keeping negative elements fixed in their original positions.
4 *
5 * @param nums - The input array of numbers (negative values act as fixed anchors)
6 * @param k - The number of positions to rotate the non-negative elements
7 * @returns The same array with its non-negative elements rotated in place
8 */
9function rotateElements(nums: number[], k: number): number[] {
10 // Extract all non-negative elements, preserving their relative order
11 const positives: number[] = nums.filter((value) => value >= 0);
12 const count: number = positives.length;
13
14 // Guard against division/modulo by zero when there are no movable elements
15 if (count === 0) {
16 return nums;
17 }
18
19 // Build the rotated sequence: each element at index i moves to index (i - k)
20 // The double-modulo handles negative results to keep the index within [0, count)
21 const rotated: number[] = new Array<number>(count);
22 for (let i = 0; i < count; i++) {
23 const targetIndex: number = (((i - k) % count) + count) % count;
24 rotated[targetIndex] = positives[i];
25 }
26
27 // Write the rotated values back into the original non-negative slots,
28 // leaving negative elements untouched
29 let writeIndex: number = 0;
30 for (let i = 0; i < nums.length; i++) {
31 if (nums[i] >= 0) {
32 nums[i] = rotated[writeIndex++];
33 }
34 }
35
36 return nums;
37}
38Time and Space Complexity
-
Time Complexity:
O(n), wherenis the length of the arraynums. The first list comprehension[x for x in nums if x >= 0]iterates over allnelements to extract the non-negative ones. The loop that fillsdrunsmtimes (wheremis the number of non-negative elements), and the final loop iterates over allnelements again to write the rotated values back. Sincem ≤ n, the total work is dominated by theO(n)passes, giving an overall time complexity ofO(n). -
Space Complexity:
O(m), wheremis the number of non-negative elements innums. The auxiliary liststanddeach store themnon-negative elements, requiringO(m)extra space. The remaining variables use constant space, so the overall space complexity isO(m).
Common Pitfalls
Pitfall 1: Dividing by zero (or crashing) when there are no non-negative elements
The most common mistake is forgetting to handle the case where count == 0, i.e., when every element in nums is negative.
If count is 0, the rotation line:
rotated[((i - k) % count + count) % count] = num
would attempt a modulo by zero. While the for loop body never actually executes (because non_negatives is empty), some refactored versions that compute the modulus before the loop, or that normalize k with k %= count up front, will crash with a ZeroDivisionError.
Example trigger:
nums = [-3, -1, -7] k = 2 # non_negatives = [] -> count = 0 # k %= count # ZeroDivisionError!
Solution: Guard against the empty case before doing any modular arithmetic.
class Solution:
def rotateElements(self, nums: List[int], k: int) -> List[int]:
non_negatives = [num for num in nums if num >= 0]
count = len(non_negatives)
# Early return: nothing to rotate
if count == 0:
return nums
k %= count # safe now that count > 0
rotated = [0] * count
for i, num in enumerate(non_negatives):
rotated[(i - k) % count] = num
pointer = 0
for i, num in enumerate(nums):
if num >= 0:
nums[i] = rotated[pointer]
pointer += 1
return nums
Pitfall 2: Confusing left rotation with right rotation
The problem asks for a left rotation, but it is easy to accidentally implement a right rotation. With a left rotation by k, the element originally at index i moves to index i - k (cyclically). A right rotation would place it at i + k.
Example of the bug:
# WRONG: this is a RIGHT rotation rotated[(i + k) % count] = num
For non_negatives = [3, 5, 7] and k = 1:
- Correct left rotation:
[5, 7, 3] - Incorrect right rotation:
[7, 3, 5]
Solution: Always use (i - k) % count (normalized to be non-negative) for a left rotation. A quick sanity check against the worked example in the problem statement catches this immediately.
Pitfall 3: Treating 0 as a negative (or "movable" anchor) inconsistently
The rule is that negative elements stay fixed, which means 0 is treated as a non-negative element that does rotate. A subtle bug arises if the extraction step uses num > 0 while the write-back step uses num >= 0 (or vice versa). The two passes would then disagree on which slots are "rotatable," causing the pointer to misalign or an IndexError.
Example of the bug:
non_negatives = [num for num in nums if num > 0] # excludes 0
...
for i, num in enumerate(nums):
if num >= 0: # includes 0
nums[i] = rotated[pointer] # pointer runs out of range
Solution: Use the identical condition (num >= 0) in both the extraction pass and the write-back pass so the set of "rotatable" indices is exactly consistent.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhat does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
141public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
271function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!