2
.gitattributes
vendored
|
@ -1 +1 @@
|
|||
* linguist-language=java
|
||||
*.md linguist-language=java
|
||||
|
|
60
README.md
|
@ -1,29 +1,31 @@
|
|||
<div align="center">
|
||||
<a href="https://github.com/CyC2018/CS-Notes"> <img src="https://badgen.net/github/stars/CyC2018/CS-Notes?icon=github&color=4ab8a1"></a>
|
||||
<a href="other/download.md"> <img src="https://badgen.net/badge/OvO/%E7%A6%BB%E7%BA%BF%E4%B8%8B%E8%BD%BD?icon=telegram&color=4ab8a1"></a>
|
||||
<a href="https://cyc2018.github.io/CS-Notes"> <img src="https://badgen.net/badge/CyC/%E5%9C%A8%E7%BA%BF%E9%98%85%E8%AF%BB?icon=sourcegraph&color=4ab8a1"></a>
|
||||
<a href="https://gitstar-ranking.com/repositories"> <img src="https://badgen.net/badge/Rank/20?icon=github&color=4ab8a1"></a>
|
||||
<a href="assets/download.md"> <img src="https://badgen.net/badge/OvO/%E7%A6%BB%E7%BA%BF%E4%B8%8B%E8%BD%BD?icon=telegram&color=4ab8a1"></a>
|
||||
<a href="https://cyc2018.github.io/CS-Notes"> <img src="https://badgen.net/badge/CyC/%E5%9C%A8%E7%BA%BF%E9%98%85%E8%AF%BB?icon=sourcegraph&color=4ab8a1"></a>
|
||||
<a href="#微信公众号"> <img src="https://badgen.net/badge/%e5%85%ac%e4%bc%97%e5%8f%b7/CyC2018?icon=rss&color=4ab8a1"></a>
|
||||
</div>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
| 算法 | 操作系统 | 网络 | 面向对象 | 数据库 | Java | 系统设计 | 工具 | 编码实践 | 后记 |
|
||||
| :------------------------: | :------------------------------: | :--------------------: | :--------------------: | :----------------------------------: | :--------------------------------------: | :----------------------: | :--------------------------------------: | :----------------------------------: | :--------------------------------------: |
|
||||
| [:pencil2:](#pencil2-算法) | [:computer:](#computer-操作系统) | [:cloud:](#cloud-网络) | [:art:](#art-面向对象) | [:floppy_disk:](#floppy_disk-数据库) | [:coffee:](#coffee-java) | [:bulb:](#bulb-系统设计) | [:wrench:](#wrench-工具) | [:watermelon:](#watermelon-编码实践) | [:memo:](#memo-后记) |
|
||||
| 算法 | 操作系统 | 网络 |面向对象| 数据库 | Java | 系统设计| 工具 |编码实践| 后记 |
|
||||
| :---: | :----: | :---: | :----: | :----: | :----: | :----: | :----: | :----: | :----: |
|
||||
| [:pencil2:](#pencil2-算法) | [:computer:](#computer-操作系统) | [:cloud:](#cloud-网络) | [:art:](#art-面向对象) | [:floppy_disk:](#floppy_disk-数据库) |[:coffee:](#coffee-java)| [:bulb:](#bulb-系统设计) |[:wrench:](#wrench-工具)| [:watermelon:](#watermelon-编码实践) |[:memo:](#memo-后记)|
|
||||
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
<img src="assets/LogoMakr_0zpEzN.png" width="200px">
|
||||
<img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/LogoMakr_0zpEzN.png" width="200px">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
## :pencil2: 算法
|
||||
|
||||
- [剑指 Offer 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/剑指%20Offer%20题解%20-%20目录.md)
|
||||
- [Leetcode 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20题解%20-%20目录.md)
|
||||
- [算法](https://github.com/CyC2018/CS-Notes/blob/master/notes/算法%20-%20目录.md)
|
||||
- [笔试面试题库](https://www.nowcoder.com/contestRoom?from=cyc_github)
|
||||
|
||||
## :computer: 操作系统
|
||||
|
||||
|
@ -38,10 +40,10 @@
|
|||
|
||||
## :art: 面向对象
|
||||
|
||||
- [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/notes/设计模式.md)
|
||||
- [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/notes/设计模式%20-%20目录.md)
|
||||
- [面向对象思想](https://github.com/CyC2018/CS-Notes/blob/master/notes/面向对象思想.md)
|
||||
|
||||
## :floppy_disk: 数据库
|
||||
## :floppy_disk: 数据库
|
||||
|
||||
- [数据库系统原理](https://github.com/CyC2018/CS-Notes/blob/master/notes/数据库系统原理.md)
|
||||
- [SQL](https://github.com/CyC2018/CS-Notes/blob/master/notes/SQL.md)
|
||||
|
@ -78,25 +80,15 @@
|
|||
- [代码可读性](https://github.com/CyC2018/CS-Notes/blob/master/notes/代码可读性.md)
|
||||
- [代码风格规范](https://github.com/CyC2018/CS-Notes/blob/master/notes/代码风格规范.md)
|
||||
|
||||
<div align="center"><img width="580px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img></div>
|
||||
## :memo: 后记
|
||||
|
||||
## :memo: 后记
|
||||
<div align="center">
|
||||
<a href="https://www.nowcoder.com/discuss/137593?from=cyc_github"> 我的面经 </a> / <a href="https://cyc2018.github.io"> 我的简历 </a> / <a href="https://github.com/CyC2018/Markdown-Resume"> 简历模版 </a> / <a href="https://github.com/CyC2018/Job-Recommend"> 内推 </a> / <a href="https://xiaozhuanlan.com/CyC2018"> 专栏 </a> / <a href="assets/QQ2群.png"> QQ 群</a>
|
||||
<br><br>
|
||||
<img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img>
|
||||
</div>
|
||||
|
||||
### 更多内容
|
||||
|
||||
- 内推:[Job-Recommend](https://github.com/CyC2018/Job-Recommend)
|
||||
- 简历模版:[Markdown-Resume](https://github.com/CyC2018/Markdown-Resume)
|
||||
- 面经:[2018 这一年](https://www.nowcoder.com/discuss/137593)
|
||||
- 简历:https://cyc2018.github.io
|
||||
- 知乎:[CyC2018](https://www.zhihu.com/people/xiao-shi-guang-33/activities)
|
||||
- 小专栏:[后端面试进阶指南](https://xiaozhuanlan.com/CyC2018)
|
||||
- QQ 交流群:[857210598](assets/group.png)
|
||||
|
||||
### 工具
|
||||
|
||||
- Github Pages:[docsify](https://docsify.js.org/#/)
|
||||
- 绘图:[draw.io](https://www.draw.io/)
|
||||
- Logo:[logomakr](https://logomakr.com/)
|
||||
|
||||
### 排版
|
||||
|
||||
|
@ -106,17 +98,13 @@
|
|||
|
||||
在线排版工具:[Text-Typesetting](https://github.com/CyC2018/Text-Typesetting)。
|
||||
|
||||
### 上传方案
|
||||
|
||||
为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括提取图片、Markdown 文档转换、Git 同步。进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。
|
||||
|
||||
GFM 转换工具:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。
|
||||
|
||||
### License
|
||||
|
||||
本仓库内容 **不是** 将网上的资料随意拼凑而来,除了少部分引用书上和技术文档的原文,其余都是我的原创。在您引用本仓库内容或者对内容进行修改演绎时,请署名并以相同方式共享,谢谢。
|
||||
本仓库的内容不是将网上的资料随意拼凑而来,除了少部分引用书上和技术文档的原文(这部分内容都在末尾的参考链接中加了出处),其余都是我的原创。在您引用本仓库内容或者对内容进行修改演绎时,请署名并以相同方式共享,谢谢。
|
||||
|
||||
转载文章请在开头明显处标明该页面地址,公众号等其它转载其联系 zhengyc101@163.com。
|
||||
转载文章请在开头明显处标明该页面地址,公众号等其它转载请联系 zhengyc101@163.com。
|
||||
|
||||
Logo:[logomakr](https://logomakr.com/)
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="知识共享许可协议" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a>
|
||||
|
||||
|
|
BIN
assets/QQ2群.png
Normal file
After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 271 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 220 KiB |
|
@ -28,6 +28,7 @@
|
|||
|
||||
# 如何下载
|
||||
|
||||
离线版本由公众号 **CyC2018** 发布,最新版本也会在上面及时发布,在公众号后台回复 **离线下载** 即可获取下载链接。
|
||||
离线版本由公众号 **CyC2018** 发布,最新版本也会在上面及时发布,在后台回复 **CyC** 即可获取下载链接。
|
||||
|
||||
<div align="center"><img width="350px" src="公众号二维码-1.png"></img></div>
|
||||
|
||||
<div align="center"><img width="600px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/QQ截图20190608120206.png"></img></div>
|
BIN
assets/group.png
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 506 KiB |
BIN
assets/公众号二维码-1.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
assets/公众号二维码.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
assets/公众号海报7.png
Normal file
After Width: | Height: | Size: 83 KiB |
|
@ -20,7 +20,7 @@
|
|||
|
||||
## 🎨 面向对象
|
||||
|
||||
- [设计模式](notes/设计模式.md) </br>
|
||||
- [设计模式](notes/设计模式%20-%20目录1.md) </br>
|
||||
- [面向对象思想](notes/面向对象思想.md)
|
||||
|
||||
## 💾 数据库
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=2.0, minimum-scale=1.0">
|
||||
<link rel="icon" href=" https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/LogoMakr_1J56bI.png">
|
||||
<link rel="stylesheet" href="https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/vue.css">
|
||||
|
||||
<!-- 将自定义样式放在 Github 上会导致加载速度变得非常慢,所以采取直接内嵌的方式 -->
|
||||
<style type="text/css">
|
||||
|
||||
/* 隐藏头部的目录 */
|
||||
#main>ul:nth-child(1) {
|
||||
display: none;
|
||||
|
@ -34,9 +36,14 @@
|
|||
|
||||
img,
|
||||
pre {
|
||||
border-radius: 8px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.markdown-section p.tip,
|
||||
.markdown-section tr:nth-child(1n) {
|
||||
background-color: #f8f8f8 !important;
|
||||
}
|
||||
|
||||
.content,
|
||||
.sidebar,
|
||||
.markdown-section,
|
||||
|
@ -60,6 +67,15 @@
|
|||
/*font-family: Microsoft YaHei, Source Sans Pro, Helvetica Neue, Arial, sans-serif !important;*/
|
||||
}
|
||||
|
||||
.markdown-section pre>code {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
code,
|
||||
pre {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.markdown-section>p {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
@ -68,9 +84,18 @@
|
|||
font-family: Consolas, Roboto Mono, Monaco, courier, monospace !important;
|
||||
}
|
||||
|
||||
p, h1, h2, h3, h4, ol, ul {
|
||||
letter-spacing: 2px !important;
|
||||
}
|
||||
|
||||
p, ol, ul {
|
||||
line-height: 30px !important;
|
||||
}
|
||||
|
||||
@media (min-width:600px) {
|
||||
.markdown-section pre>code {
|
||||
font-size: .9rem !important;
|
||||
letter-spacing: 1.1px !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +103,7 @@
|
|||
.markdown-section pre>code {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
|
||||
pre:after {
|
||||
|
@ -100,7 +126,7 @@
|
|||
|
||||
.markdown-section > div > img,
|
||||
.markdown-section pre {
|
||||
box-shadow: 2px 2px 20px 6px #ddd !important;
|
||||
box-shadow: 0px 0px 20px 11px #eaeaea;
|
||||
}
|
||||
|
||||
pre {
|
||||
|
@ -117,10 +143,14 @@
|
|||
|
||||
@media (max-width:600px) {
|
||||
pre {
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
padding-left: 3px !important;
|
||||
padding-right: 3px !important;
|
||||
margin-left: -20px !important;
|
||||
margin-right: -20px !important;
|
||||
box-shadow: 0px 0px 20px 0px #eee !important;
|
||||
}
|
||||
|
||||
|
||||
.docsify-copy-code-button {
|
||||
display: none;
|
||||
}
|
||||
|
@ -247,8 +277,8 @@
|
|||
|
||||
/*深蓝加粗*/
|
||||
.token.keyword {
|
||||
color: #124363;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
|
||||
|
|
76
docs/notes/10.1 斐波那契数列.md
Normal file
|
@ -0,0 +1,76 @@
|
|||
# 10.1 斐波那契数列
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=13&tqId=11160&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
求斐波那契数列的第 n 项,n <= 39。
|
||||
|
||||
<!--<div align="center"><img src="https://latex.codecogs.com/gif.latex?f(n)=\left\{\begin{array}{rcl}0&&{n=0}\\1&&{n=1}\\f(n-1)+f(n-2)&&{n>1}\end{array}\right." class="mathjax-pic"/></div> <br> -->
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/45be9587-6069-4ab7-b9ac-840db1a53744.jpg" width="330px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
如果使用递归求解,会重复计算一些子问题。例如,计算 f(4) 需要计算 f(3) 和 f(2),计算 f(3) 需要计算 f(2) 和 f(1),可以看到 f(2) 被重复计算了。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png" width="350px"/> </div><br>
|
||||
|
||||
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
|
||||
|
||||
```java
|
||||
public int Fibonacci(int n) {
|
||||
if (n <= 1)
|
||||
return n;
|
||||
int[] fib = new int[n + 1];
|
||||
fib[1] = 1;
|
||||
for (int i = 2; i <= n; i++)
|
||||
fib[i] = fib[i - 1] + fib[i - 2];
|
||||
return fib[n];
|
||||
}
|
||||
```
|
||||
|
||||
考虑到第 i 项只与第 i-1 和第 i-2 项有关,因此只需要存储前两项的值就能求解第 i 项,从而将空间复杂度由 O(N) 降低为 O(1)。
|
||||
|
||||
```java
|
||||
public int Fibonacci(int n) {
|
||||
if (n <= 1)
|
||||
return n;
|
||||
int pre2 = 0, pre1 = 1;
|
||||
int fib = 0;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
fib = pre2 + pre1;
|
||||
pre2 = pre1;
|
||||
pre1 = fib;
|
||||
}
|
||||
return fib;
|
||||
}
|
||||
```
|
||||
|
||||
由于待求解的 n 小于 40,因此可以将前 40 项的结果先进行计算,之后就能以 O(1) 时间复杂度得到第 n 项的值。
|
||||
|
||||
```java
|
||||
public class Solution {
|
||||
|
||||
private int[] fib = new int[40];
|
||||
|
||||
public Solution() {
|
||||
fib[1] = 1;
|
||||
for (int i = 2; i < fib.length; i++)
|
||||
fib[i] = fib[i - 1] + fib[i - 2];
|
||||
}
|
||||
|
||||
public int Fibonacci(int n) {
|
||||
return fib[n];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
49
docs/notes/10.2 矩形覆盖.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# 10.2 矩形覆盖
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6?tpId=13&tqId=11163&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法?
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/b903fda8-07d0-46a7-91a7-e803892895cf.gif" width="100px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
当 n 为 1 时,只有一种覆盖方法:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f6e146f1-57ad-411b-beb3-770a142164ef.png" width="100px"> </div><br>
|
||||
|
||||
当 n 为 2 时,有两种覆盖方法:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/fb3b8f7a-4293-4a38-aae1-62284db979a3.png" width="200px"> </div><br>
|
||||
|
||||
要覆盖 2\*n 的大矩形,可以先覆盖 2\*1 的矩形,再覆盖 2\*(n-1) 的矩形;或者先覆盖 2\*2 的矩形,再覆盖 2\*(n-2) 的矩形。而覆盖 2\*(n-1) 和 2\*(n-2) 的矩形可以看成子问题。该问题的递推公式如下:
|
||||
|
||||
<!-- <div align="center"><img src="https://latex.codecogs.com/gif.latex?f(n)=\left\{\begin{array}{rcl}1&&{n=1}\\2&&{n=2}\\f(n-1)+f(n-2)&&{n>1}\end{array}\right." class="mathjax-pic"/></div> <br> -->
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg" width="370px"> </div><br>
|
||||
|
||||
```java
|
||||
public int RectCover(int n) {
|
||||
if (n <= 2)
|
||||
return n;
|
||||
int pre2 = 1, pre1 = 2;
|
||||
int result = 0;
|
||||
for (int i = 3; i <= n; i++) {
|
||||
result = pre2 + pre1;
|
||||
pre2 = pre1;
|
||||
pre1 = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/10.3 跳台阶.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 10.3 跳台阶
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4?tpId=13&tqId=11161&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/9dae7475-934f-42e5-b3b3-12724337170a.png" width="380px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
当 n = 1 时,只有一种跳法:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png" width="250px"> </div><br>
|
||||
|
||||
当 n = 2 时,有两种跳法:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png" width="300px"> </div><br>
|
||||
|
||||
跳 n 阶台阶,可以先跳 1 阶台阶,再跳 n-1 阶台阶;或者先跳 2 阶台阶,再跳 n-2 阶台阶。而 n-1 和 n-2 阶台阶的跳法可以看成子问题,该问题的递推公式为:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg" width="350px"> </div><br>
|
||||
|
||||
```java
|
||||
public int JumpFloor(int n) {
|
||||
if (n <= 2)
|
||||
return n;
|
||||
int pre2 = 1, pre1 = 2;
|
||||
int result = 0;
|
||||
for (int i = 2; i < n; i++) {
|
||||
result = pre2 + pre1;
|
||||
pre2 = pre1;
|
||||
pre1 = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
67
docs/notes/10.4 变态跳台阶.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# 10.4 变态跳台阶
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387?tpId=13&tqId=11162&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/cd411a94-3786-4c94-9e08-f28320e010d5.png" width="380px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 动态规划
|
||||
|
||||
```java
|
||||
public int JumpFloorII(int target) {
|
||||
int[] dp = new int[target];
|
||||
Arrays.fill(dp, 1);
|
||||
for (int i = 1; i < target; i++)
|
||||
for (int j = 0; j < i; j++)
|
||||
dp[i] += dp[j];
|
||||
return dp[target - 1];
|
||||
}
|
||||
```
|
||||
|
||||
### 数学推导
|
||||
|
||||
跳上 n-1 级台阶,可以从 n-2 级跳 1 级上去,也可以从 n-3 级跳 2 级上去...,那么
|
||||
|
||||
```
|
||||
f(n-1) = f(n-2) + f(n-3) + ... + f(0)
|
||||
```
|
||||
|
||||
同样,跳上 n 级台阶,可以从 n-1 级跳 1 级上去,也可以从 n-2 级跳 2 级上去... ,那么
|
||||
|
||||
```
|
||||
f(n) = f(n-1) + f(n-2) + ... + f(0)
|
||||
```
|
||||
|
||||
综上可得
|
||||
|
||||
```
|
||||
f(n) - f(n-1) = f(n-1)
|
||||
```
|
||||
|
||||
即
|
||||
|
||||
```
|
||||
f(n) = 2*f(n-1)
|
||||
```
|
||||
|
||||
所以 f(n) 是一个等比数列
|
||||
|
||||
```source-java
|
||||
public int JumpFloorII(int target) {
|
||||
return (int) Math.pow(2, target - 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
72
docs/notes/11. 旋转数组的最小数字.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# 11. 旋转数组的最小数字
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/0038204c-4b8a-42a5-921d-080f6674f989.png" width="210px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
将旋转数组对半分可以得到一个包含最小元素的新旋转数组,以及一个非递减排序的数组。新的旋转数组的数组元素是原数组的一半,从而将问题规模减少了一半,这种折半性质的算法的时间复杂度为 O(logN)(为了方便,这里将 log<sub>2</sub>N 写为 logN)。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/424f34ab-a9fd-49a6-9969-d76b42251365.png" width="300px"> </div><br>
|
||||
|
||||
此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组,哪一个是非递减数组。我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素。
|
||||
|
||||
通过修改二分查找算法进行求解(l 代表 low,m 代表 mid,h 代表 high):
|
||||
|
||||
- 当 nums[m] <= nums[h] 时,表示 [m, h] 区间内的数组是非递减数组,[l, m] 区间内的数组是旋转数组,此时令 h = m;
|
||||
- 否则 [m + 1, h] 区间内的数组是旋转数组,令 l = m + 1。
|
||||
|
||||
```java
|
||||
public int minNumberInRotateArray(int[] nums) {
|
||||
if (nums.length == 0)
|
||||
return 0;
|
||||
int l = 0, h = nums.length - 1;
|
||||
while (l < h) {
|
||||
int m = l + (h - l) / 2;
|
||||
if (nums[m] <= nums[h])
|
||||
h = m;
|
||||
else
|
||||
l = m + 1;
|
||||
}
|
||||
return nums[l];
|
||||
}
|
||||
```
|
||||
|
||||
如果数组元素允许重复,会出现一个特殊的情况:nums[l] == nums[m] == nums[h],此时无法确定解在哪个区间,需要切换到顺序查找。例如对于数组 {1,1,1,0,1},l、m 和 h 指向的数都为 1,此时无法知道最小数字 0 在哪个区间。
|
||||
|
||||
```java
|
||||
public int minNumberInRotateArray(int[] nums) {
|
||||
if (nums.length == 0)
|
||||
return 0;
|
||||
int l = 0, h = nums.length - 1;
|
||||
while (l < h) {
|
||||
int m = l + (h - l) / 2;
|
||||
if (nums[l] == nums[m] && nums[m] == nums[h])
|
||||
return minNumber(nums, l, h);
|
||||
else if (nums[m] <= nums[h])
|
||||
h = m;
|
||||
else
|
||||
l = m + 1;
|
||||
}
|
||||
return nums[l];
|
||||
}
|
||||
|
||||
private int minNumber(int[] nums, int l, int h) {
|
||||
for (int i = l; i < h; i++)
|
||||
if (nums[i] > nums[i + 1])
|
||||
return nums[i + 1];
|
||||
return nums[l];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
71
docs/notes/12. 矩阵中的路径.md
Normal file
|
@ -0,0 +1,71 @@
|
|||
# 12. 矩阵中的路径
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向上下左右移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。
|
||||
|
||||
例如下面的矩阵包含了一条 bfce 路径。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png" width="200px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用回溯法(backtracking)进行求解,它是一种暴力搜索方法,通过搜索所有可能的结果来求解问题。回溯法在一次搜索结束时需要进行回溯(回退),将这一次搜索过程中设置的状态进行清除,从而开始一次新的搜索过程。例如下图示例中,从 f 开始,下一步有 4 种搜索可能,如果先搜索 b,需要将 b 标记为已经使用,防止重复使用。在这一次搜索结束之后,需要将 b 的已经使用状态清除,并搜索 c。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png" width="200px"> </div><br>
|
||||
|
||||
本题的输入是数组而不是矩阵(二维数组),因此需要先将数组转换成矩阵。
|
||||
|
||||
```java
|
||||
private final static int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
|
||||
private int rows;
|
||||
private int cols;
|
||||
|
||||
public boolean hasPath(char[] array, int rows, int cols, char[] str) {
|
||||
if (rows == 0 || cols == 0) return false;
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
boolean[][] marked = new boolean[rows][cols];
|
||||
char[][] matrix = buildMatrix(array);
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < cols; j++)
|
||||
if (backtracking(matrix, str, marked, 0, i, j))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean backtracking(char[][] matrix, char[] str,
|
||||
boolean[][] marked, int pathLen, int r, int c) {
|
||||
|
||||
if (pathLen == str.length) return true;
|
||||
if (r < 0 || r >= rows || c < 0 || c >= cols
|
||||
|| matrix[r][c] != str[pathLen] || marked[r][c]) {
|
||||
|
||||
return false;
|
||||
}
|
||||
marked[r][c] = true;
|
||||
for (int[] n : next)
|
||||
if (backtracking(matrix, str, marked, pathLen + 1, r + n[0], c + n[1]))
|
||||
return true;
|
||||
marked[r][c] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private char[][] buildMatrix(char[] array) {
|
||||
char[][] matrix = new char[rows][cols];
|
||||
for (int r = 0, idx = 0; r < rows; r++)
|
||||
for (int c = 0; c < cols; c++)
|
||||
matrix[r][c] = array[idx++];
|
||||
return matrix;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
65
docs/notes/13. 机器人的运动范围.md
Normal file
|
@ -0,0 +1,65 @@
|
|||
# 13. 机器人的运动范围
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6e5207314b5241fb83f2329e89fdecc8?tpId=13&tqId=11219&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。
|
||||
|
||||
例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子?
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用深度优先搜索(Depth First Search,DFS)方法进行求解。回溯是深度优先搜索的一种特例,它在一次搜索过程中需要设置一些本次搜索过程的局部状态,并在本次搜索结束之后清除状态。而普通的深度优先搜索并不需要使用这些局部状态,虽然还是有可能设置一些全局状态。
|
||||
|
||||
```java
|
||||
private static final int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
|
||||
private int cnt = 0;
|
||||
private int rows;
|
||||
private int cols;
|
||||
private int threshold;
|
||||
private int[][] digitSum;
|
||||
|
||||
public int movingCount(int threshold, int rows, int cols) {
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
this.threshold = threshold;
|
||||
initDigitSum();
|
||||
boolean[][] marked = new boolean[rows][cols];
|
||||
dfs(marked, 0, 0);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
private void dfs(boolean[][] marked, int r, int c) {
|
||||
if (r < 0 || r >= rows || c < 0 || c >= cols || marked[r][c])
|
||||
return;
|
||||
marked[r][c] = true;
|
||||
if (this.digitSum[r][c] > this.threshold)
|
||||
return;
|
||||
cnt++;
|
||||
for (int[] n : next)
|
||||
dfs(marked, r + n[0], c + n[1]);
|
||||
}
|
||||
|
||||
private void initDigitSum() {
|
||||
int[] digitSumOne = new int[Math.max(rows, cols)];
|
||||
for (int i = 0; i < digitSumOne.length; i++) {
|
||||
int n = i;
|
||||
while (n > 0) {
|
||||
digitSumOne[i] += n % 10;
|
||||
n /= 10;
|
||||
}
|
||||
}
|
||||
this.digitSum = new int[rows][cols];
|
||||
for (int i = 0; i < this.rows; i++)
|
||||
for (int j = 0; j < this.cols; j++)
|
||||
this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
59
docs/notes/14. 剪绳子.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# 14. 剪绳子
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/integer-break/description/)
|
||||
|
||||
## 题目描述
|
||||
|
||||
把一根绳子剪成多段,并且使得每段的长度乘积最大。
|
||||
|
||||
```html
|
||||
n = 2
|
||||
return 1 (2 = 1 + 1)
|
||||
|
||||
n = 10
|
||||
return 36 (10 = 3 + 3 + 4)
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 贪心
|
||||
|
||||
尽可能多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。
|
||||
|
||||
证明:当 n >= 5 时,3(n - 3) - n = 2n - 9 > 0,且 2(n - 2) - n = n - 4 > 0。因此在 n >= 5 的情况下,将绳子剪成一段为 2 或者 3,得到的乘积会更大。又因为 3(n - 3) - 2(n - 2) = n - 5 >= 0,所以剪成一段长度为 3 比长度为 2 得到的乘积更大。
|
||||
|
||||
```java
|
||||
public int integerBreak(int n) {
|
||||
if (n < 2)
|
||||
return 0;
|
||||
if (n == 2)
|
||||
return 1;
|
||||
if (n == 3)
|
||||
return 2;
|
||||
int timesOf3 = n / 3;
|
||||
if (n - timesOf3 * 3 == 1)
|
||||
timesOf3--;
|
||||
int timesOf2 = (n - timesOf3 * 3) / 2;
|
||||
return (int) (Math.pow(3, timesOf3)) * (int) (Math.pow(2, timesOf2));
|
||||
}
|
||||
```
|
||||
|
||||
### 动态规划
|
||||
|
||||
```java
|
||||
public int integerBreak(int n) {
|
||||
int[] dp = new int[n + 1];
|
||||
dp[1] = 1;
|
||||
for (int i = 2; i <= n; i++)
|
||||
for (int j = 1; j < i; j++)
|
||||
dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/15. 二进制中 1 的个数.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 15. 二进制中 1 的个数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/8ee967e43c2c4ec193b040ea7fbb10b8?tpId=13&tqId=11164&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个整数,输出该数二进制表示中 1 的个数。
|
||||
|
||||
### n&(n-1)
|
||||
|
||||
该位运算去除 n 的位级表示中最低的那一位。
|
||||
|
||||
```
|
||||
n : 10110100
|
||||
n-1 : 10110011
|
||||
n&(n-1) : 10110000
|
||||
```
|
||||
|
||||
时间复杂度:O(M),其中 M 表示 1 的个数。
|
||||
|
||||
|
||||
```java
|
||||
public int NumberOf1(int n) {
|
||||
int cnt = 0;
|
||||
while (n != 0) {
|
||||
cnt++;
|
||||
n &= (n - 1);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Integer.bitCount()
|
||||
|
||||
```java
|
||||
public int NumberOf1(int n) {
|
||||
return Integer.bitCount(n);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
43
docs/notes/16. 数值的整数次方.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 16. 数值的整数次方
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent,求 base 的 exponent 次方。
|
||||
|
||||
## 解题思路
|
||||
|
||||
下面的讨论中 x 代表 base,n 代表 exponent。
|
||||
|
||||
<!--<div align="center"><img src="https://latex.codecogs.com/gif.latex?x^n=\left\{\begin{array}{rcl}(x*x)^{n/2}&&{n\%2=0}\\x*(x*x)^{n/2}&&{n\%2=1}\end{array}\right." class="mathjax-pic"/></div> <br>-->
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/48b1d459-8832-4e92-938a-728aae730739.jpg" width="330px"> </div><br>
|
||||
|
||||
|
||||
因为 (x\*x)<sup>n/2</sup> 可以通过递归求解,并且每次递归 n 都减小一半,因此整个算法的时间复杂度为 O(logN)。
|
||||
|
||||
```java
|
||||
public double Power(double base, int exponent) {
|
||||
if (exponent == 0)
|
||||
return 1;
|
||||
if (exponent == 1)
|
||||
return base;
|
||||
boolean isNegative = false;
|
||||
if (exponent < 0) {
|
||||
exponent = -exponent;
|
||||
isNegative = true;
|
||||
}
|
||||
double pow = Power(base * base, exponent / 2);
|
||||
if (exponent % 2 != 0)
|
||||
pow = pow * base;
|
||||
return isNegative ? 1 / pow : pow;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/17. 打印从 1 到最大的 n 位数.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 17. 打印从 1 到最大的 n 位数
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数即 999。
|
||||
|
||||
## 解题思路
|
||||
|
||||
由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。
|
||||
|
||||
使用回溯法得到所有的数。
|
||||
|
||||
```java
|
||||
public void print1ToMaxOfNDigits(int n) {
|
||||
if (n <= 0)
|
||||
return;
|
||||
char[] number = new char[n];
|
||||
print1ToMaxOfNDigits(number, 0);
|
||||
}
|
||||
|
||||
private void print1ToMaxOfNDigits(char[] number, int digit) {
|
||||
if (digit == number.length) {
|
||||
printNumber(number);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
number[digit] = (char) (i + '0');
|
||||
print1ToMaxOfNDigits(number, digit + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void printNumber(char[] number) {
|
||||
int index = 0;
|
||||
while (index < number.length && number[index] == '0')
|
||||
index++;
|
||||
while (index < number.length)
|
||||
System.out.print(number[index++]);
|
||||
System.out.println();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
44
docs/notes/18.1 在 O(1) 时间内删除链表节点.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# 18.1 在 O(1) 时间内删除链表节点
|
||||
|
||||
## 解题思路
|
||||
|
||||
① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/1176f9e1-3442-4808-a47a-76fbaea1b806.png" width="600"/> </div><br>
|
||||
|
||||
② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png" width="600"/> </div><br>
|
||||
|
||||
综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。
|
||||
|
||||
```java
|
||||
public ListNode deleteNode(ListNode head, ListNode tobeDelete) {
|
||||
if (head == null || tobeDelete == null)
|
||||
return null;
|
||||
if (tobeDelete.next != null) {
|
||||
// 要删除的节点不是尾节点
|
||||
ListNode next = tobeDelete.next;
|
||||
tobeDelete.val = next.val;
|
||||
tobeDelete.next = next.next;
|
||||
} else {
|
||||
if (head == tobeDelete)
|
||||
// 只有一个节点
|
||||
head = null;
|
||||
else {
|
||||
ListNode cur = head;
|
||||
while (cur.next != tobeDelete)
|
||||
cur = cur.next;
|
||||
cur.next = null;
|
||||
}
|
||||
}
|
||||
return head;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
32
docs/notes/18.2 删除链表中重复的结点.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# 18.2 删除链表中重复的结点
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?tpId=13&tqId=11209&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/17e301df-52e8-4886-b593-841a16d13e44.png" width="450"/> </div><br>
|
||||
|
||||
## 解题描述
|
||||
|
||||
```java
|
||||
public ListNode deleteDuplication(ListNode pHead) {
|
||||
if (pHead == null || pHead.next == null)
|
||||
return pHead;
|
||||
ListNode next = pHead.next;
|
||||
if (pHead.val == next.val) {
|
||||
while (next != null && pHead.val == next.val)
|
||||
next = next.next;
|
||||
return deleteDuplication(next);
|
||||
} else {
|
||||
pHead.next = deleteDuplication(pHead.next);
|
||||
return pHead;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/19. 正则表达式匹配.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 19. 正则表达式匹配
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/45327ae22b7b413ea21df13ee7d6429c?tpId=13&tqId=11205&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。
|
||||
|
||||
在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。
|
||||
|
||||
## 解题思路
|
||||
|
||||
应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。
|
||||
|
||||
```java
|
||||
public boolean match(char[] str, char[] pattern) {
|
||||
|
||||
int m = str.length, n = pattern.length;
|
||||
boolean[][] dp = new boolean[m + 1][n + 1];
|
||||
|
||||
dp[0][0] = true;
|
||||
for (int i = 1; i <= n; i++)
|
||||
if (pattern[i - 1] == '*')
|
||||
dp[0][i] = dp[0][i - 2];
|
||||
|
||||
for (int i = 1; i <= m; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
if (str[i - 1] == pattern[j - 1] || pattern[j - 1] == '.')
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
else if (pattern[j - 1] == '*')
|
||||
if (pattern[j - 2] == str[i - 1] || pattern[j - 2] == '.') {
|
||||
dp[i][j] |= dp[i][j - 1]; // a* counts as single a
|
||||
dp[i][j] |= dp[i - 1][j]; // a* counts as multiple a
|
||||
dp[i][j] |= dp[i][j - 2]; // a* counts as empty
|
||||
} else
|
||||
dp[i][j] = dp[i][j - 2]; // a* only counts as empty
|
||||
|
||||
return dp[m][n];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
56
docs/notes/20. 表示数值的字符串.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# 20. 表示数值的字符串
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6f8c901d091949a5837e24bb82a731f2?tpId=13&tqId=11206&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
```
|
||||
true
|
||||
|
||||
"+100"
|
||||
"5e2"
|
||||
"-123"
|
||||
"3.1416"
|
||||
"-1E-16"
|
||||
```
|
||||
|
||||
```
|
||||
false
|
||||
|
||||
"12e"
|
||||
"1a3.14"
|
||||
"1.2.3"
|
||||
"+-5"
|
||||
"12e+4.3"
|
||||
```
|
||||
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用正则表达式进行匹配。
|
||||
|
||||
```html
|
||||
[] : 字符集合
|
||||
() : 分组
|
||||
? : 重复 0 ~ 1 次
|
||||
+ : 重复 1 ~ n 次
|
||||
* : 重复 0 ~ n 次
|
||||
. : 任意字符
|
||||
\\. : 转义后的 .
|
||||
\\d : 数字
|
||||
```
|
||||
|
||||
```java
|
||||
public boolean isNumeric(char[] str) {
|
||||
if (str == null || str.length == 0)
|
||||
return false;
|
||||
return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
67
docs/notes/21. 调整数组顺序使奇数位于偶数前面.md
Normal file
|
@ -0,0 +1,67 @@
|
|||
# 21. 调整数组顺序使奇数位于偶数前面
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/beb5aa231adc45b2a5dcc5b62c93f593?tpId=13&tqId=11166&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png" width="200px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
方法一:创建一个新数组,时间复杂度 O(N),空间复杂度 O(N)。
|
||||
|
||||
```java
|
||||
public void reOrderArray(int[] nums) {
|
||||
// 奇数个数
|
||||
int oddCnt = 0;
|
||||
for (int x : nums)
|
||||
if (!isEven(x))
|
||||
oddCnt++;
|
||||
int[] copy = nums.clone();
|
||||
int i = 0, j = oddCnt;
|
||||
for (int num : copy) {
|
||||
if (num % 2 == 1)
|
||||
nums[i++] = num;
|
||||
else
|
||||
nums[j++] = num;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEven(int x) {
|
||||
return x % 2 == 0;
|
||||
}
|
||||
```
|
||||
|
||||
方法二:使用冒泡思想,每次都当前偶数上浮到当前最右边。时间复杂度 O(N<sup>2</sup>),空间复杂度 O(1),时间换空间。
|
||||
|
||||
```java
|
||||
public void reOrderArray(int[] nums) {
|
||||
int N = nums.length;
|
||||
for (int i = N - 1; i > 0; i--) {
|
||||
for (int j = 0; j < i; j++) {
|
||||
if (isEven(nums[j]) && !isEven(nums[j + 1])) {
|
||||
swap(nums, j, j + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isEven(int x) {
|
||||
return x % 2 == 0;
|
||||
}
|
||||
|
||||
private void swap(int[] nums, int i, int j) {
|
||||
int t = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/22. 链表中倒数第 K 个结点.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 22. 链表中倒数第 K 个结点
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
设链表的长度为 N。设置两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png" width="500"/> </div><br>
|
||||
|
||||
```java
|
||||
public ListNode FindKthToTail(ListNode head, int k) {
|
||||
if (head == null)
|
||||
return null;
|
||||
ListNode P1 = head;
|
||||
while (P1 != null && k-- > 0)
|
||||
P1 = P1.next;
|
||||
if (k > 0)
|
||||
return null;
|
||||
ListNode P2 = head;
|
||||
while (P1 != null) {
|
||||
P1 = P1.next;
|
||||
P2 = P2.next;
|
||||
}
|
||||
return P2;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
40
docs/notes/23. 链表中环的入口结点.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# 23. 链表中环的入口结点
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?tpId=13&tqId=11208&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
一个链表中包含环,请找出该链表的环的入口结点。要求不能使用额外的空间。
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用双指针,一个指针 fast 每次移动两个节点,一个指针 slow 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。假设相遇点在下图的 z1 位置,此时 fast 移动的节点数为 x+2y+z,slow 为 x+y,由于 fast 速度比 slow 快一倍,因此 x+2y+z=2(x+y),得到 x=z。
|
||||
|
||||
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png" width="500"/> </div><br>
|
||||
|
||||
```java
|
||||
public ListNode EntryNodeOfLoop(ListNode pHead) {
|
||||
if (pHead == null || pHead.next == null)
|
||||
return null;
|
||||
ListNode slow = pHead, fast = pHead;
|
||||
do {
|
||||
fast = fast.next.next;
|
||||
slow = slow.next;
|
||||
} while (slow != fast);
|
||||
fast = pHead;
|
||||
while (slow != fast) {
|
||||
slow = slow.next;
|
||||
fast = fast.next;
|
||||
}
|
||||
return slow;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
43
docs/notes/24. 反转链表.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 24. 反转链表
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId=13&tqId=11168&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 递归
|
||||
|
||||
```java
|
||||
public ListNode ReverseList(ListNode head) {
|
||||
if (head == null || head.next == null)
|
||||
return head;
|
||||
ListNode next = head.next;
|
||||
head.next = null;
|
||||
ListNode newHead = ReverseList(next);
|
||||
next.next = head;
|
||||
return newHead;
|
||||
}
|
||||
```
|
||||
|
||||
### 迭代
|
||||
|
||||
使用头插法。
|
||||
|
||||
```java
|
||||
public ListNode ReverseList(ListNode head) {
|
||||
ListNode newList = new ListNode(-1);
|
||||
while (head != null) {
|
||||
ListNode next = head.next;
|
||||
head.next = newList.next;
|
||||
newList.next = head;
|
||||
head = next;
|
||||
}
|
||||
return newList.next;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
58
docs/notes/25. 合并两个排序的链表.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# 25. 合并两个排序的链表
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png" width="400"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 递归
|
||||
|
||||
```java
|
||||
public ListNode Merge(ListNode list1, ListNode list2) {
|
||||
if (list1 == null)
|
||||
return list2;
|
||||
if (list2 == null)
|
||||
return list1;
|
||||
if (list1.val <= list2.val) {
|
||||
list1.next = Merge(list1.next, list2);
|
||||
return list1;
|
||||
} else {
|
||||
list2.next = Merge(list1, list2.next);
|
||||
return list2;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 迭代
|
||||
|
||||
```java
|
||||
public ListNode Merge(ListNode list1, ListNode list2) {
|
||||
ListNode head = new ListNode(-1);
|
||||
ListNode cur = head;
|
||||
while (list1 != null && list2 != null) {
|
||||
if (list1.val <= list2.val) {
|
||||
cur.next = list1;
|
||||
list1 = list1.next;
|
||||
} else {
|
||||
cur.next = list2;
|
||||
list2 = list2.next;
|
||||
}
|
||||
cur = cur.next;
|
||||
}
|
||||
if (list1 != null)
|
||||
cur.next = list1;
|
||||
if (list2 != null)
|
||||
cur.next = list2;
|
||||
return head.next;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/26. 树的子结构.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 26. 树的子结构
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6e196c44c7004d15b1610b9afca8bd88?tpId=13&tqId=11170&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg" width="450"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
|
||||
if (root1 == null || root2 == null)
|
||||
return false;
|
||||
return isSubtreeWithRoot(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
|
||||
}
|
||||
|
||||
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) {
|
||||
if (root2 == null)
|
||||
return true;
|
||||
if (root1 == null)
|
||||
return false;
|
||||
if (root1.val != root2.val)
|
||||
return false;
|
||||
return isSubtreeWithRoot(root1.left, root2.left) && isSubtreeWithRoot(root1.right, root2.right);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
32
docs/notes/27. 二叉树的镜像.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# 27. 二叉树的镜像
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/564f4c26aa584921bc75623e48ca3011?tpId=13&tqId=11171&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg" width="300"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public void Mirror(TreeNode root) {
|
||||
if (root == null)
|
||||
return;
|
||||
swap(root);
|
||||
Mirror(root.left);
|
||||
Mirror(root.right);
|
||||
}
|
||||
|
||||
private void swap(TreeNode root) {
|
||||
TreeNode t = root.left;
|
||||
root.left = root.right;
|
||||
root.right = t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/28. 对称的二叉树.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 28. 对称的二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg" width="300"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
boolean isSymmetrical(TreeNode pRoot) {
|
||||
if (pRoot == null)
|
||||
return true;
|
||||
return isSymmetrical(pRoot.left, pRoot.right);
|
||||
}
|
||||
|
||||
boolean isSymmetrical(TreeNode t1, TreeNode t2) {
|
||||
if (t1 == null && t2 == null)
|
||||
return true;
|
||||
if (t1 == null || t2 == null)
|
||||
return false;
|
||||
if (t1.val != t2.val)
|
||||
return false;
|
||||
return isSymmetrical(t1.left, t2.right) && isSymmetrical(t1.right, t2.left);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
39
docs/notes/29. 顺时针打印矩阵.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# 29. 顺时针打印矩阵
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a?tpId=13&tqId=11172&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/48517227-324c-4664-bd26-a2d2cffe2bfe.png" width="200px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printMatrix(int[][] matrix) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
|
||||
while (r1 <= r2 && c1 <= c2) {
|
||||
for (int i = c1; i <= c2; i++)
|
||||
ret.add(matrix[r1][i]);
|
||||
for (int i = r1 + 1; i <= r2; i++)
|
||||
ret.add(matrix[i][c2]);
|
||||
if (r1 != r2)
|
||||
for (int i = c2 - 1; i >= c1; i--)
|
||||
ret.add(matrix[r2][i]);
|
||||
if (c1 != c2)
|
||||
for (int i = r2 - 1; i > r1; i--)
|
||||
ret.add(matrix[i][c1]);
|
||||
r1++; r2--; c1++; c2--;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
58
docs/notes/3. 数组中重复的数字.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# 3. 数组中重复的数字
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/623a5ac0ea5b4e5f95552655361ae0a8?tpId=13&tqId=11203&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
|
||||
|
||||
```html
|
||||
Input:
|
||||
{2, 3, 1, 0, 2, 5}
|
||||
|
||||
Output:
|
||||
2
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
要求时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。
|
||||
|
||||
对于这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。本题要求找出重复的数字,因此在调整过程中,如果第 i 位置上已经有一个值为 i 的元素,就可以知道 i 值重复。
|
||||
|
||||
以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复:
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/643b6f18-f933-4ac5-aa7a-e304dbd7fe49.gif" width="350px"> </div><br>
|
||||
|
||||
|
||||
```java
|
||||
public boolean duplicate(int[] nums, int length, int[] duplication) {
|
||||
if (nums == null || length <= 0)
|
||||
return false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
while (nums[i] != i) {
|
||||
if (nums[i] == nums[nums[i]]) {
|
||||
duplication[0] = nums[i];
|
||||
return true;
|
||||
}
|
||||
swap(nums, i, nums[i]);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void swap(int[] nums, int i, int j) {
|
||||
int t = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
39
docs/notes/30. 包含 min 函数的栈.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# 30. 包含 min 函数的栈
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/4c776177d2c04c2494f2555c9fcc1e49?tpId=13&tqId=11173&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private Stack<Integer> dataStack = new Stack<>();
|
||||
private Stack<Integer> minStack = new Stack<>();
|
||||
|
||||
public void push(int node) {
|
||||
dataStack.push(node);
|
||||
minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
|
||||
}
|
||||
|
||||
public void pop() {
|
||||
dataStack.pop();
|
||||
minStack.pop();
|
||||
}
|
||||
|
||||
public int top() {
|
||||
return dataStack.peek();
|
||||
}
|
||||
|
||||
public int min() {
|
||||
return minStack.peek();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
36
docs/notes/31. 栈的压入、弹出序列.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# 31. 栈的压入、弹出序列
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&tqId=11174&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。
|
||||
|
||||
例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用一个栈来模拟压入弹出操作。
|
||||
|
||||
```java
|
||||
public boolean IsPopOrder(int[] pushSequence, int[] popSequence) {
|
||||
int n = pushSequence.length;
|
||||
Stack<Integer> stack = new Stack<>();
|
||||
for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
|
||||
stack.push(pushSequence[pushIndex]);
|
||||
while (popIndex < n && !stack.isEmpty()
|
||||
&& stack.peek() == popSequence[popIndex]) {
|
||||
stack.pop();
|
||||
popIndex++;
|
||||
}
|
||||
}
|
||||
return stack.isEmpty();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
44
docs/notes/32.1 从上往下打印二叉树.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# 32.1 从上往下打印二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/7fe2212963db4790b57431d9ed259701?tpId=13&tqId=11175&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
|
||||
|
||||
例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg" width="250"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用队列来进行层次遍历。
|
||||
|
||||
不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
queue.add(root);
|
||||
while (!queue.isEmpty()) {
|
||||
int cnt = queue.size();
|
||||
while (cnt-- > 0) {
|
||||
TreeNode t = queue.poll();
|
||||
if (t == null)
|
||||
continue;
|
||||
ret.add(t.val);
|
||||
queue.add(t.left);
|
||||
queue.add(t.right);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
39
docs/notes/32.2 把二叉树打印成多行.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
# 32.2 把二叉树打印成多行
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/445c44d982d04483b04a54f298796288?tpId=13&tqId=11213&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
和上题几乎一样。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
|
||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
queue.add(pRoot);
|
||||
while (!queue.isEmpty()) {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
int cnt = queue.size();
|
||||
while (cnt-- > 0) {
|
||||
TreeNode node = queue.poll();
|
||||
if (node == null)
|
||||
continue;
|
||||
list.add(node.val);
|
||||
queue.add(node.left);
|
||||
queue.add(node.right);
|
||||
}
|
||||
if (list.size() != 0)
|
||||
ret.add(list);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
43
docs/notes/32.3 按之字形顺序打印二叉树.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 32.3 按之字形顺序打印二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0?tpId=13&tqId=11212&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
|
||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||
Queue<TreeNode> queue = new LinkedList<>();
|
||||
queue.add(pRoot);
|
||||
boolean reverse = false;
|
||||
while (!queue.isEmpty()) {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
int cnt = queue.size();
|
||||
while (cnt-- > 0) {
|
||||
TreeNode node = queue.poll();
|
||||
if (node == null)
|
||||
continue;
|
||||
list.add(node.val);
|
||||
queue.add(node.left);
|
||||
queue.add(node.right);
|
||||
}
|
||||
if (reverse)
|
||||
Collections.reverse(list);
|
||||
reverse = !reverse;
|
||||
if (list.size() != 0)
|
||||
ret.add(list);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
41
docs/notes/33. 二叉搜索树的后序遍历序列.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# 33. 二叉搜索树的后序遍历序列
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/a861533d45854474ac791d90e447bafd?tpId=13&tqId=11176&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。假设输入的数组的任意两个数字都互不相同。
|
||||
|
||||
例如,下图是后序遍历序列 1,3,2 所对应的二叉搜索树。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/13454fa1-23a8-4578-9663-2b13a6af564a.jpg" width="150"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public boolean VerifySquenceOfBST(int[] sequence) {
|
||||
if (sequence == null || sequence.length == 0)
|
||||
return false;
|
||||
return verify(sequence, 0, sequence.length - 1);
|
||||
}
|
||||
|
||||
private boolean verify(int[] sequence, int first, int last) {
|
||||
if (last - first <= 1)
|
||||
return true;
|
||||
int rootVal = sequence[last];
|
||||
int cutIndex = first;
|
||||
while (cutIndex < last && sequence[cutIndex] <= rootVal)
|
||||
cutIndex++;
|
||||
for (int i = cutIndex; i < last; i++)
|
||||
if (sequence[i] < rootVal)
|
||||
return false;
|
||||
return verify(sequence, first, cutIndex - 1) && verify(sequence, cutIndex, last - 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
43
docs/notes/34. 二叉树中和为某一值的路径.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 34. 二叉树中和为某一值的路径
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
|
||||
|
||||
下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg" width="200"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||
|
||||
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
|
||||
backtracking(root, target, new ArrayList<>());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void backtracking(TreeNode node, int target, ArrayList<Integer> path) {
|
||||
if (node == null)
|
||||
return;
|
||||
path.add(node.val);
|
||||
target -= node.val;
|
||||
if (target == 0 && node.left == null && node.right == null) {
|
||||
ret.add(new ArrayList<>(path));
|
||||
} else {
|
||||
backtracking(node.left, target, path);
|
||||
backtracking(node.right, target, path);
|
||||
}
|
||||
path.remove(path.size() - 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
74
docs/notes/35. 复杂链表的复制.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# 35. 复杂链表的复制
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba?tpId=13&tqId=11178&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
|
||||
|
||||
```java
|
||||
public class RandomListNode {
|
||||
int label;
|
||||
RandomListNode next = null;
|
||||
RandomListNode random = null;
|
||||
|
||||
RandomListNode(int label) {
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/66a01953-5303-43b1-8646-0c77b825e980.png" width="300"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
第一步,在每个节点的后面插入复制的节点。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png" width="600"/> </div><br>
|
||||
|
||||
第二步,对复制节点的 random 链接进行赋值。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png" width="600"/> </div><br>
|
||||
|
||||
第三步,拆分。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/e151b5df-5390-4365-b66e-b130cd253c12.png" width="600"/> </div><br>
|
||||
|
||||
```java
|
||||
public RandomListNode Clone(RandomListNode pHead) {
|
||||
if (pHead == null)
|
||||
return null;
|
||||
// 插入新节点
|
||||
RandomListNode cur = pHead;
|
||||
while (cur != null) {
|
||||
RandomListNode clone = new RandomListNode(cur.label);
|
||||
clone.next = cur.next;
|
||||
cur.next = clone;
|
||||
cur = clone.next;
|
||||
}
|
||||
// 建立 random 链接
|
||||
cur = pHead;
|
||||
while (cur != null) {
|
||||
RandomListNode clone = cur.next;
|
||||
if (cur.random != null)
|
||||
clone.random = cur.random.next;
|
||||
cur = clone.next;
|
||||
}
|
||||
// 拆分
|
||||
cur = pHead;
|
||||
RandomListNode pCloneHead = pHead.next;
|
||||
while (cur.next != null) {
|
||||
RandomListNode next = cur.next;
|
||||
cur.next = next.next;
|
||||
cur = next;
|
||||
}
|
||||
return pCloneHead;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
41
docs/notes/36. 二叉搜索树与双向链表.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# 36. 二叉搜索树与双向链表
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg" width="400"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private TreeNode pre = null;
|
||||
private TreeNode head = null;
|
||||
|
||||
public TreeNode Convert(TreeNode root) {
|
||||
inOrder(root);
|
||||
return head;
|
||||
}
|
||||
|
||||
private void inOrder(TreeNode node) {
|
||||
if (node == null)
|
||||
return;
|
||||
inOrder(node.left);
|
||||
node.left = pre;
|
||||
if (pre != null)
|
||||
pre.right = node;
|
||||
pre = node;
|
||||
if (head == null)
|
||||
head = node;
|
||||
inOrder(node.right);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
46
docs/notes/37. 序列化二叉树.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# 37. 序列化二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/cf7e25aa97c04cc1a68c8f040e71fb84?tpId=13&tqId=11214&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
请实现两个函数,分别用来序列化和反序列化二叉树。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private String deserializeStr;
|
||||
|
||||
public String Serialize(TreeNode root) {
|
||||
if (root == null)
|
||||
return "#";
|
||||
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
|
||||
}
|
||||
|
||||
public TreeNode Deserialize(String str) {
|
||||
deserializeStr = str;
|
||||
return Deserialize();
|
||||
}
|
||||
|
||||
private TreeNode Deserialize() {
|
||||
if (deserializeStr.length() == 0)
|
||||
return null;
|
||||
int index = deserializeStr.indexOf(" ");
|
||||
String node = index == -1 ? deserializeStr : deserializeStr.substring(0, index);
|
||||
deserializeStr = index == -1 ? "" : deserializeStr.substring(index + 1);
|
||||
if (node.equals("#"))
|
||||
return null;
|
||||
int val = Integer.valueOf(node);
|
||||
TreeNode t = new TreeNode(val);
|
||||
t.left = Deserialize();
|
||||
t.right = Deserialize();
|
||||
return t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/38. 字符串的排列.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 38. 字符串的排列
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private ArrayList<String> ret = new ArrayList<>();
|
||||
|
||||
public ArrayList<String> Permutation(String str) {
|
||||
if (str.length() == 0)
|
||||
return ret;
|
||||
char[] chars = str.toCharArray();
|
||||
Arrays.sort(chars);
|
||||
backtracking(chars, new boolean[chars.length], new StringBuilder());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
|
||||
if (s.length() == chars.length) {
|
||||
ret.add(s.toString());
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (hasUsed[i])
|
||||
continue;
|
||||
if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) /* 保证不重复 */
|
||||
continue;
|
||||
hasUsed[i] = true;
|
||||
s.append(chars[i]);
|
||||
backtracking(chars, hasUsed, s);
|
||||
s.deleteCharAt(s.length() - 1);
|
||||
hasUsed[i] = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/39. 数组中出现次数超过一半的数字.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 39. 数组中出现次数超过一半的数字
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&tqId=11181&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。
|
||||
|
||||
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素相等时,令 cnt++,否则令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
|
||||
|
||||
```java
|
||||
public int MoreThanHalfNum_Solution(int[] nums) {
|
||||
int majority = nums[0];
|
||||
for (int i = 1, cnt = 1; i < nums.length; i++) {
|
||||
cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
|
||||
if (cnt == 0) {
|
||||
majority = nums[i];
|
||||
cnt = 1;
|
||||
}
|
||||
}
|
||||
int cnt = 0;
|
||||
for (int val : nums)
|
||||
if (val == majority)
|
||||
cnt++;
|
||||
return cnt > nums.length / 2 ? majority : 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
56
docs/notes/4. 二维数组中的查找.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# 4. 二维数组中的查找
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个二维数组,其每一行从左到右递增排序,从上到下也是递增排序。给定一个数,判断这个数是否在该二维数组中。
|
||||
|
||||
```html
|
||||
Consider the following matrix:
|
||||
[
|
||||
[1, 4, 7, 11, 15],
|
||||
[2, 5, 8, 12, 19],
|
||||
[3, 6, 9, 16, 22],
|
||||
[10, 13, 14, 17, 24],
|
||||
[18, 21, 23, 26, 30]
|
||||
]
|
||||
|
||||
Given target = 5, return true.
|
||||
Given target = 20, return false.
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
要求时间复杂度 O(M + N),空间复杂度 O(1)。其中 M 为行数,N 为 列数。
|
||||
|
||||
该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/35a8c711-0dc0-4613-95f3-be96c6c6e104.gif" width="400px"> </div><br>
|
||||
|
||||
```java
|
||||
public boolean Find(int target, int[][] matrix) {
|
||||
if (matrix == null || matrix.length == 0 || matrix[0].length == 0)
|
||||
return false;
|
||||
int rows = matrix.length, cols = matrix[0].length;
|
||||
int r = 0, c = cols - 1; // 从右上角开始
|
||||
while (r <= rows - 1 && c >= 0) {
|
||||
if (target == matrix[r][c])
|
||||
return true;
|
||||
else if (target > matrix[r][c])
|
||||
r++;
|
||||
else
|
||||
c--;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
88
docs/notes/40. 最小的 K 个数.md
Normal file
|
@ -0,0 +1,88 @@
|
|||
# 40. 最小的 K 个数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 快速选择
|
||||
|
||||
- 复杂度:O(N) + O(1)
|
||||
- 只有当允许修改数组元素时才可以使用
|
||||
|
||||
快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
if (k > nums.length || k <= 0)
|
||||
return ret;
|
||||
findKthSmallest(nums, k - 1);
|
||||
/* findKthSmallest 会改变数组,使得前 k 个数都是最小的 k 个数 */
|
||||
for (int i = 0; i < k; i++)
|
||||
ret.add(nums[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void findKthSmallest(int[] nums, int k) {
|
||||
int l = 0, h = nums.length - 1;
|
||||
while (l < h) {
|
||||
int j = partition(nums, l, h);
|
||||
if (j == k)
|
||||
break;
|
||||
if (j > k)
|
||||
h = j - 1;
|
||||
else
|
||||
l = j + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private int partition(int[] nums, int l, int h) {
|
||||
int p = nums[l]; /* 切分元素 */
|
||||
int i = l, j = h + 1;
|
||||
while (true) {
|
||||
while (i != h && nums[++i] < p) ;
|
||||
while (j != l && nums[--j] > p) ;
|
||||
if (i >= j)
|
||||
break;
|
||||
swap(nums, i, j);
|
||||
}
|
||||
swap(nums, l, j);
|
||||
return j;
|
||||
}
|
||||
|
||||
private void swap(int[] nums, int i, int j) {
|
||||
int t = nums[i];
|
||||
nums[i] = nums[j];
|
||||
nums[j] = t;
|
||||
}
|
||||
```
|
||||
|
||||
### 大小为 K 的最小堆
|
||||
|
||||
- 复杂度:O(NlogK) + O(K)
|
||||
- 特别适合处理海量数据
|
||||
|
||||
应该使用大顶堆来维护最小堆,而不能直接创建一个小顶堆并设置一个大小,企图让小顶堆中的元素都是最小元素。
|
||||
|
||||
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
|
||||
if (k > nums.length || k <= 0)
|
||||
return new ArrayList<>();
|
||||
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
|
||||
for (int num : nums) {
|
||||
maxHeap.add(num);
|
||||
if (maxHeap.size() > k)
|
||||
maxHeap.poll();
|
||||
}
|
||||
return new ArrayList<>(maxHeap);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
47
docs/notes/41.1 数据流中的中位数.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
# 41.1 数据流中的中位数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1?tpId=13&tqId=11216&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
/* 大顶堆,存储左半边元素 */
|
||||
private PriorityQueue<Integer> left = new PriorityQueue<>((o1, o2) -> o2 - o1);
|
||||
/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */
|
||||
private PriorityQueue<Integer> right = new PriorityQueue<>();
|
||||
/* 当前数据流读入的元素个数 */
|
||||
private int N = 0;
|
||||
|
||||
public void Insert(Integer val) {
|
||||
/* 插入要保证两个堆存于平衡状态 */
|
||||
if (N % 2 == 0) {
|
||||
/* N 为偶数的情况下插入到右半边。
|
||||
* 因为右半边元素都要大于左半边,但是新插入的元素不一定比左半边元素来的大,
|
||||
* 因此需要先将元素插入左半边,然后利用左半边为大顶堆的特点,取出堆顶元素即为最大元素,此时插入右半边 */
|
||||
left.add(val);
|
||||
right.add(left.poll());
|
||||
} else {
|
||||
right.add(val);
|
||||
left.add(right.poll());
|
||||
}
|
||||
N++;
|
||||
}
|
||||
|
||||
public Double GetMedian() {
|
||||
if (N % 2 == 0)
|
||||
return (left.peek() + right.peek()) / 2.0;
|
||||
else
|
||||
return (double) right.peek();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
32
docs/notes/41.2 字符流中第一个不重复的字符.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# 41.2 字符流中第一个不重复的字符
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/00de97733b8e4f97a3fb5c680ee10720?tpId=13&tqId=11207&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private int[] cnts = new int[256];
|
||||
private Queue<Character> queue = new LinkedList<>();
|
||||
|
||||
public void Insert(char ch) {
|
||||
cnts[ch]++;
|
||||
queue.add(ch);
|
||||
while (!queue.isEmpty() && cnts[queue.peek()] > 1)
|
||||
queue.poll();
|
||||
}
|
||||
|
||||
public char FirstAppearingOnce() {
|
||||
return queue.isEmpty() ? '#' : queue.peek();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
31
docs/notes/42. 连续子数组的最大和.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 42. 连续子数组的最大和
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&tqId=11183&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
{6, -3, -2, 7, -15, 1, 2, 2},连续子数组的最大和为 8(从第 0 个开始,到第 3 个为止)。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int FindGreatestSumOfSubArray(int[] nums) {
|
||||
if (nums == null || nums.length == 0)
|
||||
return 0;
|
||||
int greatestSum = Integer.MIN_VALUE;
|
||||
int sum = 0;
|
||||
for (int val : nums) {
|
||||
sum = sum <= 0 ? val : sum + val;
|
||||
greatestSum = Math.max(greatestSum, sum);
|
||||
}
|
||||
return greatestSum;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
25
docs/notes/43. 从 1 到 n 整数中 1 出现的次数.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# 43. 从 1 到 n 整数中 1 出现的次数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/bd7f978302044eee894445e244c7eee6?tpId=13&tqId=11184&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int NumberOf1Between1AndN_Solution(int n) {
|
||||
int cnt = 0;
|
||||
for (int m = 1; m <= n; m *= 10) {
|
||||
int a = n / m, b = n % m;
|
||||
cnt += (a + 8) / 10 * m + (a % 10 == 1 ? b + 1 : 0);
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
```
|
||||
|
||||
> [Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
61
docs/notes/44. 数字序列中的某一位数字.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# 44. 数字序列中的某一位数字
|
||||
|
||||
## 题目描述
|
||||
|
||||
数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int getDigitAtIndex(int index) {
|
||||
if (index < 0)
|
||||
return -1;
|
||||
int place = 1; // 1 表示个位,2 表示 十位...
|
||||
while (true) {
|
||||
int amount = getAmountOfPlace(place);
|
||||
int totalAmount = amount * place;
|
||||
if (index < totalAmount)
|
||||
return getDigitAtIndex(index, place);
|
||||
index -= totalAmount;
|
||||
place++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* place 位数的数字组成的字符串长度
|
||||
* 10, 90, 900, ...
|
||||
*/
|
||||
private int getAmountOfPlace(int place) {
|
||||
if (place == 1)
|
||||
return 10;
|
||||
return (int) Math.pow(10, place - 1) * 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* place 位数的起始数字
|
||||
* 0, 10, 100, ...
|
||||
*/
|
||||
private int getBeginNumberOfPlace(int place) {
|
||||
if (place == 1)
|
||||
return 0;
|
||||
return (int) Math.pow(10, place - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 place 位数组成的字符串中,第 index 个数
|
||||
*/
|
||||
private int getDigitAtIndex(int index, int place) {
|
||||
int beginNumber = getBeginNumberOfPlace(place);
|
||||
int shiftNumber = index / place;
|
||||
String number = (beginNumber + shiftNumber) + "";
|
||||
int count = index % place;
|
||||
return number.charAt(count) - '0';
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/45. 把数组排成最小的数.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 45. 把数组排成最小的数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/8fecd3f8ba334add803bf2a06af1b993?tpId=13&tqId=11185&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。
|
||||
|
||||
## 解题思路
|
||||
|
||||
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。
|
||||
|
||||
```java
|
||||
public String PrintMinNumber(int[] numbers) {
|
||||
if (numbers == null || numbers.length == 0)
|
||||
return "";
|
||||
int n = numbers.length;
|
||||
String[] nums = new String[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
nums[i] = numbers[i] + "";
|
||||
Arrays.sort(nums, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
|
||||
String ret = "";
|
||||
for (String str : nums)
|
||||
ret += str;
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
38
docs/notes/46. 把数字翻译成字符串.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# 46. 把数字翻译成字符串
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/decode-ways/description/)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个数字,按照如下规则翻译成字符串:1 翻译成“a”,2 翻译成“b”... 26 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 abbeh,lbeh,aveh,abyh,lyh。实现一个函数,用来计算一个数字有多少种不同的翻译方法。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int numDecodings(String s) {
|
||||
if (s == null || s.length() == 0)
|
||||
return 0;
|
||||
int n = s.length();
|
||||
int[] dp = new int[n + 1];
|
||||
dp[0] = 1;
|
||||
dp[1] = s.charAt(0) == '0' ? 0 : 1;
|
||||
for (int i = 2; i <= n; i++) {
|
||||
int one = Integer.valueOf(s.substring(i - 1, i));
|
||||
if (one != 0)
|
||||
dp[i] += dp[i - 1];
|
||||
if (s.charAt(i - 2) == '0')
|
||||
continue;
|
||||
int two = Integer.valueOf(s.substring(i - 2, i));
|
||||
if (two <= 26)
|
||||
dp[i] += dp[i - 2];
|
||||
}
|
||||
return dp[n];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
42
docs/notes/47. 礼物的最大价值.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# 47. 礼物的最大价值
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/questionTerminal/72a99e28381a407991f2c96d8cb238ab)
|
||||
|
||||
## 题目描述
|
||||
|
||||
在一个 m\*n 的棋盘的每一个格都放有一个礼物,每个礼物都有一定价值(大于 0)。从左上角开始拿礼物,每次向右或向下移动一格,直到右下角结束。给定一个棋盘,求拿到礼物的最大价值。例如,对于如下棋盘
|
||||
|
||||
```
|
||||
1 10 3 8
|
||||
12 2 9 6
|
||||
5 7 4 11
|
||||
3 7 16 5
|
||||
```
|
||||
|
||||
礼物的最大价值为 1+12+5+7+7+16+5=53。
|
||||
|
||||
## 解题思路
|
||||
|
||||
应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。
|
||||
|
||||
```java
|
||||
public int getMost(int[][] values) {
|
||||
if (values == null || values.length == 0 || values[0].length == 0)
|
||||
return 0;
|
||||
int n = values[0].length;
|
||||
int[] dp = new int[n];
|
||||
for (int[] value : values) {
|
||||
dp[0] += value[0];
|
||||
for (int i = 1; i < n; i++)
|
||||
dp[i] = Math.max(dp[i], dp[i - 1]) + value[i];
|
||||
}
|
||||
return dp[n - 1];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
36
docs/notes/48. 最长不含重复字符的子字符串.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# 48. 最长不含重复字符的子字符串
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个字符串(只包含 a\~z 的字符),求其最长不含重复字符的子字符串的长度。例如对于 arabcacfr,最长不含重复字符的子字符串为 acfr,长度为 4。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int longestSubStringWithoutDuplication(String str) {
|
||||
int curLen = 0;
|
||||
int maxLen = 0;
|
||||
int[] preIndexs = new int[26];
|
||||
Arrays.fill(preIndexs, -1);
|
||||
for (int curI = 0; curI < str.length(); curI++) {
|
||||
int c = str.charAt(curI) - 'a';
|
||||
int preI = preIndexs[c];
|
||||
if (preI == -1 || curI - preI > curLen) {
|
||||
curLen++;
|
||||
} else {
|
||||
maxLen = Math.max(maxLen, curLen);
|
||||
curLen = curI - preI;
|
||||
}
|
||||
preIndexs[c] = curI;
|
||||
}
|
||||
maxLen = Math.max(maxLen, curLen);
|
||||
return maxLen;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
37
docs/notes/49. 丑数.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# 49. 丑数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b?tpId=13&tqId=11186&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int GetUglyNumber_Solution(int N) {
|
||||
if (N <= 6)
|
||||
return N;
|
||||
int i2 = 0, i3 = 0, i5 = 0;
|
||||
int[] dp = new int[N];
|
||||
dp[0] = 1;
|
||||
for (int i = 1; i < N; i++) {
|
||||
int next2 = dp[i2] * 2, next3 = dp[i3] * 3, next5 = dp[i5] * 5;
|
||||
dp[i] = Math.min(next2, Math.min(next3, next5));
|
||||
if (dp[i] == next2)
|
||||
i2++;
|
||||
if (dp[i] == next3)
|
||||
i3++;
|
||||
if (dp[i] == next5)
|
||||
i5++;
|
||||
}
|
||||
return dp[N - 1];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
59
docs/notes/5. 替换空格.md
Normal file
|
@ -0,0 +1,59 @@
|
|||
# 5. 替换空格
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/4060ac7e3e404ad1a894ef3e17650423?tpId=13&tqId=11155&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
|
||||
将一个字符串中的空格替换成 "%20"。
|
||||
|
||||
```text
|
||||
Input:
|
||||
"A B"
|
||||
|
||||
Output:
|
||||
"A%20B"
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
① 在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。因为一个空格要替换成三个字符(%20),所以当遍历到一个空格时,需要在尾部填充两个任意字符。
|
||||
|
||||
② 令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2 从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。
|
||||
|
||||
③ 当 P2 遇到 P1 时(P2 <= P1),或者遍历结束(P1 < 0),退出。
|
||||
|
||||
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c.gif" width="350px"> </div><br>
|
||||
|
||||
```java
|
||||
public String replaceSpace(StringBuffer str) {
|
||||
int P1 = str.length() - 1;
|
||||
for (int i = 0; i <= P1; i++)
|
||||
if (str.charAt(i) == ' ')
|
||||
str.append(" ");
|
||||
|
||||
int P2 = str.length() - 1;
|
||||
while (P1 >= 0 && P2 > P1) {
|
||||
char c = str.charAt(P1--);
|
||||
if (c == ' ') {
|
||||
str.setCharAt(P2--, '0');
|
||||
str.setCharAt(P2--, '2');
|
||||
str.setCharAt(P2--, '%');
|
||||
} else {
|
||||
str.setCharAt(P2--, c);
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
56
docs/notes/50. 第一个只出现一次的字符位置.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# 50. 第一个只出现一次的字符位置
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c?tpId=13&tqId=11187&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
在一个字符串中找到第一个只出现一次的字符,并返回它的位置。
|
||||
|
||||
```
|
||||
Input: abacc
|
||||
Output: b
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap,从而将空间复杂度由 O(N) 降低为 O(1)。
|
||||
|
||||
```java
|
||||
public int FirstNotRepeatingChar(String str) {
|
||||
int[] cnts = new int[256];
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
cnts[str.charAt(i)]++;
|
||||
for (int i = 0; i < str.length(); i++)
|
||||
if (cnts[str.charAt(i)] == 1)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
|
||||
|
||||
```java
|
||||
public int FirstNotRepeatingChar2(String str) {
|
||||
BitSet bs1 = new BitSet(256);
|
||||
BitSet bs2 = new BitSet(256);
|
||||
for (char c : str.toCharArray()) {
|
||||
if (!bs1.get(c) && !bs2.get(c))
|
||||
bs1.set(c); // 0 0 -> 0 1
|
||||
else if (bs1.get(c) && !bs2.get(c))
|
||||
bs2.set(c); // 0 1 -> 1 1
|
||||
}
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (bs1.get(c) && !bs2.get(c)) // 0 1
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
55
docs/notes/51. 数组中的逆序对.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# 51. 数组中的逆序对
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5?tpId=13&tqId=11188&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private long cnt = 0;
|
||||
private int[] tmp; // 在这里声明辅助数组,而不是在 merge() 递归函数中声明
|
||||
|
||||
public int InversePairs(int[] nums) {
|
||||
tmp = new int[nums.length];
|
||||
mergeSort(nums, 0, nums.length - 1);
|
||||
return (int) (cnt % 1000000007);
|
||||
}
|
||||
|
||||
private void mergeSort(int[] nums, int l, int h) {
|
||||
if (h - l < 1)
|
||||
return;
|
||||
int m = l + (h - l) / 2;
|
||||
mergeSort(nums, l, m);
|
||||
mergeSort(nums, m + 1, h);
|
||||
merge(nums, l, m, h);
|
||||
}
|
||||
|
||||
private void merge(int[] nums, int l, int m, int h) {
|
||||
int i = l, j = m + 1, k = l;
|
||||
while (i <= m || j <= h) {
|
||||
if (i > m)
|
||||
tmp[k] = nums[j++];
|
||||
else if (j > h)
|
||||
tmp[k] = nums[i++];
|
||||
else if (nums[i] <= nums[j])
|
||||
tmp[k] = nums[i++];
|
||||
else {
|
||||
tmp[k] = nums[j++];
|
||||
this.cnt += m - i + 1; // nums[i] > nums[j],说明 nums[i...mid] 都大于 nums[j]
|
||||
}
|
||||
k++;
|
||||
}
|
||||
for (k = l; k <= h; k++)
|
||||
nums[k] = tmp[k];
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
31
docs/notes/52. 两个链表的第一个公共结点.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 52. 两个链表的第一个公共结点
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?tpId=13&tqId=11189&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png" width="500"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。
|
||||
|
||||
当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
|
||||
|
||||
```java
|
||||
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
|
||||
ListNode l1 = pHead1, l2 = pHead2;
|
||||
while (l1 != l2) {
|
||||
l1 = (l1 == null) ? pHead2 : l1.next;
|
||||
l2 = (l2 == null) ? pHead1 : l2.next;
|
||||
}
|
||||
return l1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
43
docs/notes/53. 数字在排序数组中出现的次数.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# 53. 数字在排序数组中出现的次数
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
```html
|
||||
Input:
|
||||
nums = 1, 2, 3, 3, 3, 3, 4, 6
|
||||
K = 3
|
||||
|
||||
Output:
|
||||
4
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int GetNumberOfK(int[] nums, int K) {
|
||||
int first = binarySearch(nums, K);
|
||||
int last = binarySearch(nums, K + 1);
|
||||
return (first == nums.length || nums[first] != K) ? 0 : last - first;
|
||||
}
|
||||
|
||||
private int binarySearch(int[] nums, int K) {
|
||||
int l = 0, h = nums.length;
|
||||
while (l < h) {
|
||||
int m = l + (h - l) / 2;
|
||||
if (nums[m] >= K)
|
||||
h = m;
|
||||
else
|
||||
l = m + 1;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
34
docs/notes/54. 二叉查找树的第 K 个结点.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# 54. 二叉查找树的第 K 个结点
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a?tpId=13&tqId=11215&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 解题思路
|
||||
|
||||
利用二叉查找树中序遍历有序的特点。
|
||||
|
||||
```java
|
||||
private TreeNode ret;
|
||||
private int cnt = 0;
|
||||
|
||||
public TreeNode KthNode(TreeNode pRoot, int k) {
|
||||
inOrder(pRoot, k);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void inOrder(TreeNode root, int k) {
|
||||
if (root == null || cnt >= k)
|
||||
return;
|
||||
inOrder(root.left, k);
|
||||
cnt++;
|
||||
if (cnt == k)
|
||||
ret = root;
|
||||
inOrder(root.right, k);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
24
docs/notes/55.1 二叉树的深度.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
# 55.1 二叉树的深度
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/435fb86331474282a3499955f0a41e8b?tpId=13&tqId=11191&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ba355101-4a93-4c71-94fb-1da83639727b.jpg" width="350px"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int TreeDepth(TreeNode root) {
|
||||
return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
37
docs/notes/55.2 平衡二叉树.md
Normal file
|
@ -0,0 +1,37 @@
|
|||
# 55.2 平衡二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId=13&tqId=11192&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
平衡二叉树左右子树高度差不超过 1。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg" width="250px"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
private boolean isBalanced = true;
|
||||
|
||||
public boolean IsBalanced_Solution(TreeNode root) {
|
||||
height(root);
|
||||
return isBalanced;
|
||||
}
|
||||
|
||||
private int height(TreeNode root) {
|
||||
if (root == null || !isBalanced)
|
||||
return 0;
|
||||
int left = height(root.left);
|
||||
int right = height(root.right);
|
||||
if (Math.abs(left - right) > 1)
|
||||
isBalanced = false;
|
||||
return 1 + Math.max(left, right);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
35
docs/notes/56. 数组中只出现一次的数字.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# 56. 数组中只出现一次的数字
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/e02fdb54d7524710a7d664d082bb7811?tpId=13&tqId=11193&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。
|
||||
|
||||
## 解题思路
|
||||
|
||||
两个不相等的元素在位级表示上必定会有一位存在不同,将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。
|
||||
|
||||
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
|
||||
|
||||
```java
|
||||
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
|
||||
int diff = 0;
|
||||
for (int num : nums)
|
||||
diff ^= num;
|
||||
diff &= -diff;
|
||||
for (int num : nums) {
|
||||
if ((num & diff) == 0)
|
||||
num1[0] ^= num;
|
||||
else
|
||||
num2[0] ^= num;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
38
docs/notes/57.1 和为 S 的两个数字.md
Normal file
|
@ -0,0 +1,38 @@
|
|||
# 57.1 和为 S 的两个数字
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/390da4f7a00f44bea7c2f3d19491311b?tpId=13&tqId=11195&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输入一个递增排序的数组和一个数字 S,在数组中查找两个数,使得他们的和正好是 S。如果有多对数字的和等于 S,输出两个数的乘积最小的。
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
|
||||
|
||||
- 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
|
||||
- 如果 sum > target,移动较大的元素,使 sum 变小一些;
|
||||
- 如果 sum < target,移动较小的元素,使 sum 变大一些。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
|
||||
int i = 0, j = array.length - 1;
|
||||
while (i < j) {
|
||||
int cur = array[i] + array[j];
|
||||
if (cur == sum)
|
||||
return new ArrayList<>(Arrays.asList(array[i], array[j]));
|
||||
if (cur < sum)
|
||||
i++;
|
||||
else
|
||||
j--;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
50
docs/notes/57.2 和为 S 的连续正数序列.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
# 57.2 和为 S 的连续正数序列
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/c451a3fd84b64cb19485dad758a55ebe?tpId=13&tqId=11194&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
输出所有和为 S 的连续正数序列。
|
||||
|
||||
例如和为 100 的连续序列有:
|
||||
|
||||
```
|
||||
[9, 10, 11, 12, 13, 14, 15, 16]
|
||||
[18, 19, 20, 21, 22]。
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
|
||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||
int start = 1, end = 2;
|
||||
int curSum = 3;
|
||||
while (end < sum) {
|
||||
if (curSum > sum) {
|
||||
curSum -= start;
|
||||
start++;
|
||||
} else if (curSum < sum) {
|
||||
end++;
|
||||
curSum += end;
|
||||
} else {
|
||||
ArrayList<Integer> list = new ArrayList<>();
|
||||
for (int i = start; i <= end; i++)
|
||||
list.add(i);
|
||||
ret.add(list);
|
||||
curSum -= start;
|
||||
start++;
|
||||
end++;
|
||||
curSum += end;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
54
docs/notes/58.1 翻转单词顺序列.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# 58.1 翻转单词顺序列
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/3194a4f4cf814f63919d0790578d51f3?tpId=13&tqId=11197&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
```html
|
||||
Input:
|
||||
"I am a student."
|
||||
|
||||
Output:
|
||||
"student. a am I"
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
题目应该有一个隐含条件,就是不能用额外的空间。虽然 Java 的题目输入参数为 String 类型,需要先创建一个字符数组使得空间复杂度为 O(N),但是正确的参数类型应该和原书一样,为字符数组,并且只能使用该字符数组的空间。任何使用了额外空间的解法在面试时都会大打折扣,包括递归解法。
|
||||
|
||||
正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
|
||||
|
||||
```java
|
||||
public String ReverseSentence(String str) {
|
||||
int n = str.length();
|
||||
char[] chars = str.toCharArray();
|
||||
int i = 0, j = 0;
|
||||
while (j <= n) {
|
||||
if (j == n || chars[j] == ' ') {
|
||||
reverse(chars, i, j - 1);
|
||||
i = j + 1;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
reverse(chars, 0, n - 1);
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
private void reverse(char[] c, int i, int j) {
|
||||
while (i < j)
|
||||
swap(c, i++, j--);
|
||||
}
|
||||
|
||||
private void swap(char[] c, int i, int j) {
|
||||
char t = c[i];
|
||||
c[i] = c[j];
|
||||
c[j] = t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
48
docs/notes/58.2 左旋转字符串.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# 58.2 左旋转字符串
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/12d959b108cb42b1ab72cef4d36af5ec?tpId=13&tqId=11196&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
```html
|
||||
Input:
|
||||
S="abcXYZdef"
|
||||
K=3
|
||||
|
||||
Output:
|
||||
"XYZdefabc"
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
|
||||
|
||||
```java
|
||||
public String LeftRotateString(String str, int n) {
|
||||
if (n >= str.length())
|
||||
return str;
|
||||
char[] chars = str.toCharArray();
|
||||
reverse(chars, 0, n - 1);
|
||||
reverse(chars, n, chars.length - 1);
|
||||
reverse(chars, 0, chars.length - 1);
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
private void reverse(char[] chars, int i, int j) {
|
||||
while (i < j)
|
||||
swap(chars, i++, j--);
|
||||
}
|
||||
|
||||
private void swap(char[] chars, int i, int j) {
|
||||
char t = chars[i];
|
||||
chars[i] = chars[j];
|
||||
chars[j] = t;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
36
docs/notes/59. 滑动窗口的最大值.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# 59. 滑动窗口的最大值
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788?tpId=13&tqId=11217&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
|
||||
|
||||
例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> maxInWindows(int[] num, int size) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
if (size > num.length || size < 1)
|
||||
return ret;
|
||||
PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1); /* 大顶堆 */
|
||||
for (int i = 0; i < size; i++)
|
||||
heap.add(num[i]);
|
||||
ret.add(heap.peek());
|
||||
for (int i = 0, j = i + size; j < num.length; i++, j++) { /* 维护一个大小为 size 的大顶堆 */
|
||||
heap.remove(num[i]);
|
||||
heap.add(num[j]);
|
||||
ret.add(heap.peek());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
96
docs/notes/6. 从尾到头打印链表.md
Normal file
|
@ -0,0 +1,96 @@
|
|||
# 6. 从尾到头打印链表
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035?tpId=13&tqId=11156&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
从尾到头反过来打印出每个结点的值。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png" width="300px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 1. 使用递归
|
||||
|
||||
要逆序打印链表 1->2->3(3,2,1),可以先逆序打印链表 2->3(3,2),最后再打印第一个节点 1。而链表 2->3 可以看成一个新的链表,要逆序打印该链表可以继续使用求解函数,也就是在求解函数中调用自己,这就是递归函数。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
if (listNode != null) {
|
||||
ret.addAll(printListFromTailToHead(listNode.next));
|
||||
ret.add(listNode.val);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 使用头插法
|
||||
|
||||
头插法顾名思义是将节点插入到头部:在遍历原始链表时,将当前节点插入新链表的头部,使其成为第一个节点。
|
||||
|
||||
链表的操作需要维护后继关系,例如在某个节点 node1 之后插入一个节点 node2,我们可以通过修改后继关系来实现:
|
||||
|
||||
```java
|
||||
node3 = node1.next;
|
||||
node2.next = node3;
|
||||
node1.next = node2;
|
||||
```
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/58c8e370-3bec-4c2b-bf17-c8d34345dd17.gif" width="220px"> </div><br>
|
||||
|
||||
|
||||
|
||||
为了能将一个节点插入头部,我们引入了一个叫头结点的辅助节点,该节点不存储值,只是为了方便进行插入操作。不要将头结点与第一个节点混起来,第一个节点是链表中第一个真正存储值的节点。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/0dae7e93-cfd1-4bd3-97e8-325b032b716f-1572687622947.gif" width="420px"> </div><br>
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
// 头插法构建逆序链表
|
||||
ListNode head = new ListNode(-1);
|
||||
while (listNode != null) {
|
||||
ListNode memo = listNode.next;
|
||||
listNode.next = head.next;
|
||||
head.next = listNode;
|
||||
listNode = memo;
|
||||
}
|
||||
// 构建 ArrayList
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
head = head.next;
|
||||
while (head != null) {
|
||||
ret.add(head.val);
|
||||
head = head.next;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 使用栈
|
||||
|
||||
栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif" width="340px"> </div><br>
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
Stack<Integer> stack = new Stack<>();
|
||||
while (listNode != null) {
|
||||
stack.add(listNode.val);
|
||||
listNode = listNode.next;
|
||||
}
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
while (!stack.isEmpty())
|
||||
ret.add(stack.pop());
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
81
docs/notes/60. n 个骰子的点数.md
Normal file
|
@ -0,0 +1,81 @@
|
|||
# 60. n 个骰子的点数
|
||||
|
||||
## 题目链接
|
||||
|
||||
[Lintcode](https://www.lintcode.com/en/problem/dices-sum/)
|
||||
|
||||
## 题目描述
|
||||
|
||||
把 n 个骰子扔在地上,求点数和为 s 的概率。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/195f8693-5ec4-4987-8560-f25e365879dd.png" width="300px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
### 动态规划
|
||||
|
||||
使用一个二维数组 dp 存储点数出现的次数,其中 dp\[i]\[j] 表示前 i 个骰子产生点数 j 的次数。
|
||||
|
||||
空间复杂度:O(N<sup>2</sup>)
|
||||
|
||||
```java
|
||||
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
|
||||
final int face = 6;
|
||||
final int pointNum = face * n;
|
||||
long[][] dp = new long[n + 1][pointNum + 1];
|
||||
|
||||
for (int i = 1; i <= face; i++)
|
||||
dp[1][i] = 1;
|
||||
|
||||
for (int i = 2; i <= n; i++)
|
||||
for (int j = i; j <= pointNum; j++) /* 使用 i 个骰子最小点数为 i */
|
||||
for (int k = 1; k <= face && k <= j; k++)
|
||||
dp[i][j] += dp[i - 1][j - k];
|
||||
|
||||
final double totalNum = Math.pow(6, n);
|
||||
List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
|
||||
for (int i = n; i <= pointNum; i++)
|
||||
ret.add(new AbstractMap.SimpleEntry<>(i, dp[n][i] / totalNum));
|
||||
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### 动态规划 + 旋转数组
|
||||
|
||||
空间复杂度:O(N)
|
||||
|
||||
```java
|
||||
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
|
||||
final int face = 6;
|
||||
final int pointNum = face * n;
|
||||
long[][] dp = new long[2][pointNum + 1];
|
||||
|
||||
for (int i = 1; i <= face; i++)
|
||||
dp[0][i] = 1;
|
||||
|
||||
int flag = 1; /* 旋转标记 */
|
||||
for (int i = 2; i <= n; i++, flag = 1 - flag) {
|
||||
for (int j = 0; j <= pointNum; j++)
|
||||
dp[flag][j] = 0; /* 旋转数组清零 */
|
||||
|
||||
for (int j = i; j <= pointNum; j++)
|
||||
for (int k = 1; k <= face && k <= j; k++)
|
||||
dp[flag][j] += dp[1 - flag][j - k];
|
||||
}
|
||||
|
||||
final double totalNum = Math.pow(6, n);
|
||||
List<Map.Entry<Integer, Double>> ret = new ArrayList<>();
|
||||
for (int i = n; i <= pointNum; i++)
|
||||
ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum));
|
||||
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
46
docs/notes/61. 扑克牌顺子.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# 61. 扑克牌顺子
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/762836f4d43d43ca9deb273b3de8e1f4?tpId=13&tqId=11198&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
五张牌,其中大小鬼为癞子,牌面为 0。判断这五张牌是否能组成顺子。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/eaa506b6-0747-4bee-81f8-3cda795d8154.png" width="350px"> </div><br>
|
||||
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public boolean isContinuous(int[] nums) {
|
||||
|
||||
if (nums.length < 5)
|
||||
return false;
|
||||
|
||||
Arrays.sort(nums);
|
||||
|
||||
// 统计癞子数量
|
||||
int cnt = 0;
|
||||
for (int num : nums)
|
||||
if (num == 0)
|
||||
cnt++;
|
||||
|
||||
// 使用癞子去补全不连续的顺子
|
||||
for (int i = cnt; i < nums.length - 1; i++) {
|
||||
if (nums[i + 1] == nums[i])
|
||||
return false;
|
||||
cnt -= nums[i + 1] - nums[i] - 1;
|
||||
}
|
||||
|
||||
return cnt >= 0;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
30
docs/notes/62. 圆圈中最后剩下的数.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# 62. 圆圈中最后剩下的数
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
让小朋友们围成一个大圈。然后,随机指定一个数 m,让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友,可以不用表演。
|
||||
|
||||
## 解题思路
|
||||
|
||||
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
|
||||
|
||||
```java
|
||||
public int LastRemaining_Solution(int n, int m) {
|
||||
if (n == 0) /* 特殊输入的处理 */
|
||||
return -1;
|
||||
if (n == 1) /* 递归返回条件 */
|
||||
return 0;
|
||||
return (LastRemaining_Solution(n - 1, m) + m) % n;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
36
docs/notes/63. 股票的最大利润.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# 63. 股票的最大利润
|
||||
|
||||
## 题目链接
|
||||
|
||||
[Leetcode:121. Best Time to Buy and Sell Stock ](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/)
|
||||
|
||||
## 题目描述
|
||||
|
||||
可以有一次买入和一次卖出,买入必须在前。求最大收益。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/42661013-750f-420b-b3c1-437e9a11fb65.png" width="220px"> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
|
||||
|
||||
```java
|
||||
public int maxProfit(int[] prices) {
|
||||
if (prices == null || prices.length == 0)
|
||||
return 0;
|
||||
int soFarMin = prices[0];
|
||||
int maxProfit = 0;
|
||||
for (int i = 1; i < prices.length; i++) {
|
||||
soFarMin = Math.min(soFarMin, prices[i]);
|
||||
maxProfit = Math.max(maxProfit, prices[i] - soFarMin);
|
||||
}
|
||||
return maxProfit;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
32
docs/notes/64. 求 1+2+3+...+n.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# 64. 求 1+2+3+...+n
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句 A ? B : C。
|
||||
|
||||
## 解题思路
|
||||
|
||||
使用递归解法最重要的是指定返回条件,但是本题无法直接使用 if 语句来指定返回条件。
|
||||
|
||||
条件与 && 具有短路原则,即在第一个条件语句为 false 的情况下不会去执行第二个条件语句。利用这一特性,将递归的返回条件取非然后作为 && 的第一个条件语句,递归的主体转换为第二个条件语句,那么当递归的返回条件为 true 的情况下就不会执行递归的主体部分,递归返回。
|
||||
|
||||
本题的递归返回条件为 n <= 0,取非后就是 n > 0;递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
|
||||
|
||||
```java
|
||||
public int Sum_Solution(int n) {
|
||||
int sum = n;
|
||||
boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
|
||||
return sum;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
28
docs/notes/65. 不用加减乘除做加法.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# 65. 不用加减乘除做加法
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/59ac416b4b944300b617d4f7f111b215?tpId=13&tqId=11201&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
写一个函数,求两个整数之和,要求不得使用 +、-、\*、/ 四则运算符号。
|
||||
|
||||
## 解题思路
|
||||
|
||||
a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。
|
||||
|
||||
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
|
||||
|
||||
```java
|
||||
public int Add(int a, int b) {
|
||||
return b == 0 ? a : Add(a ^ b, (a & b) << 1);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
33
docs/notes/66. 构建乘积数组.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# 66. 构建乘积数组
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/94a4d381a68b47b7a8bed86f2975db46?tpId=13&tqId=11204&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个数组 A[0, 1,..., n-1],请构建一个数组 B[0, 1,..., n-1],其中 B 中的元素 B[i]=A[0]\*A[1]\*...\*A[i-1]\*A[i+1]\*...\*A[n-1]。要求不能使用除法。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/4240a69f-4d51-4d16-b797-2dfe110f30bd.png" width="250px"> </div><br>
|
||||
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int[] multiply(int[] A) {
|
||||
int n = A.length;
|
||||
int[] B = new int[n];
|
||||
for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */
|
||||
B[i] = product;
|
||||
for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--) /* 从右往左累乘 */
|
||||
B[i] *= product;
|
||||
return B;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
46
docs/notes/67. 把字符串转换成整数.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# 67. 把字符串转换成整数
|
||||
|
||||
## 题目链接
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/1277c681251b4372bdef344468e4f26e?tpId=13&tqId=11202&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
将一个字符串转换成一个整数,字符串不是一个合法的数值则返回 0,要求不能使用字符串转换整数的库函数。
|
||||
|
||||
```html
|
||||
Iuput:
|
||||
+2147483647
|
||||
1a33
|
||||
|
||||
Output:
|
||||
2147483647
|
||||
0
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
```java
|
||||
public int StrToInt(String str) {
|
||||
if (str == null || str.length() == 0)
|
||||
return 0;
|
||||
boolean isNegative = str.charAt(0) == '-';
|
||||
int ret = 0;
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (i == 0 && (c == '+' || c == '-')) /* 符号判定 */
|
||||
continue;
|
||||
if (c < '0' || c > '9') /* 非法输入 */
|
||||
return 0;
|
||||
ret = ret * 10 + (c - '0');
|
||||
}
|
||||
return isNegative ? -ret : ret;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
55
docs/notes/68. 树中两个节点的最低公共祖先.md
Normal file
|
@ -0,0 +1,55 @@
|
|||
# 68. 树中两个节点的最低公共祖先
|
||||
|
||||
|
||||
## 68.1 二叉查找树
|
||||
|
||||
### 题目链接
|
||||
|
||||
[Leetcode : 235. Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/)
|
||||
|
||||
### 解题思路
|
||||
|
||||
在二叉查找树中,两个节点 p, q 的公共祖先 root 满足 root.val >= p.val && root.val <= q.val。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/047faac4-a368-4565-8331-2b66253080d3.jpg" width="250"/> </div><br>
|
||||
|
||||
```java
|
||||
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
|
||||
if (root == null)
|
||||
return root;
|
||||
if (root.val > p.val && root.val > q.val)
|
||||
return lowestCommonAncestor(root.left, p, q);
|
||||
if (root.val < p.val && root.val < q.val)
|
||||
return lowestCommonAncestor(root.right, p, q);
|
||||
return root;
|
||||
}
|
||||
```
|
||||
|
||||
## 68.2 普通二叉树
|
||||
|
||||
### 题目链接
|
||||
|
||||
[Leetcode : 236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/)
|
||||
|
||||
### 解题思路
|
||||
|
||||
在左右子树中查找是否存在 p 或者 q,如果 p 和 q 分别在两个子树中,那么就说明根节点就是最低公共祖先。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg" width="250"/> </div><br>
|
||||
|
||||
```java
|
||||
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
|
||||
if (root == null || root == p || root == q)
|
||||
return root;
|
||||
TreeNode left = lowestCommonAncestor(root.left, p, q);
|
||||
TreeNode right = lowestCommonAncestor(root.right, p, q);
|
||||
return left == null ? right : right == null ? left : root;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
48
docs/notes/7. 重建二叉树.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# 7. 重建二叉树
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=13&tqId=11157&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
|
||||
|
||||
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/image-20191102210342488.png" width="400"/> </div><br>
|
||||
|
||||
## 解题思路
|
||||
|
||||
前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。然后分别对左右子树递归地求解。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/60c4a44c-7829-4242-b3a1-26c3b513aaf0.gif" width="430px"> </div><br>
|
||||
|
||||
```java
|
||||
// 缓存中序遍历数组每个值对应的索引
|
||||
private Map<Integer, Integer> indexForInOrders = new HashMap<>();
|
||||
|
||||
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
|
||||
for (int i = 0; i < in.length; i++)
|
||||
indexForInOrders.put(in[i], i);
|
||||
return reConstructBinaryTree(pre, 0, pre.length - 1, 0);
|
||||
}
|
||||
|
||||
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL) {
|
||||
if (preL > preR)
|
||||
return null;
|
||||
TreeNode root = new TreeNode(pre[preL]);
|
||||
int inIndex = indexForInOrders.get(root.val);
|
||||
int leftTreeSize = inIndex - inL;
|
||||
root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL);
|
||||
root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1);
|
||||
return root;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
74
docs/notes/8. 二叉树的下一个结点.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# 8. 二叉树的下一个结点
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回 。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
|
||||
|
||||
```java
|
||||
public class TreeLinkNode {
|
||||
|
||||
int val;
|
||||
TreeLinkNode left = null;
|
||||
TreeLinkNode right = null;
|
||||
TreeLinkNode next = null; // 指向父结点的指针
|
||||
|
||||
TreeLinkNode(int val) {
|
||||
this.val = val;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
我们先来回顾一下中序遍历的过程:先遍历树的左子树,再遍历根节点,最后再遍历右子树。所以最左节点是中序遍历的第一个节点。
|
||||
|
||||
```java
|
||||
void traverse(TreeNode root) {
|
||||
if (root == null) return;
|
||||
traverse(root.left);
|
||||
visit(root);
|
||||
traverse(root.right);
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/ad5cc8fc-d59b-45ce-8899-63a18320d97e.gif" width="300px"/> </div><br>
|
||||
|
||||
|
||||
|
||||
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/7008dc2b-6f13-4174-a516-28b2d75b0152.gif" width="300px"/> </div><br>
|
||||
|
||||
② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/094e3ac8-e080-4e94-9f0a-64c25abc695e.gif" width="300px"/> </div><br>
|
||||
|
||||
```java
|
||||
public TreeLinkNode GetNext(TreeLinkNode pNode) {
|
||||
if (pNode.right != null) {
|
||||
TreeLinkNode node = pNode.right;
|
||||
while (node.left != null)
|
||||
node = node.left;
|
||||
return node;
|
||||
} else {
|
||||
while (pNode.next != null) {
|
||||
TreeLinkNode parent = pNode.next;
|
||||
if (parent.left == pNode)
|
||||
return parent;
|
||||
pNode = pNode.next;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
42
docs/notes/9. 用两个栈实现队列.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
# 9. 用两个栈实现队列
|
||||
|
||||
## 题目链接
|
||||
|
||||
[牛客网](https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6?tpId=13&tqId=11158&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
|
||||
|
||||
## 题目描述
|
||||
|
||||
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。
|
||||
|
||||
## 解题思路
|
||||
|
||||
in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/3ea280b5-be7d-471b-ac76-ff020384357c.gif" width="450"/> </div><br>
|
||||
|
||||
```java
|
||||
Stack<Integer> in = new Stack<Integer>();
|
||||
Stack<Integer> out = new Stack<Integer>();
|
||||
|
||||
public void push(int node) {
|
||||
in.push(node);
|
||||
}
|
||||
|
||||
public int pop() throws Exception {
|
||||
if (out.isEmpty())
|
||||
while (!in.isEmpty())
|
||||
out.push(in.pop());
|
||||
|
||||
if (out.isEmpty())
|
||||
throw new Exception("queue is empty");
|
||||
|
||||
return out.pop();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
|
@ -91,4 +91,6 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -47,14 +47,14 @@ Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本
|
|||
- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files
|
||||
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/603dbb49-dac5-4825-9694-5f1d65cefd44.png" width="320px"> </div><br>
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/72ee7e9a-194d-42e9-b4d7-29c23417ca18.png" width="320px"> </div><br>
|
||||
|
||||
可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。
|
||||
|
||||
- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交
|
||||
- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/265bab88-7be9-44c5-a33f-f93d9882c096.png" width="500px"> </div><br>
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/a4a0a6e6-386b-4bfa-b899-ec33d3310f3e.png" width="500px"> </div><br>
|
||||
|
||||
# 分支实现
|
||||
|
||||
|
@ -162,4 +162,6 @@ $ ssh-keygen -t rsa -C "youremail@example.com"
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -76,7 +76,7 @@ URI 包含 URL 和 URN。
|
|||
|
||||
# 二、HTTP 方法
|
||||
|
||||
客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。
|
||||
客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。
|
||||
|
||||
## GET
|
||||
|
||||
|
@ -173,7 +173,7 @@ CONNECT www.example.com:443 HTTP/1.1
|
|||
|
||||
# 三、HTTP 状态码
|
||||
|
||||
服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。
|
||||
服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。
|
||||
|
||||
| 状态码 | 类别 | 含义 |
|
||||
| :---: | :---: | :---: |
|
||||
|
@ -185,45 +185,45 @@ CONNECT www.example.com:443 HTTP/1.1
|
|||
|
||||
## 1XX 信息
|
||||
|
||||
- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
|
||||
- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
|
||||
|
||||
## 2XX 成功
|
||||
|
||||
- **200 OK**
|
||||
- **200 OK**
|
||||
|
||||
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
|
||||
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
|
||||
|
||||
- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
|
||||
- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
|
||||
|
||||
## 3XX 重定向
|
||||
|
||||
- **301 Moved Permanently** :永久性重定向
|
||||
- **301 Moved Permanently** :永久性重定向
|
||||
|
||||
- **302 Found** :临时性重定向
|
||||
- **302 Found** :临时性重定向
|
||||
|
||||
- **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。
|
||||
- **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。
|
||||
|
||||
- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。
|
||||
|
||||
- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。
|
||||
- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。
|
||||
|
||||
- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。
|
||||
- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。
|
||||
|
||||
## 4XX 客户端错误
|
||||
|
||||
- **400 Bad Request** :请求报文中存在语法错误。
|
||||
- **400 Bad Request** :请求报文中存在语法错误。
|
||||
|
||||
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
|
||||
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
|
||||
|
||||
- **403 Forbidden** :请求被拒绝。
|
||||
- **403 Forbidden** :请求被拒绝。
|
||||
|
||||
- **404 Not Found**
|
||||
- **404 Not Found**
|
||||
|
||||
## 5XX 服务器错误
|
||||
|
||||
- **500 Internal Server Error** :服务器正在执行请求时发生错误。
|
||||
- **500 Internal Server Error** :服务器正在执行请求时发生错误。
|
||||
|
||||
- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
|
||||
- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
|
||||
|
||||
# 四、HTTP 首部
|
||||
|
||||
|
@ -436,7 +436,7 @@ Session 可以存储在服务器上的文件、数据库或者内存中。也可
|
|||
|
||||
HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。
|
||||
|
||||
**3.1 禁止进行缓存**
|
||||
**3.1 禁止进行缓存**
|
||||
|
||||
no-store 指令规定不能对请求或响应的任何一部分进行缓存。
|
||||
|
||||
|
@ -444,7 +444,7 @@ no-store 指令规定不能对请求或响应的任何一部分进行缓存。
|
|||
Cache-Control: no-store
|
||||
```
|
||||
|
||||
**3.2 强制确认缓存**
|
||||
**3.2 强制确认缓存**
|
||||
|
||||
no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效时才能使用该缓存对客户端的请求进行响应。
|
||||
|
||||
|
@ -452,7 +452,7 @@ no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源
|
|||
Cache-Control: no-cache
|
||||
```
|
||||
|
||||
**3.3 私有缓存和公共缓存**
|
||||
**3.3 私有缓存和公共缓存**
|
||||
|
||||
private 指令规定了将资源作为私有缓存,只能被单独用户使用,一般存储在用户浏览器中。
|
||||
|
||||
|
@ -466,7 +466,7 @@ public 指令规定了将资源作为公共缓存,可以被多个用户使用
|
|||
Cache-Control: public
|
||||
```
|
||||
|
||||
**3.4 缓存过期机制**
|
||||
**3.4 缓存过期机制**
|
||||
|
||||
max-age 指令出现在请求报文,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。
|
||||
|
||||
|
@ -515,7 +515,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
|
|||
|
||||
### 1. 类型
|
||||
|
||||
**1.1 服务端驱动型**
|
||||
**1.1 服务端驱动型**
|
||||
|
||||
客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。
|
||||
|
||||
|
@ -525,7 +525,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
|
|||
- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术);
|
||||
- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。
|
||||
|
||||
**1.2 代理驱动型**
|
||||
**1.2 代理驱动型**
|
||||
|
||||
服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。
|
||||
|
||||
|
@ -691,7 +691,7 @@ HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对
|
|||
|
||||
## 认证
|
||||
|
||||
通过使用 **证书** 来对通信方进行认证。
|
||||
通过使用 **证书** 来对通信方进行认证。
|
||||
|
||||
数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。
|
||||
|
||||
|
@ -881,4 +881,6 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -622,4 +622,6 @@ NIO 与普通 I/O 的区别主要有以下两点:
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -63,10 +63,12 @@ boolean 只有两个值:true、false,可以使用 1 bit 来存储,但是
|
|||
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
|
||||
|
||||
```java
|
||||
Integer x = 2; // 装箱
|
||||
int y = x; // 拆箱
|
||||
Integer x = 2; // 装箱 调用了 Integer.valueOf(2)
|
||||
int y = x; // 拆箱 调用了 X.intValue()
|
||||
```
|
||||
|
||||
- [Autoboxing and Unboxing](https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html)
|
||||
|
||||
## 缓存池
|
||||
|
||||
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
|
||||
|
@ -154,7 +156,7 @@ System.out.println(m == n); // true
|
|||
|
||||
## 概览
|
||||
|
||||
String 被声明为 final,因此它不可被继承。
|
||||
String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承)
|
||||
|
||||
在 Java 8 中,String 内部使用 char 数组存储数据。
|
||||
|
||||
|
@ -183,21 +185,21 @@ value 数组被声明为 final,这意味着 value 数组初始化之后就不
|
|||
|
||||
## 不可变的好处
|
||||
|
||||
**1. 可以缓存 hash 值**
|
||||
**1. 可以缓存 hash 值**
|
||||
|
||||
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
|
||||
|
||||
**2. String Pool 的需要**
|
||||
**2. String Pool 的需要**
|
||||
|
||||
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/9112288f-23f5-4e53-b222-a46fdbca1603.png" width="300px"> </div><br>
|
||||
|
||||
**3. 安全性**
|
||||
**3. 安全性**
|
||||
|
||||
String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。
|
||||
|
||||
**4. 线程安全**
|
||||
**4. 线程安全**
|
||||
|
||||
String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
|
||||
|
||||
|
@ -205,12 +207,12 @@ String 不可变性天生具备线程安全,可以在多个线程中安全地
|
|||
|
||||
## String, StringBuffer and StringBuilder
|
||||
|
||||
**1. 可变性**
|
||||
**1. 可变性**
|
||||
|
||||
- String 不可变
|
||||
- StringBuffer 和 StringBuilder 可变
|
||||
|
||||
**2. 线程安全**
|
||||
**2. 线程安全**
|
||||
|
||||
- String 不可变,因此是线程安全的
|
||||
- StringBuilder 不是线程安全的
|
||||
|
@ -507,7 +509,7 @@ public class AccessWithInnerClassExample {
|
|||
|
||||
## 抽象类与接口
|
||||
|
||||
**1. 抽象类**
|
||||
**1. 抽象类**
|
||||
|
||||
抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。
|
||||
|
||||
|
@ -542,7 +544,7 @@ AbstractClassExample ac2 = new AbstractExtendClassExample();
|
|||
ac2.func1();
|
||||
```
|
||||
|
||||
**2. 接口**
|
||||
**2. 接口**
|
||||
|
||||
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。
|
||||
|
||||
|
@ -586,14 +588,14 @@ ie2.func1();
|
|||
System.out.println(InterfaceExample.x);
|
||||
```
|
||||
|
||||
**3. 比较**
|
||||
**3. 比较**
|
||||
|
||||
- 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。
|
||||
- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
|
||||
- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
|
||||
- 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。
|
||||
|
||||
**4. 使用选择**
|
||||
**4. 使用选择**
|
||||
|
||||
使用接口:
|
||||
|
||||
|
@ -615,7 +617,7 @@ System.out.println(InterfaceExample.x);
|
|||
|
||||
## super
|
||||
|
||||
- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
|
||||
- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。应该注意到,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它构造函数,那么就可以使用 super 函数。
|
||||
- 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
|
||||
|
||||
```java
|
||||
|
@ -667,7 +669,7 @@ SuperExtendExample.func()
|
|||
|
||||
## 重写与重载
|
||||
|
||||
**1. 重写(Override)**
|
||||
**1. 重写(Override)**
|
||||
|
||||
存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
|
||||
|
||||
|
@ -771,7 +773,7 @@ public static void main(String[] args) {
|
|||
}
|
||||
```
|
||||
|
||||
**2. 重载(Overload)**
|
||||
**2. 重载(Overload)**
|
||||
|
||||
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
|
||||
|
||||
|
@ -808,7 +810,7 @@ public final void wait() throws InterruptedException
|
|||
|
||||
## equals()
|
||||
|
||||
**1. 等价关系**
|
||||
**1. 等价关系**
|
||||
|
||||
Ⅰ 自反性
|
||||
|
||||
|
@ -845,7 +847,7 @@ x.equals(y) == x.equals(y); // true
|
|||
x.equals(null); // false;
|
||||
```
|
||||
|
||||
**2. 等价与相等**
|
||||
**2. 等价与相等**
|
||||
|
||||
- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
|
||||
- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
|
||||
|
@ -857,7 +859,7 @@ System.out.println(x.equals(y)); // true
|
|||
System.out.println(x == y); // false
|
||||
```
|
||||
|
||||
**3. 实现**
|
||||
**3. 实现**
|
||||
|
||||
- 检查是否为同一个对象的引用,如果是直接返回 true;
|
||||
- 检查是否是同一个类型,如果不是,直接返回 false;
|
||||
|
@ -897,7 +899,7 @@ hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等
|
|||
|
||||
在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。
|
||||
|
||||
下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。
|
||||
下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hashCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。
|
||||
|
||||
```java
|
||||
EqualExample e1 = new EqualExample(1, 1, 1);
|
||||
|
@ -950,7 +952,7 @@ ToStringExample@4554617c
|
|||
|
||||
## clone()
|
||||
|
||||
**1. cloneable**
|
||||
**1. cloneable**
|
||||
|
||||
clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。
|
||||
|
||||
|
@ -1009,7 +1011,7 @@ public class CloneExample implements Cloneable {
|
|||
}
|
||||
```
|
||||
|
||||
**2. 浅拷贝**
|
||||
**2. 浅拷贝**
|
||||
|
||||
拷贝对象和原始对象的引用类型引用同一个对象。
|
||||
|
||||
|
@ -1052,7 +1054,7 @@ e1.set(2, 222);
|
|||
System.out.println(e2.get(2)); // 222
|
||||
```
|
||||
|
||||
**3. 深拷贝**
|
||||
**3. 深拷贝**
|
||||
|
||||
拷贝对象和原始对象的引用类型引用不同对象。
|
||||
|
||||
|
@ -1100,7 +1102,7 @@ e1.set(2, 222);
|
|||
System.out.println(e2.get(2)); // 2
|
||||
```
|
||||
|
||||
**4. clone() 的替代方案**
|
||||
**4. clone() 的替代方案**
|
||||
|
||||
使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
|
||||
|
||||
|
@ -1144,7 +1146,7 @@ System.out.println(e2.get(2)); // 2
|
|||
|
||||
## final
|
||||
|
||||
**1. 数据**
|
||||
**1. 数据**
|
||||
|
||||
声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
|
||||
|
||||
|
@ -1158,19 +1160,19 @@ final A y = new A();
|
|||
y.a = 1;
|
||||
```
|
||||
|
||||
**2. 方法**
|
||||
**2. 方法**
|
||||
|
||||
声明方法不能被子类重写。
|
||||
|
||||
private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。
|
||||
|
||||
**3. 类**
|
||||
**3. 类**
|
||||
|
||||
声明类不允许被继承。
|
||||
|
||||
## static
|
||||
|
||||
**1. 静态变量**
|
||||
**1. 静态变量**
|
||||
|
||||
- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
|
||||
- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
|
||||
|
@ -1190,7 +1192,7 @@ public class A {
|
|||
}
|
||||
```
|
||||
|
||||
**2. 静态方法**
|
||||
**2. 静态方法**
|
||||
|
||||
静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
|
||||
|
||||
|
@ -1218,7 +1220,7 @@ public class A {
|
|||
}
|
||||
```
|
||||
|
||||
**3. 静态语句块**
|
||||
**3. 静态语句块**
|
||||
|
||||
静态语句块在类初始化时运行一次。
|
||||
|
||||
|
@ -1239,7 +1241,7 @@ public class A {
|
|||
123
|
||||
```
|
||||
|
||||
**4. 静态内部类**
|
||||
**4. 静态内部类**
|
||||
|
||||
非静态内部类依赖于外部类的实例,而静态内部类不需要。
|
||||
|
||||
|
@ -1263,7 +1265,7 @@ public class OuterClass {
|
|||
|
||||
静态内部类不能访问外部类的非静态的变量和方法。
|
||||
|
||||
**5. 静态导包**
|
||||
**5. 静态导包**
|
||||
|
||||
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
|
||||
|
||||
|
@ -1271,7 +1273,7 @@ public class OuterClass {
|
|||
import static com.xxx.ClassName.*
|
||||
```
|
||||
|
||||
**6. 初始化顺序**
|
||||
**6. 初始化顺序**
|
||||
|
||||
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
|
||||
|
||||
|
@ -1315,7 +1317,7 @@ public InitialOrderTest() {
|
|||
|
||||
# 七、反射
|
||||
|
||||
每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
|
||||
每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
|
||||
|
||||
类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。
|
||||
|
||||
|
@ -1323,25 +1325,25 @@ public InitialOrderTest() {
|
|||
|
||||
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
|
||||
|
||||
- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
|
||||
- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法;
|
||||
- **Constructor** :可以用 Constructor 创建新的对象。
|
||||
- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
|
||||
- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法;
|
||||
- **Constructor** :可以用 Constructor 的 newInstance() 创建新的对象。
|
||||
|
||||
**反射的优点:**
|
||||
**反射的优点:**
|
||||
|
||||
* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
|
||||
* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
|
||||
* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
|
||||
* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
|
||||
* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
|
||||
* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
|
||||
|
||||
**反射的缺点:**
|
||||
**反射的缺点:**
|
||||
|
||||
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。
|
||||
|
||||
* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
|
||||
* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
|
||||
|
||||
* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
|
||||
* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
|
||||
|
||||
* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
|
||||
* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
|
||||
|
||||
|
||||
- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html)
|
||||
|
@ -1349,10 +1351,10 @@ Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect
|
|||
|
||||
# 八、异常
|
||||
|
||||
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种:
|
||||
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种:
|
||||
|
||||
- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
|
||||
- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。
|
||||
- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
|
||||
- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/PPjwP.png" width="600"/> </div><br>
|
||||
|
||||
|
@ -1383,7 +1385,7 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
|
|||
|
||||
## Java 各版本的新特性
|
||||
|
||||
**New highlights in Java SE 8**
|
||||
**New highlights in Java SE 8**
|
||||
|
||||
1. Lambda Expressions
|
||||
2. Pipelines and Streams
|
||||
|
@ -1395,7 +1397,7 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
|
|||
8. Parallel operations
|
||||
9. PermGen Error Removed
|
||||
|
||||
**New highlights in Java SE 7**
|
||||
**New highlights in Java SE 7**
|
||||
|
||||
1. Strings in Switch Statement
|
||||
2. Type Inference for Generic Instance Creation
|
||||
|
@ -1418,7 +1420,6 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
|
|||
- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
|
||||
- Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。
|
||||
- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。
|
||||
- Java 不支持条件编译,C++ 通过 #ifdef #ifndef 等预处理命令从而实现条件编译。
|
||||
|
||||
[What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php)
|
||||
|
||||
|
@ -1435,4 +1436,6 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
- HashMap:基于哈希表实现。
|
||||
|
||||
- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
|
||||
- Hashtable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 Hashtable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
|
||||
|
||||
- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
|
||||
|
||||
|
@ -299,12 +299,53 @@ public synchronized E get(int index) {
|
|||
}
|
||||
```
|
||||
|
||||
### 2. 与 ArrayList 的比较
|
||||
### 2. 扩容
|
||||
|
||||
Vector 的构造函数可以传入 capacityIncrement 参数,它的作用是在扩容时使容量 capacity 增长 capacityIncrement。如果这个参数的值小于等于 0,扩容时每次都令 capacity 为原来的两倍。
|
||||
|
||||
```java
|
||||
public Vector(int initialCapacity, int capacityIncrement) {
|
||||
super();
|
||||
if (initialCapacity < 0)
|
||||
throw new IllegalArgumentException("Illegal Capacity: "+
|
||||
initialCapacity);
|
||||
this.elementData = new Object[initialCapacity];
|
||||
this.capacityIncrement = capacityIncrement;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
private void grow(int minCapacity) {
|
||||
// overflow-conscious code
|
||||
int oldCapacity = elementData.length;
|
||||
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
|
||||
capacityIncrement : oldCapacity);
|
||||
if (newCapacity - minCapacity < 0)
|
||||
newCapacity = minCapacity;
|
||||
if (newCapacity - MAX_ARRAY_SIZE > 0)
|
||||
newCapacity = hugeCapacity(minCapacity);
|
||||
elementData = Arrays.copyOf(elementData, newCapacity);
|
||||
}
|
||||
```
|
||||
|
||||
调用没有 capacityIncrement 的构造函数时,capacityIncrement 值被设置为 0,也就是说默认情况下 Vector 每次扩容时容量都会翻倍。
|
||||
|
||||
```java
|
||||
public Vector(int initialCapacity) {
|
||||
this(initialCapacity, 0);
|
||||
}
|
||||
|
||||
public Vector() {
|
||||
this(10);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 与 ArrayList 的比较
|
||||
|
||||
- Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
|
||||
- Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。
|
||||
- Vector 每次扩容请求其大小的 2 倍(也可以通过构造函数设置增长的容量),而 ArrayList 是 1.5 倍。
|
||||
|
||||
### 3. 替代方案
|
||||
### 4. 替代方案
|
||||
|
||||
可以使用 `Collections.synchronizedList();` 得到一个线程安全的 ArrayList。
|
||||
|
||||
|
@ -577,7 +618,7 @@ int hash = hash(key);
|
|||
int i = indexFor(hash, table.length);
|
||||
```
|
||||
|
||||
**4.1 计算 hash 值**
|
||||
**4.1 计算 hash 值**
|
||||
|
||||
```java
|
||||
final int hash(Object k) {
|
||||
|
@ -602,7 +643,7 @@ public final int hashCode() {
|
|||
}
|
||||
```
|
||||
|
||||
**4.2 取模**
|
||||
**4.2 取模**
|
||||
|
||||
令 x = 1<<4,即 x 为 2 的 4 次方,它具有以下性质:
|
||||
|
||||
|
@ -650,7 +691,7 @@ static int indexFor(int h, int length) {
|
|||
| capacity | table 的容量大小,默认为 16。需要注意的是 capacity 必须保证为 2 的 n 次方。|
|
||||
| size | 键值对数量。 |
|
||||
| threshold | size 的临界值,当 size 大于等于 threshold 就必须进行扩容操作。 |
|
||||
| loadFactor | 装载因子,table 能够使用的比例,threshold = capacity * loadFactor。|
|
||||
| loadFactor | 装载因子,table 能够使用的比例,threshold = (int)(newCapacity * loadFactor)。|
|
||||
|
||||
```java
|
||||
static final int DEFAULT_INITIAL_CAPACITY = 16;
|
||||
|
@ -718,7 +759,7 @@ void transfer(Entry[] newTable) {
|
|||
|
||||
### 6. 扩容-重新计算桶下标
|
||||
|
||||
在进行扩容时,需要把键值对重新放到对应的桶上。HashMap 使用了一个特殊的机制,可以降低重新计算桶下标的操作。
|
||||
在进行扩容时,需要把键值对重新计算桶下标,从而放到对应的桶上。在前面提到,HashMap 使用 hash%capacity 来确定桶下标。HashMap capacity 为 2 的 n 次方这一特点能够极大降低重新计算桶下标操作的复杂度。
|
||||
|
||||
假设原数组长度 capacity 为 16,扩容之后 new capacity 为 32:
|
||||
|
||||
|
@ -727,10 +768,10 @@ capacity : 00010000
|
|||
new capacity : 00100000
|
||||
```
|
||||
|
||||
对于一个 Key,
|
||||
对于一个 Key,它的哈希值 hash 在第 5 位:
|
||||
|
||||
- 它的哈希值如果在第 5 位上为 0,那么取模得到的结果和之前一样;
|
||||
- 如果为 1,那么得到的结果为原来的结果 +16。
|
||||
- 为 0,那么 hash%00010000 = hash%00100000,桶位置和原来一致;
|
||||
- 为 1,hash%00010000 = hash%00100000 + 16,桶位置是原位置 + 16。
|
||||
|
||||
### 7. 计算数组容量
|
||||
|
||||
|
@ -767,11 +808,11 @@ static final int tableSizeFor(int cap) {
|
|||
|
||||
### 8. 链表转红黑树
|
||||
|
||||
从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。
|
||||
从 JDK 1.8 开始,一个桶存储的链表长度大于等于 8 时会将链表转换为红黑树。
|
||||
|
||||
### 9. 与 HashTable 的比较
|
||||
### 9. 与 Hashtable 的比较
|
||||
|
||||
- HashTable 使用 synchronized 来进行同步。
|
||||
- Hashtable 使用 synchronized 来进行同步。
|
||||
- HashMap 可以插入键为 null 的 Entry。
|
||||
- HashMap 的迭代器是 fail-fast 迭代器。
|
||||
- HashMap 不能保证随着时间的推移 Map 中的元素次序是不变的。
|
||||
|
@ -1113,4 +1154,6 @@ public final class ConcurrentCache<K, V> {
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -223,7 +223,7 @@ public static void main(String[] args) {
|
|||
|
||||
main() 属于非守护线程。
|
||||
|
||||
使用 setDaemon() 方法将一个线程设置为守护线程。
|
||||
在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程。
|
||||
|
||||
```java
|
||||
public static void main(String[] args) {
|
||||
|
@ -384,7 +384,7 @@ Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问
|
|||
|
||||
## synchronized
|
||||
|
||||
**1. 同步一个代码块**
|
||||
**1. 同步一个代码块**
|
||||
|
||||
```java
|
||||
public void func() {
|
||||
|
@ -441,7 +441,7 @@ public static void main(String[] args) {
|
|||
```
|
||||
|
||||
|
||||
**2. 同步一个方法**
|
||||
**2. 同步一个方法**
|
||||
|
||||
```java
|
||||
public synchronized void func () {
|
||||
|
@ -451,7 +451,7 @@ public synchronized void func () {
|
|||
|
||||
它和同步代码块一样,作用于同一个对象。
|
||||
|
||||
**3. 同步一个类**
|
||||
**3. 同步一个类**
|
||||
|
||||
```java
|
||||
public void func() {
|
||||
|
@ -490,7 +490,7 @@ public static void main(String[] args) {
|
|||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
|
||||
```
|
||||
|
||||
**4. 同步一个静态方法**
|
||||
**4. 同步一个静态方法**
|
||||
|
||||
```java
|
||||
public synchronized static void fun() {
|
||||
|
@ -538,27 +538,27 @@ public static void main(String[] args) {
|
|||
|
||||
## 比较
|
||||
|
||||
**1. 锁的实现**
|
||||
**1. 锁的实现**
|
||||
|
||||
synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。
|
||||
|
||||
**2. 性能**
|
||||
**2. 性能**
|
||||
|
||||
新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。
|
||||
|
||||
**3. 等待可中断**
|
||||
**3. 等待可中断**
|
||||
|
||||
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
|
||||
|
||||
ReentrantLock 可中断,而 synchronized 不行。
|
||||
|
||||
**4. 公平锁**
|
||||
**4. 公平锁**
|
||||
|
||||
公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。
|
||||
|
||||
synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
|
||||
|
||||
**5. 锁绑定多个条件**
|
||||
**5. 锁绑定多个条件**
|
||||
|
||||
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
|
||||
|
||||
|
@ -669,7 +669,7 @@ before
|
|||
after
|
||||
```
|
||||
|
||||
**wait() 和 sleep() 的区别**
|
||||
**wait() 和 sleep() 的区别**
|
||||
|
||||
- wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
|
||||
- wait() 会释放锁,sleep() 不会。
|
||||
|
@ -732,7 +732,7 @@ java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.
|
|||
|
||||
## CountDownLatch
|
||||
|
||||
用来控制一个线程等待多个线程。
|
||||
用来控制一个或者多个线程等待多个线程。
|
||||
|
||||
维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。
|
||||
|
||||
|
@ -907,12 +907,12 @@ other task is running...
|
|||
|
||||
java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现:
|
||||
|
||||
- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度)
|
||||
- **优先级队列** :PriorityBlockingQueue
|
||||
- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度)
|
||||
- **优先级队列** :PriorityBlockingQueue
|
||||
|
||||
提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置。
|
||||
|
||||
**使用 BlockingQueue 实现生产者消费者问题**
|
||||
**使用 BlockingQueue 实现生产者消费者问题**
|
||||
|
||||
```java
|
||||
public class ProducerConsumer {
|
||||
|
@ -1329,10 +1329,10 @@ synchronized 和 ReentrantLock。
|
|||
|
||||
互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。
|
||||
|
||||
### 1. CAS
|
||||
|
||||
随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要将线程阻塞,因此这种同步操作称为非阻塞同步。
|
||||
|
||||
### 1. CAS
|
||||
|
||||
乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。
|
||||
|
||||
### 2. AtomicInteger
|
||||
|
@ -1638,4 +1638,6 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
本文大部分内容参考 **周志明《深入理解 Java 虚拟机》** ,想要深入学习的话请看原书。
|
||||
本文大部分内容参考 **周志明《深入理解 Java 虚拟机》** ,想要深入学习的话请看原书。
|
||||
|
||||
# 一、运行时数据区域
|
||||
|
||||
|
@ -462,11 +462,11 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和
|
|||
|
||||
包括以下 7 个阶段:
|
||||
|
||||
- **加载(Loading)**
|
||||
- **验证(Verification)**
|
||||
- **准备(Preparation)**
|
||||
- **解析(Resolution)**
|
||||
- **初始化(Initialization)**
|
||||
- **加载(Loading)**
|
||||
- **验证(Verification)**
|
||||
- **准备(Preparation)**
|
||||
- **解析(Resolution)**
|
||||
- **初始化(Initialization)**
|
||||
- 使用(Using)
|
||||
- 卸载(Unloading)
|
||||
|
||||
|
@ -521,11 +521,9 @@ public static final int value = 123;
|
|||
其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。
|
||||
|
||||
<div data="补充为什么可以支持动态绑定 --> <--"></div>
|
||||
|
||||
### 5. 初始化
|
||||
|
||||
<div data="modify -->"></div>
|
||||
|
||||
初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。
|
||||
|
||||
<clinit>() 是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。例如以下代码:
|
||||
|
@ -624,7 +622,6 @@ System.out.println(ConstClass.HELLOWORLD);
|
|||
- 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
|
||||
|
||||
<div data="modify <--"></div>
|
||||
|
||||
## 双亲委派模型
|
||||
|
||||
应用程序是由三种类加载器互相配合从而实现类加载,除此之外还可以加入自己定义的类加载器。
|
||||
|
@ -760,4 +757,6 @@ public class FileSystemClassLoader extends ClassLoader {
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
**正常实现**
|
||||
正常实现**
|
||||
|
||||
```text
|
||||
Input : [1,2,3,4,5]
|
||||
|
@ -33,11 +33,11 @@ public int binarySearch(int[] nums, int key) {
|
|||
}
|
||||
```
|
||||
|
||||
**时间复杂度**
|
||||
**时间复杂度**
|
||||
|
||||
二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。
|
||||
|
||||
**m 计算**
|
||||
**m 计算**
|
||||
|
||||
有两种计算中值 m 的方式:
|
||||
|
||||
|
@ -46,14 +46,14 @@ public int binarySearch(int[] nums, int key) {
|
|||
|
||||
l + h 可能出现加法溢出,也就是说加法的结果大于整型能够表示的范围。但是 l 和 h 都为正数,因此 h - l 不会出现加法溢出问题。所以,最好使用第二种计算法方法。
|
||||
|
||||
**未成功查找的返回值**
|
||||
**未成功查找的返回值**
|
||||
|
||||
循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值:
|
||||
|
||||
- -1:以一个错误码表示没有查找到 key
|
||||
- l:将 key 插入到 nums 中的正确位置
|
||||
|
||||
**变种**
|
||||
**变种**
|
||||
|
||||
二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下:
|
||||
|
||||
|
@ -96,7 +96,9 @@ l m h
|
|||
|
||||
# 1. 求开方
|
||||
|
||||
[69. Sqrt(x) (Easy)](https://leetcode.com/problems/sqrtx/description/)
|
||||
69\. Sqrt(x) (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/sqrtx/description/) / [力扣](https://leetcode-cn.com/problems/sqrtx/description/)
|
||||
|
||||
```html
|
||||
Input: 4
|
||||
|
@ -134,7 +136,9 @@ public int mySqrt(int x) {
|
|||
|
||||
# 2. 大于给定元素的最小元素
|
||||
|
||||
[744. Find Smallest Letter Greater Than Target (Easy)](https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/)
|
||||
744\. Find Smallest Letter Greater Than Target (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/) / [力扣](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/description/)
|
||||
|
||||
```html
|
||||
Input:
|
||||
|
@ -168,7 +172,9 @@ public char nextGreatestLetter(char[] letters, char target) {
|
|||
|
||||
# 3. 有序数组的 Single Element
|
||||
|
||||
[540. Single Element in a Sorted Array (Medium)](https://leetcode.com/problems/single-element-in-a-sorted-array/description/)
|
||||
540\. Single Element in a Sorted Array (Medium)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/single-element-in-a-sorted-array/description/) / [力扣](https://leetcode-cn.com/problems/single-element-in-a-sorted-array/description/)
|
||||
|
||||
```html
|
||||
Input: [1, 1, 2, 3, 3, 4, 4, 8, 8]
|
||||
|
@ -205,7 +211,9 @@ public int singleNonDuplicate(int[] nums) {
|
|||
|
||||
# 4. 第一个错误的版本
|
||||
|
||||
[278. First Bad Version (Easy)](https://leetcode.com/problems/first-bad-version/description/)
|
||||
278\. First Bad Version (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/first-bad-version/description/) / [力扣](https://leetcode-cn.com/problems/first-bad-version/description/)
|
||||
|
||||
题目描述:给定一个元素 n 代表有 [1, 2, ..., n] 版本,在第 x 位置开始出现错误版本,导致后面的版本都错误。可以调用 isBadVersion(int x) 知道某个版本是否错误,要求找到第一个错误的版本。
|
||||
|
||||
|
@ -230,7 +238,9 @@ public int firstBadVersion(int n) {
|
|||
|
||||
# 5. 旋转数组的最小数字
|
||||
|
||||
[153. Find Minimum in Rotated Sorted Array (Medium)](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/)
|
||||
153\. Find Minimum in Rotated Sorted Array (Medium)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/) / [力扣](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/description/)
|
||||
|
||||
```html
|
||||
Input: [3,4,5,1,2],
|
||||
|
@ -254,7 +264,9 @@ public int findMin(int[] nums) {
|
|||
|
||||
# 6. 查找区间
|
||||
|
||||
[34. Find First and Last Position of Element in Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
|
||||
34\. Find First and Last Position of Element in Sorted Array
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) / [力扣](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
|
||||
|
||||
```html
|
||||
Input: nums = [5,7,7,8,8,10], target = 8
|
||||
|
@ -293,4 +305,6 @@ private int binarySearch(int[] nums, int target) {
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
**基本原理**
|
||||
**基本原理**
|
||||
|
||||
0s 表示一串 0,1s 表示一串 1。
|
||||
|
||||
|
@ -41,17 +41,17 @@ x ^ x = 0 x & x = x x | x = x
|
|||
- \>\>\> n 为无符号右移,左边会补上 0。
|
||||
- << n 为算术左移,相当于乘以 2<sup>n</sup>。
|
||||
|
||||
** mask 计算**
|
||||
** mask 计算**
|
||||
|
||||
要获取 111111111,将 0 取反即可,\~0。
|
||||
|
||||
要得到只有第 i 位为 1 的 mask,将 1 向左移动 i-1 位即可,1<<(i-1) 。例如 1<<4 得到只有第 5 位为 1 的 mask :00010000。
|
||||
|
||||
要得到 1 到 i 位为 1 的 mask,1<<(i+1)-1 即可,例如将 1<<(4+1)-1 = 00010000-1 = 00001111。
|
||||
要得到 1 到 i 位为 1 的 mask,(1<<i)-1 即可,例如将 (1<<4)-1 = 00010000-1 = 00001111。
|
||||
|
||||
要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~(1<<(i+1)-1)。
|
||||
要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~((1<<i)-1)。
|
||||
|
||||
**Java 中的位操作**
|
||||
**Java 中的位操作**
|
||||
|
||||
```html
|
||||
static int Integer.bitCount(); // 统计 1 的数量
|
||||
|
@ -61,7 +61,9 @@ static String toBinaryString(int i); // 转换为二进制表示的字符串
|
|||
|
||||
# 1. 统计两个数的二进制表示有多少位不同
|
||||
|
||||
[461. Hamming Distance (Easy)](https://leetcode.com/problems/hamming-distance/)
|
||||
461. Hamming Distance (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/hamming-distance/) / [力扣](https://leetcode-cn.com/problems/hamming-distance/)
|
||||
|
||||
```html
|
||||
Input: x = 1, y = 4
|
||||
|
@ -114,7 +116,9 @@ public int hammingDistance(int x, int y) {
|
|||
|
||||
# 2. 数组中唯一一个不重复的元素
|
||||
|
||||
[136. Single Number (Easy)](https://leetcode.com/problems/single-number/description/)
|
||||
136\. Single Number (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/single-number/description/) / [力扣](https://leetcode-cn.com/problems/single-number/description/)
|
||||
|
||||
```html
|
||||
Input: [4,1,2,1,2]
|
||||
|
@ -133,7 +137,9 @@ public int singleNumber(int[] nums) {
|
|||
|
||||
# 3. 找出数组中缺失的那个数
|
||||
|
||||
[268. Missing Number (Easy)](https://leetcode.com/problems/missing-number/description/)
|
||||
268\. Missing Number (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/missing-number/description/) / [力扣](https://leetcode-cn.com/problems/missing-number/description/)
|
||||
|
||||
```html
|
||||
Input: [3,0,1]
|
||||
|
@ -154,7 +160,9 @@ public int missingNumber(int[] nums) {
|
|||
|
||||
# 4. 数组中不重复的两个元素
|
||||
|
||||
[260. Single Number III (Medium)](https://leetcode.com/problems/single-number-iii/description/)
|
||||
260\. Single Number III (Medium)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/single-number-iii/description/) / [力扣](https://leetcode-cn.com/problems/single-number-iii/description/)
|
||||
|
||||
两个不相等的元素在位级表示上必定会有一位存在不同。
|
||||
|
||||
|
@ -178,7 +186,9 @@ public int[] singleNumber(int[] nums) {
|
|||
|
||||
# 5. 翻转一个数的比特位
|
||||
|
||||
[190. Reverse Bits (Easy)](https://leetcode.com/problems/reverse-bits/description/)
|
||||
190\. Reverse Bits (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/reverse-bits/description/) / [力扣](https://leetcode-cn.com/problems/reverse-bits/description/)
|
||||
|
||||
```java
|
||||
public int reverseBits(int n) {
|
||||
|
@ -233,7 +243,9 @@ a = a ^ b;
|
|||
|
||||
# 7. 判断一个数是不是 2 的 n 次方
|
||||
|
||||
[231. Power of Two (Easy)](https://leetcode.com/problems/power-of-two/description/)
|
||||
231\. Power of Two (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/power-of-two/description/) / [力扣](https://leetcode-cn.com/problems/power-of-two/description/)
|
||||
|
||||
二进制表示只有一个 1 存在。
|
||||
|
||||
|
@ -253,7 +265,9 @@ public boolean isPowerOfTwo(int n) {
|
|||
|
||||
# 8. 判断一个数是不是 4 的 n 次方
|
||||
|
||||
[342. Power of Four (Easy)](https://leetcode.com/problems/power-of-four/)
|
||||
342\. Power of Four (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/power-of-four/) / [力扣](https://leetcode-cn.com/problems/power-of-four/)
|
||||
|
||||
这种数在二进制表示中有且只有一个奇数位为 1,例如 16(10000)。
|
||||
|
||||
|
@ -273,7 +287,9 @@ public boolean isPowerOfFour(int num) {
|
|||
|
||||
# 9. 判断一个数的位级表示是否不会出现连续的 0 和 1
|
||||
|
||||
[693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/)
|
||||
693\. Binary Number with Alternating Bits (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/binary-number-with-alternating-bits/description/) / [力扣](https://leetcode-cn.com/problems/binary-number-with-alternating-bits/description/)
|
||||
|
||||
```html
|
||||
Input: 10
|
||||
|
@ -298,7 +314,9 @@ public boolean hasAlternatingBits(int n) {
|
|||
|
||||
# 10. 求一个数的补码
|
||||
|
||||
[476. Number Complement (Easy)](https://leetcode.com/problems/number-complement/description/)
|
||||
476\. Number Complement (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/number-complement/description/) / [力扣](https://leetcode-cn.com/problems/number-complement/description/)
|
||||
|
||||
```html
|
||||
Input: 5
|
||||
|
@ -353,7 +371,9 @@ public int findComplement(int num) {
|
|||
|
||||
# 11. 实现整数的加法
|
||||
|
||||
[371. Sum of Two Integers (Easy)](https://leetcode.com/problems/sum-of-two-integers/description/)
|
||||
371\. Sum of Two Integers (Easy)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/sum-of-two-integers/description/) / [力扣](https://leetcode-cn.com/problems/sum-of-two-integers/description/)
|
||||
|
||||
a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。
|
||||
|
||||
|
@ -367,7 +387,9 @@ public int getSum(int a, int b) {
|
|||
|
||||
# 12. 字符串数组最大乘积
|
||||
|
||||
[318. Maximum Product of Word Lengths (Medium)](https://leetcode.com/problems/maximum-product-of-word-lengths/description/)
|
||||
318\. Maximum Product of Word Lengths (Medium)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/maximum-product-of-word-lengths/description/) / [力扣](https://leetcode-cn.com/problems/maximum-product-of-word-lengths/description/)
|
||||
|
||||
```html
|
||||
Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"]
|
||||
|
@ -402,7 +424,9 @@ public int maxProduct(String[] words) {
|
|||
|
||||
# 13. 统计从 0 \~ n 每个数的二进制表示中 1 的个数
|
||||
|
||||
[338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/)
|
||||
338\. Counting Bits (Medium)
|
||||
|
||||
[Leetcode](https://leetcode.com/problems/counting-bits/description/) / [力扣](https://leetcode-cn.com/problems/counting-bits/description/)
|
||||
|
||||
对于数字 6(110),它可以看成是 4(100) 再加一个 2(10),因此 dp[i] = dp[i&(i-1)] + 1;
|
||||
|
||||
|
@ -420,4 +444,6 @@ public int[] countBits(int num) {
|
|||
|
||||
|
||||
|
||||
<img width="650px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/other/公众号海报1.png"></img>
|
||||
|
||||
|
||||
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>
|
||||
|
|