腾讯2020年校招[2/5]

腾讯2020年校招[2/5]

此博客用作本人进行校招试题及解法的记录,内容主要用于本人今后的阅读,故博客写法以便于本人后续阅读的方式为主。

试题

题目描述

小Q在周末的时候和他的小伙伴来到大城市逛街,一条步行街上有很多高楼,共有n座高楼排成一行。小Q从第一栋一直走到了最后一栋,小Q从来都没有见到这么多的楼,所以他想知道他在每栋楼的位置处能看到多少栋楼呢?(当前面的楼的高度大于等于后面的楼时,后面的楼将被挡住)

输入描述
输入第一行将包含一个数字n,代表楼的栋数,接下来的一行将包含n个数字wi(1<=i<=n)
代表每一栋楼的高度。
1<=n<=100000;1<=wi<=100000;
输出描述
输出一行,包含空格分割的n个数字vi,分别代表小Q在第i栋楼时能看到的楼的数量。
输入例子
6
5 3 8 3 2 5
输出例子
3 3 5 4 4 4
例子说明
当小Q处于位置3时,他可以向前看到位置2,1处的楼,向后看到位置4,6处的楼,加上第3栋
楼,共可看到5栋楼。当小Q处于位置4时,他可以向前看到位置3处的楼,向后看到位置5,6
处的楼,加上第4栋楼,共可看到4栋楼。

解答

通过阅读题目,我们最容易想到的解法就是暴力遍历,其时间复杂度为O(n2)用这种方法肯定会超时,使用单调栈可以完成算法在时间上的优化,将时间复杂度降低到O(nlogn)。
首先先了解一下什么是单调栈。
单调栈中存放的数据应该是有序的,分为单调递增栈和单调递减栈

  • 单调递增栈:数据出栈的序列为单调递增序列
  • 单调递减栈:数据出栈的序列为单调递减序列

这里一定要注意所说的递增递减指的是出栈的顺序,而不是在栈中数据的顺序

例子

现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。

  • 10入栈时,栈为空,直接入栈,栈内元素为10。
  • 3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
  • 7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
  • 4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
  • 12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
继续解答

通过上述方法我们就能设计出符合要求的算法。将每一个位置能看见的房屋看成左右两个部分,要求栈中数据从栈底到栈顶要递减,且左边的房屋要从左往右遍历,右边的房屋要从右往左遍历,且下一次遍历都是在上一次的结果上直接进行(左右遍历是独立的,需要两个栈,并用两个数组存储栈内所含元素的各数,这两个数分别标识向左看能看见的房屋数和向右看能看见的房屋数),这样一来就不用每一个元素都从头比较一遍。

代码
#include <stack>
#include <iostream>
using namespace std;

int main(){
    int building[1000000];
    int num, i;
    cin>>num;
    for(i = 0; i < num;i++){
        cin>>building[i];
    }
    stack<int> right;//向右看能看见的楼高度
    stack<int> left;//向左看能看见的楼高度
    int R[1000000], L[100000];//左右分别可以看见的楼的数量

    for(i = 0; i < num;i++){
        R[i] = right.size();
        if(i == 0)
            right.push(building[0]);
        else if (right.top() > building[i]){
            right.push(building[i]);
        }
        else{
            while(!right.empty() && right.top() <= building[i]){
                right.pop();
            }
            right.push(building[i]);
        }
    }

    for(i = num - 1; i >= 0;i--){
        L[i] = left.size();
        if(i == num - 1)
            left.push(building[num - 1]);
        else if (left.top() > building[i]){
            left.push(building[i]);
        }
        else{
            while(!left.empty() && left.top() <= building[i]){
                left.pop();
            }
            left.push(building[i]);
        }
    }
    for(i = 0;i < num; i++)
        cout<<L[i] + R[i] + 1 <<" ";
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值