leetcode 栈:一般题

173.二叉搜索树迭代器

因为题目要求的空间复杂度是O(h),所以如果存储整个二叉树的中序遍历时空间复杂度是O(N).
注意:栈stack中存储的是若干子树,而不是存储的数字,因此next()时要用.val来获取值

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class BSTIterator:

    def __init__(self, root: TreeNode):
        # stack中只存储了一部分节点,不超过树高h
        self.stack = []
        self._leftmost_inorder(root)


    # 将左子树最左侧根节点压入栈中
    def _leftmost_inorder(self, root):
        while root:
            self.stack.append(root)
            root = root.left
        
        
    def next(self) -> int:
        """
        @return the next smallest number
        """
        min_num = self.stack.pop()
        if min_num.right:
            self._leftmost_inorder(min_num.right)
        return min_num.val
        

    def hasNext(self) -> bool:
        """
        @return whether we have a next smallest number
        """
        return len(self.stack) != 0
        


# Your BSTIterator object will be instantiated and called as such:
# obj = BSTIterator(root)
# param_1 = obj.next()
# param_2 = obj.hasNext()
        

94.二叉树的中序遍历

思路:类似上题,遍历根节点即可(利用左右子树)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res , stack = [],[]
        while stack or root:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            res.append(root.val)
            root = root.right

        return res

921.使括号有效的最少添加

class Solution:
    def minAddToMakeValid(self, S: str) -> int:
        res = 0
        stack = []
        for st in S:
            if st == '(':
                stack.append(st)
            else:
                if len(stack)==0:
                    res += 1
                else:
                    num = stack.pop()
                    if num == ')':
                        res += 1
        res = res + len(stack)
        return res

144.二叉树的前序遍历

注意别粗心调换whileifif更省时间。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if root is None:
            return []
        
        stack, res = [root], []
        
        while stack:
            root = stack.pop()
            if root:      #此处if换成while的话会超时,写的时候注意是判断还是循环
                res.append(root.val)
                if root.right:
                    stack.append(root.right)
                if root.left:
                    stack.append(root.left)
                    
        return res
        

341.扁平化嵌套列表迭代器

.isInteger()判断是否为整数;
.getInteger()获取
.getList()获取

# """
# This is the interface that allows for creating nested lists.
# You should not implement it, or speculate about its implementation
# """
#class NestedInteger:
#    def isInteger(self) -> bool:
#        """
#        @return True if this NestedInteger holds a single integer, rather than a nested list.
#        """
#
#    def getInteger(self) -> int:
#        """
#        @return the single integer that this NestedInteger holds, if it holds a single integer
#        Return None if this NestedInteger holds a nested list
#        """
#
#    def getList(self) -> [NestedInteger]:
#        """
#        @return the nested list that this NestedInteger holds, if it holds a nested list
#        Return None if this NestedInteger holds a single integer
#        """

class NestedIterator:
    def __init__(self, nestedList: [NestedInteger]):
        self.stack = []
        self.reverse_and_delete(nestedList)

    
    def reverse_and_delete(self,nestedList):
        for i in range(len(nestedList)-1,-1,-1):
            if nestedList[i].isInteger():
                self.stack.append(nestedList[i].getInteger())
            else:
                self.reverse_and_delete(nestedList[i].getList())
    
    def next(self) -> int:
        return self.stack.pop()
        
    
    def hasNext(self) -> bool:
         return len(self.stack) > 0

# Your NestedIterator object will be instantiated and called as such:
# i, v = NestedIterator(nestedList), []
# while i.hasNext(): v.append(i.next())

739.每日温度

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        res = [0] * len(T)
        stack = []
        for i in range(len(T)-1,-1,-1):
            while stack and T[i] >= T[stack[-1]]:
                stack.pop()
            
            if stack:
                res[i] = stack[-1] - i

            stack.append(i)

        return res

856.括号的分数

思路很巧

