2020字节跳动笔试第一题:单调栈(和延伸)

排队队伍中,站在每个同学X前面的所有同学中,第一个比X高的同学的身高,如果没有这个同学,则输出-1.
输入:第一行:排队同学个数
第二行:队伍中同学的身高
(多组输入)
输出:对应身高

例:
输入:
5
5,4,3,2,1
5
1,2,3,4,5
输出:
-1,5,4,3,2
-1,-1,-1,-1,-1

这道题是第一题,首先问题在多组输入,一开始按一组输入做的,浪费时间。。。
怎么多组输入呢?

input_list = []
    try:
        lis = []
        while True:
            line = sys.stdin.readline().strip()
            if line == '':
                break
            line = list(map(int, line.split()))
            input_list.append(line)
    except:
        pass

这个时候就是将所有的行读进来,并按整形保存每个数,列表保存每一行,并去除其他换行符等。
然后这个题目,需要两行两行的进行计算,用for分开就可以了。

然后搞好了输入,在算法中直接用了两个for循环,暴力求解,果然超时。懵逼之后不知所措,放弃做之后的题目去了。。。(当然之后的也不会做。。。)
回来看了下大神指点,说这个题目要用到单调栈。

什么是单调栈呢?
我的理解就是给你随机的一串数,让你按顺序把他们压进栈,栈中的数只能单调递增或者单调递减。如果最新进栈的数会破坏这个规则,那么就把栈内的数弹出,知道当前数符合要求为止。
比如一串数[5,3,6,8,2,1,6]
按单调递增的顺序:
[5]
3<5, 5弹出,[3]
[3,6]
[3,6,8]
2<8, 8弹出,2<6, 6弹出,2<3, 3弹出,[2]
1<2, 2 弹出,[1]
[1,6]
最后留下的一个单调递增的栈
单调递减同样的道理。

单调栈可以用来求出左起或右起第一个比自己大或者小的元素。

最后的代码:

import sys

def next_max_num(n,line):
    d=[-1]*n
    stack = []
    for i in range(n):
        while stack and line[i] > stack[-1]:  # 判断新的数是否是大数
            stack.pop()  #如果是,把之前比它小的数弹出
        d[i] = stack[-1] if stack != [] else -1  #保存当前位置上比他大的第一个数,如果没有保留-1
        stack.append(line[i])  #将大数压进栈

    return d

if __name__ =='__main__':
    input_list = []
    output_list = []
    try:
        lis = []
        while True:
            line = sys.stdin.readline().strip()
            if line == '':
                break
            line = list(map(int, line.split()))
            input_list.append(line)
    except:
        pass

    length = len(input_list)
    for i in range(int(length/2)):
        result = next_max_num(input_list[2*i][0], input_list[2*i+1])
        print(str(result).strip('[]').replace(',',' '))
#         print (' '.join(map(str, result)))

相关拓展:
Leetcode 84. Largest Rectangle in Histogram
Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.

For example,
Given heights = [2,1,5,6,2,3],
return 10.
思路: 找到直方图中左起第一个比自己小的数,和右起第一个比自己小的数
单调递增栈,栈中存放索引

import sys
import numpy as np

def maxmumofhis(height):
    n=len(height)
    stack = []
    index_left=[0]*n
    index_right=[0]*n
    for i in range(n):     # 正向遍历,找到左起第一个比自己小的数的索引,若相等也要弹出,因为同样可以构成矩形
        while stack and height[i]<=height[stack[-1]]:
            stack.pop()
        index_left[i] = stack[-1] if stack!=[] else -1
        stack.append(i)

    stack = []
    for i in range(n):      # 逆向遍历,找到右起第一个比自己小的数的索引,若相等也要弹出,因为同样可以构成矩形
        while stack and height[n-i-1]<=height[stack[-1]]:
            stack.pop()
        index_right[n-i-1] = stack[-1] if stack!=[] else n
        stack.append(n-i-1)

    width = np.array(index_right)-np.array(index_left)-1
    area = height*width
    area.sort()
    return area[n-1]


if __name__=='__main__':
    try:
        lis=[]
        while True:
            line = sys.stdin.readline().strip()
            if line == '':
                break
            line = list(map(int, line.split()))
            lis.append(line)
    except:
        pass

    for i in lis:
        area = maxmumofhis(i)
        print(area)

85 最大矩形leetcode
给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
输入:
[
[“1”,“0”,“1”,“0”,“0”],
[“1”,“0”,“1”,“1”,“1”],
[“1”,“1”,“1”,“1”,“1”],
[“1”,“0”,“0”,“1”,“0”]
]
输出: 6

**动态规划+单调栈思路:**和求最大矩形面积的思路相同,问题是把矩阵转化成直方图。
扫描每一行,求每一行的最大矩阵面积,如果是1则高度累加,如果是0,则高度归零。留下最后最大的值。
第一行:
1 0 1 0 0:
对应直方图:
在这里插入图片描述
最大面积:1

扫描第二行:2 0 2 1 1
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200720232505173.png
最大面积:3

扫描第三行: 3 1 3 2 2
在这里插入图片描述
最大面积:6

扫描第四行:4 0 0 3 0
在这里插入图片描述
最大面积:4

所以最终最大面积为:6

代码实现:

import sys
import numpy as np


def convert_histogram(lis):
    row = len(lis)
    col = len(lis[0])
    height = [0]*col
    res = 0

    for i in range(row):
        for j in range(col):
            if lis[i][j] == 1:
                height[j] += 1
            else:
                height[j] = 0
        res = max(res, maxarea(height))
    return res


def maxarea(height):
    n = len(height)
    stack = []
    index_left = [0]*n
    index_right = [0]*n

    for i in range(n):
        while stack and height[i] <= height[stack[-1]]:
            stack.pop()
        index_left[i] = stack[-1] if stack!=[] else -1
        stack.append(i)

    stack = []
    for i in range(n):
        while stack and height[n - i - 1] <= height[stack[-1]]:
            stack.pop()
        index_right[n - i - 1] = stack[-1] if stack != [] else n
        stack.append(n - i - 1)

    width = np.array(index_right) - np.array(index_left) - 1
    area = height * width
    area.sort()
    return area[n - 1]


if __name__=='__main__':
    try:
        lis=[]
        while True:
            line = sys.stdin.readline().strip()
            if line == '':
                break
            line = list(map(int, line.split()))
            lis.append(line)
    except:
        pass
        
    maxret = convert_histogram(lis)
    print(maxret)

PS:
有些文章中说这道题的两种思路:单调栈和动态规划。
但是我看了一下两种思路有点懵逼,我觉得这两个本身就是一种思想啊,就是每一行遍历,求出第i行的最大面积,然后和第i+1行的结果进行比较,再保留最大数。
其实遍历行的过程,就是动态规划,
求最大面积的过程,就是单调栈的两次使用。
我是这么想的,如果有朋友能指出这两种思路上本质的区别,麻烦赐教。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值