栈和队列

1.最小栈(easy)

设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

思路:
1)借用辅助栈
设置两个栈,一个用来存入数据,另一个用来存入最小值。
push:数据栈每次存入数据,辅助栈用来保存当前栈内的最小值
两个栈为空时,都加入数据
数据栈每次存入数据时,辅助栈判断当前栈内的最小值与存入数据的大小,存入的数小于或者等于辅助栈栈顶元素的时候,才存入辅助栈
pop:数据栈出栈,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈
top:获取的是数据栈栈顶元素
getMin:获取的是辅助站栈顶的元素
2)用一个栈同时保存当前值和栈内最小值
存入栈的是一个元组(当前值,栈内最小值),同时进栈和出栈。
push:当栈为空,保存元组(x,x);
不为空时,将当前值存入,并且将栈内最小值也存入(取当前值和栈内最小值的较小值),即min(x,stack[-1][1])
pop:删除的是栈顶元组
top:获取的栈顶元素是栈的最顶元组的第一个值(当前值)
getMin:获取栈顶元素是栈的最顶元组的第二个值(栈内最小值)

1)借用辅助栈
class MinStack:
    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []				#数据栈
        self.minstack = []			#辅助栈

    def push(self, x: int) -> None:
        self.stack.append(x)
        if len(self.minstack)==0 or x <=self.minstack[-1]:
            self.minstack.append(x)
    def pop(self) -> None:
        if self.minstack:
            if self.stack[-1]==self.minstack[-1]:
                self.minstack.pop()
        return self.stack.pop()
    def top(self) -> int:
        if self.stack:
            return self.stack[-1]
    def getMin(self) -> int:
        if self.minstack:
            return self.minstack[-1]

2)用一个栈同时保存当前值和栈内最小值
class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []

    def push(self, x: int) -> None:
        if len(self.stack)==0:
            self.stack.append((x,x))
        else:
            self.stack.append((x,min(x,self.stack[-1][1])))
        
    def pop(self) -> None:
        if self.stack:
            return self.stack.pop()


    def top(self) -> int:
        if self.stack:
            return self.stack[-1][0]


    def getMin(self) -> int:
        if self.stack:
            return self.stack[-1][1]

单调栈

定义:栈内元素递增或者单调递减的栈,并且只在栈顶操作。元素加入栈前会把栈顶破坏单调性的元素删除。所有元素只会进栈一次。单调栈的维护是O(n)的时间复杂度,
主要的特点:
1)离当前栈顶最近(栈的先进后出)
2)比栈顶大或者小;
一般模板:

stack = []
for i in range(len(nums)):
	while len(stack)>0 and nums[i] > /< stack[-1]:
		stack.pop()
		.....
	stack.append(i)
2.柱形图中最大的矩形(hard)

题目:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。

思路:更详细题解可参加weiwei题解
1.暴力法:固定一个柱子的高,分别要左右两边遍历,即以第i根柱子为最矮柱子所能延伸的最大面积,直到出现柱子的高小于当前柱子的高,则停止,记录下此时面积,最终求面积的最大值。因为每个柱子都要重新遍历,因此时间复杂度为O(n*n),空间复杂度为O(1),使用python会超时
2.使用栈:
利用栈先进后出的原理,
1)依次遍历数组,当栈为空时,将当前数组元素进栈,当栈顶元素b大于当前数组元素c时则出栈,栈顶元素所延神的最大面积的宽的右边界就确定了,左边界则为栈顶元素的下一个栈顶元素a【a<b】,栈存储的是下标,此时则可以得到当前栈顶元素的最大面积。
2)如果栈里还有元素,则此时栈顶元素的右边界都为数组的长度,直到栈空则结束。
考虑特殊情况:在出栈时栈为空,以及遍历完栈里还有元素。
3.优化【单调栈】:
采用哨兵,在输入数组的两端加上两个高度为 0 的柱形,可以避免上面这种特殊情况。
1)左边的柱形(第 1 个柱形)由于它一定比输入数组里任何一个元素小,它肯定不会出栈,因此栈一定不会为空;
2)右边的柱形(第 2 个柱形)也正是因为它一定比输入数组里任何一个元素小,它会让所有输入数组里的元素出栈(第 1 个哨兵元素除外)。
这里栈对应到高度,呈单调增加不减的形态,因此称为单调栈(Monotone Stack)。它是暴力解法的优化,计算每个柱形的高度对应的最大矩形的顺序由出栈顺序决定。

