3397. Maximum Number of Distinct Elements After Operations
Problem Description
You are given an integer array nums
and an integer k
.
You are allowed to perform the following operation on each element of the array at most once:
- Add an integer in the range
[-k, k]
to the element.
Return the maximum possible number of distinct elements in nums
after performing the operations.
Intuition
To solve this problem, we can use a greedy strategy combined with sorting. The goal is to maximize the number of distinct elements in the array after modifying each element within the allowed range.
Key Steps:
-
Sorting: First, sort the array
nums
. Sorting helps us to process elements in a structured order so that we can apply the greedy approach effectively. -
Initial Setup: Initialize a variable
pre
to negative infinity to keep track of the latest distinct value we have achieved so far. This helps in ensuring that each new distinct value we choose is indeed distinct. -
Iterate and Adjust: For each element
x
in the sorted array:- Calculate the potential new value for the element as
min(x + k, max(x - k, pre + 1))
. - Here,
x - k
would make the element as small as possible, andpre + 1
ensures this new value is larger than the last distinct value. - Take the minimum of
x + k
and this calculated value to find the feasible distinct value for the current element. - If this value is larger than
pre
, we accept it, increase our distinct count, and updatepre
.
- Calculate the potential new value for the element as
-
Result: By iterating through the entire array with this logic, the algorithm ensures that it obtains as many distinct values as possible, given the permissible modifications.
This greedy approach effectively balances increasing each element's distinctness within the constraints of modification by k
.
Solution Approach
To solve the problem of maximizing distinct elements after modifying each element in the array nums
within a range [-k, k]
, we use a greedy approach with the help of sorting. Here is the step-by-step explanation of the solution:
-
Sorting: First, we sort the array
nums
. Sorting allows us to approach the problem systematically, dealing with smaller elements first and gradually moving towards larger ones, which helps in making effective use of the available range for modification. -
Initialize Variables:
- We initialize
ans
to 0 to keep track of the count of distinct elements we manage to achieve. - The variable
pre
is initialized to negative infinity (-inf
). This variable keeps track of the last distinct element we have placed. It ensures that the current element is modified to ensure distinctiveness.
- We initialize
-
Iterate through the Sorted Array:
- For each element
x
in the sorted arraynums
, calculate a potential new valuecur
. - The calculation for
cur
uses the formula:min(x + k, max(x - k, pre + 1))
.x - k
: This makes the element as small as possible to maximize the room for distinct values.pre + 1
: This ensures the new value is larger than the last distinct value to maintain distinctness.x + k
: Represents the maximum possible value within the range we can add tox
.
- We choose the smallest valid value
cur
that is larger thanpre
but still within the adjustment range ofx + k
.
- For each element
-
Update Count and Track:
- If
cur
(our new distinct candidate for this element) is greater thanpre
, we know it is a new distinct value. - Increment
ans
by 1. - Update
pre
tocur
, marking this as the latest distinct value considered.
- If
-
Return Result: After processing all elements,
ans
holds the maximum number of distinct elements that we can achieve after optimal adjustments.
This approach guarantees that each element is modified only once and optimally within the given constraints to achieve the maximum possible distinct values.
Here's the implementation in Python:
class Solution:
def maxDistinctElements(self, nums: List[int], k: int) -> int:
nums.sort()
ans = 0
pre = -float('inf')
for x in nums:
cur = min(x + k, max(x - k, pre + 1))
if cur > pre:
ans += 1
pre = cur
return ans
This carefully planned greedy strategy efficiently solves the problem with a time complexity dominated by the sorting step, i.e., O(n log n)
, where n
is the length of the array nums
.
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 walk through a small example to illustrate the solution approach.
Example:
nums = [1, 2, 2, 3]
k = 1
Step 1: Sorting the Array
First, sort the array nums
. In this case, the array is already sorted: [1, 2, 2, 3]
.
Step 2: Initialize Variables
- Set
ans
to0
to count distinct elements. - Set
pre
to-inf
as it will track the last distinct value achieved.
Step 3: Iterate through the Sorted Array
-
Element:
1
- Calculate
cur = min(1 + 1, max(1 - 1, -inf + 1))
cur = min(2, 1) = 1
cur > pre
(1 > -inf), so incrementans
to1
and updatepre
to1
.
- Calculate
-
Element:
2
- Calculate
cur = min(2 + 1, max(2 - 1, 1 + 1))
cur = min(3, 2) = 2
cur > pre
(2 > 1), so incrementans
to2
and updatepre
to2
.
- Calculate
-
Element:
2
- Calculate
cur = min(2 + 1, max(2 - 1, 2 + 1))
cur = min(3, 3) = 3
cur > pre
(3 > 2), so incrementans
to3
and updatepre
to3
.
- Calculate
-
Element:
3
- Calculate
cur = min(3 + 1, max(3 - 1, 3 + 1))
cur = min(4, 4) = 4
cur > pre
(4 > 3), so incrementans
to4
and updatepre
to4
.
- Calculate
Step 4: Return the Result
After processing the entire array, the value of ans
is 4
, which represents the maximum number of distinct elements we can achieve by modifying the array.
Solution Implementation
1from typing import List
2from math import inf
3
4class Solution:
5 def maxDistinctElements(self, nums: List[int], k: int) -> int:
6 # Sort the input list to handle elements in order.
7 nums.sort()
8
9 # Initialize the count of distinct elements.
10 distinct_count = 0
11
12 # Initialize the previous element encountered as negative infinity.
13 previous = -inf
14
15 for current in nums:
16 # Determine the smallest feasible number within range [current-k, current+k]
17 # that is greater than the previous element.
18 feasible_val = min(current + k, max(current - k, previous + 1))
19
20 # Check if this feasible value is greater than the previous element.
21 if feasible_val > previous:
22 # Increment the count of distinct elements.
23 distinct_count += 1
24
25 # Update the previous element to the feasible value.
26 previous = feasible_val
27
28 # Return the total count of distinct elements achievable.
29 return distinct_count
30
1import java.util.Arrays;
2
3class Solution {
4 public int maxDistinctElements(int[] nums, int k) {
5 // Sort the array to handle elements in a sequential manner
6 Arrays.sort(nums);
7
8 int n = nums.length; // Length of the array
9 int result = 0; // Variable to store the count of distinct elements
10 int previous = Integer.MIN_VALUE; // Tracks the previous maximum distinct element
11
12 // Loop through every element in the sorted array
13 for (int x : nums) {
14 // Calculate the current max distinct element
15 int current = Math.min(x + k, Math.max(x - k, previous + 1));
16
17 // If the current calculated element is greater than the previous one, it is distinct
18 if (current > previous) {
19 result++; // Increment the count of distinct elements
20 previous = current; // Update the previous element to the current one
21 }
22 }
23
24 // Return the total count of distinct elements
25 return result;
26 }
27}
28
1#include <vector>
2#include <algorithm>
3#include <climits>
4
5// Define the Solution class
6class Solution {
7public:
8 // Function to find the maximum number of distinct elements in the vector after modifying each element by at most k
9 int maxDistinctElements(std::vector<int>& nums, int k) {
10 // Sort the vector in non-decreasing order
11 std::sort(nums.begin(), nums.end());
12
13 int distinctCount = 0; // To count the number of distinct elements
14 int previous = INT_MIN; // To keep track of the previous element in the sequence
15
16 // Iterate through each number in the sorted vector
17 for (int number : nums) {
18 // Calculate the current number by making it as close to previous + 1 as possible, within the bounds of modifying by ยฑk
19 int current = std::min(number + k, std::max(number - k, previous + 1));
20
21 // Only consider the current number if it's greater than the previous
22 if (current > previous) {
23 ++distinctCount; // Increment distinct count
24 previous = current; // Update the previous element
25 }
26 }
27
28 // Return the count of distinct elements
29 return distinctCount;
30 }
31};
32
1/**
2 * Calculates the maximum number of distinct elements possible by adjusting
3 * each element within a range of [-k, k].
4 *
5 * @param nums - An array of numbers.
6 * @param k - An integer representing the range allowed for adjustment.
7 * @returns The maximum count of distinct elements achievable.
8 */
9function maxDistinctElements(nums: number[], k: number): number {
10 // Sort the input array in ascending order.
11 nums.sort((a, b) => a - b);
12
13 // Initialize the count of distinct elements and a variable to track the previous adjusted element.
14 let [ans, pre] = [0, -Infinity];
15
16 // Iterate over each element in the sorted array.
17 for (const x of nums) {
18 // Calculate the current adjusted element, within range [-k, k] and greater than previous.
19 const cur = Math.min(x + k, Math.max(x - k, pre + 1));
20
21 // If the adjusted current number is greater than the previous adjusted number...
22 if (cur > pre) {
23 // Increment the count of distinct elements.
24 ++ans;
25 // Update the previous adjusted element to the current one.
26 pre = cur;
27 }
28 }
29
30 // Return the maximum number of distinct elements.
31 return ans;
32}
33
Time and Space Complexity
The time complexity of the code is O(n \log n)
. This is because the code includes sorting the list nums
, which takes O(n \log n)
time, where n
is the length of the array nums
. The subsequent iteration through the list runs in O(n)
time, but the sorting step dominates, resulting in a total time complexity of O(n \log n)
.
The space complexity is O(\log n)
, which is used by the sorting algorithm. The built-in Python sort function is typically implemented with Timsort, which has a space complexity of O(\log n)
for putting items on the call stack during sorting.
Learn more about how to find time and space complexity quickly.
Which of the following uses divide and conquer strategy?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
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
Want a Structured Path to Master System Design Too? Donโt Miss This!