单调栈——子数组范围和

原题在这里

概述:求给定数组的 所有子数组的极差 之和。

基本暴力:

 

class Solution
{
public:
    long long subArrayRanges(vector<int> &nums)
    {
        long long ans = 0, l = nums.size();
        int mx, mi;
        for (int i = 0; i < l; ++i)
        {
            mx = mi = nums[i];
            for (int j = i + 1; j < l; ++j)
            {
                mx = max(mx, nums[j]);
                mi = min(mi, nums[j]);
            }
            ans += mx - mi;
        }
        return ans;
    }
};
O(n^2)

 

要求进阶算法的复杂度为O(n),

进一步思考,

要求所有极差之和,换句话说,就是找子数组内的最大元素之和减去最小元素之和

因为所有子数组都会考虑,所以可以换成:

  考虑每一个数字作为最大/最小值的次数,进行累加累减即可

那么,转换成公式就是

对于元素nums[i],在区间[l,r]中,对最终答案有ans(+/-)=(i - l + 1) * (r - i + 1) * nums[i]

然后就是找区间最值的问题了,这里就用单调栈

 tips:

  1.最大最小相关,就应该想到单调栈

  2.求最大元素,用递减栈;求最小元素,用递增栈

  3.求左边的最值,应从左往右遍历;求右边最值,从右往左遍历(理应这样,也不一定)

 

【关键是怎么使用单调栈】

  我即便是已经知道用单调栈还是没想明白怎么写那个逻辑,还是经验+智慧不足了。

  1.找区间范围,所以入栈元素应该是下标

  2.怎么考虑左右端点的出栈or入栈情况,

      对于找nums[i]为最大值的区间,误!!!

      就是这里没想明白,起点应该是对于一个单调栈的top元素

   对于(找最大元素的递减栈)nums[stack.top()],如果nums[i]值更大,那么top出栈,

      此时,可以确定,top范围区间为

        左区间[stack[top-1],stack.top()]  +  右区间[stack.top(),i]

   ----------------------------------------------

   对于(找最小元素的递增栈)nums[stack.top()],如果nums[i]值更小,那么top出栈,

      区间也是一样的处理

【OVER】

下面就上代码:

 

class Solution
{
public:
    long long subArrayRanges(vector<int> &nums)
    {
        long long ans = 0, len = nums.size();
        for (int i = 0, l, r; i < len; i++)
        {
            //最大
            l = r = i;
            while (l - 1 >= 0 && nums[l - 1] <= nums[i])
                l--;
            while (r + 1 < len && nums[r + 1] < nums[i])
                r++;
            ans += (i - l + 1) * (r - i + 1) * (long long)nums[i];
            //最小
            l = r = i;
            while (l - 1 >= 0 && nums[l - 1] >= nums[i])
                l--;
            while (r + 1 < len && nums[r + 1] > nums[i])
                r++;
            ans -= (i - l + 1) * (r - i + 1) * (long long)nums[i];
        }
        return ans;
    }
};
while思路非栈版本

 

normal:

 

class Solution
{
    long long maxsum(vector<int> nums)
    {
        long long ans = 0, l = nums.size();
        stack<int> st;
        for (int i = 0; i <= l; ++i)
        {
            while (!st.empty() && (i == l || nums[st.top()] < nums[i]))
            {
                int last1 = st.top();
                st.pop();
                int last2 = st.empty() ? -1 : st.top();
                ans += (long long)nums[last1] * (i - last1) * (last1 - last2);
            }
            st.push(i);
        }
        return ans;
    }
    long long minsum(vector<int> nums)
    {
        long long ans = 0, l = nums.size();
        stack<int> st;
        for (int i = 0; i <= l; ++i)
        {
            while (!st.empty() && (i == l || nums[st.top()] > nums[i]))
            {
                int last1 = st.top();
                st.pop();
                int last2 = st.empty() ? -1 : st.top();
                ans += (long long)nums[last1] * (i - last1) * (last1 - last2);
            }
            st.push(i);
        }
        return ans;
    }

public:
    long long subArrayRanges(vector<int> &nums)
    {
        return maxsum(nums) - minsum(nums);
    }
};
普通写法max-min

 

精简:

 

class Solution
{
    long long sum(vector<int> nums, bool pd)
    {
        long long ans = 0, l = nums.size();
        stack<int> st;
        for (int i = 0; i <= l; ++i)
        {
            while (!st.empty() && (i == l || (nums[st.top()] > nums[i] && !pd) || (nums[st.top()] < nums[i] && pd)))
            {
                int last1 = st.top();
                st.pop();
                int last2 = st.empty() ? -1 : st.top();
                ans += (long long)nums[last1] * (i - last1) * (last1 - last2);
            }
            st.push(i);
        }
        return ans;
    }

public:
    long long subArrayRanges(vector<int> &nums)
    {
        return sum(nums, 1) - sum(nums, 0);
    }
};
一段函数

 

 

posted @ 2022-03-05 17:24  Renhr  阅读(72)  评论(0)    收藏  举报