1534. Count Good Triplets
Problem Description
You are given an array of integers arr
and three integer values a
, b
, and c
. Your task is to count how many "good triplets" exist in the array.
A triplet consists of three elements from the array: (arr[i], arr[j], arr[k])
. For a triplet to be considered "good", it must satisfy all of the following conditions:
- The indices must be in strictly increasing order:
0 <= i < j < k < arr.length
- The absolute difference between the first and second elements must not exceed
a
:|arr[i] - arr[j]| <= a
- The absolute difference between the second and third elements must not exceed
b
:|arr[j] - arr[k]| <= b
- The absolute difference between the first and third elements must not exceed
c
:|arr[i] - arr[k]| <= c
Here, |x|
represents the absolute value of x
.
For example, if arr = [3, 0, 1, 1, 9, 7]
, a = 7
, b = 2
, and c = 3
:
- The triplet
(arr[0], arr[2], arr[3])
=(3, 1, 1)
is good because:|3 - 1| = 2 <= 7
(satisfies condition witha
)|1 - 1| = 0 <= 2
(satisfies condition withb
)|3 - 1| = 2 <= 3
(satisfies condition withc
)
Your goal is to return the total count of all good triplets in the array.
Intuition
Since we need to find triplets that satisfy specific conditions on their pairwise differences, the most straightforward approach is to check every possible triplet in the array.
The key insight is that we're looking for combinations of three elements where their indices follow a specific order (i < j < k
). This naturally leads us to think about using three nested loops - one for each position in the triplet.
For each potential triplet, we need to verify three conditions involving absolute differences. The brute force approach works well here because:
- We must examine the relationships between all three pairs of elements:
(arr[i], arr[j])
,(arr[j], arr[k])
, and(arr[i], arr[k])
- There's no obvious way to predict whether a triplet will be "good" without actually checking all three conditions
- The conditions are interdependent - even if two elements satisfy one condition, we can't guarantee the third element will make a valid triplet
The solution naturally emerges as a triple nested loop where:
- The outer loop picks the first element at index
i
- The middle loop picks the second element at index
j
(wherej > i
) - The inner loop picks the third element at index
k
(wherek > j
)
For each combination, we simply check if all three absolute difference conditions are met. If they are, we count it as a good triplet. This exhaustive search guarantees we won't miss any valid triplets while maintaining the required index ordering.
Solution Approach
The implementation uses a brute force enumeration approach with three nested loops to check all possible triplets.
Algorithm Steps:
-
Initialize a counter
ans
to 0 to keep track of good triplets, and get the lengthn
of the input array. -
Use three nested loops to generate all possible triplets with indices
(i, j, k)
:- The outer loop iterates
i
from0
ton-1
- The middle loop iterates
j
fromi+1
ton-1
(ensuringj > i
) - The inner loop iterates
k
fromj+1
ton-1
(ensuringk > j
)
- The outer loop iterates
-
For each triplet
(arr[i], arr[j], arr[k])
, check all three conditions:abs(arr[i] - arr[j]) <= a
abs(arr[j] - arr[k]) <= b
abs(arr[i] - arr[k]) <= c
-
If all three conditions are satisfied (using logical AND), increment the counter by 1. In Python, this is elegantly done by adding the boolean result (which evaluates to 1 for True, 0 for False).
-
After checking all possible triplets, return the final count.
Time Complexity: O(n³)
where n is the length of the array, as we have three nested loops each iterating through the array.
Space Complexity: O(1)
as we only use a constant amount of extra space for the counter and loop variables.
The solution leverages Python's ability to treat boolean expressions as integers (True = 1, False = 0), allowing us to directly add the result of the condition check to our answer counter. This makes the code more concise while maintaining readability.
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 to illustrate the solution approach.
Input: arr = [1, 5, 3, 2]
, a = 2
, b = 3
, c = 4
We'll systematically check all possible triplets using three nested loops:
Step 1: Initialize
ans = 0
(counter for good triplets)n = 4
(array length)
Step 2: Iterate through all triplets
When i = 0
(arr[0] = 1):
- When
j = 1
(arr[1] = 5):- When
k = 2
(arr[2] = 3):- Triplet: (1, 5, 3)
- Check: |1-5| = 4 > 2 (fails condition with
a
) - Not a good triplet
- When
k = 3
(arr[3] = 2):- Triplet: (1, 5, 2)
- Check: |1-5| = 4 > 2 (fails condition with
a
) - Not a good triplet
- When
- When
j = 2
(arr[2] = 3):- When
k = 3
(arr[3] = 2):- Triplet: (1, 3, 2)
- Check: |1-3| = 2 ≤ 2 ✓, |3-2| = 1 ≤ 3 ✓, |1-2| = 1 ≤ 4 ✓
- All conditions satisfied!
ans = 1
- When
When i = 1
(arr[1] = 5):
- When
j = 2
(arr[2] = 3):- When
k = 3
(arr[3] = 2):- Triplet: (5, 3, 2)
- Check: |5-3| = 2 ≤ 2 ✓, |3-2| = 1 ≤ 3 ✓, |5-2| = 3 ≤ 4 ✓
- All conditions satisfied!
ans = 2
- When
When i = 2
(arr[2] = 3):
- When
j = 3
(arr[3] = 2):- No valid k (k must be > j = 3, but array ends at index 3)
Step 3: Return result
- Total good triplets found:
2
The algorithm correctly identifies the two good triplets: (1, 3, 2) at indices (0, 2, 3) and (5, 3, 2) at indices (1, 2, 3).
Solution Implementation
1class Solution:
2 def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
3 """
4 Count the number of good triplets in the array.
5 A triplet (arr[i], arr[j], arr[k]) is good if:
6 - 0 <= i < j < k < n
7 - |arr[i] - arr[j]| <= a
8 - |arr[j] - arr[k]| <= b
9 - |arr[i] - arr[k]| <= c
10
11 Args:
12 arr: Input array of integers
13 a: Maximum allowed difference between arr[i] and arr[j]
14 b: Maximum allowed difference between arr[j] and arr[k]
15 c: Maximum allowed difference between arr[i] and arr[k]
16
17 Returns:
18 Number of good triplets in the array
19 """
20 # Initialize counter and get array length
21 count = 0
22 n = len(arr)
23
24 # Iterate through all possible triplets with i < j < k
25 for i in range(n):
26 for j in range(i + 1, n):
27 for k in range(j + 1, n):
28 # Check if current triplet satisfies all three conditions
29 if (abs(arr[i] - arr[j]) <= a and
30 abs(arr[j] - arr[k]) <= b and
31 abs(arr[i] - arr[k]) <= c):
32 count += 1
33
34 return count
35
1class Solution {
2 /**
3 * Counts the number of good triplets in the array.
4 * A triplet (arr[i], arr[j], arr[k]) is good if:
5 * - 0 <= i < j < k < arr.length
6 * - |arr[i] - arr[j]| <= a
7 * - |arr[j] - arr[k]| <= b
8 * - |arr[i] - arr[k]| <= c
9 *
10 * @param arr the input array
11 * @param a the maximum allowed difference between arr[i] and arr[j]
12 * @param b the maximum allowed difference between arr[j] and arr[k]
13 * @param c the maximum allowed difference between arr[i] and arr[k]
14 * @return the count of good triplets
15 */
16 public int countGoodTriplets(int[] arr, int a, int b, int c) {
17 int arrayLength = arr.length;
18 int goodTripletCount = 0;
19
20 // Iterate through all possible triplets with i < j < k
21 for (int i = 0; i < arrayLength; i++) {
22 for (int j = i + 1; j < arrayLength; j++) {
23 // Early termination: skip if first condition not met
24 if (Math.abs(arr[i] - arr[j]) > a) {
25 continue;
26 }
27
28 for (int k = j + 1; k < arrayLength; k++) {
29 // Check all three conditions for a good triplet
30 boolean firstCondition = Math.abs(arr[i] - arr[j]) <= a;
31 boolean secondCondition = Math.abs(arr[j] - arr[k]) <= b;
32 boolean thirdCondition = Math.abs(arr[i] - arr[k]) <= c;
33
34 if (firstCondition && secondCondition && thirdCondition) {
35 goodTripletCount++;
36 }
37 }
38 }
39 }
40
41 return goodTripletCount;
42 }
43}
44
1class Solution {
2public:
3 int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
4 // Get the size of the input array
5 int arraySize = arr.size();
6
7 // Initialize counter for good triplets
8 int goodTripletsCount = 0;
9
10 // Iterate through all possible triplets (i, j, k) where i < j < k
11 for (int i = 0; i < arraySize; ++i) {
12 for (int j = i + 1; j < arraySize; ++j) {
13 for (int k = j + 1; k < arraySize; ++k) {
14 // Check if current triplet satisfies all three conditions:
15 // 1. |arr[i] - arr[j]| <= a
16 // 2. |arr[j] - arr[k]| <= b
17 // 3. |arr[i] - arr[k]| <= c
18 bool isFirstConditionMet = abs(arr[i] - arr[j]) <= a;
19 bool isSecondConditionMet = abs(arr[j] - arr[k]) <= b;
20 bool isThirdConditionMet = abs(arr[i] - arr[k]) <= c;
21
22 // If all conditions are satisfied, increment the counter
23 if (isFirstConditionMet && isSecondConditionMet && isThirdConditionMet) {
24 goodTripletsCount++;
25 }
26 }
27 }
28 }
29
30 // Return the total count of good triplets
31 return goodTripletsCount;
32 }
33};
34
1/**
2 * Counts the number of good triplets in an array.
3 * A triplet (arr[i], arr[j], arr[k]) is good if:
4 * - 0 <= i < j < k < arr.length
5 * - |arr[i] - arr[j]| <= a
6 * - |arr[j] - arr[k]| <= b
7 * - |arr[i] - arr[k]| <= c
8 *
9 * @param arr - The input array of numbers
10 * @param a - Maximum allowed absolute difference between arr[i] and arr[j]
11 * @param b - Maximum allowed absolute difference between arr[j] and arr[k]
12 * @param c - Maximum allowed absolute difference between arr[i] and arr[k]
13 * @returns The count of good triplets
14 */
15function countGoodTriplets(arr: number[], a: number, b: number, c: number): number {
16 const arrayLength: number = arr.length;
17 let goodTripletsCount: number = 0;
18
19 // Iterate through all possible triplets with i < j < k
20 for (let i = 0; i < arrayLength; i++) {
21 for (let j = i + 1; j < arrayLength; j++) {
22 // Early termination: skip if first condition not met
23 if (Math.abs(arr[i] - arr[j]) > a) {
24 continue;
25 }
26
27 for (let k = j + 1; k < arrayLength; k++) {
28 // Check all three conditions for a good triplet
29 const firstCondition: boolean = Math.abs(arr[i] - arr[j]) <= a;
30 const secondCondition: boolean = Math.abs(arr[j] - arr[k]) <= b;
31 const thirdCondition: boolean = Math.abs(arr[i] - arr[k]) <= c;
32
33 if (firstCondition && secondCondition && thirdCondition) {
34 goodTripletsCount++;
35 }
36 }
37 }
38 }
39
40 return goodTripletsCount;
41}
42
Time and Space Complexity
The time complexity is O(n³)
, where n
is the length of the array arr
. This is because the code uses three nested loops:
- The outer loop runs from
i = 0
ton-1
, executingn
times - The middle loop runs from
j = i+1
ton-1
, executing approximatelyn-i-1
times for eachi
- The inner loop runs from
k = j+1
ton-1
, executing approximatelyn-j-1
times for eachj
The total number of iterations is the number of ways to choose 3 indices from n
elements, which is C(n,3) = n(n-1)(n-2)/6 = O(n³)
.
The space complexity is O(1)
because the algorithm only uses a constant amount of extra space. The variables ans
, n
, i
, j
, and k
are the only additional storage used, regardless of the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Inefficient Early Termination Opportunities
The brute force solution checks all three conditions for every triplet, even when early termination could save computation time. If the first condition |arr[i] - arr[j]| <= a
fails, there's no need to continue checking that particular j
with the current i
for any k
values.
Solution: Add early termination by checking conditions progressively:
class Solution:
def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
count = 0
n = len(arr)
for i in range(n):
for j in range(i + 1, n):
# Early termination: if first condition fails, skip to next j
if abs(arr[i] - arr[j]) > a:
continue
for k in range(j + 1, n):
# Check remaining conditions only if first condition passed
if (abs(arr[j] - arr[k]) <= b and
abs(arr[i] - arr[k]) <= c):
count += 1
return count
2. Index Boundary Optimization
The current solution iterates i
all the way to n-1
, but since we need at least 3 elements for a triplet, we can stop earlier.
Solution: Optimize loop boundaries:
class Solution:
def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
count = 0
n = len(arr)
# Stop i at n-3 since we need at least 2 more elements after i
for i in range(n - 2):
# Stop j at n-2 since we need at least 1 more element after j
for j in range(i + 1, n - 1):
if abs(arr[i] - arr[j]) > a:
continue
for k in range(j + 1, n):
if (abs(arr[j] - arr[k]) <= b and
abs(arr[i] - arr[k]) <= c):
count += 1
return count
3. Missing Edge Case Handling
While the original solution handles most cases correctly, developers might forget to validate input constraints or handle edge cases like very small arrays.
Solution: Add input validation:
class Solution:
def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
# Handle edge case: array too small for any triplet
if len(arr) < 3:
return 0
count = 0
n = len(arr)
for i in range(n - 2):
for j in range(i + 1, n - 1):
if abs(arr[i] - arr[j]) > a:
continue
for k in range(j + 1, n):
if (abs(arr[j] - arr[k]) <= b and
abs(arr[i] - arr[k]) <= c):
count += 1
return count
These optimizations can provide modest performance improvements, especially for larger arrays or when many triplets fail the first condition.
In a binary min heap, the maximum element can be found in:
Recommended 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 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
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 represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!