Facebook Pixel

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 is 6 at index 1. Since 6 >= 2 * 3 (the second largest), we return 1
  • If nums = [1, 2, 3, 4], the largest element is 4 at index 3. Since 4 < 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Find the largest element
  2. Find the second-largest element
  3. 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)

  1. Extract the two largest elements: Use nlargest(2, nums) to get the largest value x and second-largest value y from the array in one operation.

  2. 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.

  3. 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

Method 2: Two-pass traversal (alternative approach from reference)

  1. First pass - Find maximum and its index: Traverse the array once to find the maximum value x and its index k.

  2. Second pass - Validate dominance: Traverse the array again, and for each element at position i where i != k:

    • Check if x < 2 * nums[i]
    • If this condition is true for any element, return -1 immediately
  3. 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 Evaluator

Example 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: Is 8 >= 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 index 1
  • 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
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

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

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More