栈——单调栈

栈的特性:后进先出

栈是一种数据结构,数组是实现它的方式

在python中,stack=[]

stack.append(),往数组末尾添加一个元素

stack.pop(),数组末尾的元素弹出

这样就实现了一个栈的数据结构

1.普通栈,往栈里存放元素

例1:判断字符串括号是否合法

注:

  1. 字符串为空,直接返回true
  2. 字符串为奇数,直接返回false
'''
0616
判断字符串括号是否合法
'''


def isvalid(s: str) -> bool:
    # 字符串为空,直接返回true
    if len(s) == 0:
        return True
    # 字符串为奇数,直接返回false
    if len(s) % 2 == 1:
        return False
    # 遍历字符串,如果是(入栈,)出栈
    stack = []
    for i in s:
        if i == "(":
            stack.append(i)
        elif i == ")":
            # 这里如果字符串只包含),栈弹出时会报错
            # stack.pop()
            if len(stack) == 0:
                return False
            stack.pop()
        else:
            return False
    # 遍历完之后,查看栈内剩余字符
    # 这里只用返回栈长度是否等于0即可
    # if len(stack) == 0:
    #     return True
    # else:
    #     return False
    return len(stack) == 0


if __name__ == '__main__':
    s = '))'
    print(isvalid(s))
    assert isvalid("")
    assert not isvalid("(")
    assert not isvalid(")")

    assert isvalid("()")
    assert not isvalid("((")
    assert not isvalid("))")
    assert not isvalid(")(")

    assert not isvalid("())")
    assert not isvalid("(((")
    assert not isvalid(")))")
    assert not isvalid(")()")

    assert isvalid("()()")
    assert isvalid("(())")
    assert not isvalid("))((")

    assert isvalid("()()()")
    assert isvalid("((()))")
    assert isvalid("()(())")
    assert not isvalid("()(()(")

例1-1,栈中元素相同时,只需要记录栈中元素个数

'''
0618
例1-1,栈中元素相同时,只需要记录栈中元素个数
'''


def isvalid(s: str) -> bool:
    if len(s) == 0:
        return True
    if len(s) % 2 == 1:
        return False

    num = 0
    for i in s:
        if i == "(":
            num += 1
        elif i == ")":
            if num <= 0:
                return False
            num -= 1
    return num == 0


if __name__ == '__main__':
    s = '(())'
    print(isvalid(s))
    assert isvalid("")
    assert not isvalid("(")
    assert not isvalid(")")

    assert isvalid("()")
    assert not isvalid("((")
    assert not isvalid("))")
    assert not isvalid(")(")

    assert not isvalid("())")
    assert not isvalid("(((")
    assert not isvalid(")))")
    assert not isvalid(")()")

    assert isvalid("()()")
    assert isvalid("(())")
    assert not isvalid("))((")

    assert isvalid("()()()")
    assert isvalid("((()))")
    assert isvalid("()(())")
    assert not isvalid("()(()(")

例1-2,包含(),{},[]的字符串进行判断

'''
0618 例1-2
包含(),{},[]的字符串进行判断
'''


def isvalid(s: str) -> bool:
    if not s or len(s) == 0:
        return True
    if len(s) % 2 == 1:
        return False
    # 借用字典来判断是否匹配
    # d = {'(': ')', '{': '}', '[': ']'}
    # t = []
    # for i in s:
    #     if i in ('(', '{', '['):
    #         t.append(i)
    #     elif i in (')', '}', ']'):
    #         if len(t) == 0:
    #             return False
    #         elif d[t[-1]] == i:
    #             t.pop()
    #         else:
    #             return False
    #     else:
    #         return False
    # return len(t) == 0

    t = []
    for c in s:
        if c == '[' or c == '{' or c == '(':
            t.append(c)
        elif c == ']':
            last = t.pop() if t else '#'
            if last != '[':
                return False
        elif c == '}':
            last = t.pop() if t else '#'
            if last != '{':
                return False
        elif c == ')':
            last = t.pop() if t else '#'
            if last != '(':
                return False
        else:
            return False

    return not t