1.暴力法
  def largestRectangleArea(self, heights: List[int]) -> int:
        length = len(heights)
        res = 0 
        for i in range(length):
            left = i
            right = i
            while  left>=0 and heights[left]>= heights[i]:
                left-= 1
            while right<length and heights[right]>=heights[i]:
                right+=1
            res = max(res,(right - left - 1) * heights[i])
        return res
2.1def largestRectangleArea(self, heights: List[int]) -> int:
        length = len(heights)
        res = 0 
        stack = []
        for i in range(length):
            while  len(stack) > 0 and heights[i]<heights[stack[-1]]: #出现右边小于当前栈顶
                cur_height = heights[stack.pop()]
                 while len(stack) > 0 and cur_height == heights[stack[-1]]:
                    stack.pop()
                if len(stack)>0:
                    cur_width = i - stack[-1] -1
                else:
                    cur_width = i
                res = max(res, cur_width*cur_height)
            stack.append(i)
        while len(stack)>0: #剩下的右边界都为len
            cur_height = heights[stack.pop()]
            while len(stack) > 0 and cur_height == heights[stack[-1]]:
                    stack.pop()
            if len(stack)>0:
                cur_width = len(heights) - stack[-1] -1
            else:
                cur_width = len(heights)
            res = max(res, cur_width*cur_height)
        return res
2.2单调栈
 def largestRectangleArea(self, heights: List[int]) -> int:
        length = len(heights)
        res = 0 
        heights = [0] +heights + [0]        
        stack = [0]
        length+=2
        for i in range(length):
            while  heights[i]<heights[stack[-1]]:
                cur_height = heights[stack.pop()]
                cur_width = i - stack[-1] -1
                res = max(res, cur_width*cur_height)
            stack.append(i)
        return res
3.下一个最大元素 I(easy)

题目:给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。
nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。
示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。
对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。
对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。

思路:
1)对nums2建立一个递减栈。
当遇到一个更大的数时,将所有小于这个大数的栈内元素出栈,将栈里的元素与此大数放入哈希表中,即保存栈顶元素第一次遇到比他大的数,再将此大数存入栈内。所有数组元素都会进栈。
遍历完nums2,如果栈内还有元素,则将他们元素对应的值为-1 存入哈希表。
2)将nums1的数映射到nums2查找即可。ps:使用get函数也行,默认在哈希表中找不到直接赋值为-1

  def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res ={}
        stack = []
        t =[]
        for i in  range(len(nums2)):
            while len(stack)>0 and nums2[i]>stack[-1]:
                tmp = stack.pop()
                res[stack.pop()] = nums2[i]
            stack.append(nums2[i])
        #-------------------------------------
        while len(stack)>0:
            tmp = stack.pop()
            res[tmp]= -1
        for i in nums1:
            t.append(res.get(i))
        #-------------------------------------
        for i in nums1:
            t.append(res.get(i,-1))
        return t
 
4.下一个更大元素 II(medium)

题目:给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

