Find the Median of the Uniqueness Array solution and explanation
4 more videos on this problem
3134. Find the Median of the Uniqueness Array (Leetcode Hard)Programming Live with Larry · 2:06:58 · 763 views
【每日一题】LeetCode 3134. Find the Median of the Uniqueness ArrayHuifeng Guan · 21:35 · 573 views
Find the Median of the Uniqueness Array - LEETCODE WEEKLY CONTEST 395Joyjit Codes · 5:31 · 516 views
DECODE DSA #LEVEL UP#CONTEST SOL#leetcode#challenge#3134.Find the Median of the Unique Array#LC-3134Decode · 7:18 · 272 views
Leetcode Weekly Contest 395 - D :- Median of Uniqueness ArrayAlgoChief · 19:24 · 188 views
The problem
You are given an integer array nums. The uniqueness array of nums is the sorted array that contains the number of distinct elements of all the subarrays of nums. In other words, it is a sorted array consisting of distinct(nums[i..j]), for all 0 <= i <= j < nums.length.
Here, distinct(nums[i..j]) denotes the number of distinct elements in the subarray that starts at index i and ends at index j.
Return the median of the uniqueness array of nums.
Note that the median of an array is defined as the middle element of the array when it is sorted in non-decreasing order. If there are two choices for a median, the smaller of the two values is taken.
Example 1:
Input: nums = [1,2,3]
Output: 1
Explanation:
The uniqueness array of nums is [distinct(nums[0..0]), distinct(nums[1..1]), distinct(nums[2..2]), distinct(nums[0..1]), distinct(nums[1..2]), distinct(nums[0..2])] which is equal to [1, 1, 1, 2, 2, 3]. The uniqueness array has a median of 1. Therefore, the answer is 1.
Example 2:
Input: nums = [3,4,3,4,5]
Output: 2
Explanation:
The uniqueness array of nums is [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3]. The uniqueness array has a median of 2. Therefore, the answer is 2.
Example 3:
Input: nums = [4,3,5,4]
Output: 2
Explanation:
The uniqueness array of nums is [1, 1, 1, 1, 2, 2, 2, 3, 3, 3]. The uniqueness array has a median of 2. Therefore, the answer is 2.
Constraints:
1 <= nums.length <= 1051 <= nums[i] <= 105
Approach overview
Problem Overview: You are given an integer array and must compute the uniqueness value (number of distinct elements) for every subarray. The task is to find the median among all those uniqueness values. Since an array of length n has n*(n+1)/2 subarrays, directly computing and sorting every uniqueness value quickly becomes too slow.
Approach 1: Brute Force with Hash Set (O(n²) time, O(n) space)
Iterate over every possible starting index and expand the subarray to the right. Maintain a hash set to track distinct elements in the current subarray. Each expansion updates the uniqueness count, which you store in a list. After generating all n*(n+1)/2 values, sort the list and return the middle element. This approach clearly models the problem but quickly becomes impractical because generating and sorting all uniqueness values costs O(n²) memory and up to O(n² log n) time.
Approach 2: Binary Search + Sliding Window (O(n log n) time, O(n) space)
The key insight: instead of explicitly generating all uniqueness values, you can binary search the median. Suppose you guess a value k. You only need to check how many subarrays have uniqueness ≤ k. If that count is at least the median position, the answer is ≤ k; otherwise it must be larger.
Counting subarrays with at most k distinct elements can be done in linear time using a sliding window. Move the right pointer while tracking frequencies in a hash table. If distinct elements exceed k, move the left pointer until the window becomes valid again. For every right pointer position, add the window length to the total count. This gives the number of valid subarrays in O(n) time.
Combine this counting routine with binary search over the possible uniqueness range [1, distinct(nums)]. Each step checks whether at least half of all subarrays have uniqueness ≤ k. The search converges to the smallest k that satisfies the median condition.
Recommended for interviews: The binary search + sliding window approach is what interviewers expect. Brute force demonstrates understanding of the uniqueness definition, but recognizing that the median can be found through counting and monotonic binary search shows strong algorithmic intuition and mastery of sliding window patterns.
Approach 1: Divide and Conquer
This approach involves dividing the problem into smaller subproblems, solving each subproblem independently, and combining the results. This is useful for problems that can be broken down recursively.
def divide_and_conquer(arr, left, right):
if left >= right:
return
mid = (left + right) // 2
divide_and_conquer(arr, left, mid)
divide_and_conquer(arr, mid + 1, right)
# Combine results
arr = [2, 4, 1, 5, 3]
divide_and_conquer(arr, 0, len(arr) - 1)
# Output resultpublic class DivideConquer {
void solve(int[] arr, int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
solve(arr, left, mid);
solve(arr, mid + 1, right);
// Combine results
}
public static void main(String[] args) {
int[] arr = {2, 4, 1, 5, 3};
new DivideConquer().solve(arr, 0, arr.length - 1);
// Output result
}
}#include <iostream>
#include <vector>
void divideAndConquer(std::vector<int>& arr, int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
divideAndConquer(arr, left, mid);
divideAndConquer(arr, mid + 1, right);
// Combine results
}
int main() {
std::vector<int> arr = {2, 4, 1, 5, 3};
divideAndConquer(arr, 0, arr.size() - 1);
// Output result
return 0;
}function divideAndConquer(arr, left, right) {
if (left >= right) return;
let mid = Math.floor((left + right) / 2);
divideAndConquer(arr, left, mid);
divideAndConquer(arr, mid + 1, right);
// Combine results
}
let arr = [2, 4, 1, 5, 3];
divideAndConquer(arr, 0, arr.length - 1);
// Output result#include <stdio.h>
void solve(int arr[], int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
solve(arr, left, mid);
solve(arr, mid + 1, right);
// Combine results
}
int main() {
int arr[] = {2, 4, 1, 5, 3};
solve(arr, 0, 4);
// Output result
return 0;
}using System;
public class DivideConquer {
public void Solve(int[] arr, int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
Solve(arr, left, mid);
Solve(arr, mid + 1, right);
// Combine results
}
public static void Main() {
int[] arr = {2, 4, 1, 5, 3};
new DivideConquer().Solve(arr, 0, arr.Length - 1);
// Output result
}
}This Python code sample uses a function to perform divide and conquer on an array, recursively processing each half. It's elegant for problems that benefit from recursive splitting.
Time Complexity: O(n log n)
Space Complexity: O(log n) due to recursion.
Approach 2: Iterative Solution With Stack
This approach uses an explicit stack to replace the function call stack, providing an iterative solution to problems that are typically solved recursively. It's beneficial in avoiding stack overflow and managing deeper recursion paths.
def iterative_solve(arr):
stack = [(0, len(arr) - 1)]
while stack:
left, right = stack.pop()
if left < right:
mid = (left + right) // 2
# Push subproblems onto stack
stack.append((mid + 1, right))
stack.append((left, mid))
arr = [2, 4, 1, 5, 3]
iterative_solve(arr)
# Output resultimport java.util.Stack;
public class IterativeSolution {
void solve(int[] arr) {
Stack<int[]> stk = new Stack<>();
stk.push(new int[]{0, arr.length - 1});
while (!stk.isEmpty()) {
int[] range = stk.pop();
int left = range[0], right = range[1];
if (left < right) {
int mid = (left + right) / 2;
// Push subproblems onto stack
stk.push(new int[]{mid + 1, right});
stk.push(new int[]{left, mid});
}
}
}
public static void main(String[] args) {
int[] arr = {2, 4, 1, 5, 3};
new IterativeSolution().solve(arr);
// Output result
}
}#include <iostream>
#include <stack>
#include <vector>
void iterativeSolve(std::vector<int>& arr) {
std::stack<int> stk;
stk.push(0);
stk.push(arr.size() - 1);
while (!stk.empty()) {
int right = stk.top(); stk.pop();
int left = stk.top(); stk.pop();
if (left < right) {
int mid = (left + right) / 2;
// Push subproblems onto stack
stk.push(mid + 1);
stk.push(right);
stk.push(left);
stk.push(mid);
}
}
}
int main() {
std::vector<int> arr = {2, 4, 1, 5, 3};
iterativeSolve(arr);
// Output result
return 0;
}function iterativeSolve(arr) {
let stack = [];
stack.push([0, arr.length - 1]);
while (stack.length > 0) {
let [left, right] = stack.pop();
if (left < right) {
let mid = Math.floor((left + right) / 2);
// Push subproblems onto stack
stack.push([mid + 1, right]);
stack.push([left, mid]);
}
}
}
let arr = [2, 4, 1, 5, 3];
iterativeSolve(arr);
// Output result#include <stdio.h>
#include <stdlib.h>
void iterativeSolve(int arr[], int n) {
int* stack = malloc(n * sizeof(int));
int top = -1;
stack[++top] = n - 1;
stack[++top] = 0;
while (top >= 0) {
int left = stack[top--];
int right = stack[top--];
if (left < right) {
int mid = (left + right) / 2;
// Push subproblems onto stack
stack[++top] = right;
stack[++top] = mid + 1;
stack[++top] = mid;
stack[++top] = left;
}
}
free(stack);
}
int main() {
int arr[] = {2, 4, 1, 5, 3};
iterativeSolve(arr, 5);
// Output result
return 0;
}using System;
using System.Collections.Generic;
public class IterativeSolution {
public void Solve(int[] arr) {
Stack<(int, int)> stack = new Stack<(int, int)>();
stack.Push((0, arr.Length - 1));
while (stack.Count > 0) {
var (left, right) = stack.Pop();
if (left < right) {
int mid = (left + right) / 2;
// Push subproblems onto stack
stack.Push((mid + 1, right));
stack.Push((left, mid));
}
}
}
public static void Main() {
int[] arr = {2, 4, 1, 5, 3};
new IterativeSolution().Solve(arr);
// Output result
}
}This Python code substitutes recursive function calls with an explicit stack, avoiding potential recursion depth issues while maintaining problem-solving integrity.
Time Complexity: O(n log n)
Space Complexity: O(n) due to explicit stack use.
Approach 3: Binary Search + Two Pointers
Let the length of the array nums be n. The length of the uniqueness array is m = \frac{(1 + n) times n}{2}, and the median of the uniqueness array is the \frac{m + 1}{2}-th smallest number among these m numbers.
Consider how many numbers in the uniqueness array are less than or equal to x. As x increases, there will be more and more numbers less than or equal to x. This property is monotonic, so we can use binary search to enumerate x and find the first x such that the number of elements in the uniqueness array less than or equal to x is greater than or equal to \frac{m + 1}{2}. This x is the median of the uniqueness array.
We define the left boundary of the binary search as l = 0 and the right boundary as r = n. Then we perform binary search. For each mid, we check whether the number of elements in the uniqueness array less than or equal to mid is greater than or equal to \frac{m + 1}{2}. We achieve this through the function check(mx).
The implementation idea of the function check(mx) is as follows:
Since the longer the subarray, the more different elements it contains, we can use two pointers to maintain a sliding window such that the number of different elements in the window does not exceed mx. Specifically, we maintain a hash table cnt, where cnt[x] represents the number of occurrences of element x in the window. We use two pointers l and r, where l represents the left boundary of the window and r represents the right boundary. Initially, l = r = 0.
We enumerate r. For each r, we add nums[r] to the window and update cnt[nums[r]]. If the number of different elements in the window exceeds mx, we need to move l to the right until the number of different elements in the window does not exceed mx. At this point, the subarrays with the right endpoint r and left endpoints in the range [l, .., r] all meet the condition, and there are r - l + 1 such subarrays. We accumulate this count into k. If k is greater than or equal to \frac{m + 1}{2}, it means that the number of elements in the uniqueness array less than or equal to mid is greater than or equal to \frac{m + 1}{2}, and we return true; otherwise, we return false.
The time complexity is O(n times log n), and the space complexity is O(n). Here, n is the length of the array nums.
function medianOfUniquenessArray(nums: number[]): number {
const n = nums.length;
const m = Math.floor(((1 + n) * n) / 2);
let [l, r] = [0, n];
const check = (mx: number): boolean => {
const cnt = new Map<number, number>();
let [l, k] = [0, 0];
for (let r = 0; r < n; ++r) {
const x = nums[r];
cnt.set(x, (cnt.get(x) || 0) + 1);
while (cnt.size > mx) {
const y = nums[l++];
cnt.set(y, cnt.get(y)! - 1);
if (cnt.get(y) === 0) {
cnt.delete(y);
}
}
k += r - l + 1;
if (k >= Math.floor((m + 1) / 2)) {
return true;
}
}
return false;
};
while (l < r) {
const mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}func medianOfUniquenessArray(nums []int) int {
n := len(nums)
m := (1 + n) * n / 2
return sort.Search(n, func(mx int) bool {
cnt := map[int]int{}
l, k := 0, 0
for r, x := range nums {
cnt[x]++
for len(cnt) > mx {
y := nums[l]
cnt[y]--
if cnt[y] == 0 {
delete(cnt, y)
}
l++
}
k += r - l + 1
if k >= (m+1)/2 {
return true
}
}
return false
})
}Complexity comparison
| Approach | Complexity |
|---|---|
| Divide and Conquer | Time Complexity: O(n log n) |
| Iterative Solution With Stack | Time Complexity: O(n log n) |
| Binary Search + Two Pointers | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force with Hash Set | O(n² log n) | O(n²) | Understanding the problem or when n is very small |
| Binary Search + Sliding Window | O(n log n) | O(n) | General optimal solution for large arrays |
Frequently asked questions
Is Find the Median of the Uniqueness Array easy or hard?
Find the Median of the Uniqueness Array is considered a Hard problem because it combines multiple techniques: counting subarrays, sliding window frequency tracking, and binary search on the answer. Recognizing that the median can be found through monotonic counting rather than explicit enumeration is the main challenge.
Find the Median of the Uniqueness Array Python/Java solution
In Python or Java, implement a helper function that counts subarrays with at most k distinct elements using a hash map and two pointers. Wrap this function inside a binary search over the possible uniqueness range. This pattern keeps the implementation concise while maintaining O(n log n) performance.
How to solve Find the Median of the Uniqueness Array in O(n log n)?
First compute the total number of subarrays and determine the median index. Then binary search the possible uniqueness values. For each candidate value k, use a sliding window with a hash table to count how many subarrays contain at most k distinct elements. If the count is at least the median position, move the search left; otherwise move right.
What is the best approach for Find the Median of the Uniqueness Array?
The most efficient approach uses binary search on the answer combined with a sliding window counting technique. For each candidate uniqueness value k, count how many subarrays have at most k distinct elements using a hash map and two pointers. Binary search over the range of possible distinct counts finds the smallest value that satisfies the median position. The overall complexity is O(n log n) time and O(n) space.
Is Find the Median of the Uniqueness Array asked at Google/Amazon/Meta?
Problems combining sliding window counting with binary search frequently appear in interviews at companies like Google, Amazon, and Meta. Variants of counting subarrays with at most k distinct elements are especially common because they test understanding of two‑pointer techniques and frequency maps.
What data structure is used in Find the Median of the Uniqueness Array?
The solution mainly relies on a hash table (or dictionary) to store frequencies of elements in the sliding window. Two pointers maintain the window boundaries, and binary search is used to determine the median uniqueness value without enumerating every subarray.
What is the time complexity of Find the Median of the Uniqueness Array?
The optimal solution runs in O(n log n) time. Each step of the binary search performs a sliding window scan of the array in O(n) time to count subarrays with at most k distinct values. Space complexity is O(n) due to the frequency hash table used by the sliding window.
