Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
lfmiao0
Fucking Algorithm
提交
8bbbebd1
F
Fucking Algorithm
项目概览
lfmiao0
/
Fucking Algorithm
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
F
Fucking Algorithm
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
8bbbebd1
编写于
3月 13, 2021
作者:
B
BruceCat
提交者:
GitHub
3月 13, 2021
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'master' into BST_delete_node
上级
4e00179e
b4e436fb
变更
31
隐藏空白更改
内联
并排
Showing
31 changed file
with
1474 addition
and
79 deletion
+1474
-79
README.md
README.md
+21
-8
出版推广1.jpeg
出版推广1.jpeg
+0
-0
动态规划系列/动态规划之KMP字符匹配算法.md
动态规划系列/动态规划之KMP字符匹配算法.md
+37
-2
动态规划系列/动态规划之博弈问题.md
动态规划系列/动态规划之博弈问题.md
+114
-3
动态规划系列/动态规划设计:最长递增子序列.md
动态规划系列/动态规划设计:最长递增子序列.md
+93
-7
动态规划系列/动态规划详解进阶.md
动态规划系列/动态规划详解进阶.md
+46
-3
动态规划系列/团灭股票问题.md
动态规划系列/团灭股票问题.md
+2
-2
动态规划系列/抢房子.md
动态规划系列/抢房子.md
+63
-2
动态规划系列/最优子结构.md
动态规划系列/最优子结构.md
+1
-1
动态规划系列/最长公共子序列.md
动态规划系列/最长公共子序列.md
+62
-3
动态规划系列/编辑距离.md
动态规划系列/编辑距离.md
+29
-2
动态规划系列/贪心算法之区间调度问题.md
动态规划系列/贪心算法之区间调度问题.md
+42
-3
动态规划系列/高楼扔鸡蛋问题.md
动态规划系列/高楼扔鸡蛋问题.md
+1
-1
数据结构系列/二叉搜索树操作集锦.md
数据结构系列/二叉搜索树操作集锦.md
+89
-4
数据结构系列/单调栈.md
数据结构系列/单调栈.md
+26
-5
数据结构系列/单调队列.md
数据结构系列/单调队列.md
+102
-2
数据结构系列/设计Twitter.md
数据结构系列/设计Twitter.md
+119
-2
数据结构系列/递归反转链表的一部分.md
数据结构系列/递归反转链表的一部分.md
+37
-2
算法思维系列/二分查找详解.md
算法思维系列/二分查找详解.md
+2
-2
算法思维系列/双指针技巧.md
算法思维系列/双指针技巧.md
+28
-3
算法思维系列/字符串乘法.md
算法思维系列/字符串乘法.md
+45
-2
算法思维系列/滑动窗口技巧.md
算法思维系列/滑动窗口技巧.md
+2
-2
高频面试系列/LRU算法.md
高频面试系列/LRU算法.md
+118
-2
高频面试系列/koko偷香蕉.md
高频面试系列/koko偷香蕉.md
+42
-2
高频面试系列/二分查找判定子序列.md
高频面试系列/二分查找判定子序列.md
+64
-2
高频面试系列/判断回文链表.md
高频面试系列/判断回文链表.md
+32
-2
高频面试系列/合法括号判定.md
高频面试系列/合法括号判定.md
+48
-2
高频面试系列/打印素数.md
高频面试系列/打印素数.md
+38
-2
高频面试系列/接雨水.md
高频面试系列/接雨水.md
+97
-2
高频面试系列/消失的元素.md
高频面试系列/消失的元素.md
+47
-2
高频面试系列/缺失和重复的元素.md
高频面试系列/缺失和重复的元素.md
+27
-2
未找到文件。
README.md
浏览文件 @
8bbbebd1
...
...
@@ -3,7 +3,7 @@ English version repo and Gitbook is on [english branch](https://github.com/labul
# labuladong 的算法小抄
<p
align=
'center'
>
<a
href=
"https://labuladong.git
book.io/algo"
target=
"_blank"
><img
alt=
"Website"
src=
"https://img.shields.io/website?label=%E5%9C%A8%E7%BA%BF%E7%94%B5%E5%AD%90%E4%B9%A6&style=flat-square&down_color=blue&down_message=%E7%82%B9%E8%BF%99%E9%87%8C&up_color=blue&up_message=%E7%82%B9%E8%BF%99%E9%87%8C&url=https%3A%2F%2Flabuladong.gitbook
.io%2Falgo&logo=Gitea"
></a>
<a
href=
"https://labuladong.git
ee.io/algo"
target=
"_blank"
><img
alt=
"Website"
src=
"https://img.shields.io/website?label=%E5%9C%A8%E7%BA%BF%E7%94%B5%E5%AD%90%E4%B9%A6&style=flat-square&down_color=blue&down_message=%E7%82%B9%E8%BF%99%E9%87%8C&up_color=blue&up_message=%E7%82%B9%E8%BF%99%E9%87%8C&url=https%3A%2F%2Flabuladong.gitee
.io%2Falgo&logo=Gitea"
></a>
<a
href=
"https://github.com/labuladong/fucking-algorithm"
target=
"_blank"
><img
alt=
"GitHub"
src=
"https://img.shields.io/github/stars/labuladong/fucking-algorithm?label=Stars&style=flat-square&logo=GitHub"
></a>
</p>
...
...
@@ -14,6 +14,10 @@ English version repo and Gitbook is on [english branch](https://github.com/labul
<a href="https://space.bilibili.com/14089380" target="_blank"><img src="https://img.shields.io/badge/B站-@labuladong-000000.svg?style=flat-square&logo=Bilibili"></a>
</p>
![](
pictures/souyisou.png
)
好消息,《labuladong 的算法小抄》纸质书出版啦!关注公众号查看详情👆
<p
align=
'center'
>
<img
src=
"https://gitee.com/labuladong/pictures/raw/master/starHistory.png"
width =
"600"
/>
</p>
...
...
@@ -25,27 +29,36 @@ English version repo and Gitbook is on [english branch](https://github.com/labul
只想要答案的话很容易,题目评论区五花八门的答案,动不动就秀 python 一行代码解决,有那么多人点赞。问题是,你去做算法题,是去学习编程语言的奇技淫巧的,还是学习算法思维的呢?你的快乐,到底源自复制别人的一行代码通过测试,已完成题目 +1,还是源自自己通过逻辑推理和算法框架不看答案写出解法?
网上总有大佬喷我,说我写这玩意太基础了,根本没必要啰嗦。我只能说大家刷算法就是找工作吃饭的,不是打竞赛的,我也是一路摸爬滚打过来的,我们要的是清楚明白有所得,不是故弄玄虚无所指。不想办法做到通俗易懂,难道要上来先把《算法导论》吹上天,然后把人家都心怀敬仰地劝退?
网上总有大佬喷我,说我写的东西太基础,要么说不能借助框架思维来学习算法。我只能说大家刷算法就是找工作吃饭的,不是打竞赛的,我也是一路摸爬滚打过来的,我们要的是清楚明白有所得,不是故弄玄虚无所指。
不想办法做到通俗易懂,难道要上来先把《算法导论》吹上天,然后把人家都心怀敬仰地劝退?
**做啥事情做多了,都能发现套路的,我把各种算法套路框架总结出来,相信可以帮助其他人少走弯路**
。我这个纯靠自学的小童鞋,花了一年时间刷题和总结,自己写了一份算法小抄,后面有目录,这里就不废话了。
### 使用方法
1、
**先给本仓库点个 star,满足一下我的虚荣心**
,文章质量绝对值你一个 star。我还在继续创作,给我一点继续写文的动力,感谢。
**1、先给本仓库点个 star,满足一下我的虚荣心**
,文章质量绝对值你一个 star。我还在继续创作,给我一点继续写文的动力,感谢。
**2、建议收藏我的在线网站,每篇文章开头都有对应的力扣题目链接,可以边看文章边刷题**
:
Gitbook 地址:https://labuladong.gitbook.io/algo
GitBook 在国内访问速度很慢,且常被攻击,我特意部署了两个镜像站点,大家可根据网络情况自行选择:
GitHub Pages 地址:https://labuladong.github.io/algo
2、
**建议收藏我的 Gitbook 网站,每篇文章开头都有对应的力扣题目链接,可以边看文章边刷题**
:
Gitee Pages 地址:https://labuladong.gitee.io/algo
Gitbook 地址:https://labuladong.gitbook.io/algo/
3、建议关注我的公众号
**labuladong**
,坚持高质量原创,说是最良心最硬核的技术公众号都不为过。本仓库的文章就是从公众号里整理出来的
**一部分**
内容,公众号后台回复关键词【电子书】可以获得这份小抄的完整版本;回复
【加群】可以加入我们的刷题群,和大家一起讨论算法问题,分享内推机会:
**3、建议关注我的公众号 labuladong,坚持高质量原创,说是最良心最硬核的技术公众号都不为过**
。本仓库的文章就是从公众号里整理出来的
**一部分**
内容,公众号可以查看更多内容;公众号后台回复关键词
【加群】可以加入我们的刷题群,和大家一起讨论算法问题,分享内推机会:
<p
align=
'center'
>
<img
src=
"https://gitee.com/labuladong/pictures/raw/master/qrcode.jpg"
width =
"200"
/>
</p>
4、欢迎关注
[
我的知乎
](
https://www.zhihu.com/people/labuladong
)
。
**4、欢迎关注 [我的知乎](https://www.zhihu.com/people/labuladong)**
。
我一直在写优质文章,但是后续的文章只发布到公众号/
gitbook
/知乎,不能开放到 GitHub。因为本仓库太火了,很多人直接拿我的文章去开付费专栏,价格还不便宜,我这免费写给您看,何必掏冤枉钱呢?所以多多关注本作者,多多宣传,谁也不希望劣币驱逐良币不是么?
我一直在写优质文章,但是后续的文章只发布到公众号/
网站
/知乎,不能开放到 GitHub。因为本仓库太火了,很多人直接拿我的文章去开付费专栏,价格还不便宜,我这免费写给您看,何必掏冤枉钱呢?所以多多关注本作者,多多宣传,谁也不希望劣币驱逐良币不是么?
其他的先不多说了,直接上干货吧,我们一起搞定 LeetCode,感受一下支配算法的乐趣。
...
...
出版推广1.jpeg
0 → 100644
浏览文件 @
8bbbebd1
120.0 KB
动态规划系列/动态规划之KMP字符匹配算法.md
浏览文件 @
8bbbebd1
...
...
@@ -431,4 +431,40 @@ KMP 算法也就是动态规划那点事,我们的公众号文章目录有一
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
MoguCloud
](
https://github.com/MoguCloud
)
提供 实现 strStr() 的 Python 完整代码:
```
py
class
Solution
:
def
strStr
(
self
,
haystack
:
str
,
needle
:
str
)
->
int
:
# 边界条件判断
if
not
needle
:
return
0
pat
=
needle
txt
=
haystack
M
=
len
(
pat
)
# dp[状态][字符] = 下个状态
dp
=
[[
0
for
_
in
range
(
256
)]
for
_
in
pat
]
# base case
dp
[
0
][
ord
(
pat
[
0
])]
=
1
# 影子状态 X 初始化为 0
X
=
0
for
j
in
range
(
1
,
M
):
for
c
in
range
(
256
):
dp
[
j
][
c
]
=
dp
[
X
][
c
]
dp
[
j
][
ord
(
pat
[
j
])]
=
j
+
1
# 更新影子状态
X
=
dp
[
X
][
ord
(
pat
[
j
])]
N
=
len
(
txt
)
# pat 初始状态为 0
j
=
0
for
i
in
range
(
N
):
# 计算 pat 的下一个状态
j
=
dp
[
j
][
ord
(
txt
[
i
])]
# 到达终止态,返回结果
if
j
==
M
:
return
i
-
M
+
1
# 没到达终止态,匹配失败
return
-
1
```
动态规划系列/动态规划之博弈问题.md
浏览文件 @
8bbbebd1
...
...
@@ -22,7 +22,7 @@
上一篇文章
[
几道智力题
](
https://labuladong.gitbook.io/algo
)
中讨论到一个有趣的「石头游戏」,通过题目的限制条件,这个游戏是先手必胜的。但是智力题终究是智力题,真正的算法问题肯定不会是投机取巧能搞定的。所以,本文就借石头游戏来讲讲「假设两个人都足够聪明,最后谁会获胜」这一类问题该如何用动态规划算法解决。
博弈类问题的套路都差不多,下文
举例
讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。
博弈类问题的套路都差不多,下文
参考
[
这个 YouTube 视频
](
https://www.youtube.com/watch?v=WxpIHvsu1RI
)
的思路
讲解,其核心思路是在二维 dp 的基础上使用元组分别存储两个人的博弈结果。掌握了这个技巧以后,别人再问你什么俩海盗分宝石,俩人拿硬币的问题,你就告诉别人:我懒得想,直接给你写个算法算一下得了。
我们「石头游戏」改的更具有一般性:
...
...
@@ -215,4 +215,116 @@ int stoneGame(int[] piles) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
*
python3版本
由
[
SCUHZS
](
https://github.com/brucecat
)
提供
这里采取的是三维的做法
```
python
class
Solution
:
def
stoneGame
(
self
,
piles
:
List
[
int
])
->
bool
:
n
=
len
(
piles
)
# 初始化一个n*n的矩阵 dp数组
dp
=
[[
None
]
*
n
for
i
in
range
(
0
,
n
)]
# 在三角区域填充
for
i
in
range
(
n
):
for
j
in
range
(
i
,
n
):
dp
[
i
][
j
]
=
[
0
,
0
]
# 填入base case
for
i
in
range
(
0
,
n
):
dp
[
i
][
i
][
0
]
=
piles
[
i
]
dp
[
i
][
i
][
1
]
=
0
# 斜着遍历数组
for
l
in
range
(
2
,
n
+
1
):
for
i
in
range
(
0
,
n
-
l
+
1
):
j
=
l
+
i
-
1
# 先手选择最左边或最右边的分数
left
=
piles
[
i
]
+
dp
[
i
+
1
][
j
][
1
]
right
=
piles
[
j
]
+
dp
[
i
][
j
-
1
][
1
]
# 套用状态转移方程
if
left
>
right
:
dp
[
i
][
j
][
0
]
=
left
dp
[
i
][
j
][
1
]
=
dp
[
i
+
1
][
j
][
0
]
else
:
dp
[
i
][
j
][
0
]
=
right
dp
[
i
][
j
][
1
]
=
dp
[
i
][
j
-
1
][
0
]
res
=
dp
[
0
][
n
-
1
]
return
res
[
0
]
-
res
[
1
]
>
0
```
压缩成一维数组,以减小空间复杂度,做法如下。
```
python
class
Solution
:
def
stoneGame
(
self
,
piles
:
List
[
int
])
->
bool
:
dp
=
piles
.
copy
()
for
i
in
range
(
len
(
piles
)
-
1
,
-
1
,
-
1
):
# 从下往上遍历
for
j
in
range
(
i
,
len
(
piles
)):
# 从前往后遍历
dp
[
j
]
=
max
(
piles
[
i
]
-
dp
[
j
],
piles
[
j
]
-
dp
[
j
-
1
])
# 计算之后覆盖一维数组的对应位置
return
dp
[
len
(
piles
)
-
1
]
>
0
```
*
C++ 版本
由
[
TCeason
](
https://github.com/TCeason
)
提供
这里采用 hash map 来解决问题
```
cpp
class
Solution
{
public:
unordered_map
<
int
,
int
>
memo
;
int
dfs
(
vector
<
int
>
&
piles
,
int
index
)
{
// 从两边向中间获取
// index 值为 1/2 piles.size() 时可以停止算法
if
(
index
==
piles
.
size
()
/
2
)
return
0
;
// 减少计算,快速返回已有结果
if
(
memo
.
count
(
index
))
return
memo
[
index
];
// 防止第一次取最右时越界
int
n
=
piles
.
size
()
-
1
;
// 先手选择最左边或最右边后的分数
int
l
=
piles
[
index
]
+
dfs
(
piles
,
index
+
1
);
int
r
=
piles
[
n
-
index
]
+
dfs
(
piles
,
index
+
1
);
// 返回先手左或右边的最高分
return
memo
[
index
]
=
max
(
l
,
r
);
}
bool
stoneGame
(
vector
<
int
>&
piles
)
{
// 最佳发挥时:
// 先手得分 * 2 > 总大小 则先手者胜利
return
dfs
(
piles
,
0
)
*
2
>
accumulate
(
begin
(
piles
),
end
(
piles
),
0
);
}
};
```
动态规划系列/动态规划设计:最长递增子序列.md
浏览文件 @
8bbbebd1
...
...
@@ -10,8 +10,8 @@
![](
../pictures/souyisou.png
)
相关推荐:
*
[
动态规划设计:最大子数组
](
../动态规划系列/最大子数组.md
)
*
[
一文学会递归解题
](
../投稿/一文学会递归解题.md
)
*
[
动态规划设计:最大子数组
](
https://labuladong.gitbook.io/algo
)
*
[
一文学会递归解题
](
https://labuladong.gitbook.io/algo
)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
...
...
@@ -19,7 +19,7 @@
**-----------**
也许有读者看了前文
[
动态规划详解
](
../动态规划系列/动态规划详解进阶.md
)
,学会了动态规划的套路:找到了问题的「状态」,明确了
`dp`
数组/函数的含义,定义了 base case;但是不知道如何确定「选择」,也就是不到状态转移的关系,依然写不出动态规划解法,怎么办?
也许有读者看了前文
[
动态规划详解
](
https://labuladong.gitbook.io/algo
)
,学会了动态规划的套路:找到了问题的「状态」,明确了
`dp`
数组/函数的含义,定义了 base case;但是不知道如何确定「选择」,也就是不到状态转移的关系,依然写不出动态规划解法,怎么办?
不要担心,动态规划的难点本来就在于寻找正确的状态转移方程,本文就借助经典的「最长递增子序列问题」来讲一讲设计动态规划的通用技巧:
**数学归纳思想**
。
...
...
@@ -43,7 +43,7 @@
**我们的定义是这样的:`dp[i]` 表示以 `nums[i]` 这个数结尾的最长递增子序列的长度。**
PS:为什么这样定义呢?这是解决子序列问题的一个套路,后文
[
动态规划之子序列问题解题模板
](
../动态规划系列/子序列问题模板.md
)
总结了几种常见套路。你读完本章所有的动态规划问题,就会发现
`dp`
数组的定义方法也就那几种。
PS:为什么这样定义呢?这是解决子序列问题的一个套路,后文
[
动态规划之子序列问题解题模板
](
https://labuladong.gitbook.io/algo
)
总结了几种常见套路。你读完本章所有的动态规划问题,就会发现
`dp`
数组的定义方法也就那几种。
根据这个定义,我们就可以推出 base case:
`dp[i]`
初始值为 1,因为以
`nums[i]`
结尾的最长递增子序列起码要包含它自己。
...
...
@@ -164,7 +164,7 @@ public int lengthOfLIS(int[] nums) {
我们只要把处理扑克牌的过程编程写出来即可。每次处理一张扑克牌不是要找一个合适的牌堆顶来放吗,牌堆顶的牌不是
**有序**
吗,这就能用到二分查找了:用二分查找来搜索当前牌应放置的位置。
PS:旧文
[
二分查找算法详解
](
../算法思维系列/二分查找详解.md
)
详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。
PS:旧文
[
二分查找算法详解
](
https://labuladong.gitbook.io/algo
)
详细介绍了二分查找的细节及变体,这里就完美应用上了,如果没读过强烈建议阅读。
```
java
public
int
lengthOfLIS
(
int
[]
nums
)
{
...
...
@@ -215,4 +215,91 @@ public int lengthOfLIS(int[] nums) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
```
python 动态规划
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
f = [1] * (n)
for i in range(n):
for j in range(i):
if nums[j] < nums[i]:
f[i] = max(f[i], f[j] + 1)
res = 0
for i in range(n):
res = max(res, f[i])
return res
```
```
python 二分查找
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
stack = []
def find_index(num):
l, r = 0, len(stack)
while l < r:
mid = l + r >> 1
if stack[mid] >= num:
r = mid
else:
l = mid + 1
return r
for num in nums:
if not stack or num > stack[-1]:
stack.append(num)
else:
position = find_index(num)
stack[position] = num
return len(stack)
```
[
Kian
](
https://github.com/KianKw/
)
提供 C++ 代码
```
c++
class
Solution
{
public:
int
lengthOfLIS
(
vector
<
int
>&
nums
)
{
/* len 为牌的数量 */
int
len
=
nums
.
size
();
vector
<
int
>
top
(
len
,
0
);
/* 牌堆数初始化为0 */
int
piles
=
0
;
for
(
int
i
=
0
;
i
<
len
;
i
++
)
{
/* nums[i] 为要处理的扑克牌 */
int
poker
=
nums
[
i
];
/***** 搜索左侧边界的二分查找 *****/
int
left
=
0
,
right
=
piles
;
while
(
left
<
right
)
{
int
mid
=
left
+
(
right
-
left
)
/
2
;
if
(
top
[
mid
]
>
poker
)
{
right
=
mid
;
}
else
if
(
top
[
mid
]
<
poker
)
{
left
=
mid
+
1
;
}
else
if
(
top
[
mid
]
==
poker
)
{
right
=
mid
;
}
}
/*********************************/
/* 没找到合适的牌堆,新建一堆 */
if
(
left
==
piles
)
piles
++
;
/* 把这张牌放到牌堆顶 */
top
[
left
]
=
poker
;
}
/* 牌堆数就是 LIS 长度 */
return
piles
;
}
};
```
动态规划系列/动态规划详解进阶.md
浏览文件 @
8bbbebd1
...
...
@@ -146,6 +146,8 @@ int helper(vector<int>& memo, int n) {
```
cpp
int
fib
(
int
N
)
{
if
(
N
==
0
)
return
0
;
if
(
N
==
1
)
return
1
;
vector
<
int
>
dp
(
N
+
1
,
0
);
// base case
dp
[
1
]
=
dp
[
2
]
=
1
;
...
...
@@ -200,7 +202,7 @@ int coinChange(int[] coins, int amount);
比如说
`k = 3`
,面值分别为 1,2,5,总金额
`amount = 11`
。那么最少需要 3 枚硬币凑出,即 11 = 5 + 5 + 1。
你认为计算机应该如何解决这个问题?显然,就是把所有
肯
能的凑硬币方法都穷举出来,然后找找看最少需要多少枚硬币。
你认为计算机应该如何解决这个问题?显然,就是把所有
可
能的凑硬币方法都穷举出来,然后找找看最少需要多少枚硬币。
**1、暴力递归**
...
...
@@ -366,4 +368,45 @@ PS:为啥 `dp` 数组初始化为 `amount + 1` 呢,因为凑成 `amount` 金
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
DapangLiu
](
https://github.com/DapangLiu
)
提供 509. 斐波那契数 Python3 解法代码:
递归写法
```
python
class
Solution
:
def
fib
(
self
,
N
:
int
)
->
int
:
if
N
<=
1
:
return
N
return
self
.
fib
(
N
-
1
)
+
self
.
fib
(
N
-
2
)
```
动态规划写法
```
python
class
Solution
:
def
fib
(
self
,
N
:
int
)
->
int
:
if
N
==
0
:
return
0
# init
result
=
[
0
for
i
in
range
(
N
+
1
)]
result
[
1
]
=
1
# status transition
for
j
in
range
(
2
,
N
+
1
):
result
[
j
]
=
result
[
j
-
1
]
+
result
[
j
-
2
]
return
result
[
-
1
]
```
动态规划写法 (状态压缩)
```
python
class
Solution
:
def
fib
(
self
,
n
:
int
)
->
int
:
# current status only depends on two previous status
dp_0
,
dp_1
=
0
,
1
for
_
in
range
(
n
):
dp_0
,
dp_1
=
dp_1
,
dp_0
+
dp_1
return
dp_0
```
\ No newline at end of file
动态规划系列/团灭股票问题.md
浏览文件 @
8bbbebd1
...
...
@@ -16,7 +16,7 @@
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
[
买卖股票的最佳时机
](
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
/solution/
)
[
买卖股票的最佳时机
](
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
)
[
买卖股票的最佳时机 II
](
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
)
...
...
@@ -32,7 +32,7 @@
很多读者抱怨 LeetCode 的股票系列问题奇技淫巧太多,如果面试真的遇到这类问题,基本不会想到那些巧妙的办法,怎么办?
**所以本文拒绝奇技淫巧,而是稳扎稳打,只用一种通用方法解决所用问题,以不变应万变**
。
这篇文章用状态机的技巧来解决,可以全部提交通过。不要觉得这个名词高大上,文学词汇而已,实际上就是 DP table,看一眼就明白了。
这篇文章
参考
[
英文版高赞题解
](
https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/discuss/108870/Most-consistent-ways-of-dealing-with-the-series-of-stock-problems
)
的思路,
用状态机的技巧来解决,可以全部提交通过。不要觉得这个名词高大上,文学词汇而已,实际上就是 DP table,看一眼就明白了。
先随便抽出一道题,看看别人的解法:
...
...
动态规划系列/抢房子.md
浏览文件 @
8bbbebd1
...
...
@@ -258,4 +258,66 @@ int[] dp(TreeNode root) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
Shantom
](
https://github.com/Shantom
)
提供 198. House Robber I Python3 解法代码:
```
Python
class Solution:
def rob(self, nums: List[int]) -> int:
# 当前,上一间,上上间
cur, pre1, pre2 = 0, 0, 0
for num in nums:
# 当前 = max(上上间+(抢当前),上间(放弃当前))
cur = max(pre2 + num, pre1)
pre2 = pre1
pre1 = cur
return cur
```
[
Shantom
](
https://github.com/Shantom
)
提供 213. House Robber II Python3 解法代码:
```
Python
class Solution:
def rob(self, nums: List[int]) -> int:
# 只有一间时不成环
if len(nums) == 1:
return nums[0]
# 该函数同198题
def subRob(nums: List[int]) -> int:
# 当前,上一间,上上间
cur, pre1, pre2 = 0, 0, 0
for num in nums:
# 当前 = max(上上间+(抢当前),上间(放弃当前))
cur = max(pre2 + num, pre1)
pre2 = pre1
pre1 = cur
return cur
# 不考虑第一间或者不考虑最后一间
return max(subRob(nums[:-1]), subRob(nums[1:]))
```
[
Shantom
](
https://github.com/Shantom
)
提供 337. House Robber III Python3 解法代码:
```
Python
class Solution:
def rob(self, root: TreeNode) -> int:
# 返回值0项为不抢该节点,1项为抢该节点
def dp(root):
if not root:
return 0, 0
left = dp(root.left)
right = dp(root.right)
# 抢当前,则两个下家不抢
do = root.val + left[0] + right[0]
# 不抢当前,则下家随意
do_not = max(left) + max(right)
return do_not, do
return max(dp(root))
```
动态规划系列/最优子结构.md
浏览文件 @
8bbbebd1
...
...
@@ -54,7 +54,7 @@ return result;
当然,上面这个例子太简单了,不过请读者回顾一下,我们做动态规划问题,是不是一直在求各种最值,本质跟我们举的例子没啥区别,无非需要处理一下重叠子问题。
前文不
[
同定义不同解法
](
../动态规划系列/动态规划之四键键盘.md
)
和
[
高楼扔鸡蛋进阶
](
../动态规划系列/高楼扔鸡蛋问题.md
)
就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。
前文不
[
同定义不同解法
](
https://labuladong.gitbook.io/algo
)
和
[
高楼扔鸡蛋进阶
](
https://labuladong.gitbook.io/algo
)
就展示了如何改造问题,不同的最优子结构,可能导致不同的解法和效率。
再举个常见但也十分简单的例子,求一棵二叉树的最大值,不难吧(简单起见,假设节点中的值都是非负数):
...
...
动态规划系列/最长公共子序列.md
浏览文件 @
8bbbebd1
# 最长公共子序列
# 最长公共子序列
<p
align=
'center'
>
...
...
@@ -147,4 +147,64 @@ else:
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
Edwenc
](
https://github.com/Edwenc
)
提供 C++ 代码:
```
C++
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
// 先计算两条字符串的长度
int m = text1.size();
int n = text2.size();
// 构建dp矩阵 默认初始值0
// 这里会多扩建一边和一列
// 因为dp[i][j]的含义是:对于 s1[1..i] 和 s2[1..j],它们的LCS长度是 dp[i][j]。
// 所以当i或者j为零时 LCS的长度默认为0
vector< vector<int> > dp ( m+1 , vector<int> ( n+1 , 0 ) );
// 状态转移
// i、j都从1开始遍历 因为下面的操作中都会-1 相当于从0开始
for ( int i=1 ; i<m+1 ; i++ ){
for ( int j=1 ; j<n+1 ; j++ ){
// 如果text1和text2相同
// 就在它们的前一位基础上加一
// 如果不同 只能在之前的两者中去最大
dp[i][j] = (text1[i-1] == text2[j-1]) ? dp[i-1][j-1] + 1 : max( dp[i-1][j] , dp[i][j-1] );
}
}
// 返回最终右下角的值
return dp[m][n];
}
};
```
[
Shawn
](
https://github.com/Shawn-Hx
)
提供 Java 代码:
```
java
public
int
longestCommonSubsequence
(
String
text1
,
String
text2
)
{
// 字符串转为char数组以加快访问速度
char
[]
str1
=
text1
.
toCharArray
();
char
[]
str2
=
text2
.
toCharArray
();
int
m
=
str1
.
length
,
n
=
str2
.
length
;
// 构建dp table,初始值默认为0
int
[][]
dp
=
new
int
[
m
+
1
][
n
+
1
];
// 状态转移
for
(
int
i
=
1
;
i
<=
m
;
i
++)
for
(
int
j
=
1
;
j
<=
n
;
j
++)
if
(
str1
[
i
-
1
]
==
str2
[
j
-
1
])
// 找到LCS中的字符
dp
[
i
][
j
]
=
dp
[
i
-
1
][
j
-
1
]
+
1
;
else
dp
[
i
][
j
]
=
Math
.
max
(
dp
[
i
-
1
][
j
],
dp
[
i
][
j
-
1
]);
return
dp
[
m
][
n
];
}
```
动态规划系列/编辑距离.md
浏览文件 @
8bbbebd1
...
...
@@ -291,4 +291,31 @@ class Node {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
ChenjieXu
](
https://github.com/ChenjieXu
)
提供Python版本代码:
```
python3
def minDistance(word1, word2):
m, n = len(word1), len(word2)
# 创建 DP 数组
dp = [[0] * (n + 1) for _ in range(m + 1)]
# base case初始化
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
# 自底向上求解
for i in range(1, m + 1):
for j in range(1, n + 1):
# 状态转移方程
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j] + 1,
dp[i][j - 1] + 1,
dp[i - 1][j - 1] + 1)
# 储存着整个 word1 和 word2 的最小编辑距离
return dp[m][n]
```
`
\ No newline at end of file
动态规划系列/贪心算法之区间调度问题.md
浏览文件 @
8bbbebd1
# 贪心算法之区间调度问题
# 贪心算法之区间调度问题
<p
align=
'center'
>
...
...
@@ -158,4 +158,43 @@ int findMinArrowShots(int[][] intvs) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
### python
Edwenc 提供 第435题的python3 代码:
```
python
class
Solution
:
def
eraseOverlapIntervals
(
self
,
intervals
:
List
[
List
[
int
]])
->
int
:
### 思路是首先找到不重叠的区间的个数
### 然后再用总个数减去不重叠个数
### 获得的就是 需要移除的个数
# 首先获得区间的个数 为0的话就不用移除
n
=
len
(
intervals
)
if
n
==
0
:
return
0
# 按照每个区间的右端点值进行排序
sorted_list
=
sorted
(
intervals
,
key
=
lambda
x
:
x
[
1
]
)
# 不重叠区间个数至少是1
count
=
1
# end是所有不重叠的区间中 最大的右端点
# end的初始值即是sorted_list[0]的右端点
end
=
sorted_list
[
0
][
1
]
# 从1开始往后找 因为0在上面已经取过了
for
i
in
range
(
1
,
n
):
# start是当前区间左端点值
start
=
sorted_list
[
i
][
0
]
# 如果当前左端点比最大右端点都大了(可能相等)
# 说明两区间不重叠 count+1 再更新end
if
start
>=
end
:
count
+=
1
end
=
sorted_list
[
i
][
1
]
# 最后返回的是 需要移除的区间个数
return
n
-
count
```
\ No newline at end of file
动态规划系列/高楼扔鸡蛋问题.md
浏览文件 @
8bbbebd1
...
...
@@ -243,7 +243,7 @@ def superEggDrop(self, K: int, N: int) -> int:
return
dp
(
K
,
N
)
```
这里就不展开其他解法了,留在下一篇文章
[
高楼扔鸡蛋进阶
](
../动态规划系列/高楼扔鸡蛋进阶.md
)
这里就不展开其他解法了,留在下一篇文章
[
高楼扔鸡蛋进阶
](
https://labuladong.gitbook.io/algo
)
我觉得吧,我们这种解法就够了:找状态,做选择,足够清晰易懂,可流程化,可举一反三。掌握这套框架学有余力的话,再去考虑那些奇技淫巧也不迟。
...
...
数据结构系列/二叉搜索树操作集锦.md
浏览文件 @
8bbbebd1
...
...
@@ -312,10 +312,97 @@ void BST(TreeNode root, int target) {
======其他语言代码======
[
Edwenc
](
https://github.com/Edwenc
)
提供 leetcode第450题的python3 代码:
### c++
[
dekunma
](
https://www.linkedin.com/in/dekun-ma-036a9b198/
)
提供第98题C++代码:
```
c++
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class
Solution
{
public:
bool
isValidBST
(
TreeNode
*
root
)
{
// 用helper method求解
return
isValidBST
(
root
,
nullptr
,
nullptr
);
}
bool
isValidBST
(
TreeNode
*
root
,
TreeNode
*
min
,
TreeNode
*
max
)
{
// base case, root为nullptr
if
(
!
root
)
return
true
;
// 不符合BST的条件
if
(
min
&&
root
->
val
<=
min
->
val
)
return
false
;
if
(
max
&&
root
->
val
>=
max
->
val
)
return
false
;
// 向左右子树分别递归求解
return
isValidBST
(
root
->
left
,
min
,
root
)
&&
isValidBST
(
root
->
right
,
root
,
max
);
}
};
```
### python
[
ChenjieXu
](
https://github.com/ChenjieXu
)
提供第98题Python3代码:
```
python
def
isValidBST
(
self
,
root
):
# 递归函数
def
helper
(
node
,
lower
=
float
(
'-inf'
),
upper
=
float
(
'inf'
)):
if
not
node
:
return
True
val
=
node
.
val
if
val
<=
lower
or
val
>=
upper
:
return
False
# 右节点
if
not
helper
(
node
.
right
,
val
,
upper
):
return
False
# 左节点
if
not
helper
(
node
.
left
,
lower
,
val
):
return
False
return
True
return
helper
(
root
)
```
[
lixiandea
](
https://github.com/lixiandea
)
提供第100题Python3代码:
```
python3
```
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class
Solution
:
def
isSameTree
(
self
,
p
:
TreeNode
,
q
:
TreeNode
)
->
bool
:
'''
当前节点值相等且树的子树相等,则树相等。
递归退出条件:两个节点存在一个节点为空
'''
if
p
==
None
:
if
q
==
None
:
return
True
else
:
return
False
if
q
==
None
:
return
False
# 当前节点相同且左子树和右子树分别相同
return
p
.
val
==
q
.
val
and
self
.
isSameTree
(
p
.
left
,
q
.
left
)
and
self
.
isSameTree
(
p
.
right
,
q
.
right
)
```
[
Edwenc
](
https://github.com/Edwenc
)
提供 leetcode第450题的python3 代码:
```
python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
...
...
@@ -361,5 +448,4 @@ class Solution:
while
node
.
right
:
node
=
node
.
right
return
node
```
\ No newline at end of file
数据结构系列/单调栈.md
浏览文件 @
8bbbebd1
...
...
@@ -11,7 +11,7 @@
![](
../pictures/souyisou.png
)
相关推荐:
*
[
回溯算法解题套路框架
](
https://labuladong.gitbook.io/algo
)
*
[
回溯算法解题套路框架
](
https://labuladong.gitbook.io/algo
)
*
[
动态规划解题套路框架
](
https://labuladong.gitbook.io/algo
)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
...
...
@@ -20,7 +20,7 @@
[
503.下一个更大元素II
](
https://leetcode-cn.com/problems/next-greater-element-ii
)
[
1118.一月有多少天
](
https://leetcode-cn.com/problems/number-of-days-in-a-month
)
[
739.每日温度
](
https://leetcode-cn.com/problems/daily-temperatures/
)
**-----------**
...
...
@@ -82,7 +82,7 @@ vector<int> nextGreaterElement(vector<int>& nums) {
### 问题变形
单调栈的使用技巧差不多了,来一个简单的变形,力扣第
1118 题「一月有多少天
」:
单调栈的使用技巧差不多了,来一个简单的变形,力扣第
739 题「每日温度
」:
给你一个数组
`T`
,这个数组存放的是近几天的天气气温,你返回一个等长的数组,计算:
**对于每一天,你还要至少等多少天才能等到一个更暖和的气温;如果等不到那一天,填 0**
。
...
...
@@ -181,4 +181,26 @@ vector<int> nextGreaterElements(vector<int>& nums) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
### java
```
java
// 739. Daily Temperatures
class
Solution
{
public
int
[]
dailyTemperatures
(
int
[]
T
)
{
Stack
<
Integer
>
stack
=
new
Stack
<>();
int
[]
ans
=
new
int
[
T
.
length
];
for
(
int
i
=
0
;
i
<
T
.
length
;
i
++)
{
// 如果压栈之后不满足单调递减,弹出元素,直至保持单调性
while
(!
stack
.
isEmpty
()
&&
T
[
i
]
>
T
[
stack
.
peek
()])
{
int
index
=
stack
.
pop
();
// 被弹出的元素(T[index])都是小于当前的元素(T[i]),由于栈内元素单调递减,大于被弹出元素(index)的最近的就是当前元素(i)
ans
[
index
]
=
i
-
index
;
}
stack
.
push
(
i
);
}
return
ans
;
}
}
```
数据结构系列/单调队列.md
浏览文件 @
8bbbebd1
...
...
@@ -210,4 +210,105 @@ vector<int> maxSlidingWindow(vector<int>& nums, int k) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
### python3
由
[
SCUHZS
](
ttps://github.com/brucecat
)
提供
```
python
from
collections
import
deque
class
MonotonicQueue
(
object
):
def
__init__
(
self
):
# 双端队列
self
.
data
=
deque
()
def
push
(
self
,
n
):
# 实现单调队列的push方法
while
self
.
data
and
self
.
data
[
-
1
]
<
n
:
self
.
data
.
pop
()
self
.
data
.
append
(
n
)
def
max
(
self
):
# 取得单调队列中的最大值
return
self
.
data
[
0
]
def
pop
(
self
,
n
):
# 实现单调队列的pop方法
if
self
.
data
and
self
.
data
[
0
]
==
n
:
self
.
data
.
popleft
()
class
Solution
:
def
maxSlidingWindow
(
self
,
nums
:
List
[
int
],
k
:
int
)
->
List
[
int
]:
# 单调队列实现的窗口
window
=
MonotonicQueue
()
# 结果
res
=
[]
for
i
in
range
(
0
,
len
(
nums
)):
if
i
<
k
-
1
:
# 先填满窗口前k-1
window
.
push
(
nums
[
i
])
else
:
# 窗口向前滑动
window
.
push
(
nums
[
i
])
res
.
append
(
window
.
max
())
window
.
pop
(
nums
[
i
-
k
+
1
])
return
res
```
### java
```
java
class
Solution
{
public
int
[]
maxSlidingWindow
(
int
[]
nums
,
int
k
)
{
int
len
=
nums
.
length
;
// 判断数组或者窗口长度为0的情况
if
(
len
*
k
==
0
)
{
return
new
int
[
0
];
}
/*
采用两端扫描的方法
将数组分成大小为 k 的若干个窗口, 对每个窗口分别从左往右和从右往左扫描, 记录扫描的最大值
left[] 记录从左往右扫描的最大值
right[] 记录从右往左扫描的最大值
*/
int
[]
left
=
new
int
[
len
];
int
[]
right
=
new
int
[
len
];
for
(
int
i
=
0
;
i
<
len
;
i
=
i
+
k
)
{
// 每个窗口中的第一个值
left
[
i
]
=
nums
[
i
];
// 窗口的最后边界
int
index
=
i
+
k
-
1
>=
len
?
len
-
1
:
i
+
k
-
1
;
// 每个窗口的最后一个值
right
[
index
]
=
nums
[
index
];
// 对该窗口从左往右扫描
for
(
int
j
=
i
+
1
;
j
<=
index
;
j
++)
{
left
[
j
]
=
Math
.
max
(
left
[
j
-
1
],
nums
[
j
]);
}
// 对该窗口从右往左扫描
for
(
int
j
=
index
-
1
;
j
>=
i
;
j
--)
{
right
[
j
]
=
Math
.
max
(
right
[
j
+
1
],
nums
[
j
]);
}
}
int
[]
arr
=
new
int
[
len
-
k
+
1
];
// 对于第 i 个位置, 它一定是该窗口从右往左扫描数组中的最后一个值, 相对的 i + k - 1 是该窗口从左向右扫描数组中的最后一个位置
// 对两者取最大值即可
for
(
int
i
=
0
;
i
<
len
-
k
+
1
;
i
++)
{
arr
[
i
]
=
Math
.
max
(
right
[
i
],
left
[
i
+
k
-
1
]);
}
return
arr
;
}
}
```
数据结构系列/设计Twitter.md
浏览文件 @
8bbbebd1
...
...
@@ -302,4 +302,121 @@ PS:本文前两张图片和 GIF 是我第一次尝试用平板的绘图软件
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
happy-yuxuan
](
https://github.com/happy-yuxuan
)
提供 C++ 代码:
```
c++
static
int
timestamp
=
0
;
class
Tweet
{
private:
int
id
;
int
time
;
public:
Tweet
*
next
;
// id为推文内容,time为发文时间
Tweet
(
int
id
,
int
time
)
{
this
->
id
=
id
;
this
->
time
=
time
;
next
=
nullptr
;
}
int
getId
()
const
{
return
this
->
id
;
}
int
getTime
()
const
{
return
this
->
time
;
}
};
class
User
{
private:
int
id
;
public:
Tweet
*
head
;
// 发布的Twitter,用链表表示
unordered_set
<
int
>
followed
;
// 用户关注了那些人
User
(
int
userId
)
{
this
->
id
=
userId
;
head
=
nullptr
;
// 要先把自己关注了
followed
.
insert
(
id
);
}
void
follow
(
int
userId
)
{
followed
.
insert
(
userId
);
}
void
unfollow
(
int
userId
)
{
// 不可以取关自己
if
(
userId
!=
this
->
id
)
followed
.
erase
(
userId
);
}
void
post
(
int
contentId
)
{
Tweet
*
twt
=
new
Tweet
(
contentId
,
timestamp
);
timestamp
++
;
// 将新建的推文插入链表头
// 越靠前的推文 timestamp 值越大
twt
->
next
=
head
;
head
=
twt
;
}
};
class
Twitter
{
private:
// 映射将 userId 和 User 对象对应起来
unordered_map
<
int
,
User
*>
userMap
;
// 判断该用户存不存在系统中,即userMap中存不存在id
inline
bool
contain
(
int
id
)
{
return
userMap
.
find
(
id
)
!=
userMap
.
end
();
}
public:
Twitter
()
{
userMap
.
clear
();
}
/* user 发表一条 tweet 动态 */
void
postTweet
(
int
userId
,
int
tweetId
)
{
if
(
!
contain
(
userId
))
userMap
[
userId
]
=
new
User
(
userId
);
userMap
[
userId
]
->
post
(
tweetId
);
}
/* 返回该 user 关注的人(包括他自己)最近的动态 id,
最多 10 条,而且这些动态必须按从新到旧的时间线顺序排列。*/
vector
<
int
>
getNewsFeed
(
int
userId
)
{
vector
<
int
>
ret
;
if
(
!
contain
(
userId
))
return
ret
;
// 构造一个自动通过Tweet发布的time属性从大到小排序的二叉堆
typedef
function
<
bool
(
const
Tweet
*
,
const
Tweet
*
)
>
Compare
;
Compare
cmp
=
[](
const
Tweet
*
a
,
const
Tweet
*
b
)
{
return
a
->
getTime
()
<
b
->
getTime
();
};
priority_queue
<
Tweet
*
,
vector
<
Tweet
*>
,
Compare
>
q
(
cmp
);
// 关注列表的用户Id
unordered_set
<
int
>
&
users
=
userMap
[
userId
]
->
followed
;
// 先将所有链表头节点插入优先级队列
for
(
int
id
:
users
)
{
if
(
!
contain
(
id
))
continue
;
Tweet
*
twt
=
userMap
[
id
]
->
head
;
if
(
twt
==
nullptr
)
continue
;
q
.
push
(
twt
);
}
while
(
!
q
.
empty
())
{
Tweet
*
t
=
q
.
top
();
q
.
pop
();
ret
.
push_back
(
t
->
getId
());
if
(
ret
.
size
()
==
10
)
return
ret
;
// 最多返回 10 条就够了
if
(
t
->
next
)
q
.
push
(
t
->
next
);
}
return
ret
;
}
/* follower 关注 followee */
void
follow
(
int
followerId
,
int
followeeId
)
{
// 若 follower 不存在,则新建
if
(
!
contain
(
followerId
))
userMap
[
followerId
]
=
new
User
(
followerId
);
// 若 followee 不存在,则新建
if
(
!
contain
(
followeeId
))
userMap
[
followeeId
]
=
new
User
(
followeeId
);
userMap
[
followerId
]
->
follow
(
followeeId
);
}
/* follower 取关 followee,如果 Id 不存在则什么都不做 */
void
unfollow
(
int
followerId
,
int
followeeId
)
{
if
(
contain
(
followerId
))
userMap
[
followerId
]
->
unfollow
(
followeeId
);
}
};
```
\ No newline at end of file
数据结构系列/递归反转链表的一部分.md
浏览文件 @
8bbbebd1
...
...
@@ -218,4 +218,40 @@ ListNode reverseBetween(ListNode head, int m, int n) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
DiamondI
](
https://github.com/DiamondI
)
提供python3版本代码:
思路:递归。时间复杂度为O(n),由于递归调用需要借助栈的空间,因此空间复杂度亦为O(n)。
```
python3
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def __init__(self):
self.__successor = None
def __reverseN(self, head: ListNode, n: int) -> ListNode:
if n == 1:
# 记录第 n + 1 个节点
self.__successor = head.next;
return head;
# 以 head.next 为起点,需要反转前 n - 1 个节点
last = self.__reverseN(head.next, n - 1);
head.next.next = head;
# 让反转之后的 head 节点和后面的节点连起来
head.next = self.__successor;
return last;
def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:
# base case
if m == 1:
return self.__reverseN(head, n);
# 前进到反转的起点触发 base case
head.next = self.reverseBetween(head.next, m - 1, n - 1);
return head;
```
算法思维系列/二分查找详解.md
浏览文件 @
8bbbebd1
...
...
@@ -65,7 +65,7 @@ int binarySearch(int[] nums, int target) {
### 一、寻找一个数(基本的二分搜索)
这个场景是最简单的,
肯
能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回 -1。
这个场景是最简单的,
可
能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回 -1。
```
java
int
binarySearch
(
int
[]
nums
,
int
target
)
{
...
...
@@ -104,7 +104,7 @@ int binarySearch(int[] nums, int target) {
`while(left <= right)`
的终止条件是
`left == right + 1`
,写成区间的形式就是
`[right + 1, right]`
,或者带个具体的数字进去
`[3, 2]`
,可见
**这时候区间为空**
,因为没有数字既大于等于 3 又小于等于 2 的吧。所以这时候 while 循环终止是正确的,直接返回 -1 即可。
`while(left < right)`
的终止条件是
`left == right`
,写成区间的形式就是
`[
lef
t, right]`
,或者带个具体的数字进去
`[2, 2]`
,
**这时候区间非空**
,还有一个数 2,但此时 while 循环终止了。也就是说这区间
`[2, 2]`
被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。
`while(left < right)`
的终止条件是
`left == right`
,写成区间的形式就是
`[
righ
t, right]`
,或者带个具体的数字进去
`[2, 2]`
,
**这时候区间非空**
,还有一个数 2,但此时 while 循环终止了。也就是说这区间
`[2, 2]`
被漏掉了,索引 2 没有被搜索,如果这时候直接返回 -1 就是错误的。
当然,如果你非要用
`while(left < right)`
也可以,我们已经知道了出错的原因,就打个补丁好了:
...
...
算法思维系列/双指针技巧.md
浏览文件 @
8bbbebd1
...
...
@@ -20,7 +20,7 @@
[
141.环形链表II
](
https://leetcode-cn.com/problems/linked-list-cycle-ii
)
[
167.两数之和 II - 输入有序数组
](
https://leetcode-cn.com/problems/two-sum
)
[
167.两数之和 II - 输入有序数组
](
https://leetcode-cn.com/problems/two-sum
-ii-input-array-is-sorted
)
**-----------**
...
...
@@ -80,6 +80,11 @@ ListNode detectCycle(ListNode head) {
if
(
fast
==
slow
)
break
;
}
// 上面的代码类似 hasCycle 函数
if
(
fast
==
null
||
fast
.
next
==
null
)
{
// fast 遇到空指针说明没有环
return
null
;
}
slow
=
head
;
while
(
slow
!=
fast
)
{
fast
=
fast
.
next
;
...
...
@@ -230,4 +235,25 @@ void reverse(int[] nums) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
ryandeng32
](
https://github.com/ryandeng32/
)
提供 Python 代码
```
python
class
Solution
:
def
hasCycle
(
self
,
head
:
ListNode
)
->
bool
:
# 检查链表头是否为None,是的话则不可能为环形
if
head
is
None
:
return
False
# 快慢指针初始化
slow
=
fast
=
head
# 若链表非环形则快指针终究会遇到None,然后退出循环
while
fast
.
next
and
fast
.
next
.
next
:
# 更新快慢指针
slow
=
slow
.
next
fast
=
fast
.
next
.
next
# 快指针追上慢指针则链表为环形
if
slow
==
fast
:
return
True
# 退出循环,则链表有结束,不可能为环形
return
False
```
算法思维系列/字符串乘法.md
浏览文件 @
8bbbebd1
...
...
@@ -101,4 +101,47 @@ string multiply(string num1, string num2) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
Zane Wang
](
https://github.com/zanecat
)
提供 Java 解法代码:
```
java
public
String
multiply
(
String
num1
,
String
num2
)
{
// 初始化字符数组
char
[]
s1
=
num1
.
toCharArray
();
char
[]
s2
=
num2
.
toCharArray
();
// 结果长度最多为两字符串长度之和
int
[]
res
=
new
int
[
s1
.
length
+
s2
.
length
];
// 从个位开始遍历,把两数字中每一位相乘
for
(
int
i
=
s1
.
length
-
1
;
i
>=
0
;
i
--)
{
for
(
int
j
=
s2
.
length
-
1
;
j
>=
0
;
j
--)
{
// 计算乘积,并把乘积放在 res 对应的位置, 暂时不考虑进位
res
[
i
+
j
+
1
]
+=
(
s1
[
i
]
-
'0'
)
*
(
s2
[
j
]
-
'0'
);
}
}
// 从个位再次遍历,如果上一次遍历中两数乘积为两位数,进位并叠加到前面一位
int
carry
=
0
;
for
(
int
i
=
res
.
length
-
1
;
i
>=
0
;
i
--)
{
int
sum
=
res
[
i
]
+
carry
;
res
[
i
]
=
sum
%
10
;
carry
=
sum
/
10
;
}
//遍历res数组,构造最终答案字符串
StringBuilder
ans
=
new
StringBuilder
();
int
i
=
0
;
// 首先找到不为0的第一位
while
(
i
<
res
.
length
-
1
&&
res
[
i
]
==
0
)
{
i
++;
}
// 将后面的数字附加到ans后面
while
(
i
<
res
.
length
)
{
ans
.
append
(
res
[
i
++]);
}
return
ans
.
toString
();
}
```
\ No newline at end of file
算法思维系列/滑动窗口技巧.md
浏览文件 @
8bbbebd1
...
...
@@ -12,8 +12,8 @@
**最新消息:关注公众号参与活动,有机会成为 [70k star 算法仓库](https://github.com/labuladong/fucking-algorithm) 的贡献者,机不可失时不再来**
!
相关推荐:
*
[
东哥吃葡萄时竟然吃出一道算法题!
](
../高频面试系列/吃葡萄.md
)
*
[
如何寻找缺失的元素
](
../高频面试系列/消失的元素.md
)
*
[
东哥吃葡萄时竟然吃出一道算法题!
](
https://labuladong.gitbook.io/algo
)
*
[
如何寻找缺失的元素
](
https://labuladong.gitbook.io/algo
)
读完本文,你不仅学会了算法套路,还可以顺便去 LeetCode 上拿下如下题目:
...
...
高频面试系列/LRU算法.md
浏览文件 @
8bbbebd1
...
...
@@ -346,4 +346,121 @@ class LRUCache {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
gowufang
](
https://github.com/gowufang
)
提供第146题C++代码:
```
cpp
class
LRUCache
{
public:
struct
node
{
int
val
;
int
key
;
node
*
pre
;
//当前节点的前一个节点
node
*
next
;
//当前节点的后一个节点
node
(){}
node
(
int
key
,
int
val
)
:
key
(
key
),
val
(
val
),
pre
(
NULL
),
next
(
NULL
){}
};
LRUCache
(
int
size
)
{
this
->
size
=
size
;
head
=
new
node
();
tail
=
new
node
();
head
->
next
=
tail
;
tail
->
pre
=
head
;
}
void
movetohead
(
node
*
cur
)
//相当于一个insert操作,在head 和 head的next之间插入一个节点
{
node
*
next
=
head
->
next
;
//head的next先保存起来
head
->
next
=
cur
;
//将当前节点移动到head的后面
cur
->
pre
=
head
;
//当前节点cur的pre指向head
next
->
pre
=
cur
;
cur
->
next
=
next
;
}
node
*
deletecurrentnode
(
node
*
cur
)
//移除当前节点
{
cur
->
pre
->
next
=
cur
->
next
;
cur
->
next
->
pre
=
cur
->
pre
;
return
cur
;
}
void
makerecently
(
node
*
cur
)
{
node
*
temp
=
deletecurrentnode
(
cur
);
// 删除 cur,要重新插入到对头
movetohead
(
temp
);
//cur放到队头去
}
int
get
(
int
key
)
{
int
ret
=
-
1
;
if
(
map
.
count
(
key
))
{
node
*
temp
=
map
[
key
];
makerecently
(
temp
);
// 将 key 变为最近使用
ret
=
temp
->
val
;
}
return
ret
;
}
void
put
(
int
key
,
int
value
)
{
if
(
map
.
count
(
key
))
{
// 修改 key 的值
node
*
temp
=
map
[
key
];
temp
->
val
=
value
;
// 将 key 变为最近使用
makerecently
(
temp
);
}
else
{
node
*
cur
=
new
node
(
key
,
value
);
if
(
map
.
size
()
==
size
)
{
// 链表头部就是最久未使用的 key
node
*
temp
=
deletecurrentnode
(
tail
->
pre
);
map
.
erase
(
temp
->
key
);
}
movetohead
(
cur
);
map
[
key
]
=
cur
;
}
}
unordered_map
<
int
,
node
*>
map
;
int
size
;
node
*
head
,
*
tail
;
};
```
```
python3
"""
所谓LRU缓存,根本的难点在于记录最久被使用的键值对,这就设计到排序的问题,
在python中,天生具备排序功能的字典就是OrderDict。
注意到,记录最久未被使用的键值对的充要条件是将每一次put/get的键值对都定义为
最近访问,那么最久未被使用的键值对自然就会排到最后。
如果你深入python OrderDict的底层实现,就会知道它的本质是个双向链表+字典。
它内置支持了
1. move_to_end来重排链表顺序,它可以让我们将最近访问的键值对放到最后面
2. popitem来弹出键值对,它既可以弹出最近的,也可以弹出最远的,弹出最远的就是我们要的操作。
"""
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.capacity = capacity # cache的容量
self.visited = OrderedDict() # python内置的OrderDict具备排序的功能
def get(self, key: int) -> int:
if key not in self.visited:
return -1
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
return self.visited[key]
def put(self, key: int, value: int) -> None:
if key not in self.visited and len(self.visited) == self.capacity:
# last=False时,按照FIFO顺序弹出键值对
# 因为我们将最近访问的放到最后,所以最远访问的就是最前的,也就是最first的,故要用FIFO顺序
self.visited.popitem(last=False)
self.visited[key]=value
self.visited.move_to_end(key) # 最近访问的放到链表最后,维护好顺序
```
高频面试系列/koko偷香蕉.md
浏览文件 @
8bbbebd1
...
...
@@ -169,4 +169,45 @@ for (int i = 0; i < n; i++)
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
tonytang731
](
https://https://github.com/tonytang731
)
提供 Python3 代码:
```
python
import
math
class
Solution
:
def
minEatingSpeed
(
self
,
piles
,
H
):
# 初始化起点和终点, 最快的速度可以一次拿完最大的一堆
start
=
1
end
=
max
(
piles
)
# while loop进行二分查找
while
start
+
1
<
end
:
mid
=
start
+
(
end
-
start
)
//
2
# 如果中点所需时间大于H, 我们需要加速, 将起点设为中点
if
self
.
timeH
(
piles
,
mid
)
>
H
:
start
=
mid
# 如果中点所需时间小于H, 我们需要减速, 将终点设为中点
else
:
end
=
mid
# 提交前确认起点是否满足条件,我们要尽量慢拿
if
self
.
timeH
(
piles
,
start
)
<=
H
:
return
start
# 若起点不符合, 则中点是答案
return
end
def
timeH
(
self
,
piles
,
K
):
# 初始化时间
H
=
0
#求拿每一堆需要多长时间
for
pile
in
piles
:
H
+=
math
.
ceil
(
pile
/
K
)
return
H
```
高频面试系列/二分查找判定子序列.md
浏览文件 @
8bbbebd1
...
...
@@ -168,4 +168,67 @@ boolean isSubsequence(String s, String t) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
dekunma
](
https://www.linkedin.com/in/dekun-ma-036a9b198/
)
提供C++代码
**解法一:遍历(也可以用双指针):**
```
C++
class Solution {
public:
bool isSubsequence(string s, string t) {
// 遍历s
for(int i = 0; i < s.size(); i++) {
// 找到s[i]字符在t中的位置
size_t pos = t.find(s[i]);
// 如果s[i]字符不在t中,返回false
if(pos == std::string::npos) return false;
// 如果s[i]在t中,后面就只看pos以后的字串,防止重复查找
else t = t.substr(pos + 1);
}
return true;
}
};
```
**解法二:二分查找:**
```
C++
class Solution {
public:
bool isSubsequence(string s, string t) {
int m = s.size(), n = t.size();
// 对 t 进行预处理
vector<int> index[256];
for (int i = 0; i < n; i++) {
char c = t[i];
index[c].push_back(i);
}
// 串 t 上的指针
int j = 0;
// 借助 index 查找 s[i]
for (int i = 0; i < m; i++) {
char c = s[i];
// 整个 t 压根儿没有字符 c
if (index[c].empty()) return false;
int pos = left_bound(index[c], j);
// 二分搜索区间中没有找到字符 c
if (pos == index[c].size()) return false;
// 向前移动指针 j
j = index[c][pos] + 1;
}
return true;
}
// 查找左侧边界的二分查找
int left_bound(vector<int> arr, int tar) {
int lo = 0, hi = arr.size();
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (tar > arr[mid]) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
};
```
高频面试系列/判断回文链表.md
浏览文件 @
8bbbebd1
...
...
@@ -237,4 +237,35 @@ p.next = reverse(q);
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
C++版本:
```
cpp
bool
isPalindrome
(
ListNode
*
head
)
{
if
(
head
==
nullptr
||
head
->
next
==
nullptr
)
//为空或者只有一个节点时,直接判断为true
return
true
;
ListNode
*
slow
=
head
,
*
fast
=
head
;
while
(
fast
!=
nullptr
)
{
//首先找到中间节点
slow
=
slow
->
next
;
fast
=
fast
->
next
==
nullptr
?
fast
->
next
:
fast
->
next
->
next
;
//因为链表长度可能是奇数或偶数,所以需要进行判断
}
ListNode
*
temp
=
nullptr
,
*
pre
=
nullptr
;
//pre始终保持后续链表的头部,temp节点则作为中间零时替换的节点
while
(
slow
!=
nullptr
)
{
//利用头插法,将当前节点与后续链表断链处理,反转后半部分的链表
temp
=
slow
->
next
;
slow
->
next
=
pre
;
//建立连接
pre
=
slow
;
//pre始终作为后续链表的头部
slow
=
temp
;
}
while
(
head
!=
nullptr
&&
pre
!=
nullptr
)
{
//同步进行比较
if
(
head
->
val
!=
pre
->
val
)
{
//值有不一样的,说明不是回文联表,直接返回false了
return
false
;
}
head
=
head
->
next
;
//head向下走,直到走到空
pre
=
pre
->
next
;
//pre节点也向下走,直到走到空
}
return
true
;
//到此说明当前链表是回文链表返回true即可
}
```
高频面试系列/合法括号判定.md
浏览文件 @
8bbbebd1
...
...
@@ -112,4 +112,51 @@ char leftOf(char c) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
### Python3
```
python
def
isValid
(
self
,
s
:
str
)
->
bool
:
left
=
[]
leftOf
=
{
')'
:
'('
,
']'
:
'['
,
'}'
:
'{'
}
for
c
in
s
:
if
c
in
'([{'
:
left
.
append
(
c
)
elif
left
and
leftOf
[
c
]
==
left
[
-
1
]:
# 右括号 + left不为空 + 和最近左括号能匹配
left
.
pop
()
else
:
# 右括号 + (left为空 / 和堆顶括号不匹配)
return
False
# left中所有左括号都被匹配则return True 反之False
return
not
left
```
```
java
//基本思想:每次遇到左括号时都将相对应的右括号')',']'或'}'推入堆栈
//如果在字符串中出现右括号,则需要检查堆栈是否为空,以及顶部元素是否与该右括号相同。如果不是,则该字符串无效。
//最后,我们还需要检查堆栈是否为空
public
boolean
isValid
(
String
s
)
{
Deque
<
Character
>
stack
=
new
ArrayDeque
<>();
for
(
char
c
:
s
.
toCharArray
()){
//是左括号就将相对应的右括号入栈
if
(
c
==
'('
)
{
stack
.
offerLast
(
')'
);
}
else
if
(
c
==
'{'
){
stack
.
offerLast
(
'}'
);
}
else
if
(
c
==
'['
){
stack
.
offerLast
(
']'
);
}
else
if
(
stack
.
isEmpty
()
||
stack
.
pollLast
()!=
c
){
//出现右括号,检查堆栈是否为空,以及顶部元素是否与该右括号相同
return
false
;
}
}
return
stack
.
isEmpty
();
}
```
高频面试系列/打印素数.md
浏览文件 @
8bbbebd1
...
...
@@ -178,4 +178,41 @@ int countPrimes(int n) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
C++解法:
采用的算法是埃拉托斯特尼筛法
埃拉托斯特尼筛法的具体内容就是:
**要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。**
同时考虑到大于2的偶数都不是素数,所以可以进一步优化成:
**要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的奇数倍剔除,剩下的奇数就是素数。**
此算法其实就是上面的Java解法所采用的。
这里提供C++的代码:
```
C++
class Solution {
public:
int countPrimes(int n) {
int res = 0;
bool prime[n+1];
for(int i = 0; i < n; ++i)
prime[i] = true;
for(int i = 2; i <= sqrt(n); ++i) //计数过程
{ //外循环优化,因为判断一个数是否为质数只需要整除到sqrt(n),反推亦然
if(prime[i])
{
for(int j = i * i; j < n; j += i) //内循环优化,i*i之前的比如i*2,i*3等,在之前的循环中已经验证了
{
prime[j] = false;
}
}
}
for (int i = 2; i < n; ++i)
if (prime[i]) res++; //最后遍历统计一遍,存入res
return res;
}
};
```
高频面试系列/接雨水.md
浏览文件 @
8bbbebd1
...
...
@@ -211,4 +211,99 @@ if (l_max < r_max) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
Yifan Zhang
](
https://github.com/FanFan0919
)
提供 java 代码
**双指针解法**
:时间复杂度 O(N),空间复杂度 O(1)
对cpp版本的解法有非常微小的优化。
因为我们每次循环只会选 left 或者 right 处的柱子来计算,因此我们并不需要在每次循环中同时更新
`maxLeft`
和
`maxRight`
。
我们可以先比较
`maxLeft`
和
`maxRight`
,决定这次选择计算的柱子是
`height[left]`
或者
`height[right]`
后再更新对应的
`maxLeft`
或
`maxRight`
。
当然这并不会在时间上带来什么优化,只是提供一种思路。
```
java
class
Solution
{
public
int
trap
(
int
[]
height
)
{
if
(
height
==
null
||
height
.
length
==
0
)
return
0
;
int
left
=
0
,
right
=
height
.
length
-
1
;
int
maxLeft
=
height
[
left
],
maxRight
=
height
[
right
];
int
res
=
0
;
while
(
left
<
right
)
{
// 比较 maxLeft 和 maxRight,决定这次计算 left 还是 right 处的柱子
if
(
maxLeft
<
maxRight
)
{
left
++;
maxLeft
=
Math
.
max
(
maxLeft
,
height
[
left
]);
// update maxLeft
res
+=
maxLeft
-
height
[
left
];
}
else
{
right
--;
maxRight
=
Math
.
max
(
maxRight
,
height
[
right
]);
// update maxRight
res
+=
maxRight
-
height
[
right
];
}
}
return
res
;
}
}
```
附上暴力解法以及备忘录解法的 java 代码
**暴力解法**
:时间复杂度 O(N^2),空间复杂度 O(1)
```
java
class
Solution
{
public
int
trap
(
int
[]
height
)
{
if
(
height
==
null
||
height
.
length
==
0
)
return
0
;
int
n
=
height
.
length
;
int
res
=
0
;
// 跳过最左边和最右边的柱子,从第二个柱子开始
for
(
int
i
=
1
;
i
<
n
-
1
;
i
++)
{
int
maxLeft
=
0
,
maxRight
=
0
;
// 找右边最高的柱子
for
(
int
j
=
i
;
j
<
n
;
j
++)
{
maxRight
=
Math
.
max
(
maxRight
,
height
[
j
]);
}
// 找左边最高的柱子
for
(
int
j
=
i
;
j
>=
0
;
j
--)
{
maxLeft
=
Math
.
max
(
maxLeft
,
height
[
j
]);
}
// 如果自己就是最高的话,
// maxLeft == maxRight == height[i]
res
+=
Math
.
min
(
maxLeft
,
maxRight
)
-
height
[
i
];
}
return
res
;
}
}
```
**备忘录解法**
:时间复杂度 O(N),空间复杂度 O(N)
```
java
class
Solution
{
public
int
trap
(
int
[]
height
)
{
if
(
height
==
null
||
height
.
length
==
0
)
return
0
;
int
n
=
height
.
length
;
int
res
=
0
;
// 数组充当备忘录
int
[]
maxLeft
=
new
int
[
n
];
int
[]
maxRight
=
new
int
[
n
];
// 初始化 base case
maxLeft
[
0
]
=
height
[
0
];
maxRight
[
n
-
1
]
=
height
[
n
-
1
];
// 从左向右计算 maxLeft
for
(
int
i
=
1
;
i
<
n
;
i
++)
{
maxLeft
[
i
]
=
Math
.
max
(
maxLeft
[
i
-
1
],
height
[
i
]);
}
// 从右向左计算 maxRight
for
(
int
i
=
n
-
2
;
i
>=
0
;
i
--)
{
maxRight
[
i
]
=
Math
.
max
(
maxRight
[
i
+
1
],
height
[
i
]);
}
// 计算答案
for
(
int
i
=
1
;
i
<
n
;
i
++)
{
res
+=
Math
.
min
(
maxLeft
[
i
],
maxRight
[
i
])
-
height
[
i
];
}
return
res
;
}
}
```
\ No newline at end of file
高频面试系列/消失的元素.md
浏览文件 @
8bbbebd1
...
...
@@ -89,6 +89,7 @@ int missingNumber(int[] nums) {
for
(
int
x
:
nums
)
sum
+=
x
;
return
expect
-
sum
;
}
```
你看,这种解法应该是最简单的,但说实话,我自己也没想到这个解法,而且我去问了几个大佬,他们也没想到这个最简单的思路。相反,如果去问一个初中生,他也许很快就能想到。
...
...
@@ -132,4 +133,49 @@ public int missingNumber(int[] nums) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
happy-yuxuan
](
https://github.com/happy-yuxuan
)
提供 三种方法的 C++ 代码:
```
c++
// 方法:异或元素和索引
int
missingNumber
(
vector
<
int
>&
nums
)
{
int
n
=
nums
.
size
();
int
res
=
0
;
// 先和新补的索引异或一下
res
^=
n
;
// 和其他的元素、索引做异或
for
(
int
i
=
0
;
i
<
n
;
i
++
)
res
^=
i
^
nums
[
i
];
return
res
;
}
```
```
c++
// 方法:等差数列求和
int
missingNumber
(
vector
<
int
>&
nums
)
{
int
n
=
nums
.
size
();
// 公式:(首项 + 末项) * 项数 / 2
int
expect
=
(
0
+
n
)
*
(
n
+
1
)
/
2
;
int
sum
=
0
;
for
(
int
x
:
nums
)
sum
+=
x
;
return
expect
-
sum
;
}
```
```
c++
// 方法:防止整型溢出
int
missingNumber
(
vector
<
int
>&
nums
)
{
int
n
=
nums
.
size
();
int
res
=
0
;
// 新补的索引
res
+=
n
-
0
;
// 剩下索引和元素的差加起来
for
(
int
i
=
0
;
i
<
n
;
i
++
)
res
+=
i
-
nums
[
i
];
return
res
;
}
```
高频面试系列/缺失和重复的元素.md
浏览文件 @
8bbbebd1
...
...
@@ -139,4 +139,30 @@ vector<int> findErrorNums(vector<int>& nums) {
<img
src=
"../pictures/qrcode.jpg"
width=
200
>
</p>
======其他语言代码======
\ No newline at end of file
======其他语言代码======
[
zhuli
](
https://github.com/1097452462
"zhuli"
)
提供的Java代码:
```
java
class
Solution
{
public
int
[]
findErrorNums
(
int
[]
nums
)
{
int
n
=
nums
.
length
;
int
dup
=
-
1
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
// 元素是从 1 开始的
int
index
=
Math
.
abs
(
nums
[
i
])
-
1
;
// nums[index] 小于 0 则说明重复访问
if
(
nums
[
index
]
<
0
)
dup
=
Math
.
abs
(
nums
[
i
]);
else
nums
[
index
]
*=
-
1
;
}
int
missing
=
-
1
;
for
(
int
i
=
0
;
i
<
n
;
i
++)
// nums[i] 大于 0 则说明没有访问
if
(
nums
[
i
]
>
0
)
// 将索引转换成元素
missing
=
i
+
1
;
return
new
int
[]{
dup
,
missing
};
}
}
```
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录