【c++实现】单调栈计算右边第一个最大值的距离

学校的练习题:

现在,给你n个整数,这n个整数表示在这n天内,同学们每天的预算。你的任务是重新生成一个具有n个元素的列表v,v[i]表示对于第i天的预算,如果想要得到比第i天更高的预算,至少需要等待的天数。如果没有,则记为0。

输入

第一行,一个整数n
第二行,n个整数,表示每天的预算m

数据说明如下:

  • 1≤n≤100000
  • 1≤m≤100000

输出

n行,每行一个整数,第i行的整数是v[i]。

样例输入

8
73 74 75 71 69 72 76 73

样例输出

1
1
4
2
1
1
0
0

 对于这道题呢:

考点可能是这个:

因为给的数据比较多,如果用一个数组录入数据并且一个个计算,时间复杂度会很大,可能导致超时(虽然学校给的样例数据过水,一个数组的方法可以过),所以我们考虑用栈,因为栈可以及时弹出“无关项”,大大减少了我们每次计算的时间。

那么使用栈的思路简单叙述如下:(以从栈顶到栈底单调递增的单调栈为例子)

1、由于这道题只考虑右边的第一个最大数字,我们不妨倒序来历遍 输入数据的数组arr[N]

例如:

从右往左开始压栈,arr[N-1]一定会压入,因为没得比较,并且这里输出答案v[N-1]一定是0;

然后看倒数第二个数字arr[N-2]:如果它比前一个小,那么压入栈实现单调递增,此时答案v[N-2]=1,因为它右边第一个数就比它大了嘛;如果它比前一个大,那么就弹出前一个,并且自己压入,此时答案v[N-2]=0,因为它比右边的大,并且再右边已经没有数字了;

2、照此思路,我们考虑如果在某一个位置,假设它是arr[N-x],它准备压入栈了,先比较栈顶元素,发现大于栈顶元素,我们决定要弹出栈顶,并且再比较弹出后的栈的栈顶元素,发现还是arr[N-x]比较大,所以继续……终于,有一个时刻栈顶元素Y大于arr[N-x]了,那么我们就压入栈,此时,元素Y在arr数组的位置与N-x的差,就是arr[N-x]右边第一个比它大的元素离它的距离;

(注意,这里不是说弹出多少个元素就是差多少,因为这些元素入栈时可能也弹出过其他元素,这是不准确的,因此,我们需要栈内有另一个变量记录下arr[i]的i这个值)

3、由于是从右往左循环的,所以栈顶的元素就是每个即将压入栈的元素 它右边第一个最大的元素。

因此,我们给出代码实现:

#include "iostream"
using namespace std;

struct node{
    int val;//数值 
    int pos;//在数组中的编号 
    node* next;

    node()
    {
        pos=0;
        val=0;
        next=nullptr;
    }
};

class list{
    public:
        list()
        {
            head=new node;
        }

        ~list()
        {
            node* temp=head->next;
            while(temp!=nullptr)
            {
                head->next=temp->next;
                delete temp;
                temp=head->next;
            }
            delete head;
        }

        bool empty()
        {
            if(head->next==nullptr)
            return true;
            else
            return false;
        }

        void push(int x,int pos)
        {
            node* temp=new node;
            temp->val=x;
            temp->pos=pos;
            temp->next=head->next;
            head->next=temp;
            //cout<<"successflly push "<<x<<" into the stack"<<endl;
        }
        int peek()
        {
            if(head->next!=nullptr)
            return head->next->val;
            else
            return 100001;
        }
        int peek_pos()//方便计算距离的 
        {
            if(head->next!=nullptr)
            return head->next->pos;
            else
            return 0;
        }
        void pop()
        {
            node* temp=head->next;
            head->next=temp->next;
            delete temp;
            //cout<<"successflly pop"<<endl;
        }
        void print()
        {
            node* temp=head->next;
            while(temp!=nullptr)
            {   
                cout<<temp->val<<"->";
                temp=temp->next;
            }
            //cout<<"empty"<<endl;
        }

    private:
        node* head;
};



int main()
{
    int n;
    cin>>n;
    int* arr=new int[n];//输入的数据存放在这个数组里 
    int* ans=new int[n];//答案数据存放在这个数组里 

    for(int i=0;i<n;i++)
    {
        cin>>arr[i];//输入数据 
    }

    
    list stack;

    for(int i=n-1;i>=0;i--) //倒序历遍输入的数据的数组 
    {
        if(stack.empty())//为空直接push就可以了 
        {
            stack.push(arr[i],i);
            ans[i]=0;//答案自然为0,因为右边(我们是从右往左历遍的)不会有再大的数了 
        }
        else if(arr[i]>=stack.peek())//如果输入元素大于栈顶元素,说明不是单调的 
        {
            while(arr[i]>=stack.peek())//持续比较,直到栈顶元素大于输入元素 
									   //(这里会有一个最大值100001,就是栈为空的时候)
            {
                stack.pop();//弹出多余无关的数据 
            }

            if(stack.empty())//同上,如果全都弹完了,就可以直接push了 
            {
                ans[i]=0;
                stack.push(arr[i],i);
            }
            else//否则计算相邻两个的pos之差就是距离(因为这是第一个比输入数据大的) 
            {
                ans[i]=stack.peek_pos()-i;//先计算再push,不然只会得到0! 
                stack.push(arr[i],i);
            }
        }
        else if(arr[i]<=stack.peek())//如果本来输入就小于栈顶,直接就是1了 
        {
            ans[i]=stack.peek_pos()-i;
            stack.push(arr[i],i);
            
        }
    }

    for(int i=0;i<n;i++)
        cout<<ans[i]<<endl;//输出答案就可以了! 


    delete arr;
    delete ans;//new的动态数组一定要delete释放!
	 
    return 0; 
}

当然,可能会有更加简单的办法,蛋果我也只是初学者&学生一枚,欢迎各位正在解决相似问题的小伙伴指出不足:D

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值