class Solution:
    def scoreOfParentheses(self, S: str) -> int:
        stack = [0]
        for s in S:
            if s == '(':
                stack.append(0)
            else:
                if stack[-1] == 0:             # 如果是最简单的()得一分
                    stack.pop()
                    stack[-1] = stack[-1] + 1
                else:                          
                    # 如果是嵌套的形式,如(())中最后一个右括号输入时将所有的都加起来翻倍
                    num = stack[-1]
                    stack.pop()
                    stack[-1] += num * 2

        return stack[-1]

1130.叶值的最小代价生成树

因为左右子树都是取叶子节点的最大值作乘积,因此每次都从栈中取最小值:维护单调递减的栈:

class Solution:
    def mctFromLeafValues(self, arr: List[int]) -> int:
        stack = []
        res = 0
        for num in arr:
            while stack and num > stack[-1]:
                min_1 = stack.pop()
                if stack:
                    min_2 = min(num,stack[-1])
                else:
                    min_2 = num
                res += min_1 * min_2
            
            stack.append(num)
        
        while len(stack) > 1:
            res += stack.pop() * stack[-1]
        
        return res

1003.检查替换后的词是否有效

因为无效的情况很多,因此要判断有效的情况,else即为无效

class Solution:
    def isValid(self, S: str) -> bool:
        stack = []
        for s in S:
            if s == 'a' or s == 'b':
                stack.append(s)
            else:
                if len(stack) > 1 and stack[-1] == 'b' and stack[-2] == 'a':
                    stack.pop()
                    stack.pop()
                else:
                    return False
        
        return len(stack) == 0

1249.移除无效的括号

重点在从右向左用栈存储遍历的时候,会逆序,第二次遍历的时候注意看好左右括号。

class Solution:
    def minRemoveToMakeValid(self, s: str) -> str:
        num1,num2 = 0,0
        stack,res = [],[]
        for i in range(len(s)-1,-1,-1):
            if s[i] == ')':
                stack.append(")")
                num1 += 1
            elif s[i] == '(':
                if num1 > 0:
                    num1 -= 1
                    stack.append('(')
            else:
                stack.append(s[i])
        s2 = ''.join(stack)

        for i in range(len(s2)-1,-1,-1):
            if s2[i] == '(':
                res.append('(')
                num2 += 1
            elif s2[i] == ')':
                if num2 > 0:
                    num2 -= 1
                    res.append(')')
            else:
                res.append(s2[i])
                
        return ''.join(res)


946.验证栈序列

注意:while循环条件一定要判断stack是非空的,不然连续取出元素会导致stack空发生下标越界错误

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        stack = []
        num = 0
        for i in pushed:
            stack.append(i)
            while stack and popped[num] == stack[-1]: # 一定要判断stack是非空的,不然可能会出现下标越界错误
                stack.pop()
                num += 1
        
        if len(stack) == 0:
            return True
        else:return False

103.二叉树的锯齿形层次遍历

此题虽然用到了append函数,但是并不是用了栈的特性,而仅仅是当列表使用,每次遍历都是从左向右进行的

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
        res = []
        if not root:
            return res
        level = 0
        cur_level = [root]
        while cur_level:
            next_level = []
            res1 = []
            for node in cur_level:
                res1.append(node.val)
                if node.left:
                    next_level.append(node.left)
                if node.right:
                    next_level.append(node.right)
            
            level += 1
            if level % 2 == 0:
                res.append(res1[::-1])
            else:
                res.append(res1)

            cur_level = next_level

        return res

1209.删除字母中的所有相邻重复项2

思路:讲字母与目前连续的个数一同作为列表保存在stack中,是最为好的选择,而不是讲连续个数单拿出来保存.

class Solution:
    def removeDuplicates(self, s: str, k: int) -> str:
        stack = []
        for i in s:
            if not stack or stack[-1][0] != i:
                stack.append([i,1])
            else:
                if stack[-1][1] < k-1:
                    stack[-1][1] += 1
                else:
                    stack.pop()
        res = ''
        for chars,num in stack:
            res += chars * num
        
        return res

503.下一个更大元素2

无论是第一次遍历的弹出,还是第二次遍历的stack的弹出,都需要用while而不是if,因为可能连续弹出。
傻了,while stack 才表示栈非空,而不是 while not stack