if __name__ == '__main__':
    s = ']]'
    print(isvalid(s))
    assert not isvalid("(")
    assert not isvalid(")")

    assert isvalid("()")
    assert not isvalid("((")
    assert not isvalid("))")
    assert not isvalid(")(")

    assert not isvalid("())")
    assert not isvalid("(((")
    assert not isvalid("){)")
    assert not isvalid(")()")

    assert isvalid("()()")
    assert isvalid("([])")
    assert not isvalid("))((")

    assert isvalid("()()()")
    assert isvalid("({[]})")
    assert isvalid("()(())")
    assert not isvalid("()(()(")

2.普通栈,栈中存放索引

例2:大鱼吃小鱼

'''
0618 大鱼吃小鱼
两个数组,一个是鱼的大小,一个是鱼的方向
计算剩下几条鱼
'''


def solution(size, dir) -> int:
    # 只有一条鱼直接返回
    fish_num = len(size)
    if fish_num == 1:
        return fish_num

    left = 0
    right = 1

    # 一个栈
    t = []
    for i in range(0, fish_num):
        # 当前鱼的方向和大小
        cur_fish_size = size[i]
        cur_fish_dir = dir[i]

        # 一个标记,表示当前的鱼是否被栈中的鱼吃掉了
        has_eat = False

        # 如果栈中还有鱼,并且栈中鱼向右,当前鱼向左
        while len(t) > 0 and dir[t[-1]] == right and cur_fish_dir == left:
            # 比较鱼的大小
            # 栈中鱼大于当前鱼,当前鱼被栈中鱼吃掉
            if size[t[-1]] > cur_fish_size:
                has_eat = True
                break
            # 如果栈中鱼小,就被吃掉
            t.pop()

        # 新来的鱼没有被吃掉,压入栈
        if not has_eat:
            t.append(i)
    return len(t)


'''
1.判断的时候用了-1,一旦栈空就会报错
2.先判断,再将元素放入栈中
3.只需要一个存放索引的栈即可
4.一个标志记录新元素是否被栈中元素吃掉了
5.判断方向是否相对,栈中向右,新元素向左即可

    t1 = []
    t2 = []
    # 循环,先把第一个放到栈中
    for i in range(len(size)):
        if len(t1) == 0:
            t1.append(size[i])
            t2.append(dir[i])
            continue
        # 先判断新进来的元素与栈顶元素的方向
        # 如果方向不同,且相对,比较两个元素大小,并弹出栈中较小的元素
        while t2[-1] != dir[i] and t2[-1] - dir[i] == 1:
            # 栈顶元素与新元素比较
            if t1[-1] > size[i]:
                break
            else:
                t1.pop()
                t2.pop()
                if len(t1) == 0:
                    break

        # 如果上一步新元素把栈中元素全部吃完,则添加新元素到栈中
        if len(t1) == 0:
            t1.append(size[i])
            t2.append(dir[i])
            continue
        # 如果方向相同或者方向相反,则新元素加入栈
        elif t2[-1] == dir[i] or t2[-1] - dir[i] == -1:
            t1.append(size[i])
            t2.append(dir[i])

    return len(t1)
'''

if __name__ == '__main__':
    size = [4, 2, 5, 3, 1]
    dir = [1, 1, 0, 0, 0]
    print(solution(size, dir))

3.单调栈

例3:找出右边第一个比我小的元素

'''
0618 单调栈
找出右边第一个比我小的元素
'''


def find_right_small(A):
    if not A or len(A) == 0:
        return []
    ans = [0] * len(A)
    t = []  # 只保存下标 这个就是单调栈
    for i in range(len(A)):
        x = A[i]
        # 栈中有值,栈顶元素比当前元素大
        while len(t) > 0 and A[t[-1]] > x:
            # 结果记录栈顶元素右边第一个比它小的值就是当前元素
            ans[t[-1]] = i
            # 记录完毕,弹出栈顶元素
            t.pop()
        # 将当前元素压入栈
        t.append(i)
    # 循环完后将剩余元素结果记录为-1
    while len(t) > 0:
        ans[t[-1]] = -1
        t.pop()
    return ans


