文章目录
- 骚题目
- 蓄水池抽样
- Alias Sampling
- AUC曲线计算
- 1363. 形成三的最大倍数
- 403. 青蛙过河
- 不同路径(考虑障碍物的DP)
- 股票难题
- 315. 计算右侧小于当前元素的个数
- 255. 验证前序遍历序列二叉搜索树
- 376. 摆动序列
- 582. 杀掉进程
- 718. 最长重复子数组
- 剑指 Offer 41. 数据流中的中位数
- 516. 最长回文子序列
- 97. 交错字符串
- 416. 分割等和子集
- 312. 戳气球
- 583. 两个字符串的删除操作
- 234. 回文链表
- 726. 原子的数量
- 405. 数字转换为十六进制数
- 1. 两数之和
- 3. 无重复字符的最长子串
- 2. 两数相加
- 5. 最长回文子串
- 42. 接雨水
- 206. 反转链表
- 4. 寻找两个正序数组的中位数
- 21. 合并两个有序链表
- 15. 三数之和
- 146. LRU 缓存机制
- 53. 最大子序和
- 25. K 个一组翻转链表
- 11. 盛最多水的容器
- 121. 买卖股票的最佳时机
- 33. 搜索旋转排序数组
- 46. 全排列
- 20. 有效的括号
- 31. 下一个排列
- 23. 合并K个升序链表
- 56. 合并区间
- 70. 爬楼梯
- 7. 整数反转
- 215. 数组中的第K个最大元素
- 199. 二叉树的右视图
- 124. 二叉树中的最大路径和
- 200. 岛屿数量
- 92. 反转链表 II
- 93. 复原IP地址
- 22. 括号生成
- 72. 编辑距离
- 102. 二叉树的层序遍历
- 148. 排序链表
- ※ 440. 字典序的第K小数字
- 19. 删除链表的倒数第 N 个结点
- 54. 螺旋矩阵
- 105. 从前序与中序遍历序列构造二叉树
- 32. 最长有效括号
- 103. 二叉树的锯齿形层序遍历
- 143. 重排链表
- 10. 正则表达式匹配
- 300. 最长递增子序列
- 24. 两两交换链表中的节点
- 9. 回文数
- ※ 41. 缺失的第一个正数
- 135. 分发糖果
- 88. 合并两个有序数组
- 322. 零钱兑换
- 78. 子集
- 14. 最长公共前缀
- 79. 单词搜索
- 94. 二叉树的中序遍历
- 112. 路径总和
- 175. 组合两个表
- 543. 二叉树的直径
- 198. 打家劫舍
- 剑指 Offer 03. 数组中重复的数字
- 141. 环形链表
- 176. 第二高的薪水
- 101. 对称二叉树
- 160. 相交链表
- 221. 最大正方形
- 104. 二叉树的最大深度
- 76. 最小覆盖子串
- 236. 二叉树的最近公共祖先
- 347. 前 K 个高频元素
- 8. 字符串转换整数 (atoi)
- 122. 买卖股票的最佳时机 II
- 45. 跳跃游戏 II
- 98. 验证二叉搜索树
- 128. 最长连续序列
- 84. 柱状图中最大的矩形
- 64. 最小路径和
- 445. 两数相加 II
- 26. 删除排序数组中的重复项
- 6. Z 字形变换
- 96. 不同的二叉搜索树
- 739. 每日温度
- 240. 搜索二维矩阵 II
- 51. N 皇后
- 39. 组合总和
- 85. 最大矩形
- 82. 删除排序链表中的重复元素 II
- 38. 外观数列
- 120. 三角形最小路径和
- 力扣找不到的字节面试题——36进制加法
- 头条笔试题 - 折木棍
- 逻辑题
- 双向链表的反转
- 补充题
用 O ( N ) O(N) O(N)的时间复杂度从 N N N个数中无放回等可能抽样 K K K个数
用于不知道数据规模的情况,保证每个样本被抽中的概率是等可能的
- 算法步骤
假设数据序列的规模为 n n n,需要采样的数量的为 k k k。
首先构建一个可容纳 k k k个元素的数组,将序列的前 k k k个元素放入数组中。
然后从第
k
+
1
k+1
k+1个元素开始,以
k
/
n
k/n
k/n (
n
n
n表示动态增长的数据规模)的概率来决定该元素是否被替换到数组中(数组中的元素被替换的概率是相同的)。 当遍历完所有元素之后,数组中剩下的元素即为所需采取的样本。
class Solution:
def __init__(self, head: ListNode):
"""
@param head The linked list's head.
Note that the head is guaranteed to be not null, so it contains at least one node.
"""
self.res=[]
self.K=1
self.head=head
def getRandom(self) -> int:
"""
Returns a random node's value.
"""
k=self.K
p=self.head
while k and p:
self.res.append(p.val)
k-=1
p=p.next
p=self.head
i=0
while p:
ix=random.randint(0, i)
if ix < self.K:
self.res[ix]=p.val
i+=1
p=p.next
return self.res[0]
这题也是蓄水池抽样
shuffle算法本质上也是蓄水池抽样,就是动作换成swap
全Shuffle和抽m个Shuffle:
from random import randint
def shuffle(nums):
n = len(nums)
for i in range(n):
ri = randint(0, i)
nums[i], nums[ri] = nums[ri], nums[i]
return nums
def shuffle_m(nums, m):
n = len(nums)
for i in range(n):
ri = randint(0, i)
if ri < m:
nums[i], nums[ri] = nums[ri], nums[i]
return nums[:m]
print(shuffle(list(range(10))))
print(shuffle_m(list(range(10)),3))
Alias Sampling
class Solution:
def __init__(self, w: List[int]):
N = len(w)
sum_ = sum(w)
prob = [p / sum_ for p in w]
alias = [0] * N
alias_prob = [p * N for p in prob]
small_q = []
large_q = []
for i, p in enumerate(alias_prob):
if p < 1:
small_q.append(i)
else:
large_q.append(i)
while small_q and large_q:
small = small_q.pop(0)
large = large_q.pop(0)
alias[small] = large
alias_prob[large] -= (1 - alias_prob[small])
if alias_prob[large] < 1:
small_q.append(large)
else:
large_q.append(large)
self.alias = alias
self.N = N
self.alias_prob = alias_prob
def pickIndex(self) -> int:
ix = random.randint(0, self.N - 1)
p = random.random()
if p < self.alias_prob[ix]:
return ix
else:
return self.alias[ix]
补充其他与随机数相关的算法:
- 线性同余法
linear congruential generator (LCG)
R a n d S e e d = ( A ∗ R a n d S e e d + B ) % M RandSeed = (A * RandSeed + B) \% M RandSeed=(A∗RandSeed+B)%M
LCG的周期最大为 M,但大部分情况都会少于M。要令LCG达到最大周期,应符合以下条件:
- 中心极限生成高斯分布
反函数法
一般,一种概率分布,如果其分布函数为 y = F ( x ) y=F(x) y=F(x),那么,y的范围是0~1,求其反函数 G G G,然后产生0到1之间的随机数作为输入,那么输出的就是符合该分布的随机数了:
y = G ( x ) y= G(x) y=G(x)
中心极限定理
分大量的批次,每个批次生成12个 [ 0 , 1 ] [0,1] [0,1]间的随机数,然后求和。求和后的随机变量方差为 1 / 12 × 12 = 1 1/12\times 12=1 1/12×12=1,均值为 1 / 2 × 12 = 6 1/2 \times 12=6 1/2×12=6。然后-6。只要产生的批次足够多,就能生成总体服从 N ( 0 , 1 ) \mathcal{N}(0,1) N(0,1)的分布。
import numpy as np
import pylab as plt
n = 12
N = 5000
x = np.zeros([N])
for j in range(N):
a = np.random.rand(n)
u = sum(a)
x[j] = u - n * 0.5
plt.hist(x)
plt.show()
Box Muller
import numpy as np
import pylab as plt
N = 1000
x1 = np.random.rand(1, N)
x2 = np.random.rand(1, N)
y1 = np.sqrt(-2 * np.log(x1)) * np.cos(2 * np.pi * x2)
y2 = np.sqrt(-2 * np.log(x1)) * np.sin(2 * np.pi * x2)
y = np.hstack([y1, y2])
plt.hist(y)
plt.show()
- 接受拒绝采样生成高斯分布
逆采样(Inverse Sampling)和拒绝采样(Reject Sampling)原理详解
- 接受拒绝采样求 π \pi π
from math import sqrt
from random import random
def sample_pi(max_cnt=100000):
acc_cnt=0
for i in range(max_cnt):
x=random()
y=random()
if sqrt(x**2+y**2)<1:
acc_cnt+=1
print("pi =",acc_cnt/max_cnt*4)
sample_pi()
- 高斯分布生成均匀分布
a r c t a n ( z 0 z 1 ) + 0.5 arctan(\frac{z0}{z1})+0.5 arctan(z1z0)+0.5
生成二维标准正态分布的方法就是取两个独立的标准正态分布变量X和Y放在一起(X, Y)就行了
然后二维标准正态分布在直角坐标系里有各向同性,也就是(X, Y)这个点所指的方向和X轴(或者任何一个给定方向)的夹角是均匀分布的
很好理解, t a n ( θ ) = y x tan(\theta )=\frac{y}{x} tan(θ)=xy
反着用Box-Muller
AUC曲线计算
按照prob对【标签-样本】pairs进行排序,如果模型具有良好的排序能力,结果应该是
[
0
,
0
,
1
,
1
,
1
]
[0,0,1,1,1]
[0,0,1,1,1],正样本数
M
=
3
M=3
M=3,负样本数
N
=
2
N=2
N=2
只考虑正样本获取对应的排序值rankList
,为
[
3
,
4
,
5
]
[3,4,5]
[3,4,5],求和为12 。右边公式算出来是6
,分母6
,结果1,符合定义。
def calAUC(prob,labels):
f = list(zip(prob,labels))
rank = [values2 for values1,values2 in sorted(f,key=lambda x:x[0])]
rankList = [i+1 for i in range(len(rank)) if rank[i]==1]
posNum = 0
negNum = 0
for i in range(len(labels)):
if(labels[i]==1):
posNum+=1
else:
negNum+=1
auc = 0
auc = (sum(rankList)- (posNum*(posNum+1))/2)/(posNum*negNum)
print(auc)
return auc
1363. 形成三的最大倍数
class Solution:
def largestMultipleOfThree(self, digits: List[int]) -> str:
n=len(digits)
count=[0]*10
modulo=[0]*3
s=0
for digit in digits:
s+=digit
count[digit]+=1
modulo[digit%3]+=1
if s%3==1:
if modulo[1]>=1:
rcnt=1
rmod=1
else:
rcnt=2
rmod=2
elif s%3==2:
if modulo[2]>=1:
rcnt=1
rmod=2
else:
rcnt=2
rmod=1
else:
rcnt=0
rmod=0
ans=""
for i in range(10):
for j in range(count[i]):
if rcnt>0 and i%3==rmod:
rcnt-=1
else:
ans+=str(i)
if len(ans)>0 and ans[-1]=='0':
return '0'
return ans[::-1]
403. 青蛙过河
时间复杂度: O ( N 2 ) O(N^2) O(N2)
class Solution:
def canCross(self, stones: List[int]) -> bool:
mapper={}
for stone in stones:
mapper[stone]=set()
mapper[0].add(0)
for stone in stones:
for k in mapper[stone]:
for step in range(k-1,k+2):
if step>0 and stone+step in mapper:
mapper[stone+step].add(step)
return len(mapper[stones[-1]])>0
不同路径(考虑障碍物的DP)
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp=[[0]*(n+1) for _ in range(m+1)]
dp[1][1]=1
for i in range(1, m+1):
for j in range(1, n+1):
if i==1 and j==1:
continue
dp[i][j]=dp[i-1][j]+dp[i][j-1]
return dp[m][n]
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m=len(obstacleGrid)
n=len(obstacleGrid[0])
dp=[[0]*(n+1) for _ in range(m+1)]
# dp[1][1]=1
for i in range(1, m+1):
for j in range(1, n+1):
if obstacleGrid[i-1][j-1]==1:
continue
if i==1 and j==1:
dp[1][1]=1
continue
dp[i][j]=dp[i-1][j]+dp[i][j-1]
return dp[m][n]
股票难题
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
N=len(prices)
K=k
dp=[[[0]*2 for _ in range(K+1)] for _ in range(N+1)]
for k in range(K+1):
dp[0][k][1]=-inf
for i in range(N+1):
dp[i][0][1]=-inf
for i in range(1, N+1):
# for k in range(K,0,-1):
for k in range(1, K+1):
# 卖
dp[i][k][0]=max(dp[i-1][k][0], dp[i-1][k][1]+prices[i-1])
# 买
dp[i][k][1]=max(dp[i-1][k][1], dp[i-1][k-1][0]-prices[i-1])
return dp[N][K][0]
class Solution:
def maxProfit(self, prices: List[int]) -> int:
N=len(prices)
dp=[[0]*2 for _ in range(N+1)]
dp[0][1]=-inf
for i in range(1, N+1):
# 卖
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i-1])
# 买
dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i-1])
return dp[N][0]
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
if not prices:
return 0
N = len(prices)
dp_i_0 = 0
dp_i_1 = -inf
for i in range(N):
dp_i_0 = max(dp_i_0, dp_i_1 + prices[i] - fee)
dp_i_1 = max(dp_i_1, dp_i_0 - prices[i])
return dp_i_0
315. 计算右侧小于当前元素的个数
其实和逆序对差不多,就是改为对index排序(要记录下标)
和逆序对一样,在归并的统计区做数据统计,其他基本不变
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
counts = [0] * len(nums)
index = [i for i in range(len(nums))]
def merge_sort(l, r):
if l >= r:
return
mid = (r + l) // 2
merge_sort(l, mid)
merge_sort(mid + 1, r)
# 统计
b = mid + 1
for a in range(l, mid + 1):
while b <= r and nums[index[b]] < nums[index[a]]:
b += 1
counts[index[a]] += b - mid - 1
# 归并
a = l
b = mid + 1
merged = []
while a <= mid and b <= r:
if nums[index[a]] <= nums[index[b]]:
merged.append(index[a])
a += 1
else:
merged.append(index[b])
b += 1
while a <= mid:
merged.append(index[a])
a += 1
while b <= r:
merged.append(index[b])
b += 1
index[l:r + 1] = merged
merge_sort(0, len(nums) - 1)
# print(index)
return counts
255. 验证前序遍历序列二叉搜索树
入栈的时候递减,出栈的时候递增
class Solution:
def verifyPreorder(self, preorder: List[int]) -> bool:
root=-inf
stack=[]
# 局部递减,全局递增
for val in preorder:
if val<root:
return False
while stack and stack[-1]<val:
root=stack.pop()
stack.append(val)
return True
面试题33. 二叉搜索树的后序遍历序列(递归分治 / 单调栈,清晰图解)
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
root=inf
stack=[]
# 局部递增,全局递减
for val in reversed(postorder):
if val>root:
return False
while stack and stack[-1]>val:
root=stack.pop()
stack.append(val)
return True
376. 摆动序列
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
n=len(nums)
if n<=1:
return n
pre_delta=nums[1]-nums[0]
ans=1 if pre_delta==0 else 2
for i in range(2,n):
delta=nums[i]-nums[i-1]
if (delta<0 and pre_delta>=0) or (delta>0 and pre_delta<=0):
ans+=1
pre_delta=delta
return ans
582. 杀掉进程
- 并查集
考虑一种极端情况,数的结构类似链表,那么时间复杂度直接飙到 N 2 N^2 N2,就TLE
hashmap并查集的话,如果某个结点不存在,如x not in parent
,可以直接让parent[x]=x
,然后返回x
,这样连初始化都免了
class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
result = [kill]
parent = dict(zip(pid, ppid))
parent[0] = 0
for i in parent.keys():
x = self.find(i, parent, kill)
if x == kill:
result.append(i)
return result
def find(self, x, parent, kill):
# 在kill处早停
if parent[x] == kill:
return kill
# 路径压缩
p = parent[x]
if parent[x] != x:
parent[x] = self.find(parent[x], parent, kill)
return parent[x]
- 模拟树
的确是种好方法
反正都差不多,这样就 O ( N ) O(N) O(N)了
# 模拟树(哈希表)
class Node:
def __init__(self,val,children):
self.val = val
self.children = children
class Solution:
def killProcess(self, pid: List[int], ppid: List[int], kill: int) -> List[int]:
def getAllChildren(p, l):
"""递归搜索子进程"""
for n in p.children:
l.append(n.val)
getAllChildren(n,l)
mapping = {}
for _, val in enumerate(pid):
mapping[val] = Node(val,[])
for i in range(len(ppid)): # 父子关系连接
if ppid[i] >0:
cur = mapping[ppid[i]]
cur.children.append(mapping[pid[i]])
l = []
l.append(kill)
getAllChildren(mapping[kill],l)
return l
718. 最长重复子数组
- 动态规划
O ( N M ) O(NM) O(NM)
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
n, m = len(A), len(B)
dp = [[0] * (m + 1) for _ in range(n + 1)]
ans = 0
for i in range(1,n+1):
for j in range(1,m+1):
dp[i][j] = dp[i - 1][j - 1] + 1 if A[i-1] == B[j-1] else 0
ans = max(ans, dp[i][j])
return ans
- 滑动窗口
class Solution:
def findLength(self, A: List[int], B: List[int]) -> int:
def maxLength(addA: int, addB: int, length: int) -> int:
ret = k = 0
for i in range(length):
if A[addA + i] == B[addB + i]:
k += 1
ret = max(ret, k)
else:
k = 0
return ret
n, m = len(A), len(B)
ret = 0
for i in range(n):
length = min(m, n - i)
ret = max(ret, maxLength(i, 0, length))
for i in range(m):
length = min(n, m - i)
ret = max(ret, maxLength(0, i, length))
return ret
class Solution {
public:
int findLength(vector<int> &A, vector<int> &B) {
int m = A.size(), n = B.size(), res = 0;
// 枚举对应关系
for (int diff = -(m - 1); diff <= n - 1; ++diff) {
// 遍历公共部分
for (int i = max(0, -diff), l = 0; i < min(m, n - diff); ++i) {
l = (A[i] == B[i + diff]) ? (l + 1) : 0;
res = max(res, l);
}
}
return res;
}
};
剑指 Offer 41. 数据流中的中位数
class MedianFinder {
public:
priority_queue<int> lo;
priority_queue<int, vector<int>, greater<int> > hi;
void addNum(int num) {
lo.push(num);
hi.push(lo.top());
lo.pop();
if (lo.size() < hi.size()) {
lo.push(hi.top());
hi.pop();
}
}
double findMedian() {
if (lo.size() == hi.size()) {
return (lo.top() + hi.top()) / 2.0;
} else {
return lo.top();
}
}
};
516. 最长回文子序列
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
# 初始化1
for i in range(n):
dp[i][i] = 1
# 初始化2
for i in range(n - 1):
dp[i][i + 1] = 2 if s[i] == s[i + 1] else 1
# 遍历
# 间隔
for k in range(2,n):
# 起点
for i in range(n - k):
# 终点
j = i + k
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
97. 交错字符串
class Solution:
def isInterleave(self, s1: str, s2: str, s3: str) -> bool:
l1=len(s1)
l2=len(s2)
l3=len(s3)
if l1+l2!=l3:
return False
dp=[[False]*(l2+1) for _ in range(l1+1)]
dp[0][0]=True
for i in range(1,l1+1):
dp[i][0]=(dp[i-1][0] and s1[i-1]==s3[i-1])
for i in range(1,l2+1):
dp[0][i]=(dp[0][i-1] and s2[i-1]==s3[i-1])
for i in range(1,l1+1):
for j in range(1,l2+1):
if s3[i+j-1]==s1[i-1] and dp[i-1][j]:
dp[i][j]=True
elif s3[i+j-1]==s2[j-1] and dp[i][j-1]:
dp[i][j]=True
return dp[l1][l2]
416. 分割等和子集
todo: 条件可以改成尽量分成两个子集,求选中了哪些物品
其实这题就是转化过的01背包
class Solution:
def canPartition(self, nums: List[int]) -> bool:
L = len(nums)
sum_ = sum(nums)
if sum_ % 2:
return False
target = sum_ // 2
# L 行 target + 1 列
dp = [[0] * (target + 1) for _ in range(L + 1)]
# 容量为0的时候,绝逼恰好能装满
dp[0][0] = True
# 处理数据,让nums从1索引
nums.insert(0,0)
# 开始DP
for i in range(1, L+1):
for j in range(target + 1):
if j >= nums[i]:
dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
else:
dp[i][j] = dp[i - 1][j]
# 只装i个物品就装满了
if dp[i][target]:
return True
return False
312. 戳气球
有空再研究
class Solution:
def maxCoins(self, nums: List[int]) -> int:
n = len(nums)
points = [1] * (n + 2)
for i in range(n):
points[i + 1] = nums[i]
dp = [[0] * (n + 2) for _ in range(n + 2)]
for itv in range(2, n+2): # [2,n+1]
for i in range(0, n - itv + 2):
j = i + itv
for k in range(i + 1, j):
dp[i][j] = max(
dp[i][j],
dp[i][k] + dp[k][j] + points[i] * points[j] * points[k],
)
return dp[0][n + 1]
583. 两个字符串的删除操作
def longestCommonSubsequence(text1: str, text2: str) -> int:
l1=len(text1)
l2=len(text2)
dp=[[0]*(l2+1) for _ in range(l1+1)]
for i in range(1, l1+1):
for j in range(1, l2+1):
if text1[i-1]==text2[j-1]:
dp[i][j]=dp[i-1][j-1]+1
else:
dp[i][j]=max(dp[i][j-1],dp[i-1][j])
return dp[l1][l2]
class Solution:
def minDistance(self, word1: str, word2: str) -> int:
return len(word1)+len(word2)-2*longestCommonSubsequence(word1,word2)
234. 回文链表
- 递归
class Solution:
def recur(self,left):
if left is None:
return True
ans=self.recur(left.next)
ans &= (left.val==self.right.val)
self.right=self.right.next
return ans
def isPalindrome(self, head: ListNode) -> bool:
self.right=head
return self.recur(head)
- 快慢+翻转
def reverse(head):
if head is None or head.next is None:
return head
last=reverse(head.next)
head.next.next=head
head.next=None
return last
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
if head is None or head.next is None:
return True
slow=head
fast=head.next
while fast and fast.next:
slow=slow.next
fast=fast.next.next
p2=slow.next
slow.next=None
p2=reverse(p2)
p1=head
while p1 and p2:
if p1.val !=p2.val:
return False
p1=p1.next
p2=p2.next
return True
726. 原子的数量
class Solution:
def countOfAtoms(self, formula: str):
N = len(formula)
stack = [Counter()]
i = 0
while i < N:
if formula[i] == "(":
stack.append(Counter())
i += 1
elif formula[i] == ")":
top = stack.pop()
i += 1
i_start = i
while i < N and formula[i].isdigit():
i += 1
multi = int(formula[i_start:i] or 1)
for name, cnt in top.items():
stack[-1][name] += cnt * multi
else:
i_start = i
i += 1
while i < N and formula[i].islower():
i += 1
name = formula[i_start:i]
i_start = i
while i < N and formula[i].isdigit():
i += 1
multi = int(formula[i_start:i] or 1)
stack[-1][name] += multi
return "".join(name + (str(stack[-1][name]) if stack[-1][name] > 1 else '')
for name in sorted(stack[-1]))
405. 数字转换为十六进制数
class Solution {
public:
string get_hex(int num) {
char chr[2] = {0};
if (num <= 9) chr[0] = char('0' + num);
else chr[0] = char('a' + num - 10);
return string(chr);
}
string toHex(int num) {
if(num==0)
return "0";
unsigned int u = num;
string ans;
while (u) {
ans += get_hex(u & 15);
u >>= 4;
}
reverse(ans.begin(), ans.end());
return ans;
}
};
1. 两数之和
class Solution {
public:
vector<int> twoSum(vector<int> &nums, int target) {
map<int, int> sub2ix;
for (int i = 0; i < nums.size(); ++i) {
int sub = target - nums[i];
if (sub2ix.count(sub))
return {i, sub2ix[sub]};
else
sub2ix[nums[i]] = i;
}
return {0, 0};
}
};
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
num2ix = {}
for i, num in enumerate(nums):
other = target - num
if other in num2ix:
return [i, num2ix[other]]
num2ix[num] = i
raise ValueError
如果遇到N数之和的问题,马上想到东哥的NSum函数,关键知识点:
- 去重
- 遍历前排序
- 双指针
- 递归
class Solution {
public:
vector<vector<int>> nSum(vector<int> &nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> ans;
if (n < 2 || sz < n)
return ans;
if (n == 2) {
int lo = start, hi = sz - 1;
while (lo < hi) {
int left = nums[lo];
int right = nums[hi]; //忘了使用 nums[]
if (nums[lo] + nums[hi] < target)
while (lo < hi && nums[lo] == left) lo++;
else if (nums[lo] + nums[hi] > target)
while (lo < hi && nums[hi] == right) hi--;
else {
ans.push_back({nums[lo], nums[hi]});
// 等号判断条件写错
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
} else {
int i = start;
while (i < sz) { // 与3数和不同,这题的递归函数中考虑了 sz<n 的边界条件, 所以这里的边界条件可以简化
int cur_num = nums[i];
vector<vector<int>> cur_ans = nSum(nums, n - 1, i + 1, target - nums[i]);
if (cur_ans.size() > 0) {
for (auto vec:cur_ans) {
vec.push_back(nums[i]);
ans.push_back(vec);
}
}
while (i < sz and nums[i] == cur_num) i++;
}
}
return ans;
}
vector<vector<int>> fourSum(vector<int> &nums, int target) {
sort(nums.begin(),nums.end());
return nSum(nums, 4, 0, target);
}
};
3. 无重复字符的最长子串
滑动窗口
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> window;
// ans 应该初始化为0而不是负数,以适应s为空的情况
int l = 0, ans = 0;
for (int r = 0; r < s.size(); ++r) {
char c = s[r];
while (window.count(c)) {
window.erase(s[l++]);
}
window.insert(c);
ans = max(ans, (int) window.size());
}
return ans;
}
};
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
window = set()
l = 0
max_len = 0
for r in range(len(s)):
# 右端点元素进窗口之前,需要通过不断右移左端点保证当前窗口所有元素唯一
while s[r] in window:
window.remove(s[l])
l += 1
window.add(s[r])
max_len = max(max_len, r - l + 1) # 可以用 len(windows)代替
return max_len
- 至多包含 K 个不同字符的最长子串
class Solution:
def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int:
window = {}
l = 0
max_len = 0
for r in range(len(s)):
window[s[r]] = window.get(s[r], 0) + 1
# 注意是 <= ,考虑 k=0 的边界
while l <= r and len(window) > k:
window[s[l]] -= 1
if window[s[l]] == 0:
window.pop(s[l])
l += 1
max_len = max(max_len, r - l + 1)
return max_len
- 992.K 个不同整数的子数组
挂起
2. 两数相加链表
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode()
p = dummy
p1 = l1
p2 = l2
carry = 0
while p1 or p2:
v1 = p1.val if p1 else 0
v2 = p2.val if p2 else 0
val = v1 + v2 + carry
p.next = ListNode(val % 10)
p = p.next
carry = val // 10
p1 = p1.next if p1 else None
p2 = p2.next if p2 else None
if carry:
p.next = ListNode(carry)
return dummy.next
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* p1=l1, *p2=l2;
int carry=0;
ListNode *dummy=new ListNode(0);
ListNode *dp=dummy;
while(p1!=nullptr || p2!=nullptr || carry){
int a=p1==nullptr?0:p1->val;
int b=p2==nullptr?0:p2->val;
int s=a+b+carry;
dp->next=new ListNode(s%10);
dp=dp->next;
carry=(int)s/10;
p1=p1==nullptr?nullptr:p1->next;
p2=p2==nullptr?nullptr:p2->next;
}
return dummy->next;
}
};
相比于原版倒了,可以用【栈】或者【翻转链表】
位运算
模拟二进制加法
两个大数的题
5. 最长回文子串- 中心扩散法
- DP(待办)
class Solution:
def longestPalindrome(self, s: str) -> str:
def func(s, i, j):
while 0 <= i and j < len(s) and s[i] == s[j]:
i -= 1
j += 1
i += 1
j -= 1
return i, j
sa, sb = 0, 0
for i in range(len(s)):
a, b = func(s, i, i)
if b - a > sb - sa:
sa, sb = a, b
if i < len(s) - 1:
a, b = func(s, i, i + 1)
if b - a > sb - sa:
sa, sb = a, b
return s[sa:(sb + 1)]
哈希,KMP,马拉车。。我先选择放弃
奇数key不超过1
return sum(1 for val, cnt in collections.Counter(s).items() if cnt%2)<=1
字典树+马拉车,放弃
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for i in range(n - 1):
dp[i][i + 1] = 2 if s[i] == s[i + 1] else 1
for k in range(2,n):
for i in range(n - k):
j = i + k
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]
有空看,头痛
应该就是特化版的
但是马拉车很快,ON
class Solution:
#中心扩散+马拉车优化 时间复杂度o(n),空间复杂度 o(n)
def countSubstrings(self, s: str) -> int:
s = "#" + "#".join(s) + "#"
right = -1
maxlen = [0]*len(s)
res = 0
for i in range(len(s)):
if i<=right:
im = 2*j-i
minlen = min(maxlen[im],right-i)
tmp = self.expand(s,i-minlen,i+minlen)
else:
tmp = self.expand(s,i,i)
maxlen[i] = tmp
if i+tmp>right:
right = i+tmp
j = i
if i%2==1:
res += maxlen[i]//2 + 1
else:
res += (maxlen[i]+1)//2
return res
def expand(self,s,l,r):
while 0<=l and r<len(s) and s[l] == s[r]:
l -= 1
r += 1
return (r-l)//2 - 1
class Solution:
def countSubstrings(self, s: str) -> int:
res = 0
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(i + 1):
if s[i] == s[j] and (i - j < 2 or dp[j + 1][i - 1]):
dp[j][i] = 1
res += 1
return res
class Solution:
def countSubstrings(self, s: str) -> int:
L = len(s)
cnt = 0
# 以某一个元素为中心的奇数长度的回文串的情况
for center in range(L):
left = right = center
while left >= 0 and right < L and s[left] == s[right]:
cnt += 1
left -= 1
right += 1
# 以某对元素为中心的偶数长度的回文串的情况
for left in range(L - 1):
right = left + 1
while left >= 0 and right < L and s[left] == s[right]:
cnt += 1
left -= 1
right += 1
return cnt
42. 接雨水
- DP
class Solution:
def trap(self, height: List[int]) -> int:
sum = 0
n = len(height)
max_left = [0] * n
max_right = [0] * n
for i in range(1, n - 1):
max_left[i] = max(max_left[i - 1], height[i - 1])
for i in reversed(range(1, n - 1)): # range(n - 2, 0, -1):
max_right[i] = max(max_right[i + 1], height[i + 1])
for i in range(1, n - 1):
min_height = min(max_left[i], max_right[i])
sum += max(0, min_height - height[i])
return sum
class Solution {
public:
int trap(vector<int>& height) {
int n=height.size();
vector<int> left(n,0);
vector<int> right(n,0);
int ans=0;
for(int i=1;i<n;++i){
left[i]=max(left[i-1],height[i-1]);
}
for(int i=n-2;i>=0;--i){
right[i]=max(right[i+1],height[i+1]);
}
for(int i=1;i<n-1;++i){
ans+=max(0,
min(left[i],right[i])-height[i]
);
}
return ans;
}
};
- 单调栈
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
stack = []
ans = 0
# □
# □ □
# □ □ □ □
# 2 1 0 1 3
# ↑ ↑ 触发出栈情况
# h: 1 1
# l: 1 3
# 出站后的pre变量代表了前一个高度
for i in range(n):
while stack and height[stack[-1]] < height[i]:
pre = stack.pop()
# 如果stack为空,表示 [2, 3, 4] 这种情况,围不起来
if not stack:
break
t = stack[-1]
h = min(height[t], height[i]) - height[pre] # 当前高度 - 上一个高度
l = i - t - 1
ans += h * l
stack.append(i)
return ans
- 堆
O ( N log N ) O(N\log N) O(NlogN)
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
vis=[0]*n
pq=[]
for i in (0, n-1):
vis[i]=True
heapq.heappush(pq, (height[i], i))
ans=0
while pq:
wh,x=heapq.heappop(pq)
for dx in [-1, 1]:
cx=x+dx
if not (0<=cx<n):
continue
if vis[cx]:
continue
ch=height[cx]
ans+=max(0, wh-ch)
vis[cx]=True
heapq.heappush(pq,(max(ch,wh),cx))
return ans
O ( N M log ( N + M ) ) O(NM\log (N+M)) O(NMlog(N+M))
class Solution:
def trapRainWater(self, heightMap: List[List[int]]) -> int:
m, n = len(heightMap), len(heightMap[0])
pq = []
vis = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
if i in (0, m - 1) or j in (0, n - 1):
heapq.heappush(pq, (heightMap[i][j], i, j))
vis[i][j] = 1
dirs = [-1, 0, 1, 0, -1]
ans = 0
while pq:
wh, x, y = heapq.heappop(pq)
for k in range(4):
nx, ny = x + dirs[k], y + dirs[k + 1]
if 0 <= nx < m and 0 <= ny < n and vis[nx][ny] == 0:
ch = heightMap[nx][ny]
ans += max(0, wh - ch)
vis[nx][ny] = 1
heapq.heappush(pq, (max(wh, ch), nx, ny))
return ans
206. 反转链表
我的天,写这种题都会卡壳
- 迭代
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
p = head
pre = None
while p:
aft = p.next
p.next = pre
pre = p
p = aft
return pre
- 递归
递归法就记一张图
1->2->3->^
1->2<-3
|->^
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return head
last=self.reverseList(head.next)
head.next.next=head
head.next=None
return last
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr || head->next==nullptr)
return head;
ListNode* last=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return last;
}
};
4. 寻找两个正序数组的中位数
令人智熄的题
智商不够,记忆力来凑(背下来)
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
l1 = len(nums1)
l2 = len(nums2)
tot = l1 + l2
def findKthElement(k):
ix1 = ix2 = 0
while True:
# 注意顺序
if ix1 == l1:
return nums2[ix2 + k - 1]
if ix2 == l2:
return nums1[ix1 + k - 1]
if k == 1:
return min(nums1[ix1], nums2[ix2])
nix1 = min(ix1 + k // 2 - 1, l1 - 1)
nix2 = min(ix2 + k // 2 - 1, l2 - 1)
if nums1[nix1] < nums2[nix2]:
k -= (nix1 - ix1 + 1) # -= 写成了 =
ix1 = nix1 + 1
else:
k -= (nix2 - ix2 + 1)
ix2 = nix2 + 1
return findKthElement(tot // 2 + 1) if tot % 2 else (findKthElement(tot // 2) + findKthElement(
tot // 2 + 1)) / 2
21. 合并两个有序链表
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
dummy=ListNode()
p=dummy
while l1 and l2:
if l1.val<l2.val:
p.next=l1
l1=l1.next
else:
p.next=l2
l2=l2.next
p=p.next
p.next=l1 if l1 else l2
return dummy.next
TODO: 合并K个有序链表
15. 三数之和class Solution:
def twoSum(self, nums, start, target):
l = start
r = len(nums) - 1
ans = []
while l < r:
lo = nums[l]
hi = nums[r]
if lo + hi == target:
ans.append([lo, hi])
# 判断条件写成了 !=
while l < r and nums[r] == hi: r -= 1 # 写成了 += 1
while l < r and nums[l] == lo: l += 1
elif lo + hi < target:
while l < r and nums[l] == lo: l += 1
else:
while l < r and nums[r] == hi: r -= 1
return ans
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
n = len(nums)
ans = []
i = 0
while (i < n - 2): # 错误地使用了for循环
num = nums[i]
cur_ans = self.twoSum(nums, i + 1, 0 - num)
if cur_ans:
ans += [item + [num] for item in cur_ans]
while i < n - 2 and nums[i] == num: i += 1
return ans
146. LRU 缓存机制
看了一下以前的代码,迷迷糊糊默写的,居然过了,离谱。。
双向链表:
- addLast : 队尾入队
- remove:删除一个结点
- removeFirst:队首出队
LRUCache:
- makeRecently:将元素拿到队尾
- removeLeastRecently:删除对少最近,其实就是删队头
- addRecently: 入队
- removeKey: 删除任意一个结点
class Node():
def __init__(self, k, v):
self.k = k
self.v = v
self.prev = None
self.next = None
class DoubleList():
def __init__(self):
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
self.size = 0
def addLast(self, x: Node):
x.next = self.tail
x.prev = self.tail.prev
self.tail.prev.next = x
self.tail.prev = x
self.size += 1
def remove(self, x):
x.prev.next = x.next
x.next.prev = x.prev
self.size -= 1
def removeFirst(self):
if self.head.next == self.tail:
return None
first = self.head.next
self.remove(first)
return first
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity
self.cache = DoubleList()
self.map: Dict[int, Node] = {}
def get(self, key: int) -> int:
if key in self.map:
self.makeRecently(key)
return self.map[key].v
return -1
def put(self, key: int, value: int) -> None:
if key not in self.map:
if self.cache.size == self.capacity:
self.removeLeastRecently()
self.addRecently(key, value)
self.removeKey(key)
self.addRecently(key, value)
def makeRecently(self, key):
node = self.map[key]
self.cache.remove(node)
self.cache.addLast(node)
def removeLeastRecently(self):
first = self.cache.removeFirst()
self.map.pop(first.k)
def addRecently(self, key, value):
node = Node(key, value)
self.map[key] = node
self.cache.addLast(node)
如果用Java的LinkedHashSet
来刷的话,会很方便,不过需要记住删队头结点的代码是取KeySet
然后迭代得到第一个key最后remove掉
import java.util.LinkedHashMap;
import java.util.Scanner;
class LRU {
int capacity;
LinkedHashMap<Integer, Integer> cache;
LRU(int capacity) {
this.capacity = capacity;
cache = new LinkedHashMap<>();
}
int makeRecently(int k) {
int v = cache.get(k);
cache.remove(k);
cache.put(k, v);
return v;
}
void addRecently(int k, int v) {
cache.put(k, v);
}
void removeKey(int k) {
cache.remove(k);
}
void removeLeastRecently() {
int oldestKey = cache.keySet().iterator().next();
cache.remove(oldestKey);
}
void put(int k, int v) {
if (cache.containsKey(k)) {
removeKey(k);
addRecently(k, v);
} else {
if (cache.size() >= capacity) {
removeLeastRecently();
}
addRecently(k, v);
}
}
int get(int k) {
if (cache.containsKey(k))
return makeRecently(k);
return -1;
}
}
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int capacity = input.nextInt();
input.nextLine();
LRU lru = new LRU(capacity);
for (int i = 0; i < n; i++) {
String[] line = input.nextLine().split(" ");
if (line[0].equals("GET")) System.out.println(lru.get(Integer.valueOf(line[1])));
if (line[0].equals("PUT")) lru.put(Integer.valueOf(line[1]), Integer.valueOf(line[2]));
}
}
}
import java.util.*;
class LRU{
int capacity;
LinkedHashMap<Integer,Integer> cache;
LRU(int capacity){
this.capacity=capacity;
cache=new LinkedHashMap<Integer,Integer>();
}
void put(int k, int v){
if(cache.containsKey(k)){
cache.remove(k);
cache.put(k,v);
}else{
if(cache.size()>=capacity){
int oldK=cache.keySet().iterator().next();
cache.remove(oldK);
}
cache.put(k, v);
}
}
int get(int k){
if(cache.containsKey(k)){
int v=cache.get(k);
cache.remove(k);
cache.put(k,v);
return v;
}
return -1;
}
}
public class Main{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
int n=input.nextInt();
int capacity=input.nextInt();
input.nextLine();
LRU lru=new LRU(capacity);
while(n-->0){
String [] line=input.nextLine().split(" ");
if(line.length==2){
System.out.println(lru.get(Integer.parseInt(line[1])));
}else{
lru.put(Integer.parseInt(line[1]), Integer.parseInt(line[2]));
}
}
}
}
53. 最大子序和
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
dp=0
ans=-inf
for num in nums:
if dp<0: # 要写在前面
dp=0
dp+=num
ans=max(ans,dp)
return ans
25. K 个一组翻转链表
class Solution:
def reverse(self, a, b):
# 相当于左开右闭,例如k=2,
# 1->2->3
# a↑ b↑
# 最后返回是的 2->1->∅
pre = None
p = a
while p != b:
aft = p.next
p.next = pre
pre = p
p = aft
return pre
def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
a = b = head
# 循环体写错 (老错误)
for _ in range(k):
if b is None:
return head
b = b.next
new_head = self.reverse(a, b)
# 把 a 写成了 new_head
a.next = self.reverseKGroup(b, k) # b 写错 (老错误)
return new_head
TODO: 默写
11. 盛最多水的容器class Solution:
def maxArea(self, height: List[int]) -> int:
l=0
n=len(height)
r=n-1
ans=0
while l<r:
ans=max((r-l)*min(height[l], height[r]),ans)
if height[l]<height[r]:
l+=1
else:
r-=1
return ans
TODO: 单调栈
121. 买卖股票的最佳时机class Solution:
def maxProfit(self, prices: List[int]) -> int:
min_=inf
ans=-inf
for price in prices:
min_=min(min_,price)
ans=max(ans,price-min_)
return ans
TODO: 刷其他5题相似的
33. 搜索旋转排序数组class Solution:
def search(self, nums: List[int], target: int) -> int:
n=len(nums)
l=0
r=n-1
while l<=r:
mid=(l+r)//2
if nums[mid]==target:
return mid
# 左边有序
if nums[l]<nums[mid] or l==mid:
if nums[l]<=target<nums[mid]:
r=mid-1
else:
l=mid+1
else:
if nums[mid]<target<=nums[r]:
l=mid+1
else:
r=mid-1
return -1
class Solution:
def search(self, nums: List[int], target: int) -> int:
n=len(nums)
l=0
r=n-1
while l<=r:
mid=(l+r)//2
if nums[mid]==target:
return True
if nums[l]==nums[mid] and l!=mid:
l+=1
continue
# 左边有序
if nums[l]<nums[mid] or l==mid:
if nums[l]<=target<nums[mid]:
r=mid-1
else:
l=mid+1
else:
if nums[mid]<target<=nums[r]:
l=mid+1
else:
r=mid-1
return False
class Solution:
def findMin(self, numbers: List[int]) -> int:
l = 0
r = len(numbers) - 1
while l < r:
mid = (l + r) // 2
if numbers[l] >= numbers[r]:
if numbers[mid] < numbers[r]:
r = mid
else:
l = mid + 1
else:
return numbers[l]
return numbers[r]
46. 全排列
TODO:
复习了一下笔记。粘贴我的笔记:
我采用中规中矩的排列树做的,最后加个去重,并对两个参数的取值进行了思考
class Solution:
def permutation(self, s: str) -> List[str]:
path = list(s)
ans = []
L = len(path)
def backtrace(t):
# t == L 和 t == L - 1 一样
# 因为如果 t == L 的话,在上一个状态 t=L-1时,
# 是在做原地交换,相当于没做任何事
# 所以 t == L - 1 即可
if t == L - 1:
ans.append("".join(path))
return
# 初始条件必须是 t 而不是 t+1
# t 看似是在原地交换,但是要考虑当前状态不做任何事的情况进入下个状态
for i in range(t, L): # i 最大为 L-1
path[t], path[i] = path[i], path[t]
backtrace(t + 1)
path[t], path[i] = path[i], path[t]
backtrace(0)
return list(set(ans))
看了题解后,我做了修改
虽然时间复杂度还是 O ( N ! ) O(N!) O(N!) 但是实际的运行时间还是会减少的
class Solution:
def permutation(self, s: str) -> List[str]:
path = list(s)
ans = []
L = len(path)
def backtrace(t):
if t == L :
ans.append("".join(path))
return
visit = set()
for i in range(t, L):
if path[i] in visit:
continue
visit.add(path[i])
path[t], path[i] = path[i], path[t]
backtrace(t + 1)
path[t], path[i] = path[i], path[t]
backtrace(0)
return list(set(ans))
20. 有效的括号
class Solution:
def isValid(self, s: str) -> bool:
stack=collections.deque()
mapper={
')':'(',
'}':'{',
']':'[',
}
for c in s:
if c in mapper:
if not stack:
return False
if mapper[c]!=stack.pop():
return False
else:
stack.append(c)
return not stack # 这里写错
31. 下一个排列
next-permutation的题,记3个图,3个步骤就好了
- 单调下降的序列无论怎么交换元素,无无法让字典序更大
- 只能找到单调下降前的那个元素,让那个元素稍稍变大一点
- 搞完之后
把单调下降
的序列变成单调增序列,因为除了要让字典序变大,还要保证变大后的字典序是尽可能小的。
编码时多想想case,1 5 1
, 1 1 5
, 5 1 1
之类的 ,弄清楚等号
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
n=len(nums)
# 找到第一个比后面小的元素
i=n-2
while i>=0 and nums[i]>=nums[i+1]:
i-=1
if i>=0:
j=n-1 # [1,5,1] 的 case
while j>=0 and nums[i]>=nums[j]: # 就差一个等于号就全盘写对了,牢记!!!!!!!!
j-=1
nums[i], nums[j]=nums[j], nums[i]
i=i+1
j=n-1
while i<j:
nums[i],nums[j]=nums[j],nums[i]
i+=1
j-=1
23. 合并K个升序链表
方法 | 暴力 | 分治 | 堆 |
---|---|---|---|
时间复杂度 | O ( k 2 n ) O(k^2n) O(k2n) | O ( k log k n ) O(k\log kn) O(klogkn) | O ( k log k n ) O(k\log kn) O(klogkn) |
空间复杂度 | O ( 1 ) O(1) O(1) | O ( log k ) O(\log k) O(logk) | O ( n ) O(n) O(n) |
class MyNode:
def __init__(self, node: ListNode):
self.node = node
def __lt__(self, other):
return self.node.val < other.node.val
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
my_lists=[MyNode(node) for node in lists if node] # 记得把阴间结点去掉
heapq.heapify(my_lists)
dummy = ListNode(0)
p = dummy
while my_lists:
node = heapq.heappop(my_lists).node
p.next = node
p = p.next
if node.next:
heapq.heappush(my_lists, MyNode(node.next))
p.next = None
return dummy.next
def merge(a: ListNode, b: ListNode):
dummy = ListNode(0)
dp = dummy
while a and b:
if a.val < b.val:
dp.next = a
a = a.next
else:
dp.next = b
b = b.next
dp = dp.next
dp.next = a if a else b
return dummy.next
def partition(lists):
n = len(lists)
if n == 0:
return None
elif n == 1:
return lists[0]
else:
mid = len(lists) // 2
return merge(partition(lists[:mid]), partition(lists[mid:]))
class Solution:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
return partition(lists)
56. 合并区间
- 二刷 默都默不出
class Solution:
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda x:x[0])
res=[]
for itv in intervals:
if res and res[-1][1]>=itv[0]: # >=
res[-1][1]=max(res[-1][1], itv[1])
else:
res.append(itv)
return res
直接在上题基础上append一个newInterval,然后用上题的代码做
但是这么做会导致时间复杂度为 O ( N log N ) O(N\log N) O(NlogN)
但没有关系,插入的时候用类似插入排序的做法就OK了
用类似归并排序的方法做
class Solution:
def intervalIntersection(self, A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
i = j = 0
res = []
while i < len(A) and j < len(B):
a1, a2 = A[i][0], A[i][1]
b1, b2 = B[j][0], B[j][1]
if a1 <= b2 and a2 >= b1:
res.append([max(a1, b1), min(a2, b2)])
if b2 > a2:
i += 1
else:
j += 1
return res
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
if not intervals:
return 0
intervals.sort(key=lambda x: x[1])
n = len(intervals)
right = intervals[0][1]
ans = 1
for i in range(1, n):
if intervals[i][0] >= right:
ans += 1
right = intervals[i][1]
return n - ans
70. 爬楼梯
class Solution:
def climbStairs(self, n: int) -> int:
dp=[0]*(n+1)
dp[0]=dp[1]=1
for i in range(2, n+1):
dp[i]=dp[i-1]+dp[i-2]
return dp[n]
TODO: 常数时间复杂度
变态跳台阶:
f ( n ) = 2 f ( n − 1 ) f(n)=2f(n-1) f(n)=2f(n−1)
# -*- coding:utf-8 -*-
class Solution:
def jumpFloorII(self, number):
# write code here
if number <= 2:
return number
total = 1
for _ in range(1, number):
total *= 2
return total
跳台阶,不能连续跳2阶
f
f
f: 跳到n阶的总方法数
g
g
g: 最后一步跳一阶跳到n阶的总方法数
初始值:
f
=
[
1
,
2
,
3
]
f=[1,2,3]
f=[1,2,3]
g
=
[
1
,
1
,
2
]
g=[1,1,2]
g=[1,1,2]
初始值很容易求得, f f f不用说, g g g可以用 g ( n ) = f ( n − 1 ) g(n)=f(n-1) g(n)=f(n−1)求得
f ( n ) = f ( n − 1 ) + g ( n − 2 ) f(n)=f(n-1)+g(n-2) f(n)=f(n−1)+g(n−2)
解释:跳一步+跳两步。跳两步是最后一步跳一阶的情况
g ( n ) = f ( n − 1 ) g(n)=f(n-1) g(n)=f(n−1)
解释:最后一步跳一阶
有趣的是,这两个递推公式可以合并
public int modifiedJumpStairs(int n) {
if (n <= 3)
return n;
int[] dp = new int[n];
for (int i = 0; i < n; i++) {
if (i <= 2) {
dp[i] = i+1;
} else {
dp[i] = dp[i-1] + dp[i-3];
}
}
return dp[dp.length-1];
}
7. 整数反转
class Solution {
public:
int reverse(int x) {
int ret=0;
while(x!=0){
int pop=x%10;
x/=10;
if(ret>INT_MAX/10 ||(ret==INT_MAX && pop>7)) return 0;
if(ret<INT_MIN/10 ||(ret==INT_MIN && pop<-7)) return 0;
ret=ret*10+pop;
}
return ret;
}
};
215. 数组中的第K个最大元素
- 调用系统的堆
// 除了用greater<int> , 还可以用以下的 cmp
struct cmp {
bool operator()(int &a, int &b) {
return a > b; // 注意是 >
}
};
class Solution {
public:
int findKthLargest(vector<int> &nums, int k) {
priority_queue<int,vector<int>,greater<int> > heap;
for (int num:nums) {
if (heap.size() < k) {
heap.push(num);
} else if (num > heap.top()) {
heap.pop();
heap.push(num);
}
}
return heap.top();
}
};
- 自己写堆的解法
void sink(vector<int> &nums, int n, int k) {
while (true) {
int i = k * 2 + 1, j = k * 2 + 2;
if (i >= n) break;
int ix = i;
if (j < n && nums[j] < nums[i]) ix = j;
if (nums[k] < nums[ix]) break;
swap(nums[k], nums[ix]);
k = ix;
}
}
void swim(vector<int> &nums, int k) {
for (int p = (k - 1) / 2; p >= 0 && nums[p] > nums[k]; k = p, p = (k - 1) / 2) {
swap(nums[p], nums[k]);
}
}
class Solution {
public:
int findKthLargest(vector<int> &nums, int k) {
vector<int> heap;
for (int num:nums) {
if (heap.size() < k) {
heap.push_back(num);
swim(heap, heap.size() - 1);
} else if (num > heap[0]) {
heap[0] = num;
sink(heap, heap.size(), 0);
}
}
return heap[0];
}
};
- cpp写的快排
int partition(vector<int> &nums, int l, int r) {
int rix = rand() % (r - l + 1) + l;
swap(nums[rix], nums[l]);
int j = l, pivot = nums[l];
for (int i = l + 1; i <= r; ++i) {
if (nums[i] < pivot) {
++j;
swap(nums[j], nums[i]);
}
}
swap(nums[j], nums[l]);
return j;
}
class Solution {
public:
int findKthLargest(vector<int> &nums, int k) {
int n = nums.size();
int l = 0, r = n - 1;
int target = n - k;
while (true) {
int j = partition(nums, l, r);
if (j == target) return nums[j];
else if (j < target) l = j + 1;
else r = j - 1;
}
}
};
TODO: 用cpp刷, 快排
通过 partition 减治 + 优先队列(Java、C++、Python)
发现了一种更好写的Partition方法。
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
partition分治 | O ( N ) O(N) O(N) | O ( 1 ) O(1) O(1) |
堆方法 | O ( N log K ) O(N\log K) O(NlogK) | O ( K ) O(K) O(K) |
def partition(nums, l, r):
rix = random.randint(l, r)
nums[rix], nums[l] = nums[l], nums[rix]
j = l
pivot = nums[l]
for i in range(l + 1, r + 1):
if nums[i] < pivot:
j += 1
nums[j], nums[i] = nums[i], nums[j]
nums[l], nums[j] = nums[j], nums[l]
return j
def partition2(nums, l, r):
rix = random.randint(l, r)
nums[rix], nums[l] = nums[l], nums[rix]
pivot = nums[l]
while l < r:
while l < r and nums[r] > pivot:
r -= 1
if l < r:
nums[l], nums[r] = nums[r], nums[l]
l += 1
while l < r and nums[l] < pivot:
l += 1
if l < r:
nums[l], nums[r] = nums[r], nums[l]
r -= 1
nums[l] = pivot
return l
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
n = len(nums)
l, r = 0, n - 1
target = n - k
while True:
index = partition2(nums, l, r)
if index == target:
return nums[index]
elif index < target:
l = index + 1
else:
r = index - 1
199. 二叉树的右视图
class Solution:
def rightSideView(self, root: TreeNode) -> List[int]:
if not root:
return []
queue=[root]
ans=[]
while queue:
sz=len(queue)
cur_level=[]
for _ in range(sz):
top=queue.pop(0)
cur_level.append(top.val)
if top.left:
queue.append(top.left)
if top.right:
queue.append(top.right)
ans.append(cur_level[-1])
return ans
124. 二叉树中的最大路径和
class Solution(object):
def maxPathSum(self, root: TreeNode) -> int:
res = -inf # 写成了0 错了
def dfs(node):
nonlocal res
if node is None:
return 0
left = max(0, dfs(node.left))
right = max(0, dfs(node.right))
res = max(res, (left + right + node.val))
return max(left, right) + node.val
dfs(root)
return res
200. 岛屿数量
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
n=len(grid)
if not n:
return 0
m=len(grid[0])
vis=[[0]*m for _ in range(n)]
def dfs(x, y):
if not (0<=x<n and 0<=y<m):
return
if vis[x][y] or grid[x][y]=='0':
return
vis[x][y]=1
dfs(x+1,y)
dfs(x-1,y)
dfs(x,y-1)
dfs(x,y+1)
ans=0
for x in range(n):
for y in range(m):
if vis[x][y] or grid[x][y]=='0':
continue
dfs(x, y)
ans+=1
return ans
92. 反转链表 II
class Solution:
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
# 以case 1, 2, 3, 4, 5 为例,left = 2, right = 4
p = head
L = right - left + 1 # 翻转的部分是 2 3 4, 长度为3
dummy=ListNode(0)
dp=dummy
for _ in range(left - 1): # 把 翻转部分 2 之前的添加到dummy中(dummy用来返回)
dp.next=p
dp=dp.next
p = p.next
# 迭代完之后 p=2
aft = p.next
pre = None
l2_tail = p # 第二段(l2) 的开头,但翻转后会变成末尾
for _ in range(L):
aft = p.next
p.next = pre
pre = p
p = aft
dp.next = pre # 接上翻转的列表, 即 1 4 3 2
l2_tail.next = aft # l2 段 接上剩下没迭代的
return dummy.next
class Solution:
successor=None
def reverseN(self,head, n):
if n==1:
self.successor=head.next
return head
last=self.reverseN(head.next,n-1)
head.next.next=head
head.next=self.successor
return last
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
if left==1:
return self.reverseN(head, right)
head.next=self.reverseBetween(head.next,left-1, right-1)
return head
93. 复原IP地址
- DFS
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
ans = []
def dfs(s, ix, path):
if s == "" and len(path) == 4:
ans.append(".".join(path))
return
# 不选
if ix <= len(s) - 1:
dfs(s, ix + 1, path)
# 选
if len(path) >= 4 or ix == 0:
return
chose, rest = s[:ix], s[ix:]
if chose=="" or int(chose) > 255 or (chose.startswith('0') and chose != '0'):
return
cur_path = path + [chose]
if len(cur_path) == 4 and rest != "":
return
dfs(rest, 0, cur_path)
dfs(s, 1, [])
return ans
22. 括号生成
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = []
def dfs(s, left, right):
if left > n or right > n:
return
if left == right == n:
ans.append(s)
dfs(s + "(", left + 1, right)
if left > right:
dfs(s + ")", left, right + 1)
dfs("", 0, 0)
return ans
TODO: 用递归+cache做,理解时间复杂度
72. 编辑距离olution:
def minDistance(self, word1: str, word2: str) -> int:
l1 = len(word1)
l2 = len(word2)
dp = [[0] * (l2 + 1) for _ in range(l1 + 1)]
# 错在没+1
for i in range(1, l1 + 1):
dp[i][0] = i
for i in range(1, l2 + 1):
dp[0][i] = i
for i in range(1, l1 + 1):
for j in range(1, l2 + 1):
# 错在没+1
left = dp[i][j - 1] + 1
down = dp[i - 1][j] + 1
ld = dp[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
ld += 1
dp[i][j] = min(left, down, ld)
return dp[l1][l2]
插入删除替换的惩罚不一样的编辑距离,面经也有提到
102. 二叉树的层序遍历不浪费时间了
148. 排序链表- 插入排序
class Solution:
def insertionSortList(self, head: ListNode) -> ListNode:
if not head: return head
dummy = ListNode(-inf)
dummy.next = head # 忘了
tail = head
p = head.next
tail.next = None
while p:
aft = p.next
if p.val >= tail.val:
tail.next = p
tail = p
tail.next = None
else:
dp = dummy
# 结束循环后,dp.next.val > p.val
while dp.next and dp.next.val <= p.val:
dp = dp.next
p.next = dp.next # 写成了dp
dp.next = p
p = aft
return dummy.next
- 归并排序
需要牢记这题不同的快慢指正初始化条件
class Solution:
def sortList(self, head: ListNode) -> ListNode:
# 递归终点
if not head or not head.next:
return head
# 中点寻找
slow = head
fast = head.next # 否则爆栈
while fast and fast.next:
slow = slow.next
fast = fast.next.next
mid = slow.next
slow.next = None
# 处理左链表与右链表
left = self.sortList(head)
right = self.sortList(mid)
# 合并两个排序连败哦
dummy = dp = ListNode(0)
while left and right:
if left.val < right.val:
dp.next = left
left = left.next
else:
dp.next = right
right = right.next
dp = dp.next
dp.next = left if left else right
return dummy.next
※ 440. 字典序的第K小数字
真的很头大,有空默写一遍
[字节跳动最常考题之一]本题史上最完整具体的手摸手解答,时间效率超越100%用户
class Solution:
def findKthNumber(self, n: int, k: int) -> int:
def get_count(prefix):
nxt = prefix + 1
count = 0
while prefix <= n:
count += min(nxt, n + 1) - prefix
prefix *= 10
nxt *= 10
return count
p = 1
prefix = 1
while p < k:
count = get_count(prefix)
if k < p + count:
p += 1
prefix *= 10
else:
p += count
prefix += 1
return prefix
19. 删除链表的倒数第 N 个结点
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
if not head:
return None
fast=head
while fast and n:
n-=1
fast=fast.next
dummy=ListNode(0)
dummy.next=head
p=dummy
while fast:
fast=fast.next
p=p.next
if p and p.next:
p.next=p.next.next
return dummy.next
54. 螺旋矩阵
注意break的判断条件
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
m, n = len(matrix), len(matrix[0])
left = top = 0
right, bottom = n - 1, m - 1
ans = []
while True:
for i in range(left, right + 1):
ans.append(matrix[top][i])
top += 1
if top > bottom or left > right: break
for i in range(top, bottom + 1):
ans.append(matrix[i][right])
right -= 1
if top > bottom or left > right: break
for i in range(right, left - 1, -1):
ans.append(matrix[bottom][i])
bottom -= 1
if top > bottom or left > right: break
for i in range(bottom, top - 1, -1):
ans.append(matrix[i][left])
left += 1
if top > bottom or left > right: break
return ans
105. 从前序与中序遍历序列构造二叉树
32. 最长有效括号
class Solution:
def longestValidParentheses(self, s: str) -> int:
n = len(s)
dp = [0] * n
ans = 0
for i in range(1, n):
if s[i] == ")":
if s[i - 1] == "(":
dp[i] = 2 + (
dp[i - 2] if i - 2 >= 0 else 0
)
else:
pre = dp[i - 1]
pre_ix = i - 1 - pre
if pre_ix >= 0 and s[pre_ix] == "(":
dp[i] = dp[i - 1] + 2
# 没把条件缩进来
if pre_ix - 1 > 0: # ()(())
dp[i] += dp[pre_ix - 1]
ans = max(ans, dp[i])
return ans
TODO: 栈
103. 二叉树的锯齿形层序遍历 143. 重排链表这道题反过来就是字节的一个题了
def reverse(head: ListNode):
p=head
pre=None
while p:
aft=p.next
p.next=pre
pre=p
p=aft
return pre
class Solution:
def reorderList(self, head: ListNode) -> None:
if head is None or head.next is None: # 边界
return head
slow=head
fast=head.next
while fast and fast.next:
fast=fast.next.next
slow=slow.next
p2=slow.next
if p2 is None: # 边界
return head
slow.next=None
p2=reverse(p2)
a=head
b=p2
while a and b:
an=a.next
bn=b.next
a.next=b
b.next=an
a=an
b=bn
return head
10. 正则表达式匹配
class Solution:
def isMatch(self, s: str, p: str) -> bool:
ls=len(s)
lp=len(p)
def match(i,j):
if i<=0 or j<=0:
return False
if p[j-1]=='.':
return True
if s[i-1]==p[j-1]:
return True
return False
dp=[[False]*(lp+1) for _ in range(ls+1)]
dp[0][0]=True
for i in range(ls+1):
for j in range(1, lp+1):
if p[j-1]=='*':
if match(i,j-1):
dp[i][j] = dp[i][j-2] | dp[i-1][j]
else:
dp[i][j] = dp[i][j-2]
else:
if match(i,j):
dp[i][j] |= dp[i-1][j-1]
else:
dp[i][j]=False
return dp[ls][lp]
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s), len(p)
dp = [[False] * (n + 1) for _ in range(m + 1)]
dp[0][0] = True
for i in range(1, n + 1):
if p[i - 1] == '*':
dp[0][i] = True
else:
break
for i in range(1, m + 1):
for j in range(1, n + 1):
if p[j - 1] == '*':
dp[i][j] = dp[i][j - 1] | dp[i - 1][j]
elif p[j - 1] == '?' or s[i - 1] == p[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
return dp[m][n]
300. 最长递增子序列
- n 2 n^2 n2
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
dp = [1] * n
ans = 0
for i in range(1, n):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1) # 错在没取max
ans = max(ans, dp[i])
return max(dp)
- n log n n\log n nlogn
lower_bound
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
dp = [nums[0]]
for num in nums[1:]:
if num > dp[-1]:
dp.append(num)
else:
l, r = 0, len(dp)
while l < r:
mid = (l + r) // 2
if dp[mid] == num:
# l = mid + 1
r = mid
elif dp[mid] < num:
l = mid + 1
else:
r = mid
dp[l] = num
print(dp)
return len(dp)
class Solution:
def findNumberOfLIS(self, nums: List[int]) -> int:
n=len(nums)
length=[0]*n
count=[1]*n
for j in range(1, n):
for i in range(j):
if nums[j]>nums[i]:
if length[i]+1==length[j]:
length[j]=length[i]+1
count[j]+=count[i]
elif length[i]+1>length[j]:
length[j]=length[i]+1
count[j]=count[i]
max_len=max(length)
ans_cnt=sum([count[i] for i,l in enumerate(length) if l==max_len])
return ans_cnt
class Solution:
def increasingTriplet(self, nums: List[int]) -> bool:
first=inf
mid=inf
for num in nums:
if num<=first:
first=num
elif num<=mid:
mid=num
else:
return True
return False
def lower_bound(nums, target):
l=0
r=len(nums)
while l<r:
mid=(l+r)//2
if nums[mid]==target:
r=mid
elif nums[mid]>target:
r=mid
else:
l=mid+1
return l
class Solution:
def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
# 后面这个操作是为了处理 第一个值相等的情况
envelopes.sort(key=lambda x:(x[0], -x[1]))
nums=[envelope[1] for envelope in envelopes]
dp=[nums[0]]
for num in nums[1:]:
if num>dp[-1]:
dp.append(num)
else:
dp[lower_bound(dp, num)]=num
return len(dp)
还有个三维的题目,在【面试题】里面
24. 两两交换链表中的节点class Solution:
def swapPairs(self, head: ListNode) -> ListNode:
dummy = ListNode(0)
dummy.next = head
dp = dummy
while dp.next and dp.next.next:
p1 = dp.next
p2 = dp.next.next
dp.next = p2
p1.next = p2.next
p2.next = p1
dp = p1
return dummy.next
9. 回文数
class Solution:
def isPalindrome(self, x: int) -> bool:
return False if x<0 else str(x)==str(x)[::-1]
你能不将整数转为字符串来解决这个问题吗?
class Solution:
def isPalindrome(self, x: int) -> bool:
# 排除 10 和 负数 的特殊情况
if x < 0 or (x % 10 == 0 and x != 0):
return False
rx = 0
while x > rx:
rx = rx * 10 + x % 10
x //= 10
return rx == x or rx // 10 == x
※ 41. 缺失的第一个正数
这题的hash法我看懂了,就是没时间写注释
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
if nums[i] <= 0:
nums[i] = n + 1
for i in range(n):
num = abs(nums[i])
if num <= n:
nums[num - 1] = -abs(nums[num - 1])
for i in range(n):
if nums[i] > 0:
return i + 1
return n + 1
复用上题的hash法
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
n=len(nums)
for i in range(n):
num=abs(nums[i])
if num<=n:
nums[num-1]=-abs(nums[num-1])
ans=[]
for i in range(n):
if nums[i]>0:
ans.append(i+1)
return ans
- 数学tricky
用求和公式求出 [ 0 ⋯ n ] [0\cdots n] [0⋯n] 的和,减去数组中所有数的和,就得到了缺失数字
求和,题解用的高斯求和,有玩家认为会溢出,提出了以下方法:
public int missingNumber(int[] nums) {
int sum = 0;
for (int i = 1; i <= nums.length; i++) {
sum += i;
sum -= nums[i - 1];
}
return sum;
}
- 位运算
先和
[
0
⋯
n
]
[0\cdots n]
[0⋯n]的数都xor一遍,根据a ^ a = 0
的定理,a ^ a ^ b = b
,就找到了
class Solution:
def missingNumber(self, nums):
missing = len(nums)
for i, num in enumerate(nums):
missing ^= i ^ num
return missing
复用剑指的代码
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
nums[i] -= 1
for i in range(n):
while nums[i] != i:
if nums[i] == nums[nums[i]]:
return nums[i] + 1
t = nums[i]
nums[i] = nums[t]
nums[t] = t
135. 分发糖果
class Solution:
def candy(self, ratings: List[int]) -> int:
n = len(ratings)
left = [1] * n
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
left[i] = left[i - 1] + 1
right = 1
ans = left[n - 1]
for i in range(n - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
right += 1
else: # 错在这
right = 1
ans += max(right, left[i])
return ans
88. 合并两个有序数组
322. 零钱兑换
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
# 语义:凑出dp_i 的金额需要的硬币数
dp=[inf]*(amount+1)
dp[0]=0
for i in range(1, amount+1):
cur=inf
for coin in coins:
if i-coin>=0:
cur=min(cur, dp[i-coin])
dp[i]=cur+1
return -1 if dp[amount]==inf else dp[amount]
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp=[0]*(amount+1)
dp[0]=1
for coin in coins:
for x in range(coin, amount+1):
dp[x]+=dp[x-coin]
return dp[amount]
78. 子集
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
ans=[]
def dfs(path, begin):
ans.append(path)
for i in range(begin, n):
dfs(path+[nums[i]], i+1)
dfs([],0)
return ans
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
# 注意
nums.sort()
n=len(nums)
path = []
ans = []
def dfs(begin):
ans.append(path[:]) # 注意
for i in range(begin, n):
# 注意
if i>begin and nums[i]==nums[i-1]:
continue
path.append(nums[i])
dfs(i+1) # 注意
path.pop()
dfs(0)
return ans
14. 最长公共前缀
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
l1 = len(strs)
if l1 == 0:
return ""
l2 = min(map(len, strs))
for i in range(l2):
for j in range(1, l1):
if strs[j][i] != strs[j - 1][i]:
return strs[0][:i]
return strs[0][:l2] # 错在这
79. 单词搜索
好像在剑指刷过
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
m,n=len(board),len(board[0])
L=len(word)
def dfs(x, y, t):
if t==L:
return True
if not (0<=x<m and 0<=y<n):
return False
if board[x][y]!=word[t]:
return False
board[x][y]='-'
ans=dfs(x+1,y,t+1) or dfs(x,y+1,t+1) or dfs(x-1,y,t+1) or dfs(x,y-1,t+1)
board[x][y]=word[t]
return ans
for x in range(m):
for y in range(n):
if dfs(x, y, 0):
return True
return False
我是傻逼
TODO: 写一个更优雅的答案
94. 二叉树的中序遍历class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
stack = []
ans = []
while root or stack:
while root:
stack.append(root)
root = root.left
root = stack.pop()
ans.append(root.val)
root = root.right
return ans
112. 路径总和
这样的简单题也能卡住,一定要多思考多练习
class Solution:
def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
self.ans=False
def dfs(root, x):
if self.ans:return
if not root:
return
if root.left is None and root.right is None and x+root.val==targetSum:
self.ans=True
return
dfs(root.left, x+root.val)
dfs(root.right, x+root.val)
dfs(root, 0)
return self.ans
175. 组合两个表
select FirstName, LastName, City, State
from Person left join Address
on Person.PersonId = Address.PersonId;
543. 二叉树的直径
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
ans = 1 # 注意
def rec(node:TreeNode):
nonlocal ans
if node is None:
return 0
rl, rr = rec(node.left), rec(node.right)
ans = max(ans, 1 + rl + rr)
return max(rl, rr) + 1
rec(root)
return ans - 1 # 注意
198. 打家劫舍
乱写的
class Solution:
def rob(self, nums: List[int]) -> int:
if not nums:
return 0
n=len(nums)
dp=[0]*(n+1) # 负索引
dp[0]=nums[0]
for i in range(1,n):
dp[i]=max(dp[i-1],dp[i-2]+nums[i]) # 负索引
return max(dp)
剑指 Offer 03. 数组中重复的数字
和前面的一道题可以合起来看看
class Solution:
def findRepeatNumber(self, nums: List[int]) -> int:
n = len(nums)
for i in range(n):
# 有一个顾虑,就是如果不存在数字i会出现死循环,
# 但不用担心,如 [1, 1, 1] ,会直接返回掉
while nums[i] != i:
if nums[i] == nums[nums[i]]:
return nums[i]
# ↓ 这么干就凉了
# nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
tmp = nums[nums[i]]
nums[nums[i]] = nums[i]
nums[i] = tmp
return -1
141. 环形链表
class Solution:
def hasCycle(self, head: ListNode) -> bool:
slow=head
fast=head
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if fast==slow:
return True
return False
这题还是不熟练
class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
slow=head
fast=head
while fast and fast.next:
fast=fast.next.next
slow=slow.next
if fast==slow:
break
if fast and fast.next:
slow=head
while slow!=fast: # 判断条件瞎搞导致出错
slow=slow.next
fast=fast.next
return slow
return None
176. 第二高的薪水
select IFNULL(
(select
distinct Salary
from Employee
order by Salary DESC
limit 1 offset 1), -- offset 要放后面
null
) as SecondHighestSalary ;
101. 对称二叉树
def recur(n1, n2):
if n1 is None and n2 is None:
return True
if n1 is None or n2 is None:
return False
if n1.val!=n2.val:
return False
return recur(n1.left, n2.right) and recur(n1.right, n2.left)
class Solution:
def isSymmetric(self, root: TreeNode) -> bool:
if root is None:
return True
return recur(root.left, root.right)
160. 相交链表
class Solution:
def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
p1,p2=headA,headB
while p1!=p2:
if p1:
p1=p1.next
else:
p1=headB
if p2:
p2=p2.next
else:
p2=headA
return p1
221. 最大正方形
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
# 处理matrix使其方便访问
m,n=len(matrix),len(matrix[0])
dp=[[0]*(n+1) for _ in range(m+1)]
ans=0
for i in range(1,m+1):
for j in range(1,n+1):
if matrix[i-1][j-1]=='1':
dp[i][j]=min(dp[i-1][j] , dp[i][j-1] , dp[i-1][j-1]) + 1
ans=max(ans,dp[i][j])
return ans**2
class Solution:
def countSquares(self, matrix: List[List[int]]) -> int:
# 处理matrix使其方便访问
m,n=len(matrix),len(matrix[0])
dp=[[0]*(n+1) for _ in range(m+1)]
ans=0
for i in range(1,m+1):
for j in range(1,n+1):
if matrix[i-1][j-1]==1:
dp[i][j]=min(dp[i-1][j] , dp[i][j-1] , dp[i-1][j-1])+1
ans+=dp[i][j]
return ans
104. 二叉树的最大深度
class Solution:
def maxDepth(self, root: TreeNode) -> int:
if not root:
return 0
return max(
self.maxDepth(root.left),
self.maxDepth(root.right),
)+1
76. 最小覆盖子串
class Solution:
def minWindow(self, s: str, t: str) -> str:
need=collections.Counter(t)
window=collections.defaultdict(int)
valid=0
n=len(s)
l=0
a,b=-1,n
for r in range(n):
c=s[r]
if c in need:
window[c]+=1
if window[c]==need[c]:
valid+=1
# 注意 l<=r 的判断条件
while l<=r and valid==len(need): # 条件写错
if r-l<b-a:
a,b=l,r
c=s[l]
if c in need:
if window[c]==need[c]:
valid-=1
window[c]-=1
l+=1
return "" if a==-1 else s[a:b+1]
236. 二叉树的最近公共祖先
因为判断条件顺序
的bug导致卡了很久
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root is None:
return None
if root==p or root==q:
return root
left=self.lowestCommonAncestor(root.left,p,q)
right=self.lowestCommonAncestor(root.right,p,q)
# bug: 判断条件的顺序
if left and right:
return root
if left or right:
return left if left else right
return None
347. 前 K 个高频元素
class Solution:
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
counter = collections.Counter(nums)
heap=[]
for num,cnt in counter.items():
heapq.heappush(heap, (cnt, num))
if len(heap)>k:
heapq.heappop(heap)
return [num for _,num in heap]
8. 字符串转换整数 (atoi)
- DFA
- if-else
- long
- int, × 10 \times10 ×10前判断
- Python, 正则表达式
写得头痛。。
int my_atoi(string &s) {
int num = 0;
int i = 0;
int len = s.size();
int flag = 1;
while (s[i] == ' ') {
i++;
}
if (s[i] == '+') {
i++;
} else if (s[i] == '-') {
flag = -1;
i++;
}
while (i < len) {
if (s[i] < '0' || s[i] > '9') break;
int cur = (int) (s[i] - '0');
if (num > INT_MAX / 10) { return flag == 1 ? INT_MAX : INT_MIN; }
if (num == INT_MAX / 10) {
if (flag == 1 && cur >= 7) return INT_MAX;
if (flag == -1 && cur >= 8) return INT_MIN;
}
num = num * 10 + cur;
i++;
}
return flag * num;
}
class Solution {
public:
int myAtoi(string s) {
return my_atoi(s);
}
};
122. 买卖股票的最佳时机 II
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n=len(prices)
ans=0
for i in range(1, n):
ans+=max(0, prices[i]-prices[i-1])
return ans
45. 跳跃游戏 II
要求能背这两题
- 基本题是贪心
class Solution:
def canJump(self, nums: List[int]) -> bool:
k=0
n=len(nums)
for i in range(n):
if i>k:
return False
k=max(k, i+nums[i])
return True
- 升级题还是贪心。。
def jump(nums):
ans = 0
start = 0
end = 1
n = len(nums)
while end < n:
max_pos = 0
for i in range(start, end):
max_pos = max(max_pos, i + nums[i])
start = end
end = max_pos + 1
ans += 1
return ans
98. 验证二叉搜索树
class Solution:
def isValidBST(self, root: TreeNode, min_node=None, max_node=None) -> bool:
if root is None:
return True
if min_node and min_node.val>=root.val: return False
if max_node and max_node.val<=root.val: return False
return self.isValidBST(root.left, min_node, root) and self.isValidBST(root.right, root, max_node)
128. 最长连续序列
class Solution:
def longestConsecutive(self, nums: List[int]) -> int:
num_set=set(nums)
max_len=0
for num in num_set:
if num-1 not in num_set:
cur_len=1
cur_num=num+1
while cur_num in num_set:
cur_len+=1
cur_num+=1
max_len=max(max_len, cur_len)
return max_len
84. 柱状图中最大的矩形
注意是个单调递增栈
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
stack=[]
n=len(heights)
left=[-1]*n
right=[n]*n
for i, height in enumerate(heights):
# 单调性写错
# 单调递增
while stack and heights[i]<heights[stack[-1]]:
top=stack.pop()
right[top]=i
if stack:
left[i]=stack[-1]
stack.append(i)
ans=0
for i in range(n):
l=left[i]
r=right[i]
ans=max(ans, (r-l-1)*heights[i])
return ans
64. 最小路径和
刚开始用回溯写的,我是傻逼
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m,n=len(grid),len(grid[0])
visit=[[0]*n for _ in range(m)]
# path=[]
ans=inf
def dfs(i,j,cur_sum):
nonlocal ans
if i==m-1 and j==n-1:
# sum_=sum(path)
ans=min(ans, cur_sum)
for dx,dy in [[1,0],[-1,0],[0,1],[0,-1]]:
x,y=i+dx,j+dy
# val=
if (0<=x<m and 0<=y<n) and visit[x][y]==0 and cur_sum+grid[x][y]<ans:
visit[x][y]=1
# path.append(val))
dfs(x,y,cur_sum+grid[x][y])
# path.pop()
visit[x][y]=0
visit[0][0]=1
# path.append(grid[0][0])
dfs(0,0, grid[0][0])
return ans
做的我有点想吐了。。
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
m, n = len(grid), len(grid[0])
dp = [[0] * (n) for _ in range(m)]
dp[0][0] = grid[0][0]
for i in range(1, m):
dp[i][0] = dp[i - 1][0] + grid[i][0]
for i in range(1, n):
dp[0][i] = dp[0][i - 1] + grid[0][i]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[m - 1][n - 1]
445. 两数相加 II
def reverse(p):
pre=None
while p:
aft=p.next
p.next=pre
pre=p
p=aft
return pre
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
l1=reverse(l1)
l2=reverse(l2)
dummy=ListNode(0)
dp=dummy
carry=0
while l1 or l2:
v1=l1.val if l1 else 0
v2=l2.val if l2 else 0
l1=l1.next if l1 else None
l2=l2.next if l2 else None
v=v1+v2+carry
carry=v//10
dp.next=ListNode(v%10)
dp=dp.next
if carry:
dp.next=ListNode(carry)
return reverse(dummy.next)
26. 删除排序数组中的重复项
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow=fast=0
n=len(nums)
while fast<n:
if nums[slow]!=nums[fast]:
slow+=1
nums[slow]=nums[fast]
fast+=1
return slow+1
6. Z 字形变换
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows==1:
return s
rows=[""]*numRows
i=0
flag=-1
for c in s:
rows[i]+=c
if i in (0, numRows-1):
flag*=-1
i+=flag
return "".join(rows)
96. 不同的二叉搜索树
def C(a, b):
ans = 1
for _ in range(b):
ans *= a
a -= 1
while b >= 1:
ans //= b
b -= 1
return ans
def catalan(n):
return C(2*n, n) // (n + 1)
class Solution:
def numTrees(self, n: int) -> int:
return (catalan(n))
739. 每日温度
class Solution:
def dailyTemperatures(self, T: List[int]) -> List[int]:
stack=[]
# 从右到左,第一个更小的
n=len(T)
right=[0]*n
for i in range(n-1, -1, -1):
# 单调递减
while stack and T[stack[-1]]<=T[i]: # 要求更高的气温
stack.pop()
# 这里卡住了
right[i]=stack[-1]-i if stack else 0
stack.append(i)
return right
240. 搜索二维矩阵 II
从右上角开始遍历,时间复杂度 O ( N + M ) O(N+M) O(N+M)
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m,n=len(matrix),len(matrix[0])
i,j=0,n-1
while True:
if not (0<=i<m and 0<=j<n):
return False
val=matrix[i][j]
if val<target:
i+=1
elif val>target:
j-=1
else:
return True
def binary_search(nums, target):
l,r=0,len(nums)-1
while l<=r:
mid=(l+r)//2
if nums[mid]==target:
return True
elif nums[mid]>target:
r=mid-1
else:
l=mid+1
return False
def upper_bound(nums, target):
l,r=0,len(nums)
while l<r:
mid=(l+r)//2
if nums[mid]==target:
l=mid+1
elif nums[mid]>target:
r=mid
else:
l=mid+1
return l
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m, n=len(matrix), len(matrix[0])
first_line=[matrix[0][i] for i in range(n)]
ub=upper_bound(first_line, target)
for col_ix in range(ub):
col=[matrix[i][col_ix] for i in range(m)]
if binary_search(col, target):
return True
return False
51. N 皇后
主要涉及:
- 列
col
- 主对角线
dj1
- 斜对角线
dj2
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
board=[["."]*n for _ in range(n)]
dj1=[0]*(2*n)
dj2=[0]*(2*n)
col=[0]*n
ans=[]
# j 表示行
def dfs(j):
# i 表示列
if j==n:
ans.append(["".join(line) for line in board])
for i in range(n):
ix1=i+j
ix2=i-j+n
if col[i]==1 or dj1[ix1]==1 or dj2[ix2]==1:
continue
col[i]=1;dj1[ix1]=1;dj2[ix2]=1
board[i][j]='Q'
dfs(j+1)
board[i][j]='.'
col[i]=0;dj1[ix1]=0;dj2[ix2]=0
dfs(0)
return ans
39. 组合总和
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
ans=[]
n=len(candidates)
if not n:
return []
def dfs(begin ,path, target):
if target==0:
ans.append(path)
return
for i in range(begin, n):
if target-candidates[i]<0:
break
dfs(i, path+[candidates[i]], target-candidates[i])
candidates.sort()
dfs(0, [],target)
return ans
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort()
n = len(candidates)
ans = []
def dfs(begin, target, path):
if target == 0:
ans.append(path)
return
for i in range(begin, n):
if target - candidates[i] < 0:
break
if i > begin and candidates[i] == candidates[i - 1]:
continue
# 不是begin而是i
dfs(i + 1, target - candidates[i], path + [candidates[i]])
dfs(0, target, [])
return ans
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
n=len(nums)
ans=[]
def dfs(path, begin):
ans.append(path)
for i in range(begin, n):
dfs(path+[nums[i]], i+1)
dfs([],0)
return ans
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
# 注意
nums.sort()
n=len(nums)
path = []
ans = []
def dfs(begin):
ans.append(path[:]) # 注意
for i in range(begin, n):
# 注意
if i>begin and nums[i]==nums[i-1]:
continue
path.append(nums[i])
dfs(i+1) # 注意
path.pop()
dfs(0)
return ans
85. 最大矩形
def max_hist_rect(heights):
n=len(heights)
left=[0]*n
right=[n]*n
stack=[]
for i in range(n):
# 单调递增栈 栈顶比当前元素大
while stack and heights[stack[-1]]>heights[i]:
top = stack.pop()
right[top]=i
left[i]=stack[-1] if stack else -1
stack.append(i)
ans=0
for i in range(n):
ans=max(ans, heights[i]*(right[i]-left[i]-1))
return ans
class Solution:
def maximalRectangle(self, matrix: List[List[str]]) -> int:
if len(matrix)==0:
return 0
m, n = len(matrix), len(matrix[0])
hist=[0]*n
ans=0
for i in range(m):
for j in range(n):
if matrix[i][j]=='0':
hist[j]=0
else:
hist[j]+=1
ans=max(ans, max_hist_rect(hist))
return ans
82. 删除排序链表中的重复元素 II
背下来
class Solution:
def deleteDuplicates(self, head: ListNode) -> ListNode:
# 节点数 <= 1 直接返回
if not (head and head.next):
return head
dummy=ListNode(-inf)
dummy.next=head
a=dummy
b=head
while b and b.next:
if a.next.val != b.next.val:
a=a.next
b=b.next
else:
while b and b.next and a.next.val == b.next.val:
b=b.next
a.next=b.next
b=b.next
return dummy.next
38. 外观数列
class Solution:
def countAndSay(self, n: int) -> str:
pre = ''
cur = '1'
for _ in range(1, n):
pre = cur
cur = ''
start = 0
end = 0
while end < len(pre):
while end<len(pre) and pre[start] == pre[end]:
end += 1
cur += str(end - start) + pre[start]
start = end
return cur
120. 三角形最小路径和
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
n=len(triangle)
dp=copy.deepcopy(triangle)
for i in range(1, n):
for j in range(i+1):
min_=inf
for k in range(max(0, j-1),min(i,j+1)):
min_=min(min_, dp[i-1][k])
min_+=triangle[i][j]
dp[i][j]=min_
return min(dp[n-1])
力扣找不到的字节面试题——36进制加法
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
l1 = len(num1)
l2 = len(num2)
i1 = l1 - 1
i2 = l2 - 1
carry = 0
base = 10
ans = ''
while i1 >= 0 or i2 >= 0 or carry:
a = int(num1[i1]) if i1 >= 0 else 0
b = int(num2[i2]) if i2 >= 0 else 0
sum_ = a + b + carry
ans = ans + str(sum_ % base)
carry = sum_ // base
i1 -= 1
i2 -= 1
return ans[::-1]
O ( ( n + m ) × n ) O((n+m)\times n) O((n+m)×n)
class Solution:
def multiply(self, num1: str, num2: str) -> str:
if num1 == "0" or num2 == "0":
return "0"
ans = "0"
m, n = len(num1), len(num2)
for i in range(n - 1, -1, -1):
add = 0
y = int(num2[i])
curr = ["0"] * (n - i - 1)
for j in range(m - 1, -1, -1):
product = int(num1[j]) * y + add
curr.append(str(product % 10))
add = product // 10
if add > 0:
curr.append(str(add))
curr = "".join(curr[::-1])
ans = self.addStrings(ans, curr)
return ans
def addStrings(self, num1: str, num2: str) -> str:
i, j = len(num1) - 1, len(num2) - 1
add = 0
ans = list()
while i >= 0 or j >= 0 or add != 0:
x = int(num1[i]) if i >= 0 else 0
y = int(num2[j]) if j >= 0 else 0
result = x + y + add
ans.append(str(result % 10))
add = result // 10
i -= 1
j -= 1
return "".join(ans[::-1])
头条笔试题 - 折木棍
TODO: 用合理的思路再重刷一遍
int breakNum(vector<int>& nums) {
int ans = 0;
for (int i = nums.size() - 2; i >= 0; i--) {
if (nums[i + 1] >= nums[i]) continue;
int t = (nums[i] - 1) / nums[i + 1];
ans += t;
nums[i] /= (t + 1);
}
return ans;
}
基本思路大概知道,两个比较难理解的点:
int t = (nums[i] - 1) / nums[i + 1];
nums[i] /= (t + 1);
第一个,t
表示折出多少段。如 12 6, 可以折 6 , 6,t=1
但是 12/6=2,所以- 1
第二个,折出t段,加上自己,一共t+1,故除 (t + 1)
逻辑题100个人从1到100,每个回合去掉奇数序号的人,剩下的人向前排列,最后一回合剩下的人是第几个序号
双向链表的反转 补充题字节跳动高频题——排序奇升偶降链表
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def sortOddEvenList(self,head):
if not head or not head.next:
return head
oddList,evenList = self.partition(head)
evenList = self.reverse(evenList)
return self.merge(oddList,evenList)
def partition(self, head: ListNode) -> ListNode:
evenHead = head.next
odd, even = head, evenHead
while even and even.next:
odd.next = even.next
odd = odd.next
even.next = odd.next
even = even.next
odd.next = None
return [head,evenHead]
def reverse(self,head):
dumpy = ListNode(-1)
p = head
while p:
temp = p.next
p.next = dumpy.next
dumpy.next = p
p = temp
return dumpy.next
def merge(self,p,q):
head = ListNode(-1)
r = head
while p and q:
if p.val <= q.val:
r.next = p
p = p.next
else:
r.next = q
q = q.next
r = r.next
if p:
r.next = p
if q:
r.next = q
return head.next
感觉交替切分链表不太熟,练了下
def partition(head: ListNode):
if not head or not head.next:
return head
p1, p2 = head, head.next
head1, head2 = p1, p2
# p2 后面至少有1个结点
while p2 and p2.next:
p1.next = p2.next
p1 = p1.next
p2.next = p1.next
p2 = p2.next
p1.next = None
return head1, head2
字节跳动高频题——圆环回原点问题
设dp[i][j]
为从0点出发走i步到j点的方案数
class Solution:
def backToOrigin(self,n):
#点的个数为10
length = 10
dp = [[0 for i in range(length)] for j in range(n+1)]
dp[0][0] = 1
for i in range(1,n+1):
for j in range(length):
#dp[i][j]表示从0出发,走i步到j的方案数
dp[i][j] = dp[i-1][(j-1+length)%length] + dp[i-1][(j+1)%length]
return dp[n][0]
面试字节前必看的高频题——木头切割问题
#include <iostream>
using namespace std;
const int N = 100010;
int a[N];
int n, k;
int check(int mid)
{
int res = 0;
for (int i = 0; i < n; i ++ ) res += a[i] / mid;
return res;
}
int main()
{
cin >> n >> k;
int l = 1, r = -1;
for (int i = 0; i < n; i ++ )
{
cin >> a[i];
r = max(r, a[i]);
}
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid) >= k) l = mid;
else r = mid - 1;
}
cout << l << endl;
return 0;
}
# 数组长度为【n】,切出至少【k】个相同长度为【m】的木块,已知【k】,求【m】
def check(nums, n, mid):
res = 0
for i in range(n):
res += nums[i] // mid
return res
def cut_wood(nums, k):
n = len(nums)
l, r = 1, max(nums)
while l < r:
cur_m = (l + r) // 2
# m越大,k越小
cur_k = check(nums, n, cur_m)
if cur_k == k:
r = cur_m
elif cur_k < k:
r = cur_m
else:
l = cur_m + 1
return l
m = cut_wood([4, 7, 2, 10, 5], 5)
print(m)
求区间最小数乘区间和的最大值
五分钟掌握:二叉树的下一个节点
- 二叉树为空,则返回空;
- 节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
- 节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;否则继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{
if(pNode==NULL)
return NULL;
if(pNode->right!=NULL)
{
pNode=pNode->right;
while(pNode->left!=NULL)
pNode=pNode->left;
return pNode;
}
while(pNode->next!=NULL)
{
TreeLinkNode *proot=pNode->next;
if(proot->left==pNode)
return proot;
pNode=pNode->next;
}
return NULL;
}
};