Problem Statement
Given an integer array nums, return the second largest distinct element in the array.
The answer must be strictly smaller than the largest element. Return -1 if fewer than two distinct values exist.
Example 1
Input: nums = [12, 35, 1, 10, 34, 1]
Output: 34
Explanation: The largest element is 35, and the second-largest distinct element is 34.
Example 2
Input: nums = [10, 10, 10]
Output: -1
Explanation: All elements are equal, so no second-largest distinct element exists.
Example 3
Input: nums = [5]
Output: -1
Explanation: A single element cannot have a second-largest element.
Brute Force Approach
Sorting arranges the values in non-decreasing order, placing the largest value at the final index.
The positions before the largest may contain duplicate copies of the same value. Moving backward and skipping those duplicates reveals the first value strictly smaller than the largest, which is the second-largest distinct element.
Algorithm
Store the array size in
n. Ifn < 2, return-1because two distinct values cannot exist.Create
sortedNumsas a copy ofnumsso that sorting does not change the original array.Sort
sortedNumsin non-decreasing order and take its last element aslargest, since sorting places the maximum value at the end.Start from index
n - 2and move backward while the current value equalslargest, as duplicate copies of the maximum cannot be considered distinct.Return the first value that is smaller than
largest, since it is the greatest distinct value below the maximum.If no such value is found, return
-1because the array contains fewer than two distinct values.
Dry Run
Second Largest Element Brute Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: int secondLargest(vector<int>& nums) { int n = nums.size(); /* * At least two values are needed * to have a distinct second largest. */ if (n < 2) { return -1; } vector<int> sortedNums = nums; sort(sortedNums.begin(), sortedNums.end()); int largest = sortedNums[n - 1]; /* * Move backward until a value smaller * than the maximum is found. */ for (int index = n - 2; index >= 0; index--) { /* * The first smaller value is the * second-largest distinct element. */ if (sortedNums[index] < largest) { return sortedNums[index]; } } // No second-largest distinct value exists. return -1; }};int main() { vector<int> nums = {8, 8, 5, 3, 5}; Solution solution; cout << solution.secondLargest(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N log N), where N represents the number of elements. Sorting requires O(N log N) time, while the backward scan takes at most O(N) time.
Space Complexity: O(N), because a copied array of size N preserves the original array. The sorting method may also use language-specific internal memory.
Better Approach
Sorting the entire array is unnecessary because only the largest and second-largest distinct values matter.
The largest value can be identified in the first traversal. A second traversal can then ignore every occurrence of the largest value and retain the greatest remaining value.
Algorithm
Store the array size in
n. Ifn < 2, return-1because a second distinct value cannot exist.Initialize
largestwithnums[0]and traverse the array once to find the maximum value.Keep
secondLargestfor the best valid candidate andhasSecondto indicate whether such a candidate has been found. The flag avoids relying on a fixed sentinel value.Traverse the array again and consider only values strictly smaller than
largest, since values equal to the maximum do not satisfy the distinctness requirement.If no second-largest candidate exists yet, or the current value is greater than
secondLargest, updatesecondLargestand markhasSecondas true.Return
secondLargestwhen a valid candidate exists; otherwise, return-1.
Dry Run
Second Largest Element Better Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: int secondLargest(vector<int>& nums) { int n = nums.size(); /* * At least two values are needed * to have a distinct second largest. */ if (n < 2) { return -1; } int largest = nums[0]; // First pass finds the largest value. for (int index = 1; index < n; index++) { if (nums[index] > largest) { largest = nums[index]; } } int secondLargest = 0; bool hasSecond = false; /* * Only values smaller than largest * can be valid second-largest values. */ for (int num : nums) { if (num < largest) { /* * Keep the greatest valid value * found below the maximum. */ if (!hasSecond || num > secondLargest) { secondLargest = num; hasSecond = true; } } } // No valid candidate was found. if (!hasSecond) { return -1; } return secondLargest; }};int main() { vector<int> nums = {8, 8, 5, 3, 5}; Solution solution; cout << solution.secondLargest(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N), where N represents the number of elements. Two complete traversals require O(2N) operations, which simplifies to O(N).
Space Complexity: O(1), because only the largest value, second-largest candidate, and validity flag require auxiliary storage.
Optimal Approach
Both required values can be maintained during a single traversal.
Start with the first array element as the current largest value. Whenever a larger value appears, the previous largest becomes the second-largest candidate before the new value replaces it.
If the current value is smaller than the largest, it can become the second largest only when no valid candidate exists yet or when it is greater than the current second-largest value.
Values equal to the largest are ignored because the required answer must be distinct.
Algorithm
Store the array size in
n. Ifn < 2, return-1because two distinct values cannot exist.Initialize
largestwithnums[0]. KeepsecondLargestfor the second-largest candidate andhasSecondto track whether a valid candidate has been found.Traverse the array from index
1, since the first element has already been used to initializelargest.If the current value is greater than
largest, move the previouslargestintosecondLargestbefore updatinglargest. The previous maximum now becomes the best value strictly below the new maximum.Otherwise, if the current value is strictly smaller than
largest, updatesecondLargestwhen no candidate exists yet or when the current value is greater than the existing candidate. Values equal tolargestare ignored to preserve distinctness.Return
secondLargestwhenhasSecondis true; otherwise, return-1.
Dry Run
Second Largest Element Optimal Force Dry Run.png
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: int secondLargest(vector<int>& nums) { int n = nums.size(); /* * At least two values are needed * to have a distinct second largest. */ if (n < 2) { return -1; } int largest = nums[0]; int secondLargest = 0; bool hasSecond = false; for (int index = 1; index < n; index++) { int current = nums[index]; /* * A new maximum moves the previous * maximum into second place. */ if (current > largest) { secondLargest = largest; largest = current; hasSecond = true; } /* * Values equal to largest are ignored * because the answer must be distinct. */ else if (current < largest) { /* * Keep the greatest valid value * found below the current maximum. */ if (!hasSecond || current > secondLargest) { secondLargest = current; hasSecond = true; } } } // No valid candidate was found. if (!hasSecond) { return -1; } return secondLargest; }};int main() { vector<int> nums = {8, 8, 5, 3, 5}; Solution solution; cout << solution.secondLargest(nums) << endl; return 0;}Complexity Analysis
Time Complexity: O(N), where N represents the number of elements. Every value is processed exactly once.
Space Complexity: O(1), because only two candidate values and two validity flags require auxiliary storage.
FAQs
Q1. Why do duplicate maximum values not qualify as the second-largest value?
The requirement asks for a distinct value strictly smaller than the maximum. Repeated maximum values represent the same distinct value.
Q2. Why are validity flags useful instead of fixed sentinel values?
Validity flags separate candidate existence from candidate value. Negative arrays and full integer ranges remain safe without dependence on artificial minimum constants.
Q3. Does the two-pass approach remain optimal in asymptotic complexity?
Yes. Two linear traversals still produce O(N) time. The one-pass approach reduces traversal count while preserving the same asymptotic complexity.
Q4. Can the index of the second-largest distinct element also be returned?
Yes. An additional index variable can be updated whenever secondLargest changes. If the value appears multiple times, the implementation should clearly define whether the first or any occurrence is returned.
Q5. How can the Kth-largest distinct value be found?
Sorting with duplicate removal, an ordered set, or a size-K heap can extend the same distinct-ranking requirement.
Be the first to add a comment.