942.增减字符串匹配
我们维护当前未使用的最小和最大的数,它们对应的区间为当前未使用的数的集合。从左向右扫描字符串,如果碰到 ‘I’,就取出当前最小的数,否则取出当前最大的数。
class Solution:
def diStringMatch(self, S: str) -> List[int]:
n=len(S)
l,h=0,n
ans=[]
for i in S:
if i=='I':
ans.append(l)
l+=1
if i=='D':
ans.append(h)
h-=1
return ans+[l]
堆: heapq模块
栈顶元素heap[0]总是最小的那个元素,此外,接下来的最小元素可以一次通过heapq.heappop()的方法轻松的找到
nlargest()和nsmallest():在某个集合中找出最大或最小的N个元素
nums=[1,2,3,4,5,6]
heapq.nlargest(3,nums)
堆是二叉树,最大堆中父节点大于或等于两个子节点,最小堆父节点小于或等于两个子节点。堆总是一棵完全树。即除了最底层,其他层的节点都被元素填满,且最底层尽可能地从左到右填入
堆的主要操作是插入和删除最小元素(元素值本身为优先级键值,小元素享有高优先级)
heapq有两种方式创建堆, 一种是使用一个空列表,然后使用heapq.heappush(堆,值)函数把值加入堆中,同时维持堆得排序要求。
另外一种就是使用heap.heapify(list)转换列表成为堆结构
如果需要删除堆中最小元素并加入一个元素,可以使用heapq.heaprepalce() 函数:heapq.heapreplace(nums, 23)
heapreplace(a,x) 弹出最初在a中的最小值,而不管x的值如何,然后将x压进去
heappushpop(a,x) 在弹出最小值之前将x推送到a上.
703 数据流中第K大元素
堆排序:
import heapq
class KthLargest:
def __init__(self, k: int, nums: List[int]):
self.k=k
self.pool=heapq.nlargest(self.k,nums)
heapq.heapify(self.pool)
def add(self, val: int) -> int:
if len(self.pool)<self.k:
heapq.heappush(self.pool,val)
else:
heapq.heappushpop(self.pool,val)
return self.pool[0]
面试题17。14 最小K个数
堆排序:
堆 复杂度Nlogk
import heapq
class Solution:
def smallestK(self, arr: List[int], k: int) -> List[int]:
pool=heapq.nsmallest(k,arr)
heapq.heapify(pool)
return [heapq.heappop(pool) for x in range(len(pool)) ]
#return sorted(arr)[:k] 一行更快
快速排序: 最慢
#快速选择算法 复杂度N
#快排
def kuaipai(arr):
n=len(arr)
if n<2:
return arr
mid=n//2
pivort=arr[mid]
left,right=[],[]
arr.remove(pivort)
for i in range(len(arr)):
if arr[i]<pivort:
left.append(arr[i])
else:
right.append(arr[i])
return kuaipai(left)+[pivort]+kuaipai(right)
return kuaipai(arr)[:k]
快排标准写法:
排序直接操作原始数组,
程序一定要先从右端开始遍历,因为两端遍历最终停下的条件肯定是相遇的时候,如果左端先移动,则最后停下时的数值肯定比基数大,若将这个 数字与基数交换,则基数左边的数字就不是全部比基数小了,程序运行就不正确了。
每次找到一个分割点,当k-1是分割点时,前k-1个数即为要求
class Solution:
def smallestK(self, arr: List[int], k: int) -> List[int]:
if k==0:
return []
#快排
def helper(left,right,k):
tmp=arr[left]
i,j=left,right
while i<j:
while i<j and arr[j]>=tmp:
j-=1
arr[i]=arr[j]
while i<j and arr[i]<=tmp:
i+=1
arr[j]=arr[i]
arr[i]=tmp
#分隔点是前k个最小数,则返回
if i==k-1:
return
elif i>k-1:
helper(left,i-1,k)
else:
helper(i+1,right,k)
helper(0,len(arr)-1,k)
return arr[:k]
23 合并K个排序链表
法一:暴力法
所有数据放到一个数组里,重新进行排序后构造链表
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if not lists:
return
ans=[]
dummy=ListNode(0)
cur=dummy
for lst in lists:
while lst:
ans.append(lst.val)
lst=lst.next
ans.sort()
for i in range(len(ans)):
cur.next=ListNode(ans[i])
cur=cur.next
return dummy.next
法二:分治法 类似148
将一个规模为N的问题,分解成K个规模较小的子问题,这些子问题相互独立且月原问题性质相同。 求解出子问题的解,合并得到原问题的解。
首先实现2个有序链表的合并,而后按照归并排序的思路,持续对K个链表进行两两排序,如果是奇数个,则最后一个链表本轮不排序直接进入下一轮,直至最终只剩下一个链表结束。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
import heapq
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
if len(lists)==0:
return
if len(lists)==1:
return lists[0]
mid=len(lists)//2
#分治时每次合并两个链表
return self.merge(self.mergeKLists(lists[:mid]),self.mergeKLists(lists[mid:]))
def merge(self,node_a,node_b):
dummry=ListNode(0)
cur=dummry
while node_a and node_b:
if node_a.val<node_b.val:
cur.next=ListNode(node_a.val)
node_a=node_a.next
else:
cur.next=ListNode(node_b.val)
node_b=node_b.next
cur=cur.next
# 有一方的next的为空,就没有比较的必要了,直接把不空的一边加入到结果的 next 上
if node_a:
cur.next=node_a
if node_b:
cur.next=node_b
return dummry.next
1382 将二叉搜索树变平衡
利用二叉搜索树性质
先中序遍历获取二叉搜索树的数据(递增数列),然后构造平衡二叉树,将数组从中点一分为二,左边是左子树,右边是右子树,中点是根节点
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def balanceBST(self, root: TreeNode) -> TreeNode:
self.res=[]
self.tree(root)
return self.struct_bst(self.res)
def struct_bst(self,nums):
if len(nums)==0:
return
if len(nums)==1:
return TreeNode(nums[0])
mid=len(nums)//2
newroot=TreeNode(nums[mid])
newroot.left=self.struct_bst(nums[:mid])
newroot.right=self.struct_bst(nums[mid+1:])
return newroot
def tree(self,head):
if not head:
return
self.tree(head.left)
self.res.append(head.val)
self.tree(head.right)
return self.res
1038 从二叉搜索树到更大和树
利用二叉搜索树中序遍历是递增序列
反向中序遍历即可,并把每次的节点值进行累加,就能得到最终的累加树。而且这样保证了我们对每个节点只访问了一次。
从右子树开始中序遍历,修改每个节点为他之前节点的和加上节点自身的值
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.sum=0
def bstToGst(self, root: TreeNode) -> TreeNode:
if not root:
return
self.bstToGst(root.right)
self.sum+=root.val
root.val=self.sum
self.bstToGst(root.left)
return root
538 把二叉搜索树转换为求和树
反向中序遍历
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.sum=0
def convertBST(self, root: TreeNode) -> TreeNode:
if not root:
return
self.convertBST(root.right)
self.sum+=root.val
root.val=self.sum
self.convertBST(root.left)
return root
1373 二叉搜索子树的最大键值和
搜索数的判断在每个节点的逻辑为,判断左子树最大值是否小于当前节点(或者左子树不存在),右子树的最小值是否大于当前节点(或者右子树不存在),若满足则该子树为二叉搜索树。
分解子问题,对每个子树分别求递归所需的5个子问题:
1.BST 子树最大节点和(即最终问题答案,用一个全局变量在递归中逐个检查每个节点)
2.以当前节点为root的子树是否是BST
3.BST子树的最小值
4.BST子树的最大值
5.以当前节点为root的子树对应的节点之和,若非BST,则回 0,
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def maxSumBST(self, root: TreeNode) -> int:
self.maxsum=0
self.isbst(root)
return self.maxsum
def isbst(self,node):
if not node:
#空值都可以作为二叉搜索树的左右节点
isbst,max_value,min_value,sum_val=True,float('-inf'),float('inf'),0
return isbst,max_value,min_value,sum_val
isbst_l,max_value_l,min_value_l,sum_val_l=self.isbst(node.left)
isbst_r,max_value_r,min_value_r,sum_val_r=self.isbst(node.right)
#判断以node为根节点的树是否是二叉搜索树
#左右子树都是BST且左子树最大值<根节点<右子树最小值
if isbst_l and isbst_r and max_value_l<node.val<min_value_r:
#是二叉搜索树,更新该搜索树最大值,最小值,节点和,向上传递
max_value=max(node.val,max_value_r)
min_value=min(node.val,min_value_l)
sum_val=sum_val_l+sum_val_r+node.val
isbst=True
self.maxsum=max(self.maxsum,sum_val)
else:
#不是二叉搜索树,则将最大值,最小值设置的不可能满足条件
isbst=False
max_value,min_value=float('inf'),float('-inf')
sum_val=0
return isbst,max_value,min_value,sum_val
938 二叉搜索树的范围和
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def rangeSumBST(self, root: TreeNode, L: int, R: int) -> int:
self.res=0
def dfs(node,l,r):
if not node:
return
dfs(node.left,l,r)
if node.val>=l and node.val<=r:
self.res+=node.val
dfs(node.right,l,r)
return self.res
return dfs(root,L,R)
623 在二叉树中增加一行
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def addOneRow(self, root: TreeNode, v: int, d: int) -> TreeNode:
if d==1:
newroot=TreeNode(v)
newroot.left=root
return newroot
stack=[root]
depth=0
while stack:
depth+=1
for _ in range(len(stack)):
node=stack.pop(0)
if depth==d-1:
if node.left:
#保存节点原来的左子树
left=node.left
newl=TreeNode(v)
node.left=newl
newl.left=left
else:
newl=TreeNode(v)
node.left=newl
if node.right:
#保存节点原来的右子树
right=node.right
newr=TreeNode(v)
node.right=newr
newr.right=right
else:
newr=TreeNode(v)
node.right=newr
else:
#正常添加左右节点
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return root
563 二叉树的坡度
递归:在任何结点调用该函数,都会返回当前结点下面(包括其自身)的结点和。借助于任何结点的左右子结点的这一和值,我们可以直接获得该结点所对应的坡度。
class Solution:
def findTilt(self, root: TreeNode) -> int:
self.po=0
def helper(root):
if not root:
return 0
#当前节点左子树的和
left=helper(root.left)
#当前节点右子树的和
right=helper(root.right)
#坡度
self.po+=abs(left-right)
#当前节点下面(包括自身)的节点和,向上传递
return left+right+root.val
helper(root)
return self.po
145 二叉树的后序遍历
迭代实现:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def postorderTraversal(self, root: TreeNode) -> List[int]:
if not root:
return []
stack=[]
res=[]
node=root
while stack or node:
while node:
#第一次入栈的事根节点
stack.append(node)
#判断当前节点的左子树是否存在,若存在则持续左下行,若不存在就转向右子树
node=node.left if node.left else node.right
#到达最左叶子结点
node=stack.pop()
res.append(node.val)
#判断该节点是父节点的左子树还是右子树
if stack and stack[-1].left==node:
#是左子树则转去访问右子树,此时栈顶元素为Node的父节点
node=stack[-1].right
else:
#是右子树则退栈,接下来访问栈顶元素
node=None
return res
590 N叉树的后序遍历
迭代:
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def postorder(self, root: 'Node') -> List[int]:
if not root:
return []
stack=[root]
node=root
res=[]
while stack:
root=stack.pop()
if root:
res.append(root.val)
for c in root.children:
stack.append(c)
return res[::-1]
递归:
class Solution:
def postorder(self, root: 'Node') -> List[int]:
if not root:
return []
self.res=[]
def dfs(root):
if not root:
return
for i in root.children:
dfs(i)
self.res.append(root.val)
return self.res
dfs(root)
return self.res
589 N叉树前序遍历:
递归:
"""
# Definition for a Node.
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
"""
class Solution:
def preorder(self, root: 'Node') -> List[int]:
if not root:
return []
self.res=[]
def dfs(root):
if not root:
return
self.res.append(root.val)
for i in root.children:
dfs(i)
return self.res
dfs(root)
return self.res
迭代:
class Solution:
def preorder(self, root: 'Node') -> List[int]:
if not root:
return []
res=[]
stack=[root]
while stack:
root=stack.pop()
res.append(root.val)
if root.children:
for i in root.children[::-1]:
stack.append(i)
return res