747. Largest Number At Least Twice of Others
Problem Description
You are given an integer array nums
where the largest integer is unique (appears only once).
Your task is to determine if the largest element in the array is at least twice as large as every other number in the array.
- If the largest element is at least twice as large as all other elements, return the index of this largest element
- If the largest element is not at least twice as large as some other element, return
-1
For example:
- If
nums = [3, 6, 1, 0]
, the largest element is6
at index1
. Since6 >= 2 * 3
(the second largest), we return1
- If
nums = [1, 2, 3, 4]
, the largest element is4
at index3
. Since4 < 2 * 3
(the second largest), we return-1
The solution finds the two largest elements in the array using nlargest(2, nums)
to get the maximum value x
and second maximum value y
. It then checks if x >= 2 * y
. If this condition is satisfied, it returns the index of x
using nums.index(x)
, otherwise it returns -1
.
Intuition
The key insight is that if the largest element needs to be at least twice as large as every other element in the array, we only need to check it against the second-largest element.
Think about it this way: if we have elements sorted as [a, b, c, ..., second_largest, largest]
, and if largest >= 2 * second_largest
, then automatically largest >= 2 * c
, largest >= 2 * b
, and so on, because all these elements are smaller than or equal to second_largest
.
This observation dramatically simplifies our problem. Instead of comparing the maximum with every single element (which would require checking n-1
comparisons), we only need to:
- Find the largest element
- Find the second-largest element
- Check if
largest >= 2 * second_largest
If this single condition is satisfied, we know the largest dominates all other elements. If it fails, then the largest element is not dominant.
The solution elegantly uses nlargest(2, nums)
to extract the two largest values in one go, making the check straightforward: x >= 2 * y
. This reduces what could be an O(n) comparison problem after finding the max to just an O(1) check after finding the top two elements.
Learn more about Sorting patterns.
Solution Approach
The implementation uses a straightforward approach by finding the two largest elements in the array and checking the dominance condition.
Method 1: Using nlargest
(as shown in the code)
-
Extract the two largest elements: Use
nlargest(2, nums)
to get the largest valuex
and second-largest valuey
from the array in one operation. -
Check the dominance condition: Verify if
x >= 2 * y
. This single comparison determines if the largest element is at least twice as large as all other elements. -
Return the result:
- If the condition is satisfied, use
nums.index(x)
to find and return the index of the largest element - Otherwise, return
-1
- If the condition is satisfied, use
Method 2: Two-pass traversal (alternative approach from reference)
-
First pass - Find maximum and its index: Traverse the array once to find the maximum value
x
and its indexk
. -
Second pass - Validate dominance: Traverse the array again, and for each element at position
i
wherei != k
:- Check if
x < 2 * nums[i]
- If this condition is true for any element, return
-1
immediately
- Check if
-
Return the index: If the traversal completes without finding any violating element, return
k
.
Both methods have O(n) time complexity, but the first method using nlargest
is more concise and directly addresses the problem by focusing on the relationship between the largest and second-largest elements. The nlargest
function internally uses a heap-based approach to efficiently find the k largest elements.
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 the solution with the array nums = [1, 8, 3, 4]
.
Step 1: Find the two largest elements
Using nlargest(2, nums)
, we extract the two largest values:
- Largest element:
x = 8
- Second-largest element:
y = 4
Step 2: Check the dominance condition We need to verify if the largest element is at least twice the second-largest:
- Calculate
2 * y = 2 * 4 = 8
- Check if
x >= 2 * y
: Is8 >= 8
? Yes, this is true.
Step 3: Return the result Since the condition is satisfied, we find the index of the largest element:
nums.index(8)
returns index1
- Final answer:
1
Let's verify this makes sense: The largest element 8
needs to be at least twice every other element:
8 >= 2 * 1
? Yes (8 >= 2)8 >= 2 * 3
? Yes (8 >= 6)8 >= 2 * 4
? Yes (8 >= 8)
Since we only needed to check against the second-largest (4
), and that condition passed, we know 8
dominates all other elements.
Counter-example: If we had nums = [1, 8, 3, 5]
instead:
- Largest:
x = 8
, Second-largest:y = 5
- Check: Is
8 >= 2 * 5 = 10
? No,8 < 10
- Return:
-1
(the largest element doesn't dominate)
Solution Implementation
1from typing import List
2from heapq import nlargest
3
4class Solution:
5 def dominantIndex(self, nums: List[int]) -> int:
6 # Find the two largest elements in the array
7 # nlargest returns elements in descending order
8 largest, second_largest = nlargest(2, nums)
9
10 # Check if the largest element is at least twice as large as the second largest
11 # If true, return its index; otherwise return -1
12 if largest >= 2 * second_largest:
13 return nums.index(largest)
14 else:
15 return -1
16
1class Solution {
2 public int dominantIndex(int[] nums) {
3 int arrayLength = nums.length;
4
5 // Find the index of the maximum element
6 int maxIndex = 0;
7 for (int i = 0; i < arrayLength; ++i) {
8 if (nums[maxIndex] < nums[i]) {
9 maxIndex = i;
10 }
11 }
12
13 // Check if the maximum element is at least twice as large as every other element
14 for (int i = 0; i < arrayLength; ++i) {
15 // Skip comparing the maximum element with itself
16 if (maxIndex != i && nums[maxIndex] < nums[i] * 2) {
17 // Maximum element is not dominant (not at least twice as large)
18 return -1;
19 }
20 }
21
22 // Return the index of the dominant element
23 return maxIndex;
24 }
25}
26
1class Solution {
2public:
3 int dominantIndex(vector<int>& nums) {
4 int n = nums.size();
5
6 // Find the index of the maximum element
7 int maxIndex = 0;
8 for (int i = 0; i < n; ++i) {
9 if (nums[maxIndex] < nums[i]) {
10 maxIndex = i;
11 }
12 }
13
14 // Check if the maximum element is at least twice as large as every other element
15 for (int i = 0; i < n; ++i) {
16 // Skip comparing the maximum element with itself
17 if (maxIndex != i && nums[maxIndex] < nums[i] * 2) {
18 // Maximum element is not dominant (less than twice of some other element)
19 return -1;
20 }
21 }
22
23 // Maximum element satisfies the dominant condition
24 return maxIndex;
25 }
26};
27
1/**
2 * Finds the index of the dominant element in the array.
3 * A dominant element is at least twice as large as every other element.
4 *
5 * @param nums - Array of integers to search for dominant element
6 * @returns Index of the dominant element if it exists, otherwise -1
7 */
8function dominantIndex(nums: number[]): number {
9 // Find the index of the maximum element
10 let maxIndex: number = 0;
11 for (let i: number = 0; i < nums.length; ++i) {
12 if (nums[i] > nums[maxIndex]) {
13 maxIndex = i;
14 }
15 }
16
17 // Check if the maximum element is at least twice as large as all other elements
18 for (let i: number = 0; i < nums.length; ++i) {
19 // Skip comparison with itself
20 if (i !== maxIndex && nums[maxIndex] < nums[i] * 2) {
21 // Maximum element is not dominant (not at least twice as large)
22 return -1;
23 }
24 }
25
26 // Return the index of the dominant element
27 return maxIndex;
28}
29
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The nlargest(2, nums)
function needs to traverse the entire array to find the two largest elements, which takes O(n)
time. The nums.index(x)
operation also requires O(n)
time in the worst case to find the index of the maximum element. Since these operations are performed sequentially, the overall time complexity remains O(n)
.
The space complexity is O(1)
. The nlargest(2, nums)
function returns only 2 elements regardless of the input size, and the variables x
and y
use constant extra space. No additional data structures that scale with the input size are created.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Edge Case with Array Length Less Than 2
The current solution using nlargest(2, nums)
will fail when the array has only one element. For example, if nums = [1]
, calling nlargest(2, nums)
will return [1]
(a list with only one element), and attempting to unpack it into two variables largest, second_largest
will raise a ValueError
.
Solution: Handle the single-element case explicitly:
def dominantIndex(self, nums: List[int]) -> int:
if len(nums) == 1:
return 0 # Single element is always dominant
largest, second_largest = nlargest(2, nums)
if largest >= 2 * second_largest:
return nums.index(largest)
else:
return -1
Pitfall 2: Duplicate Maximum Values
While the problem states the largest integer is unique, if you modify this solution for similar problems where duplicates might exist, using nums.index(largest)
will always return the index of the first occurrence of the maximum value, which might not be the intended behavior.
Solution: If handling duplicates, track the index explicitly during the search:
def dominantIndex(self, nums: List[int]) -> int:
max_val = max(nums)
max_idx = nums.index(max_val)
for i, num in enumerate(nums):
if i != max_idx and max_val < 2 * num:
return -1
return max_idx
Pitfall 3: Performance with Very Large Arrays
While nlargest(2, nums)
has O(n) time complexity, it creates a heap internally and has higher constant overhead compared to a simple linear scan. For very large arrays or performance-critical applications, a single-pass solution might be more efficient.
Solution: Use a single-pass approach to find both the maximum and second maximum:
def dominantIndex(self, nums: List[int]) -> int:
if len(nums) == 1:
return 0
max_idx = 0
second_max = float('-inf')
# Find maximum and its index
for i in range(1, len(nums)):
if nums[i] > nums[max_idx]:
second_max = nums[max_idx]
max_idx = i
elif nums[i] > second_max:
second_max = nums[i]
return max_idx if nums[max_idx] >= 2 * second_max else -1
Consider the classic dynamic programming of longest increasing subsequence:
Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.
For example, the length of LIS for [50, 3, 10, 7, 40, 80]
is 4
and LIS is
[3, 7, 40, 80]
.
What is the recurrence relation?
Recommended Readings
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
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
Want a Structured Path to Master System Design Too? Donβt Miss This!