2164. Sort Even and Odd Indices Independently
Problem Description
You are given a 0-indexed integer array nums
. Your task is to rearrange the array by applying two sorting rules based on the index positions:
-
Sort elements at odd indices (1, 3, 5, ...) in non-increasing order (from largest to smallest)
- For example, if
nums = [4, 1, 2, 3]
, the elements at odd indices are at positions 1 and 3, which are values1
and3
. After sorting these in non-increasing order, they become3
and1
, resulting in[4, 3, 2, 1]
.
- For example, if
-
Sort elements at even indices (0, 2, 4, ...) in non-decreasing order (from smallest to largest)
- For example, if
nums = [4, 1, 2, 3]
, the elements at even indices are at positions 0 and 2, which are values4
and2
. After sorting these in non-decreasing order, they become2
and4
, resulting in[2, 1, 4, 3]
.
- For example, if
The solution approach extracts elements at odd and even positions separately using Python slicing. nums[::2]
gets all elements at even indices, while nums[1::2]
gets all elements at odd indices. After sorting each group according to their respective rules, the sorted values are placed back into their original positions in the array using the same slicing technique.
The time complexity is O(n log n)
due to the sorting operations, where n
is the length of the input array. The space complexity is O(n)
for storing the extracted odd and even indexed elements.
Intuition
The key insight is recognizing that elements at odd indices and even indices need to be sorted independently of each other. Since these two groups don't interact during sorting, we can handle them separately.
Think of the array as two interlaced subsequences - one containing all elements at even positions and another containing all elements at odd positions. Instead of trying to sort the entire array with complex comparison logic that checks indices, we can:
- Separate the problem into two simpler subproblems
- Sort each group according to its own rule
- Merge them back into their original positions
This approach naturally leads us to use Python's slicing feature. The notation nums[::2]
extracts every second element starting from index 0 (even indices), while nums[1::2]
extracts every second element starting from index 1 (odd indices).
Why does this work? Because sorting preserves the relative order within each group. When we extract elements at positions [0, 2, 4, ...]
, sort them, and place them back at the same positions [0, 2, 4, ...]
, we maintain the alternating pattern. The same logic applies to odd positions.
The beauty of this solution is that it transforms a seemingly complex rearrangement problem into two standard sorting operations. By decomposing the array based on index parity, we avoid the need for custom sorting comparators or complex index manipulation during the sorting process.
Learn more about Sorting patterns.
Solution Approach
The implementation follows a straightforward four-step process using Python's list slicing capabilities:
Step 1: Extract elements at even indices
a = sorted(nums[::2])
Using slice notation nums[::2]
, we extract all elements at even positions (0, 2, 4, ...). The ::2
means "start from the beginning, go to the end, with a step of 2". These elements are then sorted in ascending order using Python's built-in sorted()
function, which returns a new sorted list.
Step 2: Extract elements at odd indices
b = sorted(nums[1::2], reverse=True)
Similarly, nums[1::2]
extracts all elements at odd positions (1, 3, 5, ...). The 1::2
means "start from index 1, go to the end, with a step of 2". We sort these elements in descending order by setting reverse=True
in the sorted()
function.
Step 3: Place sorted elements back
nums[::2] = a nums[1::2] = b
Python's slice assignment allows us to replace elements at specific positions. nums[::2] = a
replaces all elements at even indices with the sorted values from list a
. Similarly, nums[1::2] = b
replaces all elements at odd indices with the sorted values from list b
.
Step 4: Return the modified array
return nums
The original array nums
has been modified in-place with the rearranged values.
Example walkthrough:
For nums = [4, 1, 2, 3]
:
- Even indices (0, 2): values are
[4, 2]
, sorted becomes[2, 4]
- Odd indices (1, 3): values are
[1, 3]
, sorted in reverse becomes[3, 1]
- Merging back: position 0 gets
2
, position 1 gets3
, position 2 gets4
, position 3 gets1
- Final result:
[2, 3, 4, 1]
The elegance of this solution lies in Python's slice notation, which handles the index management automatically, making the code both concise and readable.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [5, 8, 3, 6, 2, 9]
:
Initial array visualization by index:
Index: 0 1 2 3 4 5 Value: 5 8 3 6 2 9 Type: E O E O E O (E=Even index, O=Odd index)
Step 1: Extract and sort even-indexed elements
- Extract
nums[::2]
β gets indices 0, 2, 4 β values[5, 3, 2]
- Sort in non-decreasing order β
[2, 3, 5]
Step 2: Extract and sort odd-indexed elements
- Extract
nums[1::2]
β gets indices 1, 3, 5 β values[8, 6, 9]
- Sort in non-increasing order (reverse=True) β
[9, 8, 6]
Step 3: Place sorted elements back into original positions
- Even indices (0, 2, 4) get
[2, 3, 5]
:- Index 0 β 2
- Index 2 β 3
- Index 4 β 5
- Odd indices (1, 3, 5) get
[9, 8, 6]
:- Index 1 β 9
- Index 3 β 8
- Index 5 β 6
Final result:
Index: 0 1 2 3 4 5 Value: 2 9 3 8 5 6
The array transforms from [5, 8, 3, 6, 2, 9]
to [2, 9, 3, 8, 5, 6]
, where even indices [2, 3, 5]
are sorted ascending and odd indices [9, 8, 6]
are sorted descending.
Solution Implementation
1class Solution:
2 def sortEvenOdd(self, nums: List[int]) -> List[int]:
3 """
4 Sort the array such that:
5 - Elements at even indices (0, 2, 4, ...) are sorted in ascending order
6 - Elements at odd indices (1, 3, 5, ...) are sorted in descending order
7
8 Args:
9 nums: List of integers to be sorted
10
11 Returns:
12 The modified list with even/odd index sorting applied
13 """
14 # Extract elements at even indices and sort them in ascending order
15 even_index_elements = sorted(nums[::2])
16
17 # Extract elements at odd indices and sort them in descending order
18 odd_index_elements = sorted(nums[1::2], reverse=True)
19
20 # Place the sorted even-index elements back at their even positions
21 nums[::2] = even_index_elements
22
23 # Place the sorted odd-index elements back at their odd positions
24 nums[1::2] = odd_index_elements
25
26 return nums
27
1class Solution {
2 public int[] sortEvenOdd(int[] nums) {
3 int n = nums.length;
4
5 // Create arrays to store elements at even and odd indices
6 // evenIndices array size: ceiling of n/2 (handles both even and odd length arrays)
7 int[] evenIndices = new int[(n + 1) / 2];
8 // oddIndices array size: floor of n/2
9 int[] oddIndices = new int[n / 2];
10
11 // Extract elements at even indices (0, 2, 4, ...) and odd indices (1, 3, 5, ...)
12 for (int i = 0, j = 0; j < n / 2; i += 2, j++) {
13 evenIndices[j] = nums[i]; // Store element at even index
14 oddIndices[j] = nums[i + 1]; // Store element at odd index
15 }
16
17 // Handle the last element if array length is odd (it will be at an even index)
18 if (n % 2 == 1) {
19 evenIndices[evenIndices.length - 1] = nums[n - 1];
20 }
21
22 // Sort both arrays
23 Arrays.sort(evenIndices); // Sort in non-decreasing order
24 Arrays.sort(oddIndices); // Sort in non-decreasing order (will be reversed when placing back)
25
26 // Create result array
27 int[] result = new int[n];
28
29 // Place even-indexed elements in non-decreasing order
30 for (int i = 0, j = 0; j < evenIndices.length; i += 2, j++) {
31 result[i] = evenIndices[j];
32 }
33
34 // Place odd-indexed elements in non-increasing order (traverse oddIndices array backwards)
35 for (int i = 1, j = oddIndices.length - 1; j >= 0; i += 2, j--) {
36 result[i] = oddIndices[j];
37 }
38
39 return result;
40 }
41}
42
1class Solution {
2public:
3 vector<int> sortEvenOdd(vector<int>& nums) {
4 int n = nums.size();
5
6 // Separate elements by their index parity
7 vector<int> evenIndexElements; // Elements at even indices (0, 2, 4, ...)
8 vector<int> oddIndexElements; // Elements at odd indices (1, 3, 5, ...)
9
10 // Extract elements based on their index position
11 for (int i = 0; i < n; ++i) {
12 if (i % 2 == 0) {
13 evenIndexElements.push_back(nums[i]);
14 } else {
15 oddIndexElements.push_back(nums[i]);
16 }
17 }
18
19 // Sort even-indexed elements in ascending order
20 sort(evenIndexElements.begin(), evenIndexElements.end());
21
22 // Sort odd-indexed elements in descending order
23 sort(oddIndexElements.rbegin(), oddIndexElements.rend());
24
25 // Reconstruct the result array
26 vector<int> result(n);
27
28 // Place sorted even-indexed elements back at even positions
29 for (int resultIdx = 0, evenIdx = 0; evenIdx < evenIndexElements.size(); resultIdx += 2, ++evenIdx) {
30 result[resultIdx] = evenIndexElements[evenIdx];
31 }
32
33 // Place sorted odd-indexed elements back at odd positions
34 for (int resultIdx = 1, oddIdx = 0; oddIdx < oddIndexElements.size(); resultIdx += 2, ++oddIdx) {
35 result[resultIdx] = oddIndexElements[oddIdx];
36 }
37
38 return result;
39 }
40};
41
1/**
2 * Sorts an array where elements at even indices are sorted in ascending order
3 * and elements at odd indices are sorted in descending order
4 * @param nums - The input array of numbers to be sorted
5 * @returns A new array with the sorted elements according to the rules
6 */
7function sortEvenOdd(nums: number[]): number[] {
8 const arrayLength: number = nums.length;
9
10 // Arrays to store elements at even and odd indices
11 const evenIndexElements: number[] = [];
12 const oddIndexElements: number[] = [];
13
14 // Separate elements based on their index position
15 for (let i = 0; i < arrayLength; i++) {
16 if (i % 2 === 0) {
17 // Even index: 0, 2, 4, ...
18 evenIndexElements.push(nums[i]);
19 } else {
20 // Odd index: 1, 3, 5, ...
21 oddIndexElements.push(nums[i]);
22 }
23 }
24
25 // Sort even index elements in ascending order
26 evenIndexElements.sort((a, b) => a - b);
27
28 // Sort odd index elements in descending order
29 oddIndexElements.sort((a, b) => b - a);
30
31 // Initialize result array with proper length
32 const result: number[] = new Array(arrayLength);
33
34 // Place sorted even index elements back to even positions
35 for (let i = 0, j = 0; j < evenIndexElements.length; i += 2, j++) {
36 result[i] = evenIndexElements[j];
37 }
38
39 // Place sorted odd index elements back to odd positions
40 for (let i = 1, j = 0; j < oddIndexElements.length; i += 2, j++) {
41 result[i] = oddIndexElements[j];
42 }
43
44 return result;
45}
46
Time and Space Complexity
Time Complexity: O(n log n)
The code performs the following operations:
nums[::2]
extracts elements at even indices, which takesO(n/2)
=O(n)
timesorted(nums[::2])
sorts approximatelyn/2
elements, which takesO((n/2) log(n/2))
=O(n log n)
timenums[1::2]
extracts elements at odd indices, which takesO(n/2)
=O(n)
timesorted(nums[1::2], reverse=True)
sorts approximatelyn/2
elements, which takesO((n/2) log(n/2))
=O(n log n)
time- Assignment operations
nums[::2] = a
andnums[1::2] = b
each takeO(n/2)
=O(n)
time
The dominant operations are the two sorting steps, giving an overall time complexity of O(n log n)
.
Space Complexity: O(n)
The code creates:
- List
a
containing approximatelyn/2
elements from even indices - List
b
containing approximatelyn/2
elements from odd indices
Together, these two lists store all n
elements, resulting in a space complexity of O(n)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding Index vs. Position
A frequent mistake is confusing the index-based sorting requirement with value-based sorting. The problem asks to sort elements at even/odd indices, not elements that are even/odd numbers.
Incorrect approach:
# Wrong: This sorts based on whether the VALUE is even or odd
evens = sorted([x for x in nums if x % 2 == 0])
odds = sorted([x for x in nums if x % 2 == 1], reverse=True)
Correct approach:
# Right: This sorts based on the INDEX position
even_indices = sorted(nums[::2]) # Elements at positions 0, 2, 4...
odd_indices = sorted(nums[1::2], reverse=True) # Elements at positions 1, 3, 5...
2. Attempting Manual Index Reconstruction
Some developers try to manually merge the sorted arrays back together, which is error-prone and unnecessarily complex.
Inefficient approach:
result = []
for i in range(len(nums)):
if i % 2 == 0:
result.append(even_sorted[i // 2])
else:
result.append(odd_sorted[i // 2])
return result
Better approach: Use slice assignment to directly place elements back:
nums[::2] = even_sorted nums[1::2] = odd_sorted return nums
3. Creating Unnecessary Copies
When the problem allows in-place modification, creating a new array wastes space.
Space-inefficient:
result = [0] * len(nums)
# ... populate result array
return result
Space-efficient:
# Modify nums directly using slice assignment
nums[::2] = sorted(nums[::2])
nums[1::2] = sorted(nums[1::2], reverse=True)
return nums
4. Forgetting Edge Cases
Not handling arrays with only one element or empty arrays properly.
Solution: The slice notation naturally handles these cases:
- For
nums = []
: Bothnums[::2]
andnums[1::2]
return empty lists - For
nums = [5]
:nums[::2]
returns[5]
,nums[1::2]
returns[]
- The code works correctly without special case handling
5. Incorrect Slice Notation
Using wrong slice syntax can lead to unexpected results.
Common mistakes:
nums[0::2] # This is actually correct for even indices (same as nums[::2]) nums[2::2] # Wrong: This starts from index 2, missing index 0 nums[1:2] # Wrong: This only gets one element at index 1
Correct notation:
nums[::2] # All even indices: 0, 2, 4, ... nums[1::2] # All odd indices: 1, 3, 5, ...
Which of these pictures shows the visit order of a depth-first search?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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 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
Want a Structured Path to Master System Design Too? Donβt Miss This!