'''
    1.结果栈只需要全0即可
    2,考虑为空的情况
    # 单调递增栈
    t = []
    # 结果记录栈
    res = [i for i in range(len(A))]
    for i in range(len(A)):
        # 当前元素
        cur = A[i]
        while len(t) > 0:
            # 比较栈顶元素和当前元素,栈顶元素比当前元素大,弹出,并记录栈顶元素的右边最小是当前元素
            if A[t[-1]] > cur:
                res[t.pop()] = i
            else:
                break
        t.append(i)
    # 将栈中剩余元素的索引置为-1
    for i in t:
        res[i] = -1

    return res
 
'''

if __name__ == '__main__':
    A = [1, 2, 4, 9, 4, 0, 5]
    print(find_right_small(A))

例4:取出最小的数

'''
0618 单调栈
取出最小的数
'''


def find_small_seq(A, k):
    if not A or len(A) == 0 or k <= 0:
        return []

    t = []
    ans = [0] * k

    for i in range(0, len(A)):
        # 当前元素
        x = A[i]
        # 剩余长度
        left = len(A) - i
        # 栈存在,栈顶元素大于当前元素,且栈长度和剩余长度相加大于k
        while len(t) > 0 and (len(t) + left) > k and t[-1] > x:
            # 弹出栈顶元素
            t.pop()
        # 将当前元素加入到栈中
        t.append(x)

    # 栈长度大于k,弹出啊栈顶元素,直到长度满足
    while len(t) > k:
        t.pop()
    # 将结果存到ans中
    for i in range(k - 1, -1, -1):
        ans[i] = t[-1]
        t.pop()
    return ans


'''
1.又没写边界条件处理
    t = []
    for i in range(len(A)):
        cur = A[i]
        # 栈顶元素和新元素
        while len(t) > 0 and t[-1] > cur:
            # 栈中元素与数组剩余元素个数之和大于k,弹出
            if len(t) + len(A) - i > k:
                t.pop()
            else:
                break
        t.append(cur)
    # 当递增栈元素大于K个,只取前K个元素
    while len(t) > k:
        t.pop()
    return t
'''

if __name__ == '__main__':
    k = 3
    A = [9, 2, 4, 5, 1, 2, 3, 4]
    print(find_small_seq(A, k))

例5:柱状图中最大的矩形

'''
0618 单调栈
柱状图中最大的矩形
1.单调栈存放柱子的下标,下标对应的柱子高度要递增
2.当前元素比栈顶元素小,则弹出栈顶元素并计算以栈顶元素为高的矩形面积,宽度右边是当前元素,宽度左边的栈中该元素的左边一个元素
3.计算面积,保留最大面积
4.最后一个元素设置为-1,将栈清空
空间,时间o(n)
'''


def largest_rectangle_area(A):
    N = 0 if not A else len(A)

    t = []
    ans = 0

    for i in range(0, N + 1):
        # 如果当前元素不是最后一个元素
        x = A[i] if i < N else -1
        while len(t) > 0 and A[t[-1]] > x:  # 当前元素小于栈顶元素,出栈进行计算
            idx = t[-1]
            height = A[idx]
            t.pop()

            right_pos = i
            left_pos = t[-1] if len(t) > 0 else -1  # 宽度左边,如果栈内还有元素就是当前栈顶,如果没有就设为-1

            width = right_pos - left_pos - 1  # 计算宽度
            area = width * height  # 计算面积

            ans = max(ans, area)
        t.append(i)
    return ans


if __name__ == '__main__':
    A = [2, 1, 5, 6, 2, 3]
    print(largest_rectangle_area(A))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值