2475. Number of Unequal Triplets in Array
Problem Description
You are provided with an array of positive integers called nums
. Each element of the array has a 0-based index. The task is to find out how many groups of three indices (i, j, k)
exist such that the following conditions are met:
- The indices are in strictly increasing order, i.e.,
i < j < k
. - The values of
nums
at these indices are pairwise distinct. This means thatnums[i]
,nums[j]
, andnums[k]
should all be different from each other.
In other words, for a triplet (i, j, k)
to be counted, it must consist of three unique numbers from the nums
array, each coming from a unique position in the array, where i
, j
, and k
represent the positions (indices) of these numbers.
The goal is to return the count of all such triplets.
Intuition
To solve this problem, we need to find a way to calculate the number of distinct triplets without having to manually check each possible combination, which would be inefficient especially for large arrays.
We can use the following approach:
- Count the frequency of each number in the array using a data structure like a dictionary (
Counter
in Python). - Iterate through each unique number's frequency, calculating how many distinct triplets can be formed with it.
The intuition stems from the fact that if we know how many times a particular number occurs, and how many numbers are before and after it in the array (that haven't been used in a triplet yet), we can calculate all the valid triplets it can form.
Here are the steps taken in the solution code:
- Use
Counter
to get the frequency of each distinct number innums
. - Initialize two counters,
ans
anda
.ans
will hold the final count of triplets, anda
will keep track of the count of numbers processed so far. - Loop through the frequency count of each distinct number:
- Let
b
represent the frequency of the current number. - Calculate
c
as the number of remaining elements that come after the current number, which isn - a - b
(where n is the total length ofnums
). - The number of triplets we can form with the current number as the middle element is
a * b * c
. This product comes from selecting one of the numbers before the current one (a
choices), the current number itself (b
choices), and one of the numbers after (c
choices). - Add this triplet count to the
ans
. - Update
a
to include the current number as well by addingb
to it.
- Let
- The final answer is contained in
ans
, so we return it.
By using this method, we efficiently calculate the number of distinct triplets without the need to individually consider each possible combination of three numbers.
Solution Approach
The solution approach is based on the concept of counting and combinatorics. To implement the solution, the following components are used:
-
Data Structures:
- We use the
Counter
class from Python'scollections
module to efficiently count the frequency of each element in thenums
array. - A dictionary-like container maps unique elements to their respective frequencies.
- We use the
-
Variables:
cnt
: ACounter
object that holds the frequency of each unique element innums
.n
: The total number of elements innums
.ans
: An accumulator to sum the number of valid triplets found.a
: Keeps track of the number of elements processed so far (left side of the current element).b
: The frequency of the current element being considered as the middle element of a triplet.c
: Represents the number of elements that are available to be picked after the current element.
-
Algorithm:
- Count the frequency of each unique number using
cnt = Counter(nums)
. - Initialize a variable
ans
to accumulate the total number of valid triplets and a variablea
to keep track of the count of numbers already processed. - Iterate over each count
b
in the values ofcnt
(since keys represent the unique numbers and values their respective counts):- Calculate the number of available elements after the current element (which could potentially be the third element of the triplet) as
c = n - a - b
. - Calculate the number of triplets possible with the current number as the middle element as
a * b * c
and add this toans
. - Update
a
to include the element just processed by addingb
to it.
- Calculate the number of available elements after the current element (which could potentially be the third element of the triplet) as
- After the loop ends,
ans
holds the total number of valid triplets, which is returned as the final answer.
- Count the frequency of each unique number using
Each step of the algorithm is designed to avoid checking each triplet individually by leveraging the frequency counts and positions of numbers to calculate the number of potential triplets directly. This leads to a more efficient implementation that can handle large arrays without incurring a performance penalty typical of brute-force solutions.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider an example nums
array to illustrate the solution approach:
nums = [1, 1, 2, 2, 3]
From the problem statement, we want to count distinct triplets (i, j, k)
such that nums[i]
, nums[j]
, and nums[k]
are all unique.
- First, we use
Counter
to get the frequency of each unique number innums
.
from collections import Counter cnt = Counter(nums) # cnt will be Counter({1: 2, 2: 2, 3: 1})
-
We set
n
to the total number of elements innums
, which is5
. -
Initialize the variables
ans
for the total number of valid triplets anda
for counting processed numbers.
ans = 0 a = 0
-
Now, we iterate over each count
b
in the values ofcnt
. The dictionarycnt
has elements along with their frequencies:{1: 2, 2: 2, 3: 1}
.- First iteration (for number 1):
b
for number 1 is2
.c = n - a - b
which is5 - 0 - 2 = 3
. There are three elements that can come after the first1
to form a triplet.- Triplet count for
1
isa * b * c = 0 * 2 * 3 = 0
(sincea
is 0, no triplets can be formed with 1 as the middle element yet). - Update
a = a + b
to2
(since we've now processed two occurrences of 1).
- Second iteration (for number 2):
b
for number 2 is2
.c = n - a - b
which is5 - 2 - 2 = 1
. There is one element that can come after2
to form a triplet.- Triplet count for
2
isa * b * c = 2 * 2 * 1 = 4
. We can form 4 triplets with2
as the middle element. - Update
ans
by adding the triplet count,ans = ans + 4
to4
. - Update
a = a + b
to4
(as we've now processed two occurrences of 2).
- Third iteration (for number 3):
b
for number 3 is1
.c = n - a - b
which is5 - 4 - 1 = 0
. There are no elements left to form triplets with3
as the middle element.- Triplet count for
3
isa * b * c = 4 * 1 * 0 = 0
. No additional triplets can be formed. ans
remains4
.
- First iteration (for number 1):
-
After the iteration ends,
ans
holds the total number of valid triplets. For this example, there are 4 valid triplets, and thus we return4
.
To conclude, using this example with nums = [1, 1, 2, 2, 3]
, the algorithm efficiently found the total number of 4 distinct triplets without having to check each possible combination of three numbers.
Solution Implementation
1from collections import Counter
2
3class Solution:
4 def unequalTriplets(self, nums: List[int]) -> int:
5 # Count the frequency of each number in nums
6 number_counts = Counter(nums)
7 total_numbers = len(nums)
8
9 # Initialize the answer and the count for the first number in the triplet
10 answer = count_first_number = 0
11
12 # Iterating through the counts of each unique number
13 for count_second_number in number_counts.values():
14 # Count of the third number in the triplet is total_numbers
15 # minus the count_first_number and count_second_number
16 count_third_number = total_numbers - count_first_number - count_second_number
17
18 # Calculate the number of triplets where the first, second, and third numbers
19 # are all different
20 answer += count_first_number * count_second_number * count_third_number
21
22 # Increment count_first_number for the next iteration of the loop
23 count_first_number += count_second_number
24
25 # Return the total number of unequal triplets
26 return answer
27
1class Solution {
2 public int unequalTriplets(int[] nums) {
3 // Create a map to store the frequency of each number in the array
4 Map<Integer, Integer> frequencyMap = new HashMap<>();
5 for (int num : nums) {
6 // If the number is already in the map, increment its frequency,
7 // otherwise insert it with frequency 1
8 frequencyMap.merge(num, 1, Integer::sum);
9 }
10
11 // Initialize the answer variable to store the count of unequal triplets
12 int answer = 0;
13
14 // Variable 'prefixCount' is used to keep track of the count of numbers processed so far
15 int prefixCount = 0;
16
17 // 'n' is the total number of elements in the input array
18 int n = nums.length;
19
20 // Iterate through the frequency map
21 for (int frequency : frequencyMap.values()) {
22 // Calculate the count of numbers remaining after excluding the current number
23 int suffixCount = n - prefixCount - frequency;
24
25 // Update the answer by adding the number of unequal triplets that can be formed
26 answer += prefixCount * frequency * suffixCount;
27
28 // Update the prefix count by adding the frequency of the current number
29 prefixCount += frequency;
30 }
31
32 // Return the final count of unequal triplets
33 return answer;
34 }
35}
36
1#include <vector>
2#include <unordered_map>
3using namespace std;
4
5class Solution {
6public:
7 // This function counts the number of unequal triplets within the input vector.
8 int unequalTriplets(vector<int>& nums) {
9 // Create a hash table to keep track of the count of each number in the vector.
10 unordered_map<int, int> numCounts;
11 // Increment the count for each value in the nums vector.
12 for (int value : nums) {
13 ++numCounts[value];
14 }
15
16 int result = 0; // Variable to store the result.
17 int accumulated = 0; // Accumulated counts of previous numbers.
18
19 // Calculate the number of unequal triplets.
20 for (auto& [value, count] : numCounts) {
21 int remaining = nums.size() - accumulated - count; // Count of numbers that are not equal to current number.
22 result += accumulated * count * remaining; // Multiply by count to form unequal triplets.
23 accumulated += count; // Update accumulated with count of the current number.
24 }
25
26 // Return the total number of unequal triplets.
27 return result;
28 }
29};
30
1function unequalTriplets(nums: number[]): number {
2 // Find the length of the nums array
3 const lengthOfNums = nums.length;
4 // Initialize a map to keep count of each number's occurrences
5 const countMap = new Map<number, number>();
6
7 // Count the occurrences of each number in the nums array
8 for (const num of nums) {
9 countMap.set(num, (countMap.get(num) ?? 0) + 1);
10 }
11
12 // Initialize variable to hold the result
13 let result = 0;
14 // 'accumulated' will keep track of the accumulated counts for each processed number
15 let accumulated = 0;
16
17 // Iterate over the map to calculate the answer using the formula
18 for (const currentCount of countMap.values()) {
19 // Calculate the count for the third element in the triplet
20 const remaining = lengthOfNums - accumulated - currentCount;
21 // Calculate the number of unequal triplets for the current iteration
22 result += accumulated * currentCount * remaining;
23 // Update the accumulated count
24 accumulated += currentCount;
25 }
26
27 // Return the total number of unequal triplets
28 return result;
29}
30
Time and Space Complexity
Time Complexity
The time complexity of the code is primarily determined by the number of operations within the for loop which iterates over the values of the Counter
object cnt
. The Counter
object itself is created by iterating over the nums
list once, which takes O(n)
time where n
is the number of elements in nums
.
Inside the for loop, each operation is constant time, so the overall time complexity of the for loop is O(k)
, where k
is the number of unique elements in nums
. Since k
can vary from 1
to n
, in the worst case where all elements are unique, k
is equal to n
.
Therefore, the total time complexity is O(n) + O(k) = O(n)
as the first iteration to create Counter
and the second iteration over unique values can be bounded by n
.
Space Complexity
The space complexity is affected by the storage used for the Counter
object cnt
which stores the frequency of each unique element in nums
. In the worst case, if all elements of nums
are unique, the Counter
would take O(n)
space where n
is the number of elements in nums
.
Thus, the overall space complexity of the code is O(n)
.
Learn more about how to find time and space complexity quickly using problem constraints.
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
LeetCode 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 we
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 algomonster s3 us east 2 amazonaws com recursion jpg You first
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!