Facebook Pixel

3819. Rotate Non Negative Elements

Medium
LeetCode ↗

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] and k = 1.
  • The non-negative elements are [3, 5, 7].
  • Rotating [3, 5, 7] to the left by 1 gives [5, 7, 3].
  • Now place them back into the non-negative positions (indices 0, 2, and 4), keeping the negative elements (indices 1 and 3) in place.
  • The result is [5, -1, 7, -2, 3].
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Simulation / Basic DSA?

This problem maps to Simulation / Basic DSA through a short path in the full flowchart.

JustsimulatetheyesComplexdatastructure?noSimulation /Basic DSA

Extracting non-negative elements, rotating them cyclically, and placing them back into their original positions is a direct simulation.

Open in Flowchart

Intuition

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 (t and d) to isolate and reorder only the non-negative elements.
  • Modular arithmetic (((i - k) % m + m) % m) to handle the cyclic left rotation safely, even when k is larger than m or 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), where n is the length of nums. We make a constant number of passes over the array.
  • Space complexity: O(n), for the auxiliary arrays t and d that 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:

Index012345
Value4-308-52
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:

it[i](i - k)((i - k) % m + m) % mPlacement
04-2((-2 % 4) + 4) % 4 = 2d[2] = 4
10-1((-1 % 4) + 4) % 4 = 3d[3] = 0
280((0 % 4) + 4) % 4 = 0d[0] = 8
321((1 % 4) + 4) % 4 = 1d[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:

IndexOriginalNon-negative?Actionj afterArray state
04nums[0] = d[0] = 81[8, -3, 0, 8, -5, 2]
1-3skip1[8, -3, 0, 8, -5, 2]
20nums[2] = d[1] = 22[8, -3, 2, 8, -5, 2]
38nums[3] = d[2] = 43[8, -3, 2, 4, -5, 2]
4-5skip3[8, -3, 2, 4, -5, 2]
52nums[5] = d[3] = 04[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
27
1class 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}
43
1class 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};
42
1/**
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}
38

Time and Space Complexity

  • Time Complexity: O(n), where n is the length of the array nums. The first list comprehension [x for x in nums if x >= 0] iterates over all n elements to extract the non-negative ones. The loop that fills d runs m times (where m is the number of non-negative elements), and the final loop iterates over all n elements again to write the rotated values back. Since m ≤ n, the total work is dominated by the O(n) passes, giving an overall time complexity of O(n).

  • Space Complexity: O(m), where m is the number of non-negative elements in nums. The auxiliary lists t and d each store the m non-negative elements, requiring O(m) extra space. The remaining variables use constant space, so the overall space complexity is O(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 Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

What 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
14
1public 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}
27
1function 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}
27

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More