1051. Height Checker
Problem Description
You have a line of students who need to be arranged for a school photo. The students should stand in a single line ordered by their heights from shortest to tallest (or equal heights can be in any order among themselves). This is called non-decreasing order.
You're given an array heights
that shows the current order of students in line, where heights[i]
represents the height of the student at position i
(using 0-based indexing).
Your task is to figure out how many students are standing in the wrong position. A student is in the wrong position if their current position differs from where they should be when the line is properly sorted by height.
For example:
- If
heights = [1, 1, 4, 2, 1, 3]
- The properly sorted order would be
[1, 1, 1, 2, 3, 4]
- Comparing position by position:
- Position 0:
1 = 1
β (correct) - Position 1:
1 = 1
β (correct) - Position 2:
4 β 1
β (wrong) - Position 3:
2 = 2
β (correct) - Position 4:
1 β 3
β (wrong) - Position 5:
3 β 4
β (wrong)
- Position 0:
- Total students in wrong positions: 3
The solution approach is straightforward: create a sorted version of the heights array (this represents where students should be standing), then compare it with the original array and count how many positions have different values. The code uses sorted(heights)
to get the expected arrangement and then counts mismatches using a generator expression with sum()
and zip()
to compare corresponding positions.
Intuition
The key insight is recognizing that we need to compare the current arrangement with the ideal arrangement. When students are asked to line up by height in non-decreasing order, there's only one correct arrangement possible - the sorted order.
Think of it this way: if we know what the line should look like (sorted by height) and what it currently looks like, we can simply compare these two arrangements position by position. Any position where the heights don't match means a student is standing in the wrong spot.
The problem essentially boils down to asking: "How different is the current arrangement from the sorted arrangement?" We don't need to actually move students or track which student goes where - we just need to count the differences.
Why does this work? Because when we sort the heights array, we get the "expected" positions for each height value. If a student at position i
has a different height than what should be at position i
in the sorted array, that student needs to move.
For instance, if we see a tall student at the beginning of the line where a short student should be, we know that position is wrong. By sorting the array and comparing it with the original, we identify all such misplaced positions.
The elegance of this approach lies in its simplicity - rather than trying to simulate the rearrangement process or track individual student movements, we just create the target state (sorted array) and count the mismatches. Each mismatch represents a student who isn't where they should be in the properly ordered line.
Learn more about Sorting patterns.
Solution Approach
The implementation follows a straightforward sorting and comparison strategy:
Step 1: Create the Expected Arrangement
expected = sorted(heights)
We use Python's built-in sorted()
function to create a new list containing all the heights in non-decreasing order. This represents where each student should be standing. The sorted()
function returns a new list, leaving the original heights
array unchanged.
Step 2: Compare and Count Differences
return sum(a != b for a, b in zip(heights, expected))
This line does several things efficiently:
-
zip(heights, expected)
- Pairs up elements from both arrays at corresponding positions. For example, ifheights = [1, 2, 3]
andexpected = [1, 3, 2]
, thenzip()
produces pairs:(1,1)
,(2,3)
,(3,2)
. -
a != b for a, b in ...
- This is a generator expression that compares each pair. For each position, it evaluates toTrue
(which equals 1 in Python) if the heights are different, orFalse
(which equals 0) if they're the same. -
sum(...)
- Adds up all the boolean values. SinceTrue = 1
andFalse = 0
, this effectively counts how many positions have mismatched heights.
Time Complexity: O(n log n)
where n
is the number of students. The sorting operation dominates the time complexity.
Space Complexity: O(n)
for storing the sorted array expected
.
The beauty of this solution is its conciseness - in just two lines, we create the target state and count the differences. The use of zip()
and a generator expression makes the comparison elegant and memory-efficient, as it doesn't create an intermediate list of comparison results.
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 a small example with heights = [2, 1, 3, 1, 2]
.
Step 1: Create the Expected Arrangement
First, we sort the heights to get the proper order:
- Original:
[2, 1, 3, 1, 2]
- Sorted:
[1, 1, 2, 2, 3]
This sorted array represents where students should be standing - shortest to tallest.
Step 2: Compare Position by Position
Now we compare each position using zip(heights, expected)
:
Position | Current Height | Expected Height | Match? | Count |
---|---|---|---|---|
0 | 2 | 1 | β | +1 |
1 | 1 | 1 | β | +0 |
2 | 3 | 2 | β | +1 |
3 | 1 | 2 | β | +1 |
4 | 2 | 3 | β | +1 |
Step 3: Count the Mismatches
The comparison a != b
produces:
- Position 0:
2 != 1
βTrue
(counts as 1) - Position 1:
1 != 1
βFalse
(counts as 0) - Position 2:
3 != 2
βTrue
(counts as 1) - Position 3:
1 != 2
βTrue
(counts as 1) - Position 4:
2 != 3
βTrue
(counts as 1)
Using sum()
on these boolean values: 1 + 0 + 1 + 1 + 1 = 4
Result: 4 students are standing in the wrong positions and need to move for the photo to be properly arranged by height.
Solution Implementation
1class Solution:
2 def heightChecker(self, heights: List[int]) -> int:
3 """
4 Count the number of students standing in the wrong positions.
5
6 A student is in the wrong position if their height doesn't match
7 the expected height when all students are arranged in non-decreasing order.
8
9 Args:
10 heights: List of integers representing student heights
11
12 Returns:
13 Number of students not standing in the correct position
14 """
15 # Create the expected arrangement by sorting heights in non-decreasing order
16 expected_heights = sorted(heights)
17
18 # Count mismatches between actual and expected positions
19 # zip pairs elements from both lists at corresponding indices
20 # The generator expression evaluates to 1 for mismatches, 0 for matches
21 mismatch_count = sum(
22 actual != expected
23 for actual, expected in zip(heights, expected_heights)
24 )
25
26 return mismatch_count
27
1class Solution {
2 /**
3 * Counts the number of students standing in the wrong positions
4 * compared to their expected positions when sorted by height.
5 *
6 * @param heights Array containing the current heights of students
7 * @return The number of students not in their expected positions
8 */
9 public int heightChecker(int[] heights) {
10 // Create a copy of the original heights array to preserve original order
11 int[] expectedHeights = heights.clone();
12
13 // Sort the expected heights array in non-decreasing order
14 Arrays.sort(expectedHeights);
15
16 // Initialize counter for mismatched positions
17 int mismatchCount = 0;
18
19 // Compare each position in original and sorted arrays
20 for (int i = 0; i < heights.length; i++) {
21 // If height at current position doesn't match expected height
22 if (heights[i] != expectedHeights[i]) {
23 mismatchCount++;
24 }
25 }
26
27 // Return the total number of mismatched positions
28 return mismatchCount;
29 }
30}
31
1class Solution {
2public:
3 int heightChecker(vector<int>& heights) {
4 // Create a copy of the original heights array to store the expected order
5 vector<int> expected = heights;
6
7 // Sort the expected array to get the correct non-decreasing order
8 sort(expected.begin(), expected.end());
9
10 // Initialize counter for mismatched positions
11 int mismatchCount = 0;
12
13 // Compare each position between original and expected arrays
14 for (int i = 0; i < heights.size(); ++i) {
15 // Increment counter if heights don't match at current position
16 if (heights[i] != expected[i]) {
17 mismatchCount++;
18 }
19 }
20
21 // Return the total number of students in wrong positions
22 return mismatchCount;
23 }
24};
25
1/**
2 * Counts the number of students who are not standing in the correct position
3 * when arranged by height in non-decreasing order.
4 *
5 * @param heights - Array of student heights
6 * @returns The number of students in wrong positions
7 */
8function heightChecker(heights: number[]): number {
9 // Create a copy of heights array and sort it in ascending order
10 // This represents the expected order of students by height
11 const expectedOrder: number[] = [...heights].sort((a: number, b: number) => a - b);
12
13 // Count how many positions have different values between original and expected arrays
14 // For each index, increment counter if the height doesn't match expected position
15 const mismatchCount: number = heights.reduce(
16 (accumulator: number, currentHeight: number, index: number) => {
17 // Add 1 if current height doesn't match expected height at this position, otherwise add 0
18 return accumulator + (currentHeight !== expectedOrder[index] ? 1 : 0);
19 },
20 0 // Initial value for accumulator
21 );
22
23 return mismatchCount;
24}
25
Time and Space Complexity
Time Complexity: O(n Γ log n)
The time complexity is dominated by the sorted()
function, which uses Timsort algorithm with a time complexity of O(n Γ log n)
where n
is the number of elements in the heights list. The subsequent operations - zip()
and sum()
with comparison - both take O(n)
time, but since O(n Γ log n) + O(n) = O(n Γ log n)
, the overall time complexity remains O(n Γ log n)
.
Space Complexity: O(n)
The space complexity is O(n)
because the sorted()
function creates a new list called expected
that contains all n
elements from the original heights list. The zip()
operation creates an iterator that doesn't require additional space proportional to the input size, and the generator expression in sum()
processes elements one at a time without storing them all in memory.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Attempting to Sort In-Place
A common mistake is trying to sort the original array in-place and then comparing it with itself, which would always result in 0 mismatches:
Incorrect Approach:
def heightChecker(self, heights: List[int]) -> int:
original = heights # This doesn't create a copy!
heights.sort() # Sorts in-place, modifying the original
return sum(a != b for a, b in zip(original, heights)) # Always returns 0
Why it fails: In Python, original = heights
creates a reference to the same list object, not a copy. When heights.sort()
is called, both original
and heights
point to the same sorted list.
Solution: Always create a copy when you need to preserve the original order:
def heightChecker(self, heights: List[int]) -> int:
original = heights.copy() # Or heights[:] or list(heights)
sorted_heights = sorted(heights) # Or use this to create a new sorted list
return sum(a != b for a, b in zip(original, sorted_heights))
Pitfall 2: Misunderstanding the Problem - Counting Unique Heights
Some might misinterpret the problem as counting how many distinct heights are out of order:
Incorrect Interpretation:
def heightChecker(self, heights: List[int]) -> int:
# Wrong: Counting unique values that are misplaced
return len(set(heights) - set(sorted(heights))) # This is incorrect!
Why it fails: The problem asks for the number of positions (indices) where students are wrong, not the number of unique heights that are misplaced. If heights = [1, 1, 4, 2, 1, 3]
, we need to count 3 positions that are wrong, not unique values.
Solution: Stick to position-by-position comparison as shown in the correct implementation.
Pitfall 3: Off-by-One Errors When Manually Iterating
When implementing the comparison loop manually, indexing errors are common:
Error-Prone Approach:
def heightChecker(self, heights: List[int]) -> int:
expected = sorted(heights)
count = 0
for i in range(len(heights) + 1): # Wrong: goes beyond array bounds
if heights[i] != expected[i]:
count += 1
return count
Solution: Use zip()
to avoid index management entirely, or ensure proper range bounds:
# Better: Let zip handle the pairing
return sum(a != b for a, b in zip(heights, sorted(heights)))
# Or if using indices, ensure correct range
for i in range(len(heights)): # Correct range
if heights[i] != expected[i]:
count += 1
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
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!