数组入门练习:寻找数组的最大峰值下标

NC107 寻找峰值

给出一个长度为 n n n 的数组 a r r arr arr,找出下标 i i i,满足 a r r i − 1 ≤ a r r i ≤ a r r i + 1 arr_{i-1} \le arr_i \le arr_{i+1} arri1arriarri+1。特别的:

  • i = 0 i = 0 i=0 时,可忽略 a r r i − 1 ≤ a r r i arr_{i-1} \le arr_i arri1arri 的限制。
  • i = n − 1 i = n-1 i=n1 时,可忽略 a r r i ≤ a r r i + 1 arr_i \le arr_{i+1} arriarri+1 的限制。

如果存在多个满足条件的下标,则输出最大的下标。为了方便描述,我们将这个下标成为最大峰值下标。

比如:

  • 输入为 [2,2,1,2,8,8,4],最大峰值下标为 5
  • 输入为 [1],最大峰值下标为 0
  • 输入为 [1,2],最大峰值下标为 1
  • 输入为 [2,1],最大峰值下标为 0

这个算是简单题了,按照题目寻找下标即可:枚举 i ∈ [ 0 , n ) i ∈[0,n) i[0,n),对每个 i i i 检查其左右两侧的元素是否存在且符合要求。

class Solution {
public:
    int solve(int* arr, int n) {
        int anw = 0;
        for (int i = 0; i < n; i++) {
            bool left = true, right = true;
            if (i != 0 && arr[i-1] > arr[i]) {
                left = false;
            }
            if (i != n-1 && arr[i] < arr[i+1]) {
                right = false;
            }
            if (left && right) {
                anw = i;
            }
        }
        return anw;
    }
};

考虑到要寻找「最大的」满足限制的下标,因此我们倒着枚举位置 i i i,在找到第一个满足限制的 i i i 时可直接返回,可省略变量 a n w anw anw

class Solution {
public:
    int solve(int* arr, int n) {
        for (int i = n-1; i >= 0; i--) {
            bool left = true, right = true;
            if (i != 0 && arr[i-1] > arr[i]) {
                left = false;
            }
            if (i != n-1 && arr[i] < arr[i+1]) {
                right = false;
            }
            if (left && right) {
                return i;
            }
        }
        return 0;
    }
};

当你读完上面这段代码时,是否意识到一个问题:最大峰值下标一定存在吗?接下来,先证明一下最大峰值下标的存在性。

考虑长度为 1 1 1 的情形,最大峰值下标必然为 0 0 0。利用数学归纳法,先假设长度为 n n n 的数组 a r r arr arr 必然存在最大峰值下标 i i i。我们向 a r r arr arr 尾部添加任意元素 x x x,构造一个长度为 n + 1 n+1 n+1 的数组 a r r ′ arr' arr x x x a r r n − 1 arr_{n-1} arrn1 的大小关系可分为以下两种:

  • a r r n − 1 > x arr_{n-1} \gt x arrn1>x,则 a r r ′ arr' arr 的最大峰值下标仍为 i i i
  • a r r n − 1 ≤ x arr_{n-1} \le x arrn1x,则 a r r ′ arr' arr 的最大峰值下标为 n n n

综上所述,当数组不为空时,最大峰值下标必然存在。利用上述证明过程,甚至可以 O ( n ) O(n) O(n) 的求出任意前缀的最大峰值下标。

class Solution {
public:
    int solve(int* arr, int n) {
        std::vector<int> anw(n, 0);
        for (int i = 1; i < n; i++) {
            if (arr[i-1] <= arr[i]) {
                anw[i] = i;
            } else {
                anw[i] = anw[i-1];
            }
        }
        return anw[n-1];
    }
};

分析上述代码,不难发现 a n w n − 1 anw_{n-1} anwn1 必然满足:

  • n = 1 n = 1 n=1 时, a n w n − 1 = a n w 0 = 0 anw_{n-1} = anw_{0} = 0 anwn1=anw0=0
  • n > 1 n > 1 n>1 时, a n w n − 1 anw_{n-1} anwn1 为最大的满足 a r r i − 1 ≤ a r r i arr_{i-1} \le arr_{i} arri1arri 的下标 i i i

于是,代码可以精简为:

class Solution {
public:
    int solve(int* arr, int n) {
        for (int i = n-1; i >= 1; i--) {
            if (arr[i] > arr[i-1]) {
                return i;
            }
        }
        return 0;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值