算法题 至少是其他数字两倍的最大数

747. 至少是其他数字两倍的最大数

问题描述

给你一个整数数组 nums,其中总是存在一个最大元素。判断数组中最大元素是否至少是数组中每个其他元素的两倍。如果是,则返回最大元素的下标;否则,返回 -1。

示例

输入: nums = [3,6,1,0]
输出: 1
解释: 6 是最大元素,6 >= 2*3, 6 >= 2*1, 6 >= 2*0,所以返回下标 1
输入: nums = [1,2,3,4]
输出: -1
解释: 4 是最大元素,但 4 < 2*3,所以返回 -1
输入: nums = [1]
输出: 0
解释: 只有一个元素,返回下标 0

算法思路

一次遍历法

  1. 遍历数组找到最大值及其下标
  2. 再次遍历检查最大值是否至少是每个其他元素的两倍
  3. 优化:只需要检查第二大的元素,因为如果最大值 ≥ 2×第二大,则必然 ≥ 2×其他所有元素

核心思想:要验证最大值是否至少是所有其他元素的两倍,只需验证它是否至少是第二大元素的两倍。

代码实现

方法一:一次遍历(推荐解法)

class Solution {
    /**
     * 判断最大元素是否至少是其他元素的两倍
     * 
     * @param nums 整数数组
     * @return 如果满足条件返回最大元素下标,否则返回-1
     */
    public int dominantIndex(int[] nums) {
        // 输入校验
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int n = nums.length;
        
        // 特殊情况:只有一个元素
        if (n == 1) {
            return 0;
        }
        
        // 步骤1:找到最大值和第二大值
        int max = Integer.MIN_VALUE;
        int secondMax = Integer.MIN_VALUE;
        int maxIndex = -1;
        
        for (int i = 0; i < n; i++) {
            if (nums[i] > max) {
                // 发现新的最大值
                secondMax = max;  // 原来的最大值变成第二大
                max = nums[i];
                maxIndex = i;
            } else if (nums[i] > secondMax) {
                // 发现新的第二大值(但不是最大值)
                secondMax = nums[i];
            }
        }
        
        // 步骤2:检查最大值是否至少是第二大值的两倍
        // 如果满足,则必然满足对所有其他元素的条件
        if (max >= 2 * secondMax) {
            return maxIndex;
        } else {
            return -1;
        }
    }
}

方法二:优化(一次遍历)

class Solution {
    /**
     * 优化:在一次遍历中完成
     * 
     * @param nums 输入数组
     * @return 结果下标
     */
    public int dominantIndex(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int n = nums.length;
        if (n == 1) {
            return 0;
        }
        
        int maxIndex = 0;  // 最大值下标
        int secondMax = Integer.MIN_VALUE; // 第二大值
        
        // 遍历数组
        for (int i = 1; i < n; i++) {
            if (nums[i] > nums[maxIndex]) {
                // 更新最大值和第二大值
                secondMax = nums[maxIndex];
                maxIndex = i;
            } else if (nums[i] > secondMax) {
                // 更新第二大值
                secondMax = nums[i];
            }
        }
        
        // 检查条件
        return nums[maxIndex] >= 2 * secondMax ? maxIndex : -1;
    }
}

方法三:暴力解法

class Solution {
    /**
     * 暴力解法:先找最大值,再检查每个元素
     * 时间复杂度O(n),但常数因子较大
     * 
     * @param nums 输入数组
     * @return 结果下标
     */
    public int dominantIndex(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int n = nums.length;
        if (n == 1) {
            return 0;
        }
        
        // 找到最大值和其下标
        int max = nums[0];
        int maxIndex = 0;
        
        for (int i = 1; i < n; i++) {
            if (nums[i] > max) {
                max = nums[i];
                maxIndex = i;
            }
        }
        
        // 检查最大值是否至少是每个其他元素的两倍
        for (int i = 0; i < n; i++) {
            if (i != maxIndex && max < 2 * nums[i]) {
                return -1;
            }
        }
        
        return maxIndex;
    }
}

算法分析

  • 时间复杂度:O(n)

    • 方法一和二:遍历数组一次
    • 方法三:遍历数组两次
    • 都是线性时间
  • 空间复杂度:O(1)

    • 只使用常数额外空间
  • 方法对比

    • 方法一:逻辑清晰,易于理解
    • 方法二:代码更简洁
    • 方法三:直观但效率稍低

算法过程

nums = [3,6,1,0]

  1. 遍历过程

    • i=0: max=3, maxIndex=0, secondMax=MIN_VALUE
    • i=1: 6>3secondMax=3, max=6, maxIndex=1
    • i=2: 1<61>3? 否 → secondMax保持3
    • i=3: 0<60<3 → 无变化
  2. 检查条件

    • max=6, secondMax=3
    • 6 >= 2*36 >= 6
  3. 返回maxIndex=1

测试用例

public static void main(String[] args) {
    Solution solution = new Solution();
    
    // 测试用例1:满足条件
    int[] nums1 = {3,6,1,0};
    System.out.println("Test 1: " + solution.dominantIndex(nums1)); // 1
    
    // 测试用例2:不满足条件
    int[] nums2 = {1,2,3,4};
    System.out.println("Test 2: " + solution.dominantIndex(nums2)); // -1
    
    // 测试用例3:单元素
    int[] nums3 = {1};
    System.out.println("Test 3: " + solution.dominantIndex(nums3)); // 0
    
    // 测试用例4:两个元素满足
    int[] nums4 = {1,2};
    System.out.println("Test 4: " + solution.dominantIndex(nums4)); // 1
    
    // 测试用例5:两个元素不满足
    int[] nums5 = {3,5};
    System.out.println("Test 5: " + solution.dominantIndex(nums5)); // -1
    
    // 测试用例6:空数组
    int[] nums6 = {};
    System.out.println("Test 6: " + solution.dominantIndex(nums6)); // -1
    
    // 测试用例7:包含零
    int[] nums7 = {0,0,0,1};
    System.out.println("Test 7: " + solution.dominantIndex(nums7)); // 3
    
    // 测试用例8:相等元素
    int[] nums8 = {1,1,1,1};
    System.out.println("Test 8: " + solution.dominantIndex(nums8)); // -1
}

关键点

  1. 关键

    • 只需检查第二大元素
    • 如果最大值 ≥ 2×第二大,则必然 ≥ 2×所有其他元素
  2. 边界处理

    • 空数组
    • 单元素数组
    • 包含零的情况
  3. 整数溢出

    • 2 * secondMax 可能溢出
    • 可以改用 max >= secondMax * 2 或比较 max / 2 >= secondMax
  4. 算法效率

    • 一次遍历即可完成

常见问题

  1. 为什么只需检查第二大元素?

    • 因为第二大元素是除最大值外最大的
    • 如果最大值 ≥ 2×第二大,则必然 ≥ 2×所有更小的元素
  2. 如何处理负数?

    • 算法同样适用
    • 注意负数的两倍关系
  3. 能否用排序?

    • 可以,但时间复杂度更高O(n log n)
  4. 最大可能的值?

    • LeetCode测试用例在int范围内
  5. 如何返回所有满足条件的?

    • 题目保证只有一个最大元素
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值