山峰三元组问题的O(n)解法

题目的大致描述如下:
给你一个下标从 0 开始的整数数组 nums 。
如果下标三元组 (i, j, k) 满足下述全部条件,则认为它是一个 山形三元组 :i < j < k
,nums[i] < nums[j] 且 nums[k] < nums[j]
请你找出 nums 中 元素和最小的山形三元组,并返回其元素和 。如果不存在满足条件的三元组,返回 -1 。

简单分析一下这个题目的思路。
1.题意很简单,大家估计都能看懂,我们第一时间最直观的想法就是三层for循环,分别遍历每个i,j,k然后判断合理性即可找到满足要求的最小值,如果找不到返回-1即可.但是这种做法的时间复杂度达到了O(n^3),这显然不是一个可以让人接受的时间复杂度,对于n=1000的数据都会超时(1e8),所以我们考虑如何用优化。
2.其实这个题目大家看一下它的题目,我们可以发现这样一个问题,每次是不是就是去找每一个元素的左侧和右侧的最小元素,然后比较一下和当前元素,如果都比当前位置i的元素nums[i]要小,那么就是一个合法的元素我们就可以进行更新最终结果,那么现在的问题是如何去保存前缀和后缀最小值,这个其实很简单我们只需要从后向前以及从前向后遍历一遍,利用一个数组来保存从当前位置到第1个元素位置的最小值,到最后也同理。但是这里我们还可以做一个优化就是,我们只需要求后缀最小值,前缀最小值可以在for循环遍历过程,边遍历边更新,初始化就是nums[0]
枚举 nums[j],我们需要求出 j 左边所有元素的最小值和右边所有元素的最小值。

这可以递推计算。定义 back[i]表示从 nums[i]到 nums[n−1]的最小值(后缀最小值),则有back[i]=min⁡(back[i+1],nums[i])
前缀最小值 pre的计算方式同理,可以和答案一起算,所以只需要一个变量。

那么答案就是:**pre+nums[j]+back[j+1]**的最小值

具体实现代码如下:(相关注释已经标注)

int minimumSum(vector<int>& nums) {
        int n = nums.size();
        vector<int>back(n);
        back[n - 1] = nums[n - 1];//初始化第1个back数组 也就是后缀最小值为nums的最后一个元素
        for(int i = n - 2;i > 0;i--){
            if(nums[i] < back[i + 1]){
                back[i] = nums[i];//如果遇到更小的 更新当前back[i]=nums[i]
            }else{
                back[i] = back[i + 1];//否则就是back[i+1]
            }
        }
        int pre = nums[0],minn = 205;//前缀最小初始为nums[0],然后遍历过程不断更新
        for(int i = 1;i < n - 1;i++){
            if(nums[i] > pre && nums[i] > back[i + 1]){
                minn = min(minn, nums[i] + pre + back[i + 1]);
            }
            pre = min(pre, nums[i]);
        }
        return minn == 205 ? -1:minn;

    }
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值