剑指offer整理(附python代码)——堆栈、队列

1.用两个栈实现队列

问题描述

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

考察点:栈、队列的性质
思路
  • 用两个先进后出,实现先进先出。(入队,出队)
  • “出队”:当S2不空,则对S2做弹出;若S2为空,则把S1中所有元素,逐个压入S2,最后对S2做弹出。
  • “入队:当S2空,直接对S1添加;当S2不空,也没关系,因为总会先将S2中弹空,才对S2执行压入,所以不会乱序。
class Solution1:
    def __init__(self):
        self.s1 = []
        self.s2 = []
    def push(self, node):
        #入队,跟S2状态无关
        self.s1.append(node)
    def pop(self):
        # 出队,要分S2空与否;
        #S2空,将S1中所有元素弹出到S2,再对S2弹出一个。
        if self.s2 == []:
            while self.s1:
                self.s2.append(self.s1.pop())
            return self.s2.pop()
        # 当S2不空,直接对S2弹出
        return self.s2.pop()
if __name__ == '__main__':
    s = Solution1()
    s.push([1,2,3])
    print s.pop()
[1, 2, 3]

2.包含min函数的栈

问题描述

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数

考察点
  1. 最初可能想用‘变量’保存最小元素,但是弹出最小后,次小没办法获取
  2. 引入辅助栈
测试用例
  1. 压入的比最小的大/小
  2. 弹出的是/否为当前最小值
思路
  1. 压入时,另外用辅助栈存储当前的最小值
  2. 弹出时,若弹出的为最小,则同时弹出两个栈;否则,只弹出数据栈;
class Solution2:
    def __init__(self):
        self.stack = []
        self.minstack = []
    def push(self, node):
        # write code here
        self.stack.append(node)
        if len(self.minstack)==0 or node<=self.minstack[-1]:
            self.minstack.append(node)
    def pop(self):
        # write code here
        if self.stack[-1]==self.minstack[-1]:
            self.minstack.pop(-1)
        self.stack.pop(-1)
    def top(self):
        # write code here
        return self.stack[-1]
    def min(self):
        # write code here
        return self.minstack[-1]
if __name__ == '__main__':
    s = Solution2()
    s.push([3])
    print s.min()
    s.push([4])
    print s.min()  
    s.push([2])
    print s.min()
[3]
[3]
[2]

3.栈的压入、弹出序列

问题描述

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

测试用例
  1. 两个空指针
  2. 是/非弹出序列
思路

引入辅助栈存储当前序列,弹出时分两种情况

  1. 弹出元素为辅助栈栈顶:直接弹出
  2. 弹出元素非辅助栈栈顶:将未入栈元素入栈,直到找到要弹出的元素;否则为‘非弹出序列’
# -*- coding:utf-8 -*-
class Solution:
    def IsPopOrder(self, pushV, popV):
        # write code here
        if not pushV or len(pushV)!= len(popV):
            return False
        stack = []
        for i in pushV:
            stack.append(i)
            while len(stack) and stack[-1] == popV[0]:
                stack.pop() # 弹出最后一个
                popV.pop(0) # 弹出第一个
        if len(stack):
            return False
        return True
if __name__ == '__main__':
    s = Solution()
    print s.IsPopOrder([1,2,3,4,5],[4,3,5,1,2])
False

4.滑动窗口的最大值

问题描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路1

遍历列表,用切片获取窗口,用内置函数max()获取窗口内最大值

class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if not num or size==0:
            return []
        res = []
        for i in range(len(num)-size+1):
            res.append(max(num[i:i+size]))
        return res
思路2

思路:滑动窗口应当是队列,但为了得到滑动窗口的最大值,队列序可以从两端删除元素,因此使用双端队列。
原则:
对新来的元素k,将其与双端队列中的元素相比较
1)后面比k小的,直接移出队列(因为不再可能成为后面滑动窗口的最大值了!),也保证了队首为最大。
2)判断队首X是否已不在窗口之内,不在了,直接移出队列。
队列的第一个元素是滑动窗口中的最大值。

# -*- coding:utf-8 -*-
from collections import deque
class Solution:
    def maxInWindows(self, num, size):
        # write code here
        if not num or size==0:
            return []
        d = deque()
        res = []
        for i in range(len(num)):
           #从后面依次弹出队列中比当前num值小的元素,同时也能保证队列首元素为当前窗口最大值下标
            while(len(d) and num[d[-1]]<=num[i]):
                d.pop()
            #判断队首X是否已不在窗口之内
            while(len(d) and i-d[0]+1>size):
                d.popleft()
            d.append(i)
            #当遍历了size个元素后,开始插入最大值
            if (i+1>=size):
                res.append(num[d[0]])
        return res
                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值