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.二叉树的前序遍历
注意别粗心调换while
与if
,if
更省时间。
# 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