1395. Count Number of Teams
Problem Description
You are given an array of n
soldiers standing in a line, where each soldier has a unique rating
value.
Your task is to count how many teams of exactly 3 soldiers can be formed following these rules:
- A team consists of 3 soldiers at positions
i
,j
, andk
wherei < j < k
- A team is valid if either:
- The ratings are in ascending order:
rating[i] < rating[j] < rating[k]
, OR - The ratings are in descending order:
rating[i] > rating[j] > rating[k]
- The ratings are in ascending order:
The same soldier can be part of multiple different teams.
For example, if you have ratings [2, 5, 3, 4, 1]
:
- Team
(2, 3, 4)
at indices(0, 2, 3)
is valid because2 < 3 < 4
- Team
(5, 3, 1)
at indices(1, 2, 4)
is valid because5 > 3 > 1
- Team
(5, 4, 1)
at indices(1, 3, 4)
is valid because5 > 4 > 1
The solution works by fixing each soldier as the middle element of a potential team. For each middle soldier at position i
with rating b
:
- Count how many soldiers to the left have ratings less than
b
(call thisl
) - Count how many soldiers to the right have ratings greater than
b
(call thisr
) - This gives us
l * r
ascending teams with this middle soldier - Similarly,
(i - l) * (n - i - 1 - r)
gives us descending teams with this middle soldier
The total number of valid teams is the sum of all such combinations across all possible middle soldiers.
Intuition
The key insight is that every valid team of 3 soldiers must have a middle element. Instead of trying to find all possible triplets directly (which would be O(n³)
), we can fix the middle element and count how many valid teams can be formed with it.
Think about it this way: for any soldier in the middle position of a team, what determines if we can form valid teams?
For an ascending team (a, b, c)
where b
is our fixed middle element:
- We need one soldier on the left with rating less than
b
- We need one soldier on the right with rating greater than
b
- If we have
l
choices on the left andr
choices on the right, we can forml * r
different ascending teams
For a descending team (a, b, c)
where b
is our fixed middle element:
- We need one soldier on the left with rating greater than
b
- We need one soldier on the right with rating less than
b
- If there are
i
total soldiers to the left andl
of them are smaller thanb
, then(i - l)
are greater thanb
- Similarly, if there are
(n - i - 1)
soldiers to the right andr
of them are greater thanb
, then(n - i - 1 - r)
are smaller thanb
- So we can form
(i - l) * (n - i - 1 - r)
different descending teams
By iterating through each soldier as a potential middle element and counting both ascending and descending teams they can form, we avoid checking every possible triplet combination. This reduces our time complexity from O(n³)
to O(n²)
since for each of the n
positions, we only need to count elements on the left and right once.
Learn more about Segment Tree and Dynamic Programming patterns.
Solution Approach
The implementation follows the enumerate middle element strategy described in the reference approach. Here's how the solution works step by step:
-
Initialize variables: Set
ans = 0
to store the total count of teams, and get the lengthn
of the rating array. -
Enumerate each soldier as the middle element: Use
enumerate(rating)
to iterate through each soldier, getting both the indexi
and their rating valueb
. -
Count smaller elements on the left:
l = sum(a < b for a in rating[:i])
This counts how many soldiers to the left of position
i
have ratings smaller thanb
. -
Count larger elements on the right:
r = sum(c > b for c in rating[i + 1:])
This counts how many soldiers to the right of position
i
have ratings larger thanb
. -
Calculate ascending teams:
ans += l * r
For ascending order teams
(a, b, c)
wherea < b < c
, we multiply the number of valid left choicesl
by the number of valid right choicesr
. -
Calculate descending teams:
ans += (i - l) * (n - i - 1 - r)
For descending order teams
(a, b, c)
wherea > b > c
:(i - l)
gives the count of soldiers on the left with ratings greater thanb
(n - i - 1 - r)
gives the count of soldiers on the right with ratings smaller thanb
- Their product gives all possible descending teams with
b
as the middle element
-
Return the total count: After processing all possible middle elements, return
ans
.
The time complexity is O(n²)
because for each of the n
elements, we scan the array once to count elements on both sides. The space complexity is O(1)
as we only use a few variables to store counts.
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 rating = [2, 5, 3, 4, 1]
.
We'll fix each soldier as the middle element and count valid teams:
Step 1: Middle soldier at index 0 (rating = 2)
- Left side: [] (empty)
- Right side: [5, 3, 4, 1]
- Count smaller on left (l): 0
- Count larger on right (r): 3 (values 5, 3, 4 are all > 2)
- Ascending teams: 0 × 3 = 0
- Descending teams: (0 - 0) × (4 - 3) = 0 × 1 = 0
- Total so far: 0
Step 2: Middle soldier at index 1 (rating = 5)
- Left side: [2]
- Right side: [3, 4, 1]
- Count smaller on left (l): 1 (value 2 < 5)
- Count larger on right (r): 0 (no values > 5)
- Ascending teams: 1 × 0 = 0
- Descending teams: (1 - 1) × (3 - 0) = 0 × 3 = 0
- Total so far: 0
Step 3: Middle soldier at index 2 (rating = 3)
- Left side: [2, 5]
- Right side: [4, 1]
- Count smaller on left (l): 1 (value 2 < 3)
- Count larger on right (r): 1 (value 4 > 3)
- Ascending teams: 1 × 1 = 1 (team: 2-3-4)
- Descending teams: (2 - 1) × (2 - 1) = 1 × 1 = 1 (team: 5-3-1)
- Total so far: 0 + 1 + 1 = 2
Step 4: Middle soldier at index 3 (rating = 4)
- Left side: [2, 5, 3]
- Right side: [1]
- Count smaller on left (l): 2 (values 2, 3 < 4)
- Count larger on right (r): 0 (no values > 4)
- Ascending teams: 2 × 0 = 0
- Descending teams: (3 - 2) × (1 - 0) = 1 × 1 = 1 (team: 5-4-1)
- Total so far: 2 + 0 + 1 = 3
Step 5: Middle soldier at index 4 (rating = 1)
- Left side: [2, 5, 3, 4]
- Right side: [] (empty)
- Count smaller on left (l): 0 (no values < 1)
- Count larger on right (r): 0 (empty)
- Ascending teams: 0 × 0 = 0
- Descending teams: (4 - 0) × (0 - 0) = 4 × 0 = 0
- Total so far: 3
Final Answer: 3 teams
- Ascending: (2, 3, 4)
- Descending: (5, 3, 1) and (5, 4, 1)
Solution Implementation
1class Solution:
2 def numTeams(self, rating: List[int]) -> int:
3 """
4 Count the number of valid teams of 3 soldiers.
5 A valid team (i, j, k) satisfies either:
6 - rating[i] < rating[j] < rating[k] (ascending order)
7 - rating[i] > rating[j] > rating[k] (descending order)
8
9 Args:
10 rating: List of soldier ratings
11
12 Returns:
13 Number of valid teams that can be formed
14 """
15 total_teams = 0
16 n = len(rating)
17
18 # For each soldier at position i, consider it as the middle soldier
19 for i in range(n):
20 current_rating = rating[i]
21
22 # Count soldiers to the left with smaller rating
23 smaller_left = sum(1 for left_rating in rating[:i] if left_rating < current_rating)
24
25 # Count soldiers to the right with greater rating
26 greater_right = sum(1 for right_rating in rating[i + 1:] if right_rating > current_rating)
27
28 # Ascending teams: left < current < right
29 total_teams += smaller_left * greater_right
30
31 # Calculate soldiers to the left with greater rating
32 greater_left = i - smaller_left
33
34 # Calculate soldiers to the right with smaller rating
35 smaller_right = (n - i - 1) - greater_right
36
37 # Descending teams: left > current > right
38 total_teams += greater_left * smaller_right
39
40 return total_teams
41
1class Solution {
2 public int numTeams(int[] rating) {
3 int n = rating.length;
4 int totalTeams = 0;
5
6 // For each soldier as the middle soldier of a team
7 for (int middleIndex = 0; middleIndex < n; ++middleIndex) {
8 int smallerOnLeft = 0; // Count of soldiers with smaller rating on the left
9 int greaterOnRight = 0; // Count of soldiers with greater rating on the right
10
11 // Count soldiers on the left with smaller rating than current soldier
12 for (int leftIndex = 0; leftIndex < middleIndex; ++leftIndex) {
13 if (rating[leftIndex] < rating[middleIndex]) {
14 ++smallerOnLeft;
15 }
16 }
17
18 // Count soldiers on the right with greater rating than current soldier
19 for (int rightIndex = middleIndex + 1; rightIndex < n; ++rightIndex) {
20 if (rating[rightIndex] > rating[middleIndex]) {
21 ++greaterOnRight;
22 }
23 }
24
25 // Calculate ascending teams: left < middle < right
26 totalTeams += smallerOnLeft * greaterOnRight;
27
28 // Calculate descending teams: left > middle > right
29 // greaterOnLeft = total on left - smallerOnLeft
30 // smallerOnRight = total on right - greaterOnRight
31 int greaterOnLeft = middleIndex - smallerOnLeft;
32 int smallerOnRight = (n - middleIndex - 1) - greaterOnRight;
33 totalTeams += greaterOnLeft * smallerOnRight;
34 }
35
36 return totalTeams;
37 }
38}
39
1class Solution {
2public:
3 int numTeams(vector<int>& rating) {
4 int n = rating.size();
5 int totalTeams = 0;
6
7 // For each soldier as the middle element of a team
8 for (int mid = 0; mid < n; ++mid) {
9 int leftSmaller = 0; // Count of soldiers to the left with smaller rating
10 int rightLarger = 0; // Count of soldiers to the right with larger rating
11
12 // Count soldiers to the left with smaller rating than rating[mid]
13 for (int left = 0; left < mid; ++left) {
14 if (rating[left] < rating[mid]) {
15 ++leftSmaller;
16 }
17 }
18
19 // Count soldiers to the right with larger rating than rating[mid]
20 for (int right = mid + 1; right < n; ++right) {
21 if (rating[right] > rating[mid]) {
22 ++rightLarger;
23 }
24 }
25
26 // Calculate ascending teams: smaller -> mid -> larger
27 totalTeams += leftSmaller * rightLarger;
28
29 // Calculate descending teams: larger -> mid -> smaller
30 // leftLarger = total soldiers on left - leftSmaller
31 // rightSmaller = total soldiers on right - rightLarger
32 int leftLarger = mid - leftSmaller;
33 int rightSmaller = (n - mid - 1) - rightLarger;
34 totalTeams += leftLarger * rightSmaller;
35 }
36
37 return totalTeams;
38 }
39};
40
1/**
2 * Counts the number of valid teams that can be formed from soldiers with given ratings.
3 * A valid team consists of 3 soldiers (i, j, k) where either:
4 * - rating[i] < rating[j] < rating[k] (ascending order)
5 * - rating[i] > rating[j] > rating[k] (descending order)
6 *
7 * @param rating - Array of soldier ratings
8 * @returns The total number of valid teams that can be formed
9 */
10function numTeams(rating: number[]): number {
11 let totalTeams: number = 0;
12 const arrayLength: number = rating.length;
13
14 // Iterate through each soldier as the middle element of a team
15 for (let middleIndex = 0; middleIndex < arrayLength; ++middleIndex) {
16 let smallerOnLeft: number = 0; // Count of soldiers with smaller rating to the left
17 let greaterOnRight: number = 0; // Count of soldiers with greater rating to the right
18
19 // Count soldiers to the left with smaller rating than current middle soldier
20 for (let leftIndex = 0; leftIndex < middleIndex; ++leftIndex) {
21 if (rating[leftIndex] < rating[middleIndex]) {
22 ++smallerOnLeft;
23 }
24 }
25
26 // Count soldiers to the right with greater rating than current middle soldier
27 for (let rightIndex = middleIndex + 1; rightIndex < arrayLength; ++rightIndex) {
28 if (rating[rightIndex] > rating[middleIndex]) {
29 ++greaterOnRight;
30 }
31 }
32
33 // Calculate ascending teams: smaller < middle < greater
34 totalTeams += smallerOnLeft * greaterOnRight;
35
36 // Calculate descending teams: greater > middle > smaller
37 // (middleIndex - smallerOnLeft) = soldiers on left with greater rating
38 // (arrayLength - middleIndex - 1 - greaterOnRight) = soldiers on right with smaller rating
39 totalTeams += (middleIndex - smallerOnLeft) * (arrayLength - middleIndex - 1 - greaterOnRight);
40 }
41
42 return totalTeams;
43}
44
Time and Space Complexity
The time complexity is O(n²)
and the space complexity is O(1)
, where n
is the length of the array rating
.
Time Complexity Analysis:
- The outer loop iterates through each element in the
rating
array, which runsn
times - For each iteration
i
, we count elements less thanrating[i]
inrating[:i]
which takesO(i)
time - We also count elements greater than
rating[i]
inrating[i+1:]
which takesO(n-i-1)
time - The total time for iteration
i
isO(i) + O(n-i-1) = O(n)
- Since we do this for all
n
elements, the overall time complexity isO(n) × n = O(n²)
Space Complexity Analysis:
- The algorithm only uses a constant amount of extra space for variables
ans
,n
,i
,b
,l
, andr
- No additional data structures that grow with input size are created
- Therefore, the space complexity is
O(1)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Off-by-One Errors in Index Calculations
A frequent mistake is incorrectly calculating the counts for descending teams. When computing greater_left
and smaller_right
, developers often forget to account for the exact position boundaries:
Incorrect approach:
# Wrong calculation for greater_left greater_left = i - smaller_left - 1 # Incorrectly subtracts 1 # Wrong calculation for smaller_right smaller_right = n - i - greater_right # Forgets to subtract 1 for the current element
Correct approach:
# Correct: i elements to the left, smaller_left have smaller ratings greater_left = i - smaller_left # Correct: (n - i - 1) elements to the right, greater_right have greater ratings smaller_right = (n - i - 1) - greater_right
2. Attempting to Find All Triplets Explicitly
Some developers try to use three nested loops to check all possible triplets:
Inefficient approach:
def numTeams(self, rating: List[int]) -> int:
count = 0
n = len(rating)
for i in range(n):
for j in range(i + 1, n):
for k in range(j + 1, n):
if (rating[i] < rating[j] < rating[k] or
rating[i] > rating[j] > rating[k]):
count += 1
return count
This has O(n³) time complexity and becomes inefficient for larger inputs.
3. Forgetting to Handle Both Ascending and Descending Cases
A common oversight is only counting one type of team:
Incomplete solution:
def numTeams(self, rating: List[int]) -> int:
total_teams = 0
n = len(rating)
for i in range(n):
current_rating = rating[i]
smaller_left = sum(1 for r in rating[:i] if r < current_rating)
greater_right = sum(1 for r in rating[i + 1:] if r > current_rating)
# Only counts ascending teams, forgets descending teams!
total_teams += smaller_left * greater_right
return total_teams
4. Using Incorrect Comparison Operators
Mixing up strict inequalities with non-strict ones:
Wrong:
# Using <= instead of < will count equal ratings incorrectly
smaller_left = sum(1 for r in rating[:i] if r <= current_rating)
Correct:
# Must use strict inequality since all ratings are unique
smaller_left = sum(1 for r in rating[:i] if r < current_rating)
5. Not Leveraging the Uniqueness Constraint
The problem states each soldier has a unique rating. This means we don't need to handle equal ratings, simplifying our logic. Some solutions unnecessarily complicate the code by trying to handle equal ratings that won't exist.
Solution Tips:
- Always draw out the index boundaries on paper first
- Test with small examples like
[1, 2, 3]
and[3, 2, 1]
to verify your counting logic - Remember that when a soldier is fixed as the middle element, you're essentially counting valid pairs on each side that can combine with it
Depth first search is equivalent to which of the tree traversal order?
Recommended Readings
Segment Tree Faster Range Queries For this article we want to introduce the idea of a Segment Tree Segment Trees allow us to quickly perform range queries as well as range updates Suppose we have an array and we want to know the sum of a particular range of numbers as well
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
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
Want a Structured Path to Master System Design Too? Don’t Miss This!