思路:递减栈,和上题一致,不同的就是:
1)返回的结果由下标差改为了值;
2 )循环两次,就可知数组中是否存在比当前数大的数

 def nextGreaterElements(self, nums: List[int]) -> List[int]:
        nums = nums + nums
        res = [-1] * len(nums)
        stack = []
        for i in range(len(nums)):
            while len(stack)>0 and  nums[i]>nums[stack[-1]]:
                    top = stack.pop()
                    res[top] = nums[i]   
            stack.append(i)
        return res[:len(nums)//2]
5.每日温度(medium)

题目:根据每日 气温 列表,请重新生成一个列表,对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高,请在该位置用 0 来代替。
例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

思路:采用单调递减栈,与上题思路差不多,不同的是当遇到比栈顶元素大的数时,保存当前数到栈顶元素的距离。这里我直接替换掉T里面对应元素的数值.。或者新建一个数组为全0的也可以,把那些有匹配的替换掉即可

  def dailyTemperatures(self, T: List[int]) -> List[int]:
        length = len(T)
        stack =[]
        for i in range(length):
            while len(stack)>0 and T[i]>T[stack[-1]]:
                top = stack.pop()
                s = i - top
                T[top] = s
            stack.append(i)
        while len(stack)>0:
            T[stack.pop()] = 0
        return T
6.最长有小括号

题目:给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”

思路:采用栈,遇到 “(”,就将其下标入栈,遇到")",就从栈顶弹出"(",并且计算当前到栈顶的距离【也就是两个括号之间的距离】,要求最长有效括号字串,设置一个变量来计算最长的字串长度,在每次计算两括号距离之后加一步判断当前最长字串。考虑到当遇到第一个字符串为”)"时,栈为空无法弹出,设置stack=[-1]

 def longestValidParentheses(self, s: str) -> int:
        if not s:
            return 0
        stack = [-1]
        nums = 0
        for i in range(len(s)) :
            if s[i] == '(':
                stack.append(i)   #stack 存入下标
            elif s[i] == ')':
                stack.pop()
                if stack:
                    length =  i - stack[-1]
                    nums =  max(nums, length) 
                else:
                    stack.append(i)
        return nums
7.剑指 Offer 09.用两个栈表示队列(easy)

题目:用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

思路:因为栈是先进后出,采用两个栈,将数压入栈1,再出来压入栈2。第二栈stack2出来的顺序就是和队列先进先出一致。
添加元素:即直接向stack1加入元素
删除元素:直接从stack2中pop出来。如果stack2为空,则先在stack1中将所有元素添加到stack2, 然后再pop

class CQueue:

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def appendTail(self, value: int) -> None:
        self.stack1.append(value)
        return None


    def deleteHead(self) -> int:
        if not self.stack2:
            if not self.stack1:
                return -1
            else:
                while self.stack1:
                    self.stack2.append(self.stack1.pop())
                return self.stack2.pop()
        else:
            return self.stack2.pop()
                    
8. 剑指 Offer 30.包含min函数的栈(easy)

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

思路:采用两个栈,一个栈stack1 依次存入元素,stack2作为最小栈,当stack2为空或者stack2栈顶的元素大于当前元素时,则压入stack2。
保持stack1和stack2的一致性,在pop的时候,从stack1栈顶元素出栈,并且当stack.pop()的元素等于stack2的栈顶元素,则stack2栈顶元素也出栈。
top:取stack1的栈顶元素
min:取stack2的栈顶元素

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack1 = []
        self.stack2 = []
    def push(self, x: int) -> None:
        self.stack1.append(x)
        if  not self.stack2 or self.stack2[-1] >= x:
            self.stack2.append(x)
    def pop(self) -> None:
        x = self.stack1.pop()
        if self.stack2[-1] == x:
            self.stack2.pop()
    def top(self) -> int:
        return self.stack1[-1]
    def min(self) -> int:
        return self.stack2[-1]
9.剑指 Offer 59 - II. 队列的最大值(medium)

题目:请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1

思路:采用一个队列和一个数组,队列存储,辅助数组表示递减。
队列的最大值即为辅助的数组的第一个元素。
pop_front:出队的时候,当辅助数组的首元素和队列出列的元素一致时则一起出列,否则只出列队列的元素。
push_back:将元素存入队列,当数组中的元素比当前元素小的时候则出列(保证数组里的元素递减)并将当前元素添加到数组中;否则直接加到数组中

import queue
class MaxQueue:

    def __init__(self):
        self.queue = queue.Queue()
        self.stack = []

    def max_value(self) -> int:
        return self.stack[0] if self.stack else -1

    def push_back(self, value: int) -> None:
        self.queue.put(value)
        while self.stack and self.stack[-1] < value:
            self.stack.pop()
        self.stack.append(value)

    def pop_front(self) -> int:
        if not self.stack: return -1 
        ans = self.queue.get()
        if ans == self.stack[0]:
            self.stack.pop(0)
        return ans

队列

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值