class Solution:
    def nextGreaterElements(self, nums: List[int]) -> List[int]:
        stack = []
        res = [-1] * len(nums)
        index = -1
        for num in nums:
            index += 1
            while stack and num > stack[-1][1]:
                res[stack[-1][0]] = num
                stack.pop()
            stack.append([index, num])

        for num in nums:
            while stack and num > stack[-1][1]:
                res[stack[-1][0]] = num
                stack.pop()
        
        return res

1190.反转每对括号间的子串

因为每遇到一个括号就要进行反转操作,因此用左括号配合栈来记录反转(栈深度+1),遇到右括号完成反转合并(反转并合并,栈深度-1),最后深度与开始相同,为1。因此最开始的stack不是空的,而是要带一个空字符。

class Solution:
    def reverseParentheses(self, s: str) -> str:
        stack = ['']
        for i in s:
            if i == '(':
                stack.append('')
            elif i == ')':
                s1 = stack.pop()
                stack[-1] += s1[::-1]
            else:
                stack[-1] +=  i
        
        return stack[-1]
        

1019.链表中的下一个更大节点

解法一:
与数组相比只在最开始多了一个链表转数组的操作,其他操作完全相同。
注意:链表的长度不能直接用len()函数,需要遍历来获取。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def nextLargerNodes(self, head: ListNode) -> List[int]:
        List,stack = [] ,[]
        lenth = 0
        while head:
            List.append(head.val)
            head = head.next
            lenth += 1

        res = [0] * lenth
        index = 0
        
        for num in List:
            while stack and num > stack[-1][1]:
                res[stack[-1][0]] = num
                stack.pop()
            stack.append([index,num])
            index += 1
        
        return res

解法二:
将链表长度的计算与结果的获取一起进行,但是需要更多的空间来存储res列表。是一种空间换时间的思路。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def nextLargerNodes(self, head: ListNode) -> List[int]:
        stack = []
        res = [0] * 10000
        lenth = 0
        while head:
            num = head.val
            while stack and num > stack[-1][1]:
                res[stack[-1][0]] = num
                stack.pop()
            stack.append([lenth , num])
            lenth += 1
            head = head.next
        
        return res[:lenth]

150.逆波兰表达式求值

此题很简单,只需注意取整即可。
题中要求除法运算时取整数部分,而python中的'//'运算默认向下取整,即5.6会取5-5.6会取-6,而不是-5
所以要想得到a/b的整数部分,用a//b是不可行的,可用int(a/b)实现。

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for st in tokens:
            if st == '+':
                num = stack.pop()
                stack[-1] = stack[-1] + num
            elif st == '-':
                num = stack.pop()
                stack[-1] = stack[-1] - num
            elif st == '*':
                num = stack.pop()
                stack[-1] = stack[-1] * num
            elif st == '/':
                num = stack.pop()
                stack[-1] = int(stack[-1] / num)
            else:
                stack.append(int(st))
        
        return stack[-1]

636.函数的独占世界

此题耗时较多

本题重点在于时间多一少一的调试:需要用一个单独的变量prev记录上次start或者end的时间(因为end不加入栈,所以不能单独靠栈顶元素来存储)。(调试了蛮久的)
本题中的is_start()函数可直接用if start in string:来实现,但是考虑到后期可能会用java再做一遍,就把具体实现也写了。

在这里插入图片描述

class Solution:
    def exclusiveTime(self, n: int, logs: List[str]) -> List[int]:
        stack = []
        res = [0] * n
        for record in logs:
            record_number = self.getNumber(record)
            record_time = self.getTime(record)
            isStart = self.is_start(record)
            if isStart:
                if not stack:
                    stack.append([record_number,record_time])
                    prev = record_time
                else:
                    res[stack[-1][0]] += record_time - prev 
                    stack.append([record_number,record_time])
                    prev = record_time
            else:
                res[record_number] += record_time - prev + 1
                prev = record_time +1
                stack.pop()
        return res

    def getNumber(self,string:str)->int:
        number = ''
        for st in string:
            if st == ':':
                break
            number += st
        return int(number)

    def getTime(self,string:str)->int:
        time = ''
        for st in string[::-1]:
            if st == ':':
                break
            time += st  
        return int(time[::-1])

    def is_start(self,string:str)->bool:
        start = []
        for s in string:
            start.append(s)
            if len(start) > 1 and start[-2] == ':':
                return start[-1] == 's'

