这里写自定义目录标题
二分查找(循环不变量及跳出循环的条件)
求有序数组中和target绝对值差最小的数,如果2个一样返回大的数
前缀和+defaultdict
和为K的子数组
1、为什么前缀和需要用pre[0]=1
https://leetcode.cn/problems/subarray-sum-equals-k/solutions/1447027/python3-by-wu-qiong-sheng-gao-de-qia-non-w6jw/?envType=study-plan-v2&envId=top-100-liked
TopK
从arr[1, n]这n个数中,找出最大的k个数,这就是经典的TopK问题。
一、排序
分析:明明只需要TopK,却将全局都排序了,这也是这个方法复杂度非常高的原因。那能不能不全局排序,而只局部排序呢?这就引出了第二个优化方法。
二、局部排序
冒泡,将全局排序优化为了局部排序,非TopK的元素是不需要排序的,节省了计算资源。不少朋友会想到,需求是TopK,是不是这最大的k个元素也不需要排序呢?这就引出了第三个优化方法。
def BubbleSort(arr):
n = len(arr)
for i in range(n):
# (n-i-1)这个点需要特别注意下,-1是下面利用的是arr[j] 和arr[j+1]进行比较
# -i是因为每经过一轮,已经有一个最大的元素到达了其最终的位置
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
def BubbleSortSolveTopk(arr,k):
n = len(arr)
for i in range(k):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr[n-k:]
堆
def Heapify(arr, n, i):
largest = i
l = 2*i + 1
r = 2*i + 2
if l < n and arr[l] > arr[largest]:
largest = l
if r < n and arr[r] > arr[largest]:
largest = r
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
Heapify(arr, n, largest)
def HeapSortSolveTopk(arr,k):
n = len(arr)
for i in range(k//2 - 1, -1, -1):
Heapify(arr, k, i)
for i in range(k, n):
if arr[i] > arr[0]:
arr[0], arr[i] = arr[i], arr[0]
Heapify(arr, k, 0)
return arr[:k]
用heapq头函数进行实现:最小K个数
Python 语言中的堆为小根堆,因此我们要对数组中所有的数取其相反数,才能使用小根堆维护前 k 小值。
import heapq
from typing import List
class Solution:
def smallestK(self, arr: List[int], k: int) -> List[int]:
if k == 0:
return list()
hp = [-x for x in arr[:k]]
heapq.heapify(hp)
for i in range(k, len(arr)):
if -hp[0] > arr[i]:
heapq.heappop(hp)
heapq.heappush(hp, -arr[i])
ans = [-x for x in hp]
return ans
分析
堆,将冒泡的TopK排序优化为了TopK不排序,节省了计算资源
快排思想
class Solution:
def partition(self, nums, l, r):
pivot = nums[r]
i = l - 1
for j in range(l, r):
if nums[j] <= pivot:
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[i + 1], nums[r] = nums[r], nums[i + 1]
return i + 1
def randomized_partition(self, nums, l, r):
i = random.randint(l, r)
nums[r], nums[i] = nums[i], nums[r]
return self.partition(nums, l, r)
def randomized_selected(self, arr, l, r, k):
pos = self.randomized_partition(arr, l, r)
num = pos - l + 1
if k < num:
self.randomized_selected(arr, l, pos - 1, k)
elif k > num:
self.randomized_selected(arr, pos + 1, r, k - num)
def smallestK(self, arr: List[int], k: int) -> List[int]:
if k == 0:
return list()
self.randomized_selected(arr, 0, len(arr) - 1, k)
return arr[:k]
快速排序
def quicksort(arr):
if not arr:
return []
def partition(arr,l,r):
pivot=arr[l]
while l<r:
while l<r and arr[r]>=pivot:
# 为什么这个地方是>=呢?其实都可以,主要是看相等的时候怎么进行处理
r-=1
arr[l]=arr[r]
while l<r and arr[l]<=pivot:
l+=1
arr[r]=arr[l]
arr[r]=pivot
return l
# 思考为什么要先从枢轴的反方向开始找,而不是从枢轴的方向开始找
# 这个是由算法的逻辑决定的,相当于把枢轴这个位置空出来,来存放合适的值,最后再把枢轴元素放在其合适的位置
def quicksort_helper(arr, l, r):
if l < r:
p = partition(arr, l, r)
quicksort_helper(arr, l, p - 1)
quicksort_helper(arr, p + 1, r)
quicksort_helper(arr, 0, len(arr) - 1)
return arr
另外一种partition的思路
def partition(self, nums, l, r):
pivot = nums[r]
# 用于跟踪小于等于枢轴的元素的最后一个位置
i = l - 1
for j in range(l, r):
if nums[j] <= pivot:
i += 1
nums[i], nums[j] = nums[j], nums[i]
nums[i + 1], nums[r] = nums[r], nums[i + 1]
return i + 1
kth largest element
内置函数进行排序
def findKthLargest(self, nums, k):
nums.sort()
return nums[len(nums)-k]
堆排序
解决2个问题
- 如何将无序序列构造成初始堆
- 输出堆顶元素后,如何将剩余元素调整成新的堆
# 这个词是由"heap"(堆)和"ify"(使成为)组合而成的
# n:代表待堆化的数组的长度。i:代表当前需要堆化的节点在数组中的索引位置。
def Heapify(arr, n, i):
largest = i
# 下标为0时的规则
l = 2*i + 1
r = 2*i + 2
# 判断是否越界以及是否需要进行调整
if l < n and arr[l] > arr[largest]:
largest = l
if r < n and arr[r] > arr[largest]:
largest = r
# 如果需要进行调整,进行交换再进行递归处理
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
Heapify(arr, n, largest)
def HeapSort(arr):
n = len(arr)
for i in range(n//2 - 1, -1, -1):
Heapify(arr, n, i)
for i in range(n-1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
Heapify(arr, i, 0)
return arr
DP
动态规划中每一个状态一定是由上一个状态推导出来的
解题步骤
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
斐波那契数
用DP的思想进行分析
Unique Path
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
dp = [[0] * n for _ in range(m)]
# 初始化
for i in range(m):
dp[i][0] = 1
for j in range(n):
dp[0][j] = 1
# 上面这2步能描述清楚么?
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
# [[1] * n],这种情况不要忘记了,只有这样才能构成一个列表,和后面的列表进行相加
f = [[1] * n] + [[1] + [0] * (n - 1) for _ in range(m - 1)]
print(f)
for i in range(1, m):
for j in range(1, n):
f[i][j] = f[i - 1][j] + f[i][j - 1]
return f[m - 1][n - 1]
滚动数组
f(i,j)f(i, j)f(i,j) 仅与第 iii 行和第 i−1i-1i−1 行的状态有关,因此我们可以使用滚动数组代替代码中的二维数组,使空间复杂度降低为 O(n)
组合数学
二叉树
前序和中序遍历还原二叉树(递归)
二叉树的直径
1:局部变量需要加上self
2:求某个节点的深度,应该为max(L, R) + 1
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
self.ans = 1
def depth(node):
# 访问到空节点了,返回0
if not node:
return 0
# 左儿子为根的子树的深度
L = depth(node.left)
# 右儿子为根的子树的深度
R = depth(node.right)
# 计算d_node即L+R+1 并更新ans
self.ans = max(self.ans, L + R + 1)
# 返回该节点为根的子树的深度
return max(L, R) + 1
depth(root)
return self.ans - 1
作者:力扣官方题解
链接:https://leetcode.cn/problems/diameter-of-binary-tree/solutions/139683/er-cha-shu-de-zhi-jing-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
美团2024年0406春招实习笔试
1.最少修改次数
hot100
哈希
Two Sum
字母异位词
mp = collections.defaultdict(list)的用法
the longest consecutive elements sequence.
没有考虑-109 <= nums[i] <= 109的取值范围
标记法行不通
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
if not nums:
return 0
max_length_nums = max(nums)+1
sign = [0] * max_length_nums
for num in nums:
sign[num] = 1
max_length = 0
res = 0
for i in range(max_length_nums):
if sign[i] == 1:
max_length += 1
res = max(res, max_length)
else:
max_length = 0
return res
哈希表
怎么优化到o(1)的时间复杂度
简单来说就是每个数都判断一次这个数是不是连续序列的开头那个数。
暴力法(超时)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res=set()
for i in range(len(nums)-2):
for j in range(i+1,len(nums)-1):
for k in range(j+1,len(nums)):
if nums[i]+nums[j]+nums[k]==0:
cur=tuple(sorted([nums[i],nums[j],nums[k]]))
res.add(cur)
#res.add((sorted([nums[i],nums[j],nums[k]])))
# 把set中的元素转为list
return [list(item) for item in res]
# 出现的错误
# 1:集合中只能加入元组这种不可变的类型,否则报错TypeError: unhashable type: 'list'
# 2:sort和sorted的区别:https://docs.pingcode.com/ask/29934.html
# 3:输出的格式为list中嵌套list,那么需要将set中的内容迭代进行输出
双指针
3sum
list.sort(cmp=None, key=None, reverse=False)
1:nums.sort()中的()没写
2:i=0没有进行初始化
3:i+=1
continue
没有更新i合j、k的值,这样会造成死循环
from typing import List
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
res = []
i=0
while i<len(nums)-2:
if i>0 and nums[i]==nums[i-1]:
i+=1
continue
j,k=i+1,len(nums)-1
while j<k:
if(nums[j]+nums[k]+nums[i]==0):
if j>i+1 and nums[j]==nums[j-1]:
j+=1
continue
res.append([nums[i],nums[j],nums[k]])
j+=1
k-=1
elif(nums[j]+nums[k]+nums[i]<0):
j+=1
else:
k-=1
i+=1
return res
nums = [-1,0,1,2,-1,-4]
print(Solution().threeSum(nums))
字串
链表
相交链表(双指针,清晰图解)
# 这种写法就非常巧妙
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
A, B = headA, headB
while A != B:
A = A.next if A else headB
B = B.next if B else headA
return A
学会用循环和函数的思想精简代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
m,n=0,0
cur=headA
while cur:
m+=1
cur=cur.next
cur=headB
while cur:
n+=1
cur=cur.next
cura=headA
curb=headB
if m>n:
step=m-n
while step:
cura=cura.next
step-=1
else:
step=n-m
while step:
curb=curb.next
step-=1
while cura and curb and cura!=curb:
cura=cura.next
curb=curb.next
return cura
链表逆置
二叉树
层序遍历
注意为空和temp的作用
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
if not root:
return []
res = []
deque = collections.deque()
deque.append(root)
# 用来保存当前层遍历的输出的节点
# deque.append(root)
# temp.append(root)
while deque:
# temp=[]
length = len(deque)
for i in range(length):
cur = deque.popleft()
if i ==length - 1:
res.append(cur.val)
# temp.append(cur.val)
if cur.left:
deque.append(cur.left)
if cur.right:
deque.append(cur.right)
return res
flatten the tree into a “linked list”
只考虑右指针,左指针没有断掉
for i in range(len(self.queue)-1):
self.queue[i].right=self.queue[i+1]
self.queue[i].left = None
return self.queue[0]
从前序与中序遍历序列构造二叉树
用边界坐标构造;记得用哈希表
def myBuildTree(preorder_left: int, preorder_right: int, inorder_left: int, inorder_right: int):
图论
前缀树
【【数据结构 10】Trie|前缀树|字典树】https://www.bilibili.com/video/BV1Az4y1S7c7?vd_source=22a0d494d7d586e6e37e23570688a816
强调文本 强调文本
回溯
排列
1:想当然的写成了startindex的形式def backtracing(nums, start, path),2:应该用used数组保存
3:res.append(path[:])深浅拷贝的问题4:backtrack ing直接调用没什么问题,但是如果加了self,调用的时候也应该加上
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
def backtracing(nums, used, path):
if len(path) == len(nums):
#res.append(path[:])
# res.append(path)
return
for i in range(len(nums)):
if used[i]:
continue
used[i] = True
path.append(nums[i])
backtracing(nums, used, path)
path.pop()
used[i] = False
res = []
used = [False] * len(nums)
backtracing(nums, used, [])
return res
dp
Minimum Path Sum
1:关于dp数组的构造
dp=[[[0] for _ in range(m)] for _ in range(n)]
最长回文字符串
1:dp = [[False] * n for _ in range(n)]采用循环的方式进行定义,而不是dp = [[False] * n] * n乘法,其会当你使用乘法操作符 * 来复制列表时,你并没有创建新的列表,而是创建了多个指向相同列表对象的引用
2:长度为2时需要单独进行处理
技巧
颜色分类
1:nums[i]=0的代码可以进行复用
2:if f_2<i可以加在while循环中
3:看到有增减的就要判断其合理性
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
f_0, f_2 = 0, n - 1
i = 0
while i <= f_2:
if nums[i] == 0:
nums[f_0], nums[i] = nums[i], nums[f_0]
# 考虑交换之后值的情况
f_0 += 1
while nums[i] == 2:
nums[f_2], nums[i] = nums[i], nums[f_2]
# 考虑交换之后值的情况
f_2 -= 1
if f_2<i:
break
if nums[i] == 0:
nums[f_0], nums[i] = nums[i], nums[f_0]
# 考虑交换之后值的情况
f_0 += 1
i += 1
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。1
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