394.字符串解码

不难

class Solution:
    def decodeString(self, s: str) -> str:
        stack = ['']
        stack_repeat_times = []
        repeat_times = ''
        for i in s:
            if i <= '9' and i >= '0':
                repeat_times += i
            elif i == '[':
                stack_repeat_times.append(int(repeat_times))
                repeat_times = ''
                stack.append('')
            elif i == ']':
                stack[-1] = stack[-1] * stack_repeat_times.pop()
                stack[-2] += stack[-1]
                stack.pop()
            else:
                stack[-1] += i
        
        return stack[-1]

331.验证二叉树的前序序列化

此题非常需要注意细节(特例)

特例1:
空树: preorder = ‘#’,在最开始的时候判断好。

特例2:
森林:preorder = "9,3,4,#,#,1,#,#,#,2,#,6,#,#",通过空结点与结点的差值是否等于1来判断,这个特例思考了好久才发现。

class Solution:
    def isValidSerialization(self, preorder: str) -> bool:
        if preorder == '#':
            return True
        leaf_num = 0
        stack = []
        preorder_list = preorder.split(',')
        for s in preorder_list:
            if s == '#':
                leaf_num += 1
                if stack:
                    stack[-1][1] -= 1
                else:
                    return False
            else:
                stack.append([s, 2])
            while stack and stack[-1][1] == 0:
                stack.pop()
                if stack:
                    stack[-1][1] -= 1
        node_num = len(preorder_list) - leaf_num
        if leaf_num - node_num != 1:
            return False
            
        return not stack

385.迷你语法分析器

刚开始接触有新定义类的题型,上手较难,但是熟悉后思路不难。
**存疑:**注释处不知道为什么实现的功能不同。

class Solution:
    def deserialize(self, s: str) -> NestedInteger:
        if s[0] != '[':
            return NestedInteger(int(s))
        stack = []
        num,sign,is_number = 0,1,False
        for i in s:
            if i == '[':
                stack.append(NestedInteger())
            elif i == '-':
                sign = -1
            elif i == ',' or i == ']':
                if is_number:
                    # stack[-1] = NestedInteger(sign * num)
                    cur_list = stack.pop()
                    cur_list.add(NestedInteger(sign * num))
                    stack.append(cur_list)
                if i == ']' and len(stack) > 1:
                    cur_list = stack.pop()
                    stack[-1].add(cur_list)
                    
                num ,sign,is_number = 0,1,False
            else:
                num = num *10 + int(i)
                is_number = True
        return stack[-1]

71.简化路径

大佬的思路就是牛批

class Solution:
    def simplifyPath(self, path: str) -> str:
        cur_list = path.split('/')
        stack = []
        for item in cur_list:
            if item == '..': 
                if stack :
                    stack.pop()
            elif item and item !='.':
                stack.append(item) 
        
        return '/'+'/'.join(stack)

901.股票价格跨度

实际上比我最开始思考的简单很多,因为这是最简单的从左往右数的情况。

class StockSpanner(object):
    def __init__(self):
        self.stack = []

    def next(self, price):
        day = 1
        while self.stack and price >= self.stack[-1][0]:
            day += self.stack.pop()[1]
        self.stack.append([price,day])
        return day

# Your StockSpanner object will be instantiated and called as such:
# obj = StockSpanner()
# param_1 = obj.next(price)

735.行星碰撞

本题要注意的时间限制,尽量的合并不同情况的处理,并且用continue和break来提前结束循环,不然会超时。

class Solution:
    def asteroidCollision(self, asteroids: List[int]) -> List[int]:
        stack = []
        for planet in asteroids:
            while stack and planet < 0 and stack[-1] > 0:
                if stack[-1]+planet < 0:
                    stack.pop()
                    continue
                elif stack[-1] +planet == 0:
                    stack.pop()
                break
            else:
                stack.append(planet)
        return stack

402.移掉k位数字

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        if len(num) == k:
            return '0'
            
        stack = []
        for n in num:
            stack.append(n)
            while k > 0 and len(stack)>1 and n < stack[-2]:
                stack.pop()
                stack[-1] =n
                k -= 1

        while k > 0:
            stack.pop()
            k -= 1

        return str(int(''.join(stack)))

本题不难,但是有很多特殊情况都要考虑周全。

例如:
特例1:num = '123',k=3时,需要输出'0',而不是空字符串,用以下代码实现:

if len(num) == k:
      return '0'

特例2:num= '112',k=2时,需要保证最后k等于0,所以要在遍历完num后加一个while循环,使删掉的元素达到k个,使用以下代码实现:

while k > 0:
      stack.pop()
      k -= 1

特例3:num='01100',k=2时,需要返回的是'11',而不是'011',所以需要去掉开头的零元素,在return时多一个类型转换操作即可实现。

return str(int(''.join(stack)))

907.子数组的最小值之和

思路很巧妙的一个题。第二遍时也要仔细理解。

class Solution:
    def sumSubarrayMins(self, A: List[int]) -> int:
        MOD = 10**9 + 7
        stack = []
        ans= 0
        cur_sum = 0 # 以该元素结束的所有子列表的最小值和
        for i in A:
            sum_count = 1
            while stack and stack[-1][0] >= i:
                num,count =  stack.pop()
                sum_count += count
                cur_sum -= num * count  

            stack.append([i,sum_count])
            cur_sum += i * sum_count   
            ans += cur_sum

        return ans % MOD

456.132模式

想了整整一下午

class Solution:
    def find132pattern(self, nums):
        ak = float('-inf')
        stack = []
        for num in nums[::-1]:
            # 该while循环保证了aj > ak恒成立
            while stack and num > stack[-1]:
                ak = stack.pop()
            if num < ak:
                return True
            stack.append(num)
        return False

1124.表现良好的最长时间段

大佬的解析,宛若醍醐灌顶

class Solution:
    def longestWPI(self, hours: List[int]) -> int:
        length = len(hours)
        """ 
        将最初的时间转化为是否是劳累的一天存储于数组score中(劳累记为1,不劳累记为-1)
        原问题转化为寻找连续和 >0 的最长子串长度
        """
        score = [0] * length
        for i in range(length):
            if hours[i] > 8:
                score[i] = 1
            else:score[i] = -1
        
        """
        计算前缀和,通过前缀和的差值来判断是否是良好的表现时间段
        问题转化为:寻找一对i,j;使presum[j] > presum[i],并且j-i最大,即转化为求最长上坡路问题
        """
        presum = [0] * (length + 1)
        for i in range(1,length + 1):
            presum[i] = presum[i - 1] + score[i - 1]

        """
        用单调递减栈存储presum中的元素的位置,如果理解为上坡问题的话,单调栈中维护的元素从底到顶高度依次降低,
        即越来越深入谷底,遍历完成后的栈顶位置的元素即所有元素中谷底的高度
        """
        stack = []
        for i in range(length + 1):
            if not stack or presum[i] < presum[stack[-1]]:
                stack.append(i)      

        """
        从尾部遍历presum,如果该位置元素比stack中存储的位置的元素高,则表明为上坡路,弹出栈顶元素,并记录坐标差,
        该坐标差即为上坡路的长度
        """
        ans = 0
        for i in range(length , -1, -1):
            while stack and presum[i] > presum[stack[-1]]:
                ans = max(ans,i - stack[-1])
                stack.pop()
        return ans

880.索引处的解码字符串

不能用全部写出来然后遍历的方法去做,因为K可能很大,会使内存超过限制,因此要用以下算法去做,通过逆向思维寻找到字母c,而不是将字符串解码后再去找。

class Solution(object):
    def decodeAtIndex(self, S, K):
        size = 0
        for c in S:
            if c.isdigit():
                size *= int(c)
            else:size += 1
        
        for c in S[::-1]:
            K %= size
            if K == 0 and c.isalpha():
                return c
            
            if c.isdigit():
                size /= int(c)
            else:
                size -= 1
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值