diff --git a/.gitignore b/.gitignore index c91a56b6bc..9fdf53235c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ -.DS_Store -*.txt +.DS_Store +*.txt +!tencent13147342467085209222.txt +desktop.ini \ No newline at end of file diff --git a/README.md b/README.md index 810ab82459..e7c9e6eecb 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,142 @@ - - -|    算法    | 操作系统 |    网络    | 面向对象 |   数据库   |    Java    | 系统设计 |    工具    | 编码实践 |    后记    | -| :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:| -| [:pencil2:](#pencil2-算法) | [:computer:](#computer-操作系统)|[:cloud:](#cloud-网络) | [:art:](#art-面向对象) |[:floppy_disk:](#floppy_disk-数据库)| [:coffee:](#coffee-java)| [:bulb:](#bulb-系统设计)| [:wrench:](#wrench-工具)| [:watermelon:](#watermelon-编码实践)| [:memo:](#memo-后记) | - -
- -
- -
- -

- 本项目包含了技术面试必备的基础知识,内容浅显易懂,你不需要花很长的时间去阅读和理解成堆的技术书籍就可以快速掌握这些知识,从而节省宝贵的面试复习时间。推荐使用 https://cyc2018.github.io/CS-Notes 进行阅读,从而获得更好的阅读体验。你也可以订阅 面试进阶指南,包含了学习指导和面试技巧,让你更轻松拿到满意的 Offer。

欢迎关注公众号“CyC2018”,每天发布一道高频基础知识面试题,让你在闲暇时间也能学习进步!公众号也提供了一个学习打卡圈子,记录你每天的学习收获,见证你的成长!

- -
- -
- -## :pencil2: 算法 - -- [剑指 Offer 题解](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/剑指%20Offer%20题解%20-%20目录.md) -- [Leetcode 题解](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode%20题解%20-%20目录.md) -- [算法](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/算法%20-%20目录.md) - -## :computer: 操作系统 - -- [计算机操作系统](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/计算机操作系统%20-%20目录.md) -- [Linux](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Linux.md) - -## :cloud: 网络 - -- [计算机网络](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/计算机网络%20-%20目录.md) -- [HTTP](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/HTTP.md) -- [Socket](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Socket.md) - -## :art: 面向对象 - -- [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/设计模式.md) -- [面向对象思想](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/面向对象思想.md) - -## :floppy_disk: 数据库 - -- [数据库系统原理](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/数据库系统原理.md) -- [SQL](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/SQL.md) -- [Leetcode-Database 题解](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode-Database%20题解.md) -- [MySQL](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/MySQL.md) -- [Redis](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Redis.md) - -## :coffee: Java - -- [Java 基础](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20基础.md) -- [Java 容器](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20容器.md) -- [Java 并发](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20并发.md) -- [Java 虚拟机](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20虚拟机.md) -- [Java I/O](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Java%20IO.md) - -## :bulb: 系统设计 - -- [系统设计基础](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/系统设计基础.md) -- [分布式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/分布式.md) -- [集群](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/集群.md) -- [攻击技术](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/攻击技术.md) -- [缓存](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/缓存.md) -- [消息队列](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/消息队列.md) - -## :wrench: 工具 - -- [Git](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Git.md) -- [Docker](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Docker.md) -- [构建工具](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/构建工具.md) -- [正则表达式](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/正则表达式.md) - -## :watermelon: 编码实践 - -- [代码可读性](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码可读性.md) -- [代码风格规范](https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/代码风格规范.md) - -## :memo: 后记 - -### 内推信息 - -[Job-Recommend](https://github.com/CyC2018/Job-Recommend) - -### QQ 群 - -为大家提供一个学习交流平台,在这里你可以自由地讨论技术问题。 - - - -### 后端面试指南 - - - - - -### 排版 - -笔记内容按照 [中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines) 进行排版,以保证内容的可读性。 - -不使用 `![]()` 这种方式来引用图片,而是用 `` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 [GFM](https://github.github.com/gfm/) 不支持 `
![]()
` 这种方法让图片居中显示,只能使用 `
` 达到居中的效果。 - -在线排版工具:[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 - -本仓库内容将网上的资料随意拼凑而来,除了少部分引用书上和技术文档的原文,其余都是我的原创。在您引用本仓库内容或者对内容进行修改演绎时,请署名并以相同方式共享,谢谢。 - -知识共享许可协议 - -### Logo - -Power by [logomakr](https://logomakr.com/). - -### 致谢 - -感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。 - - -​ - - -​ - - -​ - - -​ - - -​ - - -​ - - -​ - - -​ - - -​ - - +
+ + + + + + +
+
+ +| 算法  | 操作系统 | 网络 |面向对象|  数据库  | Java  |系统设计|   工具   |编码实践|   后记   | +| :---: | :----: | :---: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | +| [:pencil2:](#pencil2-算法) | [:computer:](#computer-操作系统) | [:cloud:](#cloud-网络) | [:art:](#art-面向对象) | [:floppy_disk:](#floppy_disk-数据库) |[:coffee:](#coffee-java)| [:bulb:](#bulb-系统设计) |[:wrench:](#wrench-工具)| [:watermelon:](#watermelon-编码实践) |[:memo:](#memo-后记)| + +
+ +
+ +
+ +
+ +## :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) +- [字节跳动内推](assets/内推.md) + +## :computer: 操作系统 + +- [计算机操作系统](https://github.com/CyC2018/CS-Notes/blob/master/notes/计算机操作系统%20-%20目录.md) +- [Linux](https://github.com/CyC2018/CS-Notes/blob/master/notes/Linux.md) + +## :cloud: 网络 + +- [计算机网络](https://github.com/CyC2018/CS-Notes/blob/master/notes/计算机网络%20-%20目录.md) +- [HTTP](https://github.com/CyC2018/CS-Notes/blob/master/notes/HTTP.md) +- [Socket](https://github.com/CyC2018/CS-Notes/blob/master/notes/Socket.md) + +## :floppy_disk: 数据库 + +- [数据库系统原理](https://github.com/CyC2018/CS-Notes/blob/master/notes/数据库系统原理.md) +- [SQL 语法](https://github.com/CyC2018/CS-Notes/blob/master/notes/SQL%20语法.md) +- [SQL 练习](https://github.com/CyC2018/CS-Notes/blob/master/notes/SQL%20练习.md) +- [MySQL](https://github.com/CyC2018/CS-Notes/blob/master/notes/MySQL.md) +- [Redis](https://github.com/CyC2018/CS-Notes/blob/master/notes/Redis.md) + +## :coffee: Java + +- [Java 基础](https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20基础.md) +- [Java 容器](https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20容器.md) +- [Java 并发](https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20并发.md) +- [Java 虚拟机](https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20虚拟机.md) +- [Java I/O](https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20IO.md) + +## :bulb: 系统设计 + +- [系统设计基础](https://github.com/CyC2018/CS-Notes/blob/master/notes/系统设计基础.md) +- [分布式](https://github.com/CyC2018/CS-Notes/blob/master/notes/分布式.md) +- [集群](https://github.com/CyC2018/CS-Notes/blob/master/notes/集群.md) +- [攻击技术](https://github.com/CyC2018/CS-Notes/blob/master/notes/攻击技术.md) +- [缓存](https://github.com/CyC2018/CS-Notes/blob/master/notes/缓存.md) +- [消息队列](https://github.com/CyC2018/CS-Notes/blob/master/notes/消息队列.md) + +## :art: 面向对象 + +- [面向对象思想](https://github.com/CyC2018/CS-Notes/blob/master/notes/面向对象思想.md) +- [设计模式](https://github.com/CyC2018/CS-Notes/blob/master/notes/设计模式%20-%20目录.md) + +## :wrench: 工具 + +- [Git](https://github.com/CyC2018/CS-Notes/blob/master/notes/Git.md) +- [Docker](https://github.com/CyC2018/CS-Notes/blob/master/notes/Docker.md) +- [构建工具](https://github.com/CyC2018/CS-Notes/blob/master/notes/构建工具.md) +- [正则表达式](https://github.com/CyC2018/CS-Notes/blob/master/notes/正则表达式.md) + +## :watermelon: 编码实践 + +- [代码可读性](https://github.com/CyC2018/CS-Notes/blob/master/notes/代码可读性.md) +- [代码风格规范](https://github.com/CyC2018/CS-Notes/blob/master/notes/代码风格规范.md) + +## :memo: 后记 + +### 排版 + +笔记内容按照 [中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines/blob/master/README.zh-CN.md) 进行排版,以保证内容的可读性。 + +不使用 `![]()` 这种方式来引用图片,而是用 `` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 [GFM](https://github.github.com/gfm/) 不支持 `
![]()
` 这种方法让图片居中显示,只能使用 `
` 达到居中的效果。 + +在线排版工具:[Text-Typesetting](https://github.com/CyC2018/Text-Typesetting)。 + +### License + +本仓库的内容不是将网上的资料随意拼凑而来,除了少部分引用书上和技术文档的原文(这部分内容都在末尾的参考链接中加了出处),其余都是我的原创。在您引用本仓库内容或者对内容进行修改演绎时,请署名并以相同方式共享,谢谢。 + +转载文章请在开头明显处标明该页面地址,公众号等其它转载请联系 zhengyc101@163.com。 + +Logo:[logomakr](https://logomakr.com/) + +知识共享许可协议 + +### 致谢 + +感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与我联系。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/download-html.png b/assets/download-html.png new file mode 100644 index 0000000000..75c5ec1a30 Binary files /dev/null and b/assets/download-html.png differ diff --git a/assets/download-markdown.png b/assets/download-markdown.png new file mode 100644 index 0000000000..df18175c0d Binary files /dev/null and b/assets/download-markdown.png differ diff --git a/assets/download-pdf.png b/assets/download-pdf.png new file mode 100644 index 0000000000..3b14a633f2 Binary files /dev/null and b/assets/download-pdf.png differ diff --git a/assets/download.md b/assets/download.md new file mode 100644 index 0000000000..c28bfa444b --- /dev/null +++ b/assets/download.md @@ -0,0 +1,33 @@ +# 目的 + +考虑到有部分读者的网络环境较差,有时候在线访问速度很慢,导致阅读体验不佳。另外,PDF 等格式的离线版本相比于网页在线版本更方便做笔记。因此提供离线阅读版本给大家下载。 + +# 内容 + +有三种格式的离线版本:PDF、Markdown 和 HTML 。 + +## PDF + +优点是方便做笔记;缺点是不能显示 GIF 图片(所以“剑指 Offer 题解”不建议使用 PDF 进行阅读),以及显示效果不佳。 + +![](download-pdf.png) + +## Markdown + +优点是能很好地显示 GIF 图片,显示效果也很好;缺点是由于将所有内容整合在同一个文件中,导致实时渲染有点卡顿。 + +![](download-markdown.png) + +## HTML + +优点是和 Markdown 的显示效果几乎一致,同时不需要 Markdown 的实时渲染,因此浏览速度更快;缺点是目录功能还不是很完善。 + +如果想在安卓手机端阅读,推荐使用这种格式,将 html 文件和图片文件都复制到手机上,用浏览器打开 html 文件并存成书签,以后就可以快速地离线阅读。 + +![](download-html.png) + +# 如何下载 + +离线版本由公众号 **CyC2018** 发布,最新版本也会在上面及时发布,在后台回复 **CyC** 即可获取下载链接。 + +
diff --git a/assets/group1.png b/assets/group1.png deleted file mode 100644 index 4615c5f93a..0000000000 Binary files a/assets/group1.png and /dev/null differ diff --git "a/assets/\344\273\212\346\227\245\345\244\264\346\235\241\346\213\233\350\201\230\346\265\267\346\212\245.png" "b/assets/\344\273\212\346\227\245\345\244\264\346\235\241\346\213\233\350\201\230\346\265\267\346\212\245.png" new file mode 100644 index 0000000000..3c30d5cb6a Binary files /dev/null and "b/assets/\344\273\212\346\227\245\345\244\264\346\235\241\346\213\233\350\201\230\346\265\267\346\212\245.png" differ diff --git "a/assets/\345\205\254\344\274\227\345\217\267.jpg" "b/assets/\345\205\254\344\274\227\345\217\267.jpg" deleted file mode 100644 index ab57e6b2dd..0000000000 Binary files "a/assets/\345\205\254\344\274\227\345\217\267.jpg" and /dev/null differ diff --git "a/assets/\345\205\254\344\274\227\345\217\2671.jpg" "b/assets/\345\205\254\344\274\227\345\217\2671.jpg" deleted file mode 100644 index 14e98bfca0..0000000000 Binary files "a/assets/\345\205\254\344\274\227\345\217\2671.jpg" and /dev/null differ diff --git "a/assets/\345\205\254\344\274\227\345\217\267\344\272\214\347\273\264\347\240\201-2.png" "b/assets/\345\205\254\344\274\227\345\217\267\344\272\214\347\273\264\347\240\201-2.png" new file mode 100644 index 0000000000..fec66b80ca Binary files /dev/null and "b/assets/\345\205\254\344\274\227\345\217\267\344\272\214\347\273\264\347\240\201-2.png" differ diff --git "a/assets/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2457.png" "b/assets/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2457.png" new file mode 100644 index 0000000000..8f33bd5d20 Binary files /dev/null and "b/assets/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2457.png" differ diff --git "a/assets/\345\206\205\346\216\250.md" "b/assets/\345\206\205\346\216\250.md" new file mode 100644 index 0000000000..2c3fd6fdaa --- /dev/null +++ "b/assets/\345\206\205\346\216\250.md" @@ -0,0 +1,3 @@ +https://job.toutiao.com/s/iVYD4ht + + diff --git "a/assets/\345\260\217\344\270\223\346\240\217.jpg" "b/assets/\345\260\217\344\270\223\346\240\217.jpg" deleted file mode 100644 index 5c1c880ffd..0000000000 Binary files "a/assets/\345\260\217\344\270\223\346\240\217.jpg" and /dev/null differ diff --git "a/assets/\347\211\233\345\256\242\347\275\221.png" "b/assets/\347\211\233\345\256\242\347\275\221.png" deleted file mode 100644 index 02f8199710..0000000000 Binary files "a/assets/\347\211\233\345\256\242\347\275\221.png" and /dev/null differ diff --git "a/assets/\347\237\245\344\271\216.jpg" "b/assets/\347\237\245\344\271\216.jpg" deleted file mode 100644 index 92cc845960..0000000000 Binary files "a/assets/\347\237\245\344\271\216.jpg" and /dev/null differ diff --git "a/assets/\347\237\245\350\257\206\346\230\237\347\220\203.png" "b/assets/\347\237\245\350\257\206\346\230\237\347\220\203.png" deleted file mode 100644 index 097f6a1cb2..0000000000 Binary files "a/assets/\347\237\245\350\257\206\346\230\237\347\220\203.png" and /dev/null differ diff --git a/docs/README.md b/docs/README.md index 96b3365314..756b142235 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,62 +1,2 @@ -- [点击订阅面试进阶指南](https://xiaozhuanlan.com/CyC2018) - -## ✏️ 算法 - -- [剑指 Offer 题解](notes/剑指%20Offer%20题解%20-%20目录1.md)
-- [Leetcode 题解](notes/Leetcode%20题解%20-%20目录1.md)
-- [算法](notes/算法%20-%20目录1.md)
-- [点击订阅面试进阶指南](https://xiaozhuanlan.com/CyC2018) - -## 💻 操作系统 - -- [计算机操作系统](notes/计算机操作系统%20-%20目录1.md)
-- [Linux](notes/Linux.md) - -## ☁️ 网络 - -- [计算机网络](notes/计算机网络%20-%20目录1.md)
-- [HTTP](notes/HTTP.md)
-- [Socket](notes/Socket.md) - -## 🎨 面向对象 - -- [设计模式](notes/设计模式.md)
-- [面向对象思想](notes/面向对象思想.md) - -## 💾 数据库 - -- [数据库系统原理](notes/数据库系统原理.md)
-- [SQL](notes/SQL.md)
-- [Leetcode-Database 题解](notes/Leetcode-Database%20题解.md)
-- [MySQL](notes/MySQL.md)
-- [Redis](notes/Redis.md) - -## ☕️ Java - -- [Java 基础](notes/Java%20基础.md)
-- [Java 容器](notes/Java%20容器.md)
-- [Java 并发](notes/Java%20并发.md)
-- [Java 虚拟机](notes/Java%20虚拟机.md)
-- [Java I/O](notes/Java%20IO.md) - -## 💡 系统设计 - -- [系统设计基础](notes/系统设计基础.md)
-- [分布式](notes/分布式.md)
-- [集群](notes/集群.md)
-- [攻击技术](notes/攻击技术.md)
-- [缓存](notes/缓存.md)
-- [消息队列](notes/消息队列.md) - -## 🔧 工具 - -- [Git](notes/Git.md)
-- [Docker](notes/Docker.md)
-- [正则表达式](notes/正则表达式.md)
-- [构建工具](notes/构建工具.md) - - - -欢迎关注 公众号 “CyC2018” ,每天发布一道高频基础知识面试题,让你在闲暇时间也能学习进步!公众号也提供了一个学习打卡圈子,记录你每天的学习收获,见证你的成长! - -![](https://cyc-1256109796.cos.ap-guangzhou.myqcloud.com/%E5%85%AC%E4%BC%97%E5%8F%B7.jpg) +# 😃 该网站已迁移至 >>> [www.cyc2018.xyz](http://www.cyc2018.xyz) + diff --git a/docs/_404.md b/docs/_404.md new file mode 100644 index 0000000000..831dac82e5 --- /dev/null +++ b/docs/_404.md @@ -0,0 +1 @@ +# 😃 该网站已迁移至 >>> [www.cyc2018.xyz](http://www.cyc2018.xyz) \ No newline at end of file diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 6906cd0d7b..df3eaa882a 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,12 +1,11 @@ - - -# CS-Notes - -- 本项目包含了技术面试必备的基础知识,内容浅显易懂,你不需要花很长的时间去阅读和理解成堆的技术书籍就可以快速掌握这些知识,从而节省宝贵的面试复习时间。 - - - -[![stars](https://badgen.net/github/stars/CyC2018/CS-Notes?icon=github&color=4ab8a1)](https://github.com/CyC2018/CS-Notes) [![forks](https://badgen.net/github/forks/CyC2018/CS-Notes?icon=github&color=4ab8a1)](https://github.com/CyC2018/CS-Notes) - -[Get Started](README.md) - + + + +- 本项目包含了技术面试必备的基础知识,内容浅显易懂,你不需要花很长的时间去阅读和理解成堆的技术书籍就可以快速掌握这些知识,从而节省宝贵的面试复习时间。 + + + +[![stars](https://badgen.net/github/stars/CyC2018/CS-Notes?icon=github&color=4ab8a1)](https://github.com/CyC2018/CS-Notes) [![forks](https://badgen.net/github/forks/CyC2018/CS-Notes?icon=github&color=4ab8a1)](https://github.com/CyC2018/CS-Notes) + +[开始阅读](http://www.cyc2018.xyz) + diff --git a/docs/_style/style.css b/docs/_style/style.css deleted file mode 100644 index 0b87ac126c..0000000000 --- a/docs/_style/style.css +++ /dev/null @@ -1,81 +0,0 @@ -/*隐藏头部的目录*/ -#main>ul:nth-child(1) { - display: none; -} - -#main>ul:nth-child(2) { - display: none; -} - -.markdown-section h1 { - margin: 3rem 0 2rem 0; -} - -.markdown-section h2 { - margin: 2rem 0 1rem; -} - -.content, -.sidebar, -.markdown-section, -body, -.search input, -.sidebar-toggle { - background-color: rgba(243, 242, 238, 1) !important; -} - -body { - /*font-family: Microsoft YaHei, Source Sans Pro, Helvetica Neue, Arial, sans-serif !important;*/ -} - -.markdown-section>p { - font-size: 16px !important; -} - -.markdown-section pre>code { - font-family: Consolas, Roboto Mono, Monaco, courier, monospace !important; - font-size: .9rem !important; - -} - -/*.anchor span { - color: rgb(66, 185, 131); -}*/ - -section.cover h1 { - margin: 0; -} - -body>section>div.cover-main>ul>li>a { - color: #42b983; -} - -.markdown-section img { - box-shadow: 7px 9px 10px #aaa !important; -} - - -pre { - background-color: #f3f2ee !important; -} - -@media (min-width:600px) { - pre code { - /*box-shadow: 2px 1px 20px 2px #aaa;*/ - /*border-radius: 10px !important;*/ - padding-left: 20px !important; - } -} - -@media (max-width:600px) { - pre { - padding-left: 0px !important; - padding-right: 0px !important; - } -} - -.markdown-section pre { - padding-left: 0 !important; - padding-right: 0px !important; - box-shadow: 2px 1px 20px 2px #aaa; -} \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 3a23ed3ab1..c810d4c988 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,397 +1,440 @@ - - - - - - CS-Notes - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - + + + + + + CS-Notes + + + + + + + + + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/notes/Docker.md b/docs/notes/Docker.md deleted file mode 100644 index b0945c54c3..0000000000 --- a/docs/notes/Docker.md +++ /dev/null @@ -1,97 +0,0 @@ - -* [一、解决的问题](#一解决的问题) -* [二、与虚拟机的比较](#二与虚拟机的比较) -* [三、优势](#三优势) -* [四、使用场景](#四使用场景) -* [五、镜像与容器](#五镜像与容器) -* [参考资料](#参考资料) - - - -# 一、解决的问题 - -由于不同的机器有不同的操作系统,以及不同的库和组件,在将一个应用部署到多台机器上需要进行大量的环境配置操作。 - -Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其他机器中。 - -

- -# 二、与虚拟机的比较 - -虚拟机也是一种虚拟化技术,它与 Docker 最大的区别在于它是通过模拟硬件,并在硬件上安装操作系统来实现。 - -

- -

- -## 启动速度 - -启动虚拟机需要启动虚拟机的操作系统,再启动应用,这个过程非常慢; - -而启动 Docker 相当于启动宿主操作系统上的一个进程。 - -## 占用资源 - -虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU,一台机器只能开启几十个的虚拟机。 - -而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 - -# 三、优势 - -除了启动速度快以及占用资源少之外,Docker 具有以下优势: - -## 更容易迁移 - -提供一致性的运行环境,可以在不同的机器上进行迁移,而不用担心环境变化导致无法运行。 - -## 更容易维护 - -使用分层技术和镜像,使得应用可以更容易复用重复部分。复用程度越高,维护工作也越容易。 - -## 更容易扩展 - -可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。 - -# 四、使用场景 - -## 持续集成 - -持续集成指的是频繁地将代码集成到主干上,这样能够更快地发现错误。 - -Docker 具有轻量级以及隔离性的特点,在将代码集成到一个 Docker 中不会对其它 Docker 产生影响。 - -## 提供可伸缩的云服务 - -根据应用的负载情况,可以很容易地增加或者减少 Docker。 - -## 搭建微服务架构 - -Docker 轻量级的特点使得它很适合用于部署、维护、组合微服务。 - -# 五、镜像与容器 - -镜像是一种静态的结构,可以看成面向对象里面的类,而容器是镜像的一个实例。 - -镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 - -构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 - -

- -# 参考资料 - -- [DOCKER 101: INTRODUCTION TO DOCKER WEBINAR RECAP](https://blog.docker.com/2017/08/docker-101-introduction-docker-webinar-recap/) -- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) -- [Docker container vs Virtual machine](http://www.bogotobogo.com/DevOps/Docker/Docker_Container_vs_Virtual_Machine.php) -- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) -- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) -- [为什么要使用 Docker?](https://yeasy.gitbooks.io/docker_practice/introduction/why.html) -- [What is Docker](https://www.docker.com/what-docker) -- [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/Git.md b/docs/notes/Git.md deleted file mode 100644 index a728f27f5c..0000000000 --- a/docs/notes/Git.md +++ /dev/null @@ -1,166 +0,0 @@ - -* [集中式与分布式](#集中式与分布式) -* [中心服务器](#中心服务器) -* [工作流](#工作流) -* [分支实现](#分支实现) -* [冲突](#冲突) -* [Fast forward](#fast-forward) -* [分支管理策略](#分支管理策略) -* [储藏(Stashing)](#储藏stashing) -* [SSH 传输设置](#ssh-传输设置) -* [.gitignore 文件](#gitignore-文件) -* [Git 命令一览](#git-命令一览) -* [参考资料](#参考资料) - - - -# 集中式与分布式 - -Git 属于分布式版本控制系统,而 SVN 属于集中式。 - -

- -集中式版本控制只有中心服务器拥有一份代码,而分布式版本控制每个人的电脑上就有一份完整的代码。 - -集中式版本控制有安全性问题,当中心服务器挂了所有人都没办法工作了。 - -集中式版本控制需要连网才能工作,如果网速过慢,那么提交一个文件的会慢的无法让人忍受。而分布式版本控制不需要连网就能工作。 - -分布式版本控制新建分支、合并分支操作速度非常快,而集中式版本控制新建一个分支相当于复制一份完整代码。 - -# 中心服务器 - -中心服务器用来交换每个用户的修改,没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。 - -Github 就是一个中心服务器。 - -# 工作流 - -新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。 - -Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库,History 中存有所有分支,使用一个 HEAD 指针指向当前分支。 - -

- -- git add files 把文件的修改添加到暂存区 -- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了 -- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files -- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改 - -

- -可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。 - -- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交 -- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作 - -

- -# 分支实现 - -使用指针将每个提交连接成一条时间线,HEAD 指针指向当前分支指针。 - -

- -新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。 - -

- -每次提交只会让当前分支指针向前移动,而其它分支指针不会移动。 - -

- -合并分支也只需要改变指针即可。 - -

- -# 冲突 - -当两个分支都对同一个文件的同一行进行了修改,在分支合并时就会产生冲突。 - -

- -Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。 - -``` -<<<<<<< HEAD -Creating a new branch is quick & simple. -======= -Creating a new branch is quick AND simple. ->>>>>>> feature1 -``` - -# Fast forward - -"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。 - -可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。 - -``` -$ git merge --no-ff -m "merge with no-ff" dev -``` - -

- -# 分支管理策略 - -master 分支应该是非常稳定的,只用来发布新版本; - -日常开发在开发分支 dev 上进行。 - -

- -# 储藏(Stashing) - -在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。 - -可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈上,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。 - -``` -$ git stash -Saved working directory and index state \ "WIP on master: 049d078 added the index file" -HEAD is now at 049d078 added the index file (To restore them type "git stash apply") -``` - -该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。 - -# SSH 传输设置 - -Git 仓库和 Github 中心仓库之间的传输是通过 SSH 加密。 - -如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key: - -``` -$ ssh-keygen -t rsa -C "youremail@example.com" -``` - -然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。 - -# .gitignore 文件 - -忽略以下文件: - -- 操作系统自动生成的文件,比如缩略图; -- 编译生成的中间文件,比如 Java 编译产生的 .class 文件; -- 自己的敏感信息,比如存放口令的配置文件。 - -不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。 - -# Git 命令一览 - -

- -比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf - -# 参考资料 - -- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) -- [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) -- [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) -- [Learn Git Branching](https://learngitbranching.js.org/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/HTTP.md b/docs/notes/HTTP.md deleted file mode 100644 index 3bcdafb813..0000000000 --- a/docs/notes/HTTP.md +++ /dev/null @@ -1,885 +0,0 @@ - -* [一 、基础概念](#一-基础概念) - * [URI](#uri) - * [请求和响应报文](#请求和响应报文) -* [二、HTTP 方法](#二http-方法) - * [GET](#get) - * [HEAD](#head) - * [POST](#post) - * [PUT](#put) - * [PATCH](#patch) - * [DELETE](#delete) - * [OPTIONS](#options) - * [CONNECT](#connect) - * [TRACE](#trace) -* [三、HTTP 状态码](#三http-状态码) - * [1XX 信息](#1xx-信息) - * [2XX 成功](#2xx-成功) - * [3XX 重定向](#3xx-重定向) - * [4XX 客户端错误](#4xx-客户端错误) - * [5XX 服务器错误](#5xx-服务器错误) -* [四、HTTP 首部](#四http-首部) - * [通用首部字段](#通用首部字段) - * [请求首部字段](#请求首部字段) - * [响应首部字段](#响应首部字段) - * [实体首部字段](#实体首部字段) -* [五、具体应用](#五具体应用) - * [连接管理](#连接管理) - * [Cookie](#cookie) - * [缓存](#缓存) - * [内容协商](#内容协商) - * [内容编码](#内容编码) - * [范围请求](#范围请求) - * [分块传输编码](#分块传输编码) - * [多部分对象集合](#多部分对象集合) - * [虚拟主机](#虚拟主机) - * [通信数据转发](#通信数据转发) -* [六、HTTPS](#六https) - * [加密](#加密) - * [认证](#认证) - * [完整性保护](#完整性保护) - * [HTTPS 的缺点](#https-的缺点) -* [七、HTTP/2.0](#七http20) - * [HTTP/1.x 缺陷](#http1x-缺陷) - * [二进制分帧层](#二进制分帧层) - * [服务端推送](#服务端推送) - * [首部压缩](#首部压缩) -* [八、HTTP/1.1 新特性](#八http11-新特性) -* [九、GET 和 POST 比较](#九get-和-post-比较) - * [作用](#作用) - * [参数](#参数) - * [安全](#安全) - * [幂等性](#幂等性) - * [可缓存](#可缓存) - * [XMLHttpRequest](#xmlhttprequest) -* [参考资料](#参考资料) - - - -# 一 、基础概念 - -## URI - -URI 包含 URL 和 URN。 - -

- -## 请求和响应报文 - -### 1. 请求报文 - -

- -### 2. 响应报文 - -

- -# 二、HTTP 方法 - -客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 - -## GET - -> 获取资源 - -当前网络请求中,绝大部分使用的是 GET 方法。 - -## HEAD - -> 获取报文首部 - -和 GET 方法类似,但是不返回报文实体主体部分。 - -主要用于确认 URL 的有效性以及资源更新的日期时间等。 - -## POST - -> 传输实体主体 - -POST 主要用来传输数据,而 GET 主要用来获取资源。 - -更多 POST 与 GET 的比较请见第九章。 - -## PUT - -> 上传文件 - -由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。 - -```html -PUT /new.html HTTP/1.1 -Host: example.com -Content-type: text/html -Content-length: 16 - -

New File

-``` - -## PATCH - -> 对资源进行部分修改 - -PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。 - -```html -PATCH /file.txt HTTP/1.1 -Host: www.example.com -Content-Type: application/example -If-Match: "e0023aa4e" -Content-Length: 100 - -[description of changes] -``` - -## DELETE - -> 删除文件 - -与 PUT 功能相反,并且同样不带验证机制。 - -```html -DELETE /file.html HTTP/1.1 -``` - -## OPTIONS - -> 查询支持的方法 - -查询指定的 URL 能够支持的方法。 - -会返回 `Allow: GET, POST, HEAD, OPTIONS` 这样的内容。 - -## CONNECT - -> 要求在与代理服务器通信时建立隧道 - -使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 - -```html -CONNECT www.example.com:443 HTTP/1.1 -``` - -

- -## TRACE - -> 追踪路径 - -服务器会将通信路径返回给客户端。 - -发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。 - -通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。 - -# 三、HTTP 状态码 - -服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 - -| 状态码 | 类别 | 含义 | -| :---: | :---: | :---: | -| 1XX | Informational(信息性状态码) | 接收的请求正在处理 | -| 2XX | Success(成功状态码) | 请求正常处理完毕 | -| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | -| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | -| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | - -## 1XX 信息 - -- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 - -## 2XX 成功 - -- **200 OK** - -- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 - -- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 - -## 3XX 重定向 - -- **301 Moved Permanently** :永久性重定向 - -- **302 Found** :临时性重定向 - -- **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 状态码。 - -- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 - -## 4XX 客户端错误 - -- **400 Bad Request** :请求报文中存在语法错误。 - -- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 - -- **403 Forbidden** :请求被拒绝。 - -- **404 Not Found** - -## 5XX 服务器错误 - -- **500 Internal Server Error** :服务器正在执行请求时发生错误。 - -- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 - -# 四、HTTP 首部 - -有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。 - -各种首部字段及其含义如下(不需要全记,仅供查阅): - -## 通用首部字段 - -| 首部字段名 | 说明 | -| :--: | :--: | -| Cache-Control | 控制缓存的行为 | -| Connection | 控制不再转发给代理的首部字段、管理持久连接| -| Date | 创建报文的日期时间 | -| Pragma | 报文指令 | -| Trailer | 报文末端的首部一览 | -| Transfer-Encoding | 指定报文主体的传输编码方式 | -| Upgrade | 升级为其他协议 | -| Via | 代理服务器的相关信息 | -| Warning | 错误通知 | - -## 请求首部字段 - -| 首部字段名 | 说明 | -| :--: | :--: | -| Accept | 用户代理可处理的媒体类型 | -| Accept-Charset | 优先的字符集 | -| Accept-Encoding | 优先的内容编码 | -| Accept-Language | 优先的语言(自然语言) | -| Authorization | Web 认证信息 | -| Expect | 期待服务器的特定行为 | -| From | 用户的电子邮箱地址 | -| Host | 请求资源所在服务器 | -| If-Match | 比较实体标记(ETag) | -| If-Modified-Since | 比较资源的更新时间 | -| If-None-Match | 比较实体标记(与 If-Match 相反) | -| If-Range | 资源未更新时发送实体 Byte 的范围请求 | -| If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) | -| Max-Forwards | 最大传输逐跳数 | -| Proxy-Authorization | 代理服务器要求客户端的认证信息 | -| Range | 实体的字节范围请求 | -| Referer | 对请求中 URI 的原始获取方 | -| TE | 传输编码的优先级 | -| User-Agent | HTTP 客户端程序的信息 | - -## 响应首部字段 - -| 首部字段名 | 说明 | -| :--: | :--: | -| Accept-Ranges | 是否接受字节范围请求 | -| Age | 推算资源创建经过时间 | -| ETag | 资源的匹配信息 | -| Location | 令客户端重定向至指定 URI | -| Proxy-Authenticate | 代理服务器对客户端的认证信息 | -| Retry-After | 对再次发起请求的时机要求 | -| Server | HTTP 服务器的安装信息 | -| Vary | 代理服务器缓存的管理信息 | -| WWW-Authenticate | 服务器对客户端的认证信息 | - -## 实体首部字段 - -| 首部字段名 | 说明 | -| :--: | :--: | -| Allow | 资源可支持的 HTTP 方法 | -| Content-Encoding | 实体主体适用的编码方式 | -| Content-Language | 实体主体的自然语言 | -| Content-Length | 实体主体的大小 | -| Content-Location | 替代对应资源的 URI | -| Content-MD5 | 实体主体的报文摘要 | -| Content-Range | 实体主体的位置范围 | -| Content-Type | 实体主体的媒体类型 | -| Expires | 实体主体过期的日期时间 | -| Last-Modified | 资源的最后修改日期时间 | - -# 五、具体应用 - -## 连接管理 - -

- -### 1. 短连接与长连接 - -当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。 - -长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。 - -- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 `Connection : close`; -- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 `Connection : Keep-Alive`。 - -### 2. 流水线 - -默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 - -流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。 - -## Cookie - -HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 - -Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 - -Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API(本地存储和会话存储)或 IndexedDB。 - -### 1. 用途 - -- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) -- 个性化设置(如用户自定义设置、主题等) -- 浏览器行为跟踪(如跟踪分析用户行为等) - -### 2. 创建过程 - -服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 - -```html -HTTP/1.0 200 OK -Content-type: text/html -Set-Cookie: yummy_cookie=choco -Set-Cookie: tasty_cookie=strawberry - -[page content] -``` - -客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。 - -```html -GET /sample_page.html HTTP/1.1 -Host: www.example.org -Cookie: yummy_cookie=choco; tasty_cookie=strawberry -``` - -### 3. 分类 - -- 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 -- 持久性 Cookie:指定一个特定的过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 - -```html -Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; -``` - -### 4. 作用域 - -Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。 - -Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配: - -- /docs -- /docs/Web/ -- /docs/Web/HTTP - -### 5. JavaScript - -通过 `document.cookie` 属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。 - -```html -document.cookie = "yummy_cookie=choco"; -document.cookie = "tasty_cookie=strawberry"; -console.log(document.cookie); -``` - -### 6. HttpOnly - -标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 - -```html -Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly -``` - -### 7. Secure - -标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 - -### 8. Session - -除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 - -Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。 - -使用 Session 维护用户登录状态的过程如下: - -- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; -- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID; -- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; -- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。 - -应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。 - -### 9. 浏览器禁用 Cookie - -此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。 - -### 10. Cookie 与 Session 选择 - -- Cookie 只能存储 ASCII 码字符串,而 Session 则可以存取任何类型的数据,因此在考虑数据复杂性时首选 Session; -- Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密; -- 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。 - -## 缓存 - -### 1. 优点 - -- 缓解服务器压力; -- 降低客户端获取资源的延迟:缓存通常位于内存中,读取缓存的速度更快。并且缓存在地理位置上也有可能比源服务器来得近,例如浏览器缓存。 - -### 2. 实现方法 - -- 让代理服务器进行缓存; -- 让客户端浏览器进行缓存。 - -### 3. Cache-Control - -HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。 - -**3.1 禁止进行缓存** - -no-store 指令规定不能对请求或响应的任何一部分进行缓存。 - -```html -Cache-Control: no-store -``` - -**3.2 强制确认缓存** - -no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效才将能使用该缓存对客户端的请求进行响应。 - -```html -Cache-Control: no-cache -``` - -**3.3 私有缓存和公共缓存** - -private 指令规定了将资源作为私有缓存,只能被单独用户所使用,一般存储在用户浏览器中。 - -```html -Cache-Control: private -``` - -public 指令规定了将资源作为公共缓存,可以被多个用户所使用,一般存储在代理服务器中。 - -```html -Cache-Control: public -``` - -**3.4 缓存过期机制** - -max-age 指令出现在请求报文中,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。 - -max-age 指令出现在响应报文中,表示缓存资源在缓存服务器中保存的时间。 - -```html -Cache-Control: max-age=31536000 -``` - -Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。 - -```html -Expires: Wed, 04 Jul 2012 08:26:05 GMT -``` - -- 在 HTTP/1.1 中,会优先处理 max-age 指令; -- 在 HTTP/1.0 中,max-age 指令会被忽略掉。 - -### 4. 缓存验证 - -需要先了解 ETag 首部字段的含义,它是资源的唯一标识。URL 不能唯一表示资源,例如 `http://www.google.com/` 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一标识。 - -```html -ETag: "82e22293907ce725faf67773957acd12" -``` - -可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,返回 304 Not Modified。 - -```html -If-None-Match: "82e22293907ce725faf67773957acd12" -``` - -Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。 - -```html -Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT -``` - -```html -If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT -``` - -## 内容协商 - -通过内容协商返回最合适的内容,例如根据浏览器的默认语言选择返回中文界面还是英文界面。 - -### 1. 类型 - -**1.1 服务端驱动型** - -客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。 - -它存在以下问题: - -- 服务器很难知道客户端浏览器的全部信息; -- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术); -- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。 - -**1.2 代理驱动型** - -服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。 - -### 2. Vary - -```html -Vary: Accept-Language -``` - -在使用内容协商的情况下,只有当缓存服务器中的缓存满足内容协商条件时,才能使用该缓存,否则应该向源服务器请求该资源。 - -例如,一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 `Vary: Accept-Language` 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存。 - -## 内容编码 - -内容编码将实体主体进行压缩,从而减少传输的数据量。 - -常用的内容编码有:gzip、compress、deflate、identity。 - -浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应的 Vary 首部至少要包含 Content-Encoding。 - -## 范围请求 - -如果网络出现中断,服务器只发送了一部分数据,范围请求可以使得客户端只请求服务器未发送的那部分数据,从而避免服务器重新发送所有数据。 - -### 1. Range - -在请求报文中添加 Range 首部字段指定请求的范围。 - -```html -GET /z4d4kWk.jpg HTTP/1.1 -Host: i.imgur.com -Range: bytes=0-1023 -``` - -请求成功的话服务器返回的响应包含 206 Partial Content 状态码。 - -```html -HTTP/1.1 206 Partial Content -Content-Range: bytes 0-1023/146515 -Content-Length: 1024 -... -(binary content) -``` - -### 2. Accept-Ranges - -响应首部字段 Accept-Ranges 用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none。 - -```html -Accept-Ranges: bytes -``` - -### 3. 响应状态码 - -- 在请求成功的情况下,服务器会返回 206 Partial Content 状态码。 -- 在请求的范围越界的情况下,服务器会返回 416 Requested Range Not Satisfiable 状态码。 -- 在不支持范围请求的情况下,服务器会返回 200 OK 状态码。 - -## 分块传输编码 - -Chunked Transfer Coding,可以把数据分割成多块,让浏览器逐步显示页面。 - -## 多部分对象集合 - -一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔,每个部分都可以有首部字段。 - -例如,上传多个表单时可以使用如下方式: - -```html -Content-Type: multipart/form-data; boundary=AaB03x - ---AaB03x -Content-Disposition: form-data; name="submit-name" - -Larry ---AaB03x -Content-Disposition: form-data; name="files"; filename="file1.txt" -Content-Type: text/plain - -... contents of file1.txt ... ---AaB03x-- -``` - -## 虚拟主机 - -HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 - -## 通信数据转发 - -### 1. 代理 - -代理服务器接受客户端的请求,并且转发给其它服务器。 - -使用代理的主要目的是: - -- 缓存 -- 负载均衡 -- 网络访问控制 -- 访问日志记录 - -代理服务器分为正向代理和反向代理两种: - -- 用户察觉得到正向代理的存在。 - -

- -- 而反向代理一般位于内部网络中,用户察觉不到。 - -

- -### 2. 网关 - -与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。 - -### 3. 隧道 - -使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路。 - -# 六、HTTPS - -HTTP 有以下安全性问题: - -- 使用明文进行通信,内容可能会被窃听; -- 不验证通信方的身份,通信方的身份有可能遭遇伪装; -- 无法证明报文的完整性,报文有可能遭篡改。 - -HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPS 使用了隧道进行通信。 - -通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 - -

- -## 加密 - -### 1. 对称密钥加密 - -对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。 - -- 优点:运算速度快; -- 缺点:无法安全地将密钥传输给通信方。 - -

- -### 2.非对称密钥加密 - -非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。 - -公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 - -非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。 - -- 优点:可以更安全地将公开密钥传输给通信发送方; -- 缺点:运算速度慢。 - -

- -### 3. HTTPS 采用的加密方式 - -HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥) - -

- -## 认证 - -通过使用 **证书** 来对通信方进行认证。 - -数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 - -服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。 - -进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。 - -

- -## 完整性保护 - -SSL 提供报文摘要功能来进行完整性保护。 - -HTTP 也提供了 MD5 报文摘要功能,但不是安全的。例如报文内容被篡改之后,同时重新计算 MD5 的值,通信接收方是无法意识到发生了篡改。 - -HTTPS 的报文摘要功能之所以安全,是因为它结合了加密和认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。 - -## HTTPS 的缺点 - -- 因为需要进行加密解密等过程,因此速度会更慢; -- 需要支付证书授权的高额费用。 - -# 七、HTTP/2.0 - -## HTTP/1.x 缺陷 - -HTTP/1.x 实现简单是以牺牲性能为代价的: - -- 客户端需要使用多个连接才能实现并发和缩短延迟; -- 不会压缩请求和响应首部,从而导致不必要的网络流量; -- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。 - -## 二进制分帧层 - -HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。 - -

- -在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。 - -- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。 -- 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。 -- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 - -

- -## 服务端推送 - -HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。 - -

- -## 首部压缩 - -HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。 - -HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。 - -不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。 - -

- -# 八、HTTP/1.1 新特性 - -详细内容请见上文 - -- 默认是长连接 -- 支持流水线 -- 支持同时打开多个 TCP 连接 -- 支持虚拟主机 -- 新增状态码 100 -- 支持分块传输编码 -- 新增缓存处理指令 max-age - -# 九、GET 和 POST 比较 - -## 作用 - -GET 用于获取资源,而 POST 用于传输实体主体。 - -## 参数 - -GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。 - -因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。例如 `中文` 会转换为 `%E4%B8%AD%E6%96%87`,而空格会转换为 `%20`。POST 参考支持标准字符集。 - -``` -GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 -``` - -``` -POST /test/demo_form.asp HTTP/1.1 -Host: w3schools.com -name1=value1&name2=value2 -``` - -## 安全 - -安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。 - -GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。 - -安全的方法除了 GET 之外还有:HEAD、OPTIONS。 - -不安全的方法除了 POST 之外还有 PUT、DELETE。 - -## 幂等性 - -幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。 - -所有的安全方法也都是幂等的。 - -在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。 - -GET /pageX HTTP/1.1 是幂等的,连续调用多次,客户端接收到的结果都是一样的: - -``` -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 -GET /pageX HTTP/1.1 -``` - -POST /add_row HTTP/1.1 不是幂等的,如果调用多次,就会增加多行记录: - -``` -POST /add_row HTTP/1.1 -> Adds a 1nd row -POST /add_row HTTP/1.1 -> Adds a 2nd row -POST /add_row HTTP/1.1 -> Adds a 3rd row -``` - -DELETE /idX/delete HTTP/1.1 是幂等的,即便不同的请求接收到的状态码不一样: - -``` -DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists -DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted -DELETE /idX/delete HTTP/1.1 -> Returns 404 -``` - -## 可缓存 - -如果要对响应进行缓存,需要满足以下条件: - -- 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 -- 响应报文的状态码是可缓存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。 -- 响应报文的 Cache-Control 首部字段没有指定不进行缓存。 - -## XMLHttpRequest - -为了阐述 POST 和 GET 的另一个区别,需要先了解 XMLHttpRequest: - -> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。 - -- 在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。 -- 而 GET 方法 Header 和 Data 会一起发送。 - -# 参考资料 - -- 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014. -- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) -- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) -- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php) -- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java) -- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement) -- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html) -- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) -- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) -- [Web-VPN: Secure Proxies with SPDY & Chrome](https://www.igvita.com/2011/12/01/web-vpn-secure-proxies-with-spdy-chrome/) -- [File:HTTP persistent connection.svg](http://en.wikipedia.org/wiki/File:HTTP_persistent_connection.svg) -- [Proxy server](https://en.wikipedia.org/wiki/Proxy_server) -- [What Is This HTTPS/SSL Thing And Why Should You Care?](https://www.x-cart.com/blog/what-is-https-and-ssl.html) -- [What is SSL Offloading?](https://securebox.comodo.com/ssl-sniffing/ssl-offloading/) -- [Sun Directory Server Enterprise Edition 7.0 Reference - Key Encryption](https://docs.oracle.com/cd/E19424-01/820-4811/6ng8i26bn/index.html) -- [An Introduction to Mutual SSL Authentication](https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication) -- [The Difference Between URLs and URIs](https://danielmiessler.com/study/url-uri/) -- [Cookie 与 Session 的区别](https://juejin.im/entry/5766c29d6be3ff006a31b84e#comment) -- [COOKIE 和 SESSION 有什么区别](https://www.zhihu.com/question/19786827) -- [Cookie/Session 的机制与安全](https://harttle.land/2015/08/10/cookie-session.html) -- [HTTPS 证书原理](https://shijianan.com/2017/06/11/https/) -- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) -- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) -- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/) -- [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences) -- [Web 性能优化与 HTTP/2](https://www.kancloud.cn/digest/web-performance-http2) -- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Java \345\237\272\347\241\200.md" "b/docs/notes/Java \345\237\272\347\241\200.md" deleted file mode 100644 index 0072eec972..0000000000 --- "a/docs/notes/Java \345\237\272\347\241\200.md" +++ /dev/null @@ -1,1402 +0,0 @@ - -* [一、数据类型](#一数据类型) - * [基本类型](#基本类型) - * [包装类型](#包装类型) - * [缓存池](#缓存池) -* [二、String](#二string) - * [概览](#概览) - * [不可变的好处](#不可变的好处) - * [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder) - * [String Pool](#string-pool) - * [new String("abc")](#new-string"abc") -* [三、运算](#三运算) - * [参数传递](#参数传递) - * [float 与 double](#float-与-double) - * [隐式类型转换](#隐式类型转换) - * [switch](#switch) -* [四、继承](#四继承) - * [访问权限](#访问权限) - * [抽象类与接口](#抽象类与接口) - * [super](#super) - * [重写与重载](#重写与重载) -* [五、Object 通用方法](#五object-通用方法) - * [概览](#概览) - * [equals()](#equals) - * [hashCode()](#hashcode) - * [toString()](#tostring) - * [clone()](#clone) -* [六、关键字](#六关键字) - * [final](#final) - * [static](#static) -* [七、反射](#七反射) -* [八、异常](#八异常) -* [九、泛型](#九泛型) -* [十、注解](#十注解) -* [十一、特性](#十一特性) - * [Java 各版本的新特性](#java-各版本的新特性) - * [Java 与 C++ 的区别](#java-与-c-的区别) - * [JRE or JDK](#jre-or-jdk) -* [参考资料](#参考资料) - - - -# 一、数据类型 - -## 基本类型 - -- byte/8 -- char/16 -- short/16 -- int/32 -- float/32 -- long/64 -- double/64 -- boolean/\~ - -boolean 只有两个值:true、false,可以使用 1 bit 来存储,但是具体大小没有明确规定。JVM 会在编译时期将 boolean 类型的数据转换为 int,使用 1 来表示 true,0 表示 false。JVM 并不直接支持 boolean 数组,而是使用 byte 数组来表示 int 数组。 - -- [Primitive Data Types](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) -- [The Java® Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf) - -## 包装类型 - -基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。 - -```java -Integer x = 2; // 装箱 -int y = x; // 拆箱 -``` - -## 缓存池 - -new Integer(123) 与 Integer.valueOf(123) 的区别在于: - -- new Integer(123) 每次都会新建一个对象; -- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。 - -```java -Integer x = new Integer(123); -Integer y = new Integer(123); -System.out.println(x == y); // false -Integer z = Integer.valueOf(123); -Integer k = Integer.valueOf(123); -System.out.println(z == k); // true -``` - -valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。 - -```java -public static Integer valueOf(int i) { - if (i >= IntegerCache.low && i <= IntegerCache.high) - return IntegerCache.cache[i + (-IntegerCache.low)]; - return new Integer(i); -} -``` - -在 Java 8 中,Integer 缓存池的大小默认为 -128\~127。 - -```java -static final int low = -128; -static final int high; -static final Integer cache[]; - -static { - // high value may be configured by property - int h = 127; - String integerCacheHighPropValue = - sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); - if (integerCacheHighPropValue != null) { - try { - int i = parseInt(integerCacheHighPropValue); - i = Math.max(i, 127); - // Maximum array size is Integer.MAX_VALUE - h = Math.min(i, Integer.MAX_VALUE - (-low) -1); - } catch( NumberFormatException nfe) { - // If the property cannot be parsed into an int, ignore it. - } - } - high = h; - - cache = new Integer[(high - low) + 1]; - int j = low; - for(int k = 0; k < cache.length; k++) - cache[k] = new Integer(j++); - - // range [-128, 127] must be interned (JLS7 5.1.7) - assert IntegerCache.high >= 127; -} -``` - -编译器会在自动装箱过程调用 valueOf() 方法,因此多个 Integer 实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。 - -```java -Integer m = 123; -Integer n = 123; -System.out.println(m == n); // true -``` - -基本类型对应的缓冲池如下: - -- boolean values true and false -- all byte values -- short values between -128 and 127 -- int values between -128 and 127 -- char in the range \u0000 to \u007F - -在使用这些基本类型对应的包装类型时,就可以直接使用缓冲池中的对象。 - -[StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123 -](https://stackoverflow.com/questions/9030817/differences-between-new-integer123-integer-valueof123-and-just-123) - -# 二、String - -## 概览 - -String 被声明为 final,因此它不可被继承。 - -在 Java 8 中,String 内部使用 char 数组存储数据。 - -```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - /** The value is used for character storage. */ - private final char value[]; -} -``` - -在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 `coder` 来标识使用了哪种编码。 - -```java -public final class String - implements java.io.Serializable, Comparable, CharSequence { - /** The value is used for character storage. */ - private final byte[] value; - - /** The identifier of the encoding used to encode the bytes in {@code value}. */ - private final byte coder; -} -``` - -value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。 - -## 不可变的好处 - -**1. 可以缓存 hash 值** - -因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。 - -**2. String Pool 的需要** - -如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 - -

- -**3. 安全性** - -String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。 - -**4. 线程安全** - -String 不可变性天生具备线程安全,可以在多个线程中安全地使用。 - -[Program Creek : Why String is immutable in Java?](https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/) - -## String, StringBuffer and StringBuilder - -**1. 可变性** - -- String 不可变 -- StringBuffer 和 StringBuilder 可变 - -**2. 线程安全** - -- String 不可变,因此是线程安全的 -- StringBuilder 不是线程安全的 -- StringBuffer 是线程安全的,内部使用 synchronized 进行同步 - -[StackOverflow : String, StringBuffer, and StringBuilder](https://stackoverflow.com/questions/2971315/string-stringbuffer-and-stringbuilder) - -## String Pool - -字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程中将字符串添加到 String Pool 中。 - -当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。 - -下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 方法取得一个字符串引用。intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。因此 s3 和 s4 引用的是同一个字符串。 - -```java -String s1 = new String("aaa"); -String s2 = new String("aaa"); -System.out.println(s1 == s2); // false -String s3 = s1.intern(); -String s4 = s1.intern(); -System.out.println(s3 == s4); // true -``` - -如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。 - -```java -String s5 = "bbb"; -String s6 = "bbb"; -System.out.println(s5 == s6); // true -``` - -在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。 - -- [StackOverflow : What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) -- [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html) - -## new String("abc") - -使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。 - -- "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量; -- 而使用 new 的方式会在堆中创建一个字符串对象。 - -创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。 - -```java -public class NewStringTest { - public static void main(String[] args) { - String s = new String("abc"); - } -} -``` - -使用 javap -verbose 进行反编译,得到以下内容: - -```java -// ... -Constant pool: -// ... - #2 = Class #18 // java/lang/String - #3 = String #19 // abc -// ... - #18 = Utf8 java/lang/String - #19 = Utf8 abc -// ... - - public static void main(java.lang.String[]); - descriptor: ([Ljava/lang/String;)V - flags: ACC_PUBLIC, ACC_STATIC - Code: - stack=3, locals=2, args_size=1 - 0: new #2 // class java/lang/String - 3: dup - 4: ldc #3 // String abc - 6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V - 9: astore_1 -// ... -``` - -在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。 - -以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。 - -```java -public String(String original) { - this.value = original.value; - this.hash = original.hash; -} -``` - -# 三、运算 - -## 参数传递 - -Java 的参数是以值传递的形式传入方法中,而不是引用传递。 - -以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。因此在方法中使指针引用其它对象,那么这两个指针此时指向的是完全不同的对象,在一方改变其所指向对象的内容时对另一方没有影响。 - -```java -public class Dog { - - String name; - - Dog(String name) { - this.name = name; - } - - String getName() { - return this.name; - } - - void setName(String name) { - this.name = name; - } - - String getObjectAddress() { - return super.toString(); - } -} -``` - -```java -public class PassByValueExample { - public static void main(String[] args) { - Dog dog = new Dog("A"); - System.out.println(dog.getObjectAddress()); // Dog@4554617c - func(dog); - System.out.println(dog.getObjectAddress()); // Dog@4554617c - System.out.println(dog.getName()); // A - } - - private static void func(Dog dog) { - System.out.println(dog.getObjectAddress()); // Dog@4554617c - dog = new Dog("B"); - System.out.println(dog.getObjectAddress()); // Dog@74a14482 - System.out.println(dog.getName()); // B - } -} -``` - -如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。 - -```java -class PassByValueExample { - public static void main(String[] args) { - Dog dog = new Dog("A"); - func(dog); - System.out.println(dog.getName()); // B - } - - private static void func(Dog dog) { - dog.setName("B"); - } -} -``` - -[StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) - -## float 与 double - -Java 不能隐式执行向下转型,因为这会使得精度降低。 - -1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。 - -```java -// float f = 1.1; -``` - -1.1f 字面量才是 float 类型。 - -```java -float f = 1.1f; -``` - -## 隐式类型转换 - -因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型下转型为 short 类型。 - -```java -short s1 = 1; -// s1 = s1 + 1; -``` - -但是使用 += 或者 ++ 运算符可以执行隐式类型转换。 - -```java -s1 += 1; -// s1++; -``` - -上面的语句相当于将 s1 + 1 的计算结果进行了向下转型: - -```java -s1 = (short) (s1 + 1); -``` - -[StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?](https://stackoverflow.com/questions/8710619/why-dont-javas-compound-assignment-operators-require-casting) - -## switch - -从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。 - -```java -String s = "a"; -switch (s) { - case "a": - System.out.println("aaa"); - break; - case "b": - System.out.println("bbb"); - break; -} -``` - -switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。 - -```java -// long x = 111; -// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum' -// case 111: -// System.out.println(111); -// break; -// case 222: -// System.out.println(222); -// break; -// } -``` - -[StackOverflow : Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java) - -# 四、继承 - -## 访问权限 - -Java 中有三个访问权限修饰符:private、protected 以及 public,如果不加访问修饰符,表示包级可见。 - -可以对类或类中的成员(字段以及方法)加上访问修饰符。 - -- 类可见表示其它类可以用这个类创建实例对象。 -- 成员可见表示其它类可以用这个类的实例对象访问到该成员; - -protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。 - -设计良好的模块会隐藏所有的实现细节,把它的 API 与它的实现清晰地隔离开来。模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块的内部工作情况,这个概念被称为信息隐藏或封装。因此访问权限应当尽可能地使每个类或者成员不被外界访问。 - -如果子类的方法重写了父类的方法,那么子类中该方法的访问级别不允许低于父类的访问级别。这是为了确保可以使用父类实例的地方都可以使用子类实例,也就是确保满足里氏替换原则。 - -字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,客户端可以对其随意修改。例如下面的例子中,AccessExample 拥有 id 公有字段,如果在某个时刻,我们想要使用 int 存储 id 字段,那么就需要修改所有的客户端代码。 - -```java -public class AccessExample { - public String id; -} -``` - -可以使用公有的 getter 和 setter 方法来替换公有字段,这样的话就可以控制对字段的修改行为。 - - -```java -public class AccessExample { - - private int id; - - public String getId() { - return id + ""; - } - - public void setId(String id) { - this.id = Integer.valueOf(id); - } -} -``` - -但是也有例外,如果是包级私有的类或者私有的嵌套类,那么直接暴露成员不会有特别大的影响。 - -```java -public class AccessWithInnerClassExample { - - private class InnerClass { - int x; - } - - private InnerClass innerClass; - - public AccessWithInnerClassExample() { - innerClass = new InnerClass(); - } - - public int getValue() { - return innerClass.x; // 直接访问 - } -} -``` - -## 抽象类与接口 - -**1. 抽象类** - -抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。 - -抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。 - -```java -public abstract class AbstractClassExample { - - protected int x; - private int y; - - public abstract void func1(); - - public void func2() { - System.out.println("func2"); - } -} -``` - -```java -public class AbstractExtendClassExample extends AbstractClassExample { - @Override - public void func1() { - System.out.println("func1"); - } -} -``` - -```java -// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated -AbstractClassExample ac2 = new AbstractExtendClassExample(); -ac2.func1(); -``` - -**2. 接口** - -接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。 - -从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。 - -接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。 - -接口的字段默认都是 static 和 final 的。 - -```java -public interface InterfaceExample { - - void func1(); - - default void func2(){ - System.out.println("func2"); - } - - int x = 123; - // int y; // Variable 'y' might not have been initialized - public int z = 0; // Modifier 'public' is redundant for interface fields - // private int k = 0; // Modifier 'private' not allowed here - // protected int l = 0; // Modifier 'protected' not allowed here - // private void fun3(); // Modifier 'private' not allowed here -} -``` - -```java -public class InterfaceImplementExample implements InterfaceExample { - @Override - public void func1() { - System.out.println("func1"); - } -} -``` - -```java -// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated -InterfaceExample ie2 = new InterfaceImplementExample(); -ie2.func1(); -System.out.println(InterfaceExample.x); -``` - -**3. 比较** - -- 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 -- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 -- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。 -- 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。 - -**4. 使用选择** - -使用接口: - -- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法; -- 需要使用多重继承。 - -使用抽象类: - -- 需要在几个相关的类中共享代码。 -- 需要能控制继承来的成员的访问权限,而不是都为 public。 -- 需要继承非静态和非常量字段。 - -在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。 - -- [深入理解 abstract class 和 interface](https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/) -- [When to Use Abstract Class and Interface](https://dzone.com/articles/when-to-use-abstract-class-and-intreface) - -## super - -- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。 -- 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。 - -```java -public class SuperExample { - - protected int x; - protected int y; - - public SuperExample(int x, int y) { - this.x = x; - this.y = y; - } - - public void func() { - System.out.println("SuperExample.func()"); - } -} -``` - -```java -public class SuperExtendExample extends SuperExample { - - private int z; - - public SuperExtendExample(int x, int y, int z) { - super(x, y); - this.z = z; - } - - @Override - public void func() { - super.func(); - System.out.println("SuperExtendExample.func()"); - } -} -``` - -```java -SuperExample e = new SuperExtendExample(1, 2, 3); -e.func(); -``` - -```html -SuperExample.func() -SuperExtendExample.func() -``` - -[Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html) - -## 重写与重载 - -**1. 重写(Override)** - -存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。 - -为了满足里式替换原则,重写有有以下两个限制: - -- 子类方法的访问权限必须大于等于父类方法; -- 子类方法的返回类型必须是父类方法返回类型或为其子类型。 - -使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。 - -**2. 重载(Overload)** - -存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。 - -应该注意的是,返回值不同,其它都相同不算是重载。 - -**3. 实例** - -```java -class A { - public String show(D obj) { - return ("A and D"); - } - - public String show(A obj) { - return ("A and A"); - } -} - -class B extends A { - public String show(B obj) { - return ("B and B"); - } - - public String show(A obj) { - return ("B and A"); - } -} - -class C extends B { -} - -class D extends B { -} -``` - -```java -public class Test { - - public static void main(String[] args) { - A a1 = new A(); - A a2 = new B(); - B b = new B(); - C c = new C(); - D d = new D(); - System.out.println(a1.show(b)); // A and A - System.out.println(a1.show(c)); // A and A - System.out.println(a1.show(d)); // A and D - System.out.println(a2.show(b)); // B and A - System.out.println(a2.show(c)); // B and A - System.out.println(a2.show(d)); // A and D - System.out.println(b.show(b)); // B and B - System.out.println(b.show(c)); // B and B - System.out.println(b.show(d)); // A and D - } -} -``` - -涉及到重写时,方法调用的优先级为: - -- this.show(O) -- super.show(O) -- this.show((super)O) -- super.show((super)O) - -# 五、Object 通用方法 - -## 概览 - -```java - -public native int hashCode() - -public boolean equals(Object obj) - -protected native Object clone() throws CloneNotSupportedException - -public String toString() - -public final native Class getClass() - -protected void finalize() throws Throwable {} - -public final native void notify() - -public final native void notifyAll() - -public final native void wait(long timeout) throws InterruptedException - -public final void wait(long timeout, int nanos) throws InterruptedException - -public final void wait() throws InterruptedException -``` - -## equals() - -**1. 等价关系** - -Ⅰ 自反性 - -```java -x.equals(x); // true -``` - -Ⅱ 对称性 - -```java -x.equals(y) == y.equals(x); // true -``` - -Ⅲ 传递性 - -```java -if (x.equals(y) && y.equals(z)) - x.equals(z); // true; -``` - -Ⅳ 一致性 - -多次调用 equals() 方法结果不变 - -```java -x.equals(y) == x.equals(y); // true -``` - -Ⅴ 与 null 的比较 - -对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false - -```java -x.equals(null); // false; -``` - -**2. 等价与相等** - -- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 -- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 - -```java -Integer x = new Integer(1); -Integer y = new Integer(1); -System.out.println(x.equals(y)); // true -System.out.println(x == y); // false -``` - -**3. 实现** - -- 检查是否为同一个对象的引用,如果是直接返回 true; -- 检查是否是同一个类型,如果不是,直接返回 false; -- 将 Object 对象进行转型; -- 判断每个关键域是否相等。 - -```java -public class EqualExample { - - private int x; - private int y; - private int z; - - public EqualExample(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - EqualExample that = (EqualExample) o; - - if (x != that.x) return false; - if (y != that.y) return false; - return z == that.z; - } -} -``` - -## hashCode() - -hashCode() 返回散列值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价。 - -在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象散列值也相等。 - -下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象,但是因为 EqualExample 没有实现 hasCode() 方法,因此这两个对象的散列值是不同的,最终导致集合添加了两个等价的对象。 - -```java -EqualExample e1 = new EqualExample(1, 1, 1); -EqualExample e2 = new EqualExample(1, 1, 1); -System.out.println(e1.equals(e2)); // true -HashSet set = new HashSet<>(); -set.add(e1); -set.add(e2); -System.out.println(set.size()); // 2 -``` - -理想的散列函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的散列值上。这就要求了散列函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位。 - -一个数与 31 相乘可以转换成移位和减法:`31*x == (x<<5)-x`,编译器会自动进行这个优化。 - -```java -@Override -public int hashCode() { - int result = 17; - result = 31 * result + x; - result = 31 * result + y; - result = 31 * result + z; - return result; -} -``` - -## toString() - -默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 - -```java -public class ToStringExample { - - private int number; - - public ToStringExample(int number) { - this.number = number; - } -} -``` - -```java -ToStringExample example = new ToStringExample(123); -System.out.println(example.toString()); -``` - -```html -ToStringExample@4554617c -``` - -## clone() - -**1. cloneable** - -clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 - -```java -public class CloneExample { - private int a; - private int b; -} -``` - -```java -CloneExample e1 = new CloneExample(); -// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' -``` - -重写 clone() 得到以下实现: - -```java -public class CloneExample { - private int a; - private int b; - - @Override - public CloneExample clone() throws CloneNotSupportedException { - return (CloneExample)super.clone(); - } -} -``` - -```java -CloneExample e1 = new CloneExample(); -try { - CloneExample e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); -} -``` - -```html -java.lang.CloneNotSupportedException: CloneExample -``` - -以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 - -应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 - -```java -public class CloneExample implements Cloneable { - private int a; - private int b; - - @Override - public Object clone() throws CloneNotSupportedException { - return super.clone(); - } -} -``` - -**2. 浅拷贝** - -拷贝对象和原始对象的引用类型引用同一个对象。 - -```java -public class ShallowCloneExample implements Cloneable { - - private int[] arr; - - public ShallowCloneExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } - - public void set(int index, int value) { - arr[index] = value; - } - - public int get(int index) { - return arr[index]; - } - - @Override - protected ShallowCloneExample clone() throws CloneNotSupportedException { - return (ShallowCloneExample) super.clone(); - } -} -``` - -```java -ShallowCloneExample e1 = new ShallowCloneExample(); -ShallowCloneExample e2 = null; -try { - e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); -} -e1.set(2, 222); -System.out.println(e2.get(2)); // 222 -``` - -**3. 深拷贝** - -拷贝对象和原始对象的引用类型引用不同对象。 - -```java -public class DeepCloneExample implements Cloneable { - - private int[] arr; - - public DeepCloneExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } - - public void set(int index, int value) { - arr[index] = value; - } - - public int get(int index) { - return arr[index]; - } - - @Override - protected DeepCloneExample clone() throws CloneNotSupportedException { - DeepCloneExample result = (DeepCloneExample) super.clone(); - result.arr = new int[arr.length]; - for (int i = 0; i < arr.length; i++) { - result.arr[i] = arr[i]; - } - return result; - } -} -``` - -```java -DeepCloneExample e1 = new DeepCloneExample(); -DeepCloneExample e2 = null; -try { - e2 = e1.clone(); -} catch (CloneNotSupportedException e) { - e.printStackTrace(); -} -e1.set(2, 222); -System.out.println(e2.get(2)); // 2 -``` - -**4. clone() 的替代方案** - -使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 - -```java -public class CloneConstructorExample { - - private int[] arr; - - public CloneConstructorExample() { - arr = new int[10]; - for (int i = 0; i < arr.length; i++) { - arr[i] = i; - } - } - - public CloneConstructorExample(CloneConstructorExample original) { - arr = new int[original.arr.length]; - for (int i = 0; i < original.arr.length; i++) { - arr[i] = original.arr[i]; - } - } - - public void set(int index, int value) { - arr[index] = value; - } - - public int get(int index) { - return arr[index]; - } -} -``` - -```java -CloneConstructorExample e1 = new CloneConstructorExample(); -CloneConstructorExample e2 = new CloneConstructorExample(e1); -e1.set(2, 222); -System.out.println(e2.get(2)); // 2 -``` - -# 六、关键字 - -## final - -**1. 数据** - -声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 - -- 对于基本类型,final 使数值不变; -- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 - -```java -final int x = 1; -// x = 2; // cannot assign value to final variable 'x' -final A y = new A(); -y.a = 1; -``` - -**2. 方法** - -声明方法不能被子类重写。 - -private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。 - -**3. 类** - -声明类不允许被继承。 - -## static - -**1. 静态变量** - -- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。 -- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 - -```java -public class A { - - private int x; // 实例变量 - private static int y; // 静态变量 - - public static void main(String[] args) { - // int x = A.x; // Non-static field 'x' cannot be referenced from a static context - A a = new A(); - int x = a.x; - int y = A.y; - } -} -``` - -**2. 静态方法** - -静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。 - -```java -public abstract class A { - public static void func1(){ - } - // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static' -} -``` - -只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。 - -```java -public class A { - - private static int x; - private int y; - - public static void func1(){ - int a = x; - // int b = y; // Non-static field 'y' cannot be referenced from a static context - // int b = this.y; // 'A.this' cannot be referenced from a static context - } -} -``` - -**3. 静态语句块** - -静态语句块在类初始化时运行一次。 - -```java -public class A { - static { - System.out.println("123"); - } - - public static void main(String[] args) { - A a1 = new A(); - A a2 = new A(); - } -} -``` - -```html -123 -``` - -**4. 静态内部类** - -非静态内部类依赖于外部类的实例,而静态内部类不需要。 - -```java -public class OuterClass { - - class InnerClass { - } - - static class StaticInnerClass { - } - - public static void main(String[] args) { - // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context - OuterClass outerClass = new OuterClass(); - InnerClass innerClass = outerClass.new InnerClass(); - StaticInnerClass staticInnerClass = new StaticInnerClass(); - } -} -``` - -静态内部类不能访问外部类的非静态的变量和方法。 - -**5. 静态导包** - -在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 - -```java -import static com.xxx.ClassName.* -``` - -**6. 初始化顺序** - -静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。 - -```java -public static String staticField = "静态变量"; -``` - -```java -static { - System.out.println("静态语句块"); -} -``` - -```java -public String field = "实例变量"; -``` - -```java -{ - System.out.println("普通语句块"); -} -``` - -最后才是构造函数的初始化。 - -```java -public InitialOrderTest() { - System.out.println("构造函数"); -} -``` - -存在继承的情况下,初始化顺序为: - -- 父类(静态变量、静态语句块) -- 子类(静态变量、静态语句块) -- 父类(实例变量、普通语句块) -- 父类(构造函数) -- 子类(实例变量、普通语句块) -- 子类(构造函数) - - -# 七、反射 - -每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 - -类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。 - -反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 - -Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: - -- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; -- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; -- **Constructor** :可以用 Constructor 创建新的对象。 - -**反射的优点:** - -* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 -* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 -* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 - -**反射的缺点:** - -尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。 - -* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 - -* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 - -* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 - - -- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html) -- [深入解析 Java 反射(1)- 基础](http://www.sczyh30.com/posts/Java/java-reflection-1/) - -# 八、异常 - -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: - -- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; -- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 - -

- -- [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) -- [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html) - -# 九、泛型 - -```java -public class Box { - // T stands for "Type" - private T t; - public void set(T t) { this.t = t; } - public T get() { return t; } -} -``` - -- [Java 泛型详解](http://www.importnew.com/24029.html) -- [10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693) - -# 十、注解 - -Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 - -[注解 Annotation 实现原理与自定义注解例子](https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html) - -# 十一、特性 - -## Java 各版本的新特性 - -**New highlights in Java SE 8** - -1. Lambda Expressions -2. Pipelines and Streams -3. Date and Time API -4. Default Methods -5. Type Annotations -6. Nashhorn JavaScript Engine -7. Concurrent Accumulators -8. Parallel operations -9. PermGen Error Removed - -**New highlights in Java SE 7** - -1. Strings in Switch Statement -2. Type Inference for Generic Instance Creation -3. Multiple Exception Handling -4. Support for Dynamic Languages -5. Try with Resources -6. Java nio Package -7. Binary Literals, Underscore in literals -8. Diamond Syntax - -- [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) -- [Java 8 特性](http://www.importnew.com/19345.html) - -## Java 与 C++ 的区别 - -- Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。 -- Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。 -- Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。 -- Java 支持自动垃圾回收,而 C++ 需要手动回收。 -- 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) - -## JRE or JDK - -- JRE is the JVM program, Java application need to run on JRE. -- JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac" - -# 参考资料 - -- Eckel B. Java 编程思想[M]. 机械工业出版社, 2002. -- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Java \350\231\232\346\213\237\346\234\272.md" "b/docs/notes/Java \350\231\232\346\213\237\346\234\272.md" deleted file mode 100644 index efa5ca5a67..0000000000 --- "a/docs/notes/Java \350\231\232\346\213\237\346\234\272.md" +++ /dev/null @@ -1,747 +0,0 @@ - -* [一、运行时数据区域](#一运行时数据区域) - * [程序计数器](#程序计数器) - * [Java 虚拟机栈](#java-虚拟机栈) - * [本地方法栈](#本地方法栈) - * [堆](#堆) - * [方法区](#方法区) - * [运行时常量池](#运行时常量池) - * [直接内存](#直接内存) -* [二、垃圾收集](#二垃圾收集) - * [判断一个对象是否可被回收](#判断一个对象是否可被回收) - * [引用类型](#引用类型) - * [垃圾收集算法](#垃圾收集算法) - * [垃圾收集器](#垃圾收集器) -* [三、内存分配与回收策略](#三内存分配与回收策略) - * [Minor GC 和 Full GC](#minor-gc-和-full-gc) - * [内存分配策略](#内存分配策略) - * [Full GC 的触发条件](#full-gc-的触发条件) -* [四、类加载机制](#四类加载机制) - * [类的生命周期](#类的生命周期) - * [类加载过程](#类加载过程) - * [类初始化时机](#类初始化时机) - * [类与类加载器](#类与类加载器) - * [类加载器分类](#类加载器分类) - * [双亲委派模型](#双亲委派模型) - * [自定义类加载器实现](#自定义类加载器实现) -* [参考资料](#参考资料) - - - -# 一、运行时数据区域 - -

- -## 程序计数器 - -记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。 - -## Java 虚拟机栈 - -每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 - -

- -可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小: - -```java -java -Xss512M HackTheJava -``` - -该区域可能抛出以下异常: - -- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常; -- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。 - -## 本地方法栈 - -本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。 - -本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。 - -

- -## 堆 - -所有对象都在这里分配内存,是垃圾收集的主要区域("GC 堆")。 - -现代的垃圾收集器基本都是采用分代收集算法,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块: - -- 新生代(Young Generation) -- 老年代(Old Generation) - -堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。 - -可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。 - -```java -java -Xms1M -Xmx2M HackTheJava -``` - -## 方法区 - -用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 - -和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。 - -对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。 - -HotSpot 虚拟机把它当成永久代来进行垃圾回收。但很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。 - -## 运行时常量池 - -运行时常量池是方法区的一部分。 - -Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。 - -除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。 - -## 直接内存 - -在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。 - -# 二、垃圾收集 - -垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。 - -## 判断一个对象是否可被回收 - -### 1. 引用计数算法 - -为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。 - -在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。 - -```java -public class Test { - - public Object instance = null; - - public static void main(String[] args) { - Test a = new Test(); - Test b = new Test(); - a.instance = b; - b.instance = a; - } -} -``` - -### 2. 可达性分析算法 - -以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。 - -Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容: - -- 虚拟机栈中局部变量表中引用的对象 -- 本地方法栈中 JNI 中引用的对象 -- 方法区中类静态属性引用的对象 -- 方法区中的常量引用的对象 - -

- -### 3. 方法区的回收 - -因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。 - -主要是对常量池的回收和对类的卸载。 - -为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。 - -类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载: - -- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。 -- 加载该类的 ClassLoader 已经被回收。 -- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。 - -### 4. finalize() - -类似 C++ 的析构函数,用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。 - -当一个对象可被回收时,如果需要执行该对象的 finalize() 方法,那么就有可能在该方法中让对象重新被引用,从而实现自救。自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。 - -## 引用类型 - -无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。 - -Java 提供了四种强度不同的引用类型。 - -### 1. 强引用 - -被强引用关联的对象不会被回收。 - -使用 new 一个新对象的方式来创建强引用。 - -```java -Object obj = new Object(); -``` - -### 2. 软引用 - -被软引用关联的对象只有在内存不够的情况下才会被回收。 - -使用 SoftReference 类来创建软引用。 - -```java -Object obj = new Object(); -SoftReference sf = new SoftReference(obj); -obj = null; // 使对象只被软引用关联 -``` - -### 3. 弱引用 - -被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。 - -使用 WeakReference 类来创建弱引用。 - -```java -Object obj = new Object(); -WeakReference wf = new WeakReference(obj); -obj = null; -``` - -### 4. 虚引用 - -又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。 - -为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知。 - -使用 PhantomReference 来创建虚引用。 - -```java -Object obj = new Object(); -PhantomReference pf = new PhantomReference(obj); -obj = null; -``` - -## 垃圾收集算法 - -### 1. 标记 - 清除 - -

- - -标记要回收的对象,然后清除。 - -不足: - -- 标记和清除过程效率都不高; -- 会产生大量不连续的内存碎片,导致无法给大对象分配内存。 - -### 2. 标记 - 整理 - - -

- - -让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 - -### 3. 复制 - -

- -将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 - -主要不足是只使用了内存的一半。 - -现在的商业虚拟机都采用这种收集算法回收新生代,但是并不是划分为大小相等的两块,而是一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。 - -HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 就不够用了,此时需要依赖于老年代进行空间分配担保,也就是借用老年代的空间存储放不下的对象。 - -### 4. 分代收集 - -现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。 - -一般将堆分为新生代和老年代。 - -- 新生代使用:复制算法 -- 老年代使用:标记 - 清除 或者 标记 - 整理 算法 - -## 垃圾收集器 - -

- -以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 - -- 单线程与多线程:单线程指的是垃圾收集器只使用一个线程,而多线程使用多个线程; -- 串行与并行:串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候需要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 之外,其它垃圾收集器都是以串行的方式执行。 - -### 1. Serial 收集器 - -

- -Serial 翻译为串行,也就是说它以串行的方式执行。 - -它是单线程的收集器,只会使用一个线程进行垃圾收集工作。 - -它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率。 - -它是 Client 场景下的默认新生代收集器,因为在该场景下内存一般来说不会很大。它收集一两百兆垃圾的停顿时间可以控制在一百多毫秒以内,只要不是太频繁,这点停顿时间是可以接受的。 - -### 2. ParNew 收集器 - -

- -它是 Serial 收集器的多线程版本。 - -它是 Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用。 - -### 3. Parallel Scavenge 收集器 - -与 ParNew 一样是多线程收集器。 - -其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,因此它被称为“吞吐量优先”收集器。这里的吞吐量指 CPU 用于运行用户程序的时间占总时间的比值。 - -停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。 - -缩短停顿时间是以牺牲吞吐量和新生代空间来换取的:新生代空间变小,垃圾回收变得频繁,导致吞吐量下降。 - -可以通过一个开关参数打开 GC 自适应的调节策略(GC Ergonomics),就不需要手工指定新生代的大小(-Xmn)、Eden 和 Survivor 区的比例、晋升老年代对象年龄等细节参数了。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。 - -### 4. Serial Old 收集器 - -

- -是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途: - -- 在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用。 -- 作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。 - -### 5. Parallel Old 收集器 - -

- -是 Parallel Scavenge 收集器的老年代版本。 - -在注重吞吐量以及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。 - -### 6. CMS 收集器 - -

- -CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 - -分为以下四个流程: - -- 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。 -- 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。 -- 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。 -- 并发清除:不需要停顿。 - -在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。 - -具有以下缺点: - -- 吞吐量低:低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高。 -- 无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。 -- 标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。 - -### 7. G1 收集器 - -G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。 - -堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。 - -

- -G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。 - -

- -通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。 - -每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。 - -

- -如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤: - -- 初始标记 -- 并发标记 -- 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。 -- 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。 - -具备如下特点: - -- 空间整合:整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。 -- 可预测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。 - -# 三、内存分配与回收策略 - -## Minor GC 和 Full GC - -- Minor GC:回收新生代上,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 - -- Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。 - -## 内存分配策略 - -### 1. 对象优先在 Eden 分配 - -大多数情况下,对象在新生代 Eden 区分配,当 Eden 区空间不够时,发起 Minor GC。 - -### 2. 大对象直接进入老年代 - -大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。 - -经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。 - --XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 区和 Survivor 区之间的大量内存复制。 - -### 3. 长期存活的对象进入老年代 - -为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。 - --XX:MaxTenuringThreshold 用来定义年龄的阈值。 - -### 4. 动态对象年龄判定 - -虚拟机并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 - -### 5. 空间分配担保 - -在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。 - -如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,那么就要进行一次 Full GC。 - -## Full GC 的触发条件 - -对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: - -### 1. 调用 System.gc() - -只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。 - -### 2. 老年代空间不足 - -老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。 - -为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。 - -### 3. 空间分配担保失败 - -使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。具体内容请参考上面的第五小节。 - -### 4. JDK 1.7 及以前的永久代空间不足 - -在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。 - -当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。 - -为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 - -### 5. Concurrent Mode Failure - -执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 - -# 四、类加载机制 - -类是在运行期间第一次使用时动态加载的,而不是一次性加载。因为如果一次性加载,那么会占用很多的内存。 - -## 类的生命周期 - -

- -包括以下 7 个阶段: - -- **加载(Loading)** -- **验证(Verification)** -- **准备(Preparation)** -- **解析(Resolution)** -- **初始化(Initialization)** -- 使用(Using) -- 卸载(Unloading) - -## 类加载过程 - -包含了加载、验证、准备、解析和初始化这 5 个阶段。 - -### 1. 加载 - -加载是类加载的一个阶段,注意不要混淆。 - -加载过程完成以下三件事: - -- 通过类的完全限定名称获取定义该类的二进制字节流。 -- 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。 -- 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。 - - -其中二进制字节流可以从以下方式中获取: - -- 从 ZIP 包读取,成为 JAR、EAR、WAR 格式的基础。 -- 从网络中获取,最典型的应用是 Applet。 -- 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理类的二进制字节流。 -- 由其他文件生成,例如由 JSP 文件生成对应的 Class 类。 - -### 2. 验证 - -确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 - -### 3. 准备 - -类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。 - -实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。 - -初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。 - -```java -public static int value = 123; -``` - -如果类变量是常量,那么它将初始化为表达式所定义的值而不是 0。例如下面的常量 value 被初始化为 123 而不是 0。 - -```java -public static final int value = 123; -``` - -### 4. 解析 - -将常量池的符号引用替换为直接引用的过程。 - -其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。 - -
- -### 5. 初始化 - -
- -初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。 - -在准备阶段,已经为类变量分配了系统所需的初始值,并且在初始化阶段,根据程序员通过程序进行的主观计划来初始化类变量和其他资源。 - -<clinit>() 是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。例如以下代码: - -```java -public class Test { - static { - i = 0; // 给变量赋值可以正常编译通过 - System.out.print(i); // 这句编译器会提示“非法向前引用” - } - static int i = 1; -} -``` - -由于父类的 <clinit>() 方法先执行,也就意味着父类中定义的静态语句块的执行要优先于子类。例如以下代码: - -```java -static class Parent { - public static int A = 1; - static { - A = 2; - } -} - -static class Sub extends Parent { - public static int B = A; -} - -public static void main(String[] args) { - System.out.println(Sub.B); // 2 -} -``` - -接口中不可以使用静态语句块,但仍然有类变量初始化的赋值操作,因此接口与类一样都会生成 <clinit>() 方法。但接口与类不同的是,执行接口的 <clinit>() 方法不需要先执行父接口的 <clinit>() 方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的 <clinit>() 方法。 - -虚拟机会保证一个类的 <clinit>() 方法在多线程环境下被正确的加锁和同步,如果多个线程同时初始化一个类,只会有一个线程执行这个类的 <clinit>() 方法,其它线程都会阻塞等待,直到活动线程执行 <clinit>() 方法完毕。如果在一个类的 <clinit>() 方法中有耗时的操作,就可能造成多个线程阻塞,在实际过程中此种阻塞很隐蔽。 - -## 类初始化时机 - -### 1. 主动引用 - -虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生): - -- 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。 - -- 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。 - -- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。 - -- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类; - -- 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化; - -### 2. 被动引用 - -以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括: - -- 通过子类引用父类的静态字段,不会导致子类初始化。 - -```java -System.out.println(SubClass.value); // value 字段在 SuperClass 中定义 -``` - -- 通过数组定义来引用类,不会触发此类的初始化。该过程会对数组类进行初始化,数组类是一个由虚拟机自动生成的、直接继承自 Object 的子类,其中包含了数组的属性和方法。 - -```java -SuperClass[] sca = new SuperClass[10]; -``` - -- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 - -```java -System.out.println(ConstClass.HELLOWORLD); -``` - -## 类与类加载器 - -两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。 - -这里的相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true。 - -## 类加载器分类 - -从 Java 虚拟机的角度来讲,只存在以下两种不同的类加载器: - -- 启动类加载器(Bootstrap ClassLoader),使用 C++ 实现,是虚拟机自身的一部分; - -- 所有其它类的加载器,使用 Java 实现,独立于虚拟机,继承自抽象类 java.lang.ClassLoader。 - -从 Java 开发人员的角度看,类加载器可以划分得更细致一些: - -- 启动类加载器(Bootstrap ClassLoader)此类加载器负责将存放在 <JRE_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。 - -- 扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。 - -- 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。 - -
- -## 双亲委派模型 - -应用程序是由三种类加载器互相配合从而实现类加载,除此之外还可以加入自己定义的类加载器。 - -下图展示了类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。类加载器之间的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。 - -

- -### 1. 工作过程 - -一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。 - -### 2. 好处 - -使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。 - -例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。 - -### 3. 实现 - -以下是抽象类 java.lang.ClassLoader 的代码片段,其中的 loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。 - -```java -public abstract class ClassLoader { - // The parent class loader for delegation - private final ClassLoader parent; - - public Class loadClass(String name) throws ClassNotFoundException { - return loadClass(name, false); - } - - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - synchronized (getClassLoadingLock(name)) { - // First, check if the class has already been loaded - Class c = findLoadedClass(name); - if (c == null) { - try { - if (parent != null) { - c = parent.loadClass(name, false); - } else { - c = findBootstrapClassOrNull(name); - } - } catch (ClassNotFoundException e) { - // ClassNotFoundException thrown if class not found - // from the non-null parent class loader - } - - if (c == null) { - // If still not found, then invoke findClass in order - // to find the class. - c = findClass(name); - } - } - if (resolve) { - resolveClass(c); - } - return c; - } - } - - protected Class findClass(String name) throws ClassNotFoundException { - throw new ClassNotFoundException(name); - } -} -``` - -## 自定义类加载器实现 - -FileSystemClassLoader 是自定义类加载器,继承自 java.lang.ClassLoader,用于加载文件系统上的类。它首先根据类的全名在文件系统上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass() 方法来把这些字节代码转换成 java.lang.Class 类的实例。 - -java.lang.ClassLoader 的 loadClass() 实现了双亲委派模型的逻辑,自定义类加载器一般不去重写它,但是需要重写 findClass() 方法。 - -```java -public class FileSystemClassLoader extends ClassLoader { - - private String rootDir; - - public FileSystemClassLoader(String rootDir) { - this.rootDir = rootDir; - } - - protected Class findClass(String name) throws ClassNotFoundException { - byte[] classData = getClassData(name); - if (classData == null) { - throw new ClassNotFoundException(); - } else { - return defineClass(name, classData, 0, classData.length); - } - } - - private byte[] getClassData(String className) { - String path = classNameToPath(className); - try { - InputStream ins = new FileInputStream(path); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - int bufferSize = 4096; - byte[] buffer = new byte[bufferSize]; - int bytesNumRead; - while ((bytesNumRead = ins.read(buffer)) != -1) { - baos.write(buffer, 0, bytesNumRead); - } - return baos.toByteArray(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - private String classNameToPath(String className) { - return rootDir + File.separatorChar - + className.replace('.', File.separatorChar) + ".class"; - } -} -``` - -# 参考资料 - -- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011. -- [Chapter 2. The Structure of the Java Virtual Machine](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4) -- [Jvm memory](https://www.slideshare.net/benewu/jvm-memory) -[Getting Started with the G1 Garbage Collector](http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html) -- [JNI Part1: Java Native Interface Introduction and “Hello World” application](http://electrofriends.com/articles/jni/jni-part1-java-native-interface/) -- [Memory Architecture Of JVM(Runtime Data Areas)](https://hackthejava.wordpress.com/2015/01/09/memory-architecture-by-jvmruntime-data-areas/) -- [JVM Run-Time Data Areas](https://www.programcreek.com/2013/04/jvm-run-time-data-areas/) -- [Android on x86: Java Native Interface and the Android Native Development Kit](http://www.drdobbs.com/architecture-and-design/android-on-x86-java-native-interface-and/240166271) -- [深入理解 JVM(2)——GC 算法与内存分配策略](https://crowhawk.github.io/2017/08/10/jvm_2/) -- [深入理解 JVM(3)——7 种垃圾收集器](https://crowhawk.github.io/2017/08/15/jvm_3/) -- [JVM Internals](http://blog.jamesdbloom.com/JVMInternals.html) -- [深入探讨 Java 类加载器](https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html#code6) -- [Guide to WeakHashMap in Java](http://www.baeldung.com/java-weakhashmap) -- [Tomcat example source code file (ConcurrentCache.java)](https://alvinalexander.com/java/jwarehouse/apache-tomcat-6.0.16/java/org/apache/el/util/ConcurrentCache.java.shtml) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" deleted file mode 100644 index 5183c0ef90..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" +++ /dev/null @@ -1,300 +0,0 @@ - -* [原理](#原理) - * [1. 正常实现](#1-正常实现) - * [2. 时间复杂度](#2-时间复杂度) - * [3. m 计算](#3-m-计算) - * [4. 返回值](#4-返回值) - * [5. 变种](#5-变种) -* [例题](#例题) - * [1. 求开方](#1-求开方) - * [2. 大于给定元素的最小元素](#2-大于给定元素的最小元素) - * [3. 有序数组的 Single Element](#3-有序数组的-single-element) - * [4. 第一个错误的版本](#4-第一个错误的版本) - * [5. 旋转数组的最小数字](#5-旋转数组的最小数字) - * [6. 查找区间](#6-查找区间) - - - -# 原理 - -## 1. 正常实现 - -```java -public int binarySearch(int[] nums, int key) { - int l = 0, h = nums.length - 1; - while (l <= h) { - int m = l + (h - l) / 2; - if (nums[m] == key) { - return m; - } else if (nums[m] > key) { - h = m - 1; - } else { - l = m + 1; - } - } - return -1; -} -``` - -## 2. 时间复杂度 - -二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。 - -## 3. m 计算 - -有两种计算中值 m 的方式: - -- m = (l + h) / 2 -- m = l + (h - l) / 2 - -l + h 可能出现加法溢出,最好使用第二种方式。 - -## 4. 返回值 - -循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值: - -- -1:以一个错误码表示没有查找到 key -- l:将 key 插入到 nums 中的正确位置 - -## 5. 变种 - -二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下: - -```java -public int binarySearch(int[] nums, int key) { - int l = 0, h = nums.length - 1; - while (l < h) { - int m = l + (h - l) / 2; - if (nums[m] >= key) { - h = m; - } else { - l = m + 1; - } - } - return l; -} -``` - -该实现和正常实现有以下不同: - -- 循环条件为 l < h -- h 的赋值表达式为 h = m -- 最后返回 l 而不是 -1 - -在 nums[m] >= key 的情况下,可以推导出最左 key 位于 [l, m] 区间中,这是一个闭区间。h 的赋值表达式为 h = m,因为 m 位置也可能是解。 - -在 h 的赋值表达式为 h = mid 的情况下,如果循环条件为 l <= h,那么会出现循环无法退出的情况,因此循环条件只能是 l < h。以下演示了循环条件为 l <= h 时循环无法退出的情况: - -```text -nums = {0, 1, 2}, key = 1 -l m h -0 1 2 nums[m] >= key -0 0 1 nums[m] < key -1 1 1 nums[m] >= key -1 1 1 nums[m] >= key -... -``` - -当循环体退出时,不表示没有查找到 key,因此最后返回的结果不应该为 -1。为了验证有没有查找到,需要在调用端判断一下返回位置上的值和 key 是否相等。 - -# 例题 - -## 1. 求开方 - -[69. Sqrt(x) (Easy)](https://leetcode.com/problems/sqrtx/description/) - -```html -Input: 4 -Output: 2 - -Input: 8 -Output: 2 -Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated. -``` - -一个数 x 的开方 sqrt 一定在 0 \~ x 之间,并且满足 sqrt == x / sqrt。可以利用二分查找在 0 \~ x 之间查找 sqrt。 - -对于 x = 8,它的开方是 2.82842...,最后应该返回 2 而不是 3。在循环条件为 l <= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。 - -```java -public int mySqrt(int x) { - if (x <= 1) { - return x; - } - int l = 1, h = x; - while (l <= h) { - int mid = l + (h - l) / 2; - int sqrt = x / mid; - if (sqrt == mid) { - return mid; - } else if (mid > sqrt) { - h = mid - 1; - } else { - l = mid + 1; - } - } - return h; -} -``` - -## 2. 大于给定元素的最小元素 - -[744. Find Smallest Letter Greater Than Target (Easy)](https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/) - -```html -Input: -letters = ["c", "f", "j"] -target = "d" -Output: "f" - -Input: -letters = ["c", "f", "j"] -target = "k" -Output: "c" -``` - -题目描述:给定一个有序的字符数组 letters 和一个字符 target,要求找出 letters 中大于 target 的最小字符,如果找不到就返回第 1 个字符。 - -```java -public char nextGreatestLetter(char[] letters, char target) { - int n = letters.length; - int l = 0, h = n - 1; - while (l <= h) { - int m = l + (h - l) / 2; - if (letters[m] <= target) { - l = m + 1; - } else { - h = m - 1; - } - } - return l < n ? letters[l] : letters[0]; -} -``` - -## 3. 有序数组的 Single Element - -[540. Single Element in a Sorted Array (Medium)](https://leetcode.com/problems/single-element-in-a-sorted-array/description/) - -```html -Input: [1, 1, 2, 3, 3, 4, 4, 8, 8] -Output: 2 -``` - -题目描述:一个有序数组只有一个数不出现两次,找出这个数。要求以 O(logN) 时间复杂度进行求解。 - -令 index 为 Single Element 在数组中的位置。如果 m 为偶数,并且 m + 1 < index,那么 nums[m] == nums[m + 1];m + 1 >= index,那么 nums[m] != nums[m + 1]。 - -从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 index 所在的数组位置为 [m + 2, h],此时令 l = m + 2;如果 nums[m] != nums[m + 1],那么 index 所在的数组位置为 [l, m],此时令 h = m。 - -因为 h 的赋值表达式为 h = m,那么循环条件也就只能使用 l < h 这种形式。 - -```java -public int singleNonDuplicate(int[] nums) { - int l = 0, h = nums.length - 1; - while (l < h) { - int m = l + (h - l) / 2; - if (m % 2 == 1) { - m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数 - } - if (nums[m] == nums[m + 1]) { - l = m + 2; - } else { - h = m; - } - } - return nums[l]; -} -``` - -## 4. 第一个错误的版本 - -[278. First Bad Version (Easy)](https://leetcode.com/problems/first-bad-version/description/) - -题目描述:给定一个元素 n 代表有 [1, 2, ..., n] 版本,可以调用 isBadVersion(int x) 知道某个版本是否错误,要求找到第一个错误的版本。 - -如果第 m 个版本出错,则表示第一个错误的版本在 [l, m] 之间,令 h = m;否则第一个错误的版本在 [m + 1, h] 之间,令 l = m + 1。 - -因为 h 的赋值表达式为 h = m,因此循环条件为 l < h。 - -```java -public int firstBadVersion(int n) { - int l = 1, h = n; - while (l < h) { - int mid = l + (h - l) / 2; - if (isBadVersion(mid)) { - h = mid; - } else { - l = mid + 1; - } - } - return l; -} -``` - -## 5. 旋转数组的最小数字 - -[153. Find Minimum in Rotated Sorted Array (Medium)](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/) - -```html -Input: [3,4,5,1,2], -Output: 1 -``` - -```java -public int findMin(int[] nums) { - 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]; -} -``` - -## 6. 查找区间 - -[34. Search for a Range (Medium)](https://leetcode.com/problems/search-for-a-range/description/) - -```html -Input: nums = [5,7,7,8,8,10], target = 8 -Output: [3,4] - -Input: nums = [5,7,7,8,8,10], target = 6 -Output: [-1,-1] -``` - -```java -public int[] searchRange(int[] nums, int target) { - int first = binarySearch(nums, target); - int last = binarySearch(nums, target + 1) - 1; - if (first == nums.length || nums[first] != target) { - return new int[]{-1, -1}; - } else { - return new int[]{first, Math.max(first, last)}; - } -} - -private int binarySearch(int[] nums, int target) { - int l = 0, h = nums.length; // 注意 h 的初始值 - while (l < h) { - int m = l + (h - l) / 2; - if (nums[m] >= target) { - h = m; - } else { - l = m + 1; - } - } - return l; -} -``` - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" deleted file mode 100644 index c4769f5446..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" +++ /dev/null @@ -1,433 +0,0 @@ - -* [原理](#原理) - * [1. 基本原理](#1-基本原理) - * [2. mask 计算](#2-mask-计算) - * [3. Java 中的位操作](#3-java-中的位操作) -* [例题](#例题) - * [统计两个数的二进制表示有多少位不同](#统计两个数的二进制表示有多少位不同) - * [数组中唯一一个不重复的元素](#数组中唯一一个不重复的元素) - * [找出数组中缺失的那个数](#找出数组中缺失的那个数) - * [数组中不重复的两个元素](#数组中不重复的两个元素) - * [翻转一个数的比特位](#翻转一个数的比特位) - * [不用额外变量交换两个整数](#不用额外变量交换两个整数) - * [判断一个数是不是 2 的 n 次方](#判断一个数是不是-2-的-n-次方) - * [判断一个数是不是 4 的 n 次方](#判断一个数是不是-4-的-n-次方) - * [判断一个数的位级表示是否不会出现连续的 0 和 1](#判断一个数的位级表示是否不会出现连续的-0-和-1) - * [求一个数的补码](#求一个数的补码) - * [实现整数的加法](#实现整数的加法) - * [字符串数组最大乘积](#字符串数组最大乘积) - * [统计从 0 \~ n 每个数的二进制表示中 1 的个数](#统计从-0-\~-n-每个数的二进制表示中-1-的个数) - - - -# 原理 - -## 1. 基本原理 - -0s 表示一串 0,1s 表示一串 1。 - -``` -x ^ 0s = x x & 0s = 0 x | 0s = x -x ^ 1s = ~x x & 1s = x x | 1s = 1s -x ^ x = 0 x & x = x x | x = x -``` - -- 利用 x ^ 1s = \~x 的特点,可以将位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。 -- 利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。 -- 利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。 - -位与运算技巧: - -- n&(n-1) 去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110100,减去 1 得到 10110011,这两个数相与得到 10110000。 -- n&(-n) 得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。 -- n-n&(\~n+1) 去除 n 的位级表示中最高的那一位。 - -移位运算: - -- \>\> n 为算术右移,相当于除以 2n; -- \>\>\> n 为无符号右移,左边会补上 0。 -- << n 为算术左移,相当于乘以 2n。 - -## 2. 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 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~(1<<(i+1)-1)。 - -## 3. Java 中的位操作 - -```html -static int Integer.bitCount(); // 统计 1 的数量 -static int Integer.highestOneBit(); // 获得最高位 -static String toBinaryString(int i); // 转换为二进制表示的字符串 -``` - -# 例题 - -## 统计两个数的二进制表示有多少位不同 - -[461. Hamming Distance (Easy)](https://leetcode.com/problems/hamming-distance/) - -```html -Input: x = 1, y = 4 - -Output: 2 - -Explanation: -1 (0 0 0 1) -4 (0 1 0 0) - ↑ ↑ - -The above arrows point to positions where the corresponding bits are different. -``` - -对两个数进行异或操作,位级表示不同的那一位为 1,统计有多少个 1 即可。 - -```java -public int hammingDistance(int x, int y) { - int z = x ^ y; - int cnt = 0; - while(z != 0) { - if ((z & 1) == 1) cnt++; - z = z >> 1; - } - return cnt; -} -``` - -使用 z&(z-1) 去除 z 位级表示最低的那一位。 - -```java -public int hammingDistance(int x, int y) { - int z = x ^ y; - int cnt = 0; - while (z != 0) { - z &= (z - 1); - cnt++; - } - return cnt; -} -``` - -可以使用 Integer.bitcount() 来统计 1 个的个数。 - -```java -public int hammingDistance(int x, int y) { - return Integer.bitCount(x ^ y); -} -``` - -## 数组中唯一一个不重复的元素 - -[136. Single Number (Easy)](https://leetcode.com/problems/single-number/description/) - -```html -Input: [4,1,2,1,2] -Output: 4 -``` - -两个相同的数异或的结果为 0,对所有数进行异或操作,最后的结果就是单独出现的那个数。 - -```java -public int singleNumber(int[] nums) { - int ret = 0; - for (int n : nums) ret = ret ^ n; - return ret; -} -``` - -## 找出数组中缺失的那个数 - -[268. Missing Number (Easy)](https://leetcode.com/problems/missing-number/description/) - -```html -Input: [3,0,1] -Output: 2 -``` - -题目描述:数组元素在 0-n 之间,但是有一个数是缺失的,要求找到这个缺失的数。 - -```java -public int missingNumber(int[] nums) { - int ret = 0; - for (int i = 0; i < nums.length; i++) { - ret = ret ^ i ^ nums[i]; - } - return ret ^ nums.length; -} -``` - -## 数组中不重复的两个元素 - -[260. Single Number III (Medium)](https://leetcode.com/problems/single-number-iii/description/) - -两个不相等的元素在位级表示上必定会有一位存在不同。 - -将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。 - -diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。 - -```java -public int[] singleNumber(int[] nums) { - int diff = 0; - for (int num : nums) diff ^= num; - diff &= -diff; // 得到最右一位 - int[] ret = new int[2]; - for (int num : nums) { - if ((num & diff) == 0) ret[0] ^= num; - else ret[1] ^= num; - } - return ret; -} -``` - -## 翻转一个数的比特位 - -[190. Reverse Bits (Easy)](https://leetcode.com/problems/reverse-bits/description/) - -```java -public int reverseBits(int n) { - int ret = 0; - for (int i = 0; i < 32; i++) { - ret <<= 1; - ret |= (n & 1); - n >>>= 1; - } - return ret; -} -``` - -如果该函数需要被调用很多次,可以将 int 拆成 4 个 byte,然后缓存 byte 对应的比特位翻转,最后再拼接起来。 - -```java -private static Map cache = new HashMap<>(); - -public int reverseBits(int n) { - int ret = 0; - for (int i = 0; i < 4; i++) { - ret <<= 8; - ret |= reverseByte((byte) (n & 0b11111111)); - n >>= 8; - } - return ret; -} - -private int reverseByte(byte b) { - if (cache.containsKey(b)) return cache.get(b); - int ret = 0; - byte t = b; - for (int i = 0; i < 8; i++) { - ret <<= 1; - ret |= t & 1; - t >>= 1; - } - cache.put(b, ret); - return ret; -} -``` - -## 不用额外变量交换两个整数 - -[程序员代码面试指南 :P317](#) - -```java -a = a ^ b; -b = a ^ b; -a = a ^ b; -``` - -## 判断一个数是不是 2 的 n 次方 - -[231. Power of Two (Easy)](https://leetcode.com/problems/power-of-two/description/) - -二进制表示只有一个 1 存在。 - -```java -public boolean isPowerOfTwo(int n) { - return n > 0 && Integer.bitCount(n) == 1; -} -``` - -利用 1000 & 0111 == 0 这种性质,得到以下解法: - -```java -public boolean isPowerOfTwo(int n) { - return n > 0 && (n & (n - 1)) == 0; -} -``` - -## 判断一个数是不是 4 的 n 次方 - -[342. Power of Four (Easy)](https://leetcode.com/problems/power-of-four/) - -这种数在二进制表示中有且只有一个奇数位为 1,例如 16(10000)。 - -```java -public boolean isPowerOfFour(int num) { - return num > 0 && (num & (num - 1)) == 0 && (num & 0b01010101010101010101010101010101) != 0; -} -``` - -也可以使用正则表达式进行匹配。 - -```java -public boolean isPowerOfFour(int num) { - return Integer.toString(num, 4).matches("10*"); -} -``` - -## 判断一个数的位级表示是否不会出现连续的 0 和 1 - -[693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/) - -```html -Input: 10 -Output: True -Explanation: -The binary representation of 10 is: 1010. - -Input: 11 -Output: False -Explanation: -The binary representation of 11 is: 1011. -``` - -对于 1010 这种位级表示的数,把它向右移动 1 位得到 101,这两个数每个位都不同,因此异或得到的结果为 1111。 - -```java -public boolean hasAlternatingBits(int n) { - int a = (n ^ (n >> 1)); - return (a & (a + 1)) == 0; -} -``` - -## 求一个数的补码 - -[476. Number Complement (Easy)](https://leetcode.com/problems/number-complement/description/) - -```html -Input: 5 -Output: 2 -Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2. -``` - -题目描述:不考虑二进制表示中的首 0 部分。 - -对于 00000101,要求补码可以将它与 00000111 进行异或操作。那么问题就转换为求掩码 00000111。 - -```java -public int findComplement(int num) { - if (num == 0) return 1; - int mask = 1 << 30; - while ((num & mask) == 0) mask >>= 1; - mask = (mask << 1) - 1; - return num ^ mask; -} -``` - -可以利用 Java 的 Integer.highestOneBit() 方法来获得含有首 1 的数。 - -```java -public int findComplement(int num) { - if (num == 0) return 1; - int mask = Integer.highestOneBit(num); - mask = (mask << 1) - 1; - return num ^ mask; -} -``` - -对于 10000000 这样的数要扩展成 11111111,可以利用以下方法: - -```html -mask |= mask >> 1 11000000 -mask |= mask >> 2 11110000 -mask |= mask >> 4 11111111 -``` - -```java -public int findComplement(int num) { - int mask = num; - mask |= mask >> 1; - mask |= mask >> 2; - mask |= mask >> 4; - mask |= mask >> 8; - mask |= mask >> 16; - return (mask ^ num); -} -``` - -## 实现整数的加法 - -[371. Sum of Two Integers (Easy)](https://leetcode.com/problems/sum-of-two-integers/description/) - -a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。 - -递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 - -```java -public int getSum(int a, int b) { - return b == 0 ? a : getSum((a ^ b), (a & b) << 1); -} -``` - -## 字符串数组最大乘积 - -[318. Maximum Product of Word Lengths (Medium)](https://leetcode.com/problems/maximum-product-of-word-lengths/description/) - -```html -Given ["abcw", "baz", "foo", "bar", "xtfn", "abcdef"] -Return 16 -The two words can be "abcw", "xtfn". -``` - -题目描述:字符串数组的字符串只含有小写字符。求解字符串数组中两个字符串长度的最大乘积,要求这两个字符串不能含有相同字符。 - -本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位,因此可以用一个 32 位的整数来存储每个字符是否出现过。 - -```java -public int maxProduct(String[] words) { - int n = words.length; - int[] val = new int[n]; - for (int i = 0; i < n; i++) { - for (char c : words[i].toCharArray()) { - val[i] |= 1 << (c - 'a'); - } - } - int ret = 0; - for (int i = 0; i < n; i++) { - for (int j = i + 1; j < n; j++) { - if ((val[i] & val[j]) == 0) { - ret = Math.max(ret, words[i].length() * words[j].length()); - } - } - } - return ret; -} -``` - -## 统计从 0 \~ n 每个数的二进制表示中 1 的个数 - -[338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/) - -对于数字 6(110),它可以看成是 4(100) 再加一个 2(10),因此 dp[i] = dp[i&(i-1)] + 1; - -```java -public int[] countBits(int num) { - int[] ret = new int[num + 1]; - for(int i = 1; i <= num; i++){ - ret[i] = ret[i&(i-1)] + 1; - } - return ret; -} -``` - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" deleted file mode 100644 index 17cae52a47..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" +++ /dev/null @@ -1,55 +0,0 @@ - -* [1. 给表达式加括号](#1-给表达式加括号) - - - -# 1. 给表达式加括号 - -[241. Different Ways to Add Parentheses (Medium)](https://leetcode.com/problems/different-ways-to-add-parentheses/description/) - -```html -Input: "2-1-1". - -((2-1)-1) = 0 -(2-(1-1)) = 2 - -Output : [0, 2] -``` - -```java -public List diffWaysToCompute(String input) { - List ways = new ArrayList<>(); - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - if (c == '+' || c == '-' || c == '*') { - List left = diffWaysToCompute(input.substring(0, i)); - List right = diffWaysToCompute(input.substring(i + 1)); - for (int l : left) { - for (int r : right) { - switch (c) { - case '+': - ways.add(l + r); - break; - case '-': - ways.add(l - r); - break; - case '*': - ways.add(l * r); - break; - } - } - } - } - } - if (ways.size() == 0) { - ways.add(Integer.valueOf(input)); - } - return ways; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" deleted file mode 100644 index e71b942789..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" +++ /dev/null @@ -1,1227 +0,0 @@ - -* [斐波那契数列](#斐波那契数列) - * [爬楼梯](#爬楼梯) - * [强盗抢劫](#强盗抢劫) - * [强盗在环形街区抢劫](#强盗在环形街区抢劫) - * [信件错排](#信件错排) - * [母牛生产](#母牛生产) -* [矩阵路径](#矩阵路径) - * [矩阵的最小路径和](#矩阵的最小路径和) - * [矩阵的总路径数](#矩阵的总路径数) -* [数组区间](#数组区间) - * [数组区间和](#数组区间和) - * [数组中等差递增子区间的个数](#数组中等差递增子区间的个数) -* [分割整数](#分割整数) - * [分割整数的最大乘积](#分割整数的最大乘积) - * [按平方数来分割整数](#按平方数来分割整数) - * [分割整数构成字母字符串](#分割整数构成字母字符串) -* [最长递增子序列](#最长递增子序列) - * [最长递增子序列](#最长递增子序列) - * [一组整数对能够构成的最长链](#一组整数对能够构成的最长链) - * [最长摆动子序列](#最长摆动子序列) -* [最长公共子序列](#最长公共子序列) -* [0-1 背包](#0-1-背包) - * [空间优化](#空间优化) - * [无法使用贪心算法的解释](#无法使用贪心算法的解释) - * [变种](#变种) - * [划分数组为和相等的两部分](#划分数组为和相等的两部分) - * [改变一组数的正负号使得它们的和为一给定数](#改变一组数的正负号使得它们的和为一给定数) - * [01 字符构成最多的字符串](#01-字符构成最多的字符串) - * [找零钱的最少硬币数](#找零钱的最少硬币数) - * [找零钱的硬币数组合](#找零钱的硬币数组合) - * [字符串按单词列表分割](#字符串按单词列表分割) - * [组合总和](#组合总和) -* [股票交易](#股票交易) - * [需要冷却期的股票交易](#需要冷却期的股票交易) - * [需要交易费用的股票交易](#需要交易费用的股票交易) - * [只能进行两次的股票交易](#只能进行两次的股票交易) - * [只能进行 k 次的股票交易](#只能进行-k-次的股票交易) -* [字符串编辑](#字符串编辑) - * [删除两个字符串的字符使它们相等](#删除两个字符串的字符使它们相等) - * [编辑距离](#编辑距离) - * [复制粘贴字符](#复制粘贴字符) - - - -递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。 - -# 斐波那契数列 - -## 爬楼梯 - -[70. Climbing Stairs (Easy)](https://leetcode.com/problems/climbing-stairs/description/) - -题目描述:有 N 阶楼梯,每次可以上一阶或者两阶,求有多少种上楼梯的方法。 - -定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。 - -第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。 - - - -

- - -考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。 - -```java -public int climbStairs(int n) { - if (n <= 2) { - return n; - } - int pre2 = 1, pre1 = 2; - for (int i = 2; i < n; i++) { - int cur = pre1 + pre2; - pre2 = pre1; - pre1 = cur; - } - return pre1; -} -``` - -## 强盗抢劫 - -[198. House Robber (Easy)](https://leetcode.com/problems/house-robber/description/) - -题目描述:抢劫一排住户,但是不能抢邻近的住户,求最大抢劫量。 - -定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。 - -由于不能抢劫邻近住户,如果抢劫了第 i -1 个住户,那么就不能再抢劫第 i 个住户,所以 - - - -

- -```java -public int rob(int[] nums) { - int pre2 = 0, pre1 = 0; - for (int i = 0; i < nums.length; i++) { - int cur = Math.max(pre2 + nums[i], pre1); - pre2 = pre1; - pre1 = cur; - } - return pre1; -} -``` - -## 强盗在环形街区抢劫 - -[213. House Robber II (Medium)](https://leetcode.com/problems/house-robber-ii/description/) - -```java -public int rob(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - int n = nums.length; - if (n == 1) { - return nums[0]; - } - return Math.max(rob(nums, 0, n - 2), rob(nums, 1, n - 1)); -} - -private int rob(int[] nums, int first, int last) { - int pre2 = 0, pre1 = 0; - for (int i = first; i <= last; i++) { - int cur = Math.max(pre1, pre2 + nums[i]); - pre2 = pre1; - pre1 = cur; - } - return pre1; -} -``` - -## 信件错排 - -题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。 - -定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况: - -- i==k,交换 i 和 k 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)\*dp[i-2] 种错误装信方式。 -- i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1 种取值,因此共有 (i-1)\*dp[i-1] 种错误装信方式。 - -综上所述,错误装信数量方式数量为: - - - -

- -## 母牛生产 - -[程序员代码面试指南-P181](#) - -题目描述:假设农场中成熟的母牛每年都会生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。 - -第 i 年成熟的牛的数量为: - - - -

- -# 矩阵路径 - -## 矩阵的最小路径和 - -[64. Minimum Path Sum (Medium)](https://leetcode.com/problems/minimum-path-sum/description/) - -```html -[[1,3,1], - [1,5,1], - [4,2,1]] -Given the above grid map, return 7. Because the path 1→3→1→1→1 minimizes the sum. -``` - -题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向右和向下移动。 - -```java -public int minPathSum(int[][] grid) { - if (grid.length == 0 || grid[0].length == 0) { - return 0; - } - int m = grid.length, n = grid[0].length; - int[] dp = new int[n]; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (j == 0) { - dp[j] = dp[j]; // 只能从上侧走到该位置 - } else if (i == 0) { - dp[j] = dp[j - 1]; // 只能从左侧走到该位置 - } else { - dp[j] = Math.min(dp[j - 1], dp[j]); - } - dp[j] += grid[i][j]; - } - } - return dp[n - 1]; -} -``` - -## 矩阵的总路径数 - -[62. Unique Paths (Medium)](https://leetcode.com/problems/unique-paths/description/) - -题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。 - -

- -```java -public int uniquePaths(int m, int n) { - int[] dp = new int[n]; - Arrays.fill(dp, 1); - for (int i = 1; i < m; i++) { - for (int j = 1; j < n; j++) { - dp[j] = dp[j] + dp[j - 1]; - } - } - return dp[n - 1]; -} -``` - -也可以直接用数学公式求解,这是一个组合问题。机器人总共移动的次数 S=m+n-2,向下移动的次数 D=m-1,那么问题可以看成从 S 中取出 D 个位置的组合数量,这个问题的解为 C(S, D)。 - -```java -public int uniquePaths(int m, int n) { - int S = m + n - 2; // 总共的移动次数 - int D = m - 1; // 向下的移动次数 - long ret = 1; - for (int i = 1; i <= D; i++) { - ret = ret * (S - D + i) / i; - } - return (int) ret; -} -``` - -# 数组区间 - -## 数组区间和 - -[303. Range Sum Query - Immutable (Easy)](https://leetcode.com/problems/range-sum-query-immutable/description/) - -```html -Given nums = [-2, 0, 3, -5, 2, -1] - -sumRange(0, 2) -> 1 -sumRange(2, 5) -> -1 -sumRange(0, 5) -> -3 -``` - -求区间 i \~ j 的和,可以转换为 sum[j + 1] - sum[i],其中 sum[i] 为 0 \~ i - 1 的和。 - -```java -class NumArray { - - private int[] sums; - - public NumArray(int[] nums) { - sums = new int[nums.length + 1]; - for (int i = 1; i <= nums.length; i++) { - sums[i] = sums[i - 1] + nums[i - 1]; - } - } - - public int sumRange(int i, int j) { - return sums[j + 1] - sums[i]; - } -} -``` - -## 数组中等差递增子区间的个数 - -[413. Arithmetic Slices (Medium)](https://leetcode.com/problems/arithmetic-slices/description/) - -```html -A = [1, 2, 3, 4] -return: 3, for 3 arithmetic slices in A: [1, 2, 3], [2, 3, 4] and [1, 2, 3, 4] itself. -``` - -dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。 - -在 A[i] - A[i - 1] == A[i - 1] - A[i - 2] 的条件下,{A[i - 2], A[i - 1], A[i]} 是一个等差递增子区间。如果 {A[i - 3], A[i - 2], A[i - 1]} 是一个等差递增子区间,那么 {A[i - 3], A[i - 2], A[i - 1], A[i]} 也是等差递增子区间,dp[i] = dp[i-1] + 1。 - -```java -public int numberOfArithmeticSlices(int[] A) { - if (A == null || A.length == 0) { - return 0; - } - int n = A.length; - int[] dp = new int[n]; - for (int i = 2; i < n; i++) { - if (A[i] - A[i - 1] == A[i - 1] - A[i - 2]) { - dp[i] = dp[i - 1] + 1; - } - } - int total = 0; - for (int cnt : dp) { - total += cnt; - } - return total; -} -``` - -# 分割整数 - -## 分割整数的最大乘积 - -[343. Integer Break (Medim)](https://leetcode.com/problems/integer-break/description/) - -题目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). - -```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 - 1; j++) { - dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j))); - } - } - return dp[n]; -} -``` - -## 按平方数来分割整数 - -[279. Perfect Squares(Medium)](https://leetcode.com/problems/perfect-squares/description/) - -题目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. - -```java -public int numSquares(int n) { - List squareList = generateSquareList(n); - int[] dp = new int[n + 1]; - for (int i = 1; i <= n; i++) { - int min = Integer.MAX_VALUE; - for (int square : squareList) { - if (square > i) { - break; - } - min = Math.min(min, dp[i - square] + 1); - } - dp[i] = min; - } - return dp[n]; -} - -private List generateSquareList(int n) { - List squareList = new ArrayList<>(); - int diff = 3; - int square = 1; - while (square <= n) { - squareList.add(square); - square += diff; - diff += 2; - } - return squareList; -} -``` - -## 分割整数构成字母字符串 - -[91. Decode Ways (Medium)](https://leetcode.com/problems/decode-ways/description/) - -题目描述:Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). - -```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]; -} -``` - -# 最长递增子序列 - -已知一个序列 {S1, S2,...,Sn},取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个 **子序列** 。 - -如果在子序列中,当下标 ix > iy 时,Six > Siy,称子序列为原序列的一个 **递增子序列** 。 - -定义一个数组 dp 存储最长递增子序列的长度,dp[n] 表示以 Sn 结尾的序列的最长递增子序列长度。对于一个递增子序列 {Si1, Si2,...,Sim},如果 im < n 并且 Sim < Sn,此时 {Si1, Si2,..., Sim, Sn} 为一个递增子序列,递增子序列的长度增加 1。满足上述条件的递增子序列中,长度最长的那个递增子序列就是要找的,在长度最长的递增子序列上加上 Sn 就构成了以 Sn 为结尾的最长递增子序列。因此 dp[n] = max{ dp[i]+1 | Si < Sn && i < n} 。 - -因为在求 dp[n] 时可能无法找到一个满足条件的递增子序列,此时 {Sn} 就构成了递增子序列,需要对前面的求解方程做修改,令 dp[n] 最小为 1,即: - - - -

- -对于一个长度为 N 的序列,最长递增子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,max{ dp[i] | 1 <= i <= N} 即为所求。 - -## 最长递增子序列 - -[300. Longest Increasing Subsequence (Medium)](https://leetcode.com/problems/longest-increasing-subsequence/description/) - -```java -public int lengthOfLIS(int[] nums) { - int n = nums.length; - int[] dp = new int[n]; - for (int i = 0; i < n; i++) { - int max = 1; - for (int j = 0; j < i; j++) { - if (nums[i] > nums[j]) { - max = Math.max(max, dp[j] + 1); - } - } - dp[i] = max; - } - return Arrays.stream(dp).max().orElse(0); -} -``` - -使用 Stream 求最大值会导致运行时间过长,可以改成以下形式: - -```java -int ret = 0; -for (int i = 0; i < n; i++) { - ret = Math.max(ret, dp[i]); -} -return ret; -``` - -以上解法的时间复杂度为 O(N2),可以使用二分查找将时间复杂度降低为 O(NlogN)。 - -定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素。对于一个元素 x, - -- 如果它大于 tails 数组所有的值,那么把它添加到 tails 后面,表示最长递增子序列长度加 1; -- 如果 tails[i-1] < x <= tails[i],那么更新 tails[i] = x。 - -例如对于数组 [4,3,6,5],有: - -```html -tails len num -[] 0 4 -[4] 1 3 -[3] 1 6 -[3,6] 2 5 -[3,5] 2 null -``` - -可以看出 tails 数组保持有序,因此在查找 Si 位于 tails 数组的位置时就可以使用二分查找。 - -```java -public int lengthOfLIS(int[] nums) { - int n = nums.length; - int[] tails = new int[n]; - int len = 0; - for (int num : nums) { - int index = binarySearch(tails, len, num); - tails[index] = num; - if (index == len) { - len++; - } - } - return len; -} - -private int binarySearch(int[] tails, int len, int key) { - int l = 0, h = len; - while (l < h) { - int mid = l + (h - l) / 2; - if (tails[mid] == key) { - return mid; - } else if (tails[mid] > key) { - h = mid; - } else { - l = mid + 1; - } - } - return l; -} -``` - -## 一组整数对能够构成的最长链 - -[646. Maximum Length of Pair Chain (Medium)](https://leetcode.com/problems/maximum-length-of-pair-chain/description/) - -```html -Input: [[1,2], [2,3], [3,4]] -Output: 2 -Explanation: The longest chain is [1,2] -> [3,4] -``` - -题目描述:对于 (a, b) 和 (c, d) ,如果 b < c,则它们可以构成一条链。 - -```java -public int findLongestChain(int[][] pairs) { - if (pairs == null || pairs.length == 0) { - return 0; - } - Arrays.sort(pairs, (a, b) -> (a[0] - b[0])); - int n = pairs.length; - int[] dp = new int[n]; - Arrays.fill(dp, 1); - for (int i = 1; i < n; i++) { - for (int j = 0; j < i; j++) { - if (pairs[j][1] < pairs[i][0]) { - dp[i] = Math.max(dp[i], dp[j] + 1); - } - } - } - return Arrays.stream(dp).max().orElse(0); -} -``` - -## 最长摆动子序列 - -[376. Wiggle Subsequence (Medium)](https://leetcode.com/problems/wiggle-subsequence/description/) - -```html -Input: [1,7,4,9,2,5] -Output: 6 -The entire sequence is a wiggle sequence. - -Input: [1,17,5,10,13,15,10,5,16,8] -Output: 7 -There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. - -Input: [1,2,3,4,5,6,7,8,9] -Output: 2 -``` - -要求:使用 O(N) 时间复杂度求解。 - -```java -public int wiggleMaxLength(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - int up = 1, down = 1; - for (int i = 1; i < nums.length; i++) { - if (nums[i] > nums[i - 1]) { - up = down + 1; - } else if (nums[i] < nums[i - 1]) { - down = up + 1; - } - } - return Math.max(up, down); -} -``` - -# 最长公共子序列 - -对于两个子序列 S1 和 S2,找出它们最长的公共子序列。 - -定义一个二维数组 dp 用来存储最长公共子序列的长度,其中 dp[i][j] 表示 S1 的前 i 个字符与 S2 的前 j 个字符最长公共子序列的长度。考虑 S1i 与 S2j 值是否相等,分为两种情况: - -- 当 S1i==S2j 时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上 S1i 这个值,最长公共子序列长度加 1,即 dp[i][j] = dp[i-1][j-1] + 1。 -- 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,或者 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,取它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。 - -综上,最长公共子序列的状态转移方程为: - - - -

- -对于长度为 N 的序列 S1 和长度为 M 的序列 S2,dp[N][M] 就是序列 S1 和序列 S2 的最长公共子序列长度。 - -与最长递增子序列相比,最长公共子序列有以下不同点: - -- 针对的是两个序列,求它们的最长公共子序列。 -- 在最长递增子序列中,dp[i] 表示以 Si 为结尾的最长递增子序列长度,子序列必须包含 Si ;在最长公共子序列中,dp[i][j] 表示 S1 中前 i 个字符与 S2 中前 j 个字符的最长公共子序列长度,不一定包含 S1i 和 S2j。 -- 在求最终解时,最长公共子序列中 dp[N][M] 就是最终解,而最长递增子序列中 dp[N] 不是最终解,因为以 SN 为结尾的最长递增子序列不一定是整个序列最长递增子序列,需要遍历一遍 dp 数组找到最大者。 - -```java -public int lengthOfLCS(int[] nums1, int[] nums2) { - int n1 = nums1.length, n2 = nums2.length; - int[][] dp = new int[n1 + 1][n2 + 1]; - for (int i = 1; i <= n1; i++) { - for (int j = 1; j <= n2; j++) { - if (nums1[i - 1] == nums2[j - 1]) { - dp[i][j] = dp[i - 1][j - 1] + 1; - } else { - dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); - } - } - } - return dp[n1][n2]; -} -``` - -# 0-1 背包 - -有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。 - -定义一个二维数组 dp 存储最大价值,其中 dp[i][j] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论: - -- 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i-1 件物品的最大价值,dp[i][j] = dp[i-1][j]。 -- 第 i 件物品添加到背包中,dp[i][j] = dp[i-1][j-w] + v。 - -第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为: - - - -

- -```java -public int knapsack(int W, int N, int[] weights, int[] values) { - int[][] dp = new int[N + 1][W + 1]; - for (int i = 1; i <= N; i++) { - int w = weights[i - 1], v = values[i - 1]; - for (int j = 1; j <= W; j++) { - if (j >= w) { - dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v); - } else { - dp[i][j] = dp[i - 1][j]; - } - } - } - return dp[N][W]; -} -``` - -## 空间优化 - -在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅与前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时, - - - -

- -因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],以防将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 - -```java -public int knapsack(int W, int N, int[] weights, int[] values) { - int[] dp = new int[W + 1]; - for (int i = 1; i <= N; i++) { - int w = weights[i - 1], v = values[i - 1]; - for (int j = W; j >= 1; j--) { - if (j >= w) { - dp[j] = Math.max(dp[j], dp[j - w] + v); - } - } - } - return dp[W]; -} -``` - -## 无法使用贪心算法的解释 - -0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优。考虑下面的物品和一个容量为 5 的背包,如果先添加物品 0 再添加物品 1,那么只能存放的价值为 16,浪费了大小为 2 的空间。最优的方式是存放物品 1 和物品 2,价值为 22. - -| id | w | v | v/w | -| --- | --- | --- | --- | -| 0 | 1 | 6 | 6 | -| 1 | 2 | 10 | 5 | -| 2 | 3 | 12 | 4 | - -## 变种 - -- 完全背包:物品数量为无限个 - -- 多重背包:物品数量有限制 - -- 多维费用背包:物品不仅有重量,还有体积,同时考虑这两种限制 - -- 其它:物品之间相互约束或者依赖 - -## 划分数组为和相等的两部分 - -[416. Partition Equal Subset Sum (Medium)](https://leetcode.com/problems/partition-equal-subset-sum/description/) - -```html -Input: [1, 5, 11, 5] - -Output: true - -Explanation: The array can be partitioned as [1, 5, 5] and [11]. -``` - -可以看成一个背包大小为 sum/2 的 0-1 背包问题。 - -```java -public boolean canPartition(int[] nums) { - int sum = computeArraySum(nums); - if (sum % 2 != 0) { - return false; - } - int W = sum / 2; - boolean[] dp = new boolean[W + 1]; - dp[0] = true; - for (int num : nums) { // 0-1 背包一个物品只能用一次 - for (int i = W; i >= num; i--) { // 从后往前,先计算 dp[i] 再计算 dp[i-num] - dp[i] = dp[i] || dp[i - num]; - } - } - return dp[W]; -} - -private int computeArraySum(int[] nums) { - int sum = 0; - for (int num : nums) { - sum += num; - } - return sum; -} -``` - -## 改变一组数的正负号使得它们的和为一给定数 - -[494. Target Sum (Medium)](https://leetcode.com/problems/target-sum/description/) - -```html -Input: nums is [1, 1, 1, 1, 1], S is 3. -Output: 5 -Explanation: - --1+1+1+1+1 = 3 -+1-1+1+1+1 = 3 -+1+1-1+1+1 = 3 -+1+1+1-1+1 = 3 -+1+1+1+1-1 = 3 - -There are 5 ways to assign symbols to make the sum of nums be target 3. -``` - -该问题可以转换为 Subset Sum 问题,从而使用 0-1 背包的方法来求解。 - -可以将这组数看成两部分,P 和 N,其中 P 使用正号,N 使用负号,有以下推导: - -```html - sum(P) - sum(N) = target -sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N) - 2 * sum(P) = target + sum(nums) -``` - -因此只要找到一个子集,令它们都取正号,并且和等于 (target + sum(nums))/2,就证明存在解。 - -```java -public int findTargetSumWays(int[] nums, int S) { - int sum = computeArraySum(nums); - if (sum < S || (sum + S) % 2 == 1) { - return 0; - } - int W = (sum + S) / 2; - int[] dp = new int[W + 1]; - dp[0] = 1; - for (int num : nums) { - for (int i = W; i >= num; i--) { - dp[i] = dp[i] + dp[i - num]; - } - } - return dp[W]; -} - -private int computeArraySum(int[] nums) { - int sum = 0; - for (int num : nums) { - sum += num; - } - return sum; -} -``` - -DFS 解法: - -```java -public int findTargetSumWays(int[] nums, int S) { - return findTargetSumWays(nums, 0, S); -} - -private int findTargetSumWays(int[] nums, int start, int S) { - if (start == nums.length) { - return S == 0 ? 1 : 0; - } - return findTargetSumWays(nums, start + 1, S + nums[start]) - + findTargetSumWays(nums, start + 1, S - nums[start]); -} -``` - -## 01 字符构成最多的字符串 - -[474. Ones and Zeroes (Medium)](https://leetcode.com/problems/ones-and-zeroes/description/) - -```html -Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 -Output: 4 - -Explanation: There are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are "10","0001","1","0" -``` - -这是一个多维费用的 0-1 背包问题,有两个背包大小,0 的数量和 1 的数量。 - -```java -public int findMaxForm(String[] strs, int m, int n) { - if (strs == null || strs.length == 0) { - return 0; - } - int[][] dp = new int[m + 1][n + 1]; - for (String s : strs) { // 每个字符串只能用一次 - int ones = 0, zeros = 0; - for (char c : s.toCharArray()) { - if (c == '0') { - zeros++; - } else { - ones++; - } - } - for (int i = m; i >= zeros; i--) { - for (int j = n; j >= ones; j--) { - dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1); - } - } - } - return dp[m][n]; -} -``` - -## 找零钱的最少硬币数 - -[322. Coin Change (Medium)](https://leetcode.com/problems/coin-change/description/) - -```html -Example 1: -coins = [1, 2, 5], amount = 11 -return 3 (11 = 5 + 5 + 1) - -Example 2: -coins = [2], amount = 3 -return -1. -``` - -题目描述:给一些面额的硬币,要求用这些硬币来组成给定面额的钱数,并且使得硬币数量最少。硬币可以重复使用。 - -- 物品:硬币 -- 物品大小:面额 -- 物品价值:数量 - -因为硬币可以重复使用,因此这是一个完全背包问题。完全背包只需要将 0-1 背包中逆序遍历 dp 数组改为正序遍历即可。 - -```java -public int coinChange(int[] coins, int amount) { - if (amount == 0 || coins == null || coins.length == 0) { - return 0; - } - int[] dp = new int[amount + 1]; - for (int coin : coins) { - for (int i = coin; i <= amount; i++) { //将逆序遍历改为正序遍历 - if (i == coin) { - dp[i] = 1; - } else if (dp[i] == 0 && dp[i - coin] != 0) { - dp[i] = dp[i - coin] + 1; - } else if (dp[i - coin] != 0) { - dp[i] = Math.min(dp[i], dp[i - coin] + 1); - } - } - } - return dp[amount] == 0 ? -1 : dp[amount]; -} -``` - -## 找零钱的硬币数组合 - -[518\. Coin Change 2 (Medium)](https://leetcode.com/problems/coin-change-2/description/) - -```text-html-basic -Input: amount = 5, coins = [1, 2, 5] -Output: 4 -Explanation: there are four ways to make up the amount: -5=5 -5=2+2+1 -5=2+1+1+1 -5=1+1+1+1+1 -``` - -完全背包问题,使用 dp 记录可达成目标的组合数目。 - -```java -public int change(int amount, int[] coins) { - if (amount == 0 || coins == null || coins.length == 0) { - return 0; - } - int[] dp = new int[amount + 1]; - dp[0] = 1; - for (int coin : coins) { - for (int i = coin; i <= amount; i++) { - dp[i] += dp[i - coin]; - } - } - return dp[amount]; -} -``` - -## 字符串按单词列表分割 - -[139. Word Break (Medium)](https://leetcode.com/problems/word-break/description/) - -```html -s = "leetcode", -dict = ["leet", "code"]. -Return true because "leetcode" can be segmented as "leet code". -``` - -dict 中的单词没有使用次数的限制,因此这是一个完全背包问题。该问题涉及到字典中单词的使用顺序,因此可理解为涉及顺序的完全背包问题。 - -求解顺序的完全背包问题时,对物品的迭代应该放在最里层。 - -```java -public boolean wordBreak(String s, List wordDict) { - int n = s.length(); - boolean[] dp = new boolean[n + 1]; - dp[0] = true; - for (int i = 1; i <= n; i++) { - for (String word : wordDict) { // 对物品的迭代应该放在最里层 - int len = word.length(); - if (len <= i && word.equals(s.substring(i - len, i))) { - dp[i] = dp[i] || dp[i - len]; - } - } - } - return dp[n]; -} -``` - -## 组合总和 - -[377. Combination Sum IV (Medium)](https://leetcode.com/problems/combination-sum-iv/description/) - -```html -nums = [1, 2, 3] -target = 4 - -The possible combination ways are: -(1, 1, 1, 1) -(1, 1, 2) -(1, 2, 1) -(1, 3) -(2, 1, 1) -(2, 2) -(3, 1) - -Note that different sequences are counted as different combinations. - -Therefore the output is 7. -``` - -涉及顺序的完全背包。 - -```java -public int combinationSum4(int[] nums, int target) { - if (nums == null || nums.length == 0) { - return 0; - } - int[] maximum = new int[target + 1]; - maximum[0] = 1; - Arrays.sort(nums); - for (int i = 1; i <= target; i++) { - for (int j = 0; j < nums.length && nums[j] <= i; j++) { - maximum[i] += maximum[i - nums[j]]; - } - } - return maximum[target]; -} -``` - -# 股票交易 - -## 需要冷却期的股票交易 - -[309. Best Time to Buy and Sell Stock with Cooldown(Medium)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/) - -题目描述:交易之后需要有一天的冷却时间。 - -

- -```java -public int maxProfit(int[] prices) { - if (prices == null || prices.length == 0) { - return 0; - } - int N = prices.length; - int[] buy = new int[N]; - int[] s1 = new int[N]; - int[] sell = new int[N]; - int[] s2 = new int[N]; - s1[0] = buy[0] = -prices[0]; - sell[0] = s2[0] = 0; - for (int i = 1; i < N; i++) { - buy[i] = s2[i - 1] - prices[i]; - s1[i] = Math.max(buy[i - 1], s1[i - 1]); - sell[i] = Math.max(buy[i - 1], s1[i - 1]) + prices[i]; - s2[i] = Math.max(s2[i - 1], sell[i - 1]); - } - return Math.max(sell[N - 1], s2[N - 1]); -} -``` - -## 需要交易费用的股票交易 - -[714. Best Time to Buy and Sell Stock with Transaction Fee (Medium)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/) - -```html -Input: prices = [1, 3, 2, 8, 4, 9], fee = 2 -Output: 8 -Explanation: The maximum profit can be achieved by: -Buying at prices[0] = 1 -Selling at prices[3] = 8 -Buying at prices[4] = 4 -Selling at prices[5] = 9 -The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. -``` - -题目描述:每交易一次,都要支付一定的费用。 - -

- -```java -public int maxProfit(int[] prices, int fee) { - int N = prices.length; - int[] buy = new int[N]; - int[] s1 = new int[N]; - int[] sell = new int[N]; - int[] s2 = new int[N]; - s1[0] = buy[0] = -prices[0]; - sell[0] = s2[0] = 0; - for (int i = 1; i < N; i++) { - buy[i] = Math.max(sell[i - 1], s2[i - 1]) - prices[i]; - s1[i] = Math.max(buy[i - 1], s1[i - 1]); - sell[i] = Math.max(buy[i - 1], s1[i - 1]) - fee + prices[i]; - s2[i] = Math.max(s2[i - 1], sell[i - 1]); - } - return Math.max(sell[N - 1], s2[N - 1]); -} -``` - - -## 只能进行两次的股票交易 - -[123. Best Time to Buy and Sell Stock III (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/) - -```java -public int maxProfit(int[] prices) { - int firstBuy = Integer.MIN_VALUE, firstSell = 0; - int secondBuy = Integer.MIN_VALUE, secondSell = 0; - for (int curPrice : prices) { - if (firstBuy < -curPrice) { - firstBuy = -curPrice; - } - if (firstSell < firstBuy + curPrice) { - firstSell = firstBuy + curPrice; - } - if (secondBuy < firstSell - curPrice) { - secondBuy = firstSell - curPrice; - } - if (secondSell < secondBuy + curPrice) { - secondSell = secondBuy + curPrice; - } - } - return secondSell; -} -``` - -## 只能进行 k 次的股票交易 - -[188. Best Time to Buy and Sell Stock IV (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/) - -```java -public int maxProfit(int k, int[] prices) { - int n = prices.length; - if (k >= n / 2) { // 这种情况下该问题退化为普通的股票交易问题 - int maxProfit = 0; - for (int i = 1; i < n; i++) { - if (prices[i] > prices[i - 1]) { - maxProfit += prices[i] - prices[i - 1]; - } - } - return maxProfit; - } - int[][] maxProfit = new int[k + 1][n]; - for (int i = 1; i <= k; i++) { - int localMax = maxProfit[i - 1][0] - prices[0]; - for (int j = 1; j < n; j++) { - maxProfit[i][j] = Math.max(maxProfit[i][j - 1], prices[j] + localMax); - localMax = Math.max(localMax, maxProfit[i - 1][j] - prices[j]); - } - } - return maxProfit[k][n - 1]; -} -``` - -# 字符串编辑 - -## 删除两个字符串的字符使它们相等 - -[583. Delete Operation for Two Strings (Medium)](https://leetcode.com/problems/delete-operation-for-two-strings/description/) - -```html -Input: "sea", "eat" -Output: 2 -Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". -``` - -可以转换为求两个字符串的最长公共子序列问题。 - -```java -public int minDistance(String word1, String word2) { - int m = word1.length(), n = word2.length(); - int[][] dp = new int[m + 1][n + 1]; - for (int i = 1; i <= m; i++) { - for (int j = 1; j <= n; j++) { - if (word1.charAt(i - 1) == word2.charAt(j - 1)) { - dp[i][j] = dp[i - 1][j - 1] + 1; - } else { - dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); - } - } - } - return m + n - 2 * dp[m][n]; -} -``` - -## 编辑距离 - -[72. Edit Distance (Hard)](https://leetcode.com/problems/edit-distance/description/) - -```html -Example 1: - -Input: word1 = "horse", word2 = "ros" -Output: 3 -Explanation: -horse -> rorse (replace 'h' with 'r') -rorse -> rose (remove 'r') -rose -> ros (remove 'e') -Example 2: - -Input: word1 = "intention", word2 = "execution" -Output: 5 -Explanation: -intention -> inention (remove 't') -inention -> enention (replace 'i' with 'e') -enention -> exention (replace 'n' with 'x') -exention -> exection (replace 'n' with 'c') -exection -> execution (insert 'u') -``` - -题目描述:修改一个字符串成为另一个字符串,使得修改次数最少。一次修改操作包括:插入一个字符、删除一个字符、替换一个字符。 - -```java -public int minDistance(String word1, String word2) { - if (word1 == null || word2 == null) { - return 0; - } - int m = word1.length(), n = word2.length(); - int[][] dp = new int[m + 1][n + 1]; - for (int i = 1; i <= m; i++) { - dp[i][0] = i; - } - for (int i = 1; i <= n; i++) { - dp[0][i] = i; - } - for (int i = 1; i <= m; i++) { - for (int j = 1; j <= n; j++) { - if (word1.charAt(i - 1) == word2.charAt(j - 1)) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; - } - } - } - return dp[m][n]; -} -``` - -## 复制粘贴字符 - -[650. 2 Keys Keyboard (Medium)](https://leetcode.com/problems/2-keys-keyboard/description/) - -题目描述:最开始只有一个字符 A,问需要多少次操作能够得到 n 个字符 A,每次操作可以复制当前所有的字符,或者粘贴。 - -``` -Input: 3 -Output: 3 -Explanation: -Intitally, we have one character 'A'. -In step 1, we use Copy All operation. -In step 2, we use Paste operation to get 'AA'. -In step 3, we use Paste operation to get 'AAA'. -``` - -```java -public int minSteps(int n) { - if (n == 1) return 0; - for (int i = 2; i <= Math.sqrt(n); i++) { - if (n % i == 0) return i + minSteps(n / i); - } - return n; -} -``` - -```java -public int minSteps(int n) { - int[] dp = new int[n + 1]; - int h = (int) Math.sqrt(n); - for (int i = 2; i <= n; i++) { - dp[i] = i; - for (int j = 2; j <= h; j++) { - if (i % j == 0) { - dp[i] = dp[j] + dp[i / j]; - break; - } - } - } - return dp[n]; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" deleted file mode 100644 index 397c890c65..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" +++ /dev/null @@ -1,244 +0,0 @@ - -* [有序数组的 Two Sum](#有序数组的-two-sum) -* [两数平方和](#两数平方和) -* [反转字符串中的元音字符](#反转字符串中的元音字符) -* [回文字符串](#回文字符串) -* [归并两个有序数组](#归并两个有序数组) -* [判断链表是否存在环](#判断链表是否存在环) -* [最长子序列](#最长子序列) - - - -双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 - -# 有序数组的 Two Sum - -[Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) - -```html -Input: numbers={2, 7, 11, 15}, target=9 -Output: index1=1, index2=2 -``` - -题目描述:在有序数组中找出两个数,使它们的和为 target。 - -使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 - -- 如果两个指针指向元素的和 sum == target,那么得到要求的结果; -- 如果 sum > target,移动较大的元素,使 sum 变小一些; -- 如果 sum < target,移动较小的元素,使 sum 变大一些。 - -```java -public int[] twoSum(int[] numbers, int target) { - int i = 0, j = numbers.length - 1; - while (i < j) { - int sum = numbers[i] + numbers[j]; - if (sum == target) { - return new int[]{i + 1, j + 1}; - } else if (sum < target) { - i++; - } else { - j--; - } - } - return null; -} -``` - -# 两数平方和 - -[633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/) - -```html -Input: 5 -Output: True -Explanation: 1 * 1 + 2 * 2 = 5 -``` - -题目描述:判断一个数是否为两个数的平方和。 - -```java -public boolean judgeSquareSum(int c) { - int i = 0, j = (int) Math.sqrt(c); - while (i <= j) { - int powSum = i * i + j * j; - if (powSum == c) { - return true; - } else if (powSum > c) { - j--; - } else { - i++; - } - } - return false; -} -``` - -# 反转字符串中的元音字符 - -[345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) - -```html -Given s = "leetcode", return "leotcede". -``` - -使用双指针指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。 - -```java -private final static HashSet vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); - -public String reverseVowels(String s) { - int i = 0, j = s.length() - 1; - char[] result = new char[s.length()]; - while (i <= j) { - char ci = s.charAt(i); - char cj = s.charAt(j); - if (!vowels.contains(ci)) { - result[i++] = ci; - } else if (!vowels.contains(cj)) { - result[j--] = cj; - } else { - result[i++] = cj; - result[j--] = ci; - } - } - return new String(result); -} -``` - -# 回文字符串 - -[680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/) - -```html -Input: "abca" -Output: True -Explanation: You could delete the character 'c'. -``` - -题目描述:可以删除一个字符,判断是否能构成回文字符串。 - -```java -public boolean validPalindrome(String s) { - int i = -1, j = s.length(); - while (++i < --j) { - if (s.charAt(i) != s.charAt(j)) { - return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j); - } - } - return true; -} - -private boolean isPalindrome(String s, int i, int j) { - while (i < j) { - if (s.charAt(i++) != s.charAt(j--)) { - return false; - } - } - return true; -} -``` - -# 归并两个有序数组 - -[88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/) - -```html -Input: -nums1 = [1,2,3,0,0,0], m = 3 -nums2 = [2,5,6], n = 3 - -Output: [1,2,2,3,5,6] -``` - -题目描述:把归并结果存到第一个数组上。 - -需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。 - -```java -public void merge(int[] nums1, int m, int[] nums2, int n) { - int index1 = m - 1, index2 = n - 1; - int indexMerge = m + n - 1; - while (index1 >= 0 || index2 >= 0) { - if (index1 < 0) { - nums1[indexMerge--] = nums2[index2--]; - } else if (index2 < 0) { - nums1[indexMerge--] = nums1[index1--]; - } else if (nums1[index1] > nums2[index2]) { - nums1[indexMerge--] = nums1[index1--]; - } else { - nums1[indexMerge--] = nums2[index2--]; - } - } -} -``` - -# 判断链表是否存在环 - -[141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/) - -使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。 - -```java -public boolean hasCycle(ListNode head) { - if (head == null) { - return false; - } - ListNode l1 = head, l2 = head.next; - while (l1 != null && l2 != null && l2.next != null) { - if (l1 == l2) { - return true; - } - l1 = l1.next; - l2 = l2.next.next; - } - return false; -} -``` - -# 最长子序列 - -[524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) - -``` -Input: -s = "abpcplea", d = ["ale","apple","monkey","plea"] - -Output: -"apple" -``` - -题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。 - -```java -public String findLongestWord(String s, List d) { - String longestWord = ""; - for (String target : d) { - int l1 = longestWord.length(), l2 = target.length(); - if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) { - continue; - } - if (isValid(s, target)) { - longestWord = target; - } - } - return longestWord; -} - -private boolean isValid(String s, String target) { - int i = 0, j = 0; - while (i < s.length() && j < target.length()) { - if (s.charAt(i) == target.charAt(j)) { - j++; - } - i++; - } - return j == target.length(); -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" deleted file mode 100644 index ec4a34f8b0..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" +++ /dev/null @@ -1,129 +0,0 @@ - -* [1. 数组中两个数的和为给定值](#1-数组中两个数的和为给定值) -* [2. 判断数组是否含有重复元素](#2-判断数组是否含有重复元素) -* [3. 最长和谐序列](#3-最长和谐序列) -* [4. 最长连续序列](#4-最长连续序列) - - - -哈希表使用 O(N) 空间复杂度存储数据,并且以 O(1) 时间复杂度求解问题。 - -- Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。 - -- Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中 [Leetcdoe : 535. Encode and Decode TinyURL (Medium)](https://leetcode.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源。 - - -# 1. 数组中两个数的和为给定值 - -[1. Two Sum (Easy)](https://leetcode.com/problems/two-sum/description/) - -可以先对数组进行排序,然后使用双指针方法或者二分查找方法。这样做的时间复杂度为 O(NlogN),空间复杂度为 O(1)。 - -用 HashMap 存储数组元素和索引的映射,在访问到 nums[i] 时,判断 HashMap 中是否存在 target - nums[i],如果存在说明 target - nums[i] 所在的索引和 i 就是要找的两个数。该方法的时间复杂度为 O(N),空间复杂度为 O(N),使用空间来换取时间。 - -```java -public int[] twoSum(int[] nums, int target) { - HashMap indexForNum = new HashMap<>(); - for (int i = 0; i < nums.length; i++) { - if (indexForNum.containsKey(target - nums[i])) { - return new int[]{indexForNum.get(target - nums[i]), i}; - } else { - indexForNum.put(nums[i], i); - } - } - return null; -} -``` - -# 2. 判断数组是否含有重复元素 - -[217. Contains Duplicate (Easy)](https://leetcode.com/problems/contains-duplicate/description/) - -```java -public boolean containsDuplicate(int[] nums) { - Set set = new HashSet<>(); - for (int num : nums) { - set.add(num); - } - return set.size() < nums.length; -} -``` - -# 3. 最长和谐序列 - -[594. Longest Harmonious Subsequence (Easy)](https://leetcode.com/problems/longest-harmonious-subsequence/description/) - -```html -Input: [1,3,2,2,5,2,3,7] -Output: 5 -Explanation: The longest harmonious subsequence is [3,2,2,2,3]. -``` - -和谐序列中最大数和最小数之差正好为 1,应该注意的是序列的元素不一定是数组的连续元素。 - -```java -public int findLHS(int[] nums) { - Map countForNum = new HashMap<>(); - for (int num : nums) { - countForNum.put(num, countForNum.getOrDefault(num, 0) + 1); - } - int longest = 0; - for (int num : countForNum.keySet()) { - if (countForNum.containsKey(num + 1)) { - longest = Math.max(longest, countForNum.get(num + 1) + countForNum.get(num)); - } - } - return longest; -} -``` - -# 4. 最长连续序列 - -[128. Longest Consecutive Sequence (Hard)](https://leetcode.com/problems/longest-consecutive-sequence/description/) - -```html -Given [100, 4, 200, 1, 3, 2], -The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. -``` - -要求以 O(N) 的时间复杂度求解。 - -```java -public int longestConsecutive(int[] nums) { - Map countForNum = new HashMap<>(); - for (int num : nums) { - countForNum.put(num, 1); - } - for (int num : nums) { - forward(countForNum, num); - } - return maxCount(countForNum); -} - -private int forward(Map countForNum, int num) { - if (!countForNum.containsKey(num)) { - return 0; - } - int cnt = countForNum.get(num); - if (cnt > 1) { - return cnt; - } - cnt = forward(countForNum, num + 1) + 1; - countForNum.put(num, cnt); - return cnt; -} - -private int maxCount(Map countForNum) { - int max = 0; - for (int num : countForNum.keySet()) { - max = Math.max(max, countForNum.get(num)); - } - return max; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" deleted file mode 100644 index 28d7178ba9..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" +++ /dev/null @@ -1,231 +0,0 @@ - -* [字符串循环移位包含](#字符串循环移位包含) -* [字符串循环移位](#字符串循环移位) -* [字符串中单词的翻转](#字符串中单词的翻转) -* [两个字符串包含的字符是否完全相同](#两个字符串包含的字符是否完全相同) -* [计算一组字符集合可以组成的回文字符串的最大长度](#计算一组字符集合可以组成的回文字符串的最大长度) -* [字符串同构](#字符串同构) -* [回文子字符串个数](#回文子字符串个数) -* [判断一个整数是否是回文数](#判断一个整数是否是回文数) -* [统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数](#统计二进制字符串中连续-1-和连续-0-数量相同的子字符串个数) - - - -# 字符串循环移位包含 - -[编程之美 3.1](#) - -```html -s1 = AABCD, s2 = CDAA -Return : true -``` - -给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。 - -s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。 - -# 字符串循环移位 - -[编程之美 2.17](#) - -```html -s = "abcd123" k = 3 -Return "123abcd" -``` - -将字符串向右循环移动 k 位。 - -将 abcd123 中的 abcd 和 123 单独翻转,得到 dcba321,然后对整个字符串进行翻转,得到 123abcd。 - -# 字符串中单词的翻转 - -[程序员代码面试指南](#) - -```html -s = "I am a student" -Return "student a am I" -``` - -将每个单词翻转,然后将整个字符串翻转。 - -# 两个字符串包含的字符是否完全相同 - -[242. Valid Anagram (Easy)](https://leetcode.com/problems/valid-anagram/description/) - -```html -s = "anagram", t = "nagaram", return true. -s = "rat", t = "car", return false. -``` - -可以用 HashMap 来映射字符与出现次数,然后比较两个字符串出现的字符数量是否相同。 - -由于本题的字符串只包含 26 个小写字符,因此可以使用长度为 26 的整型数组对字符串出现的字符进行统计,不再使用 HashMap。 - -```java -public boolean isAnagram(String s, String t) { - int[] cnts = new int[26]; - for (char c : s.toCharArray()) { - cnts[c - 'a']++; - } - for (char c : t.toCharArray()) { - cnts[c - 'a']--; - } - for (int cnt : cnts) { - if (cnt != 0) { - return false; - } - } - return true; -} -``` - -# 计算一组字符集合可以组成的回文字符串的最大长度 - -[409. Longest Palindrome (Easy)](https://leetcode.com/problems/longest-palindrome/description/) - -```html -Input : "abccccdd" -Output : 7 -Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7. -``` - -使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。 - -因为回文字符串最中间的那个字符可以单独出现,所以如果有单独的字符就把它放到最中间。 - -```java -public int longestPalindrome(String s) { - int[] cnts = new int[256]; - for (char c : s.toCharArray()) { - cnts[c]++; - } - int palindrome = 0; - for (int cnt : cnts) { - palindrome += (cnt / 2) * 2; - } - if (palindrome < s.length()) { - palindrome++; // 这个条件下 s 中一定有单个未使用的字符存在,可以把这个字符放到回文的最中间 - } - return palindrome; -} -``` - -# 字符串同构 - -[205. Isomorphic Strings (Easy)](https://leetcode.com/problems/isomorphic-strings/description/) - -```html -Given "egg", "add", return true. -Given "foo", "bar", return false. -Given "paper", "title", return true. -``` - -记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。 - -```java -public boolean isIsomorphic(String s, String t) { - int[] preIndexOfS = new int[256]; - int[] preIndexOfT = new int[256]; - for (int i = 0; i < s.length(); i++) { - char sc = s.charAt(i), tc = t.charAt(i); - if (preIndexOfS[sc] != preIndexOfT[tc]) { - return false; - } - preIndexOfS[sc] = i + 1; - preIndexOfT[tc] = i + 1; - } - return true; -} -``` - -# 回文子字符串个数 - -[647. Palindromic Substrings (Medium)](https://leetcode.com/problems/palindromic-substrings/description/) - -```html -Input: "aaa" -Output: 6 -Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". -``` - -从字符串的某一位开始,尝试着去扩展子字符串。 - -```java -private int cnt = 0; - -public int countSubstrings(String s) { - for (int i = 0; i < s.length(); i++) { - extendSubstrings(s, i, i); // 奇数长度 - extendSubstrings(s, i, i + 1); // 偶数长度 - } - return cnt; -} - -private void extendSubstrings(String s, int start, int end) { - while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) { - start--; - end++; - cnt++; - } -} -``` - -# 判断一个整数是否是回文数 - -[9. Palindrome Number (Easy)](https://leetcode.com/problems/palindrome-number/description/) - -要求不能使用额外空间,也就不能将整数转换为字符串进行判断。 - -将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。 - -```java -public boolean isPalindrome(int x) { - if (x == 0) { - return true; - } - if (x < 0 || x % 10 == 0) { - return false; - } - int right = 0; - while (x > right) { - right = right * 10 + x % 10; - x /= 10; - } - return x == right || x == right / 10; -} -``` - -# 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数 - -[696. Count Binary Substrings (Easy)](https://leetcode.com/problems/count-binary-substrings/description/) - -```html -Input: "00110011" -Output: 6 -Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01". -``` - -```java -public int countBinarySubstrings(String s) { - int preLen = 0, curLen = 1, count = 0; - for (int i = 1; i < s.length(); i++) { - if (s.charAt(i) == s.charAt(i - 1)) { - curLen++; - } else { - preLen = curLen; - curLen = 1; - } - - if (preLen >= curLen) { - count++; - } - } - return count; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" deleted file mode 100644 index 5c88726b3c..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" +++ /dev/null @@ -1,232 +0,0 @@ - -* [快速选择](#快速选择) -* [堆排序](#堆排序) - * [Kth Element](#kth-element) -* [桶排序](#桶排序) - * [出现频率最多的 k 个数](#出现频率最多的-k-个数) - * [按照字符出现次数对字符串排序](#按照字符出现次数对字符串排序) -* [荷兰国旗问题](#荷兰国旗问题) - * [按颜色进行排序](#按颜色进行排序) - - - -# 快速选择 - -用于求解 **Kth Element** 问题,使用快速排序的 partition() 进行实现。 - -需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 - -# 堆排序 - -用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。 - -堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。 - -快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。 - -可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 - -## Kth Element - -[215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) - -题目描述:找到第 k 大的元素。 - -**排序** :时间复杂度 O(NlogN),空间复杂度 O(1) - -```java -public int findKthLargest(int[] nums, int k) { - Arrays.sort(nums); - return nums[nums.length - k]; -} -``` - -**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。 - -```java -public int findKthLargest(int[] nums, int k) { - PriorityQueue pq = new PriorityQueue<>(); // 小顶堆 - for (int val : nums) { - pq.add(val); - if (pq.size() > k) // 维护堆的大小为 K - pq.poll(); - } - return pq.peek(); -} -``` - -**快速选择** :时间复杂度 O(N),空间复杂度 O(1) - -```java -public int findKthLargest(int[] nums, int k) { - k = nums.length - k; - int l = 0, h = nums.length - 1; - while (l < h) { - int j = partition(nums, l, h); - if (j == k) { - break; - } else if (j < k) { - l = j + 1; - } else { - h = j - 1; - } - } - return nums[k]; -} - -private int partition(int[] a, int l, int h) { - int i = l, j = h + 1; - while (true) { - while (a[++i] < a[l] && i < h) ; - while (a[--j] > a[l] && j > l) ; - if (i >= j) { - break; - } - swap(a, i, j); - } - swap(a, l, j); - return j; -} - -private void swap(int[] a, int i, int j) { - int t = a[i]; - a[i] = a[j]; - a[j] = t; -} -``` - -# 桶排序 - -## 出现频率最多的 k 个数 - -[347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) - -```html -Given [1,1,1,2,2,3] and k = 2, return [1,2]. -``` - -设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 - -把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 - -```java -public List topKFrequent(int[] nums, int k) { - Map frequencyForNum = new HashMap<>(); - for (int num : nums) { - frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1); - } - List[] buckets = new ArrayList[nums.length + 1]; - for (int key : frequencyForNum.keySet()) { - int frequency = frequencyForNum.get(key); - if (buckets[frequency] == null) { - buckets[frequency] = new ArrayList<>(); - } - buckets[frequency].add(key); - } - List topK = new ArrayList<>(); - for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) { - if (buckets[i] == null) { - continue; - } - if (buckets[i].size() <= (k - topK.size())) { - topK.addAll(buckets[i]); - } else { - topK.addAll(buckets[i].subList(0, k - topK.size())); - } - } - return topK; -} -``` - -## 按照字符出现次数对字符串排序 - -[451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/) - -```html -Input: -"tree" - -Output: -"eert" - -Explanation: -'e' appears twice while 'r' and 't' both appear once. -So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer. -``` - -```java -public String frequencySort(String s) { - Map frequencyForNum = new HashMap<>(); - for (char c : s.toCharArray()) - frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1); - - List[] frequencyBucket = new ArrayList[s.length() + 1]; - for (char c : frequencyForNum.keySet()) { - int f = frequencyForNum.get(c); - if (frequencyBucket[f] == null) { - frequencyBucket[f] = new ArrayList<>(); - } - frequencyBucket[f].add(c); - } - StringBuilder str = new StringBuilder(); - for (int i = frequencyBucket.length - 1; i >= 0; i--) { - if (frequencyBucket[i] == null) { - continue; - } - for (char c : frequencyBucket[i]) { - for (int j = 0; j < i; j++) { - str.append(c); - } - } - } - return str.toString(); -} -``` - -# 荷兰国旗问题 - -荷兰国旗包含三种颜色:红、白、蓝。 - -有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。 - -它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 - -

- -## 按颜色进行排序 - -[75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/) - -```html -Input: [2,0,2,1,1,0] -Output: [0,0,1,1,2,2] -``` - -题目描述:只有 0/1/2 三种颜色。 - -```java -public void sortColors(int[] nums) { - int zero = -1, one = 0, two = nums.length; - while (one < two) { - if (nums[one] == 0) { - swap(nums, ++zero, one++); - } else if (nums[one] == 2) { - swap(nums, --two, one); - } else { - ++one; - } - } -} - -private void swap(int[] nums, int i, int j) { - int t = nums[i]; - nums[i] = nums[j]; - nums[j] = t; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" deleted file mode 100644 index 2bbde48822..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" +++ /dev/null @@ -1,1274 +0,0 @@ - -* [BFS](#bfs) - * [计算在网格中从原点到特定点的最短路径长度](#计算在网格中从原点到特定点的最短路径长度) - * [组成整数的最小平方数数量](#组成整数的最小平方数数量) - * [最短单词路径](#最短单词路径) -* [DFS](#dfs) - * [查找最大的连通面积](#查找最大的连通面积) - * [矩阵中的连通分量数目](#矩阵中的连通分量数目) - * [好友关系的连通分量数目](#好友关系的连通分量数目) - * [填充封闭区域](#填充封闭区域) - * [能到达的太平洋和大西洋的区域](#能到达的太平洋和大西洋的区域) -* [Backtracking](#backtracking) - * [数字键盘组合](#数字键盘组合) - * [IP 地址划分](#ip-地址划分) - * [在矩阵中寻找字符串](#在矩阵中寻找字符串) - * [输出二叉树中所有从根到叶子的路径](#输出二叉树中所有从根到叶子的路径) - * [排列](#排列) - * [含有相同元素求排列](#含有相同元素求排列) - * [组合](#组合) - * [组合求和](#组合求和) - * [含有相同元素的求组合求和](#含有相同元素的求组合求和) - * [1-9 数字的组合求和](#1-9-数字的组合求和) - * [子集](#子集) - * [含有相同元素求子集](#含有相同元素求子集) - * [分割字符串使得每个部分都是回文数](#分割字符串使得每个部分都是回文数) - * [数独](#数独) - * [N 皇后](#n-皇后) - - - -深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。 - -# BFS - -

- -广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。 - -第一层: - -- 0 -> {6,2,1,5} - -第二层: - -- 6 -> {4} -- 2 -> {} -- 1 -> {} -- 5 -> {3} - -第三层: - -- 4 -> {} -- 3 -> {} - -每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径。 - -在程序实现 BFS 时需要考虑以下问题: - -- 队列:用来存储每一轮遍历得到的节点; -- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。 - -## 计算在网格中从原点到特定点的最短路径长度 - -```html -[[1,1,0,1], - [1,0,1,0], - [1,1,1,1], - [1,0,1,1]] -``` - -1 表示可以经过某个位置,求解从 (0, 0) 位置到 (tr, tc) 位置的最短路径长度。 - -```java -public int minPathLength(int[][] grids, int tr, int tc) { - final int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; - final int m = grids.length, n = grids[0].length; - Queue> queue = new LinkedList<>(); - queue.add(new Pair<>(0, 0)); - int pathLength = 0; - while (!queue.isEmpty()) { - int size = queue.size(); - pathLength++; - while (size-- > 0) { - Pair cur = queue.poll(); - int cr = cur.getKey(), cc = cur.getValue(); - grids[cr][cc] = 0; // 标记 - for (int[] d : direction) { - int nr = cr + d[0], nc = cc + d[1]; - if (nr < 0 || nr >= m || nc < 0 || nc >= n || grids[nr][nc] == 0) { - continue; - } - if (nr == tr && nc == tc) { - return pathLength; - } - queue.add(new Pair<>(nr, nc)); - } - } - } - return -1; -} -``` - -## 组成整数的最小平方数数量 - -[279. Perfect Squares (Medium)](https://leetcode.com/problems/perfect-squares/description/) - -```html -For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. -``` - -可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。 - -要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。 - -本题也可以用动态规划求解,在之后动态规划部分中会再次出现。 - -```java -public int numSquares(int n) { - List squares = generateSquares(n); - Queue queue = new LinkedList<>(); - boolean[] marked = new boolean[n + 1]; - queue.add(n); - marked[n] = true; - int level = 0; - while (!queue.isEmpty()) { - int size = queue.size(); - level++; - while (size-- > 0) { - int cur = queue.poll(); - for (int s : squares) { - int next = cur - s; - if (next < 0) { - break; - } - if (next == 0) { - return level; - } - if (marked[next]) { - continue; - } - marked[next] = true; - queue.add(next); - } - } - } - return n; -} - -/** - * 生成小于 n 的平方数序列 - * @return 1,4,9,... - */ -private List generateSquares(int n) { - List squares = new ArrayList<>(); - int square = 1; - int diff = 3; - while (square <= n) { - squares.add(square); - square += diff; - diff += 2; - } - return squares; -} -``` - -## 最短单词路径 - -[127. Word Ladder (Medium)](https://leetcode.com/problems/word-ladder/description/) - -```html -Input: -beginWord = "hit", -endWord = "cog", -wordList = ["hot","dot","dog","lot","log","cog"] - -Output: 5 - -Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", -return its length 5. -``` - -```html -Input: -beginWord = "hit" -endWord = "cog" -wordList = ["hot","dot","dog","lot","log"] - -Output: 0 - -Explanation: The endWord "cog" is not in wordList, therefore no possible transformation. -``` - -题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。 - -```java -public int ladderLength(String beginWord, String endWord, List wordList) { - wordList.add(beginWord); - int N = wordList.size(); - int start = N - 1; - int end = 0; - while (end < N && !wordList.get(end).equals(endWord)) { - end++; - } - if (end == N) { - return 0; - } - List[] graphic = buildGraphic(wordList); - return getShortestPath(graphic, start, end); -} - -private List[] buildGraphic(List wordList) { - int N = wordList.size(); - List[] graphic = new List[N]; - for (int i = 0; i < N; i++) { - graphic[i] = new ArrayList<>(); - for (int j = 0; j < N; j++) { - if (isConnect(wordList.get(i), wordList.get(j))) { - graphic[i].add(j); - } - } - } - return graphic; -} - -private boolean isConnect(String s1, String s2) { - int diffCnt = 0; - for (int i = 0; i < s1.length() && diffCnt <= 1; i++) { - if (s1.charAt(i) != s2.charAt(i)) { - diffCnt++; - } - } - return diffCnt == 1; -} - -private int getShortestPath(List[] graphic, int start, int end) { - Queue queue = new LinkedList<>(); - boolean[] marked = new boolean[graphic.length]; - queue.add(start); - marked[start] = true; - int path = 1; - while (!queue.isEmpty()) { - int size = queue.size(); - path++; - while (size-- > 0) { - int cur = queue.poll(); - for (int next : graphic[cur]) { - if (next == end) { - return path; - } - if (marked[next]) { - continue; - } - marked[next] = true; - queue.add(next); - } - } - } - return 0; -} -``` - -# DFS - -

- -广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。 - -而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 - -从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 - -在程序实现 DFS 时需要考虑以下问题: - -- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。 -- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。 - -## 查找最大的连通面积 - -[695. Max Area of Island (Easy)](https://leetcode.com/problems/max-area-of-island/description/) - -```html -[[0,0,1,0,0,0,0,1,0,0,0,0,0], - [0,0,0,0,0,0,0,1,1,1,0,0,0], - [0,1,1,0,1,0,0,0,0,0,0,0,0], - [0,1,0,0,1,1,0,0,1,0,1,0,0], - [0,1,0,0,1,1,0,0,1,1,1,0,0], - [0,0,0,0,0,0,0,0,0,0,1,0,0], - [0,0,0,0,0,0,0,1,1,1,0,0,0], - [0,0,0,0,0,0,0,1,1,0,0,0,0]] -``` - -```java -private int m, n; -private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; - -public int maxAreaOfIsland(int[][] grid) { - if (grid == null || grid.length == 0) { - return 0; - } - m = grid.length; - n = grid[0].length; - int maxArea = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - maxArea = Math.max(maxArea, dfs(grid, i, j)); - } - } - return maxArea; -} - -private int dfs(int[][] grid, int r, int c) { - if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) { - return 0; - } - grid[r][c] = 0; - int area = 1; - for (int[] d : direction) { - area += dfs(grid, r + d[0], c + d[1]); - } - return area; -} -``` - -## 矩阵中的连通分量数目 - -[200. Number of Islands (Medium)](https://leetcode.com/problems/number-of-islands/description/) - -```html -Input: -11000 -11000 -00100 -00011 - -Output: 3 -``` - -可以将矩阵表示看成一张有向图。 - -```java -private int m, n; -private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; - -public int numIslands(char[][] grid) { - if (grid == null || grid.length == 0) { - return 0; - } - m = grid.length; - n = grid[0].length; - int islandsNum = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (grid[i][j] != '0') { - dfs(grid, i, j); - islandsNum++; - } - } - } - return islandsNum; -} - -private void dfs(char[][] grid, int i, int j) { - if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') { - return; - } - grid[i][j] = '0'; - for (int[] d : direction) { - dfs(grid, i + d[0], j + d[1]); - } -} -``` - -## 好友关系的连通分量数目 - -[547. Friend Circles (Medium)](https://leetcode.com/problems/friend-circles/description/) - -```html -Input: -[[1,1,0], - [1,1,0], - [0,0,1]] - -Output: 2 - -Explanation:The 0th and 1st students are direct friends, so they are in a friend circle. -The 2nd student himself is in a friend circle. So return 2. -``` - -题目描述:好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。 - -```java -private int n; - -public int findCircleNum(int[][] M) { - n = M.length; - int circleNum = 0; - boolean[] hasVisited = new boolean[n]; - for (int i = 0; i < n; i++) { - if (!hasVisited[i]) { - dfs(M, i, hasVisited); - circleNum++; - } - } - return circleNum; -} - -private void dfs(int[][] M, int i, boolean[] hasVisited) { - hasVisited[i] = true; - for (int k = 0; k < n; k++) { - if (M[i][k] == 1 && !hasVisited[k]) { - dfs(M, k, hasVisited); - } - } -} -``` - -## 填充封闭区域 - -[130. Surrounded Regions (Medium)](https://leetcode.com/problems/surrounded-regions/description/) - -```html -For example, -X X X X -X O O X -X X O X -X O X X - -After running your function, the board should be: -X X X X -X X X X -X X X X -X O X X -``` - -题目描述:使被 'X' 包围的 'O' 转换为 'X'。 - -先填充最外侧,剩下的就是里侧了。 - -```java -private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; -private int m, n; - -public void solve(char[][] board) { - if (board == null || board.length == 0) { - return; - } - - m = board.length; - n = board[0].length; - - for (int i = 0; i < m; i++) { - dfs(board, i, 0); - dfs(board, i, n - 1); - } - for (int i = 0; i < n; i++) { - dfs(board, 0, i); - dfs(board, m - 1, i); - } - - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (board[i][j] == 'T') { - board[i][j] = 'O'; - } else if (board[i][j] == 'O') { - board[i][j] = 'X'; - } - } - } -} - -private void dfs(char[][] board, int r, int c) { - if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') { - return; - } - board[r][c] = 'T'; - for (int[] d : direction) { - dfs(board, r + d[0], c + d[1]); - } -} -``` - -## 能到达的太平洋和大西洋的区域 - -[417. Pacific Atlantic Water Flow (Medium)](https://leetcode.com/problems/pacific-atlantic-water-flow/description/) - -```html -Given the following 5x5 matrix: - - Pacific ~ ~ ~ ~ ~ - ~ 1 2 2 3 (5) * - ~ 3 2 3 (4) (4) * - ~ 2 4 (5) 3 1 * - ~ (6) (7) 1 4 5 * - ~ (5) 1 1 2 4 * - * * * * * Atlantic - -Return: -[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix). -``` - -左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。 - -```java - -private int m, n; -private int[][] matrix; -private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; - -public List pacificAtlantic(int[][] matrix) { - List ret = new ArrayList<>(); - if (matrix == null || matrix.length == 0) { - return ret; - } - - m = matrix.length; - n = matrix[0].length; - this.matrix = matrix; - boolean[][] canReachP = new boolean[m][n]; - boolean[][] canReachA = new boolean[m][n]; - - for (int i = 0; i < m; i++) { - dfs(i, 0, canReachP); - dfs(i, n - 1, canReachA); - } - for (int i = 0; i < n; i++) { - dfs(0, i, canReachP); - dfs(m - 1, i, canReachA); - } - - for (int i = 0; i < m; i++) { - for (int j = 0; j < n; j++) { - if (canReachP[i][j] && canReachA[i][j]) { - ret.add(new int[]{i, j}); - } - } - } - - return ret; -} - -private void dfs(int r, int c, boolean[][] canReach) { - if (canReach[r][c]) { - return; - } - canReach[r][c] = true; - for (int[] d : direction) { - int nextR = d[0] + r; - int nextC = d[1] + c; - if (nextR < 0 || nextR >= m || nextC < 0 || nextC >= n - || matrix[r][c] > matrix[nextR][nextC]) { - - continue; - } - dfs(nextR, nextC, canReach); - } -} -``` - -# Backtracking - -Backtracking(回溯)属于 DFS。 - -- 普通 DFS 主要用在 **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。 -- 而 Backtracking 主要用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。 - -因为 Backtracking 不是立即就返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题: - -- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素; -- 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。 - -## 数字键盘组合 - -[17. Letter Combinations of a Phone Number (Medium)](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/) - -

- -```html -Input:Digit string "23" -Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. -``` - -```java -private static final String[] KEYS = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; - -public List letterCombinations(String digits) { - List combinations = new ArrayList<>(); - if (digits == null || digits.length() == 0) { - return combinations; - } - doCombination(new StringBuilder(), combinations, digits); - return combinations; -} - -private void doCombination(StringBuilder prefix, List combinations, final String digits) { - if (prefix.length() == digits.length()) { - combinations.add(prefix.toString()); - return; - } - int curDigits = digits.charAt(prefix.length()) - '0'; - String letters = KEYS[curDigits]; - for (char c : letters.toCharArray()) { - prefix.append(c); // 添加 - doCombination(prefix, combinations, digits); - prefix.deleteCharAt(prefix.length() - 1); // 删除 - } -} -``` - -## IP 地址划分 - -[93. Restore IP Addresses(Medium)](https://leetcode.com/problems/restore-ip-addresses/description/) - -```html -Given "25525511135", -return ["255.255.11.135", "255.255.111.35"]. -``` - -```java -public List restoreIpAddresses(String s) { - List addresses = new ArrayList<>(); - StringBuilder tempAddress = new StringBuilder(); - doRestore(0, tempAddress, addresses, s); - return addresses; -} - -private void doRestore(int k, StringBuilder tempAddress, List addresses, String s) { - if (k == 4 || s.length() == 0) { - if (k == 4 && s.length() == 0) { - addresses.add(tempAddress.toString()); - } - return; - } - for (int i = 0; i < s.length() && i <= 2; i++) { - if (i != 0 && s.charAt(0) == '0') { - break; - } - String part = s.substring(0, i + 1); - if (Integer.valueOf(part) <= 255) { - if (tempAddress.length() != 0) { - part = "." + part; - } - tempAddress.append(part); - doRestore(k + 1, tempAddress, addresses, s.substring(i + 1)); - tempAddress.delete(tempAddress.length() - part.length(), tempAddress.length()); - } - } -} -``` - -## 在矩阵中寻找字符串 - -[79. Word Search (Medium)](https://leetcode.com/problems/word-search/description/) - -```html -For example, -Given board = -[ - ['A','B','C','E'], - ['S','F','C','S'], - ['A','D','E','E'] -] -word = "ABCCED", -> returns true, -word = "SEE", -> returns true, -word = "ABCB", -> returns false. -``` - -```java -private final static int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; -private int m; -private int n; - -public boolean exist(char[][] board, String word) { - if (word == null || word.length() == 0) { - return true; - } - if (board == null || board.length == 0 || board[0].length == 0) { - return false; - } - - m = board.length; - n = board[0].length; - boolean[][] hasVisited = new boolean[m][n]; - - for (int r = 0; r < m; r++) { - for (int c = 0; c < n; c++) { - if (backtracking(0, r, c, hasVisited, board, word)) { - return true; - } - } - } - - return false; -} - -private boolean backtracking(int curLen, int r, int c, boolean[][] visited, final char[][] board, final String word) { - if (curLen == word.length()) { - return true; - } - if (r < 0 || r >= m || c < 0 || c >= n - || board[r][c] != word.charAt(curLen) || visited[r][c]) { - - return false; - } - - visited[r][c] = true; - - for (int[] d : direction) { - if (backtracking(curLen + 1, r + d[0], c + d[1], visited, board, word)) { - return true; - } - } - - visited[r][c] = false; - - return false; -} -``` - -## 输出二叉树中所有从根到叶子的路径 - -[257. Binary Tree Paths (Easy)](https://leetcode.com/problems/binary-tree-paths/description/) - -```html - 1 - / \ -2 3 - \ - 5 -``` - -```html -["1->2->5", "1->3"] -``` - -```java - -public List binaryTreePaths(TreeNode root) { - List paths = new ArrayList<>(); - if (root == null) { - return paths; - } - List values = new ArrayList<>(); - backtracking(root, values, paths); - return paths; -} - -private void backtracking(TreeNode node, List values, List paths) { - if (node == null) { - return; - } - values.add(node.val); - if (isLeaf(node)) { - paths.add(buildPath(values)); - } else { - backtracking(node.left, values, paths); - backtracking(node.right, values, paths); - } - values.remove(values.size() - 1); -} - -private boolean isLeaf(TreeNode node) { - return node.left == null && node.right == null; -} - -private String buildPath(List values) { - StringBuilder str = new StringBuilder(); - for (int i = 0; i < values.size(); i++) { - str.append(values.get(i)); - if (i != values.size() - 1) { - str.append("->"); - } - } - return str.toString(); -} -``` - -## 排列 - -[46. Permutations (Medium)](https://leetcode.com/problems/permutations/description/) - -```html -[1,2,3] have the following permutations: -[ - [1,2,3], - [1,3,2], - [2,1,3], - [2,3,1], - [3,1,2], - [3,2,1] -] -``` - -```java -public List> permute(int[] nums) { - List> permutes = new ArrayList<>(); - List permuteList = new ArrayList<>(); - boolean[] hasVisited = new boolean[nums.length]; - backtracking(permuteList, permutes, hasVisited, nums); - return permutes; -} - -private void backtracking(List permuteList, List> permutes, boolean[] visited, final int[] nums) { - if (permuteList.size() == nums.length) { - permutes.add(new ArrayList<>(permuteList)); // 重新构造一个 List - return; - } - for (int i = 0; i < visited.length; i++) { - if (visited[i]) { - continue; - } - visited[i] = true; - permuteList.add(nums[i]); - backtracking(permuteList, permutes, visited, nums); - permuteList.remove(permuteList.size() - 1); - visited[i] = false; - } -} -``` - -## 含有相同元素求排列 - -[47. Permutations II (Medium)](https://leetcode.com/problems/permutations-ii/description/) - -```html -[1,1,2] have the following unique permutations: -[[1,1,2], [1,2,1], [2,1,1]] -``` - -数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。 - -在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。 - -```java -public List> permuteUnique(int[] nums) { - List> permutes = new ArrayList<>(); - List permuteList = new ArrayList<>(); - Arrays.sort(nums); // 排序 - boolean[] hasVisited = new boolean[nums.length]; - backtracking(permuteList, permutes, hasVisited, nums); - return permutes; -} - -private void backtracking(List permuteList, List> permutes, boolean[] visited, final int[] nums) { - if (permuteList.size() == nums.length) { - permutes.add(new ArrayList<>(permuteList)); - return; - } - - for (int i = 0; i < visited.length; i++) { - if (i != 0 && nums[i] == nums[i - 1] && !visited[i - 1]) { - continue; // 防止重复 - } - if (visited[i]){ - continue; - } - visited[i] = true; - permuteList.add(nums[i]); - backtracking(permuteList, permutes, visited, nums); - permuteList.remove(permuteList.size() - 1); - visited[i] = false; - } -} -``` - -## 组合 - -[77. Combinations (Medium)](https://leetcode.com/problems/combinations/description/) - -```html -If n = 4 and k = 2, a solution is: -[ - [2,4], - [3,4], - [2,3], - [1,2], - [1,3], - [1,4], -] -``` - -```java -public List> combine(int n, int k) { - List> combinations = new ArrayList<>(); - List combineList = new ArrayList<>(); - backtracking(combineList, combinations, 1, k, n); - return combinations; -} - -private void backtracking(List combineList, List> combinations, int start, int k, final int n) { - if (k == 0) { - combinations.add(new ArrayList<>(combineList)); - return; - } - for (int i = start; i <= n - k + 1; i++) { // 剪枝 - combineList.add(i); - backtracking(combineList, combinations, i + 1, k - 1, n); - combineList.remove(combineList.size() - 1); - } -} -``` - -## 组合求和 - -[39. Combination Sum (Medium)](https://leetcode.com/problems/combination-sum/description/) - -```html -given candidate set [2, 3, 6, 7] and target 7, -A solution set is: -[[7],[2, 2, 3]] -``` - -```java -public List> combinationSum(int[] candidates, int target) { - List> combinations = new ArrayList<>(); - backtracking(new ArrayList<>(), combinations, 0, target, candidates); - return combinations; -} - -private void backtracking(List tempCombination, List> combinations, - int start, int target, final int[] candidates) { - - if (target == 0) { - combinations.add(new ArrayList<>(tempCombination)); - return; - } - for (int i = start; i < candidates.length; i++) { - if (candidates[i] <= target) { - tempCombination.add(candidates[i]); - backtracking(tempCombination, combinations, i, target - candidates[i], candidates); - tempCombination.remove(tempCombination.size() - 1); - } - } -} -``` - -## 含有相同元素的求组合求和 - -[40. Combination Sum II (Medium)](https://leetcode.com/problems/combination-sum-ii/description/) - -```html -For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8, -A solution set is: -[ - [1, 7], - [1, 2, 5], - [2, 6], - [1, 1, 6] -] -``` - -```java -public List> combinationSum2(int[] candidates, int target) { - List> combinations = new ArrayList<>(); - Arrays.sort(candidates); - backtracking(new ArrayList<>(), combinations, new boolean[candidates.length], 0, target, candidates); - return combinations; -} - -private void backtracking(List tempCombination, List> combinations, - boolean[] hasVisited, int start, int target, final int[] candidates) { - - if (target == 0) { - combinations.add(new ArrayList<>(tempCombination)); - return; - } - for (int i = start; i < candidates.length; i++) { - if (i != 0 && candidates[i] == candidates[i - 1] && !hasVisited[i - 1]) { - continue; - } - if (candidates[i] <= target) { - tempCombination.add(candidates[i]); - hasVisited[i] = true; - backtracking(tempCombination, combinations, hasVisited, i + 1, target - candidates[i], candidates); - hasVisited[i] = false; - tempCombination.remove(tempCombination.size() - 1); - } - } -} -``` - -## 1-9 数字的组合求和 - -[216. Combination Sum III (Medium)](https://leetcode.com/problems/combination-sum-iii/description/) - -```html -Input: k = 3, n = 9 - -Output: - -[[1,2,6], [1,3,5], [2,3,4]] -``` - -从 1-9 数字中选出 k 个数不重复的数,使得它们的和为 n。 - -```java -public List> combinationSum3(int k, int n) { - List> combinations = new ArrayList<>(); - List path = new ArrayList<>(); - backtracking(k, n, 1, path, combinations); - return combinations; -} - -private void backtracking(int k, int n, int start, - List tempCombination, List> combinations) { - - if (k == 0 && n == 0) { - combinations.add(new ArrayList<>(tempCombination)); - return; - } - if (k == 0 || n == 0) { - return; - } - for (int i = start; i <= 9; i++) { - tempCombination.add(i); - backtracking(k - 1, n - i, i + 1, tempCombination, combinations); - tempCombination.remove(tempCombination.size() - 1); - } -} -``` - -## 子集 - -[78. Subsets (Medium)](https://leetcode.com/problems/subsets/description/) - -找出集合的所有子集,子集不能重复,[1, 2] 和 [2, 1] 这种子集算重复 - -```java -public List> subsets(int[] nums) { - List> subsets = new ArrayList<>(); - List tempSubset = new ArrayList<>(); - for (int size = 0; size <= nums.length; size++) { - backtracking(0, tempSubset, subsets, size, nums); // 不同的子集大小 - } - return subsets; -} - -private void backtracking(int start, List tempSubset, List> subsets, - final int size, final int[] nums) { - - if (tempSubset.size() == size) { - subsets.add(new ArrayList<>(tempSubset)); - return; - } - for (int i = start; i < nums.length; i++) { - tempSubset.add(nums[i]); - backtracking(i + 1, tempSubset, subsets, size, nums); - tempSubset.remove(tempSubset.size() - 1); - } -} -``` - -## 含有相同元素求子集 - -[90. Subsets II (Medium)](https://leetcode.com/problems/subsets-ii/description/) - -```html -For example, -If nums = [1,2,2], a solution is: - -[ - [2], - [1], - [1,2,2], - [2,2], - [1,2], - [] -] -``` - -```java -public List> subsetsWithDup(int[] nums) { - Arrays.sort(nums); - List> subsets = new ArrayList<>(); - List tempSubset = new ArrayList<>(); - boolean[] hasVisited = new boolean[nums.length]; - for (int size = 0; size <= nums.length; size++) { - backtracking(0, tempSubset, subsets, hasVisited, size, nums); // 不同的子集大小 - } - return subsets; -} - -private void backtracking(int start, List tempSubset, List> subsets, boolean[] hasVisited, - final int size, final int[] nums) { - - if (tempSubset.size() == size) { - subsets.add(new ArrayList<>(tempSubset)); - return; - } - for (int i = start; i < nums.length; i++) { - if (i != 0 && nums[i] == nums[i - 1] && !hasVisited[i - 1]) { - continue; - } - tempSubset.add(nums[i]); - hasVisited[i] = true; - backtracking(i + 1, tempSubset, subsets, hasVisited, size, nums); - hasVisited[i] = false; - tempSubset.remove(tempSubset.size() - 1); - } -} -``` - -## 分割字符串使得每个部分都是回文数 - -[131. Palindrome Partitioning (Medium)](https://leetcode.com/problems/palindrome-partitioning/description/) - -```html -For example, given s = "aab", -Return - -[ - ["aa","b"], - ["a","a","b"] -] -``` - -```java -public List> partition(String s) { - List> partitions = new ArrayList<>(); - List tempPartition = new ArrayList<>(); - doPartition(s, partitions, tempPartition); - return partitions; -} - -private void doPartition(String s, List> partitions, List tempPartition) { - if (s.length() == 0) { - partitions.add(new ArrayList<>(tempPartition)); - return; - } - for (int i = 0; i < s.length(); i++) { - if (isPalindrome(s, 0, i)) { - tempPartition.add(s.substring(0, i + 1)); - doPartition(s.substring(i + 1), partitions, tempPartition); - tempPartition.remove(tempPartition.size() - 1); - } - } -} - -private boolean isPalindrome(String s, int begin, int end) { - while (begin < end) { - if (s.charAt(begin++) != s.charAt(end--)) { - return false; - } - } - return true; -} -``` - -## 数独 - -[37. Sudoku Solver (Hard)](https://leetcode.com/problems/sudoku-solver/description/) - -

- -```java -private boolean[][] rowsUsed = new boolean[9][10]; -private boolean[][] colsUsed = new boolean[9][10]; -private boolean[][] cubesUsed = new boolean[9][10]; -private char[][] board; - -public void solveSudoku(char[][] board) { - this.board = board; - for (int i = 0; i < 9; i++) - for (int j = 0; j < 9; j++) { - if (board[i][j] == '.') { - continue; - } - int num = board[i][j] - '0'; - rowsUsed[i][num] = true; - colsUsed[j][num] = true; - cubesUsed[cubeNum(i, j)][num] = true; - } - backtracking(0, 0); -} - -private boolean backtracking(int row, int col) { - while (row < 9 && board[row][col] != '.') { - row = col == 8 ? row + 1 : row; - col = col == 8 ? 0 : col + 1; - } - if (row == 9) { - return true; - } - for (int num = 1; num <= 9; num++) { - if (rowsUsed[row][num] || colsUsed[col][num] || cubesUsed[cubeNum(row, col)][num]) { - continue; - } - rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = true; - board[row][col] = (char) (num + '0'); - if (backtracking(row, col)) { - return true; - } - board[row][col] = '.'; - rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = false; - } - return false; -} - -private int cubeNum(int i, int j) { - int r = i / 3; - int c = j / 3; - return r * 3 + c; -} -``` - -## N 皇后 - -[51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/) - -

- -在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,求所有的 n 皇后的解。 - -一行一行地摆放,在确定一行中的那个皇后应该摆在哪一列时,需要用三个标记数组来确定某一列是否合法,这三个标记数组分别为:列标记数组、45 度对角线标记数组和 135 度对角线标记数组。 - -45 度对角线标记数组的长度为 2 \* n - 1,通过下图可以明确 (r, c) 的位置所在的数组下标为 r + c。 - -

- -135 度对角线标记数组的长度也是 2 \* n - 1,(r, c) 的位置所在的数组下标为 n - 1 - (r - c)。 - -

- -```java -private List> solutions; -private char[][] nQueens; -private boolean[] colUsed; -private boolean[] diagonals45Used; -private boolean[] diagonals135Used; -private int n; - -public List> solveNQueens(int n) { - solutions = new ArrayList<>(); - nQueens = new char[n][n]; - for (int i = 0; i < n; i++) { - Arrays.fill(nQueens[i], '.'); - } - colUsed = new boolean[n]; - diagonals45Used = new boolean[2 * n - 1]; - diagonals135Used = new boolean[2 * n - 1]; - this.n = n; - backtracking(0); - return solutions; -} - -private void backtracking(int row) { - if (row == n) { - List list = new ArrayList<>(); - for (char[] chars : nQueens) { - list.add(new String(chars)); - } - solutions.add(list); - return; - } - - for (int col = 0; col < n; col++) { - int diagonals45Idx = row + col; - int diagonals135Idx = n - 1 - (row - col); - if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) { - continue; - } - nQueens[row][col] = 'Q'; - colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true; - backtracking(row + 1); - colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false; - nQueens[row][col] = '.'; - } -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" deleted file mode 100644 index c97d016df5..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" +++ /dev/null @@ -1,515 +0,0 @@ - -* [素数分解](#素数分解) -* [整除](#整除) - * [最大公约数最小公倍数](#最大公约数最小公倍数) - * [生成素数序列](#生成素数序列) - * [最大公约数](#最大公约数) - * [使用位操作和减法求解最大公约数](#使用位操作和减法求解最大公约数) -* [进制转换](#进制转换) - * [7 进制](#7-进制) - * [16 进制](#16-进制) - * [26 进制](#26-进制) -* [阶乘](#阶乘) - * [统计阶乘尾部有多少个 0](#统计阶乘尾部有多少个-0) -* [字符串加法减法](#字符串加法减法) - * [二进制加法](#二进制加法) - * [字符串加法](#字符串加法) -* [相遇问题](#相遇问题) - * [改变数组元素使所有的数组元素都相等](#改变数组元素使所有的数组元素都相等) - * [解法 1](#解法-1) - * [解法 2](#解法-2) -* [多数投票问题](#多数投票问题) - * [数组中出现次数多于 n / 2 的元素](#数组中出现次数多于-n--2-的元素) -* [其它](#其它) - * [平方数](#平方数) - * [3 的 n 次方](#3-的-n-次方) - * [乘积数组](#乘积数组) - * [找出数组中的乘积最大的三个数](#找出数组中的乘积最大的三个数) - - - -# 素数分解 - -每一个数都可以分解成素数的乘积,例如 84 = 22 \* 31 \* 50 \* 71 \* 110 \* 130 \* 170 \* … - -# 整除 - -令 x = 2m0 \* 3m1 \* 5m2 \* 7m3 \* 11m4 \* … - -令 y = 2n0 \* 3n1 \* 5n2 \* 7n3 \* 11n4 \* … - -如果 x 整除 y(y mod x == 0),则对于所有 i,mi <= ni。 - -## 最大公约数最小公倍数 - -x 和 y 的最大公约数为:gcd(x,y) = 2min(m0,n0) \* 3min(m1,n1) \* 5min(m2,n2) \* ... - -x 和 y 的最小公倍数为:lcm(x,y) = 2max(m0,n0) \* 3max(m1,n1) \* 5max(m2,n2) \* ... - -## 生成素数序列 - -[204. Count Primes (Easy)](https://leetcode.com/problems/count-primes/description/) - -埃拉托斯特尼筛法在每次找到一个素数时,将能被素数整除的数排除掉。 - -```java -public int countPrimes(int n) { - boolean[] notPrimes = new boolean[n + 1]; - int count = 0; - for (int i = 2; i < n; i++) { - if (notPrimes[i]) { - continue; - } - count++; - // 从 i * i 开始,因为如果 k < i,那么 k * i 在之前就已经被去除过了 - for (long j = (long) (i) * i; j < n; j += i) { - notPrimes[(int) j] = true; - } - } - return count; -} -``` - -### 最大公约数 - -```java -int gcd(int a, int b) { - return b == 0 ? a : gcd(b, a % b); -} -``` - -最小公倍数为两数的乘积除以最大公约数。 - -```java -int lcm(int a, int b) { - return a * b / gcd(a, b); -} -``` - -## 使用位操作和减法求解最大公约数 - -[编程之美:2.7](#) - -对于 a 和 b 的最大公约数 f(a, b),有: - -- 如果 a 和 b 均为偶数,f(a, b) = 2\*f(a/2, b/2); -- 如果 a 是偶数 b 是奇数,f(a, b) = f(a/2, b); -- 如果 b 是偶数 a 是奇数,f(a, b) = f(a, b/2); -- 如果 a 和 b 均为奇数,f(a, b) = f(b, a-b); - -乘 2 和除 2 都可以转换为移位操作。 - -```java -public int gcd(int a, int b) { - if (a < b) { - return gcd(b, a); - } - if (b == 0) { - return a; - } - boolean isAEven = isEven(a), isBEven = isEven(b); - if (isAEven && isBEven) { - return 2 * gcd(a >> 1, b >> 1); - } else if (isAEven && !isBEven) { - return gcd(a >> 1, b); - } else if (!isAEven && isBEven) { - return gcd(a, b >> 1); - } else { - return gcd(b, a - b); - } -} -``` - -# 进制转换 - -## 7 进制 - -[504. Base 7 (Easy)](https://leetcode.com/problems/base-7/description/) - -```java -public String convertToBase7(int num) { - if (num == 0) { - return "0"; - } - StringBuilder sb = new StringBuilder(); - boolean isNegative = num < 0; - if (isNegative) { - num = -num; - } - while (num > 0) { - sb.append(num % 7); - num /= 7; - } - String ret = sb.reverse().toString(); - return isNegative ? "-" + ret : ret; -} -``` - -Java 中 static String toString(int num, int radix) 可以将一个整数转换为 radix 进制表示的字符串。 - -```java -public String convertToBase7(int num) { - return Integer.toString(num, 7); -} -``` - -## 16 进制 - -[405. Convert a Number to Hexadecimal (Easy)](https://leetcode.com/problems/convert-a-number-to-hexadecimal/description/) - -```html -Input: -26 - -Output: -"1a" - -Input: --1 - -Output: -"ffffffff" -``` - -负数要用它的补码形式。 - -```java -public String toHex(int num) { - char[] map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - if (num == 0) return "0"; - StringBuilder sb = new StringBuilder(); - while (num != 0) { - sb.append(map[num & 0b1111]); - num >>>= 4; // 因为考虑的是补码形式,因此符号位就不能有特殊的意义,需要使用无符号右移,左边填 0 - } - return sb.reverse().toString(); -} -``` - -## 26 进制 - -[168. Excel Sheet Column Title (Easy)](https://leetcode.com/problems/excel-sheet-column-title/description/) - -```html -1 -> A -2 -> B -3 -> C -... -26 -> Z -27 -> AA -28 -> AB -``` - -因为是从 1 开始计算的,而不是从 0 开始,因此需要对 n 执行 -1 操作。 - -```java -public String convertToTitle(int n) { - if (n == 0) { - return ""; - } - n--; - return convertToTitle(n / 26) + (char) (n % 26 + 'A'); -} -``` - -# 阶乘 - -## 统计阶乘尾部有多少个 0 - -[172. Factorial Trailing Zeroes (Easy)](https://leetcode.com/problems/factorial-trailing-zeroes/description/) - -尾部的 0 由 2 * 5 得来,2 的数量明显多于 5 的数量,因此只要统计有多少个 5 即可。 - -对于一个数 N,它所包含 5 的个数为:N/5 + N/52 + N/53 + ...,其中 N/5 表示不大于 N 的数中 5 的倍数贡献一个 5,N/52 表示不大于 N 的数中 52 的倍数再贡献一个 5 ...。 - -```java -public int trailingZeroes(int n) { - return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5); -} -``` - -如果统计的是 N! 的二进制表示中最低位 1 的位置,只要统计有多少个 2 即可,该题目出自 [编程之美:2.2](#) 。和求解有多少个 5 一样,2 的个数为 N/2 + N/22 + N/23 + ... - -# 字符串加法减法 - -## 二进制加法 - -[67. Add Binary (Easy)](https://leetcode.com/problems/add-binary/description/) - -```html -a = "11" -b = "1" -Return "100". -``` - -```java -public String addBinary(String a, String b) { - int i = a.length() - 1, j = b.length() - 1, carry = 0; - StringBuilder str = new StringBuilder(); - while (carry == 1 || i >= 0 || j >= 0) { - if (i >= 0 && a.charAt(i--) == '1') { - carry++; - } - if (j >= 0 && b.charAt(j--) == '1') { - carry++; - } - str.append(carry % 2); - carry /= 2; - } - return str.reverse().toString(); -} -``` - -## 字符串加法 - -[415. Add Strings (Easy)](https://leetcode.com/problems/add-strings/description/) - -字符串的值为非负整数。 - -```java -public String addStrings(String num1, String num2) { - StringBuilder str = new StringBuilder(); - int carry = 0, i = num1.length() - 1, j = num2.length() - 1; - while (carry == 1 || i >= 0 || j >= 0) { - int x = i < 0 ? 0 : num1.charAt(i--) - '0'; - int y = j < 0 ? 0 : num2.charAt(j--) - '0'; - str.append((x + y + carry) % 10); - carry = (x + y + carry) / 10; - } - return str.reverse().toString(); -} -``` - -# 相遇问题 - -## 改变数组元素使所有的数组元素都相等 - -[462. Minimum Moves to Equal Array Elements II (Medium)](https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/description/) - -```html -Input: -[1,2,3] - -Output: -2 - -Explanation: -Only two moves are needed (remember each move increments or decrements one element): - -[1,2,3] => [2,2,3] => [2,2,2] -``` - -每次可以对一个数组元素加一或者减一,求最小的改变次数。 - -这是个典型的相遇问题,移动距离最小的方式是所有元素都移动到中位数。理由如下: - -设 m 为中位数。a 和 b 是 m 两边的两个元素,且 b > a。要使 a 和 b 相等,它们总共移动的次数为 b - a,这个值等于 (b - m) + (m - a),也就是把这两个数移动到中位数的移动次数。 - -设数组长度为 N,则可以找到 N/2 对 a 和 b 的组合,使它们都移动到 m 的位置。 - -## 解法 1 - -先排序,时间复杂度:O(NlogN) - -```java -public int minMoves2(int[] nums) { - Arrays.sort(nums); - int move = 0; - int l = 0, h = nums.length - 1; - while (l <= h) { - move += nums[h] - nums[l]; - l++; - h--; - } - return move; -} -``` - -## 解法 2 - -使用快速选择找到中位数,时间复杂度 O(N) - -```java -public int minMoves2(int[] nums) { - int move = 0; - int median = findKthSmallest(nums, nums.length / 2); - for (int num : nums) { - move += Math.abs(num - median); - } - return move; -} - -private int 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) { - l = j + 1; - } else { - h = j - 1; - } - } - return nums[k]; -} - -private int partition(int[] nums, int l, int h) { - int i = l, j = h + 1; - while (true) { - while (nums[++i] < nums[l] && i < h) ; - while (nums[--j] > nums[l] && j > l) ; - if (i >= j) { - break; - } - swap(nums, i, j); - } - swap(nums, l, j); - return j; -} - -private void swap(int[] nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; -} -``` - -# 多数投票问题 - -## 数组中出现次数多于 n / 2 的元素 - -[169. Majority Element (Easy)](https://leetcode.com/problems/majority-element/description/) - -先对数组排序,最中间那个数出现次数一定多于 n / 2。 - -```java -public int majorityElement(int[] nums) { - Arrays.sort(nums); - return nums[nums.length / 2]; -} -``` - -可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。可以这么理解该算法:使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2,因为如果多于 i / 2 的话 cnt 就一定不会为 0。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。 - -```java -public int majorityElement(int[] nums) { - int cnt = 0, majority = nums[0]; - for (int num : nums) { - majority = (cnt == 0) ? num : majority; - cnt = (majority == num) ? cnt + 1 : cnt - 1; - } - return majority; -} -``` - -# 其它 - -## 平方数 - -[367. Valid Perfect Square (Easy)](https://leetcode.com/problems/valid-perfect-square/description/) - -```html -Input: 16 -Returns: True -``` - -平方序列:1,4,9,16,.. - -间隔:3,5,7,... - -间隔为等差数列,使用这个特性可以得到从 1 开始的平方序列。 - -```java -public boolean isPerfectSquare(int num) { - int subNum = 1; - while (num > 0) { - num -= subNum; - subNum += 2; - } - return num == 0; -} -``` - -## 3 的 n 次方 - -[326. Power of Three (Easy)](https://leetcode.com/problems/power-of-three/description/) - -```java -public boolean isPowerOfThree(int n) { - return n > 0 && (1162261467 % n == 0); -} -``` - -## 乘积数组 - -[238. Product of Array Except Self (Medium)](https://leetcode.com/problems/product-of-array-except-self/description/) - -```html -For example, given [1,2,3,4], return [24,12,8,6]. -``` - -给定一个数组,创建一个新数组,新数组的每个元素为原始数组中除了该位置上的元素之外所有元素的乘积。 - -要求时间复杂度为 O(N),并且不能使用除法。 - -```java -public int[] productExceptSelf(int[] nums) { - int n = nums.length; - int[] products = new int[n]; - Arrays.fill(products, 1); - int left = 1; - for (int i = 1; i < n; i++) { - left *= nums[i - 1]; - products[i] *= left; - } - int right = 1; - for (int i = n - 2; i >= 0; i--) { - right *= nums[i + 1]; - products[i] *= right; - } - return products; -} -``` - -## 找出数组中的乘积最大的三个数 - -[628. Maximum Product of Three Numbers (Easy)](https://leetcode.com/problems/maximum-product-of-three-numbers/description/) - -```html -Input: [1,2,3,4] -Output: 24 -``` - -```java -public int maximumProduct(int[] nums) { - int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE, max3 = Integer.MIN_VALUE, min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; - for (int n : nums) { - if (n > max1) { - max3 = max2; - max2 = max1; - max1 = n; - } else if (n > max2) { - max3 = max2; - max2 = n; - } else if (n > max3) { - max3 = n; - } - - if (n < min1) { - min2 = min1; - min1 = n; - } else if (n < min2) { - min2 = n; - } - } - return Math.max(max1*max2*max3, max1*min1*min2); -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" deleted file mode 100644 index 66152af952..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" +++ /dev/null @@ -1,439 +0,0 @@ - -* [1. 把数组中的 0 移到末尾](#1-把数组中的-0-移到末尾) -* [2. 改变矩阵维度](#2-改变矩阵维度) -* [3. 找出数组中最长的连续 1](#3-找出数组中最长的连续-1) -* [4. 有序矩阵查找](#4-有序矩阵查找) -* [5. 有序矩阵的 Kth Element](#5-有序矩阵的-kth-element) -* [6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数](#6-一个数组元素在-[1,-n]-之间,其中一个数被替换为另一个数,找出重复的数和丢失的数) -* [7. 找出数组中重复的数,数组值在 [1, n] 之间](#7-找出数组中重复的数,数组值在-[1,-n]-之间) -* [8. 数组相邻差值的个数](#8-数组相邻差值的个数) -* [9. 数组的度](#9-数组的度) -* [10. 对角元素相等的矩阵](#10-对角元素相等的矩阵) -* [11. 嵌套数组](#11-嵌套数组) -* [12. 分隔数组](#12-分隔数组) - - - -# 1. 把数组中的 0 移到末尾 - -[283. Move Zeroes (Easy)](https://leetcode.com/problems/move-zeroes/description/) - -```html -For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0]. -``` - -```java -public void moveZeroes(int[] nums) { - int idx = 0; - for (int num : nums) { - if (num != 0) { - nums[idx++] = num; - } - } - while (idx < nums.length) { - nums[idx++] = 0; - } -} -``` - -# 2. 改变矩阵维度 - -[566. Reshape the Matrix (Easy)](https://leetcode.com/problems/reshape-the-matrix/description/) - -```html -Input: -nums = -[[1,2], - [3,4]] -r = 1, c = 4 - -Output: -[[1,2,3,4]] - -Explanation: -The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list. -``` - -```java -public int[][] matrixReshape(int[][] nums, int r, int c) { - int m = nums.length, n = nums[0].length; - if (m * n != r * c) { - return nums; - } - int[][] reshapedNums = new int[r][c]; - int index = 0; - for (int i = 0; i < r; i++) { - for (int j = 0; j < c; j++) { - reshapedNums[i][j] = nums[index / n][index % n]; - index++; - } - } - return reshapedNums; -} -``` - -# 3. 找出数组中最长的连续 1 - -[485. Max Consecutive Ones (Easy)](https://leetcode.com/problems/max-consecutive-ones/description/) - -```java -public int findMaxConsecutiveOnes(int[] nums) { - int max = 0, cur = 0; - for (int x : nums) { - cur = x == 0 ? 0 : cur + 1; - max = Math.max(max, cur); - } - return max; -} -``` - -# 4. 有序矩阵查找 - -[240. Search a 2D Matrix II (Medium)](https://leetcode.com/problems/search-a-2d-matrix-ii/description/) - -```html -[ - [ 1, 5, 9], - [10, 11, 13], - [12, 13, 15] -] -``` - -```java -public boolean searchMatrix(int[][] matrix, int target) { - if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false; - int m = matrix.length, n = matrix[0].length; - int row = 0, col = n - 1; - while (row < m && col >= 0) { - if (target == matrix[row][col]) return true; - else if (target < matrix[row][col]) col--; - else row++; - } - return false; -} -``` - -# 5. 有序矩阵的 Kth Element - -[378. Kth Smallest Element in a Sorted Matrix ((Medium))](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/) - -```html -matrix = [ - [ 1, 5, 9], - [10, 11, 13], - [12, 13, 15] -], -k = 8, - -return 13. -``` - -解题参考:[Share my thoughts and Clean Java Code](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/discuss/85173) - -二分查找解法: - -```java -public int kthSmallest(int[][] matrix, int k) { - int m = matrix.length, n = matrix[0].length; - int lo = matrix[0][0], hi = matrix[m - 1][n - 1]; - while (lo <= hi) { - int mid = lo + (hi - lo) / 2; - int cnt = 0; - for (int i = 0; i < m; i++) { - for (int j = 0; j < n && matrix[i][j] <= mid; j++) { - cnt++; - } - } - if (cnt < k) lo = mid + 1; - else hi = mid - 1; - } - return lo; -} -``` - -堆解法: - -```java -public int kthSmallest(int[][] matrix, int k) { - int m = matrix.length, n = matrix[0].length; - PriorityQueue pq = new PriorityQueue(); - for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j])); - for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数 - Tuple t = pq.poll(); - if(t.x == m - 1) continue; - pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y])); - } - return pq.poll().val; -} - -class Tuple implements Comparable { - int x, y, val; - public Tuple(int x, int y, int val) { - this.x = x; this.y = y; this.val = val; - } - - @Override - public int compareTo(Tuple that) { - return this.val - that.val; - } -} -``` - -# 6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数 - -[645. Set Mismatch (Easy)](https://leetcode.com/problems/set-mismatch/description/) - -```html -Input: nums = [1,2,2,4] -Output: [2,3] -``` - -```html -Input: nums = [1,2,2,4] -Output: [2,3] -``` - -最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(NlogN)。本题可以以 O(N) 的时间复杂度、O(1) 空间复杂度来求解。 - -主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。 - -```java -public int[] findErrorNums(int[] nums) { - for (int i = 0; i < nums.length; i++) { - while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) { - swap(nums, i, nums[i] - 1); - } - } - for (int i = 0; i < nums.length; i++) { - if (nums[i] != i + 1) { - return new int[]{nums[i], i + 1}; - } - } - return null; -} - -private void swap(int[] nums, int i, int j) { - int tmp = nums[i]; - nums[i] = nums[j]; - nums[j] = tmp; -} -``` - -类似题目: - -- [448. Find All Numbers Disappeared in an Array (Easy)](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/description/),寻找所有丢失的元素 -- [442. Find All Duplicates in an Array (Medium)](https://leetcode.com/problems/find-all-duplicates-in-an-array/description/),寻找所有重复的元素。 - -# 7. 找出数组中重复的数,数组值在 [1, n] 之间 - -[287. Find the Duplicate Number (Medium)](https://leetcode.com/problems/find-the-duplicate-number/description/) - -要求不能修改数组,也不能使用额外的空间。 - -二分查找解法: - -```java -public int findDuplicate(int[] nums) { - int l = 1, h = nums.length - 1; - while (l <= h) { - int mid = l + (h - l) / 2; - int cnt = 0; - for (int i = 0; i < nums.length; i++) { - if (nums[i] <= mid) cnt++; - } - if (cnt > mid) h = mid - 1; - else l = mid + 1; - } - return l; -} -``` - -双指针解法,类似于有环链表中找出环的入口: - -```java -public int findDuplicate(int[] nums) { - int slow = nums[0], fast = nums[nums[0]]; - while (slow != fast) { - slow = nums[slow]; - fast = nums[nums[fast]]; - } - fast = 0; - while (slow != fast) { - slow = nums[slow]; - fast = nums[fast]; - } - return slow; -} -``` - -# 8. 数组相邻差值的个数 - -[667. Beautiful Arrangement II (Medium)](https://leetcode.com/problems/beautiful-arrangement-ii/description/) - -```html -Input: n = 3, k = 2 -Output: [1, 3, 2] -Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2. -``` - -题目描述:数组元素为 1\~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。 - -让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 ... k/2 k/2+1. - -```java -public int[] constructArray(int n, int k) { - int[] ret = new int[n]; - ret[0] = 1; - for (int i = 1, interval = k; i <= k; i++, interval--) { - ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval; - } - for (int i = k + 1; i < n; i++) { - ret[i] = i + 1; - } - return ret; -} -``` - -# 9. 数组的度 - -[697. Degree of an Array (Easy)](https://leetcode.com/problems/degree-of-an-array/description/) - -```html -Input: [1,2,2,3,1,4,2] -Output: 6 -``` - -题目描述:数组的度定义为元素出现的最高频率,例如上面的数组度为 3。要求找到一个最小的子数组,这个子数组的度和原数组一样。 - -```java -public int findShortestSubArray(int[] nums) { - Map numsCnt = new HashMap<>(); - Map numsLastIndex = new HashMap<>(); - Map numsFirstIndex = new HashMap<>(); - for (int i = 0; i < nums.length; i++) { - int num = nums[i]; - numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1); - numsLastIndex.put(num, i); - if (!numsFirstIndex.containsKey(num)) { - numsFirstIndex.put(num, i); - } - } - int maxCnt = 0; - for (int num : nums) { - maxCnt = Math.max(maxCnt, numsCnt.get(num)); - } - int ret = nums.length; - for (int i = 0; i < nums.length; i++) { - int num = nums[i]; - int cnt = numsCnt.get(num); - if (cnt != maxCnt) continue; - ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1); - } - return ret; -} -``` - -# 10. 对角元素相等的矩阵 - -[766. Toeplitz Matrix (Easy)](https://leetcode.com/problems/toeplitz-matrix/description/) - -```html -1234 -5123 -9512 - -In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", and in each diagonal all elements are the same, so the answer is True. -``` - -```java -public boolean isToeplitzMatrix(int[][] matrix) { - for (int i = 0; i < matrix[0].length; i++) { - if (!check(matrix, matrix[0][i], 0, i)) { - return false; - } - } - for (int i = 0; i < matrix.length; i++) { - if (!check(matrix, matrix[i][0], i, 0)) { - return false; - } - } - return true; -} - -private boolean check(int[][] matrix, int expectValue, int row, int col) { - if (row >= matrix.length || col >= matrix[0].length) { - return true; - } - if (matrix[row][col] != expectValue) { - return false; - } - return check(matrix, expectValue, row + 1, col + 1); -} -``` - -# 11. 嵌套数组 - -[565. Array Nesting (Medium)](https://leetcode.com/problems/array-nesting/description/) - -```html -Input: A = [5,4,0,3,1,6,2] -Output: 4 -Explanation: -A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2. - -One of the longest S[K]: -S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0} -``` - -题目描述:S[i] 表示一个集合,集合的第一个元素是 A[i],第二个元素是 A[A[i]],如此嵌套下去。求最大的 S[i]。 - -```java -public int arrayNesting(int[] nums) { - int max = 0; - for (int i = 0; i < nums.length; i++) { - int cnt = 0; - for (int j = i; nums[j] != -1; ) { - cnt++; - int t = nums[j]; - nums[j] = -1; // 标记该位置已经被访问 - j = t; - - } - max = Math.max(max, cnt); - } - return max; -} -``` - -# 12. 分隔数组 - -[769. Max Chunks To Make Sorted (Medium)](https://leetcode.com/problems/max-chunks-to-make-sorted/description/) - -```html -Input: arr = [1,0,2,3,4] -Output: 4 -Explanation: -We can split into two chunks, such as [1, 0], [2, 3, 4]. -However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible. -``` - -题目描述:分隔数组,使得对每部分排序后数组就为有序。 - -```java -public int maxChunksToSorted(int[] arr) { - if (arr == null) return 0; - int ret = 0; - int right = arr[0]; - for (int i = 0; i < arr.length; i++) { - right = Math.max(right, arr[i]); - if (right == i) ret++; - } - return ret; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" deleted file mode 100644 index e3546535e6..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" +++ /dev/null @@ -1,226 +0,0 @@ - -* [用栈实现队列](#用栈实现队列) -* [用队列实现栈](#用队列实现栈) -* [最小值栈](#最小值栈) -* [用栈实现括号匹配](#用栈实现括号匹配) -* [数组中元素与下一个比它大的元素之间的距离](#数组中元素与下一个比它大的元素之间的距离) -* [循环数组中比当前元素大的下一个元素](#循环数组中比当前元素大的下一个元素) - - - -# 用栈实现队列 - -[232. Implement Queue using Stacks (Easy)](https://leetcode.com/problems/implement-queue-using-stacks/description/) - -栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。 - -```java -class MyQueue { - - private Stack in = new Stack<>(); - private Stack out = new Stack<>(); - - public void push(int x) { - in.push(x); - } - - public int pop() { - in2out(); - return out.pop(); - } - - public int peek() { - in2out(); - return out.peek(); - } - - private void in2out() { - if (out.isEmpty()) { - while (!in.isEmpty()) { - out.push(in.pop()); - } - } - } - - public boolean empty() { - return in.isEmpty() && out.isEmpty(); - } -} -``` - -# 用队列实现栈 - -[225. Implement Stack using Queues (Easy)](https://leetcode.com/problems/implement-stack-using-queues/description/) - -在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。 - -```java -class MyStack { - - private Queue queue; - - public MyStack() { - queue = new LinkedList<>(); - } - - public void push(int x) { - queue.add(x); - int cnt = queue.size(); - while (cnt-- > 1) { - queue.add(queue.poll()); - } - } - - public int pop() { - return queue.remove(); - } - - public int top() { - return queue.peek(); - } - - public boolean empty() { - return queue.isEmpty(); - } -} -``` - -# 最小值栈 - -[155. Min Stack (Easy)](https://leetcode.com/problems/min-stack/description/) - -```java -class MinStack { - - private Stack dataStack; - private Stack minStack; - private int min; - - public MinStack() { - dataStack = new Stack<>(); - minStack = new Stack<>(); - min = Integer.MAX_VALUE; - } - - public void push(int x) { - dataStack.add(x); - min = Math.min(min, x); - minStack.add(min); - } - - public void pop() { - dataStack.pop(); - minStack.pop(); - min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek(); - } - - public int top() { - return dataStack.peek(); - } - - public int getMin() { - return minStack.peek(); - } -} -``` - -对于实现最小值队列问题,可以先将队列使用栈来实现,然后就将问题转换为最小值栈,这个问题出现在 编程之美:3.7。 - -# 用栈实现括号匹配 - -[20. Valid Parentheses (Easy)](https://leetcode.com/problems/valid-parentheses/description/) - -```html -"()[]{}" - -Output : true -``` - -```java -public boolean isValid(String s) { - Stack stack = new Stack<>(); - for (char c : s.toCharArray()) { - if (c == '(' || c == '{' || c == '[') { - stack.push(c); - } else { - if (stack.isEmpty()) { - return false; - } - char cStack = stack.pop(); - boolean b1 = c == ')' && cStack != '('; - boolean b2 = c == ']' && cStack != '['; - boolean b3 = c == '}' && cStack != '{'; - if (b1 || b2 || b3) { - return false; - } - } - } - return stack.isEmpty(); -} -``` - -# 数组中元素与下一个比它大的元素之间的距离 - -[739. Daily Temperatures (Medium)](https://leetcode.com/problems/daily-temperatures/description/) - -```html -Input: [73, 74, 75, 71, 69, 72, 76, 73] -Output: [1, 1, 4, 2, 1, 1, 0, 0] -``` - -在遍历数组时用栈把数组中的数存起来,如果当前遍历的数比栈顶元素来的大,说明栈顶元素的下一个比它大的数就是当前元素。 - -```java -public int[] dailyTemperatures(int[] temperatures) { - int n = temperatures.length; - int[] dist = new int[n]; - Stack indexs = new Stack<>(); - for (int curIndex = 0; curIndex < n; curIndex++) { - while (!indexs.isEmpty() && temperatures[curIndex] > temperatures[indexs.peek()]) { - int preIndex = indexs.pop(); - dist[preIndex] = curIndex - preIndex; - } - indexs.add(curIndex); - } - return dist; -} -``` - -# 循环数组中比当前元素大的下一个元素 - -[503. Next Greater Element II (Medium)](https://leetcode.com/problems/next-greater-element-ii/description/) - -```text -Input: [1,2,1] -Output: [2,-1,2] -Explanation: The first 1's next greater number is 2; -The number 2 can't find next greater number; -The second 1's next greater number needs to search circularly, which is also 2. -``` - -与 739. Daily Temperatures (Medium) 不同的是,数组是循环数组,并且最后要求的不是距离而是下一个元素。 - -```java -public int[] nextGreaterElements(int[] nums) { - int n = nums.length; - int[] next = new int[n]; - Arrays.fill(next, -1); - Stack pre = new Stack<>(); - for (int i = 0; i < n * 2; i++) { - int num = nums[i % n]; - while (!pre.isEmpty() && nums[pre.peek()] < num) { - next[pre.pop()] = num; - } - if (i < n){ - pre.push(i); - } - } - return next; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" deleted file mode 100644 index cdf43d7a90..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" +++ /dev/null @@ -1,1126 +0,0 @@ - -* [递归](#递归) - * [树的高度](#树的高度) - * [平衡树](#平衡树) - * [两节点的最长路径](#两节点的最长路径) - * [翻转树](#翻转树) - * [归并两棵树](#归并两棵树) - * [判断路径和是否等于一个数](#判断路径和是否等于一个数) - * [统计路径和等于一个数的路径数量](#统计路径和等于一个数的路径数量) - * [子树](#子树) - * [树的对称](#树的对称) - * [最小路径](#最小路径) - * [统计左叶子节点的和](#统计左叶子节点的和) - * [相同节点值的最大路径长度](#相同节点值的最大路径长度) - * [间隔遍历](#间隔遍历) - * [找出二叉树中第二小的节点](#找出二叉树中第二小的节点) -* [层次遍历](#层次遍历) - * [一棵树每层节点的平均数](#一棵树每层节点的平均数) - * [得到左下角的节点](#得到左下角的节点) -* [前中后序遍历](#前中后序遍历) - * [非递归实现二叉树的前序遍历](#非递归实现二叉树的前序遍历) - * [非递归实现二叉树的后序遍历](#非递归实现二叉树的后序遍历) - * [非递归实现二叉树的中序遍历](#非递归实现二叉树的中序遍历) -* [BST](#bst) - * [修剪二叉查找树](#修剪二叉查找树) - * [寻找二叉查找树的第 k 个元素](#寻找二叉查找树的第-k-个元素) - * [把二叉查找树每个节点的值都加上比它大的节点的值](#把二叉查找树每个节点的值都加上比它大的节点的值) - * [二叉查找树的最近公共祖先](#二叉查找树的最近公共祖先) - * [二叉树的最近公共祖先](#二叉树的最近公共祖先) - * [从有序数组中构造二叉查找树](#从有序数组中构造二叉查找树) - * [根据有序链表构造平衡的二叉查找树](#根据有序链表构造平衡的二叉查找树) - * [在二叉查找树中寻找两个节点,使它们的和为一个给定值](#在二叉查找树中寻找两个节点,使它们的和为一个给定值) - * [在二叉查找树中查找两个节点之差的最小绝对值](#在二叉查找树中查找两个节点之差的最小绝对值) - * [寻找二叉查找树中出现次数最多的值](#寻找二叉查找树中出现次数最多的值) -* [Trie](#trie) - * [实现一个 Trie](#实现一个-trie) - * [实现一个 Trie,用来求前缀和](#实现一个-trie,用来求前缀和) - - - -# 递归 - -一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。 - -## 树的高度 - -[104. Maximum Depth of Binary Tree (Easy)](https://leetcode.com/problems/maximum-depth-of-binary-tree/description/) - -```java -public int maxDepth(TreeNode root) { - if (root == null) return 0; - return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; -} -``` - -## 平衡树 - -[110. Balanced Binary Tree (Easy)](https://leetcode.com/problems/balanced-binary-tree/description/) - -```html - 3 - / \ - 9 20 - / \ - 15 7 -``` - -平衡树左右子树高度差都小于等于 1 - -```java -private boolean result = true; - -public boolean isBalanced(TreeNode root) { - maxDepth(root); - return result; -} - -public int maxDepth(TreeNode root) { - if (root == null) return 0; - int l = maxDepth(root.left); - int r = maxDepth(root.right); - if (Math.abs(l - r) > 1) result = false; - return 1 + Math.max(l, r); -} -``` - -## 两节点的最长路径 - -[543. Diameter of Binary Tree (Easy)](https://leetcode.com/problems/diameter-of-binary-tree/description/) - -```html -Input: - - 1 - / \ - 2 3 - / \ - 4 5 - -Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. -``` - -```java -private int max = 0; - -public int diameterOfBinaryTree(TreeNode root) { - depth(root); - return max; -} - -private int depth(TreeNode root) { - if (root == null) return 0; - int leftDepth = depth(root.left); - int rightDepth = depth(root.right); - max = Math.max(max, leftDepth + rightDepth); - return Math.max(leftDepth, rightDepth) + 1; -} -``` - -## 翻转树 - -[226. Invert Binary Tree (Easy)](https://leetcode.com/problems/invert-binary-tree/description/) - -```java -public TreeNode invertTree(TreeNode root) { - if (root == null) return null; - TreeNode left = root.left; // 后面的操作会改变 left 指针,因此先保存下来 - root.left = invertTree(root.right); - root.right = invertTree(left); - return root; -} -``` - -## 归并两棵树 - -[617. Merge Two Binary Trees (Easy)](https://leetcode.com/problems/merge-two-binary-trees/description/) - -```html -Input: - Tree 1 Tree 2 - 1 2 - / \ / \ - 3 2 1 3 - / \ \ - 5 4 7 - -Output: - 3 - / \ - 4 5 - / \ \ - 5 4 7 -``` - -```java -public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { - if (t1 == null && t2 == null) return null; - if (t1 == null) return t2; - if (t2 == null) return t1; - TreeNode root = new TreeNode(t1.val + t2.val); - root.left = mergeTrees(t1.left, t2.left); - root.right = mergeTrees(t1.right, t2.right); - return root; -} -``` - -## 判断路径和是否等于一个数 - -[Leetcdoe : 112. Path Sum (Easy)](https://leetcode.com/problems/path-sum/description/) - -```html -Given the below binary tree and sum = 22, - - 5 - / \ - 4 8 - / / \ - 11 13 4 - / \ \ - 7 2 1 - -return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22. -``` - -路径和定义为从 root 到 leaf 的所有节点的和。 - -```java -public boolean hasPathSum(TreeNode root, int sum) { - if (root == null) return false; - if (root.left == null && root.right == null && root.val == sum) return true; - return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); -} -``` - -## 统计路径和等于一个数的路径数量 - -[437. Path Sum III (Easy)](https://leetcode.com/problems/path-sum-iii/description/) - -```html -root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 - - 10 - / \ - 5 -3 - / \ \ - 3 2 11 - / \ \ -3 -2 1 - -Return 3. The paths that sum to 8 are: - -1. 5 -> 3 -2. 5 -> 2 -> 1 -3. -3 -> 11 -``` - -路径不一定以 root 开头,也不一定以 leaf 结尾,但是必须连续。 - -```java -public int pathSum(TreeNode root, int sum) { - if (root == null) return 0; - int ret = pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum); - return ret; -} - -private int pathSumStartWithRoot(TreeNode root, int sum) { - if (root == null) return 0; - int ret = 0; - if (root.val == sum) ret++; - ret += pathSumStartWithRoot(root.left, sum - root.val) + pathSumStartWithRoot(root.right, sum - root.val); - return ret; -} -``` - -## 子树 - -[572. Subtree of Another Tree (Easy)](https://leetcode.com/problems/subtree-of-another-tree/description/) - -```html -Given tree s: - 3 - / \ - 4 5 - / \ - 1 2 - -Given tree t: - 4 - / \ - 1 2 - -Return true, because t has the same structure and node values with a subtree of s. - -Given tree s: - - 3 - / \ - 4 5 - / \ - 1 2 - / - 0 - -Given tree t: - 4 - / \ - 1 2 - -Return false. -``` - -```java -public boolean isSubtree(TreeNode s, TreeNode t) { - if (s == null) return false; - return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); -} - -private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) { - if (t == null && s == null) return true; - if (t == null || s == null) return false; - if (t.val != s.val) return false; - return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right); -} -``` - -## 树的对称 - -[101. Symmetric Tree (Easy)](https://leetcode.com/problems/symmetric-tree/description/) - -```html - 1 - / \ - 2 2 - / \ / \ -3 4 4 3 -``` - -```java -public boolean isSymmetric(TreeNode root) { - if (root == null) return true; - return isSymmetric(root.left, root.right); -} - -private boolean isSymmetric(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 isSymmetric(t1.left, t2.right) && isSymmetric(t1.right, t2.left); -} -``` - -## 最小路径 - -[111. Minimum Depth of Binary Tree (Easy)](https://leetcode.com/problems/minimum-depth-of-binary-tree/description/) - -树的根节点到叶子节点的最小路径长度 - -```java -public int minDepth(TreeNode root) { - if (root == null) return 0; - int left = minDepth(root.left); - int right = minDepth(root.right); - if (left == 0 || right == 0) return left + right + 1; - return Math.min(left, right) + 1; -} -``` - -## 统计左叶子节点的和 - -[404. Sum of Left Leaves (Easy)](https://leetcode.com/problems/sum-of-left-leaves/description/) - -```html - 3 - / \ - 9 20 - / \ - 15 7 - -There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24. -``` - -```java -public int sumOfLeftLeaves(TreeNode root) { - if (root == null) return 0; - if (isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right); - return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right); -} - -private boolean isLeaf(TreeNode node){ - if (node == null) return false; - return node.left == null && node.right == null; -} -``` - -## 相同节点值的最大路径长度 - -[687. Longest Univalue Path (Easy)](https://leetcode.com/problems/longest-univalue-path/) - -```html - 1 - / \ - 4 5 - / \ \ - 4 4 5 - -Output : 2 -``` - -```java -private int path = 0; - -public int longestUnivaluePath(TreeNode root) { - dfs(root); - return path; -} - -private int dfs(TreeNode root){ - if (root == null) return 0; - int left = dfs(root.left); - int right = dfs(root.right); - int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0; - int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0; - path = Math.max(path, leftPath + rightPath); - return Math.max(leftPath, rightPath); -} -``` - -## 间隔遍历 - -[337. House Robber III (Medium)](https://leetcode.com/problems/house-robber-iii/description/) - -```html - 3 - / \ - 2 3 - \ \ - 3 1 -Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. -``` - -```java -public int rob(TreeNode root) { - if (root == null) return 0; - int val1 = root.val; - if (root.left != null) val1 += rob(root.left.left) + rob(root.left.right); - if (root.right != null) val1 += rob(root.right.left) + rob(root.right.right); - int val2 = rob(root.left) + rob(root.right); - return Math.max(val1, val2); -} -``` - -## 找出二叉树中第二小的节点 - -[671. Second Minimum Node In a Binary Tree (Easy)](https://leetcode.com/problems/second-minimum-node-in-a-binary-tree/description/) - -```html -Input: - 2 - / \ - 2 5 - / \ - 5 7 - -Output: 5 -``` - -一个节点要么具有 0 个或 2 个子节点,如果有子节点,那么根节点是最小的节点。 - -```java -public int findSecondMinimumValue(TreeNode root) { - if (root == null) return -1; - if (root.left == null && root.right == null) return -1; - int leftVal = root.left.val; - int rightVal = root.right.val; - if (leftVal == root.val) leftVal = findSecondMinimumValue(root.left); - if (rightVal == root.val) rightVal = findSecondMinimumValue(root.right); - if (leftVal != -1 && rightVal != -1) return Math.min(leftVal, rightVal); - if (leftVal != -1) return leftVal; - return rightVal; -} -``` - -# 层次遍历 - -使用 BFS 进行层次遍历。不需要使用两个队列来分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 - -## 一棵树每层节点的平均数 - -[637. Average of Levels in Binary Tree (Easy)](https://leetcode.com/problems/average-of-levels-in-binary-tree/description/) - -```java -public List averageOfLevels(TreeNode root) { - List ret = new ArrayList<>(); - if (root == null) return ret; - Queue queue = new LinkedList<>(); - queue.add(root); - while (!queue.isEmpty()) { - int cnt = queue.size(); - double sum = 0; - for (int i = 0; i < cnt; i++) { - TreeNode node = queue.poll(); - sum += node.val; - if (node.left != null) queue.add(node.left); - if (node.right != null) queue.add(node.right); - } - ret.add(sum / cnt); - } - return ret; -} -``` - -## 得到左下角的节点 - -[513. Find Bottom Left Tree Value (Easy)](https://leetcode.com/problems/find-bottom-left-tree-value/description/) - -```html -Input: - - 1 - / \ - 2 3 - / / \ - 4 5 6 - / - 7 - -Output: -7 -``` - -```java -public int findBottomLeftValue(TreeNode root) { - Queue queue = new LinkedList<>(); - queue.add(root); - while (!queue.isEmpty()) { - root = queue.poll(); - if (root.right != null) queue.add(root.right); - if (root.left != null) queue.add(root.left); - } - return root.val; -} -``` - -# 前中后序遍历 - -```html - 1 - / \ - 2 3 - / \ \ -4 5 6 -``` - -- 层次遍历顺序:[1 2 3 4 5 6] -- 前序遍历顺序:[1 2 4 5 3 6] -- 中序遍历顺序:[4 2 5 1 3 6] -- 后序遍历顺序:[4 5 2 6 3 1] - -层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;而前序、中序、后序遍历利用了 DFS 实现。 - -前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。 - -① 前序 - -```java -void dfs(TreeNode root) { - visit(root); - dfs(root.left); - dfs(root.right); -} -``` - -② 中序 - -```java -void dfs(TreeNode root) { - dfs(root.left); - visit(root); - dfs(root.right); -} -``` - -③ 后序 - -```java -void dfs(TreeNode root) { - dfs(root.left); - dfs(root.right); - visit(root); -} -``` - -## 非递归实现二叉树的前序遍历 - -[144. Binary Tree Preorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-preorder-traversal/description/) - -```java -public List preorderTraversal(TreeNode root) { - List ret = new ArrayList<>(); - Stack stack = new Stack<>(); - stack.push(root); - while (!stack.isEmpty()) { - TreeNode node = stack.pop(); - if (node == null) continue; - ret.add(node.val); - stack.push(node.right); // 先右后左,保证左子树先遍历 - stack.push(node.left); - } - return ret; -} -``` - -## 非递归实现二叉树的后序遍历 - -[145. Binary Tree Postorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-postorder-traversal/description/) - -前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。 - -```java -public List postorderTraversal(TreeNode root) { - List ret = new ArrayList<>(); - Stack stack = new Stack<>(); - stack.push(root); - while (!stack.isEmpty()) { - TreeNode node = stack.pop(); - if (node == null) continue; - ret.add(node.val); - stack.push(node.left); - stack.push(node.right); - } - Collections.reverse(ret); - return ret; -} -``` - -## 非递归实现二叉树的中序遍历 - -[94. Binary Tree Inorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-inorder-traversal/description/) - -```java -public List inorderTraversal(TreeNode root) { - List ret = new ArrayList<>(); - if (root == null) return ret; - Stack stack = new Stack<>(); - TreeNode cur = root; - while (cur != null || !stack.isEmpty()) { - while (cur != null) { - stack.push(cur); - cur = cur.left; - } - TreeNode node = stack.pop(); - ret.add(node.val); - cur = node.right; - } - return ret; -} -``` - -# BST - -二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。 - -二叉查找树中序遍历有序。 - -## 修剪二叉查找树 - -[669. Trim a Binary Search Tree (Easy)](https://leetcode.com/problems/trim-a-binary-search-tree/description/) - -```html -Input: - - 3 - / \ - 0 4 - \ - 2 - / - 1 - - L = 1 - R = 3 - -Output: - - 3 - / - 2 - / - 1 -``` - -题目描述:只保留值在 L \~ R 之间的节点 - -```java -public TreeNode trimBST(TreeNode root, int L, int R) { - if (root == null) return null; - if (root.val > R) return trimBST(root.left, L, R); - if (root.val < L) return trimBST(root.right, L, R); - root.left = trimBST(root.left, L, R); - root.right = trimBST(root.right, L, R); - return root; -} -``` - -## 寻找二叉查找树的第 k 个元素 - -[230. Kth Smallest Element in a BST (Medium)](https://leetcode.com/problems/kth-smallest-element-in-a-bst/description/) - - -中序遍历解法: - -```java -private int cnt = 0; -private int val; - -public int kthSmallest(TreeNode root, int k) { - inOrder(root, k); - return val; -} - -private void inOrder(TreeNode node, int k) { - if (node == null) return; - inOrder(node.left, k); - cnt++; - if (cnt == k) { - val = node.val; - return; - } - inOrder(node.right, k); -} -``` - -递归解法: - -```java -public int kthSmallest(TreeNode root, int k) { - int leftCnt = count(root.left); - if (leftCnt == k - 1) return root.val; - if (leftCnt > k - 1) return kthSmallest(root.left, k); - return kthSmallest(root.right, k - leftCnt - 1); -} - -private int count(TreeNode node) { - if (node == null) return 0; - return 1 + count(node.left) + count(node.right); -} -``` - -## 把二叉查找树每个节点的值都加上比它大的节点的值 - -[Convert BST to Greater Tree (Easy)](https://leetcode.com/problems/convert-bst-to-greater-tree/description/) - -```html -Input: The root of a Binary Search Tree like this: - - 5 - / \ - 2 13 - -Output: The root of a Greater Tree like this: - - 18 - / \ - 20 13 -``` - -先遍历右子树。 - -```java -private int sum = 0; - -public TreeNode convertBST(TreeNode root) { - traver(root); - return root; -} - -private void traver(TreeNode node) { - if (node == null) return; - traver(node.right); - sum += node.val; - node.val = sum; - traver(node.left); -} -``` - -## 二叉查找树的最近公共祖先 - -[235. Lowest Common Ancestor of a Binary Search Tree (Easy)](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/) - -```html - _______6______ - / \ - ___2__ ___8__ - / \ / \ -0 4 7 9 - / \ - 3 5 - -For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. -``` - -```java -public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { - 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; -} -``` - -## 二叉树的最近公共祖先 - -[236. Lowest Common Ancestor of a Binary Tree (Medium) ](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/) - -```html - _______3______ - / \ - ___5__ ___1__ - / \ / \ -6 2 0 8 - / \ - 7 4 - -For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition. -``` - -```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; -} -``` - -## 从有序数组中构造二叉查找树 - -[108. Convert Sorted Array to Binary Search Tree (Easy)](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/) - -```java -public TreeNode sortedArrayToBST(int[] nums) { - return toBST(nums, 0, nums.length - 1); -} - -private TreeNode toBST(int[] nums, int sIdx, int eIdx){ - if (sIdx > eIdx) return null; - int mIdx = (sIdx + eIdx) / 2; - TreeNode root = new TreeNode(nums[mIdx]); - root.left = toBST(nums, sIdx, mIdx - 1); - root.right = toBST(nums, mIdx + 1, eIdx); - return root; -} -``` - -## 根据有序链表构造平衡的二叉查找树 - -[109. Convert Sorted List to Binary Search Tree (Medium)](https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/description/) - -```html -Given the sorted linked list: [-10,-3,0,5,9], - -One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: - - 0 - / \ - -3 9 - / / - -10 5 -``` - -```java -public TreeNode sortedListToBST(ListNode head) { - if (head == null) return null; - if (head.next == null) return new TreeNode(head.val); - ListNode preMid = preMid(head); - ListNode mid = preMid.next; - preMid.next = null; // 断开链表 - TreeNode t = new TreeNode(mid.val); - t.left = sortedListToBST(head); - t.right = sortedListToBST(mid.next); - return t; -} - -private ListNode preMid(ListNode head) { - ListNode slow = head, fast = head.next; - ListNode pre = head; - while (fast != null && fast.next != null) { - pre = slow; - slow = slow.next; - fast = fast.next.next; - } - return pre; -} -``` - -## 在二叉查找树中寻找两个节点,使它们的和为一个给定值 - -[653. Two Sum IV - Input is a BST (Easy)](https://leetcode.com/problems/two-sum-iv-input-is-a-bst/description/) - -```html -Input: - - 5 - / \ - 3 6 - / \ \ -2 4 7 - -Target = 9 - -Output: True -``` - -使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。 - -应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。 - -```java -public boolean findTarget(TreeNode root, int k) { - List nums = new ArrayList<>(); - inOrder(root, nums); - int i = 0, j = nums.size() - 1; - while (i < j) { - int sum = nums.get(i) + nums.get(j); - if (sum == k) return true; - if (sum < k) i++; - else j--; - } - return false; -} - -private void inOrder(TreeNode root, List nums) { - if (root == null) return; - inOrder(root.left, nums); - nums.add(root.val); - inOrder(root.right, nums); -} -``` - -## 在二叉查找树中查找两个节点之差的最小绝对值 - -[530. Minimum Absolute Difference in BST (Easy)](https://leetcode.com/problems/minimum-absolute-difference-in-bst/description/) - -```html -Input: - - 1 - \ - 3 - / - 2 - -Output: - -1 -``` - -利用二叉查找树的中序遍历为有序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。 - -```java -private int minDiff = Integer.MAX_VALUE; -private TreeNode preNode = null; - -public int getMinimumDifference(TreeNode root) { - inOrder(root); - return minDiff; -} - -private void inOrder(TreeNode node) { - if (node == null) return; - inOrder(node.left); - if (preNode != null) minDiff = Math.min(minDiff, node.val - preNode.val); - preNode = node; - inOrder(node.right); -} -``` - -## 寻找二叉查找树中出现次数最多的值 - -[501. Find Mode in Binary Search Tree (Easy)](https://leetcode.com/problems/find-mode-in-binary-search-tree/description/) - -```html - 1 - \ - 2 - / - 2 - -return [2]. -``` - -答案可能不止一个,也就是有多个值出现的次数一样多。 - -```java -private int curCnt = 1; -private int maxCnt = 1; -private TreeNode preNode = null; - -public int[] findMode(TreeNode root) { - List maxCntNums = new ArrayList<>(); - inOrder(root, maxCntNums); - int[] ret = new int[maxCntNums.size()]; - int idx = 0; - for (int num : maxCntNums) { - ret[idx++] = num; - } - return ret; -} - -private void inOrder(TreeNode node, List nums) { - if (node == null) return; - inOrder(node.left, nums); - if (preNode != null) { - if (preNode.val == node.val) curCnt++; - else curCnt = 1; - } - if (curCnt > maxCnt) { - maxCnt = curCnt; - nums.clear(); - nums.add(node.val); - } else if (curCnt == maxCnt) { - nums.add(node.val); - } - preNode = node; - inOrder(node.right, nums); -} -``` - -# Trie - -

- -Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 - -## 实现一个 Trie - -[208. Implement Trie (Prefix Tree) (Medium)](https://leetcode.com/problems/implement-trie-prefix-tree/description/) - -```java -class Trie { - - private class Node { - Node[] childs = new Node[26]; - boolean isLeaf; - } - - private Node root = new Node(); - - public Trie() { - } - - public void insert(String word) { - insert(word, root); - } - - private void insert(String word, Node node) { - if (node == null) return; - if (word.length() == 0) { - node.isLeaf = true; - return; - } - int index = indexForChar(word.charAt(0)); - if (node.childs[index] == null) { - node.childs[index] = new Node(); - } - insert(word.substring(1), node.childs[index]); - } - - public boolean search(String word) { - return search(word, root); - } - - private boolean search(String word, Node node) { - if (node == null) return false; - if (word.length() == 0) return node.isLeaf; - int index = indexForChar(word.charAt(0)); - return search(word.substring(1), node.childs[index]); - } - - public boolean startsWith(String prefix) { - return startWith(prefix, root); - } - - private boolean startWith(String prefix, Node node) { - if (node == null) return false; - if (prefix.length() == 0) return true; - int index = indexForChar(prefix.charAt(0)); - return startWith(prefix.substring(1), node.childs[index]); - } - - private int indexForChar(char c) { - return c - 'a'; - } -} -``` - -## 实现一个 Trie,用来求前缀和 - -[677. Map Sum Pairs (Medium)](https://leetcode.com/problems/map-sum-pairs/description/) - -```html -Input: insert("apple", 3), Output: Null -Input: sum("ap"), Output: 3 -Input: insert("app", 2), Output: Null -Input: sum("ap"), Output: 5 -``` - -```java -class MapSum { - - private class Node { - Node[] child = new Node[26]; - int value; - } - - private Node root = new Node(); - - public MapSum() { - - } - - public void insert(String key, int val) { - insert(key, root, val); - } - - private void insert(String key, Node node, int val) { - if (node == null) return; - if (key.length() == 0) { - node.value = val; - return; - } - int index = indexForChar(key.charAt(0)); - if (node.child[index] == null) { - node.child[index] = new Node(); - } - insert(key.substring(1), node.child[index], val); - } - - public int sum(String prefix) { - return sum(prefix, root); - } - - private int sum(String prefix, Node node) { - if (node == null) return 0; - if (prefix.length() != 0) { - int index = indexForChar(prefix.charAt(0)); - return sum(prefix.substring(1), node.child[index]); - } - int sum = node.value; - for (Node child : node.child) { - sum += sum(prefix, child); - } - return sum; - } - - private int indexForChar(char c) { - return c - 'a'; - } -} -``` - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" deleted file mode 100644 index 22764bc6af..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" +++ /dev/null @@ -1,44 +0,0 @@ - -* [算法思想](#算法思想) -* [数据结构相关](#数据结构相关) -* [参考资料](#参考资料) - - - -# 算法思想 - -- [双指针](Leetcode%20题解%20-%20双指针.md) -- [排序](Leetcode%20题解%20-%20排序.md) -- [贪心思想](Leetcode%20题解%20-%20贪心思想.md) -- [二分查找](Leetcode%20题解%20-%20二分查找.md) -- [分治](Leetcode%20题解%20-%20分治.md) -- [搜索](Leetcode%20题解%20-%20搜索.md) -- [动态规划](Leetcode%20题解%20-%20动态规划.md) -- [数学](Leetcode%20题解%20-%20数学.md) - -# 数据结构相关 - -- [链表](Leetcode%20题解%20-%20链表.md) -- [树](Leetcode%20题解%20-%20树.md) -- [栈和队列](Leetcode%20题解%20-%20栈和队列.md) -- [哈希表](Leetcode%20题解%20-%20哈希表.md) -- [字符串](Leetcode%20题解%20-%20字符串.md) -- [数组与矩阵](Leetcode%20题解%20-%20数组与矩阵.md) -- [图](Leetcode%20题解%20-%20图.md) -- [位运算](Leetcode%20题解%20-%20位运算.md) - -# 参考资料 - - -- Leetcode -- Weiss M A, 冯舜玺. 数据结构与算法分析——C 语言描述[J]. 2004. -- Sedgewick R. Algorithms[M]. Pearson Education India, 1988. -- 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014. -- 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008. -- 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" deleted file mode 100644 index 80e84238cf..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" +++ /dev/null @@ -1,44 +0,0 @@ - -* [算法思想](#算法思想) -* [数据结构相关](#数据结构相关) -* [参考资料](#参考资料) - - - -# 算法思想 - -- [双指针](notes/Leetcode%20题解%20-%20双指针.md) -- [排序](notes/Leetcode%20题解%20-%20排序.md) -- [贪心思想](notes/Leetcode%20题解%20-%20贪心思想.md) -- [二分查找](notes/Leetcode%20题解%20-%20二分查找.md) -- [分治](notes/Leetcode%20题解%20-%20分治.md) -- [搜索](notes/Leetcode%20题解%20-%20搜索.md) -- [动态规划](notes/Leetcode%20题解%20-%20动态规划.md) -- [数学](notes/Leetcode%20题解%20-%20数学.md) - -# 数据结构相关 - -- [链表](notes/Leetcode%20题解%20-%20链表.md) -- [树](notes/Leetcode%20题解%20-%20树.md) -- [栈和队列](notes/Leetcode%20题解%20-%20栈和队列.md) -- [哈希表](notes/Leetcode%20题解%20-%20哈希表.md) -- [字符串](notes/Leetcode%20题解%20-%20字符串.md) -- [数组与矩阵](notes/Leetcode%20题解%20-%20数组与矩阵.md) -- [图](notes/Leetcode%20题解%20-%20图.md) -- [位运算](notes/Leetcode%20题解%20-%20位运算.md) - -# 参考资料 - - -- Leetcode -- Weiss M A, 冯舜玺. 数据结构与算法分析——C 语言描述[J]. 2004. -- Sedgewick R. Algorithms[M]. Pearson Education India, 1988. -- 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014. -- 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008. -- 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" deleted file mode 100644 index 6249fdbe03..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" +++ /dev/null @@ -1,372 +0,0 @@ - -* [分配饼干](#分配饼干) -* [不重叠的区间个数](#不重叠的区间个数) -* [投飞镖刺破气球](#投飞镖刺破气球) -* [根据身高和序号重组队列](#根据身高和序号重组队列) -* [分隔字符串使同种字符出现在一起](#分隔字符串使同种字符出现在一起) -* [种植花朵](#种植花朵) -* [判断是否为子序列](#判断是否为子序列) -* [修改一个数成为非递减数组](#修改一个数成为非递减数组) -* [股票的最大收益](#股票的最大收益) -* [子数组最大的和](#子数组最大的和) -* [买入和售出股票最大的收益](#买入和售出股票最大的收益) - - - -保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。 - -# 分配饼干 - -[455. Assign Cookies (Easy)](https://leetcode.com/problems/assign-cookies/description/) - -```html -Input: [1,2], [1,2,3] -Output: 2 - -Explanation: You have 2 children and 3 cookies. The greed factors of 2 children are 1, 2. -You have 3 cookies and their sizes are big enough to gratify all of the children, -You need to output 2. -``` - -题目描述:每个孩子都有一个满足度,每个饼干都有一个大小,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。 - -给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。因为最小的孩子最容易得到满足,所以先满足最小的孩子。 - -证明:假设在某次选择中,贪心策略选择给当前满足度最小的孩子分配第 m 个饼干,第 m 个饼干为可以满足该孩子的最小饼干。假设存在一种最优策略,给该孩子分配第 n 个饼干,并且 m < n。我们可以发现,经过这一轮分配,贪心策略分配后剩下的饼干一定有一个比最优策略来得大。因此在后续的分配中,贪心策略一定能满足更多的孩子。也就是说不存在比贪心策略更优的策略,即贪心策略就是最优策略。 - -```java -public int findContentChildren(int[] g, int[] s) { - Arrays.sort(g); - Arrays.sort(s); - int gi = 0, si = 0; - while (gi < g.length && si < s.length) { - if (g[gi] <= s[si]) { - gi++; - } - si++; - } - return gi; -} -``` - -# 不重叠的区间个数 - -[435. Non-overlapping Intervals (Medium)](https://leetcode.com/problems/non-overlapping-intervals/description/) - -```html -Input: [ [1,2], [1,2], [1,2] ] - -Output: 2 - -Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping. -``` - -```html -Input: [ [1,2], [2,3] ] - -Output: 0 - -Explanation: You don't need to remove any of the intervals since they're already non-overlapping. -``` - -题目描述:计算让一组区间不重叠所需要移除的区间个数。 - -先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。 - -在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。 - -按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。 - -```java -public int eraseOverlapIntervals(Interval[] intervals) { - if (intervals.length == 0) { - return 0; - } - Arrays.sort(intervals, Comparator.comparingInt(o -> o.end)); - int cnt = 1; - int end = intervals[0].end; - for (int i = 1; i < intervals.length; i++) { - if (intervals[i].start < end) { - continue; - } - end = intervals[i].end; - cnt++; - } - return intervals.length - cnt; -} -``` - -使用 lambda 表示式创建 Comparator 会导致算法运行时间过长,如果注重运行时间,可以修改为普通创建 Comparator 语句: - -```java -Arrays.sort(intervals, new Comparator() { - @Override - public int compare(Interval o1, Interval o2) { - return o1.end - o2.end; - } -}); -``` - -# 投飞镖刺破气球 - -[452. Minimum Number of Arrows to Burst Balloons (Medium)](https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/description/) - -``` -Input: -[[10,16], [2,8], [1,6], [7,12]] - -Output: -2 -``` - -题目描述:气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都会刺破。求解最小的投飞镖次数使所有气球都被刺破。 - -也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。 - -```java -public int findMinArrowShots(int[][] points) { - if (points.length == 0) { - return 0; - } - Arrays.sort(points, Comparator.comparingInt(o -> o[1])); - int cnt = 1, end = points[0][1]; - for (int i = 1; i < points.length; i++) { - if (points[i][0] <= end) { - continue; - } - cnt++; - end = points[i][1]; - } - return cnt; -} -``` - -# 根据身高和序号重组队列 - -[406. Queue Reconstruction by Height(Medium)](https://leetcode.com/problems/queue-reconstruction-by-height/description/) - -```html -Input: -[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] - -Output: -[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] -``` - -题目描述:一个学生用两个分量 (h, k) 描述,h 表示身高,k 表示排在前面的有 k 个学生的身高比他高或者和他一样高。 - -为了使插入操作不影响后续的操作,身高较高的学生应该先做插入操作,否则身高较小的学生原先正确插入的第 k 个位置可能会变成第 k+1 个位置。 - -身高降序、k 值升序,然后按排好序的顺序插入队列的第 k 个位置中。 - -```java -public int[][] reconstructQueue(int[][] people) { - if (people == null || people.length == 0 || people[0].length == 0) { - return new int[0][0]; - } - Arrays.sort(people, (a, b) -> (a[0] == b[0] ? a[1] - b[1] : b[0] - a[0])); - List queue = new ArrayList<>(); - for (int[] p : people) { - queue.add(p[1], p); - } - return queue.toArray(new int[queue.size()][]); -} -``` - -# 分隔字符串使同种字符出现在一起 - -[763. Partition Labels (Medium)](https://leetcode.com/problems/partition-labels/description/) - -```html -Input: S = "ababcbacadefegdehijhklij" -Output: [9,7,8] -Explanation: -The partition is "ababcbaca", "defegde", "hijhklij". -This is a partition so that each letter appears in at most one part. -A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. -``` - -```java -public List partitionLabels(String S) { - int[] lastIndexsOfChar = new int[26]; - for (int i = 0; i < S.length(); i++) { - lastIndexsOfChar[char2Index(S.charAt(i))] = i; - } - List partitions = new ArrayList<>(); - int firstIndex = 0; - while (firstIndex < S.length()) { - int lastIndex = firstIndex; - for (int i = firstIndex; i < S.length() && i <= lastIndex; i++) { - int index = lastIndexsOfChar[char2Index(S.charAt(i))]; - if (index > lastIndex) { - lastIndex = index; - } - } - partitions.add(lastIndex - firstIndex + 1); - firstIndex = lastIndex + 1; - } - return partitions; -} - -private int char2Index(char c) { - return c - 'a'; -} -``` - - -# 种植花朵 - -[605. Can Place Flowers (Easy)](https://leetcode.com/problems/can-place-flowers/description/) - -```html -Input: flowerbed = [1,0,0,0,1], n = 1 -Output: True -``` - -题目描述:花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。 - -```java -public boolean canPlaceFlowers(int[] flowerbed, int n) { - int len = flowerbed.length; - int cnt = 0; - for (int i = 0; i < len && cnt < n; i++) { - if (flowerbed[i] == 1) { - continue; - } - int pre = i == 0 ? 0 : flowerbed[i - 1]; - int next = i == len - 1 ? 0 : flowerbed[i + 1]; - if (pre == 0 && next == 0) { - cnt++; - flowerbed[i] = 1; - } - } - return cnt >= n; -} -``` - -# 判断是否为子序列 - -[392. Is Subsequence (Medium)](https://leetcode.com/problems/is-subsequence/description/) - -```html -s = "abc", t = "ahbgdc" -Return true. -``` - -```java -public boolean isSubsequence(String s, String t) { - int index = -1; - for (char c : s.toCharArray()) { - index = t.indexOf(c, index + 1); - if (index == -1) { - return false; - } - } - return true; -} -``` - -# 修改一个数成为非递减数组 - -[665. Non-decreasing Array (Easy)](https://leetcode.com/problems/non-decreasing-array/description/) - -```html -Input: [4,2,3] -Output: True -Explanation: You could modify the first 4 to 1 to get a non-decreasing array. -``` - -题目描述:判断一个数组能不能只修改一个数就成为非递减数组。 - -在出现 nums[i] < nums[i - 1] 时,需要考虑的是应该修改数组的哪个数,使得本次修改能使 i 之前的数组成为非递减数组,并且 **不影响后续的操作** 。优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。还有一个比较特别的情况就是 nums[i] < nums[i - 2],只修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]。 - -```java -public boolean checkPossibility(int[] nums) { - int cnt = 0; - for (int i = 1; i < nums.length && cnt < 2; i++) { - if (nums[i] >= nums[i - 1]) { - continue; - } - cnt++; - if (i - 2 >= 0 && nums[i - 2] > nums[i]) { - nums[i] = nums[i - 1]; - } else { - nums[i - 1] = nums[i]; - } - } - return cnt <= 1; -} -``` - -# 股票的最大收益 - -[122. Best Time to Buy and Sell Stock II (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/) - -题目描述:一次股票交易包含买入和卖出,多个交易之间不能交叉进行。 - -对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加到收益中,从而在局部最优的情况下也保证全局最优。 - -```java -public int maxProfit(int[] prices) { - int profit = 0; - for (int i = 1; i < prices.length; i++) { - if (prices[i] > prices[i - 1]) { - profit += (prices[i] - prices[i - 1]); - } - } - return profit; -} -``` - -# 子数组最大的和 - -[53. Maximum Subarray (Easy)](https://leetcode.com/problems/maximum-subarray/description/) - -```html -For example, given the array [-2,1,-3,4,-1,2,1,-5,4], -the contiguous subarray [4,-1,2,1] has the largest sum = 6. -``` - -```java -public int maxSubArray(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - int preSum = nums[0]; - int maxSum = preSum; - for (int i = 1; i < nums.length; i++) { - preSum = preSum > 0 ? preSum + nums[i] : nums[i]; - maxSum = Math.max(maxSum, preSum); - } - return maxSum; -} -``` - -# 买入和售出股票最大的收益 - -[121. Best Time to Buy and Sell Stock (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/) - -题目描述:只进行一次交易。 - -只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。 - -```java -public int maxProfit(int[] prices) { - int n = prices.length; - if (n == 0) return 0; - int soFarMin = prices[0]; - int max = 0; - for (int i = 1; i < n; i++) { - if (soFarMin > prices[i]) soFarMin = prices[i]; - else max = Math.max(max, prices[i] - soFarMin); - } - return max; -} -``` - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" "b/docs/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" deleted file mode 100644 index 67de7e6441..0000000000 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" +++ /dev/null @@ -1,333 +0,0 @@ - -* [找出两个链表的交点](#找出两个链表的交点) -* [链表反转](#链表反转) -* [归并两个有序的链表](#归并两个有序的链表) -* [从有序链表中删除重复节点](#从有序链表中删除重复节点) -* [删除链表的倒数第 n 个节点](#删除链表的倒数第-n-个节点) -* [交换链表中的相邻结点](#交换链表中的相邻结点) -* [链表求和](#链表求和) -* [回文链表](#回文链表) -* [分隔链表](#分隔链表) -* [链表元素按奇偶聚集](#链表元素按奇偶聚集) - - - -链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。 - -# 找出两个链表的交点 - -[160. Intersection of Two Linked Lists (Easy)](https://leetcode.com/problems/intersection-of-two-linked-lists/description/) - -```html -A: a1 → a2 - ↘ - c1 → c2 → c3 - ↗ -B: b1 → b2 → b3 -``` - -要求:时间复杂度为 O(N),空间复杂度为 O(1) - -设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 - -当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 - -```java -public ListNode getIntersectionNode(ListNode headA, ListNode headB) { - ListNode l1 = headA, l2 = headB; - while (l1 != l2) { - l1 = (l1 == null) ? headB : l1.next; - l2 = (l2 == null) ? headA : l2.next; - } - return l1; -} -``` - -如果只是判断是否存在交点,那么就是另一个问题,即 [编程之美 3.6]() 的问题。有两种解法: - -- 把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环; -- 或者直接比较两个链表的最后一个节点是否相同。 - -# 链表反转 - -[206. Reverse Linked List (Easy)](https://leetcode.com/problems/reverse-linked-list/description/) - -递归 - -```java -public ListNode reverseList(ListNode head) { - if (head == null || head.next == null) { - return head; - } - ListNode next = head.next; - ListNode newHead = reverseList(next); - next.next = head; - head.next = null; - return newHead; -} -``` - -头插法 - -```java -public ListNode reverseList(ListNode head) { - ListNode newHead = new ListNode(-1); - while (head != null) { - ListNode next = head.next; - head.next = newHead.next; - newHead.next = head; - head = next; - } - return newHead.next; -} -``` - -# 归并两个有序的链表 - -[21. Merge Two Sorted Lists (Easy)](https://leetcode.com/problems/merge-two-sorted-lists/description/) - -```java -public ListNode mergeTwoLists(ListNode l1, ListNode l2) { - if (l1 == null) return l2; - if (l2 == null) return l1; - if (l1.val < l2.val) { - l1.next = mergeTwoLists(l1.next, l2); - return l1; - } else { - l2.next = mergeTwoLists(l1, l2.next); - return l2; - } -} -``` - -# 从有序链表中删除重复节点 - -[83. Remove Duplicates from Sorted List (Easy)](https://leetcode.com/problems/remove-duplicates-from-sorted-list/description/) - -```html -Given 1->1->2, return 1->2. -Given 1->1->2->3->3, return 1->2->3. -``` - -```java -public ListNode deleteDuplicates(ListNode head) { - if (head == null || head.next == null) return head; - head.next = deleteDuplicates(head.next); - return head.val == head.next.val ? head.next : head; -} -``` - -# 删除链表的倒数第 n 个节点 - -[19. Remove Nth Node From End of List (Medium)](https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/) - -```html -Given linked list: 1->2->3->4->5, and n = 2. -After removing the second node from the end, the linked list becomes 1->2->3->5. -``` - -```java -public ListNode removeNthFromEnd(ListNode head, int n) { - ListNode fast = head; - while (n-- > 0) { - fast = fast.next; - } - if (fast == null) return head.next; - ListNode slow = head; - while (fast.next != null) { - fast = fast.next; - slow = slow.next; - } - slow.next = slow.next.next; - return head; -} -``` - -# 交换链表中的相邻结点 - -[24. Swap Nodes in Pairs (Medium)](https://leetcode.com/problems/swap-nodes-in-pairs/description/) - -```html -Given 1->2->3->4, you should return the list as 2->1->4->3. -``` - -题目要求:不能修改结点的 val 值,O(1) 空间复杂度。 - -```java -public ListNode swapPairs(ListNode head) { - ListNode node = new ListNode(-1); - node.next = head; - ListNode pre = node; - while (pre.next != null && pre.next.next != null) { - ListNode l1 = pre.next, l2 = pre.next.next; - ListNode next = l2.next; - l1.next = next; - l2.next = l1; - pre.next = l2; - - pre = l1; - } - return node.next; -} -``` - -# 链表求和 - -[445. Add Two Numbers II (Medium)](https://leetcode.com/problems/add-two-numbers-ii/description/) - -```html -Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) -Output: 7 -> 8 -> 0 -> 7 -``` - -题目要求:不能修改原始链表。 - -```java -public ListNode addTwoNumbers(ListNode l1, ListNode l2) { - Stack l1Stack = buildStack(l1); - Stack l2Stack = buildStack(l2); - ListNode head = new ListNode(-1); - int carry = 0; - while (!l1Stack.isEmpty() || !l2Stack.isEmpty() || carry != 0) { - int x = l1Stack.isEmpty() ? 0 : l1Stack.pop(); - int y = l2Stack.isEmpty() ? 0 : l2Stack.pop(); - int sum = x + y + carry; - ListNode node = new ListNode(sum % 10); - node.next = head.next; - head.next = node; - carry = sum / 10; - } - return head.next; -} - -private Stack buildStack(ListNode l) { - Stack stack = new Stack<>(); - while (l != null) { - stack.push(l.val); - l = l.next; - } - return stack; -} -``` - -# 回文链表 - -[234. Palindrome Linked List (Easy)](https://leetcode.com/problems/palindrome-linked-list/description/) - -题目要求:以 O(1) 的空间复杂度来求解。 - -切成两半,把后半段反转,然后比较两半是否相等。 - -```java -public boolean isPalindrome(ListNode head) { - if (head == null || head.next == null) return true; - ListNode slow = head, fast = head.next; - while (fast != null && fast.next != null) { - slow = slow.next; - fast = fast.next.next; - } - if (fast != null) slow = slow.next; // 偶数节点,让 slow 指向下一个节点 - cut(head, slow); // 切成两个链表 - return isEqual(head, reverse(slow)); -} - -private void cut(ListNode head, ListNode cutNode) { - while (head.next != cutNode) { - head = head.next; - } - head.next = null; -} - -private ListNode reverse(ListNode head) { - ListNode newHead = null; - while (head != null) { - ListNode nextNode = head.next; - head.next = newHead; - newHead = head; - head = nextNode; - } - return newHead; -} - -private boolean isEqual(ListNode l1, ListNode l2) { - while (l1 != null && l2 != null) { - if (l1.val != l2.val) return false; - l1 = l1.next; - l2 = l2.next; - } - return true; -} -``` - -# 分隔链表 - -[725. Split Linked List in Parts(Medium)](https://leetcode.com/problems/split-linked-list-in-parts/description/) - -```html -Input: -root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3 -Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] -Explanation: -The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts. -``` - -题目描述:把链表分隔成 k 部分,每部分的长度都应该尽可能相同,排在前面的长度应该大于等于后面的。 - -```java -public ListNode[] splitListToParts(ListNode root, int k) { - int N = 0; - ListNode cur = root; - while (cur != null) { - N++; - cur = cur.next; - } - int mod = N % k; - int size = N / k; - ListNode[] ret = new ListNode[k]; - cur = root; - for (int i = 0; cur != null && i < k; i++) { - ret[i] = cur; - int curSize = size + (mod-- > 0 ? 1 : 0); - for (int j = 0; j < curSize - 1; j++) { - cur = cur.next; - } - ListNode next = cur.next; - cur.next = null; - cur = next; - } - return ret; -} -``` - -# 链表元素按奇偶聚集 - -[328. Odd Even Linked List (Medium)](https://leetcode.com/problems/odd-even-linked-list/description/) - -```html -Example: -Given 1->2->3->4->5->NULL, -return 1->3->5->2->4->NULL. -``` - -```java -public ListNode oddEvenList(ListNode head) { - if (head == null) { - return head; - } - ListNode odd = head, even = head.next, evenHead = even; - while (even != null && even.next != null) { - odd.next = odd.next.next; - odd = odd.next; - even.next = even.next.next; - even = even.next; - } - odd.next = evenHead; - return head; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Leetcode-Database \351\242\230\350\247\243.md" "b/docs/notes/Leetcode-Database \351\242\230\350\247\243.md" deleted file mode 100644 index 4fcf7ff71c..0000000000 --- "a/docs/notes/Leetcode-Database \351\242\230\350\247\243.md" +++ /dev/null @@ -1,956 +0,0 @@ - -* [595. Big Countries](#595-big-countries) -* [627. Swap Salary](#627-swap-salary) -* [620. Not Boring Movies](#620-not-boring-movies) -* [596. Classes More Than 5 Students](#596-classes-more-than-5-students) -* [182. Duplicate Emails](#182-duplicate-emails) -* [196. Delete Duplicate Emails](#196-delete-duplicate-emails) -* [175. Combine Two Tables](#175-combine-two-tables) -* [181. Employees Earning More Than Their Managers](#181-employees-earning-more-than-their-managers) -* [183. Customers Who Never Order](#183-customers-who-never-order) -* [184. Department Highest Salary](#184-department-highest-salary) -* [176. Second Highest Salary](#176-second-highest-salary) -* [177. Nth Highest Salary](#177-nth-highest-salary) -* [178. Rank Scores](#178-rank-scores) -* [180. Consecutive Numbers](#180-consecutive-numbers) -* [626. Exchange Seats](#626-exchange-seats) - - - -# 595. Big Countries - -https://leetcode.com/problems/big-countries/description/ - -## Description - -```html -+-----------------+------------+------------+--------------+---------------+ -| name | continent | area | population | gdp | -+-----------------+------------+------------+--------------+---------------+ -| Afghanistan | Asia | 652230 | 25500100 | 20343000 | -| Albania | Europe | 28748 | 2831741 | 12960000 | -| Algeria | Africa | 2381741 | 37100000 | 188681000 | -| Andorra | Europe | 468 | 78115 | 3712000 | -| Angola | Africa | 1246700 | 20609294 | 100990000 | -+-----------------+------------+------------+--------------+---------------+ -``` - -查找面积超过 3,000,000 或者人口数超过 25,000,000 的国家。 - -```html -+--------------+-------------+--------------+ -| name | population | area | -+--------------+-------------+--------------+ -| Afghanistan | 25500100 | 652230 | -| Algeria | 37100000 | 2381741 | -+--------------+-------------+--------------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS World; -CREATE TABLE World ( NAME VARCHAR ( 255 ), continent VARCHAR ( 255 ), area INT, population INT, gdp INT ); -INSERT INTO World ( NAME, continent, area, population, gdp ) -VALUES - ( 'Afghanistan', 'Asia', '652230', '25500100', '203430000' ), - ( 'Albania', 'Europe', '28748', '2831741', '129600000' ), - ( 'Algeria', 'Africa', '2381741', '37100000', '1886810000' ), - ( 'Andorra', 'Europe', '468', '78115', '37120000' ), - ( 'Angola', 'Africa', '1246700', '20609294', '1009900000' ); -``` - -## Solution - -```sql -SELECT name, - population, - area -FROM - World -WHERE - area > 3000000 - OR population > 25000000; -``` - -# 627. Swap Salary - -https://leetcode.com/problems/swap-salary/description/ - -## Description - -```html -| id | name | sex | salary | -|----|------|-----|--------| -| 1 | A | m | 2500 | -| 2 | B | f | 1500 | -| 3 | C | m | 5500 | -| 4 | D | f | 500 | -``` - -只用一个 SQL 查询,将 sex 字段反转。 - -```html -| id | name | sex | salary | -|----|------|-----|--------| -| 1 | A | f | 2500 | -| 2 | B | m | 1500 | -| 3 | C | f | 5500 | -| 4 | D | m | 500 | -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS salary; -CREATE TABLE salary ( id INT, NAME VARCHAR ( 100 ), sex CHAR ( 1 ), salary INT ); -INSERT INTO salary ( id, NAME, sex, salary ) -VALUES - ( '1', 'A', 'm', '2500' ), - ( '2', 'B', 'f', '1500' ), - ( '3', 'C', 'm', '5500' ), - ( '4', 'D', 'f', '500' ); -``` - -## Solution - -```sql -UPDATE salary -SET sex = CHAR ( ASCII(sex) ^ ASCII( 'm' ) ^ ASCII( 'f' ) ); -``` - -# 620. Not Boring Movies - -https://leetcode.com/problems/not-boring-movies/description/ - -## Description - - -```html -+---------+-----------+--------------+-----------+ -| id | movie | description | rating | -+---------+-----------+--------------+-----------+ -| 1 | War | great 3D | 8.9 | -| 2 | Science | fiction | 8.5 | -| 3 | irish | boring | 6.2 | -| 4 | Ice song | Fantacy | 8.6 | -| 5 | House card| Interesting| 9.1 | -+---------+-----------+--------------+-----------+ -``` - -查找 id 为奇数,并且 description 不是 boring 的电影,按 rating 降序。 - -```html -+---------+-----------+--------------+-----------+ -| id | movie | description | rating | -+---------+-----------+--------------+-----------+ -| 5 | House card| Interesting| 9.1 | -| 1 | War | great 3D | 8.9 | -+---------+-----------+--------------+-----------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS cinema; -CREATE TABLE cinema ( id INT, movie VARCHAR ( 255 ), description VARCHAR ( 255 ), rating FLOAT ( 2, 1 ) ); -INSERT INTO cinema ( id, movie, description, rating ) -VALUES - ( 1, 'War', 'great 3D', 8.9 ), - ( 2, 'Science', 'fiction', 8.5 ), - ( 3, 'irish', 'boring', 6.2 ), - ( 4, 'Ice song', 'Fantacy', 8.6 ), - ( 5, 'House card', 'Interesting', 9.1 ); -``` - -## Solution - -```sql -SELECT - * -FROM - cinema -WHERE - id % 2 = 1 - AND description != 'boring' -ORDER BY - rating DESC; -``` - -# 596. Classes More Than 5 Students - -https://leetcode.com/problems/classes-more-than-5-students/description/ - -## Description - -```html -+---------+------------+ -| student | class | -+---------+------------+ -| A | Math | -| B | English | -| C | Math | -| D | Biology | -| E | Math | -| F | Computer | -| G | Math | -| H | Math | -| I | Math | -+---------+------------+ -``` - -查找有五名及以上 student 的 class。 - -```html -+---------+ -| class | -+---------+ -| Math | -+---------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS courses; -CREATE TABLE courses ( student VARCHAR ( 255 ), class VARCHAR ( 255 ) ); -INSERT INTO courses ( student, class ) -VALUES - ( 'A', 'Math' ), - ( 'B', 'English' ), - ( 'C', 'Math' ), - ( 'D', 'Biology' ), - ( 'E', 'Math' ), - ( 'F', 'Computer' ), - ( 'G', 'Math' ), - ( 'H', 'Math' ), - ( 'I', 'Math' ); -``` - -## Solution - -```sql -SELECT - class -FROM - courses -GROUP BY - class -HAVING - count( DISTINCT student ) >= 5; -``` - -# 182. Duplicate Emails - -https://leetcode.com/problems/duplicate-emails/description/ - -## Description - -邮件地址表: - -```html -+----+---------+ -| Id | Email | -+----+---------+ -| 1 | a@b.com | -| 2 | c@d.com | -| 3 | a@b.com | -+----+---------+ -``` - -查找重复的邮件地址: - -```html -+---------+ -| Email | -+---------+ -| a@b.com | -+---------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Person; -CREATE TABLE Person ( Id INT, Email VARCHAR ( 255 ) ); -INSERT INTO Person ( Id, Email ) -VALUES - ( 1, 'a@b.com' ), - ( 2, 'c@d.com' ), - ( 3, 'a@b.com' ); -``` - -## Solution - -```sql -SELECT - Email -FROM - Person -GROUP BY - Email -HAVING - COUNT( * ) >= 2; -``` - -# 196. Delete Duplicate Emails - -https://leetcode.com/problems/delete-duplicate-emails/description/ - -## Description - -邮件地址表: - -```html -+----+---------+ -| Id | Email | -+----+---------+ -| 1 | a@b.com | -| 2 | c@d.com | -| 3 | a@b.com | -+----+---------+ -``` - -删除重复的邮件地址: - -```html -+----+------------------+ -| Id | Email | -+----+------------------+ -| 1 | john@example.com | -| 2 | bob@example.com | -+----+------------------+ -``` - -## SQL Schema - -与 182 相同。 - -## Solution - -连接: - -```sql -DELETE p1 -FROM - Person p1, - Person p2 -WHERE - p1.Email = p2.Email - AND p1.Id > p2.Id -``` - -子查询: - -```sql -DELETE -FROM - Person -WHERE - id NOT IN ( SELECT id FROM ( SELECT min( id ) AS id FROM Person GROUP BY email ) AS m ); -``` - -应该注意的是上述解法额外嵌套了一个 SELECT 语句,如果不这么做,会出现错误:You can't specify target table 'Person' for update in FROM clause。以下演示了这种错误解法。 - -```sql -DELETE -FROM - Person -WHERE - id NOT IN ( SELECT min( id ) AS id FROM Person GROUP BY email ); -``` - -参考:[pMySQL Error 1093 - Can't specify target table for update in FROM clause](https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause) - -# 175. Combine Two Tables - -https://leetcode.com/problems/combine-two-tables/description/ - -## Description - -Person 表: - -```html -+-------------+---------+ -| Column Name | Type | -+-------------+---------+ -| PersonId | int | -| FirstName | varchar | -| LastName | varchar | -+-------------+---------+ -PersonId is the primary key column for this table. -``` - -Address 表: - -```html -+-------------+---------+ -| Column Name | Type | -+-------------+---------+ -| AddressId | int | -| PersonId | int | -| City | varchar | -| State | varchar | -+-------------+---------+ -AddressId is the primary key column for this table. -``` - -查找 FirstName, LastName, City, State 数据,而不管一个用户有没有填地址信息。 - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Person; -CREATE TABLE Person ( PersonId INT, FirstName VARCHAR ( 255 ), LastName VARCHAR ( 255 ) ); -DROP TABLE -IF - EXISTS Address; -CREATE TABLE Address ( AddressId INT, PersonId INT, City VARCHAR ( 255 ), State VARCHAR ( 255 ) ); -INSERT INTO Person ( PersonId, LastName, FirstName ) -VALUES - ( 1, 'Wang', 'Allen' ); -INSERT INTO Address ( AddressId, PersonId, City, State ) -VALUES - ( 1, 2, 'New York City', 'New York' ); -``` - -## Solution - -使用左外连接。 - -```sql -SELECT - FirstName, - LastName, - City, - State -FROM - Person P - LEFT JOIN Address A - ON P.PersonId = A.PersonId; -``` - -# 181. Employees Earning More Than Their Managers - -https://leetcode.com/problems/employees-earning-more-than-their-managers/description/ - -## Description - -Employee 表: - -```html -+----+-------+--------+-----------+ -| Id | Name | Salary | ManagerId | -+----+-------+--------+-----------+ -| 1 | Joe | 70000 | 3 | -| 2 | Henry | 80000 | 4 | -| 3 | Sam | 60000 | NULL | -| 4 | Max | 90000 | NULL | -+----+-------+--------+-----------+ -``` - -查找薪资大于其经理薪资的员工信息。 - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Employee; -CREATE TABLE Employee ( Id INT, NAME VARCHAR ( 255 ), Salary INT, ManagerId INT ); -INSERT INTO Employee ( Id, NAME, Salary, ManagerId ) -VALUES - ( 1, 'Joe', 70000, 3 ), - ( 2, 'Henry', 80000, 4 ), - ( 3, 'Sam', 60000, NULL ), - ( 4, 'Max', 90000, NULL ); -``` - -## Solution - -```sql -SELECT - E1.NAME AS Employee -FROM - Employee E1 - INNER JOIN Employee E2 - ON E1.ManagerId = E2.Id - AND E1.Salary > E2.Salary; -``` - -# 183. Customers Who Never Order - -https://leetcode.com/problems/customers-who-never-order/description/ - -## Description - -Customers 表: - -```html -+----+-------+ -| Id | Name | -+----+-------+ -| 1 | Joe | -| 2 | Henry | -| 3 | Sam | -| 4 | Max | -+----+-------+ -``` - -Orders 表: - -```html -+----+------------+ -| Id | CustomerId | -+----+------------+ -| 1 | 3 | -| 2 | 1 | -+----+------------+ -``` - -查找没有订单的顾客信息: - -```html -+-----------+ -| Customers | -+-----------+ -| Henry | -| Max | -+-----------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Customers; -CREATE TABLE Customers ( Id INT, NAME VARCHAR ( 255 ) ); -DROP TABLE -IF - EXISTS Orders; -CREATE TABLE Orders ( Id INT, CustomerId INT ); -INSERT INTO Customers ( Id, NAME ) -VALUES - ( 1, 'Joe' ), - ( 2, 'Henry' ), - ( 3, 'Sam' ), - ( 4, 'Max' ); -INSERT INTO Orders ( Id, CustomerId ) -VALUES - ( 1, 3 ), - ( 2, 1 ); -``` - -## Solution - -左外链接 - -```sql -SELECT - C.Name AS Customers -FROM - Customers C - LEFT JOIN Orders O - ON C.Id = O.CustomerId -WHERE - O.CustomerId IS NULL; -``` - -子查询 - -```sql -SELECT - Name AS Customers -FROM - Customers -WHERE - Id NOT IN ( SELECT CustomerId FROM Orders ); -``` - -# 184. Department Highest Salary - -https://leetcode.com/problems/department-highest-salary/description/ - -## Description - -Employee 表: - -```html -+----+-------+--------+--------------+ -| Id | Name | Salary | DepartmentId | -+----+-------+--------+--------------+ -| 1 | Joe | 70000 | 1 | -| 2 | Henry | 80000 | 2 | -| 3 | Sam | 60000 | 2 | -| 4 | Max | 90000 | 1 | -+----+-------+--------+--------------+ -``` - -Department 表: - -```html -+----+----------+ -| Id | Name | -+----+----------+ -| 1 | IT | -| 2 | Sales | -+----+----------+ -``` - -查找一个 Department 中收入最高者的信息: - -```html -+------------+----------+--------+ -| Department | Employee | Salary | -+------------+----------+--------+ -| IT | Max | 90000 | -| Sales | Henry | 80000 | -+------------+----------+--------+ -``` - -## SQL Schema - -```sql -DROP TABLE IF EXISTS Employee; -CREATE TABLE Employee ( Id INT, NAME VARCHAR ( 255 ), Salary INT, DepartmentId INT ); -DROP TABLE IF EXISTS Department; -CREATE TABLE Department ( Id INT, NAME VARCHAR ( 255 ) ); -INSERT INTO Employee ( Id, NAME, Salary, DepartmentId ) -VALUES - ( 1, 'Joe', 70000, 1 ), - ( 2, 'Henry', 80000, 2 ), - ( 3, 'Sam', 60000, 2 ), - ( 4, 'Max', 90000, 1 ); -INSERT INTO Department ( Id, NAME ) -VALUES - ( 1, 'IT' ), - ( 2, 'Sales' ); -``` - -## Solution - -创建一个临时表,包含了部门员工的最大薪资。可以对部门进行分组,然后使用 MAX() 汇总函数取得最大薪资。 - -之后使用连接找到一个部门中薪资等于临时表中最大薪资的员工。 - -```sql -SELECT - D.NAME Department, - E.NAME Employee, - E.Salary -FROM - Employee E, - Department D, - ( SELECT DepartmentId, MAX( Salary ) Salary FROM Employee GROUP BY DepartmentId ) M -WHERE - E.DepartmentId = D.Id - AND E.DepartmentId = M.DepartmentId - AND E.Salary = M.Salary; -``` - -# 176. Second Highest Salary - -https://leetcode.com/problems/second-highest-salary/description/ - -## Description - -```html -+----+--------+ -| Id | Salary | -+----+--------+ -| 1 | 100 | -| 2 | 200 | -| 3 | 300 | -+----+--------+ -``` - -查找工资第二高的员工。 - -```html -+---------------------+ -| SecondHighestSalary | -+---------------------+ -| 200 | -+---------------------+ -``` - -没有找到返回 null 而不是不返回数据。 - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Employee; -CREATE TABLE Employee ( Id INT, Salary INT ); -INSERT INTO Employee ( Id, Salary ) -VALUES - ( 1, 100 ), - ( 2, 200 ), - ( 3, 300 ); -``` - -## Solution - -为了在没有查找到数据时返回 null,需要在查询结果外面再套一层 SELECT。 - -```sql -SELECT - ( SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT 1, 1 ) SecondHighestSalary; -``` - -# 177. Nth Highest Salary - -## Description - -查找工资第 N 高的员工。 - -## SQL Schema - -同 176。 - -## Solution - -```sql -CREATE FUNCTION getNthHighestSalary ( N INT ) RETURNS INT BEGIN - -SET N = N - 1; -RETURN ( SELECT ( SELECT DISTINCT Salary FROM Employee ORDER BY Salary DESC LIMIT N, 1 ) ); - -END -``` - -# 178. Rank Scores - -https://leetcode.com/problems/rank-scores/description/ - -## Description - -得分表: - -```html -+----+-------+ -| Id | Score | -+----+-------+ -| 1 | 3.50 | -| 2 | 3.65 | -| 3 | 4.00 | -| 4 | 3.85 | -| 5 | 4.00 | -| 6 | 3.65 | -+----+-------+ -``` - -将得分排序,并统计排名。 - -```html -+-------+------+ -| Score | Rank | -+-------+------+ -| 4.00 | 1 | -| 4.00 | 1 | -| 3.85 | 2 | -| 3.65 | 3 | -| 3.65 | 3 | -| 3.50 | 4 | -+-------+------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS Scores; -CREATE TABLE Scores ( Id INT, Score DECIMAL ( 3, 2 ) ); -INSERT INTO Scores ( Id, Score ) -VALUES - ( 1, 3.5 ), - ( 2, 3.65 ), - ( 3, 4.0 ), - ( 4, 3.85 ), - ( 5, 4.0 ), - ( 6, 3.65 ); -``` - -## Solution - -```sql -SELECT - S1.score, - COUNT( DISTINCT S2.score ) Rank -FROM - Scores S1 - INNER JOIN Scores S2 - ON S1.score <= S2.score -GROUP BY - S1.id -ORDER BY - S1.score DESC; -``` - -# 180. Consecutive Numbers - -https://leetcode.com/problems/consecutive-numbers/description/ - -## Description - -数字表: - -```html -+----+-----+ -| Id | Num | -+----+-----+ -| 1 | 1 | -| 2 | 1 | -| 3 | 1 | -| 4 | 2 | -| 5 | 1 | -| 6 | 2 | -| 7 | 2 | -+----+-----+ -``` - -查找连续出现三次的数字。 - -```html -+-----------------+ -| ConsecutiveNums | -+-----------------+ -| 1 | -+-----------------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS LOGS; -CREATE TABLE LOGS ( Id INT, Num INT ); -INSERT INTO LOGS ( Id, Num ) -VALUES - ( 1, 1 ), - ( 2, 1 ), - ( 3, 1 ), - ( 4, 2 ), - ( 5, 1 ), - ( 6, 2 ), - ( 7, 2 ); -``` - -## Solution - -```sql -SELECT - DISTINCT L1.num ConsecutiveNums -FROM - Logs L1, - Logs L2, - Logs L3 -WHERE L1.id = l2.id - 1 - AND L2.id = L3.id - 1 - AND L1.num = L2.num - AND l2.num = l3.num; -``` - -# 626. Exchange Seats - -https://leetcode.com/problems/exchange-seats/description/ - -## Description - -seat 表存储着座位对应的学生。 - -```html -+---------+---------+ -| id | student | -+---------+---------+ -| 1 | Abbot | -| 2 | Doris | -| 3 | Emerson | -| 4 | Green | -| 5 | Jeames | -+---------+---------+ -``` - -要求交换相邻座位的两个学生,如果最后一个座位是奇数,那么不交换这个座位上的学生。 - -```html -+---------+---------+ -| id | student | -+---------+---------+ -| 1 | Doris | -| 2 | Abbot | -| 3 | Green | -| 4 | Emerson | -| 5 | Jeames | -+---------+---------+ -``` - -## SQL Schema - -```sql -DROP TABLE -IF - EXISTS seat; -CREATE TABLE seat ( id INT, student VARCHAR ( 255 ) ); -INSERT INTO seat ( id, student ) -VALUES - ( '1', 'Abbot' ), - ( '2', 'Doris' ), - ( '3', 'Emerson' ), - ( '4', 'Green' ), - ( '5', 'Jeames' ); -``` - -## Solution - -使用多个 union。 - -```sql -SELECT - s1.id - 1 AS id, - s1.student -FROM - seat s1 -WHERE - s1.id MOD 2 = 0 UNION -SELECT - s2.id + 1 AS id, - s2.student -FROM - seat s2 -WHERE - s2.id MOD 2 = 1 - AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION -SELECT - s4.id AS id, - s4.student -FROM - seat s4 -WHERE - s4.id MOD 2 = 1 - AND s4.id = ( SELECT max( s5.id ) FROM seat s5 ) -ORDER BY - id; -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/Linux.md b/docs/notes/Linux.md deleted file mode 100644 index ee595b5553..0000000000 --- a/docs/notes/Linux.md +++ /dev/null @@ -1,1253 +0,0 @@ - -* [一、常用操作以及概念](#一常用操作以及概念) - * [快捷键](#快捷键) - * [求助](#求助) - * [关机](#关机) - * [PATH](#path) - * [sudo](#sudo) - * [包管理工具](#包管理工具) - * [发行版](#发行版) - * [VIM 三个模式](#vim-三个模式) - * [GNU](#gnu) - * [开源协议](#开源协议) -* [二、磁盘](#二磁盘) - * [磁盘接口](#磁盘接口) - * [磁盘的文件名](#磁盘的文件名) -* [三、分区](#三分区) - * [分区表](#分区表) - * [开机检测程序](#开机检测程序) -* [四、文件系统](#四文件系统) - * [分区与文件系统](#分区与文件系统) - * [组成](#组成) - * [文件读取](#文件读取) - * [磁盘碎片](#磁盘碎片) - * [block](#block) - * [inode](#inode) - * [目录](#目录) - * [日志](#日志) - * [挂载](#挂载) - * [目录配置](#目录配置) -* [五、文件](#五文件) - * [文件属性](#文件属性) - * [文件与目录的基本操作](#文件与目录的基本操作) - * [修改权限](#修改权限) - * [文件默认权限](#文件默认权限) - * [目录的权限](#目录的权限) - * [链接](#链接) - * [获取文件内容](#获取文件内容) - * [指令与文件搜索](#指令与文件搜索) -* [六、压缩与打包](#六压缩与打包) - * [压缩文件名](#压缩文件名) - * [压缩指令](#压缩指令) - * [打包](#打包) -* [七、Bash](#七bash) - * [特性](#特性) - * [变量操作](#变量操作) - * [指令搜索顺序](#指令搜索顺序) - * [数据流重定向](#数据流重定向) -* [八、管道指令](#八管道指令) - * [提取指令](#提取指令) - * [排序指令](#排序指令) - * [双向输出重定向](#双向输出重定向) - * [字符转换指令](#字符转换指令) - * [分区指令](#分区指令) -* [九、正则表达式](#九正则表达式) - * [grep](#grep) - * [printf](#printf) - * [awk](#awk) -* [十、进程管理](#十进程管理) - * [查看进程](#查看进程) - * [进程状态](#进程状态) - * [SIGCHLD](#sigchld) - * [wait()](#wait) - * [waitpid()](#waitpid) - * [孤儿进程](#孤儿进程) - * [僵尸进程](#僵尸进程) -* [参考资料](#参考资料) - - - -# 一、常用操作以及概念 - -## 快捷键 - -- Tab:命令和文件名补全; -- Ctrl+C:中断正在运行的程序; -- Ctrl+D:结束键盘输入(End Of File,EOF) - -## 求助 - -### 1. --help - -指令的基本用法与选项介绍。 - -### 2. man - -man 是 manual 的缩写,将指令的具体信息显示出来。 - -当执行`man date`时,有 DATE(1) 出现,其中的数字代表指令的类型,常用的数字及其类型如下: - -| 代号 | 类型 | -| :--: | -- | -| 1 | 用户在 shell 环境中可以操作的指令或者可执行文件 | -| 5 | 配置文件 | -| 8 | 系统管理员可以使用的管理指令 | - -### 3. info - -info 与 man 类似,但是 info 将文档分成一个个页面,每个页面可以进行跳转。 - -### 4. doc - -/usr/share/doc 存放着软件的一整套说明文件。 - -## 关机 - -### 1. who - -在关机前需要先使用 who 命令查看有没有其它用户在线。 - -### 2. sync - -为了加快对磁盘文件的读写速度,位于内存中的文件数据不会立即同步到磁盘上,因此关机之前需要先进行 sync 同步操作。 - -### 3. shutdown - -```html -# shutdown [-krhc] 时间 [信息] --k : 不会关机,只是发送警告信息,通知所有在线的用户 --r : 将系统的服务停掉后就重新启动 --h : 将系统的服务停掉后就立即关机 --c : 取消已经在进行的 shutdown 指令内容 -``` - -## PATH - -可以在环境变量 PATH 中声明可执行文件的路径,路径之间用 : 分隔。 - -```html -/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin -``` - -## sudo - -sudo 允许一般用户使用 root 可执行的命令,不过只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令。 - -## 包管理工具 - -RPM 和 DPKG 为最常见的两类软件包管理工具: - -- RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为很多 Linux 系统 (RHEL) 的既定软件标准。 -- 与 RPM 进行竞争的是基于 Debian 操作系统 (Ubuntu) 的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。 - -YUM 基于 RPM,具有依赖管理功能,并具有软件升级的功能。 - -## 发行版 - -Linux 发行版是 Linux 内核及各种应用软件的集成版本。 - -| 基于的包管理工具 | 商业发行版 | 社区发行版 | -| :--: | :--: | :--: | -| RPM | Red Hat | Fedora / CentOS | -| DPKG | Ubuntu | Debian | - -## VIM 三个模式 - -- 一般指令模式(Command mode):VIM 的默认模式,可以用于移动游标查看内容; -- 编辑模式(Insert mode):按下 "i" 等按键之后进入,可以对文本进行编辑; -- 指令列模式(Bottom-line mode):按下 ":" 按键之后进入,用于保存退出等操作。 - -

- -在指令列模式下,有以下命令用于离开或者保存文件。 - -| 命令 | 作用 | -| :--: | :--: | -| :w | 写入磁盘| -| :w! | 当文件为只读时,强制写入磁盘。到底能不能写入,与用户对该文件的权限有关 | -| :q | 离开 | -| :q! | 强制离开不保存 | -| :wq | 写入磁盘后离开 | -| :wq!| 强制写入磁盘后离开 | - -## GNU - -GNU 计划,译为革奴计划,它的目标是创建一套完全自由的操作系统,称为 GNU,其内容软件完全以 GPL 方式发布。其中 GPL 全称为 GNU 通用公共许可协议,包含了以下内容: - -- 以任何目的运行此程序的自由; -- 再复制的自由; -- 改进此程序,并公开发布改进的自由。 - -## 开源协议 - -- [Choose an open source license](https://choosealicense.com/) -- [如何选择开源许可证?](http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html) - -# 二、磁盘 - -## 磁盘接口 - -### 1. IDE - -IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/s,因为并口线的抗干扰性太差,且排线占用空间较大,不利电脑内部散热,已逐渐被 SATA 所取代。 - -

- -### 2. SATA - -SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能。SATA-II 的接口速度为 300MiB/s,而新的 SATA-III 标准可达到 600MiB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。 - -

- -### 3. SCSI - -SCSI 全称是 Small Computer System Interface(小型机系统接口),经历多代的发展,从早期的 SCSI-II 到目前的 Ultra320 SCSI 以及 Fiber-Channel(光纤通道),接口型式也多种多样。SCSI 硬盘广为工作站级个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。 - -

- -### 4. SAS - -SAS(Serial Attached SCSI)是新一代的 SCSI 技术,和 SATA 硬盘相同,都是采取序列式技术以获得更高的传输速度,可达到 6Gb/s。此外也透过缩小连接线改善系统内部空间等。 - -

- -## 磁盘的文件名 - -Linux 中每个硬件都被当做一个文件,包括磁盘。磁盘以磁盘接口类型进行命名,常见磁盘的文件名如下: - -- IDE 磁盘:/dev/hd[a-d] -- SATA/SCSI/SAS 磁盘:/dev/sd[a-p] - -其中文件名后面的序号的确定与系统检测到磁盘的顺序有关,而与磁盘所插入的插槽位置无关。 - -# 三、分区 - -## 分区表 - -磁盘分区表主要有两种格式,一种是限制较多的 MBR 分区表,一种是较新且限制较少的 GPT 分区表。 - -### 1. MBR - -MBR 中,第一个扇区最重要,里面有主要开机记录(Master boot record, MBR)及分区表(partition table),其中主要开机记录占 446 bytes,分区表占 64 bytes。 - -分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它使用其它扇区用记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。 - -Linux 也把分区当成文件,分区文件的命名方式为:磁盘文件名 + 编号,例如 /dev/sda1。注意,逻辑分区的编号从 5 开始。 - -### 2. GPT - -不同的磁盘有不同的扇区大小,例如 512 bytes 和最新磁盘的 4 k。GPT 为了兼容所有磁盘,在定义扇区上使用逻辑区块地址(Logical Block Address, LBA),LBA 默认大小为 512 bytes。 - -GPT 第 1 个区块记录了主要开机记录(MBR),紧接着是 33 个区块记录分区信息,并把最后的 33 个区块用于对分区信息进行备份。这 33 个区块第一个为 GPT 表头纪录,这个部份纪录了分区表本身的位置与大小和备份分区的位置,同时放置了分区表的校验码 (CRC32),操作系统可以根据这个校验码来判断 GPT 是否正确。若有错误,可以使用备份分区进行恢复。 - -GPT 没有扩展分区概念,都是主分区,每个 LAB 可以分 4 个分区,因此总共可以分 4 * 32 = 128 个分区。 - -MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB = 8 ZB。 - -

- -## 开机检测程序 - -### 1. BIOS - -BIOS(Basic Input/Output System,基本输入输出系统),它是一个固件(嵌入在硬件中的软件),BIOS 程序存放在断电后内容不会丢失的只读内存中。 - -

- -BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。 - -主要开机记录(MBR)中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动开机管理程序时,就可以通过选单选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。 - -下图中,第一扇区的主要开机记录(MBR)中的开机管理程序提供了两个选单:M1、M2,M1 指向了 Windows 操作系统,而 M2 指向其它分区的启动扇区,里面包含了另外一个开机管理程序,提供了一个指向 Linux 的选单。 - -

- -安装多重引导,最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉主要开机记录(MBR),而 Linux 可以选择将开机管理程序安装在主要开机记录(MBR)或者其它分区的启动扇区,并且可以设置开机管理程序的选单。 - -### 2. UEFI - -BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 - -# 四、文件系统 - -## 分区与文件系统 - -对分区进行格式化是为了在分区上建立文件系统。一个分区通常只能格式化为一个文件系统,但是磁盘阵列等技术可以将一个分区格式化为多个文件系统。 - -## 组成 - -最主要的几个组成部分如下: - -- inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号; -- block:记录文件的内容,文件太大时,会占用多个 block。 - -除此之外还包括: - -- superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等; -- block bitmap:记录 block 是否被使用的位域。 - -

- -## 文件读取 - -对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。 - -

- -而对于 FAT 文件系统,它没有 inode,每个 block 中存储着下一个 block 的编号。 - -

- -## 磁盘碎片 - -指一个文件内容所在的 block 过于分散。 - -## block - -在 Ext2 文件系统中所支持的 block 大小有 1K,2K 及 4K 三种,不同的大小限制了单个文件和文件系统的最大大小。 - -| 大小 | 1KB | 2KB | 4KB | -| :---: | :---: | :---: | :---: | -| 最大单一文件 | 16GB | 256GB | 2TB | -| 最大文件系统 | 2TB | 8TB | 16TB | - -一个 block 只能被一个文件所使用,未使用的部分直接浪费了。因此如果需要存储大量的小文件,那么最好选用比较小的 block。 - -## inode - -inode 具体包含以下信息: - -- 权限 (read/write/excute); -- 拥有者与群组 (owner/group); -- 容量; -- 建立或状态改变的时间 (ctime); -- 最近一次的读取时间 (atime); -- 最近修改的时间 (mtime); -- 定义文件特性的旗标 (flag),如 SetUID...; -- 该文件真正内容的指向 (pointer)。 - -inode 具有以下特点: - -- 每个 inode 大小均固定为 128 bytes (新的 ext4 与 xfs 可设定到 256 bytes); -- 每个文件都仅会占用一个 inode。 - -inode 中记录了文件内容所在的 block 编号,但是每个 block 非常小,一个大文件随便都需要几十万的 block。而一个 inode 大小有限,无法直接引用这么多 block 编号。因此引入了间接、双间接、三间接引用。间接引用是指,让 inode 记录的引用 block 块记录引用信息。 - -

- -## 目录 - -建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。 - -可以看出文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。 - -## 日志 - -如果突然断电,那么文件系统会发生错误,例如断电前只修改了 block bitmap,而还没有将数据真正写入 block 中。 - -ext3/ext4 文件系统引入了日志功能,可以利用日志来修复文件系统。 - -## 挂载 - -挂载利用目录作为文件系统的进入点,也就是说,进入目录之后就可以读取文件系统的数据。 - -## 目录配置 - -为了使不同 Linux 发行版本的目录结构保持一致性,Filesystem Hierarchy Standard (FHS) 规定了 Linux 的目录结构。最基础的三个目录如下: - -- / (root, 根目录) -- /usr (unix software resource):所有系统默认软件都会安装到这个目录; -- /var (variable):存放系统或程序运行过程中的数据文件。 - -

- -# 五、文件 - -## 文件属性 - -用户分为三种:文件拥有者、群组以及其它人,对不同的用户有不同的文件权限。 - -使用 ls 查看一个文件时,会显示一个文件的信息,例如 `drwxr-xr-x. 3 root root 17 May 6 00:14 .config`,对这个信息的解释如下: - -- drwxr-xr-x:文件类型以及权限,第 1 位为文件类型字段,后 9 位为文件权限字段 -- 3:链接数 -- root:文件拥有者 -- root:所属群组 -- 17:文件大小 -- May 6 00:14:文件最后被修改的时间 -- .config:文件名 - -常见的文件类型及其含义有: - -- d:目录 -- -:文件 -- l:链接文件 - -9 位的文件权限字段中,每 3 个为一组,共 3 组,每一组分别代表对文件拥有者、所属群组以及其它人的文件权限。一组权限中的 3 位分别为 r、w、x 权限,表示可读、可写、可执行。 - -文件时间有以下三种: - -- modification time (mtime):文件的内容更新就会更新; -- status time (ctime):文件的状态(权限、属性)更新就会更新; -- access time (atime):读取文件时就会更新。 - -## 文件与目录的基本操作 - -### 1. ls - -列出文件或者目录的信息,目录的信息就是其中包含的文件。 - -```html -# ls [-aAdfFhilnrRSt] file|dir --a :列出全部的文件 --d :仅列出目录本身 --l :以长数据串行列出,包含文件的属性与权限等等数据 -``` - -### 2. cd - -更换当前目录。 - -``` -cd [相对路径或绝对路径] -``` - -### 3. mkdir - -创建目录。 - -``` -# mkdir [-mp] 目录名称 --m :配置目录权限 --p :递归创建目录 -``` - -### 4. rmdir - -删除目录,目录必须为空。 - -```html -rmdir [-p] 目录名称 --p :递归删除目录 -``` - -### 5. touch - -更新文件时间或者建立新文件。 - -```html -# touch [-acdmt] filename --a : 更新 atime --c : 更新 ctime,若该文件不存在则不建立新文件 --m : 更新 mtime --d : 后面可以接更新日期而不使用当前日期,也可以使用 --date="日期或时间" --t : 后面可以接更新时间而不使用当前时间,格式为[YYYYMMDDhhmm] -``` - -### 6. cp - -复制文件。 - -如果源文件有两个以上,则目的文件一定要是目录才行。 - -```html -cp [-adfilprsu] source destination --a :相当于 -dr --preserve=all 的意思,至于 dr 请参考下列说明 --d :若来源文件为链接文件,则复制链接文件属性而非文件本身 --i :若目标文件已经存在时,在覆盖前会先询问 --p :连同文件的属性一起复制过去 --r :递归持续复制 --u :destination 比 source 旧才更新 destination,或 destination 不存在的情况下才复制 ---preserve=all :除了 -p 的权限相关参数外,还加入 SELinux 的属性, links, xattr 等也复制了 -``` - -### 7. rm - -删除文件。 - -```html -# rm [-fir] 文件或目录 --r :递归删除 -``` - -### 8. mv - -移动文件。 - -```html -# mv [-fiu] source destination -# mv [options] source1 source2 source3 .... directory --f : force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖 -``` - -## 修改权限 - -可以将一组权限用数字来表示,此时一组权限的 3 个位当做二进制数字的位,从左到右每个位的权值为 4、2、1,即每个权限对应的数字权值为 r : 4、w : 2、x : 1。 - -```html -# chmod [-R] xyz dirname/filename -``` - -示例:将 .bashrc 文件的权限修改为 -rwxr-xr--。 - -```html -# chmod 754 .bashrc -``` - -也可以使用符号来设定权限。 - -```html -# chmod [ugoa] [+-=] [rwx] dirname/filename -- u:拥有者 -- g:所属群组 -- o:其他人 -- a:所有人 -- +:添加权限 -- -:移除权限 -- =:设定权限 -``` - -示例:为 .bashrc 文件的所有用户添加写权限。 - -```html -# chmod a+w .bashrc -``` - -## 文件默认权限 - -- 文件默认权限:文件默认没有可执行权限,因此为 666,也就是 -rw-rw-rw- 。 -- 目录默认权限:目录必须要能够进入,也就是必须拥有可执行权限,因此为 777 ,也就是 drwxrwxrwx。 - -可以通过 umask 设置或者查看文件的默认权限,通常以掩码的形式来表示,例如 002 表示其它用户的权限去除了一个 2 的权限,也就是写权限,因此建立新文件时默认的权限为 -rw-rw-r--。 - -## 目录的权限 - -文件名不是存储在一个文件的内容中,而是存储在一个文件所在的目录中。因此,拥有文件的 w 权限并不能对文件名进行修改。 - -目录存储文件列表,一个目录的权限也就是对其文件列表的权限。因此,目录的 r 权限表示可以读取文件列表;w 权限表示可以修改文件列表,具体来说,就是添加删除文件,对文件名进行修改;x 权限可以让该目录成为工作目录,x 权限是 r 和 w 权限的基础,如果不能使一个目录成为工作目录,也就没办法读取文件列表以及对文件列表进行修改了。 - -## 链接 - -```html -# ln [-sf] source_filename dist_filename --s :默认是 hard link,加 -s 为 symbolic link --f :如果目标文件存在时,先删除目标文件 -``` - -

- -### 1. 实体链接 - -在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。 - -删除任意一个条目,文件还是存在,只要引用数量不为 0。 - -有以下限制:不能跨越文件系统、不能对目录进行链接。 - -```html -# ln /etc/crontab . -# ll -i /etc/crontab crontab -34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 crontab -34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab -``` - -### 2. 符号链接 - -符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式。 - -当源文件被删除了,链接文件就打不开了。 - -可以为目录建立链接。 - -```html -# ll -i /etc/crontab /root/crontab2 -34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab -53745909 lrwxrwxrwx. 1 root root 12 Jun 23 22:31 /root/crontab2 -> /etc/crontab -``` - -## 获取文件内容 - -### 1. cat - -取得文件内容。 - -```html -# cat [-AbEnTv] filename --n :打印出行号,连同空白行也会有行号,-b 不会 -``` - -### 2. tac - -是 cat 的反向操作,从最后一行开始打印。 - -### 3. more - -和 cat 不同的是它可以一页一页查看文件内容,比较适合大文件的查看。 - -### 4. less - -和 more 类似,但是多了一个向前翻页的功能。 - -### 5. head - -取得文件前几行。 - -```html -# head [-n number] filename --n :后面接数字,代表显示几行的意思 -``` - -### 6. tail - -是 head 的反向操作,只是取得是后几行。 - -### 7. od - -以字符或者十六进制的形式显示二进制文件。 - -## 指令与文件搜索 - -### 1. which - -指令搜索。 - -```html -# which [-a] command --a :将所有指令列出,而不是只列第一个 -``` - -### 2. whereis - -文件搜索。速度比较快,因为它只搜索几个特定的目录。 - -```html -# whereis [-bmsu] dirname/filename -``` - -### 3. locate - -文件搜索。可以用关键字或者正则表达式进行搜索。 - -locate 使用 /var/lib/mlocate/ 这个数据库来进行搜索,它存储在内存中,并且每天更新一次,所以无法用 locate 搜索新建的文件。可以使用 updatedb 来立即更新数据库。 - -```html -# locate [-ir] keyword --r:正则表达式 -``` - -### 4. find - -文件搜索。可以使用文件的属性和权限进行搜索。 - -```html -# find [basedir] [option] -example: find . -name "shadow*" -``` - -**① 与时间有关的选项** - -```html --mtime n :列出在 n 天前的那一天修改过内容的文件 --mtime +n :列出在 n 天之前 (不含 n 天本身) 修改过内容的文件 --mtime -n :列出在 n 天之内 (含 n 天本身) 修改过内容的文件 --newer file : 列出比 file 更新的文件 -``` - -+4、4 和 -4 的指示的时间范围如下: - -

- -**② 与文件拥有者和所属群组有关的选项** - -```html --uid n --gid n --user name --group name --nouser :搜索拥有者不存在 /etc/passwd 的文件 --nogroup:搜索所属群组不存在于 /etc/group 的文件 -``` - -**③ 与文件权限和名称有关的选项** - -```html --name filename --size [+-]SIZE:搜寻比 SIZE 还要大 (+) 或小 (-) 的文件。这个 SIZE 的规格有:c: 代表 byte,k: 代表 1024bytes。所以,要找比 50KB 还要大的文件,就是 -size +50k --type TYPE --perm mode :搜索权限等于 mode 的文件 --perm -mode :搜索权限包含 mode 的文件 --perm /mode :搜索权限包含任一 mode 的文件 -``` - -# 六、压缩与打包 - -## 压缩文件名 - -Linux 底下有很多压缩文件名,常见的如下: - -| 扩展名 | 压缩程序 | -| -- | -- | -| \*.Z | compress | -|\*.zip | zip | -|\*.gz | gzip| -|\*.bz2 | bzip2 | -|\*.xz | xz | -|\*.tar | tar 程序打包的数据,没有经过压缩 | -|\*.tar.gz | tar 程序打包的文件,经过 gzip 的压缩 | -|\*.tar.bz2 | tar 程序打包的文件,经过 bzip2 的压缩 | -|\*.tar.xz | tar 程序打包的文件,经过 xz 的压缩 | - -## 压缩指令 - -### 1. gzip - -gzip 是 Linux 使用最广的压缩指令,可以解开 compress、zip 与 gzip 所压缩的文件。 - -经过 gzip 压缩过,源文件就不存在了。 - -有 9 个不同的压缩等级可以使用。 - -可以使用 zcat、zmore、zless 来读取压缩文件的内容。 - -```html -$ gzip [-cdtv#] filename --c :将压缩的数据输出到屏幕上 --d :解压缩 --t :检验压缩文件是否出错 --v :显示压缩比等信息 --# : # 为数字的意思,代表压缩等级,数字越大压缩比越高,默认为 6 -``` - -### 2. bzip2 - -提供比 gzip 更高的压缩比。 - -查看命令:bzcat、bzmore、bzless、bzgrep。 - -```html -$ bzip2 [-cdkzv#] filename --k :保留源文件 -``` - -### 3. xz - -提供比 bzip2 更佳的压缩比。 - -可以看到,gzip、bzip2、xz 的压缩比不断优化。不过要注意的是,压缩比越高,压缩的时间也越长。 - -查看命令:xzcat、xzmore、xzless、xzgrep。 - -```html -$ xz [-dtlkc#] filename -``` - -## 打包 - -压缩指令只能对一个文件进行压缩,而打包能够将多个文件打包成一个大文件。tar 不仅可以用于打包,也可以使用 gip、bzip2、xz 将打包文件进行压缩。 - -```html -$ tar [-z|-j|-J] [cv] [-f 新建的 tar 文件] filename... ==打包压缩 -$ tar [-z|-j|-J] [tv] [-f 已有的 tar 文件] ==查看 -$ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录] ==解压缩 --z :使用 zip; --j :使用 bzip2; --J :使用 xz; --c :新建打包文件; --t :查看打包文件里面有哪些文件; --x :解打包或解压缩的功能; --v :在压缩/解压缩的过程中,显示正在处理的文件名; --f : filename:要处理的文件; --C 目录 : 在特定目录解压缩。 -``` - -| 使用方式 | 命令 | -| :---: | --- | -| 打包压缩 | tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称 | -| 查 看 | tar -jtv -f filename.tar.bz2 | -| 解压缩 | tar -jxv -f filename.tar.bz2 -C 要解压缩的目录 | - -# 七、Bash - -可以通过 Shell 请求内核提供服务,Bash 正是 Shell 的一种。 - -## 特性 - -- 命令历史:记录使用过的命令 -- 命令与文件补全:快捷键:tab -- 命名别名:例如 lm 是 ls -al 的别名 -- shell scripts -- 通配符:例如 ls -l /usr/bin/X\* 列出 /usr/bin 下面所有以 X 开头的文件 - -## 变量操作 - -对一个变量赋值直接使用 =。 - -对变量取用需要在变量前加上 \$ ,也可以用 \${} 的形式; - -输出变量使用 echo 命令。 - -```bash -$ x=abc -$ echo $x -$ echo ${x} -``` - -变量内容如果有空格,必须使用双引号或者单引号。 - -- 双引号内的特殊字符可以保留原本特性,例如 x="lang is \$LANG",则 x 的值为 lang is zh_TW.UTF-8; -- 单引号内的特殊字符就是特殊字符本身,例如 x='lang is \$LANG',则 x 的值为 lang is \$LANG。 - -可以使用 \`指令\` 或者 \$(指令) 的方式将指令的执行结果赋值给变量。例如 version=\$(uname -r),则 version 的值为 4.15.0-22-generic。 - -可以使用 export 命令将自定义变量转成环境变量,环境变量可以在子程序中使用,所谓子程序就是由当前 Bash 而产生的子 Bash。 - -Bash 的变量可以声明为数组和整数数字。注意数字类型没有浮点数。如果不进行声明,默认是字符串类型。变量的声明使用 declare 命令: - -```html -$ declare [-aixr] variable --a : 定义为数组类型 --i : 定义为整数类型 --x : 定义为环境变量 --r : 定义为 readonly 类型 -``` - -使用 [ ] 来对数组进行索引操作: - -```bash -$ array[1]=a -$ array[2]=b -$ echo ${array[1]} -``` - -## 指令搜索顺序 - -- 以绝对或相对路径来执行指令,例如 /bin/ls 或者 ./ls ; -- 由别名找到该指令来执行; -- 由 Bash 内置的指令来执行; -- 按 \$PATH 变量指定的搜索路径的顺序找到第一个指令来执行。 - -## 数据流重定向 - -重定向指的是使用文件代替标准输入、标准输出和标准错误输出。 - -| 1 | 代码 | 运算符 | -| :---: | :---: | :---:| -| 标准输入 (stdin) | 0 | < 或 << | -| 标准输出 (stdout) | 1 | > 或 >> | -| 标准错误输出 (stderr) | 2 | 2> 或 2>> | - -其中,有一个箭头的表示以覆盖的方式重定向,而有两个箭头的表示以追加的方式重定向。 - -可以将不需要的标准输出以及标准错误输出重定向到 /dev/null,相当于扔进垃圾箱。 - -如果需要将标准输出以及标准错误输出同时重定向到一个文件,需要将某个输出转换为另一个输出,例如 2>&1 表示将标准错误输出转换为标准输出。 - -```bash -$ find /home -name .bashrc > list 2>&1 -``` - -# 八、管道指令 - -管道是将一个命令的标准输出作为另一个命令的标准输入,在数据需要经过多个步骤的处理之后才能得到我们想要的内容时就可以使用管道。 - -在命令之间使用 | 分隔各个管道命令。 - -```bash -$ ls -al /etc | less -``` - -## 提取指令 - -cut 对数据进行切分,取出想要的部分。 - -切分过程一行一行地进行。 - -```html -$ cut --d :分隔符 --f :经过 -d 分隔后,使用 -f n 取出第 n 个区间 --c :以字符为单位取出区间 -``` - -示例 1:last 显示登入者的信息,取出用户名。 - -```html -$ last -root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in -root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33) -root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16) - -$ last | cut -d ' ' -f 1 -``` - -示例 2:将 export 输出的信息,取出第 12 字符以后的所有字符串。 - -```html -$ export -declare -x HISTCONTROL="ignoredups" -declare -x HISTSIZE="1000" -declare -x HOME="/home/dmtsai" -declare -x HOSTNAME="study.centos.vbird" -.....(其他省略)..... - -$ export | cut -c 12- -``` - -## 排序指令 - -**sort** 用于排序。 - -```html -$ sort [-fbMnrtuk] [file or stdin] --f :忽略大小写 --b :忽略最前面的空格 --M :以月份的名字来排序,例如 JAN,DEC --n :使用数字 --r :反向排序 --u :相当于 unique,重复的内容只出现一次 --t :分隔符,默认为 tab --k :指定排序的区间 -``` - -示例:/etc/passwd 文件内容以 : 来分隔,要求以第三列进行排序。 - -```html -$ cat /etc/passwd | sort -t ':' -k 3 -root:x:0:0:root:/root:/bin/bash -dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash -alex:x:1001:1002::/home/alex:/bin/bash -arod:x:1002:1003::/home/arod:/bin/bash -``` - -**uniq** 可以将重复的数据只取一个。 - -```html -$ uniq [-ic] --i :忽略大小写 --c :进行计数 -``` - -示例:取得每个人的登录总次数 - -```html -$ last | cut -d ' ' -f 1 | sort | uniq -c -1 -6 (unknown -47 dmtsai -4 reboot -7 root -1 wtmp -``` - -## 双向输出重定向 - -输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 - -```html -$ tee [-a] file -``` - -## 字符转换指令 - -**tr** 用来删除一行中的字符,或者对字符进行替换。 - -```html -$ tr [-ds] SET1 ... --d : 删除行中 SET1 这个字符串 -``` - -示例,将 last 输出的信息所有小写转换为大写。 - -```html -$ last | tr '[a-z]' '[A-Z]' -``` - - **col** 将 tab 字符转为空格字符。 - -```html -$ col [-xb] --x : 将 tab 键转换成对等的空格键 -``` - -**expand** 将 tab 转换一定数量的空格,默认是 8 个。 - -```html -$ expand [-t] file --t :tab 转为空格的数量 -``` - -**join** 将有相同数据的那一行合并在一起。 - -```html -$ join [-ti12] file1 file2 --t :分隔符,默认为空格 --i :忽略大小写的差异 --1 :第一个文件所用的比较字段 --2 :第二个文件所用的比较字段 -``` - -**paste** 直接将两行粘贴在一起。 - -```html -$ paste [-d] file1 file2 --d :分隔符,默认为 tab -``` - -## 分区指令 - -**split** 将一个文件划分成多个文件。 - -```html -$ split [-bl] file PREFIX --b :以大小来进行分区,可加单位,例如 b, k, m 等 --l :以行数来进行分区。 -- PREFIX :分区文件的前导名称 -``` - -# 九、正则表达式 - -## grep - -g/re/p(globally search a regular expression and print),使用正则表示式进行全局查找并打印。 - -```html -$ grep [-acinv] [--color=auto] 搜寻字符串 filename --c : 统计个数 --i : 忽略大小写 --n : 输出行号 --v : 反向选择,也就是显示出没有 搜寻字符串 内容的那一行 ---color=auto :找到的关键字加颜色显示 -``` - -示例:把含有 the 字符串的行提取出来(注意默认会有 --color=auto 选项,因此以下内容在 Linux 中有颜色显示 the 字符串) - -```html -$ grep -n 'the' regular_express.txt -8:I can't finish the test. -12:the symbol '*' is represented as start. -15:You are the best is mean you are the no. 1. -16:The world Happy is the same with "glad". -18:google is the best tools for search keyword -``` - -因为 { 和 } 在 shell 是有特殊意义的,因此必须要使用转义字符进行转义。 - -```html -$ grep -n 'go\{2,5\}g' regular_express.txt -``` - -## printf - -用于格式化输出。它不属于管道命令,在给 printf 传数据时需要使用 $( ) 形式。 - -```html -$ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt) - DmTsai 80 60 92 77.33 - VBird 75 55 80 70.00 - Ken 60 90 70 73.33 -``` - -## awk - -是由 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 创造,awk 这个名字就是这三个创始人名字的首字母。 - -awk 每次处理一行,处理的最小单位是字段,每个字段的命名方式为:\$n,n 为字段号,从 1 开始,\$0 表示一整行。 - -示例:取出最近五个登录用户的用户名和 IP - -```html -$ last -n 5 -dmtsai pts/0 192.168.1.100 Tue Jul 14 17:32 still logged in -dmtsai pts/0 192.168.1.100 Thu Jul 9 23:36 - 02:58 (03:22) -dmtsai pts/0 192.168.1.100 Thu Jul 9 17:23 - 23:36 (06:12) -dmtsai pts/0 192.168.1.100 Thu Jul 9 08:02 - 08:17 (00:14) -dmtsai tty1 Fri May 29 11:55 - 12:11 (00:15) -``` - -```html -$ last -n 5 | awk '{print $1 "\t" $3}' -``` - -可以根据字段的某些条件进行匹配,例如匹配字段小于某个值的那一行数据。 - -```html -$ awk '条件类型 1 {动作 1} 条件类型 2 {动作 2} ...' filename -``` - -示例:/etc/passwd 文件第三个字段为 UID,对 UID 小于 10 的数据进行处理。 - -```text -$ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}' -root 0 -bin 1 -daemon 2 -``` - -awk 变量: - -| 变量名称 | 代表意义 | -| :--: | -- | -| NF | 每一行拥有的字段总数 | -| NR | 目前所处理的是第几行数据 | -| FS | 目前的分隔字符,默认是空格键 | - -示例:显示正在处理的行号以及每一行有多少字段 - -```html -$ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}' -dmtsai lines: 1 columns: 10 -dmtsai lines: 2 columns: 10 -dmtsai lines: 3 columns: 10 -dmtsai lines: 4 columns: 10 -dmtsai lines: 5 columns: 9 -``` - -# 十、进程管理 - -## 查看进程 - -### 1. ps - -查看某个时间点的进程信息 - -示例一:查看自己的进程 - -```sh -# ps -l -``` - -示例二:查看系统所有进程 - -```sh -# ps aux -``` - -示例三:查看特定的进程 - -```sh -# ps aux | grep threadx -``` - -### 2. pstree - -查看进程树 - -示例:查看所有进程树 - -```sh -# pstree -A -``` - -### 3. top - -实时显示进程信息 - -示例:两秒钟刷新一次 - -```sh -# top -d 2 -``` - -### 4. netstat - -查看占用端口的进程 - -示例:查看特定端口的进程 - -```sh -# netstat -anp | grep port -``` - -## 进程状态 - -| 状态 | 说明 | -| :---: | --- | -| R | running or runnable (on run queue) | -| D | uninterruptible sleep (usually I/O) | -| S | interruptible sleep (waiting for an event to complete) | -| Z | zombie (terminated but not reaped by its parent) | -| T | stopped (either by a job control signal or because it is being traced) | -
-

- -## SIGCHLD - -当一个子进程改变了它的状态时(停止运行,继续运行或者退出),有两件事会发生在父进程中: - -- 得到 SIGCHLD 信号; -- waitpid() 或者 wait() 调用会返回。 - -其中子进程发送的 SIGCHLD 信号包含了子进程的信息,比如进程 ID、进程状态、进程使用 CPU 的时间等。 - -在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。 - -

- -## wait() - -```c -pid_t wait(int *status) -``` - -父进程调用 wait() 会一直阻塞,直到收到一个子进程退出的 SIGCHLD 信号,之后 wait() 函数会销毁子进程并返回。 - -如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1,同时 errno 被置为 ECHILD。 - -参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL。 - -## waitpid() - -```c -pid_t waitpid(pid_t pid, int *status, int options) -``` - -作用和 wait() 完全相同,但是多了两个可由用户控制的参数 pid 和 options。 - -pid 参数指示一个子进程的 ID,表示只关心这个子进程退出的 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号。 - -options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 waitpid() 调用变成非阻塞的,也就是说它会立即返回,父进程可以继续执行其它任务。 - -## 孤儿进程 - -一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。 - -孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。 - -由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。 - -## 僵尸进程 - -一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。 - -僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。 - -系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。 - -要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。 - -# 参考资料 - -- 鸟哥. 鸟 哥 的 Linux 私 房 菜 基 础 篇 第 三 版[J]. 2009. -- [Linux 平台上的软件包管理](https://www.ibm.com/developerworks/cn/linux/l-cn-rpmdpkg/index.html) -- [Linux 之守护进程、僵死进程与孤儿进程](http://liubigbin.github.io/2016/03/11/Linux-%E4%B9%8B%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E3%80%81%E5%83%B5%E6%AD%BB%E8%BF%9B%E7%A8%8B%E4%B8%8E%E5%AD%A4%E5%84%BF%E8%BF%9B%E7%A8%8B/) -- [What is the difference between a symbolic link and a hard link?](https://stackoverflow.com/questions/185899/what-is-the-difference-between-a-symbolic-link-and-a-hard-link) -- [Linux process states](https://idea.popcount.org/2012-12-11-linux-process-states/) -- [GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table) -- [详解 wait 和 waitpid 函数](https://blog.csdn.net/kevinhg/article/details/7001719) -- [IDE、SATA、SCSI、SAS、FC、SSD 硬盘类型介绍](https://blog.csdn.net/tianlesoftware/article/details/6009110) -- [Akai IB-301S SCSI Interface for S2800,S3000](http://www.mpchunter.com/s3000/akai-ib-301s-scsi-interface-for-s2800s3000/) -- [Parallel ATA](https://en.wikipedia.org/wiki/Parallel_ATA) -- [ADATA XPG SX900 256GB SATA 3 SSD Review – Expanded Capacity and SandForce Driven Speed](http://www.thessdreview.com/our-reviews/adata-xpg-sx900-256gb-sata-3-ssd-review-expanded-capacity-and-sandforce-driven-speed/4/) -- [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) -- [硬盘](https://zh.wikipedia.org/wiki/%E7%A1%AC%E7%9B%98) -- [Difference between SAS and SATA](http://www.differencebetween.info/difference-between-sas-and-sata) -- [BIOS](https://zh.wikipedia.org/wiki/BIOS) -- [File system design case studies](https://www.cs.rutgers.edu/\~pxk/416/notes/13-fs-studies.html) -- [Programming Project #4](https://classes.soe.ucsc.edu/cmps111/Fall08/proj4.shtml) -- [FILE SYSTEM DESIGN](http://web.cs.ucla.edu/classes/fall14/cs111/scribe/11a/index.html) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/Socket.md b/docs/notes/Socket.md deleted file mode 100644 index 901ef13da1..0000000000 --- a/docs/notes/Socket.md +++ /dev/null @@ -1,329 +0,0 @@ - -* [一、I/O 模型](#一io-模型) - * [阻塞式 I/O](#阻塞式-io) - * [非阻塞式 I/O](#非阻塞式-io) - * [I/O 复用](#io-复用) - * [信号驱动 I/O](#信号驱动-io) - * [异步 I/O](#异步-io) - * [五大 I/O 模型比较](#五大-io-模型比较) -* [二、I/O 复用](#二io-复用) - * [select](#select) - * [poll](#poll) - * [比较](#比较) - * [epoll](#epoll) - * [工作模式](#工作模式) - * [应用场景](#应用场景) -* [参考资料](#参考资料) - - - -# 一、I/O 模型 - -一个输入操作通常包括两个阶段: - -- 等待数据准备好 -- 从内核向进程复制数据 - -对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所等待数据到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用进程缓冲区。 - -Unix 有五种 I/O 模型: - -- 阻塞式 I/O -- 非阻塞式 I/O -- I/O 复用(select 和 poll) -- 信号驱动式 I/O(SIGIO) -- 异步 I/O(AIO) - -## 阻塞式 I/O - -应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。 - -应该注意到,在阻塞的过程中,其它应用进程还可以执行,因此阻塞不意味着整个操作系统都被阻塞。因为其它应用进程还可以执行,所以不消耗 CPU 时间,这种模型的 CPU 利用率效率会比较高。 - -下图中,recvfrom() 用于接收 Socket 传来的数据,并复制到应用进程的缓冲区 buf 中。这里把 recvfrom() 当成系统调用。 - -```c -ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); -``` - -

- -## 非阻塞式 I/O - -应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。 - -由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低。 - -

- -## I/O 复用 - -使用 select 或者 poll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中。 - -它可以让单个进程具有处理多个 I/O 事件的能力。又被称为 Event Driven I/O,即事件驱动 I/O。 - -如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。 - -

- -## 信号驱动 I/O - -应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。 - -相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。 - -

- -## 异步 I/O - -应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。 - -异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。 - -

- -## 五大 I/O 模型比较 - -- 同步 I/O:将数据从内核缓冲区复制到应用进程缓冲区的阶段,应用进程会阻塞。 -- 异步 I/O:不会阻塞。 - -阻塞式 I/O、非阻塞式 I/O、I/O 复用和信号驱动 I/O 都是同步 I/O,它们的主要区别在第一个阶段。 - -非阻塞式 I/O 、信号驱动 I/O 和异步 I/O 在第一阶段不会阻塞。 - -

- -# 二、I/O 复用 - -select/poll/epoll 都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。 - -## select - -```c -int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); -``` - -有三种类型的描述符类型:readset、writeset、exceptset,分别对应读、写、异常条件的描述符集合。fd_set 使用数组实现,数组大小使用 FD_SETSIZE 定义。 - -timeout 为超时参数,调用 select 会一直阻塞直到有描述符的事件到达或者等待的时间超过 timeout。 - -成功调用返回结果大于 0,出错返回结果为 -1,超时返回结果为 0。 - -```c -fd_set fd_in, fd_out; -struct timeval tv; - -// Reset the sets -FD_ZERO( &fd_in ); -FD_ZERO( &fd_out ); - -// Monitor sock1 for input events -FD_SET( sock1, &fd_in ); - -// Monitor sock2 for output events -FD_SET( sock2, &fd_out ); - -// Find out which socket has the largest numeric value as select requires it -int largest_sock = sock1 > sock2 ? sock1 : sock2; - -// Wait up to 10 seconds -tv.tv_sec = 10; -tv.tv_usec = 0; - -// Call the select -int ret = select( largest_sock + 1, &fd_in, &fd_out, NULL, &tv ); - -// Check if select actually succeed -if ( ret == -1 ) - // report error and abort -else if ( ret == 0 ) - // timeout; no event detected -else -{ - if ( FD_ISSET( sock1, &fd_in ) ) - // input event on sock1 - - if ( FD_ISSET( sock2, &fd_out ) ) - // output event on sock2 -} -``` - -## poll - -```c -int poll(struct pollfd *fds, unsigned int nfds, int timeout); -``` - -pollfd 使用链表实现。 - -```c -// The structure for two events -struct pollfd fds[2]; - -// Monitor sock1 for input -fds[0].fd = sock1; -fds[0].events = POLLIN; - -// Monitor sock2 for output -fds[1].fd = sock2; -fds[1].events = POLLOUT; - -// Wait 10 seconds -int ret = poll( &fds, 2, 10000 ); -// Check if poll actually succeed -if ( ret == -1 ) - // report error and abort -else if ( ret == 0 ) - // timeout; no event detected -else -{ - // If we detect the event, zero it out so we can reuse the structure - if ( fds[0].revents & POLLIN ) - fds[0].revents = 0; - // input event on sock1 - - if ( fds[1].revents & POLLOUT ) - fds[1].revents = 0; - // output event on sock2 -} -``` - -## 比较 - -### 1. 功能 - -select 和 poll 的功能基本相同,不过在一些实现细节上有所不同。 - -- select 会修改描述符,而 poll 不会; -- select 的描述符类型使用数组实现,FD_SETSIZE 大小默认为 1024,因此默认只能监听 1024 个描述符。如果要监听更多描述符的话,需要修改 FD_SETSIZE 之后重新编译;而 poll 的描述符类型使用链表实现,没有描述符数量的限制; -- poll 提供了更多的事件类型,并且对描述符的重复利用上比 select 高。 -- 如果一个线程对某个描述符调用了 select 或者 poll,另一个线程关闭了该描述符,会导致调用结果不确定。 - -### 2. 速度 - -select 和 poll 速度都比较慢。 - -- select 和 poll 每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。 -- select 和 poll 的返回结果中没有声明哪些描述符已经准备好,所以如果返回值大于 0 时,应用进程都需要使用轮询的方式来找到 I/O 完成的描述符。 - -### 3. 可移植性 - -几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 - -## epoll - -```c -int epoll_create(int size); -int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); -int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); -``` - -epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符。 - -从上面的描述可以看出,epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次,并且进程不需要通过轮询来获得事件完成的描述符。 - -epoll 仅适用于 Linux OS。 - -epoll 比 select 和 poll 更加灵活而且没有描述符数量限制。 - -epoll 对多线程编程更有友好,一个线程调用了 epoll_wait() 另一个线程关闭了同一个描述符也不会产生像 select 和 poll 的不确定情况。 - -```c -// Create the epoll descriptor. Only one is needed per app, and is used to monitor all sockets. -// The function argument is ignored (it was not before, but now it is), so put your favorite number here -int pollingfd = epoll_create( 0xCAFE ); - -if ( pollingfd < 0 ) - // report error - -// Initialize the epoll structure in case more members are added in future -struct epoll_event ev = { 0 }; - -// Associate the connection class instance with the event. You can associate anything -// you want, epoll does not use this information. We store a connection class pointer, pConnection1 -ev.data.ptr = pConnection1; - -// Monitor for input, and do not automatically rearm the descriptor after the event -ev.events = EPOLLIN | EPOLLONESHOT; -// Add the descriptor into the monitoring list. We can do it even if another thread is -// waiting in epoll_wait - the descriptor will be properly added -if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, pConnection1->getSocket(), &ev ) != 0 ) - // report error - -// Wait for up to 20 events (assuming we have added maybe 200 sockets before that it may happen) -struct epoll_event pevents[ 20 ]; - -// Wait for 10 seconds, and retrieve less than 20 epoll_event and store them into epoll_event array -int ready = epoll_wait( pollingfd, pevents, 20, 10000 ); -// Check if epoll actually succeed -if ( ret == -1 ) - // report error and abort -else if ( ret == 0 ) - // timeout; no event detected -else -{ - // Check if any events detected - for ( int i = 0; i < ret; i++ ) - { - if ( pevents[i].events & EPOLLIN ) - { - // Get back our connection pointer - Connection * c = (Connection*) pevents[i].data.ptr; - c->handleReadEvent(); - } - } -} -``` - - -## 工作模式 - -epoll 的描述符事件有两种触发模式:LT(level trigger)和 ET(edge trigger)。 - -### 1. LT 模式 - -当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。 - -### 2. ET 模式 - -和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。 - -很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 - -## 应用场景 - -很容易产生一种错觉认为只要用 epoll 就可以了,select 和 poll 都已经过时了,其实它们都有各自的使用场景。 - -### 1. select 应用场景 - -select 的 timeout 参数精度为 1ns,而 poll 和 epoll 为 1ms,因此 select 更加适用于实时性要求比较高的场景,比如核反应堆的控制。 - -select 可移植性更好,几乎被所有主流平台所支持。 - -### 2. poll 应用场景 - -poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。 - -### 3. epoll 应用场景 - -只需要运行在 Linux 平台上,有大量的描述符需要同时轮询,并且这些连接最好是长连接。 - -需要同时监控小于 1000 个描述符,就没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。 - -需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且 epoll 的描述符存储在内核,不容易调试。 - -# 参考资料 - -- Stevens W R, Fenner B, Rudoff A M. UNIX network programming[M]. Addison-Wesley Professional, 2004. -- [Boost application performance using asynchronous I/O](https://www.ibm.com/developerworks/linux/library/l-async/) -- [Synchronous and Asynchronous I/O](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx) -- [Linux IO 模式及 select、poll、epoll 详解](https://segmentfault.com/a/1190000003063859) -- [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html) -- [select / poll / epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/) -- [Browse the source code of userspace/glibc/sysdeps/unix/sysv/linux/ online](https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png b/docs/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png deleted file mode 100644 index 0e56341c39..0000000000 Binary files a/docs/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png and /dev/null differ diff --git a/docs/notes/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png b/docs/notes/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png deleted file mode 100644 index fc0999f997..0000000000 Binary files a/docs/notes/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png and /dev/null differ diff --git a/docs/notes/pics/032771e7-f60f-47bf-aa79-f45c32799211.png b/docs/notes/pics/032771e7-f60f-47bf-aa79-f45c32799211.png deleted file mode 100644 index 5b9202f6fd..0000000000 Binary files a/docs/notes/pics/032771e7-f60f-47bf-aa79-f45c32799211.png and /dev/null differ diff --git a/docs/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png b/docs/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png deleted file mode 100644 index e3054539f4..0000000000 Binary files a/docs/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png and /dev/null differ diff --git a/docs/notes/pics/0635cbe8.png b/docs/notes/pics/0635cbe8.png deleted file mode 100644 index 849c9eaf15..0000000000 Binary files a/docs/notes/pics/0635cbe8.png and /dev/null differ diff --git a/docs/notes/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg b/docs/notes/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg deleted file mode 100644 index 84d371b863..0000000000 Binary files a/docs/notes/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg and /dev/null differ diff --git a/docs/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg b/docs/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg deleted file mode 100644 index c07bd92474..0000000000 Binary files a/docs/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg and /dev/null differ diff --git a/docs/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg b/docs/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg deleted file mode 100644 index b8b100a017..0000000000 Binary files a/docs/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg and /dev/null differ diff --git a/docs/notes/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png b/docs/notes/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png deleted file mode 100644 index 13d4983652..0000000000 Binary files a/docs/notes/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png and /dev/null differ diff --git a/docs/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png b/docs/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png deleted file mode 100644 index 4d9e3e21c7..0000000000 Binary files a/docs/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png and /dev/null differ diff --git a/docs/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg b/docs/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg deleted file mode 100644 index 09bbd597bd..0000000000 Binary files a/docs/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg and /dev/null differ diff --git a/docs/notes/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png b/docs/notes/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png deleted file mode 100644 index 17f4d5b75e..0000000000 Binary files a/docs/notes/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png and /dev/null differ diff --git a/docs/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg b/docs/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg deleted file mode 100644 index 7b88e7a05f..0000000000 Binary files a/docs/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg and /dev/null differ diff --git a/docs/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png b/docs/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png deleted file mode 100644 index 78a69978e4..0000000000 Binary files a/docs/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png and /dev/null differ diff --git a/docs/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png b/docs/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png deleted file mode 100644 index d8df24d8df..0000000000 Binary files a/docs/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png and /dev/null differ diff --git a/docs/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png b/docs/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png deleted file mode 100644 index 79ec18df91..0000000000 Binary files a/docs/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png and /dev/null differ diff --git a/docs/notes/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png b/docs/notes/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png deleted file mode 100644 index 78843b1c89..0000000000 Binary files a/docs/notes/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png and /dev/null differ diff --git a/docs/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png b/docs/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png deleted file mode 100644 index 57dd8bf117..0000000000 Binary files a/docs/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png and /dev/null differ diff --git a/docs/notes/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png b/docs/notes/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png deleted file mode 100644 index 0b4a58384a..0000000000 Binary files a/docs/notes/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png and /dev/null differ diff --git a/docs/notes/pics/101550406418006.gif b/docs/notes/pics/101550406418006.gif deleted file mode 100644 index df38020900..0000000000 Binary files a/docs/notes/pics/101550406418006.gif and /dev/null differ diff --git a/docs/notes/pics/101550414151983.gif b/docs/notes/pics/101550414151983.gif deleted file mode 100644 index d52a911e5b..0000000000 Binary files a/docs/notes/pics/101550414151983.gif and /dev/null differ diff --git a/docs/notes/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png b/docs/notes/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png deleted file mode 100644 index 35c2ddb1f8..0000000000 Binary files a/docs/notes/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png and /dev/null differ diff --git a/docs/notes/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg b/docs/notes/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg deleted file mode 100644 index 043964ae9c..0000000000 Binary files a/docs/notes/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg and /dev/null differ diff --git a/docs/notes/pics/111521118015898.gif b/docs/notes/pics/111521118015898.gif deleted file mode 100644 index 5c31da1dcf..0000000000 Binary files a/docs/notes/pics/111521118015898.gif and /dev/null differ diff --git a/docs/notes/pics/111521118445538.gif b/docs/notes/pics/111521118445538.gif deleted file mode 100644 index 323d129c59..0000000000 Binary files a/docs/notes/pics/111521118445538.gif and /dev/null differ diff --git a/docs/notes/pics/111521118483039.gif b/docs/notes/pics/111521118483039.gif deleted file mode 100644 index a81124ddd0..0000000000 Binary files a/docs/notes/pics/111521118483039.gif and /dev/null differ diff --git a/docs/notes/pics/111521118640738.gif b/docs/notes/pics/111521118640738.gif deleted file mode 100644 index 7a7b05a9db..0000000000 Binary files a/docs/notes/pics/111521118640738.gif and /dev/null differ diff --git a/docs/notes/pics/111521119203347.gif b/docs/notes/pics/111521119203347.gif deleted file mode 100644 index 37cdb5a5f5..0000000000 Binary files a/docs/notes/pics/111521119203347.gif and /dev/null differ diff --git a/docs/notes/pics/111521119368714.gif b/docs/notes/pics/111521119368714.gif deleted file mode 100644 index 216c3033f5..0000000000 Binary files a/docs/notes/pics/111521119368714.gif and /dev/null differ diff --git a/docs/notes/pics/111550407277293.gif b/docs/notes/pics/111550407277293.gif deleted file mode 100644 index ce296a0562..0000000000 Binary files a/docs/notes/pics/111550407277293.gif and /dev/null differ diff --git a/docs/notes/pics/111550414182638.gif b/docs/notes/pics/111550414182638.gif deleted file mode 100644 index 5d9c6f0013..0000000000 Binary files a/docs/notes/pics/111550414182638.gif and /dev/null differ diff --git a/docs/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png b/docs/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png deleted file mode 100644 index 19cd15437a..0000000000 Binary files a/docs/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png and /dev/null differ diff --git a/docs/notes/pics/11548741556940.gif b/docs/notes/pics/11548741556940.gif deleted file mode 100644 index 25571c544c..0000000000 Binary files a/docs/notes/pics/11548741556940.gif and /dev/null differ diff --git a/docs/notes/pics/11548742010310.gif b/docs/notes/pics/11548742010310.gif deleted file mode 100644 index 7bc747345d..0000000000 Binary files a/docs/notes/pics/11548742010310.gif and /dev/null differ diff --git a/docs/notes/pics/11548742157520.gif b/docs/notes/pics/11548742157520.gif deleted file mode 100644 index 6fbe4bf26b..0000000000 Binary files a/docs/notes/pics/11548742157520.gif and /dev/null differ diff --git a/docs/notes/pics/11550465817827.gif b/docs/notes/pics/11550465817827.gif deleted file mode 100644 index 5a718e7035..0000000000 Binary files a/docs/notes/pics/11550465817827.gif and /dev/null differ diff --git a/docs/notes/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg b/docs/notes/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg deleted file mode 100644 index 1804cdb199..0000000000 Binary files a/docs/notes/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg and /dev/null differ diff --git a/docs/notes/pics/11_200.png b/docs/notes/pics/11_200.png deleted file mode 100644 index 429663222a..0000000000 Binary files a/docs/notes/pics/11_200.png and /dev/null differ diff --git a/docs/notes/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png b/docs/notes/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png deleted file mode 100644 index 5368338645..0000000000 Binary files a/docs/notes/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png and /dev/null differ diff --git a/docs/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png b/docs/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png deleted file mode 100644 index b44fa99629..0000000000 Binary files a/docs/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png and /dev/null differ diff --git a/docs/notes/pics/121550407878282.gif b/docs/notes/pics/121550407878282.gif deleted file mode 100644 index 5a17a5538a..0000000000 Binary files a/docs/notes/pics/121550407878282.gif and /dev/null differ diff --git a/docs/notes/pics/131550414680831.gif b/docs/notes/pics/131550414680831.gif deleted file mode 100644 index caa4d001f5..0000000000 Binary files a/docs/notes/pics/131550414680831.gif and /dev/null differ diff --git a/docs/notes/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png b/docs/notes/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png deleted file mode 100644 index 10d2aab07d..0000000000 Binary files a/docs/notes/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png and /dev/null differ diff --git a/docs/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png b/docs/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png deleted file mode 100644 index a5ac33caa3..0000000000 Binary files a/docs/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png and /dev/null differ diff --git a/docs/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png b/docs/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png deleted file mode 100644 index 27f1c9f152..0000000000 Binary files a/docs/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png and /dev/null differ diff --git a/docs/notes/pics/141550414746389.gif b/docs/notes/pics/141550414746389.gif deleted file mode 100644 index 17cb256226..0000000000 Binary files a/docs/notes/pics/141550414746389.gif and /dev/null differ diff --git a/docs/notes/pics/1492928105791_3.png b/docs/notes/pics/1492928105791_3.png deleted file mode 100644 index d18fc1cae9..0000000000 Binary files a/docs/notes/pics/1492928105791_3.png and /dev/null differ diff --git a/docs/notes/pics/1492928416812_4.png b/docs/notes/pics/1492928416812_4.png deleted file mode 100644 index a43a731b5e..0000000000 Binary files a/docs/notes/pics/1492928416812_4.png and /dev/null differ diff --git a/docs/notes/pics/1492929000361_5.png b/docs/notes/pics/1492929000361_5.png deleted file mode 100644 index 919d1222cf..0000000000 Binary files a/docs/notes/pics/1492929000361_5.png and /dev/null differ diff --git a/docs/notes/pics/1492929444818_6.png b/docs/notes/pics/1492929444818_6.png deleted file mode 100644 index 0aea3f9afe..0000000000 Binary files a/docs/notes/pics/1492929444818_6.png and /dev/null differ diff --git a/docs/notes/pics/1492929553651_7.png b/docs/notes/pics/1492929553651_7.png deleted file mode 100644 index 13cf0b4eec..0000000000 Binary files a/docs/notes/pics/1492929553651_7.png and /dev/null differ diff --git a/docs/notes/pics/1492930243286_8.png b/docs/notes/pics/1492930243286_8.png deleted file mode 100644 index 6ee721ff78..0000000000 Binary files a/docs/notes/pics/1492930243286_8.png and /dev/null differ diff --git a/docs/notes/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png b/docs/notes/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png deleted file mode 100644 index 399846e104..0000000000 Binary files a/docs/notes/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png and /dev/null differ diff --git a/docs/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png b/docs/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png deleted file mode 100644 index ec381029db..0000000000 Binary files a/docs/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png and /dev/null differ diff --git a/docs/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg b/docs/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg deleted file mode 100644 index cbba7f360c..0000000000 Binary files a/docs/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg and /dev/null differ diff --git a/docs/notes/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png b/docs/notes/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png deleted file mode 100644 index 85d93b2705..0000000000 Binary files a/docs/notes/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png and /dev/null differ diff --git a/docs/notes/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg b/docs/notes/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg deleted file mode 100644 index a61ab275ca..0000000000 Binary files a/docs/notes/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg and /dev/null differ diff --git a/docs/notes/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png b/docs/notes/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png deleted file mode 100644 index 276969a70e..0000000000 Binary files a/docs/notes/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png and /dev/null differ diff --git a/docs/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png b/docs/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png deleted file mode 100644 index 1523ad79dd..0000000000 Binary files a/docs/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png and /dev/null differ diff --git a/docs/notes/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg b/docs/notes/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg deleted file mode 100644 index 306d1c66e2..0000000000 Binary files a/docs/notes/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg and /dev/null differ diff --git a/docs/notes/pics/1_200.png b/docs/notes/pics/1_200.png deleted file mode 100644 index c6dc2bb569..0000000000 Binary files a/docs/notes/pics/1_200.png and /dev/null differ diff --git a/docs/notes/pics/1_2001550415765493.png b/docs/notes/pics/1_2001550415765493.png deleted file mode 100644 index d1c091ca10..0000000000 Binary files a/docs/notes/pics/1_2001550415765493.png and /dev/null differ diff --git a/docs/notes/pics/1_2001550465428749.png b/docs/notes/pics/1_2001550465428749.png deleted file mode 100644 index e21e3837c0..0000000000 Binary files a/docs/notes/pics/1_2001550465428749.png and /dev/null differ diff --git a/docs/notes/pics/1_2001550547261811.png b/docs/notes/pics/1_2001550547261811.png deleted file mode 100644 index a3331e43f2..0000000000 Binary files a/docs/notes/pics/1_2001550547261811.png and /dev/null differ diff --git a/docs/notes/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png b/docs/notes/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png deleted file mode 100644 index c459230500..0000000000 Binary files a/docs/notes/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png and /dev/null differ diff --git a/docs/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg b/docs/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg deleted file mode 100644 index 4809984f2b..0000000000 Binary files a/docs/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg and /dev/null differ diff --git a/docs/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png b/docs/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png deleted file mode 100644 index 590a4299ba..0000000000 Binary files a/docs/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png and /dev/null differ diff --git a/docs/notes/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg b/docs/notes/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg deleted file mode 100644 index 60edac95f1..0000000000 Binary files a/docs/notes/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg and /dev/null differ diff --git a/docs/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png b/docs/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png deleted file mode 100644 index e4286077d3..0000000000 Binary files a/docs/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png and /dev/null differ diff --git a/docs/notes/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png b/docs/notes/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png deleted file mode 100644 index 35e992f75a..0000000000 Binary files a/docs/notes/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png and /dev/null differ diff --git a/docs/notes/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png b/docs/notes/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png deleted file mode 100644 index 535968032a..0000000000 Binary files a/docs/notes/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png and /dev/null differ diff --git a/docs/notes/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg b/docs/notes/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg deleted file mode 100644 index a9effcf782..0000000000 Binary files a/docs/notes/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg and /dev/null differ diff --git a/docs/notes/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png b/docs/notes/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png deleted file mode 100644 index 68ab02d1e7..0000000000 Binary files a/docs/notes/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png and /dev/null differ diff --git a/docs/notes/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg b/docs/notes/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg deleted file mode 100644 index c1776a5c5a..0000000000 Binary files a/docs/notes/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg and /dev/null differ diff --git a/docs/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png b/docs/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png deleted file mode 100644 index 0f7d6660b7..0000000000 Binary files a/docs/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png and /dev/null differ diff --git a/docs/notes/pics/2017-06-11-ca.png b/docs/notes/pics/2017-06-11-ca.png deleted file mode 100644 index 550292c106..0000000000 Binary files a/docs/notes/pics/2017-06-11-ca.png and /dev/null differ diff --git a/docs/notes/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png b/docs/notes/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png deleted file mode 100644 index 7da1a21f74..0000000000 Binary files a/docs/notes/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png and /dev/null differ diff --git a/docs/notes/pics/21550397584141.gif b/docs/notes/pics/21550397584141.gif deleted file mode 100644 index df5c1792b0..0000000000 Binary files a/docs/notes/pics/21550397584141.gif and /dev/null differ diff --git a/docs/notes/pics/21550465890674.gif b/docs/notes/pics/21550465890674.gif deleted file mode 100644 index 2ef91795b6..0000000000 Binary files a/docs/notes/pics/21550465890674.gif and /dev/null differ diff --git a/docs/notes/pics/220790c6-4377-4a2e-8686-58398afc8a18.png b/docs/notes/pics/220790c6-4377-4a2e-8686-58398afc8a18.png deleted file mode 100644 index 7910525710..0000000000 Binary files a/docs/notes/pics/220790c6-4377-4a2e-8686-58398afc8a18.png and /dev/null differ diff --git a/docs/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg b/docs/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg deleted file mode 100644 index 2ce6dd1faa..0000000000 Binary files a/docs/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg and /dev/null differ diff --git a/docs/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg b/docs/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg deleted file mode 100644 index 92c11bfd91..0000000000 Binary files a/docs/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg and /dev/null differ diff --git a/docs/notes/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg b/docs/notes/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg deleted file mode 100644 index cf6ab00931..0000000000 Binary files a/docs/notes/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg and /dev/null differ diff --git a/docs/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg b/docs/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg deleted file mode 100644 index c6de4ac5b9..0000000000 Binary files a/docs/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg and /dev/null differ diff --git a/docs/notes/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg b/docs/notes/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg deleted file mode 100644 index 72177f773e..0000000000 Binary files a/docs/notes/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg and /dev/null differ diff --git a/docs/notes/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png b/docs/notes/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png deleted file mode 100644 index 1aee414c9a..0000000000 Binary files a/docs/notes/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png and /dev/null differ diff --git a/docs/notes/pics/280f7728-594f-4811-a03a-fa8d32c013da.png b/docs/notes/pics/280f7728-594f-4811-a03a-fa8d32c013da.png deleted file mode 100644 index 526b684733..0000000000 Binary files a/docs/notes/pics/280f7728-594f-4811-a03a-fa8d32c013da.png and /dev/null differ diff --git a/docs/notes/pics/2861e923-4862-4526-881c-15529279d49c.png b/docs/notes/pics/2861e923-4862-4526-881c-15529279d49c.png deleted file mode 100644 index 6cc26c26bf..0000000000 Binary files a/docs/notes/pics/2861e923-4862-4526-881c-15529279d49c.png and /dev/null differ diff --git a/docs/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg b/docs/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg deleted file mode 100644 index 38e0905b3f..0000000000 Binary files a/docs/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg and /dev/null differ diff --git a/docs/notes/pics/293d2af9-de1d-403e-bed0-85d029383528.png b/docs/notes/pics/293d2af9-de1d-403e-bed0-85d029383528.png deleted file mode 100644 index 36765e3261..0000000000 Binary files a/docs/notes/pics/293d2af9-de1d-403e-bed0-85d029383528.png and /dev/null differ diff --git a/docs/notes/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg b/docs/notes/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg deleted file mode 100644 index 09ad339e36..0000000000 Binary files a/docs/notes/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg and /dev/null differ diff --git a/docs/notes/pics/2_200.png b/docs/notes/pics/2_200.png deleted file mode 100644 index c292d4ef80..0000000000 Binary files a/docs/notes/pics/2_200.png and /dev/null differ diff --git a/docs/notes/pics/2_2001550426232419.png b/docs/notes/pics/2_2001550426232419.png deleted file mode 100644 index 810892ec49..0000000000 Binary files a/docs/notes/pics/2_2001550426232419.png and /dev/null differ diff --git a/docs/notes/pics/2_2001550466182933.png b/docs/notes/pics/2_2001550466182933.png deleted file mode 100644 index 79d1976259..0000000000 Binary files a/docs/notes/pics/2_2001550466182933.png and /dev/null differ diff --git a/docs/notes/pics/2_2001550547456403.png b/docs/notes/pics/2_2001550547456403.png deleted file mode 100644 index c9e53fe0b4..0000000000 Binary files a/docs/notes/pics/2_2001550547456403.png and /dev/null differ diff --git a/docs/notes/pics/2_2001550810366269.png b/docs/notes/pics/2_2001550810366269.png deleted file mode 100644 index 2d65f04639..0000000000 Binary files a/docs/notes/pics/2_2001550810366269.png and /dev/null differ diff --git a/docs/notes/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png b/docs/notes/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png deleted file mode 100644 index a97e49a6b7..0000000000 Binary files a/docs/notes/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png and /dev/null differ diff --git a/docs/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg b/docs/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg deleted file mode 100644 index 983ddd7070..0000000000 Binary files a/docs/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg and /dev/null differ diff --git a/docs/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png b/docs/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png deleted file mode 100644 index 384f7ef0bc..0000000000 Binary files a/docs/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png and /dev/null differ diff --git a/docs/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg b/docs/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg deleted file mode 100644 index 5398511f00..0000000000 Binary files a/docs/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg and /dev/null differ diff --git a/docs/notes/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png b/docs/notes/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png deleted file mode 100644 index 701e37f291..0000000000 Binary files a/docs/notes/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png and /dev/null differ diff --git a/docs/notes/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg b/docs/notes/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg deleted file mode 100644 index 5799fe392f..0000000000 Binary files a/docs/notes/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg and /dev/null differ diff --git a/docs/notes/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg b/docs/notes/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg deleted file mode 100644 index 0983229b68..0000000000 Binary files a/docs/notes/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg and /dev/null differ diff --git a/docs/notes/pics/3086c248-b552-499e-b101-9cffe5c2773e.png b/docs/notes/pics/3086c248-b552-499e-b101-9cffe5c2773e.png deleted file mode 100644 index c23e85c959..0000000000 Binary files a/docs/notes/pics/3086c248-b552-499e-b101-9cffe5c2773e.png and /dev/null differ diff --git a/docs/notes/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg b/docs/notes/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg deleted file mode 100644 index a805e7f95c..0000000000 Binary files a/docs/notes/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg and /dev/null differ diff --git a/docs/notes/pics/31550398353573.gif b/docs/notes/pics/31550398353573.gif deleted file mode 100644 index f9008efe79..0000000000 Binary files a/docs/notes/pics/31550398353573.gif and /dev/null differ diff --git a/docs/notes/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png b/docs/notes/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png deleted file mode 100644 index 3316254e98..0000000000 Binary files a/docs/notes/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png and /dev/null differ diff --git a/docs/notes/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg b/docs/notes/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg deleted file mode 100644 index 9de5783ddb..0000000000 Binary files a/docs/notes/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg and /dev/null differ diff --git a/docs/notes/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png b/docs/notes/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png deleted file mode 100644 index 6e8383f35e..0000000000 Binary files a/docs/notes/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png and /dev/null differ diff --git a/docs/notes/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png b/docs/notes/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png deleted file mode 100644 index cb7e36811d..0000000000 Binary files a/docs/notes/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png and /dev/null differ diff --git a/docs/notes/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png b/docs/notes/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png deleted file mode 100644 index 76d45e1984..0000000000 Binary files a/docs/notes/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png and /dev/null differ diff --git a/docs/notes/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png b/docs/notes/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png deleted file mode 100644 index c74c59ef98..0000000000 Binary files a/docs/notes/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png and /dev/null differ diff --git a/docs/notes/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png b/docs/notes/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png deleted file mode 100644 index 3b05b25b96..0000000000 Binary files a/docs/notes/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png and /dev/null differ diff --git a/docs/notes/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png b/docs/notes/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png deleted file mode 100644 index 5f5f5636d7..0000000000 Binary files a/docs/notes/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png and /dev/null differ diff --git a/docs/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg b/docs/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg deleted file mode 100644 index 78eb732bf3..0000000000 Binary files a/docs/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg and /dev/null differ diff --git a/docs/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png b/docs/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png deleted file mode 100644 index b486ec0269..0000000000 Binary files a/docs/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png and /dev/null differ diff --git a/docs/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg b/docs/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg deleted file mode 100644 index 60c19f88b5..0000000000 Binary files a/docs/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg and /dev/null differ diff --git a/docs/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png b/docs/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png deleted file mode 100644 index 8e363e4784..0000000000 Binary files a/docs/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png and /dev/null differ diff --git a/docs/notes/pics/3_200.png b/docs/notes/pics/3_200.png deleted file mode 100644 index a35b29e480..0000000000 Binary files a/docs/notes/pics/3_200.png and /dev/null differ diff --git a/docs/notes/pics/3_2001550473624627.png b/docs/notes/pics/3_2001550473624627.png deleted file mode 100644 index d8bc9db0c5..0000000000 Binary files a/docs/notes/pics/3_2001550473624627.png and /dev/null differ diff --git a/docs/notes/pics/3_2001550547558008.png b/docs/notes/pics/3_2001550547558008.png deleted file mode 100644 index a42f6dc0c7..0000000000 Binary files a/docs/notes/pics/3_2001550547558008.png and /dev/null differ diff --git a/docs/notes/pics/3_2001550810442775.png b/docs/notes/pics/3_2001550810442775.png deleted file mode 100644 index 3a29803682..0000000000 Binary files a/docs/notes/pics/3_2001550810442775.png and /dev/null differ diff --git a/docs/notes/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png b/docs/notes/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png deleted file mode 100644 index 13f53b6a58..0000000000 Binary files a/docs/notes/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png and /dev/null differ diff --git a/docs/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png b/docs/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png deleted file mode 100644 index 2f39530c99..0000000000 Binary files a/docs/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png and /dev/null differ diff --git a/docs/notes/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png b/docs/notes/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png deleted file mode 100644 index 65bb211cd6..0000000000 Binary files a/docs/notes/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png and /dev/null differ diff --git a/docs/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png b/docs/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png deleted file mode 100644 index 621245d7cf..0000000000 Binary files a/docs/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png and /dev/null differ diff --git a/docs/notes/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png b/docs/notes/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png deleted file mode 100644 index 266a4687f7..0000000000 Binary files a/docs/notes/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png and /dev/null differ diff --git a/docs/notes/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg b/docs/notes/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg deleted file mode 100644 index 355cdf5730..0000000000 Binary files a/docs/notes/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg and /dev/null differ diff --git a/docs/notes/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png b/docs/notes/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png deleted file mode 100644 index d623b1e3f2..0000000000 Binary files a/docs/notes/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png and /dev/null differ diff --git a/docs/notes/pics/420f4dc0-6c4b-486c-afea-274299014462.png b/docs/notes/pics/420f4dc0-6c4b-486c-afea-274299014462.png deleted file mode 100644 index 257d73c685..0000000000 Binary files a/docs/notes/pics/420f4dc0-6c4b-486c-afea-274299014462.png and /dev/null differ diff --git a/docs/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png b/docs/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png deleted file mode 100644 index 7b8c8c08ec..0000000000 Binary files a/docs/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png and /dev/null differ diff --git a/docs/notes/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg b/docs/notes/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg deleted file mode 100644 index 556c01ddfe..0000000000 Binary files a/docs/notes/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg and /dev/null differ diff --git a/docs/notes/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png b/docs/notes/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png deleted file mode 100644 index 010278a2ec..0000000000 Binary files a/docs/notes/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png and /dev/null differ diff --git a/docs/notes/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png b/docs/notes/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png deleted file mode 100644 index be98ce69c1..0000000000 Binary files a/docs/notes/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png and /dev/null differ diff --git a/docs/notes/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png b/docs/notes/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png deleted file mode 100644 index 6db68ca535..0000000000 Binary files a/docs/notes/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png and /dev/null differ diff --git a/docs/notes/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png b/docs/notes/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png deleted file mode 100644 index 0dcc870668..0000000000 Binary files a/docs/notes/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png and /dev/null differ diff --git a/docs/notes/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png b/docs/notes/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png deleted file mode 100644 index 83d5935956..0000000000 Binary files a/docs/notes/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png and /dev/null differ diff --git a/docs/notes/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg b/docs/notes/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg deleted file mode 100644 index 9af7bc09c0..0000000000 Binary files a/docs/notes/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg and /dev/null differ diff --git a/docs/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png b/docs/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png deleted file mode 100644 index f9f73fafda..0000000000 Binary files a/docs/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png and /dev/null differ diff --git a/docs/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png b/docs/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png deleted file mode 100644 index 9c6b68dc54..0000000000 Binary files a/docs/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png and /dev/null differ diff --git a/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png b/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png deleted file mode 100644 index e853dc13cc..0000000000 Binary files a/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png and /dev/null differ diff --git a/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d62367577.png b/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d62367577.png deleted file mode 100644 index e853dc13cc..0000000000 Binary files a/docs/notes/pics/49e53613-46f8-4308-9ee5-c09d62367577.png and /dev/null differ diff --git a/docs/notes/pics/4_2001550473915641.png b/docs/notes/pics/4_2001550473915641.png deleted file mode 100644 index f86ccece2e..0000000000 Binary files a/docs/notes/pics/4_2001550473915641.png and /dev/null differ diff --git a/docs/notes/pics/4_2001550547640585.png b/docs/notes/pics/4_2001550547640585.png deleted file mode 100644 index d3f5c798ef..0000000000 Binary files a/docs/notes/pics/4_2001550547640585.png and /dev/null differ diff --git a/docs/notes/pics/4_2001550810732828.png b/docs/notes/pics/4_2001550810732828.png deleted file mode 100644 index 134cfb6b3a..0000000000 Binary files a/docs/notes/pics/4_2001550810732828.png and /dev/null differ diff --git a/docs/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png b/docs/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png deleted file mode 100644 index 25ac4f70b3..0000000000 Binary files a/docs/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png and /dev/null differ diff --git a/docs/notes/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png b/docs/notes/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png deleted file mode 100644 index a4a5e7b045..0000000000 Binary files a/docs/notes/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png and /dev/null differ diff --git a/docs/notes/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg b/docs/notes/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg deleted file mode 100644 index febf7a179e..0000000000 Binary files a/docs/notes/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg and /dev/null differ diff --git a/docs/notes/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png b/docs/notes/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png deleted file mode 100644 index 649d16cd80..0000000000 Binary files a/docs/notes/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png and /dev/null differ diff --git a/docs/notes/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg b/docs/notes/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg deleted file mode 100644 index f81e570723..0000000000 Binary files a/docs/notes/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg and /dev/null differ diff --git a/docs/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg b/docs/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg deleted file mode 100644 index 4a3f798bca..0000000000 Binary files a/docs/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg and /dev/null differ diff --git a/docs/notes/pics/51550399426594.gif b/docs/notes/pics/51550399426594.gif deleted file mode 100644 index 6e93179134..0000000000 Binary files a/docs/notes/pics/51550399426594.gif and /dev/null differ diff --git a/docs/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg b/docs/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg deleted file mode 100644 index 595cdc584e..0000000000 Binary files a/docs/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg and /dev/null differ diff --git a/docs/notes/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png b/docs/notes/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png deleted file mode 100644 index d49b1727d8..0000000000 Binary files a/docs/notes/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png and /dev/null differ diff --git a/docs/notes/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg b/docs/notes/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg deleted file mode 100644 index 32e1f0545d..0000000000 Binary files a/docs/notes/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg and /dev/null differ diff --git a/docs/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg b/docs/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg deleted file mode 100644 index a449f38cfb..0000000000 Binary files a/docs/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg and /dev/null differ diff --git a/docs/notes/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png b/docs/notes/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png deleted file mode 100644 index 29bbc9de94..0000000000 Binary files a/docs/notes/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png and /dev/null differ diff --git a/docs/notes/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg b/docs/notes/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg deleted file mode 100644 index 0ffa2a5bc4..0000000000 Binary files a/docs/notes/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg and /dev/null differ diff --git a/docs/notes/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png b/docs/notes/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png deleted file mode 100644 index ad60a7e02c..0000000000 Binary files a/docs/notes/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png and /dev/null differ diff --git a/docs/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png b/docs/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png deleted file mode 100644 index 525b323af1..0000000000 Binary files a/docs/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png and /dev/null differ diff --git a/docs/notes/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg b/docs/notes/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg deleted file mode 100644 index 28fad66e28..0000000000 Binary files a/docs/notes/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg and /dev/null differ diff --git a/docs/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png b/docs/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png deleted file mode 100644 index b74f69b79a..0000000000 Binary files a/docs/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png and /dev/null differ diff --git a/docs/notes/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg b/docs/notes/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg deleted file mode 100644 index 5feb11d455..0000000000 Binary files a/docs/notes/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg and /dev/null differ diff --git a/docs/notes/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg b/docs/notes/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg deleted file mode 100644 index a695be3a4e..0000000000 Binary files a/docs/notes/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg and /dev/null differ diff --git a/docs/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg b/docs/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg deleted file mode 100644 index 62b39e4b00..0000000000 Binary files a/docs/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg and /dev/null differ diff --git a/docs/notes/pics/5_200.png b/docs/notes/pics/5_200.png deleted file mode 100644 index f2f98c4323..0000000000 Binary files a/docs/notes/pics/5_200.png and /dev/null differ diff --git a/docs/notes/pics/5_2001550474110029.png b/docs/notes/pics/5_2001550474110029.png deleted file mode 100644 index 858a456f90..0000000000 Binary files a/docs/notes/pics/5_2001550474110029.png and /dev/null differ diff --git a/docs/notes/pics/5_2001550810982206.png b/docs/notes/pics/5_2001550810982206.png deleted file mode 100644 index f2f98c4323..0000000000 Binary files a/docs/notes/pics/5_2001550810982206.png and /dev/null differ diff --git a/docs/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg b/docs/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg deleted file mode 100644 index ba83d34211..0000000000 Binary files a/docs/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg and /dev/null differ diff --git a/docs/notes/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png b/docs/notes/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png deleted file mode 100644 index 110bea3581..0000000000 Binary files a/docs/notes/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png and /dev/null differ diff --git a/docs/notes/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg b/docs/notes/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg deleted file mode 100644 index f2a6c03ae0..0000000000 Binary files a/docs/notes/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg and /dev/null differ diff --git a/docs/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png b/docs/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png deleted file mode 100644 index bf0834d538..0000000000 Binary files a/docs/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png and /dev/null differ diff --git a/docs/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png b/docs/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png deleted file mode 100644 index 900ee96329..0000000000 Binary files a/docs/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png and /dev/null differ diff --git a/docs/notes/pics/61550402057509.gif b/docs/notes/pics/61550402057509.gif deleted file mode 100644 index 7ba6f2807c..0000000000 Binary files a/docs/notes/pics/61550402057509.gif and /dev/null differ diff --git a/docs/notes/pics/61942711-45a0-4e11-bbc9-434e31436f33.png b/docs/notes/pics/61942711-45a0-4e11-bbc9-434e31436f33.png deleted file mode 100644 index 8f093ef638..0000000000 Binary files a/docs/notes/pics/61942711-45a0-4e11-bbc9-434e31436f33.png and /dev/null differ diff --git a/docs/notes/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png b/docs/notes/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png deleted file mode 100644 index 14d98b8c67..0000000000 Binary files a/docs/notes/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png and /dev/null differ diff --git a/docs/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg b/docs/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg deleted file mode 100644 index 90e9065cde..0000000000 Binary files a/docs/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg and /dev/null differ diff --git a/docs/notes/pics/63010737-2cb4-48f3-999f-09194481b227.png b/docs/notes/pics/63010737-2cb4-48f3-999f-09194481b227.png deleted file mode 100644 index c9d17851ce..0000000000 Binary files a/docs/notes/pics/63010737-2cb4-48f3-999f-09194481b227.png and /dev/null differ diff --git a/docs/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg b/docs/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg deleted file mode 100644 index d647162161..0000000000 Binary files a/docs/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg and /dev/null differ diff --git a/docs/notes/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png b/docs/notes/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png deleted file mode 100644 index 053a3dc69b..0000000000 Binary files a/docs/notes/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png and /dev/null differ diff --git a/docs/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png b/docs/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png deleted file mode 100644 index f488859e0e..0000000000 Binary files a/docs/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png and /dev/null differ diff --git a/docs/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg b/docs/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg deleted file mode 100644 index fc86a2360a..0000000000 Binary files a/docs/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg and /dev/null differ diff --git a/docs/notes/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg b/docs/notes/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg deleted file mode 100644 index 693d7b979d..0000000000 Binary files a/docs/notes/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg and /dev/null differ diff --git a/docs/notes/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg b/docs/notes/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg deleted file mode 100644 index 48f3136c0f..0000000000 Binary files a/docs/notes/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg and /dev/null differ diff --git a/docs/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg b/docs/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg deleted file mode 100644 index 7035f00107..0000000000 Binary files a/docs/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg and /dev/null differ diff --git a/docs/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png b/docs/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png deleted file mode 100644 index b63baeb210..0000000000 Binary files a/docs/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png and /dev/null differ diff --git a/docs/notes/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png b/docs/notes/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png deleted file mode 100644 index 39d7ec60fa..0000000000 Binary files a/docs/notes/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png and /dev/null differ diff --git a/docs/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg b/docs/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg deleted file mode 100644 index d82f46eb69..0000000000 Binary files a/docs/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg and /dev/null differ diff --git a/docs/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png b/docs/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png deleted file mode 100644 index b61e2174cd..0000000000 Binary files a/docs/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png and /dev/null differ diff --git a/docs/notes/pics/6_2001550474388460.png b/docs/notes/pics/6_2001550474388460.png deleted file mode 100644 index dadfd8fcc1..0000000000 Binary files a/docs/notes/pics/6_2001550474388460.png and /dev/null differ diff --git a/docs/notes/pics/6_2001550476096035.png b/docs/notes/pics/6_2001550476096035.png deleted file mode 100644 index bbf8dfd271..0000000000 Binary files a/docs/notes/pics/6_2001550476096035.png and /dev/null differ diff --git a/docs/notes/pics/6_2001550811175246.png b/docs/notes/pics/6_2001550811175246.png deleted file mode 100644 index 5d124af29a..0000000000 Binary files a/docs/notes/pics/6_2001550811175246.png and /dev/null differ diff --git a/docs/notes/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg b/docs/notes/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg deleted file mode 100644 index 48b1bd5db8..0000000000 Binary files a/docs/notes/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg and /dev/null differ diff --git a/docs/notes/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png b/docs/notes/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png deleted file mode 100644 index 5b9202f6fd..0000000000 Binary files a/docs/notes/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png and /dev/null differ diff --git a/docs/notes/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png b/docs/notes/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png deleted file mode 100644 index c951760fef..0000000000 Binary files a/docs/notes/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png and /dev/null differ diff --git a/docs/notes/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png b/docs/notes/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png deleted file mode 100644 index a830a7d465..0000000000 Binary files a/docs/notes/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png and /dev/null differ diff --git a/docs/notes/pics/71550414107576.gif b/docs/notes/pics/71550414107576.gif deleted file mode 100644 index fad8851319..0000000000 Binary files a/docs/notes/pics/71550414107576.gif and /dev/null differ diff --git a/docs/notes/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png b/docs/notes/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png deleted file mode 100644 index 4e99159f15..0000000000 Binary files a/docs/notes/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png and /dev/null differ diff --git a/docs/notes/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png b/docs/notes/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png deleted file mode 100644 index 5299728f6b..0000000000 Binary files a/docs/notes/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png and /dev/null differ diff --git a/docs/notes/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg b/docs/notes/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg deleted file mode 100644 index dda05f945b..0000000000 Binary files a/docs/notes/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg and /dev/null differ diff --git a/docs/notes/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg b/docs/notes/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg deleted file mode 100644 index b045287d5c..0000000000 Binary files a/docs/notes/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg and /dev/null differ diff --git a/docs/notes/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg b/docs/notes/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg deleted file mode 100644 index 5b905db167..0000000000 Binary files a/docs/notes/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg and /dev/null differ diff --git a/docs/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png b/docs/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png deleted file mode 100644 index 837f995a5c..0000000000 Binary files a/docs/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png and /dev/null differ diff --git a/docs/notes/pics/75996367-adc2-4be1-a92a-09cc4347638e.png b/docs/notes/pics/75996367-adc2-4be1-a92a-09cc4347638e.png deleted file mode 100644 index ec381029db..0000000000 Binary files a/docs/notes/pics/75996367-adc2-4be1-a92a-09cc4347638e.png and /dev/null differ diff --git a/docs/notes/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png b/docs/notes/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png deleted file mode 100644 index e8a69bff48..0000000000 Binary files a/docs/notes/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png and /dev/null differ diff --git a/docs/notes/pics/76a49594323247f21c9b3a69945445ee.png b/docs/notes/pics/76a49594323247f21c9b3a69945445ee.png deleted file mode 100644 index 788ba0b121..0000000000 Binary files a/docs/notes/pics/76a49594323247f21c9b3a69945445ee.png and /dev/null differ diff --git a/docs/notes/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg b/docs/notes/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg deleted file mode 100644 index 598ed2ed62..0000000000 Binary files a/docs/notes/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg and /dev/null differ diff --git a/docs/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png b/docs/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png deleted file mode 100644 index 6f8a3c52df..0000000000 Binary files a/docs/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png and /dev/null differ diff --git a/docs/notes/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png b/docs/notes/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png deleted file mode 100644 index d146ec06cf..0000000000 Binary files a/docs/notes/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png and /dev/null differ diff --git a/docs/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png b/docs/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png deleted file mode 100644 index 7adddd918e..0000000000 Binary files a/docs/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png and /dev/null differ diff --git a/docs/notes/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png b/docs/notes/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png deleted file mode 100644 index d2a300049b..0000000000 Binary files a/docs/notes/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png and /dev/null differ diff --git a/docs/notes/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png b/docs/notes/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png deleted file mode 100644 index 4938fc5865..0000000000 Binary files a/docs/notes/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png and /dev/null differ diff --git a/docs/notes/pics/7_2001550475133282.png b/docs/notes/pics/7_2001550475133282.png deleted file mode 100644 index 884eca8319..0000000000 Binary files a/docs/notes/pics/7_2001550475133282.png and /dev/null differ diff --git a/docs/notes/pics/7_2001550811502556.png b/docs/notes/pics/7_2001550811502556.png deleted file mode 100644 index 41da48f593..0000000000 Binary files a/docs/notes/pics/7_2001550811502556.png and /dev/null differ diff --git a/docs/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg b/docs/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg deleted file mode 100644 index a048d09419..0000000000 Binary files a/docs/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg and /dev/null differ diff --git a/docs/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg b/docs/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg deleted file mode 100644 index 8d40765ea0..0000000000 Binary files a/docs/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg and /dev/null differ diff --git a/docs/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png b/docs/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png deleted file mode 100644 index 711fb45b0d..0000000000 Binary files a/docs/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png and /dev/null differ diff --git a/docs/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png b/docs/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png deleted file mode 100644 index 8b5ce20453..0000000000 Binary files a/docs/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png and /dev/null differ diff --git a/docs/notes/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png b/docs/notes/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png deleted file mode 100644 index cf1868b606..0000000000 Binary files a/docs/notes/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png and /dev/null differ diff --git a/docs/notes/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg b/docs/notes/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg deleted file mode 100644 index 7dcb6d1c75..0000000000 Binary files a/docs/notes/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg and /dev/null differ diff --git a/docs/notes/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png b/docs/notes/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png deleted file mode 100644 index c90982c240..0000000000 Binary files a/docs/notes/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png and /dev/null differ diff --git a/docs/notes/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png b/docs/notes/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png deleted file mode 100644 index 56ca814b67..0000000000 Binary files a/docs/notes/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png and /dev/null differ diff --git a/docs/notes/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png b/docs/notes/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png deleted file mode 100644 index 0741f44ee2..0000000000 Binary files a/docs/notes/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png and /dev/null differ diff --git a/docs/notes/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png b/docs/notes/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png deleted file mode 100644 index 66e26e808a..0000000000 Binary files a/docs/notes/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png and /dev/null differ diff --git a/docs/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg b/docs/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg deleted file mode 100644 index 5753388eeb..0000000000 Binary files a/docs/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg and /dev/null differ diff --git a/docs/notes/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png b/docs/notes/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png deleted file mode 100644 index 57e71b9c7b..0000000000 Binary files a/docs/notes/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png and /dev/null differ diff --git a/docs/notes/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg b/docs/notes/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg deleted file mode 100644 index aad341fd41..0000000000 Binary files a/docs/notes/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg and /dev/null differ diff --git a/docs/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png b/docs/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png deleted file mode 100644 index b636edf5af..0000000000 Binary files a/docs/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png and /dev/null differ diff --git a/docs/notes/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg b/docs/notes/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg deleted file mode 100644 index 7491217dc8..0000000000 Binary files a/docs/notes/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg and /dev/null differ diff --git a/docs/notes/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png b/docs/notes/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png deleted file mode 100644 index 382cc19b1c..0000000000 Binary files a/docs/notes/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png and /dev/null differ diff --git a/docs/notes/pics/80646c77-1f32-484c-810e-af80ce00f902.png b/docs/notes/pics/80646c77-1f32-484c-810e-af80ce00f902.png deleted file mode 100644 index de3f5298d9..0000000000 Binary files a/docs/notes/pics/80646c77-1f32-484c-810e-af80ce00f902.png and /dev/null differ diff --git a/docs/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png b/docs/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png deleted file mode 100644 index 06c8760b9d..0000000000 Binary files a/docs/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png and /dev/null differ diff --git a/docs/notes/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg b/docs/notes/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg deleted file mode 100644 index 5f34004b11..0000000000 Binary files a/docs/notes/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg and /dev/null differ diff --git a/docs/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg b/docs/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg deleted file mode 100644 index 5446f103d0..0000000000 Binary files a/docs/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg and /dev/null differ diff --git a/docs/notes/pics/81550405360028.gif b/docs/notes/pics/81550405360028.gif deleted file mode 100644 index 7960c782a7..0000000000 Binary files a/docs/notes/pics/81550405360028.gif and /dev/null differ diff --git a/docs/notes/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png b/docs/notes/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png deleted file mode 100644 index 2fa695823e..0000000000 Binary files a/docs/notes/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png and /dev/null differ diff --git a/docs/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg b/docs/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg deleted file mode 100644 index 2b510c4365..0000000000 Binary files a/docs/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg and /dev/null differ diff --git a/docs/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg b/docs/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg deleted file mode 100644 index cb83edeaf3..0000000000 Binary files a/docs/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg and /dev/null differ diff --git a/docs/notes/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png b/docs/notes/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png deleted file mode 100644 index be8ab73787..0000000000 Binary files a/docs/notes/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png and /dev/null differ diff --git a/docs/notes/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png b/docs/notes/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png deleted file mode 100644 index 891a9a376a..0000000000 Binary files a/docs/notes/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png and /dev/null differ diff --git a/docs/notes/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png b/docs/notes/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png deleted file mode 100644 index 23fc9fbd4b..0000000000 Binary files a/docs/notes/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png and /dev/null differ diff --git a/docs/notes/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png b/docs/notes/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png deleted file mode 100644 index 5c0b076bb0..0000000000 Binary files a/docs/notes/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png and /dev/null differ diff --git a/docs/notes/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg b/docs/notes/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg deleted file mode 100644 index c68fd3ef92..0000000000 Binary files a/docs/notes/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg and /dev/null differ diff --git a/docs/notes/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png b/docs/notes/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png deleted file mode 100644 index 5423ac8596..0000000000 Binary files a/docs/notes/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png and /dev/null differ diff --git a/docs/notes/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg b/docs/notes/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg deleted file mode 100644 index 0abcf7d6eb..0000000000 Binary files a/docs/notes/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg and /dev/null differ diff --git a/docs/notes/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png b/docs/notes/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png deleted file mode 100644 index f77ff34664..0000000000 Binary files a/docs/notes/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png and /dev/null differ diff --git a/docs/notes/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png b/docs/notes/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png deleted file mode 100644 index 72cc988f12..0000000000 Binary files a/docs/notes/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png and /dev/null differ diff --git a/docs/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png b/docs/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png deleted file mode 100644 index 9eda1deb5f..0000000000 Binary files a/docs/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png and /dev/null differ diff --git a/docs/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png b/docs/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png deleted file mode 100644 index 26cb0153f2..0000000000 Binary files a/docs/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png and /dev/null differ diff --git a/docs/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png b/docs/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png deleted file mode 100644 index 56f83eafa3..0000000000 Binary files a/docs/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png and /dev/null differ diff --git a/docs/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png b/docs/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png deleted file mode 100644 index 14d98b8c67..0000000000 Binary files a/docs/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png and /dev/null differ diff --git a/docs/notes/pics/8_200.png b/docs/notes/pics/8_200.png deleted file mode 100644 index af9d663a58..0000000000 Binary files a/docs/notes/pics/8_200.png and /dev/null differ diff --git a/docs/notes/pics/8_2001550475451664.png b/docs/notes/pics/8_2001550475451664.png deleted file mode 100644 index 772df79c8d..0000000000 Binary files a/docs/notes/pics/8_2001550475451664.png and /dev/null differ diff --git a/docs/notes/pics/8_2001550812038190.png b/docs/notes/pics/8_2001550812038190.png deleted file mode 100644 index 966bd4e220..0000000000 Binary files a/docs/notes/pics/8_2001550812038190.png and /dev/null differ diff --git a/docs/notes/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png b/docs/notes/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png deleted file mode 100644 index 31ae5e0c92..0000000000 Binary files a/docs/notes/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png and /dev/null differ diff --git a/docs/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg b/docs/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg deleted file mode 100644 index dd21813321..0000000000 Binary files a/docs/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg and /dev/null differ diff --git a/docs/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png b/docs/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png deleted file mode 100644 index c90982c240..0000000000 Binary files a/docs/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png and /dev/null differ diff --git a/docs/notes/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png b/docs/notes/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png deleted file mode 100644 index 034205a83f..0000000000 Binary files a/docs/notes/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png and /dev/null differ diff --git a/docs/notes/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png b/docs/notes/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png deleted file mode 100644 index a8e36ee04f..0000000000 Binary files a/docs/notes/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png and /dev/null differ diff --git a/docs/notes/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png b/docs/notes/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png deleted file mode 100644 index 21b8a93048..0000000000 Binary files a/docs/notes/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png and /dev/null differ diff --git a/docs/notes/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg b/docs/notes/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg deleted file mode 100644 index a9e33df46a..0000000000 Binary files a/docs/notes/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg and /dev/null differ diff --git a/docs/notes/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg b/docs/notes/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg deleted file mode 100644 index 7b2d0143b4..0000000000 Binary files a/docs/notes/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg and /dev/null differ diff --git a/docs/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png b/docs/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png deleted file mode 100644 index 28362d4767..0000000000 Binary files a/docs/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png and /dev/null differ diff --git a/docs/notes/pics/9110c1a0-8a54-4145-a814-2477d0128114.png b/docs/notes/pics/9110c1a0-8a54-4145-a814-2477d0128114.png deleted file mode 100644 index 68aa9f9845..0000000000 Binary files a/docs/notes/pics/9110c1a0-8a54-4145-a814-2477d0128114.png and /dev/null differ diff --git a/docs/notes/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg b/docs/notes/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg deleted file mode 100644 index e30c690879..0000000000 Binary files a/docs/notes/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg and /dev/null differ diff --git a/docs/notes/pics/91550405374894.gif b/docs/notes/pics/91550405374894.gif deleted file mode 100644 index 2409a299a4..0000000000 Binary files a/docs/notes/pics/91550405374894.gif and /dev/null differ diff --git a/docs/notes/pics/91550414131331.gif b/docs/notes/pics/91550414131331.gif deleted file mode 100644 index f81aa26409..0000000000 Binary files a/docs/notes/pics/91550414131331.gif and /dev/null differ diff --git a/docs/notes/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png b/docs/notes/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png deleted file mode 100644 index dd90699b63..0000000000 Binary files a/docs/notes/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png and /dev/null differ diff --git a/docs/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg b/docs/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg deleted file mode 100644 index 5bb29598b2..0000000000 Binary files a/docs/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg and /dev/null differ diff --git a/docs/notes/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg b/docs/notes/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg deleted file mode 100644 index 2a736557b7..0000000000 Binary files a/docs/notes/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg and /dev/null differ diff --git a/docs/notes/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png b/docs/notes/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png deleted file mode 100644 index 9064fe0e34..0000000000 Binary files a/docs/notes/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png and /dev/null differ diff --git a/docs/notes/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg b/docs/notes/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg deleted file mode 100644 index 5c53d38bb1..0000000000 Binary files a/docs/notes/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg and /dev/null differ diff --git a/docs/notes/pics/952afa9a-458b-44ce-bba9-463e60162945.png b/docs/notes/pics/952afa9a-458b-44ce-bba9-463e60162945.png deleted file mode 100644 index db93c1ac8b..0000000000 Binary files a/docs/notes/pics/952afa9a-458b-44ce-bba9-463e60162945.png and /dev/null differ diff --git a/docs/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png b/docs/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png deleted file mode 100644 index 4983142a97..0000000000 Binary files a/docs/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png and /dev/null differ diff --git a/docs/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg b/docs/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg deleted file mode 100644 index da95c5d8f5..0000000000 Binary files a/docs/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg and /dev/null differ diff --git a/docs/notes/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg b/docs/notes/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg deleted file mode 100644 index 97104ded98..0000000000 Binary files a/docs/notes/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg and /dev/null differ diff --git a/docs/notes/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg b/docs/notes/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg deleted file mode 100644 index c4e77ba196..0000000000 Binary files a/docs/notes/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg and /dev/null differ diff --git a/docs/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg b/docs/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg deleted file mode 100644 index 8b907d6119..0000000000 Binary files a/docs/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg and /dev/null differ diff --git a/docs/notes/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png b/docs/notes/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png deleted file mode 100644 index 7d87099038..0000000000 Binary files a/docs/notes/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png and /dev/null differ diff --git a/docs/notes/pics/9_200.png b/docs/notes/pics/9_200.png deleted file mode 100644 index 4cf0f56d7c..0000000000 Binary files a/docs/notes/pics/9_200.png and /dev/null differ diff --git a/docs/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg b/docs/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg deleted file mode 100644 index 77c547801f..0000000000 Binary files a/docs/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg and /dev/null differ diff --git a/docs/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png b/docs/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png deleted file mode 100644 index c7f8713859..0000000000 Binary files a/docs/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png and /dev/null differ diff --git a/docs/notes/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg b/docs/notes/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg deleted file mode 100644 index b985fdd887..0000000000 Binary files a/docs/notes/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg and /dev/null differ diff --git a/docs/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png b/docs/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png deleted file mode 100644 index 26ac536e06..0000000000 Binary files a/docs/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png and /dev/null differ diff --git a/docs/notes/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png b/docs/notes/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png deleted file mode 100644 index f726a4c295..0000000000 Binary files a/docs/notes/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png and /dev/null differ diff --git a/docs/notes/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png b/docs/notes/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png deleted file mode 100644 index 89c84a7353..0000000000 Binary files a/docs/notes/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png and /dev/null differ diff --git a/docs/notes/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png b/docs/notes/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png deleted file mode 100644 index 34a8f664b3..0000000000 Binary files a/docs/notes/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png and /dev/null differ diff --git a/docs/notes/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg b/docs/notes/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg deleted file mode 100644 index 9f4f5f0435..0000000000 Binary files a/docs/notes/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg and /dev/null differ diff --git a/docs/notes/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg b/docs/notes/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg deleted file mode 100644 index 15d4b3c8a6..0000000000 Binary files a/docs/notes/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg and /dev/null differ diff --git a/docs/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png b/docs/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png deleted file mode 100644 index ba045e2076..0000000000 Binary files a/docs/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png and /dev/null differ diff --git a/docs/notes/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg b/docs/notes/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg deleted file mode 100644 index 2ab8530ac2..0000000000 Binary files a/docs/notes/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg and /dev/null differ diff --git a/docs/notes/pics/BSD_disk.png b/docs/notes/pics/BSD_disk.png deleted file mode 100644 index 48b0e0e82c..0000000000 Binary files a/docs/notes/pics/BSD_disk.png and /dev/null differ diff --git a/docs/notes/pics/ClienteServidorSockets1521731145260.jpg b/docs/notes/pics/ClienteServidorSockets1521731145260.jpg deleted file mode 100644 index 18976fb392..0000000000 Binary files a/docs/notes/pics/ClienteServidorSockets1521731145260.jpg and /dev/null differ diff --git a/docs/notes/pics/CountdownLatch.png b/docs/notes/pics/CountdownLatch.png deleted file mode 100644 index 1a581bbf34..0000000000 Binary files a/docs/notes/pics/CountdownLatch.png and /dev/null differ diff --git a/docs/notes/pics/CyclicBarrier.png b/docs/notes/pics/CyclicBarrier.png deleted file mode 100644 index 5fe5b88b80..0000000000 Binary files a/docs/notes/pics/CyclicBarrier.png and /dev/null differ diff --git a/docs/notes/pics/DP-Decorator-java.io.png b/docs/notes/pics/DP-Decorator-java.io.png deleted file mode 100644 index 897a510b02..0000000000 Binary files a/docs/notes/pics/DP-Decorator-java.io.png and /dev/null differ diff --git a/docs/notes/pics/GUID_Partition_Table_Scheme.svg.png b/docs/notes/pics/GUID_Partition_Table_Scheme.svg.png deleted file mode 100644 index 6638ae7ab6..0000000000 Binary files a/docs/notes/pics/GUID_Partition_Table_Scheme.svg.png and /dev/null differ diff --git a/docs/notes/pics/HTTP1_x_Connections.png b/docs/notes/pics/HTTP1_x_Connections.png deleted file mode 100644 index d8c18a3cb7..0000000000 Binary files a/docs/notes/pics/HTTP1_x_Connections.png and /dev/null differ diff --git a/docs/notes/pics/HTTP_RequestMessageExample.png b/docs/notes/pics/HTTP_RequestMessageExample.png deleted file mode 100644 index 8fd213cbec..0000000000 Binary files a/docs/notes/pics/HTTP_RequestMessageExample.png and /dev/null differ diff --git a/docs/notes/pics/HTTP_ResponseMessageExample.png b/docs/notes/pics/HTTP_ResponseMessageExample.png deleted file mode 100644 index 1adf26c539..0000000000 Binary files a/docs/notes/pics/HTTP_ResponseMessageExample.png and /dev/null differ diff --git a/docs/notes/pics/How-HTTPS-Works.png b/docs/notes/pics/How-HTTPS-Works.png deleted file mode 100644 index c10605f73f..0000000000 Binary files a/docs/notes/pics/How-HTTPS-Works.png and /dev/null differ diff --git a/docs/notes/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png b/docs/notes/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png deleted file mode 100644 index 188460a54e..0000000000 Binary files a/docs/notes/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png and /dev/null differ diff --git a/docs/notes/pics/PPjwP.png b/docs/notes/pics/PPjwP.png deleted file mode 100644 index 8063150509..0000000000 Binary files a/docs/notes/pics/PPjwP.png and /dev/null differ diff --git a/docs/notes/pics/Semaphore.png b/docs/notes/pics/Semaphore.png deleted file mode 100644 index 6b8c30b134..0000000000 Binary files a/docs/notes/pics/Semaphore.png and /dev/null differ diff --git a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png b/docs/notes/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png deleted file mode 100644 index da66da1412..0000000000 Binary files a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png and /dev/null differ diff --git a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png b/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png deleted file mode 100644 index e5ae523401..0000000000 Binary files a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png and /dev/null differ diff --git a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png b/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png deleted file mode 100644 index e31398b6fd..0000000000 Binary files a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png and /dev/null differ diff --git a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png b/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png deleted file mode 100644 index 055d995ea9..0000000000 Binary files a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png and /dev/null differ diff --git a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png b/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png deleted file mode 100644 index 37f32c4c38..0000000000 Binary files a/docs/notes/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png and /dev/null differ diff --git a/docs/notes/pics/_u4E0B_u8F7D.png b/docs/notes/pics/_u4E0B_u8F7D.png deleted file mode 100644 index 9da973302d..0000000000 Binary files a/docs/notes/pics/_u4E0B_u8F7D.png and /dev/null differ diff --git a/docs/notes/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif b/docs/notes/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif deleted file mode 100644 index 07f3a9102c..0000000000 Binary files a/docs/notes/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif and /dev/null differ diff --git a/docs/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif b/docs/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif deleted file mode 100644 index 9d9809a08f..0000000000 Binary files a/docs/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif and /dev/null differ diff --git a/docs/notes/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png b/docs/notes/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png deleted file mode 100644 index 4b9b4b485e..0000000000 Binary files a/docs/notes/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png and /dev/null differ diff --git a/docs/notes/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png b/docs/notes/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png deleted file mode 100644 index 541acf31e0..0000000000 Binary files a/docs/notes/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png and /dev/null differ diff --git a/docs/notes/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg b/docs/notes/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg deleted file mode 100644 index 30e926398f..0000000000 Binary files a/docs/notes/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg and /dev/null differ diff --git a/docs/notes/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png b/docs/notes/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png deleted file mode 100644 index edc6840ad1..0000000000 Binary files a/docs/notes/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png and /dev/null differ diff --git a/docs/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png b/docs/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png deleted file mode 100644 index 1a5a64740c..0000000000 Binary files a/docs/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png and /dev/null differ diff --git a/docs/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg b/docs/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg deleted file mode 100644 index 23258afa74..0000000000 Binary files a/docs/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg and /dev/null differ diff --git a/docs/notes/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png b/docs/notes/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png deleted file mode 100644 index 03b37a617b..0000000000 Binary files a/docs/notes/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png and /dev/null differ diff --git a/docs/notes/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg b/docs/notes/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg deleted file mode 100644 index 7b432fd1b9..0000000000 Binary files a/docs/notes/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg and /dev/null differ diff --git a/docs/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png b/docs/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png deleted file mode 100644 index e980a2e30f..0000000000 Binary files a/docs/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png and /dev/null differ diff --git a/docs/notes/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg b/docs/notes/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg deleted file mode 100644 index a7050bdf00..0000000000 Binary files a/docs/notes/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg and /dev/null differ diff --git a/docs/notes/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png b/docs/notes/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png deleted file mode 100644 index 461fffb0eb..0000000000 Binary files a/docs/notes/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png and /dev/null differ diff --git a/docs/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png b/docs/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png deleted file mode 100644 index 11d366536b..0000000000 Binary files a/docs/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png and /dev/null differ diff --git a/docs/notes/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png b/docs/notes/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png deleted file mode 100644 index 9b6bcee257..0000000000 Binary files a/docs/notes/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png and /dev/null differ diff --git a/docs/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png b/docs/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png deleted file mode 100644 index 6a6bf78f10..0000000000 Binary files a/docs/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png and /dev/null differ diff --git a/docs/notes/pics/ace830df-9919-48ca-91b5-60b193f593d2.png b/docs/notes/pics/ace830df-9919-48ca-91b5-60b193f593d2.png deleted file mode 100644 index 79efa287c3..0000000000 Binary files a/docs/notes/pics/ace830df-9919-48ca-91b5-60b193f593d2.png and /dev/null differ diff --git a/docs/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png b/docs/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png deleted file mode 100644 index 27977740b6..0000000000 Binary files a/docs/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png and /dev/null differ diff --git a/docs/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png b/docs/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png deleted file mode 100644 index 34d7a286a5..0000000000 Binary files a/docs/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png and /dev/null differ diff --git a/docs/notes/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png b/docs/notes/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png deleted file mode 100644 index 8de4fa4d38..0000000000 Binary files a/docs/notes/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png and /dev/null differ diff --git a/docs/notes/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg b/docs/notes/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg deleted file mode 100644 index cfb2586d52..0000000000 Binary files a/docs/notes/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg and /dev/null differ diff --git a/docs/notes/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg b/docs/notes/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg deleted file mode 100644 index a0df23c89e..0000000000 Binary files a/docs/notes/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg and /dev/null differ diff --git a/docs/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png b/docs/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png deleted file mode 100644 index 73dad48397..0000000000 Binary files a/docs/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png and /dev/null differ diff --git a/docs/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg b/docs/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg deleted file mode 100644 index a71d167e39..0000000000 Binary files a/docs/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg and /dev/null differ diff --git a/docs/notes/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png b/docs/notes/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png deleted file mode 100644 index 3d5adabd47..0000000000 Binary files a/docs/notes/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png and /dev/null differ diff --git a/docs/notes/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg b/docs/notes/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg deleted file mode 100644 index d8085fa02f..0000000000 Binary files a/docs/notes/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg and /dev/null differ diff --git a/docs/notes/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png b/docs/notes/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png deleted file mode 100644 index 65f73b0e86..0000000000 Binary files a/docs/notes/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png and /dev/null differ diff --git a/docs/notes/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg b/docs/notes/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg deleted file mode 100644 index fa79744a82..0000000000 Binary files a/docs/notes/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg and /dev/null differ diff --git a/docs/notes/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg b/docs/notes/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg deleted file mode 100644 index eb46d41276..0000000000 Binary files a/docs/notes/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg and /dev/null differ diff --git a/docs/notes/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png b/docs/notes/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png deleted file mode 100644 index 59ea02ccc4..0000000000 Binary files a/docs/notes/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png and /dev/null differ diff --git a/docs/notes/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png b/docs/notes/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png deleted file mode 100644 index ac9868d1eb..0000000000 Binary files a/docs/notes/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png and /dev/null differ diff --git a/docs/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png b/docs/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png deleted file mode 100644 index dd32da7026..0000000000 Binary files a/docs/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png and /dev/null differ diff --git a/docs/notes/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg b/docs/notes/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg deleted file mode 100644 index 96b80deba9..0000000000 Binary files a/docs/notes/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg and /dev/null differ diff --git a/docs/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg b/docs/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg deleted file mode 100644 index 58fb0a056e..0000000000 Binary files a/docs/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg and /dev/null differ diff --git a/docs/notes/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png b/docs/notes/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png deleted file mode 100644 index dc44da3deb..0000000000 Binary files a/docs/notes/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png and /dev/null differ diff --git a/docs/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png b/docs/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png deleted file mode 100644 index c74e816e4c..0000000000 Binary files a/docs/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png and /dev/null differ diff --git a/docs/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg b/docs/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg deleted file mode 100644 index 67339134d5..0000000000 Binary files a/docs/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg and /dev/null differ diff --git a/docs/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png b/docs/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png deleted file mode 100644 index 6664d39875..0000000000 Binary files a/docs/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png and /dev/null differ diff --git a/docs/notes/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg b/docs/notes/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg deleted file mode 100644 index c3815f20c6..0000000000 Binary files a/docs/notes/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg and /dev/null differ diff --git a/docs/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png b/docs/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png deleted file mode 100644 index 07d8692d3c..0000000000 Binary files a/docs/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png and /dev/null differ diff --git a/docs/notes/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg b/docs/notes/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg deleted file mode 100644 index b27cd02ff0..0000000000 Binary files a/docs/notes/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg and /dev/null differ diff --git a/docs/notes/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg b/docs/notes/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg deleted file mode 100644 index 4cd6f60f06..0000000000 Binary files a/docs/notes/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg and /dev/null differ diff --git a/docs/notes/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png b/docs/notes/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png deleted file mode 100644 index 2c2214ded2..0000000000 Binary files a/docs/notes/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png and /dev/null differ diff --git a/docs/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png b/docs/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png deleted file mode 100644 index b24241d106..0000000000 Binary files a/docs/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png and /dev/null differ diff --git a/docs/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg b/docs/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg deleted file mode 100644 index a83ba2755a..0000000000 Binary files a/docs/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg and /dev/null differ diff --git a/docs/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg b/docs/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg deleted file mode 100644 index 30956ccfe4..0000000000 Binary files a/docs/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg and /dev/null differ diff --git a/docs/notes/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png b/docs/notes/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png deleted file mode 100644 index b9d9dba2e6..0000000000 Binary files a/docs/notes/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png and /dev/null differ diff --git a/docs/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png b/docs/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png deleted file mode 100644 index add3c5d4d6..0000000000 Binary files a/docs/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png and /dev/null differ diff --git a/docs/notes/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png b/docs/notes/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png deleted file mode 100644 index cf8d36dc50..0000000000 Binary files a/docs/notes/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png and /dev/null differ diff --git a/docs/notes/pics/c2c2b633-c03a-426e-b436-5719a194667b.png b/docs/notes/pics/c2c2b633-c03a-426e-b436-5719a194667b.png deleted file mode 100644 index b673d4a796..0000000000 Binary files a/docs/notes/pics/c2c2b633-c03a-426e-b436-5719a194667b.png and /dev/null differ diff --git a/docs/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png b/docs/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png deleted file mode 100644 index 70ecc3ec67..0000000000 Binary files a/docs/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png and /dev/null differ diff --git a/docs/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png b/docs/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png deleted file mode 100644 index 84b2898b39..0000000000 Binary files a/docs/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png and /dev/null differ diff --git a/docs/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg b/docs/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg deleted file mode 100644 index 2c6cc2b2a6..0000000000 Binary files a/docs/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg and /dev/null differ diff --git a/docs/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png b/docs/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png deleted file mode 100644 index 48e980fc08..0000000000 Binary files a/docs/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png and /dev/null differ diff --git a/docs/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png b/docs/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png deleted file mode 100644 index 36c948aad4..0000000000 Binary files a/docs/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png and /dev/null differ diff --git a/docs/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg b/docs/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg deleted file mode 100644 index 5e392d41d0..0000000000 Binary files a/docs/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg and /dev/null differ diff --git a/docs/notes/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg b/docs/notes/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg deleted file mode 100644 index 895d1ed587..0000000000 Binary files a/docs/notes/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg and /dev/null differ diff --git a/docs/notes/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png b/docs/notes/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png deleted file mode 100644 index bbed020ac2..0000000000 Binary files a/docs/notes/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png and /dev/null differ diff --git a/docs/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png b/docs/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png deleted file mode 100644 index 91e2c14425..0000000000 Binary files a/docs/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png and /dev/null differ diff --git a/docs/notes/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png b/docs/notes/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png deleted file mode 100644 index 167c33bc53..0000000000 Binary files a/docs/notes/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png and /dev/null differ diff --git a/docs/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg b/docs/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg deleted file mode 100644 index fa7ce3ae1e..0000000000 Binary files a/docs/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg and /dev/null differ diff --git a/docs/notes/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png b/docs/notes/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png deleted file mode 100644 index 7a7c702d9f..0000000000 Binary files a/docs/notes/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png and /dev/null differ diff --git a/docs/notes/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png b/docs/notes/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png deleted file mode 100644 index 215d2c8ddc..0000000000 Binary files a/docs/notes/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png and /dev/null differ diff --git a/docs/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png b/docs/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png deleted file mode 100644 index f8550a114b..0000000000 Binary files a/docs/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png and /dev/null differ diff --git a/docs/notes/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg b/docs/notes/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg deleted file mode 100644 index b3699644c3..0000000000 Binary files a/docs/notes/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg and /dev/null differ diff --git a/docs/notes/pics/class_loader_hierarchy.png b/docs/notes/pics/class_loader_hierarchy.png deleted file mode 100644 index 6f4baf6f10..0000000000 Binary files a/docs/notes/pics/class_loader_hierarchy.png and /dev/null differ diff --git a/docs/notes/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg b/docs/notes/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg deleted file mode 100644 index 4e72308703..0000000000 Binary files a/docs/notes/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg and /dev/null differ diff --git a/docs/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png b/docs/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png deleted file mode 100644 index 58ff84fe5f..0000000000 Binary files a/docs/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png and /dev/null differ diff --git a/docs/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif b/docs/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif deleted file mode 100644 index 2c0accfc29..0000000000 Binary files a/docs/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif and /dev/null differ diff --git a/docs/notes/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png b/docs/notes/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png deleted file mode 100644 index ce6663bc18..0000000000 Binary files a/docs/notes/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png and /dev/null differ diff --git a/docs/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg b/docs/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg deleted file mode 100644 index bbc7f102b5..0000000000 Binary files a/docs/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg and /dev/null differ diff --git a/docs/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png b/docs/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png deleted file mode 100644 index 347fe31617..0000000000 Binary files a/docs/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png and /dev/null differ diff --git a/docs/notes/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png b/docs/notes/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png deleted file mode 100644 index 5a138b6f17..0000000000 Binary files a/docs/notes/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png and /dev/null differ diff --git a/docs/notes/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png b/docs/notes/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png deleted file mode 100644 index 2dde7dab4d..0000000000 Binary files a/docs/notes/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png and /dev/null differ diff --git a/docs/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png b/docs/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png deleted file mode 100644 index fdca032441..0000000000 Binary files a/docs/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png and /dev/null differ diff --git a/docs/notes/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png b/docs/notes/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png deleted file mode 100644 index fdca032441..0000000000 Binary files a/docs/notes/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png and /dev/null differ diff --git a/docs/notes/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png b/docs/notes/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png deleted file mode 100644 index 1ee66db8b7..0000000000 Binary files a/docs/notes/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png and /dev/null differ diff --git a/docs/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg b/docs/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg deleted file mode 100644 index 8090706b41..0000000000 Binary files a/docs/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg and /dev/null differ diff --git a/docs/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg b/docs/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg deleted file mode 100644 index 0be8743c8e..0000000000 Binary files a/docs/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg and /dev/null differ diff --git a/docs/notes/pics/dd15a984-e977-4644-b127-708cddb8ca99.png b/docs/notes/pics/dd15a984-e977-4644-b127-708cddb8ca99.png deleted file mode 100644 index 4397e88e81..0000000000 Binary files a/docs/notes/pics/dd15a984-e977-4644-b127-708cddb8ca99.png and /dev/null differ diff --git a/docs/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png b/docs/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png deleted file mode 100644 index 51055a2bb0..0000000000 Binary files a/docs/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png and /dev/null differ diff --git a/docs/notes/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg b/docs/notes/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg deleted file mode 100644 index 69f4dd050a..0000000000 Binary files a/docs/notes/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg and /dev/null differ diff --git a/docs/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png b/docs/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png deleted file mode 100644 index cfabd4d7c7..0000000000 Binary files a/docs/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png and /dev/null differ diff --git a/docs/notes/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg b/docs/notes/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg deleted file mode 100644 index 4a923dd802..0000000000 Binary files a/docs/notes/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg and /dev/null differ diff --git a/docs/notes/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg b/docs/notes/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg deleted file mode 100644 index 7746a71564..0000000000 Binary files a/docs/notes/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg and /dev/null differ diff --git a/docs/notes/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg b/docs/notes/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg deleted file mode 100644 index d04e9f6fe1..0000000000 Binary files a/docs/notes/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg and /dev/null differ diff --git a/docs/notes/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png b/docs/notes/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png deleted file mode 100644 index ef5b780fb3..0000000000 Binary files a/docs/notes/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png and /dev/null differ diff --git a/docs/notes/pics/docker-filesystems-busyboxrw.png b/docs/notes/pics/docker-filesystems-busyboxrw.png deleted file mode 100644 index c12710afa6..0000000000 Binary files a/docs/notes/pics/docker-filesystems-busyboxrw.png and /dev/null differ diff --git a/docs/notes/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png b/docs/notes/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png deleted file mode 100644 index 427cfbf82d..0000000000 Binary files a/docs/notes/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png and /dev/null differ diff --git a/docs/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg b/docs/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg deleted file mode 100644 index 0968e1bc2d..0000000000 Binary files a/docs/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg and /dev/null differ diff --git a/docs/notes/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png b/docs/notes/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png deleted file mode 100644 index c21e9af20f..0000000000 Binary files a/docs/notes/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png and /dev/null differ diff --git a/docs/notes/pics/e2f0d889-2330-424c-8193-198edebecff7.png b/docs/notes/pics/e2f0d889-2330-424c-8193-198edebecff7.png deleted file mode 100644 index d747d6f30c..0000000000 Binary files a/docs/notes/pics/e2f0d889-2330-424c-8193-198edebecff7.png and /dev/null differ diff --git a/docs/notes/pics/e31abb94-9201-4e06-9902-61101b92f475.png b/docs/notes/pics/e31abb94-9201-4e06-9902-61101b92f475.png deleted file mode 100644 index 90833a5c5b..0000000000 Binary files a/docs/notes/pics/e31abb94-9201-4e06-9902-61101b92f475.png and /dev/null differ diff --git a/docs/notes/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png b/docs/notes/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png deleted file mode 100644 index 118096b705..0000000000 Binary files a/docs/notes/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png and /dev/null differ diff --git a/docs/notes/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png b/docs/notes/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png deleted file mode 100644 index a8e7e53857..0000000000 Binary files a/docs/notes/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png and /dev/null differ diff --git a/docs/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png b/docs/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png deleted file mode 100644 index 105916c6a6..0000000000 Binary files a/docs/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png and /dev/null differ diff --git a/docs/notes/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg b/docs/notes/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg deleted file mode 100644 index b28765c586..0000000000 Binary files a/docs/notes/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg and /dev/null differ diff --git a/docs/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg b/docs/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg deleted file mode 100644 index 7fef7ba5c0..0000000000 Binary files a/docs/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg and /dev/null differ diff --git a/docs/notes/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg b/docs/notes/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg deleted file mode 100644 index 8eda7a0a0f..0000000000 Binary files a/docs/notes/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg and /dev/null differ diff --git a/docs/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg b/docs/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg deleted file mode 100644 index f4aa12a06d..0000000000 Binary files a/docs/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg and /dev/null differ diff --git a/docs/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png b/docs/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png deleted file mode 100644 index 1090a77959..0000000000 Binary files a/docs/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png and /dev/null differ diff --git a/docs/notes/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png b/docs/notes/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png deleted file mode 100644 index 59b54d2fa8..0000000000 Binary files a/docs/notes/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png and /dev/null differ diff --git a/docs/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png b/docs/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png deleted file mode 100644 index 007e9646c0..0000000000 Binary files a/docs/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png and /dev/null differ diff --git a/docs/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg b/docs/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg deleted file mode 100644 index 732e73e000..0000000000 Binary files a/docs/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg and /dev/null differ diff --git a/docs/notes/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png b/docs/notes/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png deleted file mode 100644 index a54c85e1f4..0000000000 Binary files a/docs/notes/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png and /dev/null differ diff --git a/docs/notes/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png b/docs/notes/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png deleted file mode 100644 index f625686748..0000000000 Binary files a/docs/notes/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png and /dev/null differ diff --git a/docs/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg b/docs/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg deleted file mode 100644 index 66a2ecbe20..0000000000 Binary files a/docs/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg and /dev/null differ diff --git a/docs/notes/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg b/docs/notes/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg deleted file mode 100644 index 4308e4c935..0000000000 Binary files a/docs/notes/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg and /dev/null differ diff --git a/docs/notes/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png b/docs/notes/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png deleted file mode 100644 index 7786868673..0000000000 Binary files a/docs/notes/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png and /dev/null differ diff --git a/docs/notes/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png b/docs/notes/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png deleted file mode 100644 index 4a5dbd1dc6..0000000000 Binary files a/docs/notes/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png and /dev/null differ diff --git a/docs/notes/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png b/docs/notes/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png deleted file mode 100644 index f358a009ca..0000000000 Binary files a/docs/notes/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png and /dev/null differ diff --git a/docs/notes/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg b/docs/notes/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg deleted file mode 100644 index 46e9f0f82b..0000000000 Binary files a/docs/notes/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg and /dev/null differ diff --git a/docs/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png b/docs/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png deleted file mode 100644 index dc0d4e3490..0000000000 Binary files a/docs/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png and /dev/null differ diff --git a/docs/notes/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg b/docs/notes/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg deleted file mode 100644 index d710e65919..0000000000 Binary files a/docs/notes/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg and /dev/null differ diff --git a/docs/notes/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg b/docs/notes/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg deleted file mode 100644 index 9fc2a27132..0000000000 Binary files a/docs/notes/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg and /dev/null differ diff --git a/docs/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png b/docs/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png deleted file mode 100644 index 26799762f2..0000000000 Binary files a/docs/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png and /dev/null differ diff --git a/docs/notes/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png b/docs/notes/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png deleted file mode 100644 index b85accf6eb..0000000000 Binary files a/docs/notes/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png and /dev/null differ diff --git a/docs/notes/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png b/docs/notes/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png deleted file mode 100644 index 86e2294f47..0000000000 Binary files a/docs/notes/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png and /dev/null differ diff --git a/docs/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg b/docs/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg deleted file mode 100644 index 6a09099323..0000000000 Binary files a/docs/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg and /dev/null differ diff --git a/docs/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png b/docs/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png deleted file mode 100644 index 4d9e3e21c7..0000000000 Binary files a/docs/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png and /dev/null differ diff --git a/docs/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg b/docs/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg deleted file mode 100644 index 95b92d603a..0000000000 Binary files a/docs/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg and /dev/null differ diff --git a/docs/notes/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png b/docs/notes/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png deleted file mode 100644 index 56f056114d..0000000000 Binary files a/docs/notes/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png and /dev/null differ diff --git a/docs/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png b/docs/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png deleted file mode 100644 index 7da6d4b2e6..0000000000 Binary files a/docs/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png and /dev/null differ diff --git a/docs/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg b/docs/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg deleted file mode 100644 index 67fac8feb2..0000000000 Binary files a/docs/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg and /dev/null differ diff --git a/docs/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg b/docs/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg deleted file mode 100644 index b6a0ba7900..0000000000 Binary files a/docs/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg and /dev/null differ diff --git a/docs/notes/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png b/docs/notes/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png deleted file mode 100644 index 824904dbc3..0000000000 Binary files a/docs/notes/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png and /dev/null differ diff --git a/docs/notes/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png b/docs/notes/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png deleted file mode 100644 index 2142bbb59f..0000000000 Binary files a/docs/notes/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png and /dev/null differ diff --git a/docs/notes/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png b/docs/notes/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png deleted file mode 100644 index 736a286164..0000000000 Binary files a/docs/notes/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png and /dev/null differ diff --git a/docs/notes/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png b/docs/notes/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png deleted file mode 100644 index 31ae3277cd..0000000000 Binary files a/docs/notes/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png and /dev/null differ diff --git a/docs/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg b/docs/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg deleted file mode 100644 index 36c1d9b723..0000000000 Binary files a/docs/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg and /dev/null differ diff --git a/docs/notes/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg b/docs/notes/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg deleted file mode 100644 index e15f11db01..0000000000 Binary files a/docs/notes/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg and /dev/null differ diff --git a/docs/notes/pics/fbe54203-c005-48f0-8883-b05e564a3173.png b/docs/notes/pics/fbe54203-c005-48f0-8883-b05e564a3173.png deleted file mode 100644 index dee6a88dff..0000000000 Binary files a/docs/notes/pics/fbe54203-c005-48f0-8883-b05e564a3173.png and /dev/null differ diff --git a/docs/notes/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png b/docs/notes/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png deleted file mode 100644 index 4eaa11ba67..0000000000 Binary files a/docs/notes/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png and /dev/null differ diff --git a/docs/notes/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg b/docs/notes/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg deleted file mode 100644 index 1fa4032047..0000000000 Binary files a/docs/notes/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg and /dev/null differ diff --git a/docs/notes/pics/flow.png b/docs/notes/pics/flow.png deleted file mode 100644 index aa0492a4e8..0000000000 Binary files a/docs/notes/pics/flow.png and /dev/null differ diff --git a/docs/notes/pics/inode_with_signatures.jpg b/docs/notes/pics/inode_with_signatures.jpg deleted file mode 100644 index 518ba5ac84..0000000000 Binary files a/docs/notes/pics/inode_with_signatures.jpg and /dev/null differ diff --git a/docs/notes/pics/linux-filesystem.png b/docs/notes/pics/linux-filesystem.png deleted file mode 100644 index ae96529529..0000000000 Binary files a/docs/notes/pics/linux-filesystem.png and /dev/null differ diff --git a/docs/notes/pics/master-slave-proxy.png b/docs/notes/pics/master-slave-proxy.png deleted file mode 100644 index 66be0d61f5..0000000000 Binary files a/docs/notes/pics/master-slave-proxy.png and /dev/null differ diff --git a/docs/notes/pics/master-slave.png b/docs/notes/pics/master-slave.png deleted file mode 100644 index 594a183a4c..0000000000 Binary files a/docs/notes/pics/master-slave.png and /dev/null differ diff --git a/docs/notes/pics/monitor-lock-rule.png b/docs/notes/pics/monitor-lock-rule.png deleted file mode 100644 index 6590d94bff..0000000000 Binary files a/docs/notes/pics/monitor-lock-rule.png and /dev/null differ diff --git a/docs/notes/pics/single-thread-rule.png b/docs/notes/pics/single-thread-rule.png deleted file mode 100644 index d6583e9e64..0000000000 Binary files a/docs/notes/pics/single-thread-rule.png and /dev/null differ diff --git a/docs/notes/pics/thread-join-rule.png b/docs/notes/pics/thread-join-rule.png deleted file mode 100644 index c17d045623..0000000000 Binary files a/docs/notes/pics/thread-join-rule.png and /dev/null differ diff --git a/docs/notes/pics/thread-start-rule.png b/docs/notes/pics/thread-start-rule.png deleted file mode 100644 index 60ee78622c..0000000000 Binary files a/docs/notes/pics/thread-start-rule.png and /dev/null differ diff --git a/docs/notes/pics/volatile-variable-rule.png b/docs/notes/pics/volatile-variable-rule.png deleted file mode 100644 index 1747664b01..0000000000 Binary files a/docs/notes/pics/volatile-variable-rule.png and /dev/null differ diff --git "a/docs/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" "b/docs/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" deleted file mode 100644 index 948c672cae..0000000000 --- "a/docs/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" +++ /dev/null @@ -1,13 +0,0 @@ - - - - -- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) -- [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) -- [阿里巴巴Java开发手册](https://github.com/alibaba/p3c/blob/master/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E8%AF%A6%E5%B0%BD%E7%89%88%EF%BC%89.pdf) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\210\206\345\270\203\345\274\217.md" "b/docs/notes/\345\210\206\345\270\203\345\274\217.md" deleted file mode 100644 index e8b81360ae..0000000000 --- "a/docs/notes/\345\210\206\345\270\203\345\274\217.md" +++ /dev/null @@ -1,349 +0,0 @@ - -* [一、分布式锁](#一分布式锁) - * [数据库的唯一索引](#数据库的唯一索引) - * [Redis 的 SETNX 指令](#redis-的-setnx-指令) - * [Redis 的 RedLock 算法](#redis-的-redlock-算法) - * [Zookeeper 的有序节点](#zookeeper-的有序节点) -* [二、分布式事务](#二分布式事务) - * [本地消息表](#本地消息表) - * [2PC](#2pc) -* [三、CAP](#三cap) - * [一致性](#一致性) - * [可用性](#可用性) - * [分区容忍性](#分区容忍性) - * [权衡](#权衡) -* [四、BASE](#四base) - * [基本可用](#基本可用) - * [软状态](#软状态) - * [最终一致性](#最终一致性) -* [五、Paxos](#五paxos) - * [执行过程](#执行过程) - * [约束条件](#约束条件) -* [六、Raft](#六raft) - * [单个 Candidate 的竞选](#单个-candidate-的竞选) - * [多个 Candidate 竞选](#多个-candidate-竞选) - * [数据同步](#数据同步) -* [参考](#参考) - - - -# 一、分布式锁 - -在单机场景下,可以使用语言的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。 - -阻塞锁通常使用互斥量来实现: - -- 互斥量为 0 表示有其它进程在使用锁,此时处于锁定状态; -- 互斥量为 1 表示未锁定状态。 - -1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示。 - -## 数据库的唯一索引 - -获得锁时向表中插入一条记录,释放锁时删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否存于锁定状态。 - -存在以下几个问题: - -- 锁没有失效时间,解锁失败的话其它进程无法再获得该锁。 -- 只能是非阻塞锁,插入失败直接就报错了,无法重试。 -- 不可重入,已经获得锁的进程也必须重新获取锁。 - -## Redis 的 SETNX 指令 - -使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。 - -SETNX 指令和数据库的唯一索引类似,保证了只存在一个 Key 的键值对,那么可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。 - -EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了数据库唯一索引实现方式中释放锁失败的问题。 - -## Redis 的 RedLock 算法 - -使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。 - -- 尝试从 N 个相互独立 Redis 实例获取锁; -- 计算获取锁消耗的时间,只有当这个时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,那么就认为锁获取成功了; -- 如果锁获取失败,就到每个实例上释放锁。 - -## Zookeeper 的有序节点 - -### 1. Zookeeper 抽象模型 - -Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点的父节点为 /app1。 - -

- -### 2. 节点类型 - -- 永久节点:不会因为会话结束或者超时而消失; -- 临时节点:如果会话结束或者超时就会消失; -- 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,以此类推。 - -### 3. 监听器 - -为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。 - -### 4. 分布式锁实现 - -- 创建一个锁目录 /lock; -- 当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点; -- 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁; -- 执行业务代码,完成后,删除对应的子节点。 - -### 5. 会话超时 - -如果一个已经获得锁的会话超时了,因为创建的是临时节点,所以该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,Zookeeper 分布式锁不会出现数据库的唯一索引实现的分布式锁释放锁失败问题。 - -### 6. 羊群效应 - -一个节点未获得锁,只需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应),而我们只希望它的后一个子节点收到通知。 - -# 二、分布式事务 - -指事务的操作位于不同的节点上,需要保证事务的 ACID 特性。 - -例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。 - -## 本地消息表 - -本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。 - -1. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。 -2. 之后将本地消息表中的消息转发到 Kafka 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 -3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。 - -

- -## 2PC - -两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。 - -### 1. 运行过程 - -#### 1.1 准备阶段 - -协调者询问参与者事务是否执行成功,参与者发回事务执行结果。 - -

- -#### 1.2 提交阶段 - -如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。 - -需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。 - -

- -### 2. 存在的问题 - -#### 2.1 同步阻塞 - -所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。 - -#### 2.2 单点问题 - -协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待,无法完成其它操作。 - -#### 2.3 数据不一致 - -在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。 - -#### 2.4 太过保守 - -任意一个节点失败就会导致整个事务失败,没有完善的容错机制。 - -# 三、CAP - -分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。 - -

- -## 一致性 - -一致性指的是多个数据副本是否能保持一致的特性,在一致性的条件下,系统在执行数据更新操作之后能够从一致性状态转移到另一个一致性状态。 - -对系统的一个数据更新成功之后,如果所有用户都能够读取到最新的值,该系统就被认为具有强一致性。 - -## 可用性 - -可用性指分布式系统在面对各种异常时可以提供正常服务的能力,可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。 - -在可用性条件下,要求系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 - -## 分区容忍性 - -网络分区指分布式系统中的节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。 - -在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。 - -## 权衡 - -在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP 理论实际上是要在可用性和一致性之间做权衡。 - -可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时, - -- 为了保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性; -- 为了保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致。 - -

- -# 四、BASE - -BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。 - -BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 - - -## 基本可用 - -指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。 - -例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。 - -## 软状态 - -指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。 - -## 最终一致性 - -最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。 - -ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过牺牲强一致性来达到可用性,通常运用在大型分布式系统中。 - -在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。 - -# 五、Paxos - -用于达成共识性问题,即对多个节点产生的值,该算法能保证只选出唯一一个值。 - -主要有三类节点: - -- 提议者(Proposer):提议一个值; -- 接受者(Acceptor):对每个提议进行投票; -- 告知者(Learner):被告知投票的结果,不参与投票过程。 - -

- -## 执行过程 - -规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。 - -### 1. Prepare 阶段 - -下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送 Prepare 请求。 - -

- -当 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n1, v1],并且之前还未接收过 Prepare 请求,那么发送一个 Prepare 响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。 - -如下图,Acceptor X 在收到 [n=2, v=8] 的 Prepare 请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的 Prepare 响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。 - -

- -如果 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 > n2,那么就丢弃该提议请求;否则,发送 Prepare 响应,该 Prepare 响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。 - -如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的 Prepare 请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n > 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的 Prepare 请求,因为之前接收到的提议为 [n=2, v=8],并且 2 <= 4,因此就发送 [n=2, v=8] 的 Prepare 响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。 - -

- -### 2. Accept 阶段 - -当一个 Proposer 接收到超过一半 Acceptor 的 Prepare 响应时,就可以发送 Accept 请求。 - -Proposer A 接收到两个 Prepare 响应之后,就发送 [n=2, v=8] Accept 请求。该 Accept 请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。 - -Proposer B 过后也收到了两个 Prepare 响应,因此也开始发送 Accept 请求。需要注意的是,Accept 请求的 v 需要取它收到的最大提议编号对应的 v 值,也就是 8。因此它发送 [n=4, v=8] 的 Accept 请求。 - -

- -### 3. Learn 阶段 - -Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送 Learn 提议给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。 - -

- -## 约束条件 - -### 1\. 正确性 - -指只有一个提议值会生效。 - -因为 Paxos 协议要求每个生效的提议被多数 Acceptor 接收,并且 Acceptor 不会接受两个不同的提议,因此可以保证正确性。 - -### 2\. 可终止性 - -指最后总会有一个提议生效。 - -Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。 - -# 六、Raft - -Raft 也是分布式一致性协议,主要是用来竞选主节点。 - -## 单个 Candidate 的竞选 - -有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。 - -- 下图展示一个分布式系统的最初阶段,此时只有 Follower 没有 Leader。Node A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。 - -

- -- 此时 Node A 发送投票请求给其它所有节点。 - -

- -- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。 - -

- -- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。 - -

- -## 多个 Candidate 竞选 - -- 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票。例如下图中 Node B 和 Node D 都获得两票,需要重新开始投票。 - -

- -- 由于每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低。 - -

- -## 数据同步 - -- 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。 - -

- -- Leader 会把修改复制到所有 Follower。 - -

- -- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。 - -

- -- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。 - -

- -# 参考 - -- 倪超. 从 Paxos 到 ZooKeeper : 分布式一致性原理与实践 [M]. 电子工业出版社, 2015. -- [Distributed locks with Redis](https://redis.io/topics/distlock) -- [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023) -- [基于 Zookeeper 的分布式锁](http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html) -- [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft) -- [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html) -- [分布式系统的事务处理](https://coolshell.cn/articles/10910.html) -- [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be) -- [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/) -- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) -- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 10~19.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 10~19.md" deleted file mode 100644 index 581af3b4d7..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 10~19.md" +++ /dev/null @@ -1,691 +0,0 @@ - -* [10.1 斐波那契数列](#101-斐波那契数列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [10.2 矩形覆盖](#102-矩形覆盖) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [10.3 跳台阶](#103-跳台阶) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [10.4 变态跳台阶](#104-变态跳台阶) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - * [动态规划](#动态规划) - * [数学推导](#数学推导) -* [11. 旋转数组的最小数字](#11-旋转数组的最小数字) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [12. 矩阵中的路径](#12-矩阵中的路径) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [13. 机器人的运动范围](#13-机器人的运动范围) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [14. 剪绳子](#14-剪绳子) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - * [贪心](#贪心) - * [动态规划](#动态规划) -* [15. 二进制中 1 的个数](#15-二进制中-1-的个数) - * [题目描述](#题目描述) - * [n&(n-1)](#n&n-1) - * [Integer.bitCount()](#integerbitcount) -* [16. 数值的整数次方](#16-数值的整数次方) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点) - * [解题思路](#解题思路) -* [18.2 删除链表中重复的结点](#182-删除链表中重复的结点) - * [题目描述](#题目描述) - * [解题描述](#解题描述) -* [19. 正则表达式匹配](#19-正则表达式匹配) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - - - -# 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) - -## 题目描述 - -求斐波那契数列的第 n 项,n <= 39。 - - - -

- -## 解题思路 - -如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。 - -

- - -递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。 - -```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; - fib[2] = 2; - for (int i = 2; i < fib.length; i++) - fib[i] = fib[i - 1] + fib[i - 2]; - } - - public int Fibonacci(int n) { - return fib[n]; - } -} -``` - -# 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) - -## 题目描述 - -我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法? - -

- -## 解题思路 - -```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; -} -``` - -# 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) - -## 题目描述 - -一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - -

- -## 解题思路 - -```java -public int JumpFloor(int n) { - if (n <= 2) - return n; - int pre2 = 1, pre1 = 2; - int result = 1; - for (int i = 2; i < n; i++) { - result = pre2 + pre1; - pre2 = pre1; - pre1 = result; - } - return result; -} -``` - -# 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) - -## 题目描述 - -一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - -

- -## 解题思路 - -### 动态规划 - -```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); -} -``` - - -# 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) - -## 题目描述 - -把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 - -例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。 - -## 解题思路 - -在一个有序数组中查找一个元素可以用二分查找,二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度都为 O(logN)。 - -本题可以修改二分查找算法进行求解: - -- 当 nums[m] <= nums[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]; -} -``` - -# 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) - -## 题目描述 - -请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 - -例如下面的矩阵包含了一条 bfce 路径。 - -

- -## 解题思路 - -```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 i = 0, idx = 0; i < rows; i++) - for (int j = 0; j < cols; j++) - matrix[i][j] = array[idx++]; - return matrix; -} -``` - -# 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) - -## 题目描述 - -地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。 - -例如,当 k 为 18 时,机器人能够进入方格 (35,37),因为 3+5+3+7=18。但是,它不能进入方格 (35,38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子? - -## 解题思路 - -```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]; -} -``` - -# 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]; -} -``` - -# 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) - -## 题目描述 - -输入一个整数,输出该数二进制表示中 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); -} -``` - -# 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) - -## 题目描述 - -给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent,求 base 的 exponent 次方。 - -## 解题思路 - -下面的讨论中 x 代表 base,n 代表 exponent。 - - - -

- -因为 (x\*x)n/2 可以通过递归求解,并且每次递归 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; -} -``` - -# 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(); -} -``` - -# 18.1 在 O(1) 时间内删除链表节点 - -## 解题思路 - -① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。 - -

- -② 如果链表只有一个节点,那么直接 - -② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。 - -

- -综上,如果进行 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; -} -``` - -# 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) - -## 题目描述 - -

- -## 解题描述 - -```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; - } -} -``` - -# 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) - -## 题目描述 - -请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 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]; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 20~29.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 20~29.md" deleted file mode 100644 index 9e1d7cbc3e..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 20~29.md" +++ /dev/null @@ -1,384 +0,0 @@ - -* [20. 表示数值的字符串](#20-表示数值的字符串) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点) - * [解题思路](#解题思路) -* [23. 链表中环的入口结点](#23-链表中环的入口结点) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [24. 反转链表](#24-反转链表) - * [解题思路](#解题思路) - * [递归](#递归) - * [迭代](#迭代) -* [25. 合并两个排序的链表](#25-合并两个排序的链表) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - * [递归](#递归) - * [迭代](#迭代) -* [26. 树的子结构](#26-树的子结构) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [27. 二叉树的镜像](#27-二叉树的镜像) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [28 对称的二叉树](#28-对称的二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [29. 顺时针打印矩阵](#29-顺时针打印矩阵) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - - - -# 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) - -## 题目描述 - -```html -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+)?"); -} -``` - -# 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) - -## 题目描述 - -需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 - -

- -## 解题思路 - -```java -public void reOrderArray(int[] nums) { - // 奇数个数 - int oddCnt = 0; - for (int val : nums) - if (val % 2 == 1) - 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; - } -} -``` - -# 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) - -## 解题思路 - -设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。 - -

- -```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; -} -``` - -# 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) - -## 题目描述 - -一个链表中包含环,请找出该链表的环的入口结点。要求不能使用额外的空间。 - -## 解题思路 - -使用双指针,一个指针 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 将在环入口点相遇。 - -

- - -```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; -} -``` - -# 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) - -## 解题思路 - -### 递归 - -```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; -} -``` - -# 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) - -## 题目描述 - -

- -## 解题思路 - -### 递归 - -```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; -} -``` - -# 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) - -## 题目描述 - -

- -## 解题思路 - -```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); -} -``` - -# 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) - -## 题目描述 - -

- -## 解题思路 - -```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; -} -``` - -# 28 对称的二叉树 - -[NowCder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -

- -## 解题思路 - -```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); -} -``` - -# 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) - -## 题目描述 - -下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 - -

- -## 解题思路 - -```java -public ArrayList printMatrix(int[][] matrix) { - ArrayList 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; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 30~39.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 30~39.md" deleted file mode 100644 index b0aaefbfdd..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 30~39.md" +++ /dev/null @@ -1,499 +0,0 @@ - -* [30. 包含 min 函数的栈](#30-包含-min-函数的栈) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [32.1 从上往下打印二叉树](#321-从上往下打印二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [32.2 把二叉树打印成多行](#322-把二叉树打印成多行) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [35. 复杂链表的复制](#35-复杂链表的复制) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [37. 序列化二叉树](#37-序列化二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [38. 字符串的排列](#38-字符串的排列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [39. 数组中出现次数超过一半的数字](#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) - -## 题目描述 - -定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。 - -## 解题思路 - -```java -private Stack dataStack = new Stack<>(); -private Stack 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(); -} -``` - -# 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) - -## 题目描述 - -输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。 - -例如序列 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 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(); -} -``` - -# 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) - -## 题目描述 - -从上往下打印出二叉树的每个节点,同层节点从左至右打印。 - -例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7 - -

- -## 解题思路 - -使用队列来进行层次遍历。 - -不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 - -```java -public ArrayList PrintFromTopToBottom(TreeNode root) { - Queue queue = new LinkedList<>(); - ArrayList 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; -} -``` - -# 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) - -## 题目描述 - -和上题几乎一样。 - -## 解题思路 - -```java -ArrayList> Print(TreeNode pRoot) { - ArrayList> ret = new ArrayList<>(); - Queue queue = new LinkedList<>(); - queue.add(pRoot); - while (!queue.isEmpty()) { - ArrayList 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; -} -``` - -# 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) - -## 题目描述 - -请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。 - -## 解题思路 - -```java -public ArrayList> Print(TreeNode pRoot) { - ArrayList> ret = new ArrayList<>(); - Queue queue = new LinkedList<>(); - queue.add(pRoot); - boolean reverse = false; - while (!queue.isEmpty()) { - ArrayList 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; -} -``` - -# 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) - -## 题目描述 - -输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。假设输入的数组的任意两个数字都互不相同。 - -例如,下图是后序遍历序列 1,3,2 所对应的二叉搜索树。 - -

- -## 解题思路 - -```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); -} -``` - -# 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) - -## 题目描述 - -输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 - -下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12 - -

- -## 解题思路 - -```java -private ArrayList> ret = new ArrayList<>(); - -public ArrayList> FindPath(TreeNode root, int target) { - backtracking(root, target, new ArrayList<>()); - return ret; -} - -private void backtracking(TreeNode node, int target, ArrayList 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); -} -``` - -# 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) - -## 题目描述 - -输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。 - -```java -public class RandomListNode { - int label; - RandomListNode next = null; - RandomListNode random = null; - - RandomListNode(int label) { - this.label = label; - } -} -``` - -

- -## 解题思路 - -第一步,在每个节点的后面插入复制的节点。 - -

- -第二步,对复制节点的 random 链接进行赋值。 - -

- -第三步,拆分。 - -

- -```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; -} -``` - -# 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) - -## 题目描述 - -输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 - -

- -## 解题思路 - -```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); -} -``` - -# 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) - -## 题目描述 - -请实现两个函数,分别用来序列化和反序列化二叉树。 - -## 解题思路 - -```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; -} -``` - -# 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) - -## 题目描述 - -输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。 - -## 解题思路 - -```java -private ArrayList ret = new ArrayList<>(); - -public ArrayList 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; - } -} -``` - -# 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) - -## 解题思路 - -多数投票问题,可以利用 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; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 3~9.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 3~9.md" deleted file mode 100644 index 8850b4cda6..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 3~9.md" +++ /dev/null @@ -1,388 +0,0 @@ - -* [3. 数组中重复的数字](#3-数组中重复的数字) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [4. 二维数组中的查找](#4-二维数组中的查找) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [5. 替换空格](#5-替换空格) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [6. 从尾到头打印链表](#6-从尾到头打印链表) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - * [使用递归](#使用递归) - * [使用头插法](#使用头插法) - * [使用栈](#使用栈) -* [7. 重建二叉树](#7-重建二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [8. 二叉树的下一个结点](#8-二叉树的下一个结点) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [9. 用两个栈实现队列](#9-用两个栈实现队列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - - - -# 3. 数组中重复的数字 - -[NowCoder](https://www.nowcoder.com/practice/623a5ac0ea5b4e5f95552655361ae0a8?tpId=13&tqId=11203&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 - -```html -Input: -{2, 3, 1, 0, 2, 5} - -Output: -2 -``` - -## 解题思路 - -要求是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。 - -对于这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。 - -以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: - -

- -```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; -} -``` - -# 4. 二维数组中的查找 - -[NowCoder](https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -给定一个二维数组,其每一行从左到右递增排序,从上到下也是递增排序。给定一个数,判断这个数是否在该二维数组中。 - -```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)。 - -该二维数组中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。 - -

- -```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; -} -``` - -# 5. 替换空格 - -[NowCoder](https://www.nowcoder.com/practice/4060ac7e3e404ad1a894ef3e17650423?tpId=13&tqId=11155&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - - -将一个字符串中的空格替换成 "%20"。 - -```text -Input: -"A B" - -Output: -"A%20B" -``` - -## 解题思路 - -在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。因为一个空格要替换成三个字符(%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。 - -令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。P1 和 P2 从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。 - -从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。 - -

- -```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(); -} -``` - -# 6. 从尾到头打印链表 - -[NowCoder](https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035?tpId=13&tqId=11156&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -从尾到头反过来打印出每个结点的值。 - -

- -## 解题思路 - -### 使用递归 - -

- - -```java -public ArrayList printListFromTailToHead(ListNode listNode) { - ArrayList ret = new ArrayList<>(); - if (listNode != null) { - ret.addAll(printListFromTailToHead(listNode.next)); - ret.add(listNode.val); - } - return ret; -} -``` - -### 使用头插法 - -利用链表头插法为逆序的特点。 - -头结点和第一个节点的区别: - -- 头结点是在头插法中使用的一个额外节点,这个节点不存储值; -- 第一个节点就是链表的第一个真正存储值的节点。 - - -

- -```java -public ArrayList 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 ret = new ArrayList<>(); - head = head.next; - while (head != null) { - ret.add(head.val); - head = head.next; - } - return ret; -} -``` - -### 使用栈 - -

- -```java -public ArrayList printListFromTailToHead(ListNode listNode) { - Stack stack = new Stack<>(); - while (listNode != null) { - stack.add(listNode.val); - listNode = listNode.next; - } - ArrayList ret = new ArrayList<>(); - while (!stack.isEmpty()) - ret.add(stack.pop()); - return ret; -} -``` - -# 7. 重建二叉树 - -[NowCoder](https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=13&tqId=11157&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 - -```html -preorder = [3,9,20,15,7] -inorder = [9,3,15,20,7] -``` - -

- -## 解题思路 - -前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。 - -

- -```java -// 缓存中序遍历数组每个值对应的索引 -private Map 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; -} -``` - -# 8. 二叉树的下一个结点 - -[NowCoder](https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 - -```java -public class TreeLinkNode { - - int val; - TreeLinkNode left = null; - TreeLinkNode right = null; - TreeLinkNode next = null; - - TreeLinkNode(int val) { - this.val = val; - } -} -``` - -## 解题思路 - -① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点; - -

- -② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 - -

- -```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; -} -``` - -# 9. 用两个栈实现队列 - -[NowCoder](https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6?tpId=13&tqId=11158&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking) - -## 题目描述 - -用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。 - -## 解题思路 - -in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。 - -

- - -```java -Stack in = new Stack(); -Stack out = new Stack(); - -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(); -} -``` - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 40~49.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 40~49.md" deleted file mode 100644 index 358a5a398a..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 40~49.md" +++ /dev/null @@ -1,446 +0,0 @@ - -* [40. 最小的 K 个数](#40-最小的-k-个数) - * [解题思路](#解题思路) - * [快速选择](#快速选择) - * [大小为 K 的最小堆](#大小为-k-的最小堆) -* [41.1 数据流中的中位数](#411-数据流中的中位数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [41.2 字符流中第一个不重复的字符](#412-字符流中第一个不重复的字符) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [42. 连续子数组的最大和](#42-连续子数组的最大和) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [43. 从 1 到 n 整数中 1 出现的次数](#43-从-1-到-n-整数中-1-出现的次数) - * [解题思路](#解题思路) -* [44. 数字序列中的某一位数字](#44-数字序列中的某一位数字) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [45. 把数组排成最小的数](#45-把数组排成最小的数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [46. 把数字翻译成字符串](#46-把数字翻译成字符串) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [47. 礼物的最大价值](#47-礼物的最大价值) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [48. 最长不含重复字符的子字符串](#48-最长不含重复字符的子字符串) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [49. 丑数](#49-丑数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - - - -# 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) - -## 解题思路 - -### 快速选择 - -- 复杂度: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 GetLeastNumbers_Solution(int[] nums, int k) { - ArrayList 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 GetLeastNumbers_Solution(int[] nums, int k) { - if (k > nums.length || k <= 0) - return new ArrayList<>(); - PriorityQueue maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1); - for (int num : nums) { - maxHeap.add(num); - if (maxHeap.size() > k) - maxHeap.poll(); - } - return new ArrayList<>(maxHeap); -} -``` - -# 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) - -## 题目描述 - -如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 - -## 解题思路 - -```java -/* 大顶堆,存储左半边元素 */ -private PriorityQueue left = new PriorityQueue<>((o1, o2) -> o2 - o1); -/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */ -private PriorityQueue 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(); -} -``` - -# 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) - -## 题目描述 - -请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。 - -## 解题思路 - -```java -private int[] cnts = new int[256]; -private Queue 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(); -} -``` - -# 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) - -## 题目描述 - -{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; -} -``` - -# 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) - -## 解题思路 - -```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) - -# 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'; -} -``` - -# 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) - -## 题目描述 - -输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {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; -} -``` - -# 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]; -} -``` - -# 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]; -} -``` - -# 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; -} -``` - -# 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) - -## 题目描述 - -把只包含因子 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]; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 50~59.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 50~59.md" deleted file mode 100644 index fb2c2cf9fd..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 50~59.md" +++ /dev/null @@ -1,505 +0,0 @@ - -* [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [51. 数组中的逆序对](#51-数组中的逆序对) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [53. 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [54. 二叉查找树的第 K 个结点](#54-二叉查找树的第-k-个结点) - * [解题思路](#解题思路) -* [55.1 二叉树的深度](#551-二叉树的深度) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [55.2 平衡二叉树](#552-平衡二叉树) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [58.1 翻转单词顺序列](#581-翻转单词顺序列) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [58.2 左旋转字符串](#582-左旋转字符串) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [59. 滑动窗口的最大值](#59-滑动窗口的最大值) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - - - -# 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) - -## 题目描述 - -在一个字符串中找到第一个只出现一次的字符,并返回它的位置。 - -## 解题思路 - -最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。 - -```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; -} -``` - -# 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) - -## 题目描述 - -在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。 - -## 解题思路 - -```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]; -} -``` - -# 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) - -## 题目描述 - -

- -## 解题思路 - -设 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; -} -``` - -# 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) - -## 题目描述 - -```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; -} -``` - -# 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) - -## 解题思路 - -利用二叉查找树中序遍历有序的特点。 - -```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); -} -``` - -# 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) - -## 题目描述 - -从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 - -

- -## 解题思路 - -```java -public int TreeDepth(TreeNode root) { - return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right)); -} -``` - -# 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) - -## 题目描述 - -平衡二叉树左右子树高度差不超过 1。 - -

- -## 解题思路 - -```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); -} -``` - -# 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) - -## 题目描述 - -一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。 - -## 解题思路 - -两个不相等的元素在位级表示上必定会有一位存在不同,将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。 - -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; - } -} -``` - -# 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) - -## 题目描述 - -输入一个递增排序的数组和一个数字 S,在数组中查找两个数,使得他们的和正好是 S。如果有多对数字的和等于 S,输出两个数的乘积最小的。 - -## 解题思路 - -使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 - -- 如果两个指针指向元素的和 sum == target,那么得到要求的结果; -- 如果 sum > target,移动较大的元素,使 sum 变小一些; -- 如果 sum < target,移动较小的元素,使 sum 变大一些。 - -```java -public ArrayList 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<>(); -} -``` - -# 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) - -## 题目描述 - -输出所有和为 S 的连续正数序列。 - -例如和为 100 的连续序列有: - -``` -[9, 10, 11, 12, 13, 14, 15, 16] -[18, 19, 20, 21, 22]。 -``` - -## 解题思路 - -```java -public ArrayList> FindContinuousSequence(int sum) { - ArrayList> 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 list = new ArrayList<>(); - for (int i = start; i <= end; i++) - list.add(i); - ret.add(list); - curSum -= start; - start++; - end++; - curSum += end; - } - } - return ret; -} -``` - -# 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) - -## 题目描述 - -```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; -} -``` - -# 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) - -## 题目描述 - -```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; -} -``` - -# 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) - -## 题目描述 - -给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。 - -例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}。 - -## 解题思路 - -```java -public ArrayList maxInWindows(int[] num, int size) { - ArrayList ret = new ArrayList<>(); - if (size > num.length || size < 1) - return ret; - PriorityQueue 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; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 60~68.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 60~68.md" deleted file mode 100644 index a3d5610f0c..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - 60~68.md" +++ /dev/null @@ -1,347 +0,0 @@ - -* [60. n 个骰子的点数](#60-n-个骰子的点数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) - * [动态规划解法](#动态规划解法) - * [动态规划解法 + 旋转数组](#动态规划解法--旋转数组) -* [61. 扑克牌顺子](#61-扑克牌顺子) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [63. 股票的最大利润](#63-股票的最大利润) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [64. 求 1+2+3+...+n](#64-求-123n) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [65. 不用加减乘除做加法](#65-不用加减乘除做加法) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [66. 构建乘积数组](#66-构建乘积数组) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [67. 把字符串转换成整数](#67-把字符串转换成整数) - * [题目描述](#题目描述) - * [解题思路](#解题思路) -* [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) - * [解题思路](#解题思路) - * [二叉查找树](#二叉查找树) - * [普通二叉树](#普通二叉树) - - - -# 60. n 个骰子的点数 - -[Lintcode](https://www.lintcode.com/en/problem/dices-sum/) - -## 题目描述 - -把 n 个骰子仍在地上,求点数和为 s 的概率。 - -

- -## 解题思路 - -### 动态规划解法 - -使用一个二维数组 dp 存储点数出现的次数,其中 dp[i][j] 表示前 i 个骰子产生点数 j 的次数。 - -空间复杂度:O(N2) - -```java -public List> 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> 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> 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> ret = new ArrayList<>(); - for (int i = n; i <= pointNum; i++) - ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum)); - - return ret; -} -``` - -# 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) - -## 题目描述 - -五张牌,其中大小鬼为癞子,牌面大小为 0。判断这五张牌是否能组成顺子。 - -

- -## 解题思路 - -```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; -} -``` - -# 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) - -## 题目描述 - -让小朋友们围成一个大圈。然后,随机指定一个数 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; -} -``` - -# 63. 股票的最大利润 - -[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/) - -## 题目描述 - -可以有一次买入和一次卖出,那么买入必须在前。求最大收益。 - -

- -## 解题思路 - -使用贪心策略,假设第 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; -} -``` - -# 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) - -## 题目描述 - -要求不能使用乘除法、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; -} -``` - -# 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) - -## 题目描述 - -写一个函数,求两个整数之和,要求不得使用 +、-、\*、/ 四则运算符号。 - -## 解题思路 - -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); -} -``` - -# 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) - -## 题目描述 - -给定一个数组 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]。要求不能使用除法。 - -

- -## 解题思路 - -```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; -} -``` - -# 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) - -## 题目描述 - -将一个字符串转换成一个整数,字符串不是一个合法的数值则返回 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; -} -``` - -# 68. 树中两个节点的最低公共祖先 - -## 解题思路 - -### 二叉查找树 - -

- -[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。 - -```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; -} -``` - -### 普通二叉树 - -

- -[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 分别在两个子树中,那么就说明根节点就是最低公共祖先。 - -```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; -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" deleted file mode 100644 index d4d5966d7f..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" +++ /dev/null @@ -1,27 +0,0 @@ - -* [目录](#目录) -* [参考文献](#参考文献) - - - -# 目录 - -部分绘图文件可以在这里免费下载:[剑指 Offer](https://www.processon.com/view/5a3e4c7be4b0909c1aa18b49),后续会慢慢把所有题目都配上 GIF 演示图。 - -- [3\~9](剑指%20Offer%20题解%20-%203\~9.md) -- [10\~19](剑指%20Offer%20题解%20-%2010\~19.md) -- [20\~29](剑指%20Offer%20题解%20-%2020\~29.md) -- [30\~39](剑指%20Offer%20题解%20-%2030\~39.md) -- [40\~49](剑指%20Offer%20题解%20-%2040\~49.md) -- [50\~59](剑指%20Offer%20题解%20-%2050\~59.md) -- [60\~68](剑指%20Offer%20题解%20-%2060\~68.md) - -# 参考文献 - -何海涛. 剑指 Offer[M]. 电子工业出版社, 2012. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" "b/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" deleted file mode 100644 index 045b515f62..0000000000 --- "a/docs/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\2251.md" +++ /dev/null @@ -1,27 +0,0 @@ - -* [目录](#目录) -* [参考文献](#参考文献) - - - -# 目录 - -部分绘图文件可以在这里免费下载:[剑指 Offer](https://www.processon.com/view/5a3e4c7be4b0909c1aa18b49),后续会慢慢把所有题目都配上 GIF 演示图。 - -- [3\~9](notes/剑指%20Offer%20题解%20-%203\~9.md) -- [10\~19](notes/剑指%20Offer%20题解%20-%2010\~19.md) -- [20\~29](notes/剑指%20Offer%20题解%20-%2020\~29.md) -- [30\~39](notes/剑指%20Offer%20题解%20-%2030\~39.md) -- [40\~49](notes/剑指%20Offer%20题解%20-%2040\~49.md) -- [50\~59](notes/剑指%20Offer%20题解%20-%2050\~59.md) -- [60\~68](notes/剑指%20Offer%20题解%20-%2060\~68.md) - -# 参考文献 - -何海涛. 剑指 Offer[M]. 电子工业出版社, 2012. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" "b/docs/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" deleted file mode 100644 index b1bfac5fa8..0000000000 --- "a/docs/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" +++ /dev/null @@ -1,587 +0,0 @@ - -* [一、事务](#一事务) - * [概念](#概念) - * [ACID](#acid) - * [AUTOCOMMIT](#autocommit) -* [二、并发一致性问题](#二并发一致性问题) - * [丢失修改](#丢失修改) - * [读脏数据](#读脏数据) - * [不可重复读](#不可重复读) - * [幻影读](#幻影读) -* [三、封锁](#三封锁) - * [封锁粒度](#封锁粒度) - * [封锁类型](#封锁类型) - * [封锁协议](#封锁协议) - * [MySQL 隐式与显示锁定](#mysql-隐式与显示锁定) -* [四、隔离级别](#四隔离级别) - * [未提交读(READ UNCOMMITTED)](#未提交读read-uncommitted) - * [提交读(READ COMMITTED)](#提交读read-committed) - * [可重复读(REPEATABLE READ)](#可重复读repeatable-read) - * [可串行化(SERIALIZABLE)](#可串行化serializable) -* [五、多版本并发控制](#五多版本并发控制) - * [版本号](#版本号) - * [隐藏的列](#隐藏的列) - * [Undo 日志](#undo-日志) - * [实现过程](#实现过程) - * [快照读与当前读](#快照读与当前读) -* [六、Next-Key Locks](#六next-key-locks) - * [Record Locks](#record-locks) - * [Gap Locks](#gap-locks) - * [Next-Key Locks](#next-key-locks) -* [七、关系数据库设计理论](#七关系数据库设计理论) - * [函数依赖](#函数依赖) - * [异常](#异常) - * [范式](#范式) -* [八、ER 图](#八er-图) - * [实体的三种联系](#实体的三种联系) - * [表示出现多次的关系](#表示出现多次的关系) - * [联系的多向性](#联系的多向性) - * [表示子类](#表示子类) -* [参考资料](#参考资料) - - - -# 一、事务 - -## 概念 - -事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。 - -

- -## ACID - -### 1. 原子性(Atomicity) - -事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。 - -回滚可以用回滚日志来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。 - -### 2. 一致性(Consistency) - -数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。 - -### 3. 隔离性(Isolation) - -一个事务所做的修改在最终提交以前,对其它事务是不可见的。 - -### 4. 持久性(Durability) - -一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。 - -使用重做日志来保证持久性。 - ----- - -事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系: - -- 只有满足一致性,事务的执行结果才是正确的。 -- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。 -- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。 -- 事务满足持久化是为了能应对数据库崩溃的情况。 - -

- -## AUTOCOMMIT - -MySQL 默认采用自动提交模式。也就是说,如果不显式使用`START TRANSACTION`语句来开始一个事务,那么每个查询都会被当做一个事务自动提交。 - -# 二、并发一致性问题 - -在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。 - -## 丢失修改 - -T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。 - -

- -## 读脏数据 - -T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。 - -

- -## 不可重复读 - -T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。 - -

- - -## 幻影读 - -T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。 - -

- - ----- - -产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。 - -# 三、封锁 - -## 封锁粒度 - -MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 - -应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。 - -但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大。 - -在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。 - -

- -## 封锁类型 - -### 1. 读写锁 - -- 排它锁(Exclusive),简写为 X 锁,又称写锁。 -- 共享锁(Shared),简写为 S 锁,又称读锁。 - -有以下两个规定: - -- 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。 -- 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。 - -锁的兼容关系如下: - -| - | X | S | -| :--: | :--: | :--: | -|X|×|×| -|S|×|√| - -### 2. 意向锁 - -使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。 - -在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。 - -意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定: - -- 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁; -- 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。 - -通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。 - -各种锁的兼容关系如下: - -| - | X | IX | S | IS | -| :--: | :--: | :--: | :--: | :--: | -|X |× |× |× | ×| -|IX |× |√ |× | √| -|S |× |× |√ | √| -|IS |× |√ |√ | √| - -解释如下: - -- 任意 IS/IX 锁之间都是兼容的,因为它们只是表示想要对表加锁,而不是真正加锁; -- S 锁只与 S 锁和 IS 锁兼容,也就是说事务 T 想要对数据行加 S 锁,其它事务可以已经获得对表或者表中的行的 S 锁。 - -## 封锁协议 - -### 1. 三级封锁协议 - -**一级封锁协议** - -事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。 - -可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。 - -| T1 | T2 | -| :--: | :--: | -| lock-x(A) | | -| read A=20 | | -| | lock-x(A) | -| | wait | -| write A=19 |. | -| commit |. | -| unlock-x(A) |. | -| | obtain | -| | read A=19 | -| | write A=21 | -| | commit | -| | unlock-x(A)| - -**二级封锁协议** - -在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。 - -可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。 - -| T1 | T2 | -| :--: | :--: | -| lock-x(A) | | -| read A=20 | | -| write A=19 | | -| | lock-s(A) | -| | wait | -| rollback | .| -| A=20 |. | -| unlock-x(A) |. | -| | obtain | -| | read A=20 | -| | unlock-s(A)| -| | commit | - -**三级封锁协议** - -在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。 - -可以解决不可重复读的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。 - -| T1 | T2 | -| :--: | :--: | -| lock-s(A) | | -| read A=20 | | -| |lock-x(A) | -| | wait | -| read A=20| . | -| commit | .| -| unlock-s(A) |. | -| | obtain | -| | read A=20 | -| | write A=19| -| | commit | -| | unlock-X(A)| - -### 2. 两段锁协议 - -加锁和解锁分为两个阶段进行。 - -可串行化调度是指,通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。 - -事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。 - -```html -lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B) -``` - -但不是必要条件,例如以下操作不满足两段锁协议,但是它还是可串行化调度。 - -```html -lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C) -``` - -## MySQL 隐式与显示锁定 - -MySQL 的 InnoDB 存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定。 - -InnoDB 也可以使用特定的语句进行显示锁定: - -```sql -SELECT ... LOCK In SHARE MODE; -SELECT ... FOR UPDATE; -``` - -# 四、隔离级别 - -## 未提交读(READ UNCOMMITTED) - -事务中的修改,即使没有提交,对其它事务也是可见的。 - -## 提交读(READ COMMITTED) - -一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。 - -## 可重复读(REPEATABLE READ) - -保证在同一个事务中多次读取同样数据的结果是一样的。 - -## 可串行化(SERIALIZABLE) - -强制事务串行执行。 - ----- - -| 隔离级别 | 脏读 | 不可重复读 | 幻影读 | 加锁读 | -| :---: | :---: | :---:| :---: | :---: | -| 未提交读 | √ | √ | √ | × | -| 提交读 | × | √ | √ | × | -| 可重复读 | × | × | √ | × | -| 可串行化 | × | × | × | √ | - -# 五、多版本并发控制 - -多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 - -## 版本号 - -- 系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。 -- 事务版本号:事务开始时的系统版本号。 - -## 隐藏的列 - -MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号: - -- 创建版本号:指示创建一个数据行的快照时的系统版本号; -- 删除版本号:如果该快照的删除版本号大于当前事务版本号表示该快照有效,否则表示该快照已经被删除了。 - -## Undo 日志 - -MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。 - -

- -## 实现过程 - -以下实现过程针对可重复读隔离级别。 - -当开始新一个事务时,该事务的版本号肯定会大于当前所有数据行快照的创建版本号,理解这一点很关键。 - -### 1. SELECT - -多个事务必须读取到同一个数据行的快照,并且这个快照是距离现在最近的一个有效快照。但是也有例外,如果有一个事务正在修改该数据行,那么它可以读取事务本身所做的修改,而不用和其它事务的读取结果一致。 - -把没有对一个数据行做修改的事务称为 T,T 所要读取的数据行快照的创建版本号必须小于 T 的版本号,因为如果大于或者等于 T 的版本号,那么表示该数据行快照是其它事务的最新修改,因此不能去读取它。除此之外,T 所要读取的数据行快照的删除版本号必须大于 T 的版本号,因为如果小于等于 T 的版本号,那么表示该数据行快照是已经被删除的,不应该去读取它。 - -### 2. INSERT - -将当前系统版本号作为数据行快照的创建版本号。 - -### 3. DELETE - -将当前系统版本号作为数据行快照的删除版本号。 - -### 4. UPDATE - -将当前系统版本号作为更新前的数据行快照的删除版本号,并将当前系统版本号作为更新后的数据行快照的创建版本号。可以理解为先执行 DELETE 后执行 INSERT。 - -## 快照读与当前读 - -### 1. 快照读 - -使用 MVCC 读取的是快照中的数据,这样可以减少加锁所带来的开销。 - -```sql -select * from table ...; -``` - -### 2. 当前读 - -读取的是最新的数据,需要加锁。以下第一个语句需要加 S 锁,其它都需要加 X 锁。 - -```sql -select * from table where ? lock in share mode; -select * from table where ? for update; -insert; -update; -delete; -``` - -# 六、Next-Key Locks - -Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。 - -MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。 - -## Record Locks - -锁定一个记录上的索引,而不是记录本身。 - -如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。 - -## Gap Locks - -锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。 - -```sql -SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; -``` - -## Next-Key Locks - -它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间: - -```sql -(negative infinity, 10] -(10, 11] -(11, 13] -(13, 20] -(20, positive infinity) -``` - -# 七、关系数据库设计理论 - -## 函数依赖 - -记 A->B 表示 A 函数决定 B,也可以说 B 函数依赖于 A。 - -如果 {A1,A2,... ,An} 是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小的,那么该集合就称为键码。 - -对于 A->B,如果能找到 A 的真子集 A',使得 A'-> B,那么 A->B 就是部分函数依赖,否则就是完全函数依赖。 - -对于 A->B,B->C,则 A->C 是一个传递函数依赖。 - -## 异常 - -以下的学生课程关系的函数依赖为 Sno, Cname -> Sname, Sdept, Mname, Grade,键码为 {Sno, Cname}。也就是说,确定学生和课程之后,就能确定其它信息。 - -| Sno | Sname | Sdept | Mname | Cname | Grade | -| :---: | :---: | :---: | :---: | :---: |:---:| -| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 | -| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 | -| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 | -| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 | - -不符合范式的关系,会产生很多异常,主要有以下四种异常: - -- 冗余数据:例如 `学生-2` 出现了两次。 -- 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。 -- 删除异常:删除一个信息,那么也会丢失其它信息。例如删除了 `课程-1` 需要删除第一行和第三行,那么 `学生-1` 的信息就会丢失。 -- 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。 - -## 范式 - -范式理论是为了解决以上提到四种异常。 - -高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。 - -

- -### 1. 第一范式 (1NF) - -属性不可分。 - -### 2. 第二范式 (2NF) - -每个非主属性完全函数依赖于键码。 - -可以通过分解来满足。 - - **分解前**
- -| Sno | Sname | Sdept | Mname | Cname | Grade | -| :---: | :---: | :---: | :---: | :---: |:---:| -| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 | -| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 | -| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 | -| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 | - -以上学生课程关系中,{Sno, Cname} 为键码,有如下函数依赖: - -- Sno -> Sname, Sdept -- Sdept -> Mname -- Sno, Cname-> Grade - -Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。 - -Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。 - - **分解后**
- -关系-1 - -| Sno | Sname | Sdept | Mname | -| :---: | :---: | :---: | :---: | -| 1 | 学生-1 | 学院-1 | 院长-1 | -| 2 | 学生-2 | 学院-2 | 院长-2 | -| 3 | 学生-3 | 学院-2 | 院长-2 | - -有以下函数依赖: - -- Sno -> Sname, Sdept -- Sdept -> Mname - -关系-2 - -| Sno | Cname | Grade | -| :---: | :---: |:---:| -| 1 | 课程-1 | 90 | -| 2 | 课程-2 | 80 | -| 2 | 课程-1 | 100 | -| 3 | 课程-2 | 95 | - -有以下函数依赖: - -- Sno, Cname -> Grade - -### 3. 第三范式 (3NF) - -非主属性不传递函数依赖于键码。 - -上面的 关系-1 中存在以下传递函数依赖: - -- Sno -> Sdept -> Mname - -可以进行以下分解: - -关系-11 - -| Sno | Sname | Sdept | -| :---: | :---: | :---: | -| 1 | 学生-1 | 学院-1 | -| 2 | 学生-2 | 学院-2 | -| 3 | 学生-3 | 学院-2 | - -关系-12 - -| Sdept | Mname | -| :---: | :---: | -| 学院-1 | 院长-1 | -| 学院-2 | 院长-2 | - -# 八、ER 图 - -Entity-Relationship,有三个组成部分:实体、属性、联系。 - -用来进行关系型数据库系统的概念设计。 - -## 实体的三种联系 - -包含一对一,一对多,多对多三种。 - -- 如果 A 到 B 是一对多关系,那么画个带箭头的线段指向 B; -- 如果是一对一,画两个带箭头的线段; -- 如果是多对多,画两个不带箭头的线段。 - -下图的 Course 和 Student 是一对多的关系。 - -

- -## 表示出现多次的关系 - -一个实体在联系出现几次,就要用几条线连接。 - -下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。 - -

- -## 联系的多向性 - -虽然老师可以开设多门课,并且可以教授多名学生,但是对于特定的学生和课程,只有一个老师教授,这就构成了一个三元联系。 - -

- -一般只使用二元联系,可以把多元联系转换为二元联系。 - -

- -## 表示子类 - -用一个三角形和两条线来连接类和子类,与子类有关的属性和联系都连到子类上,而与父类和子类都有关的连到父类上。 - -

- -# 参考资料 - -- AbrahamSilberschatz, HenryF.Korth, S.Sudarshan, 等. 数据库系统概念 [M]. 机械工业出版社, 2006. -- 施瓦茨. 高性能 MYSQL(第3版)[M]. 电子工业出版社, 2013. -- 史嘉权. 数据库系统概论[M]. 清华大学出版社有限公司, 2006. -- [The InnoDB Storage Engine](https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html) -- [Transaction isolation levels](https://www.slideshare.net/ErnestoHernandezRodriguez/transaction-isolation-levels) -- [Concurrency Control](http://scanftree.com/dbms/2-phase-locking-protocol) -- [The Nightmare of Locking, Blocking and Isolation Levels!](https://www.slideshare.net/brshristov/the-nightmare-of-locking-blocking-and-isolation-levels-46391666) -- [Database Normalization and Normal Forms with an Example](https://aksakalli.github.io/2012/03/12/database-normalization-and-normal-forms-with-an-example.html) -- [The basics of the InnoDB undo logging and history system](https://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/) -- [MySQL locking for the busy web developer](https://www.brightbox.com/blog/2013/10/31/on-mysql-locks/) -- [浅入浅出 MySQL 和 InnoDB](https://draveness.me/mysql-innodb) -- [Innodb 中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" "b/docs/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" deleted file mode 100644 index 888b9ac043..0000000000 --- "a/docs/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" +++ /dev/null @@ -1,147 +0,0 @@ - -* [一、构建工具的作用](#一构建工具的作用) -* [二、Java 主流构建工具](#二java-主流构建工具) -* [三、Maven](#三maven) -* [参考资料](#参考资料) - - - -# 一、构建工具的作用 - -构建工具是用于构建项目的自动化工具,主要包含以下工作: - -## 依赖管理 - -不再需要手动导入 Jar 依赖包,并且可以自动处理依赖关系,也就是说某个依赖如果依赖于其它依赖,构建工具可以帮助我们自动处理这种依赖管理。 - -## 运行单元测试 - -不再需要在项目代码中添加测试代码,从而避免了污染项目代码。 - -## 将源代码转化为可执行文件 - -包含预处理、编译、汇编、链接等步骤。 - -## 将可执行文件进行打包 - -不再需要使用 IDE 将应用程序打包成 Jar 包。 - -## 发布到生产服务器上 - -不再需要通过 FTP 将 Jar 包上传到服务器上。 - -# 二、Java 主流构建工具 - -主要包括 Ant、Maven 和 Gradle。 - -

- -Gradle 和 Maven 的区别是,它使用 Groovy 这种特定领域语言(DSL)来管理构建脚本,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。 - -例如要在项目中引入 Junit,Maven 的代码如下: - -```xml - - - 4.0.0 - - jizg.study.maven.hello - hello-first - 0.0.1-SNAPSHOT - - - - junit - junit - 4.10 - test - - - -``` - -而 Gradle 只需要几行代码: - -```java -dependencies { - testCompile "junit:junit:4.10" -} -``` - -# 三、Maven - -## 概述 - -提供了项目对象模型(POM)文件来管理项目的构建。 - -## 仓库 - -仓库的搜索顺序为:本地仓库、中央仓库、远程仓库。 - -- 本地仓库用来存储项目的依赖库; -- 中央仓库是下载依赖库的默认位置; -- 远程仓库,因为并非所有的库存储在中央仓库,或者中央仓库访问速度很慢,远程仓库是中央仓库的补充。 - -## POM - -POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目录的 pom.xml 文件中。 - -```xml - - junit - junit - 4.12 - test - -``` - -[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classifier 不能直接定义的,需要结合插件使用。 - - -- groupId:项目组 Id,必须全球唯一; -- artifactId:项目 Id,即项目名; -- version:项目版本; -- packaging:项目打包方式。 - -## 依赖原则 - -### 1. 依赖路径最短优先原则 - -```html -A -> B -> C -> X(1.0) -A -> D -> X(2.0) -``` -由于 X(2.0) 路径最短,所以使用 X(2.0)。 - -### 2. 声明顺序优先原则 - -```html -A -> B -> X(1.0) -A -> C -> X(2.0) -``` - -在 POM 中最先声明的优先,上面的两个依赖如果先声明 B,那么最后使用 X(1.0)。 - -### 3. 覆写优先原则 - -子 POM 内声明的依赖优先于父 POM 中声明的依赖。 - -## 解决依赖冲突 - -找到 Maven 加载的 Jar 包版本,使用 `mvn dependency:tree` 查看依赖树,根据依赖原则来调整依赖在 POM 文件的声明顺序。 - -# 参考资料 - -- [POM Reference](http://maven.apache.org/pom.html#Dependency_Version_Requirement_Specification) -- [What is a build tool?](https://stackoverflow.com/questions/7249871/what-is-a-build-tool) -- [Java Build Tools Comparisons: Ant vs Maven vs Gradle](https://programmingmitra.blogspot.com/2016/05/java-build-tools-comparisons-ant-vs.html) -- [maven 2 gradle](http://sagioto.github.io/maven2gradle/) -- [新一代构建工具 gradle](https://www.imooc.com/learn/833) - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/docs/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" deleted file mode 100644 index b735dfe99b..0000000000 --- "a/docs/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" +++ /dev/null @@ -1,394 +0,0 @@ - -* [一、概述](#一概述) -* [二、匹配单个字符](#二匹配单个字符) -* [三、匹配一组字符](#三匹配一组字符) -* [四、使用元字符](#四使用元字符) -* [五、重复匹配](#五重复匹配) -* [六、位置匹配](#六位置匹配) -* [七、使用子表达式](#七使用子表达式) -* [八、回溯引用](#八回溯引用) -* [九、前后查找](#九前后查找) -* [十、嵌入条件](#十嵌入条件) -* [参考资料](#参考资料) - - - -# 一、概述 - -正则表达式用于文本内容的查找和替换。 - -正则表达式内置于其它语言或者软件产品中,它本身不是一种语言或者软件。 - -[正则表达式在线工具](https://regexr.com/) - -# 二、匹配单个字符 - -**.** 可以用来匹配任何的单个字符,但是在绝大多数实现里面,不能匹配换行符; - -**.** 是元字符,表示它有特殊的含义,而不是字符本身的含义。如果需要匹配 . ,那么要用 \ 进行转义,即在 . 前面加上 \ 。 - -正则表达式一般是区分大小写的,但是也有些实现是不区分。 - -**正则表达式** - -``` -nam. -``` - -**匹配结果** - -My **name** is Zheng. - -# 三、匹配一组字符 - -**[ ]** 定义一个字符集合; - -0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定,字符区间在 [ ] 中使用。 - -**-** 只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; - -**^** 在 [ ] 中是取非操作。 - -**应用** - -匹配以 abc 为开头,并且最后一个字母不为数字的字符串: - -**正则表达式** - -``` -abc[^0-9] -``` - -**匹配结果** - -1. **abcd** -2. abc1 -3. abc2 - -# 四、使用元字符 - -## 匹配空白字符 - -| 元字符 | 说明 | -| :---: | :---: | -| [\b] | 回退(删除)一个字符 | -| \f | 换页符 | -| \n | 换行符 | -| \r | 回车符 | -| \t | 制表符 | -| \v | 垂直制表符 | - -\r\n 是 Windows 中的文本行结束标签,在 Unix/Linux 则是 \n。 - -\r\n\r\n 可以匹配 Windows 下的空白行,因为它将匹配两个连续的行尾标签,而这正是两条记录之间的空白行; - -## 匹配特定的字符类别 - -### 1. 数字元字符 - -| 元字符 | 说明 | -| :---: | :---: | -| \d | 数字字符,等价于 [0-9] | -| \D | 非数字字符,等价于 [^0-9] | - -### 2. 字母数字元字符 - -| 元字符 | 说明 | -| :---: | :---: | -| \w | 大小写字母,下划线和数字,等价于 [a-zA-Z0-9\_] | -| \W | 对 \w 取非 | - -### 3. 空白字符元字符 - -| 元字符 | 说明 | -| :---: | :---: | -| \s | 任何一个空白字符,等价于 [\f\n\r\t\v] | -| \S | 对 \s 取非 | - -\x 匹配十六进制字符,\0 匹配八进制,例如 \x0A 对应 ASCII 字符 10,等价于 \n。 - -# 五、重复匹配 - -- **\+** 匹配 1 个或者多个字符 -- **\** * 匹配 0 个或者多个 -- **?** 匹配 0 个或者 1 个 - -**应用** - -匹配邮箱地址。 - -**正则表达式** - -``` -[\w.]+@\w+\.\w+ -``` - -[\w.] 匹配的是字母数字或者 . ,在其后面加上 + ,表示匹配多次。在字符集合 [ ] 里,. 不是元字符; - -**匹配结果** - -**abc.def@qq.com** - -- **{n}** 匹配 n 个字符 -- **{m, n}** 匹配 m\~n 个字符 -- **{m,}** 至少匹配 m 个字符 - -\* 和 + 都是贪婪型元字符,会匹配最多的内容。在后面加 ? 可以转换为懒惰型元字符,例如 \*?、+? 和 {m, n}? 。 - -**正则表达式** - -``` -a.+c -``` - -由于 + 是贪婪型的,因此 .+ 会匹配更可能多的内容,所以会把整个 abcabcabc 文本都匹配,而不是只匹配前面的 abc 文本。用懒惰型可以实现匹配前面的。 - -**匹配结果** - -**abcabcabc** - -# 六、位置匹配 - -## 单词边界 - -**\b** 可以匹配一个单词的边界,边界是指位于 \w 和 \W 之间的位置;**\B** 匹配一个不是单词边界的位置。 - -\b 只匹配位置,不匹配字符,因此 \babc\b 匹配出来的结果为 3 个字符。 - -## 字符串边界 - -**^** 匹配整个字符串的开头,**$** 匹配结尾。 - -^ 元字符在字符集合中用作求非,在字符集合外用作匹配字符串的开头。 - -分行匹配模式(multiline)下,换行被当做字符串的边界。 - -**应用** - -匹配代码中以 // 开始的注释行 - -**正则表达式** - -``` -^\s*\/\/.*$ -``` - -

- -**匹配结果** - -1. public void fun() { -2.      **// 注释 1** -3.      int a = 1; -4.      int b = 2; -5.      **// 注释 2** -6.      int c = a + b; -7. } - -# 七、使用子表达式 - -使用 **( )** 定义一个子表达式。子表达式的内容可以当成一个独立元素,即可以将它看成一个字符,并且使用 * 等元字符。 - -子表达式可以嵌套,但是嵌套层次过深会变得很难理解。 - -**正则表达式** - -``` -(ab){2,} -``` - -**匹配结果** - -**ababab** - -**|** 是或元字符,它把左边和右边所有的部分都看成单独的两个部分,两个部分只要有一个匹配就行。 - -**正则表达式** - -``` -(19|20)\d{2} -``` - -**匹配结果** - -1. **1900** -2. **2010** -3. 1020 - -**应用** - -匹配 IP 地址。 - -IP 地址中每部分都是 0-255 的数字,用正则表达式匹配时以下情况是合法的: - -- 一位数字 -- 不以 0 开头的两位数字 -- 1 开头的三位数 -- 2 开头,第 2 位是 0-4 的三位数 -- 25 开头,第 3 位是 0-5 的三位数 - -**正则表达式** - -``` -((25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))\.){3}(25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d)) -``` - -**匹配结果** - -1. **192.168.0.1** -2. 00.00.00.00 -3. 555.555.555.555 - -# 八、回溯引用 - -回溯引用使用 **\n** 来引用某个子表达式,其中 n 代表的是子表达式的序号,从 1 开始。它和子表达式匹配的内容一致,比如子表达式匹配到 abc,那么回溯引用部分也需要匹配 abc 。 - -**应用** - -匹配 HTML 中合法的标题元素。 - -**正则表达式** - -\1 将回溯引用子表达式 (h[1-6]) 匹配的内容,也就是说必须和子表达式匹配的内容一致。 - -``` -<(h[1-6])>\w*?<\/\1> -``` - -**匹配结果** - -1. **<h1>x</h1>** -2. **<h2>x</h2>** -3. <h3>x</h1> - -## 替换 - -需要用到两个正则表达式。 - -**应用** - -修改电话号码格式。 - -**文本** - -313-555-1234 - -**查找正则表达式** - -``` -(\d{3})(-)(\d{3})(-)(\d{4}) -``` - -**替换正则表达式** - -在第一个子表达式查找的结果加上 () ,然后加一个空格,在第三个和第五个字表达式查找的结果中间加上 - 进行分隔。 - -``` -($1) $3-$5 -``` - -**结果** - -(313) 555-1234 - -## 大小写转换 - -| 元字符 | 说明 | -| :---: | :---: | -| \l | 把下个字符转换为小写 | -| \u| 把下个字符转换为大写 | -| \L | 把\L 和\E 之间的字符全部转换为小写 | -| \U | 把\U 和\E 之间的字符全部转换为大写 | -| \E | 结束\L 或者\U | - -**应用** - -把文本的第二个和第三个字符转换为大写。 - -**文本** - -abcd - -**查找** - -``` -(\w)(\w{2})(\w) -``` - -**替换** - -``` -$1\U$2\E$3 -``` - -**结果** - -aBCd - -# 九、前后查找 - -前后查找规定了匹配的内容首尾应该匹配的内容,但是又不包含首尾匹配的内容。向前查找用 **?=** 来定义,它规定了尾部匹配的内容,这个匹配的内容在 ?= 之后定义。所谓向前查找,就是规定了一个匹配的内容,然后以这个内容为尾部向前面查找需要匹配的内容。向后匹配用 ?<= 定义(注: javaScript 不支持向后匹配, java 对其支持也不完善)。 - -**应用** - -查找出邮件地址 @ 字符前面的部分。 - -**正则表达式** - -``` -\w+(?=@) -``` - -**结果** - -**abc** @qq.com - -对向前和向后查找取非,只要把 = 替换成 ! 即可,比如 (?=) 替换成 (?!) 。取非操作使得匹配那些首尾不符合要求的内容。 - -# 十、嵌入条件 - -## 回溯引用条件 - -条件判断为某个子表达式是否匹配,如果匹配则需要继续匹配条件表达式后面的内容。 - -**正则表达式** - -子表达式 (\\() 匹配一个左括号,其后的 ? 表示匹配 0 个或者 1 个。 ?(1) 为条件,当子表达式 1 匹配时条件成立,需要执行 \) 匹配,也就是匹配右括号。 - -``` -(\()?abc(?(1)\)) -``` - -**结果** - -1. **(abc)** -2. **abc** -3. (abc - -## 前后查找条件 - -条件为定义的首尾是否匹配,如果匹配,则继续执行后面的匹配。注意,首尾不包含在匹配的内容中。 - -**正则表达式** - - ?(?=-) 为前向查找条件,只有在以 - 为前向查找的结尾能匹配 \d{5} ,才继续匹配 -\d{4} 。 - -``` -\d{5}(?(?=-)-\d{4}) -``` - -**结果** - -1. **11111** -2. 22222- -3. **33333-4444** - -# 参考资料 - -- BenForta. 正则表达式必知必会 [M]. 人民邮电出版社, 2007. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/docs/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" deleted file mode 100644 index df3e619f61..0000000000 --- "a/docs/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" +++ /dev/null @@ -1,88 +0,0 @@ - -* [一、消息模型](#一消息模型) - * [点对点](#点对点) - * [发布/订阅](#发布订阅) -* [二、使用场景](#二使用场景) - * [异步处理](#异步处理) - * [流量削锋](#流量削锋) - * [应用解耦](#应用解耦) -* [三、可靠性](#三可靠性) - * [发送端的可靠性](#发送端的可靠性) - * [接收端的可靠性](#接收端的可靠性) -* [参考资料](#参考资料) - - - -# 一、消息模型 - -## 点对点 - -消息生产者向消息队列中发送了一个消息之后,只能被一个消费者消费一次。 - -

- -## 发布/订阅 - -消息生产者向频道发送一个消息之后,多个消费者可以从该频道订阅到这条消息并消费。 - -

- -发布与订阅模式和观察者模式有以下不同: - -- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。 -- 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息,可以立即返回。 - -

- - -# 二、使用场景 - -## 异步处理 - -发送者将消息发送给消息队列之后,不需要同步等待消息接收者处理完毕,而是立即返回进行其它操作。消息接收者从消息队列中订阅消息之后异步处理。 - -例如在注册流程中通常需要发送验证邮件来确保注册用户身份的合法性,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。 - -只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成注册的话,就不能再使用消息队列。 - -## 流量削锋 - -在高并发的场景下,如果短时间有大量的请求到达会压垮服务器。 - -可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。 - -## 应用解耦 - -如果模块之间不直接进行调用,模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。 - -通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。 - -# 三、可靠性 - -## 发送端的可靠性 - -发送端完成操作后一定能将消息成功发送到消息队列中。 - -实现方法: - -- 在本地数据库建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息队列中,若转移消息成功则删除消息表中的数据,否则继续重传。 - -## 接收端的可靠性 - -接收端能够从消息队列成功消费一次消息。 - -两种实现方法: - -- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 -- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 - -# 参考资料 - -- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/) -- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" "b/docs/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" deleted file mode 100644 index 6648d886ab..0000000000 --- "a/docs/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" +++ /dev/null @@ -1,198 +0,0 @@ - -* [前言](#前言) -* [Quick Find](#quick-find) -* [Quick Union](#quick-union) -* [加权 Quick Union](#加权-quick-union) -* [路径压缩的加权 Quick Union](#路径压缩的加权-quick-union) -* [比较](#比较) - - - -# 前言 - -用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。 - -

- -| 方法 | 描述 | -| :---: | :---: | -| UF(int N) | 构造一个大小为 N 的并查集 | -| void union(int p, int q) | 连接 p 和 q 节点 | -| int find(int p) | 查找 p 所在的连通分量编号 | -| boolean connected(int p, int q) | 判断 p 和 q 节点是否连通 | - -```java -public abstract class UF { - - protected int[] id; - - public UF(int N) { - id = new int[N]; - for (int i = 0; i < N; i++) { - id[i] = i; - } - } - - public boolean connected(int p, int q) { - return find(p) == find(q); - } - - public abstract int find(int p); - - public abstract void union(int p, int q); -} -``` - -# Quick Find - -可以快速进行 find 操作,也就是可以快速判断两个节点是否连通。 - -需要保证同一连通分量的所有节点的 id 值相等。 - -但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 - -

- -```java -public class QuickFindUF extends UF { - - public QuickFindUF(int N) { - super(N); - } - - - @Override - public int find(int p) { - return id[p]; - } - - - @Override - public void union(int p, int q) { - int pID = find(p); - int qID = find(q); - - if (pID == qID) { - return; - } - - for (int i = 0; i < id.length; i++) { - if (id[i] == pID) { - id[i] = qID; - } - } - } -} -``` - -# Quick Union - -可以快速进行 union 操作,只需要修改一个节点的 id 值即可。 - -但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。 - -

- -```java -public class QuickUnionUF extends UF { - - public QuickUnionUF(int N) { - super(N); - } - - - @Override - public int find(int p) { - while (p != id[p]) { - p = id[p]; - } - return p; - } - - - @Override - public void union(int p, int q) { - int pRoot = find(p); - int qRoot = find(q); - - if (pRoot != qRoot) { - id[pRoot] = qRoot; - } - } -} -``` - -这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为节点的数目。 - -

- -# 加权 Quick Union - -为了解决 quick-union 的树通常会很高的问题,加权 quick-union 在 union 操作时会让较小的树连接较大的树上面。 - -理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 - -

- -```java -public class WeightedQuickUnionUF extends UF { - - // 保存节点的数量信息 - private int[] sz; - - - public WeightedQuickUnionUF(int N) { - super(N); - this.sz = new int[N]; - for (int i = 0; i < N; i++) { - this.sz[i] = 1; - } - } - - - @Override - public int find(int p) { - while (p != id[p]) { - p = id[p]; - } - return p; - } - - - @Override - public void union(int p, int q) { - - int i = find(p); - int j = find(q); - - if (i == j) return; - - if (sz[i] < sz[j]) { - id[i] = j; - sz[j] += sz[i]; - } else { - id[j] = i; - sz[i] += sz[j]; - } - } -} -``` - -# 路径压缩的加权 Quick Union - -在检查节点的同时将它们直接链接到根节点,只需要在 find 中添加一个循环即可。 - -# 比较 - -| 算法 | union | find | -| :---: | :---: | :---: | -| Quick Find | N | 1 | -| Quick Union | 树高 | 树高 | -| 加权 Quick Union | logN | logN | -| 路径压缩的加权 Quick Union | 非常接近 1 | 非常接近 1 | - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" "b/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" deleted file mode 100644 index edfe69feae..0000000000 --- "a/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" +++ /dev/null @@ -1,27 +0,0 @@ - -* [目录](#目录) -* [参考资料](#参考资料) - - - -# 目录 - -实现代码:[Algorithm](https://github.com/CyC2018/Algorithm),绘图文件:[ProcessOn](https://www.processon.com/view/link/5a3e4c1ee4b0ce9ffea8c727)。 - - -- [算法分析](算法%20-%20算法分析.md) -- [排序](算法%20-%20排序.md) -- [并查集](算法%20-%20并查集.md) -- [栈和队列](算法%20-%20栈和队列.md) -- [符号表](算法%20-%20符号表.md) -- [其它](算法%20-%20其它.md) - -# 参考资料 - -- Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\2251.md" "b/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\2251.md" deleted file mode 100644 index 1d63773d3d..0000000000 --- "a/docs/notes/\347\256\227\346\263\225 - \347\233\256\345\275\2251.md" +++ /dev/null @@ -1,27 +0,0 @@ - -* [目录](#目录) -* [参考资料](#参考资料) - - - -# 目录 - -实现代码:[Algorithm](https://github.com/CyC2018/Algorithm),绘图文件:[ProcessOn](https://www.processon.com/view/link/5a3e4c1ee4b0ce9ffea8c727)。 - - -- [算法分析](notes/算法%20-%20算法分析.md) -- [排序](notes/算法%20-%20排序.md) -- [并查集](notes/算法%20-%20并查集.md) -- [栈和队列](notes/算法%20-%20栈和队列.md) -- [符号表](notes/算法%20-%20符号表.md) -- [其它](notes/算法%20-%20其它.md) - -# 参考资料 - -- Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" "b/docs/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" deleted file mode 100644 index 67cdb49cc3..0000000000 --- "a/docs/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" +++ /dev/null @@ -1,243 +0,0 @@ - -* [数学模型](#数学模型) - * [1. 近似](#1-近似) - * [2. 增长数量级](#2-增长数量级) - * [3. 内循环](#3-内循环) - * [4. 成本模型](#4-成本模型) -* [注意事项](#注意事项) - * [1. 大常数](#1-大常数) - * [2. 缓存](#2-缓存) - * [3. 对最坏情况下的性能的保证](#3-对最坏情况下的性能的保证) - * [4. 随机化算法](#4-随机化算法) - * [5. 均摊分析](#5-均摊分析) -* [ThreeSum](#threesum) - * [1. ThreeSumSlow](#1-threesumslow) - * [2. ThreeSumBinarySearch](#2-threesumbinarysearch) - * [3. ThreeSumTwoPointer](#3-threesumtwopointer) -* [倍率实验](#倍率实验) - - - -# 数学模型 - -## 1. 近似 - -N3/6-N2/2+N/3 \~ N3/6。使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数。 - -## 2. 增长数量级 - -N3/6-N2/2+N/3 的增长数量级为 O(N3)。增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 O(N3) 与它是否用 Java 实现,是否运行于特定计算机上无关。 - -## 3. 内循环 - -执行最频繁的指令决定了程序执行的总时间,把这些指令称为程序的内循环。 - -## 4. 成本模型 - -使用成本模型来评估算法,例如数组的访问次数就是一种成本模型。 - -# 注意事项 - -## 1. 大常数 - -在求近似时,如果低级项的常数系数很大,那么近似的结果就是错误的。 - -## 2. 缓存 - -计算机系统会使用缓存技术来组织内存,访问数组相邻的元素会比访问不相邻的元素快很多。 - -## 3. 对最坏情况下的性能的保证 - -在核反应堆、心脏起搏器或者刹车控制器中的软件,最坏情况下的性能是十分重要的。 - -## 4. 随机化算法 - -通过打乱输入,去除算法对输入的依赖。 - -## 5. 均摊分析 - -将所有操作的总成本除于操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的次数为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素的操作次数,其余的都是调整数组大小时进行复制需要的访问数组次数),均摊后访问数组的平均次数为常数。 - -# ThreeSum - -ThreeSum 用于统计一个数组中和为 0 的三元组数量。 - -```java -public interface ThreeSum { - int count(int[] nums); -} -``` - -## 1. ThreeSumSlow - -该算法的内循环为 `if (nums[i] + nums[j] + nums[k] == 0)` 语句,总共执行的次数为 N(N-1)(N-2) = N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 O(N3)。 - -```java -public class ThreeSumSlow implements ThreeSum { - @Override - public int count(int[] nums) { - int N = nums.length; - int cnt = 0; - for (int i = 0; i < N; i++) { - for (int j = i + 1; j < N; j++) { - for (int k = j + 1; k < N; k++) { - if (nums[i] + nums[j] + nums[k] == 0) { - cnt++; - } - } - } - } - return cnt; - } -} -``` - -## 2. ThreeSumBinarySearch - -通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。 - -应该注意的是,只有数组不含有相同元素才能使用这种解法,否则二分查找的结果会出错。 - -该方法可以将 ThreeSum 算法增长数量级降低为 O(N2logN)。 - -```java -public class ThreeSumBinarySearch implements ThreeSum { - - @Override - public int count(int[] nums) { - Arrays.sort(nums); - int N = nums.length; - int cnt = 0; - for (int i = 0; i < N; i++) { - for (int j = i + 1; j < N; j++) { - int target = -nums[i] - nums[j]; - int index = BinarySearch.search(nums, target); - // 应该注意这里的下标必须大于 j,否则会重复统计。 - if (index > j) { - cnt++; - } - } - } - return cnt; - } -} -``` - -```java -public class BinarySearch { - - public static int search(int[] nums, int target) { - int l = 0, h = nums.length - 1; - while (l <= h) { - int m = l + (h - l) / 2; - if (target == nums[m]) { - return m; - } else if (target > nums[m]) { - l = m + 1; - } else { - h = m - 1; - } - } - return -1; - } -} -``` - -## 3. ThreeSumTwoPointer - -更有效的方法是先将数组排序,然后使用双指针进行查找,时间复杂度为 O(N2)。 - -```java -public class ThreeSumTwoPointer implements ThreeSum { - - @Override - public int count(int[] nums) { - int N = nums.length; - int cnt = 0; - Arrays.sort(nums); - for (int i = 0; i < N - 2; i++) { - int l = i + 1, h = N - 1, target = -nums[i]; - if (i > 0 && nums[i] == nums[i - 1]) continue; - while (l < h) { - int sum = nums[l] + nums[h]; - if (sum == target) { - cnt++; - while (l < h && nums[l] == nums[l + 1]) l++; - while (l < h && nums[h] == nums[h - 1]) h--; - l++; - h--; - } else if (sum < target) { - l++; - } else { - h--; - } - } - } - return cnt; - } -} -``` - -# 倍率实验 - -如果 T(N) \~ aNblogN,那么 T(2N)/T(N) \~ 2b。 - -例如对于暴力的 ThreeSum 算法,近似时间为 \~N3/6。进行如下实验:多次运行该算法,每次取的 N 值为前一次的两倍,统计每次执行的时间,并统计本次运行时间与前一次运行时间的比值,得到如下结果: - -| N | Time(ms) | Ratio | -| :---: | :---: | :---: | -| 500 | 48 | / | -| 1000 | 320 | 6.7 | -| 2000 | 555 | 1.7 | -| 4000 | 4105 | 7.4 | -| 8000 | 33575 | 8.2 | -| 16000 | 268909 | 8.0 | - -可以看到,T(2N)/T(N) \~ 23,因此可以确定 T(N) \~ aN3logN。 - -```java -public class RatioTest { - - public static void main(String[] args) { - int N = 500; - int loopTimes = 7; - double preTime = -1; - while (loopTimes-- > 0) { - int[] nums = new int[N]; - StopWatch.start(); - ThreeSum threeSum = new ThreeSumSlow(); - int cnt = threeSum.count(nums); - System.out.println(cnt); - double elapsedTime = StopWatch.elapsedTime(); - double ratio = preTime == -1 ? 0 : elapsedTime / preTime; - System.out.println(N + " " + elapsedTime + " " + ratio); - preTime = elapsedTime; - N *= 2; - } - } -} -``` - -```java -public class StopWatch { - - private static long start; - - - public static void start() { - start = System.currentTimeMillis(); - } - - - public static double elapsedTime() { - long now = System.currentTimeMillis(); - return (now - start) / 1000.0; - } -} -``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" deleted file mode 100644 index 944694ec25..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" +++ /dev/null @@ -1,142 +0,0 @@ - -* [虚拟内存](#虚拟内存) -* [分页系统地址映射](#分页系统地址映射) -* [页面置换算法](#页面置换算法) - * [1. 最佳](#1-最佳) - * [2. 最近最久未使用](#2-最近最久未使用) - * [3. 最近未使用](#3-最近未使用) - * [4. 先进先出](#4-先进先出) - * [5. 第二次机会算法](#5-第二次机会算法) - * [6. 时钟](#6-时钟) -* [分段](#分段) -* [段页式](#段页式) -* [分页与分段的比较](#分页与分段的比较) - - - -# 虚拟内存 - -虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 - -为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。 - -从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。 - -

- -# 分页系统地址映射 - -内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。 - -一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。 - -下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。 - -

- -# 页面置换算法 - -在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。 - -页面置换算法和缓存淘汰策略类似,可以将内存看成磁盘的缓存。在缓存系统中,缓存的大小有限,当有新的缓存到达时,需要淘汰一部分已经存在的缓存,这样才有空间存放新的缓存数据。 - -页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。 - -## 1. 最佳 - -> OPT, Optimal replacement algorithm - -所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。 - -是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。 - -举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列: - -

- -开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。 - -## 2. 最近最久未使用 - -> LRU, Least Recently Used - -虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。 - -为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。 - -因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。 - -

- -

- -## 3. 最近未使用 - -> NRU, Not Recently Used - -每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类: - -- R=0,M=0 -- R=0,M=1 -- R=1,M=0 -- R=1,M=1 - -当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。 - -NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。 - -## 4. 先进先出 - -> FIFO, First In First Out - -选择换出的页面是最先进入的页面。 - -该算法会将那些经常被访问的页面也被换出,从而使缺页率升高。 - -## 5. 第二次机会算法 - -FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改: - -当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 - -

- -## 6. 时钟 - -> Clock - -第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 - -

- -# 分段 - -虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。 - -下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。 - -

- -分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 - -

- -# 段页式 - -程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。 - -# 分页与分段的比较 - -- 对程序员的透明性:分页透明,但是分段需要程序员显示划分每个段。 - -- 地址空间的维度:分页是一维地址空间,分段是二维的。 - -- 大小是否可以改变:页的大小不可变,段的大小可以动态改变。 - -- 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" deleted file mode 100644 index e7b864179c..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" +++ /dev/null @@ -1,128 +0,0 @@ - -* [基本特征](#基本特征) - * [1. 并发](#1-并发) - * [2. 共享](#2-共享) - * [3. 虚拟](#3-虚拟) - * [4. 异步](#4-异步) -* [基本功能](#基本功能) - * [1. 进程管理](#1-进程管理) - * [2. 内存管理](#2-内存管理) - * [3. 文件管理](#3-文件管理) - * [4. 设备管理](#4-设备管理) -* [系统调用](#系统调用) -* [大内核和微内核](#大内核和微内核) - * [1. 大内核](#1-大内核) - * [2. 微内核](#2-微内核) -* [中断分类](#中断分类) - * [1. 外中断](#1-外中断) - * [2. 异常](#2-异常) - * [3. 陷入](#3-陷入) - - - -# 基本特征 - -## 1. 并发 - -并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。 - -并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。 - -操作系统通过引入进程和线程,使得程序能够并发运行。 - -## 2. 共享 - -共享是指系统中的资源可以被多个并发进程共同使用。 - -有两种共享方式:互斥共享和同时共享。 - -互斥共享的资源称为临界资源,例如打印机等,在同一时间只允许一个进程访问,需要用同步机制来实现对临界资源的访问。 - -## 3. 虚拟 - -虚拟技术把一个物理实体转换为多个逻辑实体。 - -主要有两种虚拟技术:时分复用技术和空分复用技术。 - -多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占有处理器,每次只执行一小个时间片并快速切换。 - -虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。 - -## 4. 异步 - -异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。 - -# 基本功能 - -## 1. 进程管理 - -进程控制、进程同步、进程通信、死锁处理、处理机调度等。 - -## 2. 内存管理 - -内存分配、地址映射、内存保护与共享、虚拟内存等。 - -## 3. 文件管理 - -文件存储空间的管理、目录管理、文件读写管理和保护等。 - -## 4. 设备管理 - -完成用户的 I/O 请求,方便用户使用各种设备,并提高设备的利用率。 - -主要包括缓冲管理、设备分配、设备处理、虛拟设备等。 - -# 系统调用 - -如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。 - -

- -Linux 的系统调用主要有以下这些: - -| Task | Commands | -| :---: | --- | -| 进程控制 | fork(); exit(); wait(); | -| 进程通信 | pipe(); shmget(); mmap(); | -| 文件操作 | open(); read(); write(); | -| 设备操作 | ioctl(); read(); write(); | -| 信息维护 | getpid(); alarm(); sleep(); | -| 安全 | chmod(); umask(); chown(); | - -# 大内核和微内核 - -## 1. 大内核 - -大内核是将操作系统功能作为一个紧密结合的整体放到内核。 - -由于各模块共享信息,因此有很高的性能。 - -## 2. 微内核 - -由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。 - -在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。 - -因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。 - -

- -# 中断分类 - -## 1. 外中断 - -由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。 - -## 2. 异常 - -由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。 - -## 3. 陷入 - -在用户程序中使用系统调用。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" deleted file mode 100644 index b284c886a6..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" +++ /dev/null @@ -1,148 +0,0 @@ - -* [必要条件](#必要条件) -* [处理方法](#处理方法) -* [鸵鸟策略](#鸵鸟策略) -* [死锁检测与死锁恢复](#死锁检测与死锁恢复) - * [1. 每种类型一个资源的死锁检测](#1-每种类型一个资源的死锁检测) - * [2. 每种类型多个资源的死锁检测](#2-每种类型多个资源的死锁检测) - * [3. 死锁恢复](#3-死锁恢复) -* [死锁预防](#死锁预防) - * [1. 破坏互斥条件](#1-破坏互斥条件) - * [2. 破坏占有和等待条件](#2-破坏占有和等待条件) - * [3. 破坏不可抢占条件](#3-破坏不可抢占条件) - * [4. 破坏环路等待](#4-破坏环路等待) -* [死锁避免](#死锁避免) - * [1. 安全状态](#1-安全状态) - * [2. 单个资源的银行家算法](#2-单个资源的银行家算法) - * [3. 多个资源的银行家算法](#3-多个资源的银行家算法) - - - -# 必要条件 - -

- -- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。 -- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。 -- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。 -- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。 - -# 处理方法 - -主要有以下四种方法: - -- 鸵鸟策略 -- 死锁检测与死锁恢复 -- 死锁预防 -- 死锁避免 - -# 鸵鸟策略 - -把头埋在沙子里,假装根本没发生问题。 - -因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。 - -当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。 - -大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。 - -# 死锁检测与死锁恢复 - -不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。 - -## 1. 每种类型一个资源的死锁检测 - -

- -上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。 - -图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。 - -每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。 - -## 2. 每种类型多个资源的死锁检测 - -

- -上图中,有三个进程四个资源,每个数据代表的含义如下: - -- E 向量:资源总量 -- A 向量:资源剩余量 -- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量 -- R 矩阵:每个进程请求的资源数量 - -进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。 - -算法总结如下: - -每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。 - -1. 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。 -2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。 -3. 如果没有这样一个进程,算法终止。 - -## 3. 死锁恢复 - -- 利用抢占恢复 -- 利用回滚恢复 -- 通过杀死进程恢复 - -# 死锁预防 - -在程序运行之前预防发生死锁。 - -## 1. 破坏互斥条件 - -例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。 - -## 2. 破坏占有和等待条件 - -一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 - -## 3. 破坏不可抢占条件 - -## 4. 破坏环路等待 - -给资源统一编号,进程只能按编号顺序来请求资源。 - -# 死锁避免 - -在程序运行时避免发生死锁。 - -## 1. 安全状态 - -

- -图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。 - -定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。 - -安全状态的检测与死锁的检测类似,因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似,可以结合着做参考对比。 - -## 2. 单个资源的银行家算法 - -一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 - -

- -上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。 - -## 3. 多个资源的银行家算法 - -

- -上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。 - -检查一个状态是否安全的算法如下: - -- 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。 -- 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。 -- 重复以上两步,直到所有进程都标记为终止,则状态时安全的。 - -如果一个状态不是安全的,需要拒绝进入这个状态。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\2251.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\2251.md" deleted file mode 100644 index 0f17d34fc1..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\2251.md" +++ /dev/null @@ -1,32 +0,0 @@ - -* [目录](#目录) -* [参考资料](#参考资料) - - - -# 目录 - -- [概述](notes/计算机操作系统%20-%20概述.md) -- [进程管理](notes/计算机操作系统%20-%20进程管理.md) -- [死锁](notes/计算机操作系统%20-%20死锁.md) -- [内存管理](notes/计算机操作系统%20-%20内存管理.md) -- [设备管理](notes/计算机操作系统%20-%20设备管理.md) -- [链接](notes/计算机操作系统%20-%20链接.md) - -# 参考资料 - -- Tanenbaum A S, Bos H. Modern operating systems[M]. Prentice Hall Press, 2014. -- 汤子瀛, 哲凤屏, 汤小丹. 计算机操作系统[M]. 西安电子科技大学出版社, 2001. -- Bryant, R. E., & O’Hallaron, D. R. (2004). 深入理解计算机系统. -- 史蒂文斯. UNIX 环境高级编程 [M]. 人民邮电出版社, 2014. -- [Operating System Notes](https://applied-programming.github.io/Operating-Systems-Notes/) -- [Operating-System Structures](https://www.cs.uic.edu/\~jbell/CourseNotes/OperatingSystems/2_Structures.html) -- [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php) -- [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1) -- [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" deleted file mode 100644 index 997a40d694..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" +++ /dev/null @@ -1,65 +0,0 @@ - -* [磁盘结构](#磁盘结构) -* [磁盘调度算法](#磁盘调度算法) - * [1. 先来先服务](#1-先来先服务) - * [2. 最短寻道时间优先](#2-最短寻道时间优先) - * [3. 电梯算法](#3-电梯算法) - - - -# 磁盘结构 - -- 盘面(Platter):一个磁盘有多个盘面; -- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道; -- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小; -- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写); -- 制动手臂(Actuator arm):用于在磁道之间移动磁头; -- 主轴(Spindle):使整个盘面转动。 - -

- -# 磁盘调度算法 - -读写一个磁盘块的时间的影响因素有: - -- 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上) -- 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上) -- 实际的数据传输时间 - -其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。 - -## 1. 先来先服务 - -> FCFS, First Come First Served - -按照磁盘请求的顺序进行调度。 - -优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。 - -## 2. 最短寻道时间优先 - -> SSTF, Shortest Seek Time First - -优先调度与当前磁头所在磁道距离最近的磁道。 - -虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。 - -

- -## 3. 电梯算法 - -> SCAN - -电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。 - -电梯算法(扫描算法)和电梯的运行过程类似,总是按一个方向来进行磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。 - -因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。 - -

- - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" deleted file mode 100644 index fd222df4bf..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" +++ /dev/null @@ -1,71 +0,0 @@ - -* [编译系统](#编译系统) -* [静态链接](#静态链接) -* [目标文件](#目标文件) -* [动态链接](#动态链接) - - - -# 编译系统 - -以下是一个 hello.c 程序: - -```c -#include - -int main() -{ - printf("hello, world\n"); - return 0; -} -``` - -在 Unix 系统上,由编译器把源文件转换为目标文件。 - -```bash -gcc -o hello hello.c -``` - -这个过程大致如下: - -

- -- 预处理阶段:处理以 # 开头的预处理命令; -- 编译阶段:翻译成汇编文件; -- 汇编阶段:将汇编文件翻译成可重定向目标文件; -- 链接阶段:将可重定向目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。 - -# 静态链接 - -静态链接器以一组可重定向目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务: - -- 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 -- 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。 - -

- -# 目标文件 - -- 可执行目标文件:可以直接在内存中执行; -- 可重定向目标文件:可与其它可重定向目标文件在链接阶段合并,创建一个可执行目标文件; -- 共享目标文件:这是一种特殊的可重定向目标文件,可以在运行时被动态加载进内存并链接; - -# 动态链接 - -静态库有以下两个问题: - -- 当静态库更新时那么整个程序都要重新进行链接; -- 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。 - -共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点: - -- 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中; -- 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。 - -

- - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" deleted file mode 100644 index d175443fe0..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" +++ /dev/null @@ -1,1084 +0,0 @@ - -* [一、概述](#一概述) - * [基本特征](#基本特征) - * [基本功能](#基本功能) - * [系统调用](#系统调用) - * [大内核和微内核](#大内核和微内核) - * [中断分类](#中断分类) -* [二、进程管理](#二进程管理) - * [进程与线程](#进程与线程) - * [进程状态的切换](#进程状态的切换) - * [进程调度算法](#进程调度算法) - * [进程同步](#进程同步) - * [经典同步问题](#经典同步问题) - * [进程通信](#进程通信) -* [三、死锁](#三死锁) - * [必要条件](#必要条件) - * [处理方法](#处理方法) - * [鸵鸟策略](#鸵鸟策略) - * [死锁检测与死锁恢复](#死锁检测与死锁恢复) - * [死锁预防](#死锁预防) - * [死锁避免](#死锁避免) -* [四、内存管理](#四内存管理) - * [虚拟内存](#虚拟内存) - * [分页系统地址映射](#分页系统地址映射) - * [页面置换算法](#页面置换算法) - * [分段](#分段) - * [段页式](#段页式) - * [分页与分段的比较](#分页与分段的比较) -* [五、设备管理](#五设备管理) - * [磁盘结构](#磁盘结构) - * [磁盘调度算法](#磁盘调度算法) -* [六、链接](#六链接) - * [编译系统](#编译系统) - * [静态链接](#静态链接) - * [目标文件](#目标文件) - * [动态链接](#动态链接) -* [参考资料](#参考资料) - - - -# 一、概述 - -## 基本特征 - -### 1. 并发 - -并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。 - -并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。 - -操作系统通过引入进程和线程,使得程序能够并发运行。 - -### 2. 共享 - -共享是指系统中的资源可以被多个并发进程共同使用。 - -有两种共享方式:互斥共享和同时共享。 - -互斥共享的资源称为临界资源,例如打印机等,在同一时间只允许一个进程访问,需要用同步机制来实现对临界资源的访问。 - -### 3. 虚拟 - -虚拟技术把一个物理实体转换为多个逻辑实体。 - -主要有两种虚拟技术:时分复用技术和空分复用技术。 - -多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占有处理器,每次只执行一小个时间片并快速切换。 - -虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。 - -### 4. 异步 - -异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。 - -## 基本功能 - -### 1. 进程管理 - -进程控制、进程同步、进程通信、死锁处理、处理机调度等。 - -### 2. 内存管理 - -内存分配、地址映射、内存保护与共享、虚拟内存等。 - -### 3. 文件管理 - -文件存储空间的管理、目录管理、文件读写管理和保护等。 - -### 4. 设备管理 - -完成用户的 I/O 请求,方便用户使用各种设备,并提高设备的利用率。 - -主要包括缓冲管理、设备分配、设备处理、虛拟设备等。 - -## 系统调用 - -如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。 - -

- -Linux 的系统调用主要有以下这些: - -| Task | Commands | -| :---: | --- | -| 进程控制 | fork(); exit(); wait(); | -| 进程通信 | pipe(); shmget(); mmap(); | -| 文件操作 | open(); read(); write(); | -| 设备操作 | ioctl(); read(); write(); | -| 信息维护 | getpid(); alarm(); sleep(); | -| 安全 | chmod(); umask(); chown(); | - -## 大内核和微内核 - -### 1. 大内核 - -大内核是将操作系统功能作为一个紧密结合的整体放到内核。 - -由于各模块共享信息,因此有很高的性能。 - -### 2. 微内核 - -由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。 - -在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。 - -因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。 - -

- -## 中断分类 - -### 1. 外中断 - -由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。 - -### 2. 异常 - -由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。 - -### 3. 陷入 - -在用户程序中使用系统调用。 - -# 二、进程管理 - -## 进程与线程 - -### 1. 进程 - -进程是资源分配的基本单位。 - -进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB 的操作。 - -下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。 - -

- -### 2. 线程 - -线程是独立调度的基本单位。 - -一个进程中可以有多个线程,它们共享进程资源。 - -QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。 - -

- -### 3. 区别 - -Ⅰ 拥有资源 - -进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。 - -Ⅱ 调度 - -线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。 - -Ⅲ 系统开销 - -由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。 - -Ⅳ 通信方面 - -线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。 - -## 进程状态的切换 - -

- -- 就绪状态(ready):等待被调度 -- 运行状态(running) -- 阻塞状态(waiting):等待资源 - -应该注意以下内容: - -- 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。 -- 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。 - -## 进程调度算法 - -不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。 - -### 1. 批处理系统 - -批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。 - -**1.1 先来先服务 first-come first-serverd(FCFS)** - -按照请求的顺序进行调度。 - -有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 - -**1.2 短作业优先 shortest job first(SJF)** - -按估计运行时间最短的顺序进行调度。 - -长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。 - -**1.3 最短剩余时间优先 shortest remaining time next(SRTN)** - -按估计剩余时间最短的顺序进行调度。 - -### 2. 交互式系统 - -交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。 - -**2.1 时间片轮转** - -将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。 - -时间片轮转算法的效率和时间片的大小有很大关系: - -- 因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 -- 而如果时间片过长,那么实时性就不能得到保证。 - -

- -**2.2 优先级调度** - -为每个进程分配一个优先级,按优先级进行调度。 - -为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。 - -**2.3 多级反馈队列** - -一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。 - -多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。 - -每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。 - -可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 - -

- -### 3. 实时系统 - -实时系统要求一个请求在一个确定时间内得到响应。 - -分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。 - -## 进程同步 - -### 1. 临界区 - -对临界资源进行访问的那段代码称为临界区。 - -为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。 - -```html -// entry section -// critical section; -// exit section -``` - -### 2. 同步与互斥 - -- 同步:多个进程按一定顺序执行; -- 互斥:多个进程在同一时刻只有一个进程能进入临界区。 - -### 3. 信号量 - -信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。 - -- **down** : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0; -- **up** :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。 - -down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。 - -如果信号量的取值只能为 0 或者 1,那么就成为了 **互斥量(Mutex)** ,0 表示临界区已经加锁,1 表示临界区解锁。 - -```c -typedef int semaphore; -semaphore mutex = 1; -void P1() { - down(&mutex); - // 临界区 - up(&mutex); -} - -void P2() { - down(&mutex); - // 临界区 - up(&mutex); -} -``` - - **使用信号量实现生产者-消费者问题**
- -问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。 - -因为缓冲区属于临界资源,因此需要使用一个互斥量 mutex 来控制对缓冲区的互斥访问。 - -为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。 - -注意,不能先对缓冲区进行加锁,再测试信号量。也就是说,不能先执行 down(mutex) 再执行 down(empty)。如果这么做了,那么可能会出现这种情况:生产者对缓冲区加锁后,执行 down(empty) 操作,发现 empty = 0,此时生产者睡眠。消费者不能进入临界区,因为生产者对缓冲区加锁了,消费者就无法执行 up(empty) 操作,empty 永远都为 0,导致生产者永远等待下,不会释放锁,消费者因此也会永远等待下去。 - -```c -#define N 100 -typedef int semaphore; -semaphore mutex = 1; -semaphore empty = N; -semaphore full = 0; - -void producer() { - while(TRUE) { - int item = produce_item(); - down(&empty); - down(&mutex); - insert_item(item); - up(&mutex); - up(&full); - } -} - -void consumer() { - while(TRUE) { - down(&full); - down(&mutex); - int item = remove_item(); - consume_item(item); - up(&mutex); - up(&empty); - } -} -``` - -### 4. 管程 - -使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制,而管程把控制的代码独立出来,不仅不容易出错,也使得客户端代码调用更容易。 - -c 语言不支持管程,下面的示例代码使用了类 Pascal 语言来描述管程。示例代码的管程提供了 insert() 和 remove() 方法,客户端代码通过调用这两个方法来解决生产者-消费者问题。 - -```pascal -monitor ProducerConsumer - integer i; - condition c; - - procedure insert(); - begin - // ... - end; - - procedure remove(); - begin - // ... - end; -end monitor; -``` - -管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否者其它进程永远不能使用管程。 - -管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 - - **使用管程实现生产者-消费者问题**
- -```pascal -// 管程 -monitor ProducerConsumer - condition full, empty; - integer count := 0; - condition c; - - procedure insert(item: integer); - begin - if count = N then wait(full); - insert_item(item); - count := count + 1; - if count = 1 then signal(empty); - end; - - function remove: integer; - begin - if count = 0 then wait(empty); - remove = remove_item; - count := count - 1; - if count = N -1 then signal(full); - end; -end monitor; - -// 生产者客户端 -procedure producer -begin - while true do - begin - item = produce_item; - ProducerConsumer.insert(item); - end -end; - -// 消费者客户端 -procedure consumer -begin - while true do - begin - item = ProducerConsumer.remove; - consume_item(item); - end -end; -``` - -## 经典同步问题 - -生产者和消费者问题前面已经讨论过了。 - -### 1. 读者-写者问题 - -允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。 - -一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量 data_mutex 用于对读写的数据加锁。 - -```c -typedef int semaphore; -semaphore count_mutex = 1; -semaphore data_mutex = 1; -int count = 0; - -void reader() { - while(TRUE) { - down(&count_mutex); - count++; - if(count == 1) down(&data_mutex); // 第一个读者需要对数据进行加锁,防止写进程访问 - up(&count_mutex); - read(); - down(&count_mutex); - count--; - if(count == 0) up(&data_mutex); - up(&count_mutex); - } -} - -void writer() { - while(TRUE) { - down(&data_mutex); - write(); - up(&data_mutex); - } -} -``` - -以下内容由 [@Bandi Yugandhar](https://github.com/yugandharbandi) 提供。 - -The first case may result Writer to starve. This case favous Writers i.e no writer, once added to the queue, shall be kept waiting longer than absolutely necessary(only when there are readers that entered the queue before the writer). - -```source-c -int readcount, writecount; //(initial value = 0) -semaphore rmutex, wmutex, readLock, resource; //(initial value = 1) - -//READER -void reader() { - - down(&readLock); // reader is trying to enter - down(&rmutex); // lock to increase readcount - readcount++; - if (readcount == 1) - down(&resource); //if you are the first reader then lock the resource - up(&rmutex); //release for other readers - up(&readLock); //Done with trying to access the resource - - -//reading is performed - - - down(&rmutex); //reserve exit section - avoids race condition with readers - readcount--; //indicate you're leaving - if (readcount == 0) //checks if you are last reader leaving - up(&resource); //if last, you must release the locked resource - up(&rmutex); //release exit section for other readers -} - -//WRITER -void writer() { - - down(&wmutex); //reserve entry section for writers - avoids race conditions - writecount++; //report yourself as a writer entering - if (writecount == 1) //checks if you're first writer - down(&readLock); //if you're first, then you must lock the readers out. Prevent them from trying to enter CS - up(&wmutex); //release entry section - - - down(&resource); //reserve the resource for yourself - prevents other writers from simultaneously editing the shared resource - //writing is performed - up(&resource); //release file - - - down(&wmutex); //reserve exit section - writecount--; //indicate you're leaving - if (writecount == 0) //checks if you're the last writer - up(&readLock); //if you're last writer, you must unlock the readers. Allows them to try enter CS for reading - up(&wmutex); //release exit section -} -``` - -We can observe that every reader is forced to acquire ReadLock. On the otherhand, writers doesn’t need to lock individually. Once the first writer locks the ReadLock, it will be released only when there is no writer left in the queue. - -From the both cases we observed that either reader or writer has to starve. Below solutionadds the constraint that no thread shall be allowed to starve; that is, the operation of obtaining a lock on the shared data will always terminate in a bounded amount of time. - -```source-c -int readCount; // init to 0; number of readers currently accessing resource - -// all semaphores initialised to 1 -Semaphore resourceAccess; // controls access (read/write) to the resource -Semaphore readCountAccess; // for syncing changes to shared variable readCount -Semaphore serviceQueue; // FAIRNESS: preserves ordering of requests (signaling must be FIFO) - -void writer() -{ - down(&serviceQueue); // wait in line to be servicexs - // - down(&resourceAccess); // request exclusive access to resource - // - up(&serviceQueue); // let next in line be serviced - - // - writeResource(); // writing is performed - // - - // - up(&resourceAccess); // release resource access for next reader/writer - // -} - -void reader() -{ - down(&serviceQueue); // wait in line to be serviced - down(&readCountAccess); // request exclusive access to readCount - // - if (readCount == 0) // if there are no readers already reading: - down(&resourceAccess); // request resource access for readers (writers blocked) - readCount++; // update count of active readers - // - up(&serviceQueue); // let next in line be serviced - up(&readCountAccess); // release access to readCount - - // - readResource(); // reading is performed - // - - down(&readCountAccess); // request exclusive access to readCount - // - readCount--; // update count of active readers - if (readCount == 0) // if there are no readers left: - up(&resourceAccess); // release resource access for all - // - up(&readCountAccess); // release access to readCount -} - -``` - - -### 2. 哲学家进餐问题 - -

- -五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 - -下面是一种错误的解法,考虑到如果所有哲学家同时拿起左手边的筷子,那么就无法拿起右手边的筷子,造成死锁。 - -```c -#define N 5 - -void philosopher(int i) { - while(TRUE) { - think(); - take(i); // 拿起左边的筷子 - take((i+1)%N); // 拿起右边的筷子 - eat(); - put(i); - put((i+1)%N); - } -} -``` - -为了防止死锁的发生,可以设置两个条件: - -- 必须同时拿起左右两根筷子; -- 只有在两个邻居都没有进餐的情况下才允许进餐。 - -```c -#define N 5 -#define LEFT (i + N - 1) % N // 左邻居 -#define RIGHT (i + 1) % N // 右邻居 -#define THINKING 0 -#define HUNGRY 1 -#define EATING 2 -typedef int semaphore; -int state[N]; // 跟踪每个哲学家的状态 -semaphore mutex = 1; // 临界区的互斥 -semaphore s[N]; // 每个哲学家一个信号量 - -void philosopher(int i) { - while(TRUE) { - think(); - take_two(i); - eat(); - put_two(i); - } -} - -void take_two(int i) { - down(&mutex); - state[i] = HUNGRY; - test(i); - up(&mutex); - down(&s[i]); -} - -void put_two(i) { - down(&mutex); - state[i] = THINKING; - test(LEFT); - test(RIGHT); - up(&mutex); -} - -void test(i) { // 尝试拿起两把筷子 - if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) { - state[i] = EATING; - up(&s[i]); - } -} -``` - -## 进程通信 - -进程同步与进程通信很容易混淆,它们的区别在于: - -- 进程同步:控制多个进程按一定顺序执行; -- 进程通信:进程间传输信息。 - -进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。 - -### 1. 管道 - -管道是通过调用 pipe 函数创建的,fd[0] 用于读,fd[1] 用于写。 - -```c -#include -int pipe(int fd[2]); -``` - -它具有以下限制: - -- 只支持半双工通信(单向交替传输); -- 只能在父子进程中使用。 - -

- -### 2. FIFO - -也称为命名管道,去除了管道只能在父子进程中使用的限制。 - -```c -#include -int mkfifo(const char *path, mode_t mode); -int mkfifoat(int fd, const char *path, mode_t mode); -``` - -FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。 - -

- -### 3. 消息队列 - -相比于 FIFO,消息队列具有以下优点: - -- 消息队列可以独立于读写进程存在,从而避免了 FIFO 中同步管道的打开和关闭时可能产生的困难; -- 避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法; -- 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。 - -### 4. 信号量 - -它是一个计数器,用于为多个进程提供对共享数据对象的访问。 - -### 5. 共享存储 - -允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。 - -需要使用信号量用来同步对共享存储的访问。 - -多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用使用内存的匿名段。 - -### 6. 套接字 - -与其它通信机制不同的是,它可用于不同机器间的进程通信。 - -# 三、死锁 - -## 必要条件 - -

- -- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。 -- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。 -- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。 -- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。 - -## 处理方法 - -主要有以下四种方法: - -- 鸵鸟策略 -- 死锁检测与死锁恢复 -- 死锁预防 -- 死锁避免 - -## 鸵鸟策略 - -把头埋在沙子里,假装根本没发生问题。 - -因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。 - -当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。 - -大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。 - -## 死锁检测与死锁恢复 - -不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。 - -### 1. 每种类型一个资源的死锁检测 - -

- -上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。 - -图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。 - -每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。 - -### 2. 每种类型多个资源的死锁检测 - -

- -上图中,有三个进程四个资源,每个数据代表的含义如下: - -- E 向量:资源总量 -- A 向量:资源剩余量 -- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量 -- R 矩阵:每个进程请求的资源数量 - -进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。 - -算法总结如下: - -每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。 - -1. 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。 -2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。 -3. 如果没有这样一个进程,算法终止。 - -### 3. 死锁恢复 - -- 利用抢占恢复 -- 利用回滚恢复 -- 通过杀死进程恢复 - -## 死锁预防 - -在程序运行之前预防发生死锁。 - -### 1. 破坏互斥条件 - -例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。 - -### 2. 破坏占有和等待条件 - -一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 - -### 3. 破坏不可抢占条件 - -### 4. 破坏环路等待 - -给资源统一编号,进程只能按编号顺序来请求资源。 - -## 死锁避免 - -在程序运行时避免发生死锁。 - -### 1. 安全状态 - -

- -图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。 - -定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。 - -安全状态的检测与死锁的检测类似,因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似,可以结合着做参考对比。 - -### 2. 单个资源的银行家算法 - -一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 - -

- -上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。 - -### 3. 多个资源的银行家算法 - -

- -上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。 - -检查一个状态是否安全的算法如下: - -- 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。 -- 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。 -- 重复以上两步,直到所有进程都标记为终止,则状态时安全的。 - -如果一个状态不是安全的,需要拒绝进入这个状态。 - -# 四、内存管理 - -## 虚拟内存 - -虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 - -为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。 - -从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。 - -

- -## 分页系统地址映射 - -内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。 - -一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。 - -下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。 - -

- -## 页面置换算法 - -在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。 - -页面置换算法和缓存淘汰策略类似,可以将内存看成磁盘的缓存。在缓存系统中,缓存的大小有限,当有新的缓存到达时,需要淘汰一部分已经存在的缓存,这样才有空间存放新的缓存数据。 - -页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。 - -### 1. 最佳 - -> OPT, Optimal replacement algorithm - -所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。 - -是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。 - -举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列: - -

- -开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。 - -### 2. 最近最久未使用 - -> LRU, Least Recently Used - -虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。 - -为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。 - -因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。 - -

- -

- -### 3. 最近未使用 - -> NRU, Not Recently Used - -每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类: - -- R=0,M=0 -- R=0,M=1 -- R=1,M=0 -- R=1,M=1 - -当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。 - -NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。 - -### 4. 先进先出 - -> FIFO, First In First Out - -选择换出的页面是最先进入的页面。 - -该算法会将那些经常被访问的页面也被换出,从而使缺页率升高。 - -### 5. 第二次机会算法 - -FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改: - -当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 - -

- -### 6. 时钟 - -> Clock - -第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 - -

- -## 分段 - -虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。 - -下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。 - -

- -分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 - -

- -## 段页式 - -程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。 - -## 分页与分段的比较 - -- 对程序员的透明性:分页透明,但是分段需要程序员显示划分每个段。 - -- 地址空间的维度:分页是一维地址空间,分段是二维的。 - -- 大小是否可以改变:页的大小不可变,段的大小可以动态改变。 - -- 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。 - -# 五、设备管理 - -## 磁盘结构 - -- 盘面(Platter):一个磁盘有多个盘面; -- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道; -- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小; -- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写); -- 制动手臂(Actuator arm):用于在磁道之间移动磁头; -- 主轴(Spindle):使整个盘面转动。 - -

- -## 磁盘调度算法 - -读写一个磁盘块的时间的影响因素有: - -- 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上) -- 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上) -- 实际的数据传输时间 - -其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。 - -### 1. 先来先服务 - -> FCFS, First Come First Served - -按照磁盘请求的顺序进行调度。 - -优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。 - -### 2. 最短寻道时间优先 - -> SSTF, Shortest Seek Time First - -优先调度与当前磁头所在磁道距离最近的磁道。 - -虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。 - -

- -### 3. 电梯算法 - -> SCAN - -电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。 - -电梯算法(扫描算法)和电梯的运行过程类似,总是按一个方向来进行磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。 - -因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。 - -

- -# 六、链接 - -## 编译系统 - -以下是一个 hello.c 程序: - -```c -#include - -int main() -{ - printf("hello, world\n"); - return 0; -} -``` - -在 Unix 系统上,由编译器把源文件转换为目标文件。 - -```bash -gcc -o hello hello.c -``` - -这个过程大致如下: - -

- -- 预处理阶段:处理以 # 开头的预处理命令; -- 编译阶段:翻译成汇编文件; -- 汇编阶段:将汇编文件翻译成可重定向目标文件; -- 链接阶段:将可重定向目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。 - -## 静态链接 - -静态链接器以一组可重定向目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务: - -- 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 -- 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。 - -

- -## 目标文件 - -- 可执行目标文件:可以直接在内存中执行; -- 可重定向目标文件:可与其它可重定向目标文件在链接阶段合并,创建一个可执行目标文件; -- 共享目标文件:这是一种特殊的可重定向目标文件,可以在运行时被动态加载进内存并链接; - -## 动态链接 - -静态库有以下两个问题: - -- 当静态库更新时那么整个程序都要重新进行链接; -- 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。 - -共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点: - -- 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中; -- 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。 - -

- -# 参考资料 - -- Tanenbaum A S, Bos H. Modern operating systems[M]. Prentice Hall Press, 2014. -- 汤子瀛, 哲凤屏, 汤小丹. 计算机操作系统[M]. 西安电子科技大学出版社, 2001. -- Bryant, R. E., & O’Hallaron, D. R. (2004). 深入理解计算机系统. -- 史蒂文斯. UNIX 环境高级编程 [M]. 人民邮电出版社, 2014. -- [Operating System Notes](https://applied-programming.github.io/Operating-Systems-Notes/) -- [Operating-System Structures](https://www.cs.uic.edu/\~jbell/CourseNotes/OperatingSystems/2_Structures.html) -- [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php) -- [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1) -- [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" deleted file mode 100644 index 78c55af9da..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" +++ /dev/null @@ -1,171 +0,0 @@ - -* [UDP 和 TCP 的特点](#udp-和-tcp-的特点) -* [UDP 首部格式](#udp-首部格式) -* [TCP 首部格式](#tcp-首部格式) -* [TCP 的三次握手](#tcp-的三次握手) -* [TCP 的四次挥手](#tcp-的四次挥手) -* [TCP 可靠传输](#tcp-可靠传输) -* [TCP 滑动窗口](#tcp-滑动窗口) -* [TCP 流量控制](#tcp-流量控制) -* [TCP 拥塞控制](#tcp-拥塞控制) - * [1. 慢开始与拥塞避免](#1-慢开始与拥塞避免) - * [2. 快重传与快恢复](#2-快重传与快恢复) - - - -网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。 - -# UDP 和 TCP 的特点 - -- 用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。 - -- 传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。 - -# UDP 首部格式 - -

- -首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 - -# TCP 首部格式 - -

- -- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 - -- **确认号** :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 - -- **数据偏移** :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 - -- **确认 ACK** :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 - -- **同步 SYN** :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 - -- **终止 FIN** :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。 - -- **窗口** :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 - -# TCP 的三次握手 - -

- -假设 A 为客户端,B 为服务器端。 - -- 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。 - -- A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x。 - -- B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。 - -- A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。 - -- B 收到 A 的确认后,连接建立。 - -**三次握手的原因** - -第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 - -客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。 - -# TCP 的四次挥手 - -

- -以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 - -- A 发送连接释放报文,FIN=1。 - -- B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。 - -- 当 B 不再需要连接时,发送连接释放报文,FIN=1。 - -- A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。 - -- B 收到 A 的确认后释放连接。 - -**四次挥手的原因** - -客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 - -**TIME_WAIT** - -客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: - -- 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。 - -- 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。 - -# TCP 可靠传输 - -TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。 - -一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算如下: - -

- -其中,0 ≤ a < 1,RTTs 随着 a 的增加更容易受到 RTT 的影响。 - -超时时间 RTO 应该略大于 RTTs,TCP 使用的超时时间计算如下: - -

- -其中 RTTd 为偏差的加权平均值。 - -# TCP 滑动窗口 - -窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 - -发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 - -接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 - -

- -# TCP 流量控制 - -流量控制是为了控制发送方发送速率,保证接收方来得及接收。 - -接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 - -# TCP 拥塞控制 - -如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 - -

- -TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。 - -发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 - -为了便于讨论,做如下假设: - -- 接收方有足够大的接收缓存,因此不会发生流量控制; -- 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 - -

- -## 1. 慢开始与拥塞避免 - -发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ... - -注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。 - -如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。 - -## 2. 快重传与快恢复 - -在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。 - -在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。 - -在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 - -慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。 - -

- - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" deleted file mode 100644 index 8a410b3cd3..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" +++ /dev/null @@ -1,134 +0,0 @@ - -* [网络的网络](#网络的网络) -* [ISP](#isp) -* [主机之间的通信方式](#主机之间的通信方式) -* [电路交换与分组交换](#电路交换与分组交换) - * [1. 电路交换](#1-电路交换) - * [2. 分组交换](#2-分组交换) -* [时延](#时延) - * [1. 排队时延](#1-排队时延) - * [2. 处理时延](#2-处理时延) - * [3. 传输时延](#3-传输时延) - * [4. 传播时延](#4-传播时延) -* [计算机网络体系结构](#计算机网络体系结构) - * [1. 五层协议](#1-五层协议) - * [2. OSI](#2-osi) - * [3. TCP/IP](#3-tcpip) - * [4. 数据在各层之间的传递过程](#4-数据在各层之间的传递过程) - - - -# 网络的网络 - -网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 - -

- -# ISP - -互联网服务提供商 ISP 可以从互联网管理机构获得许多 IP 地址,同时拥有通信线路以及路由器等联网设备,个人或机构向 ISP 缴纳一定的费用就可以接入互联网。 - -

- -目前的互联网是一种多层次 ISP 结构,ISP 根据覆盖面积的大小分为第一层 ISP、区域 ISP 和接入 ISP。互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 - -

- -# 主机之间的通信方式 - -- 客户-服务器(C/S):客户是服务的请求方,服务器是服务的提供方。 - -

- -- 对等(P2P):不区分客户和服务器。 - -

- -# 电路交换与分组交换 - -## 1. 电路交换 - -电路交换用于电话通信系统,两个用户要通信之前需要建立一条专用的物理链路,并且在整个通信过程中始终占用该链路。由于通信的过程中不可能一直在使用传输线路,因此电路交换对线路的利用率很低,往往不到 10%。 - -## 2. 分组交换 - -每个分组都有首部和尾部,包含了源地址和目的地址等控制信息,在同一个传输线路上同时传输多个分组互相不会影响,因此在同一条传输线路上允许同时传输多个分组,也就是说分组交换不需要占用传输线路。 - -在一个邮局通信系统中,邮局收到一份邮件之后,先存储下来,然后把相同目的地的邮件一起转发到下一个目的地,这个过程就是存储转发过程,分组交换也使用了存储转发过程。 - -# 时延 - -总时延 = 排队时延 + 处理时延 + 传输时延 + 传播时延 - -

- -## 1. 排队时延 - -分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量。 - -## 2. 处理时延 - -主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据、进行差错检验或查找适当的路由等。 - -## 3. 传输时延 - -主机或路由器传输数据帧所需要的时间。 - -

- -其中 l 表示数据帧的长度,v 表示传输速率。 - -## 4. 传播时延 - -电磁波在信道中传播所需要花费的时间,电磁波传播的速度接近光速。 - -

- -其中 l 表示信道长度,v 表示电磁波在信道上的传播速度。 - -# 计算机网络体系结构 - -

- - -## 1. 五层协议 - -- **应用层** :为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。 - -- **传输层** :为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。 - -- **网络层** :为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。 - -- **数据链路层** :网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。 - -- **物理层** :考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。 - -## 2. OSI - -其中表示层和会话层用途如下: - -- **表示层** :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。 - -- **会话层** :建立及管理会话。 - -五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。 - -## 3. TCP/IP - -它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。 - -TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 - -

- -## 4. 数据在各层之间的传递过程 - -在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 - -路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" deleted file mode 100644 index 2980af331c..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" +++ /dev/null @@ -1,26 +0,0 @@ - -* [通信方式](#通信方式) -* [带通调制](#带通调制) - - - -# 通信方式 - -根据信息在传输线上的传送方向,分为以下三种通信方式: - -- 单工通信:单向传输 -- 半双工通信:双向交替传输 -- 全双工通信:双向同时传输 - -# 带通调制 - -模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 - -

- - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\2251.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\2251.md" deleted file mode 100644 index b7cc354246..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\2251.md" +++ /dev/null @@ -1,39 +0,0 @@ - -* [目录](#目录) -* [参考链接](#参考链接) - - - -# 目录 - -- [概述](notes/计算机网络%20-%20概述.md) -- [物理层](notes/计算机网络%20-%20物理层.md) -- [链路层](notes/计算机网络%20-%20链路层.md) -- [网络层](notes/计算机网络%20-%20网络层.md) -- [传输层](notes/计算机网络%20-%20传输层.md) -- [应用层](notes/计算机网络%20-%20应用层.md) - -# 参考链接 - -- 计算机网络, 谢希仁 -- JamesF.Kurose, KeithW.Ross, 库罗斯, 等. 计算机网络: 自顶向下方法 [M]. 机械工业出版社, 2014. -- W.RichardStevens. TCP/IP 详解. 卷 1, 协议 [M]. 机械工业出版社, 2006. -- [Active vs Passive FTP Mode: Which One is More Secure?](https://securitywing.com/active-vs-passive-ftp-mode/) -- [Active and Passive FTP Transfers Defined - KB Article #1138](http://www.serv-u.com/kb/1138/active-and-passive-ftp-transfers-defined) -- [Traceroute](https://zh.wikipedia.org/wiki/Traceroute) -- [ping](https://zh.wikipedia.org/wiki/Ping) -- [How DHCP works and DHCP Interview Questions and Answers](http://webcache.googleusercontent.com/search?q=cache:http://anandgiria.blogspot.com/2013/09/windows-dhcp-interview-questions-and.html) -- [What is process of DORA in DHCP?](https://www.quora.com/What-is-process-of-DORA-in-DHCP) -- [What is DHCP Server ?](https://tecadmin.net/what-is-dhcp-server/) -- [Tackling emissions targets in Tokyo](http://www.climatechangenews.com/2011/html/university-tokyo.html) -- [What does my ISP know when I use Tor?](http://www.climatechangenews.com/2011/html/university-tokyo.html) -- [Technology-Computer Networking[1]-Computer Networks and the Internet](http://www.linyibin.cn/2017/02/12/technology-ComputerNetworking-Internet/) -- [P2P 网络概述.](http://slidesplayer.com/slide/11616167/) -- [Circuit Switching (a) Circuit switching. (b) Packet switching.](http://slideplayer.com/slide/5115386/) - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" deleted file mode 100644 index 0e59b88ae8..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" +++ /dev/null @@ -1,248 +0,0 @@ - -* [概述](#概述) -* [IP 数据报格式](#ip-数据报格式) -* [IP 地址编址方式](#ip-地址编址方式) - * [1. 分类](#1-分类) - * [2. 子网划分](#2-子网划分) - * [3. 无分类](#3-无分类) -* [地址解析协议 ARP](#地址解析协议-arp) -* [网际控制报文协议 ICMP](#网际控制报文协议-icmp) - * [1. Ping](#1-ping) - * [2. Traceroute](#2-traceroute) -* [虚拟专用网 VPN](#虚拟专用网-vpn) -* [网络地址转换 NAT](#网络地址转换-nat) -* [路由器的结构](#路由器的结构) -* [路由器分组转发流程](#路由器分组转发流程) -* [路由选择协议](#路由选择协议) - * [1. 内部网关协议 RIP](#1-内部网关协议-rip) - * [2. 内部网关协议 OSPF](#2-内部网关协议-ospf) - * [3. 外部网关协议 BGP](#3-外部网关协议-bgp) - - - -# 概述 - -因为网络层是整个互联网的核心,因此应当让网络层尽可能简单。网络层向上只提供简单灵活的、无连接的、尽最大努力交互的数据报服务。 - -使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 - -

- -与 IP 协议配套使用的还有三个协议: - -- 地址解析协议 ARP(Address Resolution Protocol) -- 网际控制报文协议 ICMP(Internet Control Message Protocol) -- 网际组管理协议 IGMP(Internet Group Management Protocol) - -# IP 数据报格式 - -

- -- **版本** : 有 4(IPv4)和 6(IPv6)两个值; - -- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 - -- **区分服务** : 用来获得更好的服务,一般情况下不使用。 - -- **总长度** : 包括首部长度和数据部分长度。 - -- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 - -- **协议** :指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 - -- **首部检验和** :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 - -- **标识** : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 - -- **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 - -

- -# IP 地址编址方式 - -IP 地址的编址方式经历了三个历史阶段: - -- 分类 -- 子网划分 -- 无分类 - -## 1. 分类 - -由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的。 - -IP 地址 ::= {< 网络号 >, < 主机号 >} - -

- -## 2. 子网划分 - -通过在主机号字段中拿一部分作为子网号,把两级 IP 地址划分为三级 IP 地址。 - -IP 地址 ::= {< 网络号 >, < 子网号 >, < 主机号 >} - -要使用子网,必须配置子网掩码。一个 B 类地址的默认子网掩码为 255.255.0.0,如果 B 类地址的子网占两个比特,那么子网掩码为 11111111 11111111 11000000 00000000,也就是 255.255.192.0。 - -注意,外部网络看不到子网的存在。 - -## 3. 无分类 - -无分类编址 CIDR 消除了传统 A 类、B 类和 C 类地址以及划分子网的概念,使用网络前缀和主机号来对 IP 地址进行编码,网络前缀的长度可以根据需要变化。 - -IP 地址 ::= {< 网络前缀号 >, < 主机号 >} - -CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法,例如 128.14.35.7/20 表示前 20 位为网络前缀。 - -CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 - -一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 - -在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 - -# 地址解析协议 ARP - -网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 - -

- -ARP 实现由 IP 地址得到 MAC 地址。 - -

- -每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表。 - -如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。 - -

- -# 网际控制报文协议 ICMP - -ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。 - -

- -ICMP 报文分为差错报告报文和询问报文。 - -

- -## 1. Ping - -Ping 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。 - -Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 回答报文。Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。 - -## 2. Traceroute - -Traceroute 是 ICMP 的另一个应用,用来跟踪一个分组从源点到终点的路径。 - -Traceroute 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报,并由目的主机发送终点不可达差错报告报文。 - -- 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,当 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文; -- 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTL 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。 -- 不断执行这样的步骤,直到最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。 -- 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。 - -# 虚拟专用网 VPN - -由于 IP 地址的紧缺,一个机构能申请到的 IP 地址数往往远小于本机构所拥有的主机数。并且一个机构并不需要把所有的主机接入到外部的互联网中,机构内的计算机可以使用仅在本机构有效的 IP 地址(专用地址)。 - -有三个专用地址块: - -- 10.0.0.0 \~ 10.255.255.255 -- 172.16.0.0 \~ 172.31.255.255 -- 192.168.0.0 \~ 192.168.255.255 - -VPN 使用公用的互联网作为本机构各专用网之间的通信载体。专用指机构内的主机只与本机构内的其它主机通信;虚拟指好像是,而实际上并不是,它有经过公用的互联网。 - -下图中,场所 A 和 B 的通信经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 - -

- -# 网络地址转换 NAT - -专用网内部的主机使用本地 IP 地址又想和互联网上的主机通信时,可以使用 NAT 来将本地 IP 转换为全球 IP。 - -在以前,NAT 将本地 IP 和全球 IP 一一对应,这种方式下拥有 n 个全球 IP 地址的专用网内最多只可以同时有 n 台主机接入互联网。为了更有效地利用全球 IP 地址,现在常用的 NAT 转换表把传输层的端口号也用上了,使得多个专用网内部的主机共用一个全球 IP 地址。使用端口号的 NAT 也叫做网络地址与端口转换 NAPT。 - -

- -# 路由器的结构 - -路由器从功能上可以划分为:路由选择和分组转发。 - -分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 - -

- -# 路由器分组转发流程 - -- 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。 -- 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付; -- 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器; -- 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器; -- 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; -- 报告转发分组出错。 - -

- -# 路由选择协议 - -路由选择协议都是自适应的,能随着网络通信量和拓扑结构的变化而自适应地进行调整。 - -互联网可以划分为许多较小的自治系统 AS,一个 AS 可以使用一种和别的 AS 不同的路由选择协议。 - -可以把路由选择协议划分为两大类: - -- 自治系统内部的路由选择:RIP 和 OSPF -- 自治系统间的路由选择:BGP - -## 1. 内部网关协议 RIP - -RIP 是一种基于距离向量的路由选择协议。距离是指跳数,直接相连的路由器跳数为 1。跳数最多为 15,超过 15 表示不可达。 - -RIP 按固定的时间间隔仅和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。 - -距离向量算法: - -- 对地址为 X 的相邻路由器发来的 RIP 报文,先修改报文中的所有项目,把下一跳字段中的地址改为 X,并把所有的距离字段加 1; -- 对修改后的 RIP 报文中的每一个项目,进行以下步骤: - - 若原来的路由表中没有目的网络 N,则把该项目添加到路由表中; - - 否则:若下一跳路由器地址是 X,则把收到的项目替换原来路由表中的项目;否则:若收到的项目中的距离 d 小于路由表中的距离,则进行更新(例如原始路由表项为 Net2, 5, P,新表项为 Net2, 4, X,则更新);否则什么也不做。 -- 若 3 分钟还没有收到相邻路由器的更新路由表,则把该相邻路由器标为不可达,即把距离置为 16。 - -RIP 协议实现简单,开销小。但是 RIP 能使用的最大距离为 15,限制了网络的规模。并且当网络出现故障时,要经过比较长的时间才能将此消息传送到所有路由器。 - -## 2. 内部网关协议 OSPF - -开放最短路径优先 OSPF,是为了克服 RIP 的缺点而开发出来的。 - -开放表示 OSPF 不受某一家厂商控制,而是公开发表的;最短路径优先表示使用了 Dijkstra 提出的最短路径算法 SPF。 - -OSPF 具有以下特点: - -- 向本自治系统中的所有路由器发送信息,这种方法是洪泛法。 -- 发送的信息就是与相邻路由器的链路状态,链路状态包括与哪些路由器相连以及链路的度量,度量用费用、距离、时延、带宽等来表示。 -- 只有当链路状态发生变化时,路由器才会发送信息。 - -所有路由器都具有全网的拓扑结构图,并且是一致的。相比于 RIP,OSPF 的更新过程收敛的很快。 - -## 3. 外部网关协议 BGP - -BGP(Border Gateway Protocol,边界网关协议) - -AS 之间的路由选择很困难,主要是由于: - -- 互联网规模很大; -- 各个 AS 内部使用不同的路由选择协议,无法准确定义路径的度量; -- AS 之间的路由选择必须考虑有关的策略,比如有些 AS 不愿意让其它 AS 经过。 - -BGP 只能寻找一条比较好的路由,而不是最佳路由。 - -每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 - -

- - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" deleted file mode 100644 index 198d4b6d15..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" +++ /dev/null @@ -1,194 +0,0 @@ - -* [基本问题](#基本问题) - * [1. 封装成帧](#1-封装成帧) - * [2. 透明传输](#2-透明传输) - * [3. 差错检测](#3-差错检测) -* [信道分类](#信道分类) - * [1. 广播信道](#1-广播信道) - * [2. 点对点信道](#2-点对点信道) -* [信道复用技术](#信道复用技术) - * [1. 频分复用](#1-频分复用) - * [2. 时分复用](#2-时分复用) - * [3. 统计时分复用](#3-统计时分复用) - * [4. 波分复用](#4-波分复用) - * [5. 码分复用](#5-码分复用) -* [CSMA/CD 协议](#csmacd-协议) -* [PPP 协议](#ppp-协议) -* [MAC 地址](#mac-地址) -* [局域网](#局域网) -* [以太网](#以太网) -* [交换机](#交换机) -* [虚拟局域网](#虚拟局域网) - - - -# 基本问题 - -## 1. 封装成帧 - -将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 - -

- -## 2. 透明传输 - -透明表示一个实际存在的事物看起来好像不存在一样。 - -帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符。如果数据部分出现转义字符,那么就在转义字符前面再加个转义字符。在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 - -

- -## 3. 差错检测 - -目前数据链路层广泛使用了循环冗余检验(CRC)来检查比特差错。 - -# 信道分类 - -## 1. 广播信道 - -一对多通信,一个节点发送的数据能够被广播信道上所有的节点接收到。 - -所有的节点都在同一个广播信道上发送数据,因此需要有专门的控制方法进行协调,避免发生冲突(冲突也叫碰撞)。 - -主要有两种控制方法进行协调,一个是使用信道复用技术,一是使用 CSMA/CD 协议。 - -## 2. 点对点信道 - -一对一通信。 - -因为不会发生碰撞,因此也比较简单,使用 PPP 协议进行控制。 - -# 信道复用技术 - -## 1. 频分复用 - -频分复用的所有主机在相同的时间占用不同的频率带宽资源。 - -

- -## 2. 时分复用 - -时分复用的所有主机在不同的时间占用相同的频率带宽资源。 - -

- -使用频分复用和时分复用进行通信,在通信的过程中主机会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 - -## 3. 统计时分复用 - -是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 - -

- -## 4. 波分复用 - -光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 - -## 5. 码分复用 - -为每个用户分配 m bit 的码片,并且所有的码片正交,对于任意两个码片 有 - -

- -为了讨论方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 - -在计算时将 00011011 记作 (-1 -1 -1 +1 +1 -1 +1 +1),可以得到 - -

- -

- -其中 的反码。 - -利用上面的式子我们知道,当接收端使用码片 对接收到的数据进行内积运算时,结果为 0 的是其它用户发送的数据,结果为 1 的是用户发送的比特 1,结果为 -1 的是用户发送的比特 0。 - -码分复用需要发送的数据量为原先的 m 倍。 - -

- -# CSMA/CD 协议 - -CSMA/CD 表示载波监听多点接入 / 碰撞检测。 - -- **多点接入** :说明这是总线型网络,许多主机以多点的方式连接到总线上。 -- **载波监听** :每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。 -- **碰撞检测** :在发送中,如果监听到信道已有其它主机正在发送数据,就表示发生了碰撞。虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 - -记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 - -当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定。从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 - -

- -# PPP 协议 - -互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 - -

- -PPP 的帧格式: - -- F 字段为帧的定界符 -- A 和 C 字段暂时没有意义 -- FCS 字段是使用 CRC 的检验序列 -- 信息部分的长度不超过 1500 - -

- -# MAC 地址 - -MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标识网络适配器(网卡)。 - -一台主机拥有多少个网络适配器就有多少个 MAC 地址。例如笔记本电脑普遍存在无线网络适配器和有线网络适配器,因此就有两个 MAC 地址。 - -# 局域网 - -局域网是一种典型的广播信道,主要特点是网络为一个单位所拥有,且地理范围和站点数目均有限。 - -主要有以太网、令牌环网、FDDI 和 ATM 等局域网技术,目前以太网占领着有线局域网市场。 - -可以按照网络拓扑结构对局域网进行分类: - -

- -# 以太网 - -以太网是一种星型拓扑结构局域网。 - -早期使用集线器进行连接,集线器是一种物理层设备, 作用于比特而不是帧,当一个比特到达接口时,集线器重新生成这个比特,并将其能量强度放大,从而扩大网络的传输距离,之后再将这个比特发送到其它所有接口。如果集线器同时收到两个不同接口的帧,那么就发生了碰撞。 - -目前以太网使用交换机替代了集线器,交换机是一种链路层设备,它不会发生碰撞,能根据 MAC 地址进行存储转发。 - -以太网帧格式: - -- **类型** :标记上层使用的协议; -- **数据** :长度在 46-1500 之间,如果太小则需要填充; -- **FCS** :帧检验序列,使用的是 CRC 检验方法; - -

- -# 交换机 - -交换机具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。 - -正是由于这种自学习能力,因此交换机是一种即插即用设备,不需要网络管理员手动配置交换表内容。 - -下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 回应该帧向主机 A 发送数据包时,交换机查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 2 的映射。 - -

- -# 虚拟局域网 - -虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息。 - -例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 - -使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧上加进了 4 字节首部 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。 - -

- - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" "b/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" deleted file mode 100644 index f3337fbcab..0000000000 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" +++ /dev/null @@ -1,900 +0,0 @@ - -* [一、概述](#一概述) - * [网络的网络](#网络的网络) - * [ISP](#isp) - * [主机之间的通信方式](#主机之间的通信方式) - * [电路交换与分组交换](#电路交换与分组交换) - * [时延](#时延) - * [计算机网络体系结构](#计算机网络体系结构) -* [二、物理层](#二物理层) - * [通信方式](#通信方式) - * [带通调制](#带通调制) -* [三、数据链路层](#三数据链路层) - * [基本问题](#基本问题) - * [信道分类](#信道分类) - * [信道复用技术](#信道复用技术) - * [CSMA/CD 协议](#csmacd-协议) - * [PPP 协议](#ppp-协议) - * [MAC 地址](#mac-地址) - * [局域网](#局域网) - * [以太网](#以太网) - * [交换机](#交换机) - * [虚拟局域网](#虚拟局域网) -* [四、网络层](#四网络层) - * [概述](#概述) - * [IP 数据报格式](#ip-数据报格式) - * [IP 地址编址方式](#ip-地址编址方式) - * [地址解析协议 ARP](#地址解析协议-arp) - * [网际控制报文协议 ICMP](#网际控制报文协议-icmp) - * [虚拟专用网 VPN](#虚拟专用网-vpn) - * [网络地址转换 NAT](#网络地址转换-nat) - * [路由器的结构](#路由器的结构) - * [路由器分组转发流程](#路由器分组转发流程) - * [路由选择协议](#路由选择协议) -* [五、传输层](#五传输层) - * [UDP 和 TCP 的特点](#udp-和-tcp-的特点) - * [UDP 首部格式](#udp-首部格式) - * [TCP 首部格式](#tcp-首部格式) - * [TCP 的三次握手](#tcp-的三次握手) - * [TCP 的四次挥手](#tcp-的四次挥手) - * [TCP 可靠传输](#tcp-可靠传输) - * [TCP 滑动窗口](#tcp-滑动窗口) - * [TCP 流量控制](#tcp-流量控制) - * [TCP 拥塞控制](#tcp-拥塞控制) -* [六、应用层](#六应用层) - * [域名系统](#域名系统) - * [文件传送协议](#文件传送协议) - * [动态主机配置协议](#动态主机配置协议) - * [远程登录协议](#远程登录协议) - * [电子邮件协议](#电子邮件协议) - * [常用端口](#常用端口) - * [Web 页面请求过程](#web-页面请求过程) -* [参考资料](#参考资料) - - - -# 一、概述 - -## 网络的网络 - -网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 - -

- -## ISP - -互联网服务提供商 ISP 可以从互联网管理机构获得许多 IP 地址,同时拥有通信线路以及路由器等联网设备,个人或机构向 ISP 缴纳一定的费用就可以接入互联网。 - -

- -目前的互联网是一种多层次 ISP 结构,ISP 根据覆盖面积的大小分为第一层 ISP、区域 ISP 和接入 ISP。互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 - -

- -## 主机之间的通信方式 - -- 客户-服务器(C/S):客户是服务的请求方,服务器是服务的提供方。 - -

- -- 对等(P2P):不区分客户和服务器。 - -

- -## 电路交换与分组交换 - -### 1. 电路交换 - -电路交换用于电话通信系统,两个用户要通信之前需要建立一条专用的物理链路,并且在整个通信过程中始终占用该链路。由于通信的过程中不可能一直在使用传输线路,因此电路交换对线路的利用率很低,往往不到 10%。 - -### 2. 分组交换 - -每个分组都有首部和尾部,包含了源地址和目的地址等控制信息,在同一个传输线路上同时传输多个分组互相不会影响,因此在同一条传输线路上允许同时传输多个分组,也就是说分组交换不需要占用传输线路。 - -在一个邮局通信系统中,邮局收到一份邮件之后,先存储下来,然后把相同目的地的邮件一起转发到下一个目的地,这个过程就是存储转发过程,分组交换也使用了存储转发过程。 - -## 时延 - -总时延 = 排队时延 + 处理时延 + 传输时延 + 传播时延 - -

- -### 1. 排队时延 - -分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量。 - -### 2. 处理时延 - -主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据、进行差错检验或查找适当的路由等。 - -### 3. 传输时延 - -主机或路由器传输数据帧所需要的时间。 - -

- -其中 l 表示数据帧的长度,v 表示传输速率。 - -### 4. 传播时延 - -电磁波在信道中传播所需要花费的时间,电磁波传播的速度接近光速。 - -

- -其中 l 表示信道长度,v 表示电磁波在信道上的传播速度。 - -## 计算机网络体系结构 - -

- - -### 1. 五层协议 - -- **应用层** :为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。 - -- **传输层** :为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。 - -- **网络层** :为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。 - -- **数据链路层** :网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。 - -- **物理层** :考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。 - -### 2. OSI - -其中表示层和会话层用途如下: - -- **表示层** :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。 - -- **会话层** :建立及管理会话。 - -五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。 - -### 3. TCP/IP - -它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。 - -TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 - -

- -### 4. 数据在各层之间的传递过程 - -在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 - -路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。 - -# 二、物理层 - -## 通信方式 - -根据信息在传输线上的传送方向,分为以下三种通信方式: - -- 单工通信:单向传输 -- 半双工通信:双向交替传输 -- 全双工通信:双向同时传输 - -## 带通调制 - -模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 - -

- -# 三、数据链路层 - -## 基本问题 - -### 1. 封装成帧 - -将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 - -

- -### 2. 透明传输 - -透明表示一个实际存在的事物看起来好像不存在一样。 - -帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符。如果数据部分出现转义字符,那么就在转义字符前面再加个转义字符。在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 - -

- -### 3. 差错检测 - -目前数据链路层广泛使用了循环冗余检验(CRC)来检查比特差错。 - -## 信道分类 - -### 1. 广播信道 - -一对多通信,一个节点发送的数据能够被广播信道上所有的节点接收到。 - -所有的节点都在同一个广播信道上发送数据,因此需要有专门的控制方法进行协调,避免发生冲突(冲突也叫碰撞)。 - -主要有两种控制方法进行协调,一个是使用信道复用技术,一是使用 CSMA/CD 协议。 - -### 2. 点对点信道 - -一对一通信。 - -因为不会发生碰撞,因此也比较简单,使用 PPP 协议进行控制。 - -## 信道复用技术 - -### 1. 频分复用 - -频分复用的所有主机在相同的时间占用不同的频率带宽资源。 - -

- -### 2. 时分复用 - -时分复用的所有主机在不同的时间占用相同的频率带宽资源。 - -

- -使用频分复用和时分复用进行通信,在通信的过程中主机会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 - -### 3. 统计时分复用 - -是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 - -

- -### 4. 波分复用 - -光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 - -### 5. 码分复用 - -为每个用户分配 m bit 的码片,并且所有的码片正交,对于任意两个码片 有 - -

- -为了讨论方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 - -在计算时将 00011011 记作 (-1 -1 -1 +1 +1 -1 +1 +1),可以得到 - -

- -

- -其中 的反码。 - -利用上面的式子我们知道,当接收端使用码片 对接收到的数据进行内积运算时,结果为 0 的是其它用户发送的数据,结果为 1 的是用户发送的比特 1,结果为 -1 的是用户发送的比特 0。 - -码分复用需要发送的数据量为原先的 m 倍。 - -

- -## CSMA/CD 协议 - -CSMA/CD 表示载波监听多点接入 / 碰撞检测。 - -- **多点接入** :说明这是总线型网络,许多主机以多点的方式连接到总线上。 -- **载波监听** :每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。 -- **碰撞检测** :在发送中,如果监听到信道已有其它主机正在发送数据,就表示发生了碰撞。虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 - -记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 - -当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定。从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 - -

- -## PPP 协议 - -互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 - -

- -PPP 的帧格式: - -- F 字段为帧的定界符 -- A 和 C 字段暂时没有意义 -- FCS 字段是使用 CRC 的检验序列 -- 信息部分的长度不超过 1500 - -

- -## MAC 地址 - -MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标识网络适配器(网卡)。 - -一台主机拥有多少个网络适配器就有多少个 MAC 地址。例如笔记本电脑普遍存在无线网络适配器和有线网络适配器,因此就有两个 MAC 地址。 - -## 局域网 - -局域网是一种典型的广播信道,主要特点是网络为一个单位所拥有,且地理范围和站点数目均有限。 - -主要有以太网、令牌环网、FDDI 和 ATM 等局域网技术,目前以太网占领着有线局域网市场。 - -可以按照网络拓扑结构对局域网进行分类: - -

- -## 以太网 - -以太网是一种星型拓扑结构局域网。 - -早期使用集线器进行连接,集线器是一种物理层设备, 作用于比特而不是帧,当一个比特到达接口时,集线器重新生成这个比特,并将其能量强度放大,从而扩大网络的传输距离,之后再将这个比特发送到其它所有接口。如果集线器同时收到两个不同接口的帧,那么就发生了碰撞。 - -目前以太网使用交换机替代了集线器,交换机是一种链路层设备,它不会发生碰撞,能根据 MAC 地址进行存储转发。 - -以太网帧格式: - -- **类型** :标记上层使用的协议; -- **数据** :长度在 46-1500 之间,如果太小则需要填充; -- **FCS** :帧检验序列,使用的是 CRC 检验方法; - -

- -## 交换机 - -交换机具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。 - -正是由于这种自学习能力,因此交换机是一种即插即用设备,不需要网络管理员手动配置交换表内容。 - -下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 回应该帧向主机 A 发送数据包时,交换机查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 2 的映射。 - -

- -## 虚拟局域网 - -虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息。 - -例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 - -使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧上加进了 4 字节首部 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。 - -

- -# 四、网络层 - -## 概述 - -因为网络层是整个互联网的核心,因此应当让网络层尽可能简单。网络层向上只提供简单灵活的、无连接的、尽最大努力交互的数据报服务。 - -使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 - -

- -与 IP 协议配套使用的还有三个协议: - -- 地址解析协议 ARP(Address Resolution Protocol) -- 网际控制报文协议 ICMP(Internet Control Message Protocol) -- 网际组管理协议 IGMP(Internet Group Management Protocol) - -## IP 数据报格式 - -

- -- **版本** : 有 4(IPv4)和 6(IPv6)两个值; - -- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 - -- **区分服务** : 用来获得更好的服务,一般情况下不使用。 - -- **总长度** : 包括首部长度和数据部分长度。 - -- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 - -- **协议** :指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 - -- **首部检验和** :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 - -- **标识** : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 - -- **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 - -

- -## IP 地址编址方式 - -IP 地址的编址方式经历了三个历史阶段: - -- 分类 -- 子网划分 -- 无分类 - -### 1. 分类 - -由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的。 - -IP 地址 ::= {< 网络号 >, < 主机号 >} - -

- -### 2. 子网划分 - -通过在主机号字段中拿一部分作为子网号,把两级 IP 地址划分为三级 IP 地址。 - -IP 地址 ::= {< 网络号 >, < 子网号 >, < 主机号 >} - -要使用子网,必须配置子网掩码。一个 B 类地址的默认子网掩码为 255.255.0.0,如果 B 类地址的子网占两个比特,那么子网掩码为 11111111 11111111 11000000 00000000,也就是 255.255.192.0。 - -注意,外部网络看不到子网的存在。 - -### 3. 无分类 - -无分类编址 CIDR 消除了传统 A 类、B 类和 C 类地址以及划分子网的概念,使用网络前缀和主机号来对 IP 地址进行编码,网络前缀的长度可以根据需要变化。 - -IP 地址 ::= {< 网络前缀号 >, < 主机号 >} - -CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法,例如 128.14.35.7/20 表示前 20 位为网络前缀。 - -CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 - -一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 - -在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 - -## 地址解析协议 ARP - -网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 - -

- -ARP 实现由 IP 地址得到 MAC 地址。 - -

- -每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表。 - -如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。 - -

- -## 网际控制报文协议 ICMP - -ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。 - -

- -ICMP 报文分为差错报告报文和询问报文。 - -

- -### 1. Ping - -Ping 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。 - -Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 回答报文。Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。 - -### 2. Traceroute - -Traceroute 是 ICMP 的另一个应用,用来跟踪一个分组从源点到终点的路径。 - -Traceroute 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报,并由目的主机发送终点不可达差错报告报文。 - -- 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,当 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文; -- 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTL 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。 -- 不断执行这样的步骤,直到最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。 -- 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。 - -## 虚拟专用网 VPN - -由于 IP 地址的紧缺,一个机构能申请到的 IP 地址数往往远小于本机构所拥有的主机数。并且一个机构并不需要把所有的主机接入到外部的互联网中,机构内的计算机可以使用仅在本机构有效的 IP 地址(专用地址)。 - -有三个专用地址块: - -- 10.0.0.0 \~ 10.255.255.255 -- 172.16.0.0 \~ 172.31.255.255 -- 192.168.0.0 \~ 192.168.255.255 - -VPN 使用公用的互联网作为本机构各专用网之间的通信载体。专用指机构内的主机只与本机构内的其它主机通信;虚拟指好像是,而实际上并不是,它有经过公用的互联网。 - -下图中,场所 A 和 B 的通信经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 - -

- -## 网络地址转换 NAT - -专用网内部的主机使用本地 IP 地址又想和互联网上的主机通信时,可以使用 NAT 来将本地 IP 转换为全球 IP。 - -在以前,NAT 将本地 IP 和全球 IP 一一对应,这种方式下拥有 n 个全球 IP 地址的专用网内最多只可以同时有 n 台主机接入互联网。为了更有效地利用全球 IP 地址,现在常用的 NAT 转换表把传输层的端口号也用上了,使得多个专用网内部的主机共用一个全球 IP 地址。使用端口号的 NAT 也叫做网络地址与端口转换 NAPT。 - -

- -## 路由器的结构 - -路由器从功能上可以划分为:路由选择和分组转发。 - -分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 - -

- -## 路由器分组转发流程 - -- 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。 -- 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付; -- 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器; -- 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器; -- 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; -- 报告转发分组出错。 - -

- -## 路由选择协议 - -路由选择协议都是自适应的,能随着网络通信量和拓扑结构的变化而自适应地进行调整。 - -互联网可以划分为许多较小的自治系统 AS,一个 AS 可以使用一种和别的 AS 不同的路由选择协议。 - -可以把路由选择协议划分为两大类: - -- 自治系统内部的路由选择:RIP 和 OSPF -- 自治系统间的路由选择:BGP - -### 1. 内部网关协议 RIP - -RIP 是一种基于距离向量的路由选择协议。距离是指跳数,直接相连的路由器跳数为 1。跳数最多为 15,超过 15 表示不可达。 - -RIP 按固定的时间间隔仅和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。 - -距离向量算法: - -- 对地址为 X 的相邻路由器发来的 RIP 报文,先修改报文中的所有项目,把下一跳字段中的地址改为 X,并把所有的距离字段加 1; -- 对修改后的 RIP 报文中的每一个项目,进行以下步骤: - - 若原来的路由表中没有目的网络 N,则把该项目添加到路由表中; - - 否则:若下一跳路由器地址是 X,则把收到的项目替换原来路由表中的项目;否则:若收到的项目中的距离 d 小于路由表中的距离,则进行更新(例如原始路由表项为 Net2, 5, P,新表项为 Net2, 4, X,则更新);否则什么也不做。 -- 若 3 分钟还没有收到相邻路由器的更新路由表,则把该相邻路由器标为不可达,即把距离置为 16。 - -RIP 协议实现简单,开销小。但是 RIP 能使用的最大距离为 15,限制了网络的规模。并且当网络出现故障时,要经过比较长的时间才能将此消息传送到所有路由器。 - -### 2. 内部网关协议 OSPF - -开放最短路径优先 OSPF,是为了克服 RIP 的缺点而开发出来的。 - -开放表示 OSPF 不受某一家厂商控制,而是公开发表的;最短路径优先表示使用了 Dijkstra 提出的最短路径算法 SPF。 - -OSPF 具有以下特点: - -- 向本自治系统中的所有路由器发送信息,这种方法是洪泛法。 -- 发送的信息就是与相邻路由器的链路状态,链路状态包括与哪些路由器相连以及链路的度量,度量用费用、距离、时延、带宽等来表示。 -- 只有当链路状态发生变化时,路由器才会发送信息。 - -所有路由器都具有全网的拓扑结构图,并且是一致的。相比于 RIP,OSPF 的更新过程收敛的很快。 - -### 3. 外部网关协议 BGP - -BGP(Border Gateway Protocol,边界网关协议) - -AS 之间的路由选择很困难,主要是由于: - -- 互联网规模很大; -- 各个 AS 内部使用不同的路由选择协议,无法准确定义路径的度量; -- AS 之间的路由选择必须考虑有关的策略,比如有些 AS 不愿意让其它 AS 经过。 - -BGP 只能寻找一条比较好的路由,而不是最佳路由。 - -每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 - -

- -# 五、传输层 - -网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。 - -## UDP 和 TCP 的特点 - -- 用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。 - -- 传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。 - -## UDP 首部格式 - -

- -首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 - -## TCP 首部格式 - -

- -- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 - -- **确认号** :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 - -- **数据偏移** :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 - -- **确认 ACK** :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 - -- **同步 SYN** :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 - -- **终止 FIN** :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。 - -- **窗口** :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 - -## TCP 的三次握手 - -

- -假设 A 为客户端,B 为服务器端。 - -- 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。 - -- A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x。 - -- B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。 - -- A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。 - -- B 收到 A 的确认后,连接建立。 - -**三次握手的原因** - -第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 - -客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。 - -## TCP 的四次挥手 - -

- -以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 - -- A 发送连接释放报文,FIN=1。 - -- B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。 - -- 当 B 不再需要连接时,发送连接释放报文,FIN=1。 - -- A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。 - -- B 收到 A 的确认后释放连接。 - -**四次挥手的原因** - -客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 - -**TIME_WAIT** - -客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: - -- 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。 - -- 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。 - -## TCP 可靠传输 - -TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。 - -一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算如下: - -

- -其中,0 ≤ a < 1,RTTs 随着 a 的增加更容易受到 RTT 的影响。 - -超时时间 RTO 应该略大于 RTTs,TCP 使用的超时时间计算如下: - -

- -其中 RTTd 为偏差的加权平均值。 - -## TCP 滑动窗口 - -窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 - -发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 - -接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 - -

- -## TCP 流量控制 - -流量控制是为了控制发送方发送速率,保证接收方来得及接收。 - -接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 - -## TCP 拥塞控制 - -如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 - -

- -TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。 - -发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 - -为了便于讨论,做如下假设: - -- 接收方有足够大的接收缓存,因此不会发生流量控制; -- 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 - -

- -### 1. 慢开始与拥塞避免 - -发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ... - -注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。 - -如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。 - -### 2. 快重传与快恢复 - -在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。 - -在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。 - -在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 - -慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。 - -

- -# 六、应用层 - -## 域名系统 - -DNS 是一个分布式数据库,提供了主机名和 IP 地址之间相互转换的服务。这里的分布式数据库是指,每个站点只保留它自己的那部分数据。 - -域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。 - -

- -DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传来保证可靠性。在两种情况下会使用 TCP 进行传输: - -- 如果返回的响应超过的 512 字节(UDP 最大只支持 512 字节的数据)。 -- 区域传送(区域传送是主域名服务器向辅助域名服务器传送变化的那部分数据)。 - -## 文件传送协议 - -FTP 使用 TCP 进行连接,它需要两个连接来传送一个文件: - -- 控制连接:服务器打开端口号 21 等待客户端的连接,客户端主动建立连接后,使用这个连接将客户端的命令传送给服务器,并传回服务器的应答。 -- 数据连接:用来传送一个文件数据。 - -根据数据连接是否是服务器端主动建立,FTP 有主动和被动两种模式: - -- 主动模式:服务器端主动建立数据连接,其中服务器端的端口号为 20,客户端的端口号随机,但是必须大于 1024,因为 0\~1023 是熟知端口号。 - -

- -- 被动模式:客户端主动建立数据连接,其中客户端的端口号由客户端自己指定,服务器端的端口号随机。 - -

- -主动模式要求客户端开放端口号给服务器端,需要去配置客户端的防火墙。被动模式只需要服务器端开放端口号即可,无需客户端配置防火墙。但是被动模式会导致服务器端的安全性减弱,因为开放了过多的端口号。 - -## 动态主机配置协议 - -DHCP (Dynamic Host Configuration Protocol) 提供了即插即用的连网方式,用户不再需要去手动配置 IP 地址等信息。 - -DHCP 配置的内容不仅是 IP 地址,还包括子网掩码、网关 IP 地址。 - -DHCP 工作过程如下: - -1. 客户端发送 Discover 报文,该报文的目的地址为 255.255.255.255:67,源地址为 0.0.0.0:68,被放入 UDP 中,该报文被广播到同一个子网的所有主机上。如果客户端和 DHCP 服务器不在同一个子网,就需要使用中继代理。 -2. DHCP 服务器收到 Discover 报文之后,发送 Offer 报文给客户端,该报文包含了客户端所需要的信息。因为客户端可能收到多个 DHCP 服务器提供的信息,因此客户端需要进行选择。 -3. 如果客户端选择了某个 DHCP 服务器提供的信息,那么就发送 Request 报文给该 DHCP 服务器。 -4. DHCP 服务器发送 Ack 报文,表示客户端此时可以使用提供给它的信息。 - -

- -## 远程登录协议 - -TELNET 用于登录到远程主机上,并且远程主机上的输出也会返回。 - -TELNET 可以适应许多计算机和操作系统的差异,例如不同操作系统系统的换行符定义。 - -## 电子邮件协议 - -一个电子邮件系统由三部分组成:用户代理、邮件服务器以及邮件协议。 - -邮件协议包含发送协议和读取协议,发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 - -

- -### 1. SMTP - -SMTP 只能发送 ASCII 码,而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP,而是增加邮件主体的结构,定义了非 ASCII 码的编码规则。 - -

- -### 2. POP3 - -POP3 的特点是只要用户从服务器上读取了邮件,就把该邮件删除。 - -### 3. IMAP - -IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删除邮件,那么服务器上的邮件也不会被删除。IMAP 这种做法可以让用户随时随地去访问服务器上的邮件。 - -## 常用端口 - -|应用| 应用层协议 | 端口号 | 传输层协议 | 备注 | -| :---: | :--: | :--: | :--: | :--: | -| 域名解析 | DNS | 53 | UDP/TCP | 长度超过 512 字节时使用 TCP | -| 动态主机配置协议 | DHCP | 67/68 | UDP | | -| 简单网络管理协议 | SNMP | 161/162 | UDP | | -| 文件传送协议 | FTP | 20/21 | TCP | 控制连接 21,数据连接 20 | -| 远程终端协议 | TELNET | 23 | TCP | | -| 超文本传送协议 | HTTP | 80 | TCP | | -| 简单邮件传送协议 | SMTP | 25 | TCP | | -| 邮件读取协议 | POP3 | 110 | TCP | | -| 网际报文存取协议 | IMAP | 143 | TCP | | - -## Web 页面请求过程 - -### 1. DHCP 配置主机信息 - -- 假设主机最开始没有 IP 地址以及其它信息,那么就需要先使用 DHCP 来获取。 - -- 主机生成一个 DHCP 请求报文,并将这个报文放入具有目的端口 67 和源端口 68 的 UDP 报文段中。 - -- 该报文段则被放入在一个具有广播 IP 目的地址(255.255.255.255) 和源 IP 地址(0.0.0.0)的 IP 数据报中。 - -- 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。 - -- 连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段有被放入 IP 数据报中,最后放入 MAC 帧中。 - -- 该帧的目的地址是请求主机的 MAC 地址,因为交换机具有自学习能力,之前主机发送了广播帧之后就记录了 MAC 地址到其转发接口的交换表项,因此现在交换机就可以直接知道应该向哪个接口发送该帧。 - -- 主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。 - -### 2. ARP 解析 MAC 地址 - -- 主机通过浏览器生成一个 TCP 套接字,套接字向 HTTP 服务器发送 HTTP 请求。为了生成该套接字,主机需要知道网站的域名对应的 IP 地址。 - -- 主机生成一个 DNS 查询报文,该报文具有 53 号端口,因为 DNS 服务器的端口号是 53。 - -- 该 DNS 查询报文被放入目的地址为 DNS 服务器 IP 地址的 IP 数据报中。 - -- 该 IP 数据报被放入一个以太网帧中,该帧将发送到网关路由器。 - -- DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。 - -- 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。 - -- 网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。 - -### 3. DNS 解析域名 - -- 知道了网关路由器的 MAC 地址之后,就可以继续 DNS 的解析过程了。 - -- 网关路由器接收到包含 DNS 查询报文的以太网帧后,抽取出 IP 数据报,并根据转发表决定该 IP 数据报应该转发的路由器。 - -- 因为路由器具有内部网关协议(RIP、OSPF)和外部网关协议(BGP)这两种路由选择协议,因此路由表中已经配置了网关路由器到达 DNS 服务器的路由表项。 - -- 到达 DNS 服务器之后,DNS 服务器抽取出 DNS 查询报文,并在 DNS 数据库中查找待解析的域名。 - -- 找到 DNS 记录之后,发送 DNS 回答报文,将该回答报文放入 UDP 报文段中,然后放入 IP 数据报中,通过路由器反向转发回网关路由器,并经过以太网交换机到达主机。 - -### 4. HTTP 请求页面 - -- 有了 HTTP 服务器的 IP 地址之后,主机就能够生成 TCP 套接字,该套接字将用于向 Web 服务器发送 HTTP GET 报文。 - -- 在生成 TCP 套接字之前,必须先与 HTTP 服务器进行三次握手来建立连接。生成一个具有目的端口 80 的 TCP SYN 报文段,并向 HTTP 服务器发送该报文段。 - -- HTTP 服务器收到该报文段之后,生成 TCP SYN ACK 报文段,发回给主机。 - -- 连接建立之后,浏览器生成 HTTP GET 报文,并交付给 HTTP 服务器。 - -- HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。 - -- 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。 - - -# 参考资料 - -- 计算机网络, 谢希仁 -- JamesF.Kurose, KeithW.Ross, 库罗斯, 等. 计算机网络: 自顶向下方法 [M]. 机械工业出版社, 2014. -- W.RichardStevens. TCP/IP 详解. 卷 1, 协议 [M]. 机械工业出版社, 2006. -- [Active vs Passive FTP Mode: Which One is More Secure?](https://securitywing.com/active-vs-passive-ftp-mode/) -- [Active and Passive FTP Transfers Defined - KB Article #1138](http://www.serv-u.com/kb/1138/active-and-passive-ftp-transfers-defined) -- [Traceroute](https://zh.wikipedia.org/wiki/Traceroute) -- [ping](https://zh.wikipedia.org/wiki/Ping) -- [How DHCP works and DHCP Interview Questions and Answers](http://webcache.googleusercontent.com/search?q=cache:http://anandgiria.blogspot.com/2013/09/windows-dhcp-interview-questions-and.html) -- [What is process of DORA in DHCP?](https://www.quora.com/What-is-process-of-DORA-in-DHCP) -- [What is DHCP Server ?](https://tecadmin.net/what-is-dhcp-server/) -- [Tackling emissions targets in Tokyo](http://www.climatechangenews.com/2011/html/university-tokyo.html) -- [What does my ISP know when I use Tor?](http://www.climatechangenews.com/2011/html/university-tokyo.html) -- [Technology-Computer Networking[1]-Computer Networks and the Internet](http://www.linyibin.cn/2017/02/12/technology-ComputerNetworking-Internet/) -- [P2P 网络概述.](http://slidesplayer.com/slide/11616167/) -- [Circuit Switching (a) Circuit switching. (b) Packet switching.](http://slideplayer.com/slide/5115386/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\351\233\206\347\276\244.md" "b/docs/notes/\351\233\206\347\276\244.md" deleted file mode 100644 index fb625bbf29..0000000000 --- "a/docs/notes/\351\233\206\347\276\244.md" +++ /dev/null @@ -1,206 +0,0 @@ - -* [一、负载均衡](#一负载均衡) - * [负载均衡算法](#负载均衡算法) - * [转发实现](#转发实现) -* [二、集群下的 Session 管理](#二集群下的-session-管理) - * [Sticky Session](#sticky-session) - * [Session Replication](#session-replication) - * [Session Server](#session-server) - - - -# 一、负载均衡 - -集群中的应用服务器(节点)通常被设计成无状态,用户可以请求任何一个节点。 - -负载均衡器会根据集群中每个节点的负载情况,将用户请求转发到合适的节点上。 - -负载均衡器可以用来实现高可用以及伸缩性: - -- 高可用:当某个节点故障时,负载均衡器会将用户请求转发到另外的节点上,从而保证所有服务持续可用; -- 伸缩性:根据系统整体负载情况,可以很容易地添加或移除节点。 - -负载均衡器运行过程包含两个部分: - -1. 根据负载均衡算法得到转发的节点; -2. 进行转发。 - -## 负载均衡算法 - -### 1. 轮询(Round Robin) - -轮询算法把每个请求轮流发送到每个服务器上。 - -下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。 - -

- -该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担过大的负载(下图的 Server 2)。 - -

- -### 2. 加权轮询(Weighted Round Robbin) - -加权轮询是在轮询的基础上,根据服务器的性能差异,为服务器赋予一定的权值,性能高的服务器分配更高的权值。 - -例如下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。 - -

- -### 3. 最少连接(least Connections) - -由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过大,而另一台服务器的连接过小,造成负载不均衡。 - -例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开,此时 (6, 4) 请求连接服务器 2。该系统继续运行时,服务器 2 会承担过大的负载。 - -

- -最少连接算法就是将请求发送给当前最少连接数的服务器上。 - -例如下图中,服务器 1 当前连接数最小,那么新到来的请求 6 就会被发送到服务器 1 上。 - -

- -### 4. 加权最少连接(Weighted Least Connection) - -在最少连接的基础上,根据服务器的性能为每台服务器分配权重,再根据权重计算出每台服务器能处理的连接数。 - -### 5. 随机算法(Random) - -把请求随机发送到服务器上。 - -和轮询算法类似,该算法比较适合服务器性能差不多的场景。 - -

- -### 6. 源地址哈希法 (IP Hash) - -源地址哈希通过对客户端 IP 计算哈希值之后,再对服务器数量取模得到目标服务器的序号。 - -可以保证同一 IP 的客户端的请求会转发到同一台服务器上,用来实现会话粘滞(Sticky Session) - -

- -## 转发实现 - -### 1. HTTP 重定向 - -HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服务器的 IP 地址之后,将该地址写入 HTTP 重定向报文中,状态码为 302。客户端收到重定向报文之后,需要重新向服务器发起请求。 - -缺点: - -- 需要两次请求,因此访问延迟比较高; -- HTTP 负载均衡器处理能力有限,会限制集群的规模。 - -该负载均衡转发的缺点比较明显,实际场景中很少使用它。 - -

- -### 2. DNS 域名解析 - -在 DNS 解析域名的同时使用负载均衡算法计算服务器 IP 地址。 - -优点: - -- DNS 能够根据地理位置进行域名解析,返回离用户最近的服务器 IP 地址。 - -缺点: - -- 由于 DNS 具有多级结构,每一级的域名记录都可能被缓存,当下线一台服务器需要修改 DNS 记录时,需要过很长一段时间才能生效。 - -大型网站基本使用了 DNS 做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。也就是说,域名解析的结果为内部的负载均衡服务器 IP 地址。 - -

- -### 3. 反向代理服务器 - -反向代理服务器位于源服务器前面,用户的请求需要先经过反向代理服务器才能到达源服务器。反向代理可以用来进行缓存、日志记录等,同时也可以用来做为负载均衡服务器。 - -在这种负载均衡转发方式下,客户端不直接请求源服务器,因此源服务器不需要外部 IP 地址,而反向代理需要配置内部和外部两套 IP 地址。 - -优点: - -- 与其它功能集成在一起,部署简单。 - -缺点: - -- 所有请求和响应都需要经过反向代理服务器,它可能会成为性能瓶颈。 - -### 4. 网络层 - -在操作系统内核进程获取网络数据包,根据负载均衡算法计算源服务器的 IP 地址,并修改请求数据包的目的 IP 地址,最后进行转发。 - -源服务器返回的响应也需要经过负载均衡服务器,通常是让负载均衡服务器同时作为集群的网关服务器来实现。 - -优点: - -- 在内核进程中进行处理,性能比较高。 - -缺点: - -- 和反向代理一样,所有的请求和响应都经过负载均衡服务器,会成为性能瓶颈。 - -### 5. 链路层 - -在链路层根据负载均衡算法计算源服务器的 MAC 地址,并修改请求数据包的目的 MAC 地址,并进行转发。 - -通过配置源服务器的虚拟 IP 地址和负载均衡服务器的 IP 地址一致,从而不需要修改 IP 地址就可以进行转发。也正因为 IP 地址一样,所以源服务器的响应不需要转发回负载均衡服务器,可以直接转发给客户端,避免了负载均衡服务器的成为瓶颈。 - -这是一种三角传输模式,被称为直接路由。对于提供下载和视频服务的网站来说,直接路由避免了大量的网络传输数据经过负载均衡服务器。 - -这是目前大型网站使用最广负载均衡转发方式,在 Linux 平台可以使用的负载均衡服务器为 LVS(Linux Virtual Server)。 - -参考: - -- [Comparing Load Balancing Algorithms](http://www.jscape.com/blog/load-balancing-algorithms) -- [Redirection and Load Balancing](http://slideplayer.com/slide/6599069/#) - -# 二、集群下的 Session 管理 - -一个用户的 Session 信息如果存储在一个服务器上,那么当负载均衡器把用户的下一个请求转发到另一个服务器,由于服务器没有用户的 Session 信息,那么该用户就需要重新进行登录等操作。 - -## Sticky Session - -需要配置负载均衡器,使得一个用户的所有请求都路由到同一个服务器,这样就可以把用户的 Session 存放在该服务器中。 - -缺点: - -- 当服务器宕机时,将丢失该服务器上的所有 Session。 - -

- -## Session Replication - -在服务器之间进行 Session 同步操作,每个服务器都有所有用户的 Session 信息,因此用户可以向任何一个服务器进行请求。 - -缺点: - -- 占用过多内存; -- 同步过程占用网络带宽以及服务器处理器时间。 - -

- -## Session Server - -使用一个单独的服务器存储 Session 数据,可以使用传统的 MySQL,也使用 Redis 或者 Memcached 这种内存型数据库。 - -优点: - -- 为了使得大型网站具有伸缩性,集群中的应用服务器通常需要保持无状态,那么应用服务器不能存储用户的会话信息。Session Server 将用户的会话信息单独进行存储,从而保证了应用服务器的无状态。 - -缺点: - -- 需要去实现存取 Session 的代码。 - -

- -参考: - -- [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/) - - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" "b/docs/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" deleted file mode 100644 index f90b490712..0000000000 --- "a/docs/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" +++ /dev/null @@ -1,365 +0,0 @@ - -* [一、三大特性](#一三大特性) - * [封装](#封装) - * [继承](#继承) - * [多态](#多态) -* [二、类图](#二类图) - * [泛化关系 (Generalization)](#泛化关系-generalization) - * [实现关系 (Realization)](#实现关系-realization) - * [聚合关系 (Aggregation)](#聚合关系-aggregation) - * [组合关系 (Composition)](#组合关系-composition) - * [关联关系 (Association)](#关联关系-association) - * [依赖关系 (Dependency)](#依赖关系-dependency) -* [三、设计原则](#三设计原则) - * [S.O.L.I.D](#solid) - * [其他常见原则](#其他常见原则) -* [参考资料](#参考资料) - - - -# 一、三大特性 - -## 封装 - -利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。 - -优点: - -- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改 -- 减轻维护的负担:可以更容易被程序员理解,并且在调试的时候可以不影响其他模块 -- 有效地调节性能:可以通过剖析确定哪些模块影响了系统的性能 -- 提高软件的可重用性 -- 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的 - -以下 Person 类封装 name、gender、age 等属性,外界只能通过 get() 方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work() 方法使用。 - -注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 gender 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。 - -```java -public class Person { - - private String name; - private int gender; - private int age; - - public String getName() { - return name; - } - - public String getGender() { - return gender == 0 ? "man" : "woman"; - } - - public void work() { - if (18 <= age && age <= 50) { - System.out.println(name + " is working very hard!"); - } else { - System.out.println(name + " can't work any more!"); - } - } -} -``` - -## 继承 - -继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。 - -继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。 - -Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型** 。 - -```java -Animal animal = new Cat(); -``` - -## 多态 - -多态分为编译时多态和运行时多态: - -- 编译时多态主要指方法的重载 -- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定 - -运行时多态有三个条件: - -- 继承 -- 覆盖(重写) -- 向上转型 - -下面的代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。 - -```java -public class Instrument { - - public void play() { - System.out.println("Instument is playing..."); - } -} - -public class Wind extends Instrument { - - public void play() { - System.out.println("Wind is playing..."); - } -} - -public class Percussion extends Instrument { - - public void play() { - System.out.println("Percussion is playing..."); - } -} - -public class Music { - - public static void main(String[] args) { - List instruments = new ArrayList<>(); - instruments.add(new Wind()); - instruments.add(new Percussion()); - for(Instrument instrument : instruments) { - instrument.play(); - } - } -} -``` - -# 二、类图 - -以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ 。 - -## 泛化关系 (Generalization) - -用来描述继承关系,在 Java 中使用 extends 关键字。 - -

- -```text -@startuml - -title Generalization - -class Vihical -class Car -class Trunck - -Vihical <|-- Car -Vihical <|-- Trunck - -@enduml -``` - -## 实现关系 (Realization) - -用来实现一个接口,在 Java 中使用 implements 关键字。 - -

- -```text -@startuml - -title Realization - -interface MoveBehavior -class Fly -class Run - -MoveBehavior <|.. Fly -MoveBehavior <|.. Run - -@enduml -``` - -## 聚合关系 (Aggregation) - -表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。 - -

- -```text -@startuml - -title Aggregation - -class Computer -class Keyboard -class Mouse -class Screen - -Computer o-- Keyboard -Computer o-- Mouse -Computer o-- Screen - -@enduml -``` - -## 组合关系 (Composition) - -和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 - -

- -```text -@startuml - -title Composition - -class Company -class DepartmentA -class DepartmentB - -Company *-- DepartmentA -Company *-- DepartmentB - -@enduml -``` - -## 关联关系 (Association) - -表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。 - -

- -```text -@startuml - -title Association - -class School -class Student - -School "1" - "n" Student - -@enduml -``` - -## 依赖关系 (Dependency) - -和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式: - -- A 类是 B 类方法的局部变量; -- A 类是 B 类方法当中的一个参数; -- A 类向 B 类发送消息,从而影响 B 类发生变化。 - -

- -```text -@startuml - -title Dependency - -class Vihicle { - move(MoveBehavior) -} - -interface MoveBehavior { - move() -} - -note "MoveBehavior.move()" as N - -Vihicle ..> MoveBehavior - -Vihicle .. N - -@enduml -``` - -# 三、设计原则 - -## S.O.L.I.D - -| 简写 | 全拼 | 中文翻译 | -| :--: | :--: | :--: | -| SRP | The Single Responsibility Principle | 单一责任原则 | -| OCP | The Open Closed Principle | 开放封闭原则 | -| LSP | The Liskov Substitution Principle | 里氏替换原则 | -| ISP | The Interface Segregation Principle | 接口分离原则 | -| DIP | The Dependency Inversion Principle | 依赖倒置原则 | - -### 1. 单一责任原则 - -> 修改一个类的原因应该只有一个。 - -换句话说就是让一个类只负责一件事,当这个类需要做过多事情的时候,就需要分解这个类。 - -如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱这个类完成其它职责的能力。 - -### 2. 开放封闭原则 - -> 类应该对扩展开放,对修改关闭。 - -扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。 - -符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。 - -### 3. 里氏替换原则 - -> 子类对象必须能够替换掉所有父类对象。 - -继承是一种 IS-A 关系,子类需要能够当成父类来使用,并且需要比父类更特殊。 - -如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。 - -### 4. 接口分离原则 - -> 不应该强迫客户依赖于它们不用的方法。 - -因此使用多个专门的接口比使用单一的总接口要好。 - -### 5. 依赖倒置原则 - -> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;
抽象不应该依赖于细节,细节应该依赖于抽象。 - -高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。 - -依赖于抽象意味着: - -- 任何变量都不应该持有一个指向具体类的指针或者引用; -- 任何类都不应该从具体类派生; -- 任何方法都不应该覆写它的任何基类中的已经实现的方法。 - -## 其他常见原则 - -除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。 - -| 简写 | 全拼 | 中文翻译 | -| :--: | :--: | :--: | -|LOD| The Law of Demeter | 迪米特法则 | -|CRP| The Composite Reuse Principle | 合成复用原则 | -|CCP| The Common Closure Principle | 共同封闭原则 | -|SAP| The Stable Abstractions Principle | 稳定抽象原则 | -|SDP| The Stable Dependencies Principle | 稳定依赖原则 | - -### 1. 迪米特法则 - -迪米特法则又叫作最少知识原则(Least Knowledge Principle,简写 LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。 - -### 2. 合成复用原则 - -尽量使用对象组合,而不是通过继承来达到复用的目的。 - -### 3. 共同封闭原则 - -一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。 - -### 4. 稳定抽象原则 - -最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。 - -### 5. 稳定依赖原则 - -包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。 - -# 参考资料 - -- Java 编程思想 -- 敏捷软件开发:原则、模式与实践 -- [面向对象设计的 SOLID 原则](http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html) -- [看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization) -- [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html) -- [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png b/docs/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png deleted file mode 100644 index 0e56341c39..0000000000 Binary files a/docs/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png and /dev/null differ diff --git a/docs/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg b/docs/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg deleted file mode 100644 index 39c003ce56..0000000000 Binary files a/docs/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg and /dev/null differ diff --git a/docs/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png b/docs/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png deleted file mode 100644 index fc0999f997..0000000000 Binary files a/docs/pics/0157d362-98dd-4e51-ac26-00ecb76beb3e.png and /dev/null differ diff --git a/docs/pics/032771e7-f60f-47bf-aa79-f45c32799211.png b/docs/pics/032771e7-f60f-47bf-aa79-f45c32799211.png deleted file mode 100644 index 5b9202f6fd..0000000000 Binary files a/docs/pics/032771e7-f60f-47bf-aa79-f45c32799211.png and /dev/null differ diff --git a/docs/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg b/docs/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg deleted file mode 100644 index 9a6f75c67e..0000000000 Binary files a/docs/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg and /dev/null differ diff --git a/docs/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png b/docs/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png deleted file mode 100644 index 57d8c810e7..0000000000 Binary files a/docs/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png and /dev/null differ diff --git a/docs/pics/051e436c-0e46-4c59-8f67-52d89d656182.png b/docs/pics/051e436c-0e46-4c59-8f67-52d89d656182.png deleted file mode 100644 index e3054539f4..0000000000 Binary files a/docs/pics/051e436c-0e46-4c59-8f67-52d89d656182.png and /dev/null differ diff --git a/docs/pics/0635cbe8.png b/docs/pics/0635cbe8.png deleted file mode 100644 index 849c9eaf15..0000000000 Binary files a/docs/pics/0635cbe8.png and /dev/null differ diff --git a/docs/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg b/docs/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg deleted file mode 100644 index 84d371b863..0000000000 Binary files a/docs/pics/066f9c11-0154-42c3-8685-301a70e9bd39.jpg and /dev/null differ diff --git a/docs/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg b/docs/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg deleted file mode 100644 index c07bd92474..0000000000 Binary files a/docs/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg and /dev/null differ diff --git a/docs/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg b/docs/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg deleted file mode 100644 index b8b100a017..0000000000 Binary files a/docs/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg and /dev/null differ diff --git a/docs/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png b/docs/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png deleted file mode 100644 index 13d4983652..0000000000 Binary files a/docs/pics/08427d38-8df1-49a1-8990-e0ce5ee36ca2.png and /dev/null differ diff --git a/docs/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png b/docs/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png deleted file mode 100644 index 4d9e3e21c7..0000000000 Binary files a/docs/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png and /dev/null differ diff --git a/docs/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg b/docs/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg deleted file mode 100644 index 09bbd597bd..0000000000 Binary files a/docs/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg and /dev/null differ diff --git a/docs/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png b/docs/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png deleted file mode 100644 index 17f4d5b75e..0000000000 Binary files a/docs/pics/09184175-9bf2-40ff-8a68-3b467c77216a.png and /dev/null differ diff --git a/docs/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg b/docs/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg deleted file mode 100644 index 7b88e7a05f..0000000000 Binary files a/docs/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg and /dev/null differ diff --git a/docs/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png b/docs/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png deleted file mode 100644 index 78a69978e4..0000000000 Binary files a/docs/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png and /dev/null differ diff --git a/docs/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png b/docs/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png deleted file mode 100644 index d8df24d8df..0000000000 Binary files a/docs/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png and /dev/null differ diff --git a/docs/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png b/docs/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png deleted file mode 100644 index 79ec18df91..0000000000 Binary files a/docs/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png and /dev/null differ diff --git a/docs/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png b/docs/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png deleted file mode 100644 index 78843b1c89..0000000000 Binary files a/docs/pics/0f6f92e8-f15e-4c09-8562-b9c6114df9ce.png and /dev/null differ diff --git a/docs/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png b/docs/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png deleted file mode 100644 index 57dd8bf117..0000000000 Binary files a/docs/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png and /dev/null differ diff --git a/docs/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png b/docs/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png deleted file mode 100644 index 0b4a58384a..0000000000 Binary files a/docs/pics/0f9b9d2a-c5cc-4a3f-b138-2c1035950f39_200.png and /dev/null differ diff --git a/docs/pics/101550406418006.gif b/docs/pics/101550406418006.gif deleted file mode 100644 index df38020900..0000000000 Binary files a/docs/pics/101550406418006.gif and /dev/null differ diff --git a/docs/pics/101550414151983.gif b/docs/pics/101550414151983.gif deleted file mode 100644 index d52a911e5b..0000000000 Binary files a/docs/pics/101550414151983.gif and /dev/null differ diff --git a/docs/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png b/docs/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png deleted file mode 100644 index 35c2ddb1f8..0000000000 Binary files a/docs/pics/107a6a2b-f15b-4cad-bced-b7fb95258c9c.png and /dev/null differ diff --git a/docs/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg b/docs/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg deleted file mode 100644 index 043964ae9c..0000000000 Binary files a/docs/pics/10a6d3ee-04b2-46b4-b171-d596e5ab0f84.jpg and /dev/null differ diff --git a/docs/pics/111521118015898.gif b/docs/pics/111521118015898.gif deleted file mode 100644 index 5c31da1dcf..0000000000 Binary files a/docs/pics/111521118015898.gif and /dev/null differ diff --git a/docs/pics/111521118445538.gif b/docs/pics/111521118445538.gif deleted file mode 100644 index 323d129c59..0000000000 Binary files a/docs/pics/111521118445538.gif and /dev/null differ diff --git a/docs/pics/111521118483039.gif b/docs/pics/111521118483039.gif deleted file mode 100644 index a81124ddd0..0000000000 Binary files a/docs/pics/111521118483039.gif and /dev/null differ diff --git a/docs/pics/111521118640738.gif b/docs/pics/111521118640738.gif deleted file mode 100644 index 7a7b05a9db..0000000000 Binary files a/docs/pics/111521118640738.gif and /dev/null differ diff --git a/docs/pics/111521119203347.gif b/docs/pics/111521119203347.gif deleted file mode 100644 index 37cdb5a5f5..0000000000 Binary files a/docs/pics/111521119203347.gif and /dev/null differ diff --git a/docs/pics/111521119368714.gif b/docs/pics/111521119368714.gif deleted file mode 100644 index 216c3033f5..0000000000 Binary files a/docs/pics/111521119368714.gif and /dev/null differ diff --git a/docs/pics/111550407277293.gif b/docs/pics/111550407277293.gif deleted file mode 100644 index ce296a0562..0000000000 Binary files a/docs/pics/111550407277293.gif and /dev/null differ diff --git a/docs/pics/111550414182638.gif b/docs/pics/111550414182638.gif deleted file mode 100644 index 5d9c6f0013..0000000000 Binary files a/docs/pics/111550414182638.gif and /dev/null differ diff --git a/docs/pics/11236498-1417-46ce-a1b0-e10054256955.png b/docs/pics/11236498-1417-46ce-a1b0-e10054256955.png deleted file mode 100644 index 19cd15437a..0000000000 Binary files a/docs/pics/11236498-1417-46ce-a1b0-e10054256955.png and /dev/null differ diff --git a/docs/pics/11548741556940.gif b/docs/pics/11548741556940.gif deleted file mode 100644 index 25571c544c..0000000000 Binary files a/docs/pics/11548741556940.gif and /dev/null differ diff --git a/docs/pics/11548742010310.gif b/docs/pics/11548742010310.gif deleted file mode 100644 index 7bc747345d..0000000000 Binary files a/docs/pics/11548742010310.gif and /dev/null differ diff --git a/docs/pics/11548742157520.gif b/docs/pics/11548742157520.gif deleted file mode 100644 index 6fbe4bf26b..0000000000 Binary files a/docs/pics/11548742157520.gif and /dev/null differ diff --git a/docs/pics/11550465817827.gif b/docs/pics/11550465817827.gif deleted file mode 100644 index 5a718e7035..0000000000 Binary files a/docs/pics/11550465817827.gif and /dev/null differ diff --git a/docs/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg b/docs/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg deleted file mode 100644 index 1804cdb199..0000000000 Binary files a/docs/pics/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg and /dev/null differ diff --git a/docs/pics/11_200.png b/docs/pics/11_200.png deleted file mode 100644 index 429663222a..0000000000 Binary files a/docs/pics/11_200.png and /dev/null differ diff --git a/docs/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png b/docs/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png deleted file mode 100644 index 5368338645..0000000000 Binary files a/docs/pics/11a786f0-5e02-46a6-92f0-f302c9cf6ca3_200.png and /dev/null differ diff --git a/docs/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png b/docs/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png deleted file mode 100644 index b44fa99629..0000000000 Binary files a/docs/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png and /dev/null differ diff --git a/docs/pics/121550407878282.gif b/docs/pics/121550407878282.gif deleted file mode 100644 index 5a17a5538a..0000000000 Binary files a/docs/pics/121550407878282.gif and /dev/null differ diff --git a/docs/pics/131550414680831.gif b/docs/pics/131550414680831.gif deleted file mode 100644 index caa4d001f5..0000000000 Binary files a/docs/pics/131550414680831.gif and /dev/null differ diff --git a/docs/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png b/docs/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png deleted file mode 100644 index 10d2aab07d..0000000000 Binary files a/docs/pics/13783e94-b481-4aea-9fa2-9d1973abd47e_200.png and /dev/null differ diff --git a/docs/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png b/docs/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png deleted file mode 100644 index a5ac33caa3..0000000000 Binary files a/docs/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png and /dev/null differ diff --git a/docs/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png b/docs/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png deleted file mode 100644 index 27f1c9f152..0000000000 Binary files a/docs/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png and /dev/null differ diff --git a/docs/pics/141550414746389.gif b/docs/pics/141550414746389.gif deleted file mode 100644 index 17cb256226..0000000000 Binary files a/docs/pics/141550414746389.gif and /dev/null differ diff --git a/docs/pics/1492928105791_3.png b/docs/pics/1492928105791_3.png deleted file mode 100644 index d18fc1cae9..0000000000 Binary files a/docs/pics/1492928105791_3.png and /dev/null differ diff --git a/docs/pics/1492928416812_4.png b/docs/pics/1492928416812_4.png deleted file mode 100644 index a43a731b5e..0000000000 Binary files a/docs/pics/1492928416812_4.png and /dev/null differ diff --git a/docs/pics/1492929000361_5.png b/docs/pics/1492929000361_5.png deleted file mode 100644 index 919d1222cf..0000000000 Binary files a/docs/pics/1492929000361_5.png and /dev/null differ diff --git a/docs/pics/1492929444818_6.png b/docs/pics/1492929444818_6.png deleted file mode 100644 index 0aea3f9afe..0000000000 Binary files a/docs/pics/1492929444818_6.png and /dev/null differ diff --git a/docs/pics/1492929553651_7.png b/docs/pics/1492929553651_7.png deleted file mode 100644 index 13cf0b4eec..0000000000 Binary files a/docs/pics/1492929553651_7.png and /dev/null differ diff --git a/docs/pics/1492930243286_8.png b/docs/pics/1492930243286_8.png deleted file mode 100644 index 6ee721ff78..0000000000 Binary files a/docs/pics/1492930243286_8.png and /dev/null differ diff --git a/docs/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png b/docs/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png deleted file mode 100644 index 399846e104..0000000000 Binary files a/docs/pics/14ab3de0-0d48-4466-9ea7-90b9be822034_200.png and /dev/null differ diff --git a/docs/pics/14fe1e71-8518-458f-a220-116003061a83.png b/docs/pics/14fe1e71-8518-458f-a220-116003061a83.png deleted file mode 100644 index ec381029db..0000000000 Binary files a/docs/pics/14fe1e71-8518-458f-a220-116003061a83.png and /dev/null differ diff --git a/docs/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg b/docs/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg deleted file mode 100644 index cbba7f360c..0000000000 Binary files a/docs/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg and /dev/null differ diff --git a/docs/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg b/docs/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg deleted file mode 100644 index 94d1adc23d..0000000000 Binary files a/docs/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg and /dev/null differ diff --git a/docs/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png b/docs/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png deleted file mode 100644 index 85d93b2705..0000000000 Binary files a/docs/pics/15699a17-5a69-4fbe-852e-9d2b7cf05e80_200.png and /dev/null differ diff --git a/docs/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg b/docs/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg deleted file mode 100644 index a61ab275ca..0000000000 Binary files a/docs/pics/15b45dc6-27aa-4519-9194-f4acfa2b077f.jpg and /dev/null differ diff --git a/docs/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png b/docs/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png deleted file mode 100644 index 276969a70e..0000000000 Binary files a/docs/pics/17976404-95f5-480e-9cb4-250e6aa1d55f.png and /dev/null differ diff --git a/docs/pics/1818e141-8700-4026-99f7-900a545875f5.png b/docs/pics/1818e141-8700-4026-99f7-900a545875f5.png deleted file mode 100644 index 1523ad79dd..0000000000 Binary files a/docs/pics/1818e141-8700-4026-99f7-900a545875f5.png and /dev/null differ diff --git a/docs/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg b/docs/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg deleted file mode 100644 index 306d1c66e2..0000000000 Binary files a/docs/pics/181edd46-e640-472a-9119-a697de0d2a82.jpg and /dev/null differ diff --git a/docs/pics/1_200.png b/docs/pics/1_200.png deleted file mode 100644 index c6dc2bb569..0000000000 Binary files a/docs/pics/1_200.png and /dev/null differ diff --git a/docs/pics/1_2001550415765493.png b/docs/pics/1_2001550415765493.png deleted file mode 100644 index d1c091ca10..0000000000 Binary files a/docs/pics/1_2001550415765493.png and /dev/null differ diff --git a/docs/pics/1_2001550465428749.png b/docs/pics/1_2001550465428749.png deleted file mode 100644 index e21e3837c0..0000000000 Binary files a/docs/pics/1_2001550465428749.png and /dev/null differ diff --git a/docs/pics/1_2001550547261811.png b/docs/pics/1_2001550547261811.png deleted file mode 100644 index a3331e43f2..0000000000 Binary files a/docs/pics/1_2001550547261811.png and /dev/null differ diff --git a/docs/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png b/docs/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png deleted file mode 100644 index c459230500..0000000000 Binary files a/docs/pics/1a2f2998-d0da-41c8-8222-1fd95083a66b.png and /dev/null differ diff --git a/docs/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg b/docs/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg deleted file mode 100644 index 4809984f2b..0000000000 Binary files a/docs/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg and /dev/null differ diff --git a/docs/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png b/docs/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png deleted file mode 100644 index 590a4299ba..0000000000 Binary files a/docs/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png and /dev/null differ diff --git a/docs/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg b/docs/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg deleted file mode 100644 index 48a91211f9..0000000000 Binary files a/docs/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg and /dev/null differ diff --git a/docs/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg b/docs/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg deleted file mode 100644 index 60edac95f1..0000000000 Binary files a/docs/pics/1b718cd5-7b1e-496c-9133-2bfd12bb5f89.jpg and /dev/null differ diff --git a/docs/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png b/docs/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png deleted file mode 100644 index e4286077d3..0000000000 Binary files a/docs/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png and /dev/null differ diff --git a/docs/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png b/docs/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png deleted file mode 100644 index 35e992f75a..0000000000 Binary files a/docs/pics/1c4e8185-8153-46b6-bd5a-288b15feeae6.png and /dev/null differ diff --git a/docs/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png b/docs/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png deleted file mode 100644 index 535968032a..0000000000 Binary files a/docs/pics/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png and /dev/null differ diff --git a/docs/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg b/docs/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg deleted file mode 100644 index a9effcf782..0000000000 Binary files a/docs/pics/1d2719d5-8d60-4c9b-a4ad-b2df7c7615af.jpg and /dev/null differ diff --git a/docs/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png b/docs/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png deleted file mode 100644 index 68ab02d1e7..0000000000 Binary files a/docs/pics/1e74234e-d70b-411c-9333-226bcbb9c8f0.png and /dev/null differ diff --git a/docs/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg b/docs/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg deleted file mode 100644 index c1776a5c5a..0000000000 Binary files a/docs/pics/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg and /dev/null differ diff --git a/docs/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png b/docs/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png deleted file mode 100644 index 0f7d6660b7..0000000000 Binary files a/docs/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png and /dev/null differ diff --git a/docs/pics/2017-06-11-ca.png b/docs/pics/2017-06-11-ca.png deleted file mode 100644 index 550292c106..0000000000 Binary files a/docs/pics/2017-06-11-ca.png and /dev/null differ diff --git a/docs/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png b/docs/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png deleted file mode 100644 index 7da1a21f74..0000000000 Binary files a/docs/pics/20e61b68-effe-4a70-a7fd-58be23f9343a.png and /dev/null differ diff --git a/docs/pics/21550397584141.gif b/docs/pics/21550397584141.gif deleted file mode 100644 index df5c1792b0..0000000000 Binary files a/docs/pics/21550397584141.gif and /dev/null differ diff --git a/docs/pics/21550465890674.gif b/docs/pics/21550465890674.gif deleted file mode 100644 index 2ef91795b6..0000000000 Binary files a/docs/pics/21550465890674.gif and /dev/null differ diff --git a/docs/pics/220790c6-4377-4a2e-8686-58398afc8a18.png b/docs/pics/220790c6-4377-4a2e-8686-58398afc8a18.png deleted file mode 100644 index 7910525710..0000000000 Binary files a/docs/pics/220790c6-4377-4a2e-8686-58398afc8a18.png and /dev/null differ diff --git a/docs/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png b/docs/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png deleted file mode 100644 index 8d7dc09896..0000000000 Binary files a/docs/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png and /dev/null differ diff --git a/docs/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg b/docs/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg deleted file mode 100644 index 2ce6dd1faa..0000000000 Binary files a/docs/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg and /dev/null differ diff --git a/docs/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png b/docs/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png deleted file mode 100644 index 5fccbd1c48..0000000000 Binary files a/docs/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png and /dev/null differ diff --git a/docs/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg b/docs/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg deleted file mode 100644 index 92c11bfd91..0000000000 Binary files a/docs/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg and /dev/null differ diff --git a/docs/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg b/docs/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg deleted file mode 100644 index cf6ab00931..0000000000 Binary files a/docs/pics/26a7c9df-22f6-4df4-845a-745c053ab2e5.jpg and /dev/null differ diff --git a/docs/pics/2719067e-b299-4639-9065-bed6729dbf0b.png b/docs/pics/2719067e-b299-4639-9065-bed6729dbf0b.png deleted file mode 100644 index 95057e05ec..0000000000 Binary files a/docs/pics/2719067e-b299-4639-9065-bed6729dbf0b.png and /dev/null differ diff --git a/docs/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png b/docs/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png deleted file mode 100644 index 8de6367b8b..0000000000 Binary files a/docs/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png and /dev/null differ diff --git a/docs/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg b/docs/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg deleted file mode 100644 index c6de4ac5b9..0000000000 Binary files a/docs/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg and /dev/null differ diff --git a/docs/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg b/docs/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg deleted file mode 100644 index 72177f773e..0000000000 Binary files a/docs/pics/27fce0c6-8262-4d11-abb4-243faa2a2eef.jpg and /dev/null differ diff --git a/docs/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png b/docs/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png deleted file mode 100644 index 1aee414c9a..0000000000 Binary files a/docs/pics/27ff9548-edb6-4465-92c8-7e6386e0b185.png and /dev/null differ diff --git a/docs/pics/280f7728-594f-4811-a03a-fa8d32c013da.png b/docs/pics/280f7728-594f-4811-a03a-fa8d32c013da.png deleted file mode 100644 index 526b684733..0000000000 Binary files a/docs/pics/280f7728-594f-4811-a03a-fa8d32c013da.png and /dev/null differ diff --git a/docs/pics/2861e923-4862-4526-881c-15529279d49c.png b/docs/pics/2861e923-4862-4526-881c-15529279d49c.png deleted file mode 100644 index 6cc26c26bf..0000000000 Binary files a/docs/pics/2861e923-4862-4526-881c-15529279d49c.png and /dev/null differ diff --git a/docs/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg b/docs/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg deleted file mode 100644 index 38e0905b3f..0000000000 Binary files a/docs/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg and /dev/null differ diff --git a/docs/pics/293d2af9-de1d-403e-bed0-85d029383528.png b/docs/pics/293d2af9-de1d-403e-bed0-85d029383528.png deleted file mode 100644 index 36765e3261..0000000000 Binary files a/docs/pics/293d2af9-de1d-403e-bed0-85d029383528.png and /dev/null differ diff --git a/docs/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg b/docs/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg deleted file mode 100644 index 09ad339e36..0000000000 Binary files a/docs/pics/2959e455-e6cb-4461-aeb3-e319fe5c41db.jpg and /dev/null differ diff --git a/docs/pics/2_14_microkernelArchitecture.jpg b/docs/pics/2_14_microkernelArchitecture.jpg deleted file mode 100644 index 21c2a58fa4..0000000000 Binary files a/docs/pics/2_14_microkernelArchitecture.jpg and /dev/null differ diff --git a/docs/pics/2_200.png b/docs/pics/2_200.png deleted file mode 100644 index c292d4ef80..0000000000 Binary files a/docs/pics/2_200.png and /dev/null differ diff --git a/docs/pics/2_2001550426232419.png b/docs/pics/2_2001550426232419.png deleted file mode 100644 index 810892ec49..0000000000 Binary files a/docs/pics/2_2001550426232419.png and /dev/null differ diff --git a/docs/pics/2_2001550466182933.png b/docs/pics/2_2001550466182933.png deleted file mode 100644 index 79d1976259..0000000000 Binary files a/docs/pics/2_2001550466182933.png and /dev/null differ diff --git a/docs/pics/2_2001550547456403.png b/docs/pics/2_2001550547456403.png deleted file mode 100644 index c9e53fe0b4..0000000000 Binary files a/docs/pics/2_2001550547456403.png and /dev/null differ diff --git a/docs/pics/2_2001550810366269.png b/docs/pics/2_2001550810366269.png deleted file mode 100644 index 2d65f04639..0000000000 Binary files a/docs/pics/2_2001550810366269.png and /dev/null differ diff --git a/docs/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png b/docs/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png deleted file mode 100644 index a97e49a6b7..0000000000 Binary files a/docs/pics/2a8e1442-2381-4439-a83f-0312c8678b1f.png and /dev/null differ diff --git a/docs/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png b/docs/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png deleted file mode 100644 index 173ce970ba..0000000000 Binary files a/docs/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png and /dev/null differ diff --git a/docs/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg b/docs/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg deleted file mode 100644 index 983ddd7070..0000000000 Binary files a/docs/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg and /dev/null differ diff --git a/docs/pics/2d09a847-b854-439c-9198-b29c65810944.png b/docs/pics/2d09a847-b854-439c-9198-b29c65810944.png deleted file mode 100644 index 384f7ef0bc..0000000000 Binary files a/docs/pics/2d09a847-b854-439c-9198-b29c65810944.png and /dev/null differ diff --git a/docs/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg b/docs/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg deleted file mode 100644 index 5398511f00..0000000000 Binary files a/docs/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg and /dev/null differ diff --git a/docs/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png b/docs/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png deleted file mode 100644 index 701e37f291..0000000000 Binary files a/docs/pics/2e6c72f5-3b8e-4e32-b87b-9491322628fe.png and /dev/null differ diff --git a/docs/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg b/docs/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg deleted file mode 100644 index 5799fe392f..0000000000 Binary files a/docs/pics/2f237854-bb35-4c57-a7fe-ab2ab144f56e.jpg and /dev/null differ diff --git a/docs/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg b/docs/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg deleted file mode 100644 index 0983229b68..0000000000 Binary files a/docs/pics/303873db-0d11-4683-a43c-f319b7aef2b6.jpg and /dev/null differ diff --git a/docs/pics/3086c248-b552-499e-b101-9cffe5c2773e.png b/docs/pics/3086c248-b552-499e-b101-9cffe5c2773e.png deleted file mode 100644 index c23e85c959..0000000000 Binary files a/docs/pics/3086c248-b552-499e-b101-9cffe5c2773e.png and /dev/null differ diff --git a/docs/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg b/docs/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg deleted file mode 100644 index a805e7f95c..0000000000 Binary files a/docs/pics/3144015c-dcfb-47ac-94a5-bab3b78b0f14.jpg and /dev/null differ diff --git a/docs/pics/31550398353573.gif b/docs/pics/31550398353573.gif deleted file mode 100644 index f9008efe79..0000000000 Binary files a/docs/pics/31550398353573.gif and /dev/null differ diff --git a/docs/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png b/docs/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png deleted file mode 100644 index 3316254e98..0000000000 Binary files a/docs/pics/323ffd6c-8b54-4f3e-b361-555a6c8bf218.png and /dev/null differ diff --git a/docs/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg b/docs/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg deleted file mode 100644 index 9de5783ddb..0000000000 Binary files a/docs/pics/32b8374a-e822-4720-af0b-c0f485095ea2.jpg and /dev/null differ diff --git a/docs/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png b/docs/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png deleted file mode 100644 index 6e8383f35e..0000000000 Binary files a/docs/pics/33ac2b23-cb85-4e99-bc41-b7b7199fad1c.png and /dev/null differ diff --git a/docs/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png b/docs/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png deleted file mode 100644 index cb7e36811d..0000000000 Binary files a/docs/pics/348bc2db-582e-4aca-9f88-38c40e9a0e69.png and /dev/null differ diff --git a/docs/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png b/docs/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png deleted file mode 100644 index 76d45e1984..0000000000 Binary files a/docs/pics/3646544a-cb57-451d-9e03-d3c4f5e4434a.png and /dev/null differ diff --git a/docs/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png b/docs/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png deleted file mode 100644 index c74c59ef98..0000000000 Binary files a/docs/pics/37a72755-4890-4b42-9eab-b0084e0c54d9.png and /dev/null differ diff --git a/docs/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png b/docs/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png deleted file mode 100644 index 3b05b25b96..0000000000 Binary files a/docs/pics/37e79a32-95a9-4503-bdb1-159527e628b8.png and /dev/null differ diff --git a/docs/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png b/docs/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png deleted file mode 100644 index 5f5f5636d7..0000000000 Binary files a/docs/pics/386cd64f-7a9d-40e6-8c55-22b90ee2d258.png and /dev/null differ diff --git a/docs/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg b/docs/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg deleted file mode 100644 index 78eb732bf3..0000000000 Binary files a/docs/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg and /dev/null differ diff --git a/docs/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png b/docs/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png deleted file mode 100644 index b486ec0269..0000000000 Binary files a/docs/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png and /dev/null differ diff --git a/docs/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg b/docs/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg deleted file mode 100644 index 60c19f88b5..0000000000 Binary files a/docs/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg and /dev/null differ diff --git a/docs/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png b/docs/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png deleted file mode 100644 index 8e363e4784..0000000000 Binary files a/docs/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png and /dev/null differ diff --git a/docs/pics/3_200.png b/docs/pics/3_200.png deleted file mode 100644 index a35b29e480..0000000000 Binary files a/docs/pics/3_200.png and /dev/null differ diff --git a/docs/pics/3_2001550473624627.png b/docs/pics/3_2001550473624627.png deleted file mode 100644 index d8bc9db0c5..0000000000 Binary files a/docs/pics/3_2001550473624627.png and /dev/null differ diff --git a/docs/pics/3_2001550547558008.png b/docs/pics/3_2001550547558008.png deleted file mode 100644 index a42f6dc0c7..0000000000 Binary files a/docs/pics/3_2001550547558008.png and /dev/null differ diff --git a/docs/pics/3_2001550810442775.png b/docs/pics/3_2001550810442775.png deleted file mode 100644 index 3a29803682..0000000000 Binary files a/docs/pics/3_2001550810442775.png and /dev/null differ diff --git a/docs/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png b/docs/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png deleted file mode 100644 index 13f53b6a58..0000000000 Binary files a/docs/pics/3b49dd67-2c40-4b81-8ad2-7bbb1fe2fcbd.png and /dev/null differ diff --git a/docs/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png b/docs/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png deleted file mode 100644 index 9dc77733d3..0000000000 Binary files a/docs/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png and /dev/null differ diff --git a/docs/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png b/docs/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png deleted file mode 100644 index 2f39530c99..0000000000 Binary files a/docs/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png and /dev/null differ diff --git a/docs/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png b/docs/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png deleted file mode 100644 index 65bb211cd6..0000000000 Binary files a/docs/pics/3fa0a6cb-a0a4-490a-9a80-7f4888f2500c.png and /dev/null differ diff --git a/docs/pics/3fb5b255-b791-45b6-8754-325c8741855a.png b/docs/pics/3fb5b255-b791-45b6-8754-325c8741855a.png deleted file mode 100644 index 621245d7cf..0000000000 Binary files a/docs/pics/3fb5b255-b791-45b6-8754-325c8741855a.png and /dev/null differ diff --git a/docs/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png b/docs/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png deleted file mode 100644 index 266a4687f7..0000000000 Binary files a/docs/pics/3ff4f00a-2321-48fd-95f4-ce6001332151.png and /dev/null differ diff --git a/docs/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg b/docs/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg deleted file mode 100644 index 355cdf5730..0000000000 Binary files a/docs/pics/40f29839-fd56-4ed0-9353-39dfe6f0bba5.jpg and /dev/null differ diff --git a/docs/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png b/docs/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png deleted file mode 100644 index d623b1e3f2..0000000000 Binary files a/docs/pics/417cb02e-853d-4288-a36e-9161ded2c9fd_200.png and /dev/null differ diff --git a/docs/pics/420f4dc0-6c4b-486c-afea-274299014462.png b/docs/pics/420f4dc0-6c4b-486c-afea-274299014462.png deleted file mode 100644 index 257d73c685..0000000000 Binary files a/docs/pics/420f4dc0-6c4b-486c-afea-274299014462.png and /dev/null differ diff --git a/docs/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png b/docs/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png deleted file mode 100644 index 7b8c8c08ec..0000000000 Binary files a/docs/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png and /dev/null differ diff --git a/docs/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg b/docs/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg deleted file mode 100644 index 556c01ddfe..0000000000 Binary files a/docs/pics/43bf0957-0386-4c09-9ad7-e163c5b62559.jpg and /dev/null differ diff --git a/docs/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png b/docs/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png deleted file mode 100644 index 010278a2ec..0000000000 Binary files a/docs/pics/43f2cafa-3568-4a89-a895-4725666b94a6.png and /dev/null differ diff --git a/docs/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png b/docs/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png deleted file mode 100644 index be98ce69c1..0000000000 Binary files a/docs/pics/4583e24f-424b-4d50-8a14-2c38a1827d4a.png and /dev/null differ diff --git a/docs/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg b/docs/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg deleted file mode 100644 index 336ee59b68..0000000000 Binary files a/docs/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg and /dev/null differ diff --git a/docs/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png b/docs/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png deleted file mode 100644 index 6db68ca535..0000000000 Binary files a/docs/pics/4628274c-25b6-4053-97cf-d1239b44c43d.png and /dev/null differ diff --git a/docs/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png b/docs/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png deleted file mode 100644 index 0dcc870668..0000000000 Binary files a/docs/pics/46f66e88-e65a-4ad0-a060-3c63fe22947c.png and /dev/null differ diff --git a/docs/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png b/docs/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png deleted file mode 100644 index 83d5935956..0000000000 Binary files a/docs/pics/47358f87-bc4c-496f-9a90-8d696de94cee.png and /dev/null differ diff --git a/docs/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg b/docs/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg deleted file mode 100644 index 9af7bc09c0..0000000000 Binary files a/docs/pics/474e5579-38b1-47d2-8f76-a13ae086b039.jpg and /dev/null differ diff --git a/docs/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg b/docs/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg deleted file mode 100644 index f6ddd2c2ef..0000000000 Binary files a/docs/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg and /dev/null differ diff --git a/docs/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png b/docs/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png deleted file mode 100644 index f9f73fafda..0000000000 Binary files a/docs/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png and /dev/null differ diff --git a/docs/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png b/docs/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png deleted file mode 100644 index 9c6b68dc54..0000000000 Binary files a/docs/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png and /dev/null differ diff --git a/docs/pics/48b1d459-8832-4e92-938a-728aae730739.jpg b/docs/pics/48b1d459-8832-4e92-938a-728aae730739.jpg deleted file mode 100644 index 8adfea8264..0000000000 Binary files a/docs/pics/48b1d459-8832-4e92-938a-728aae730739.jpg and /dev/null differ diff --git a/docs/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png b/docs/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png deleted file mode 100644 index e853dc13cc..0000000000 Binary files a/docs/pics/49e53613-46f8-4308-9ee5-c09d6231552088893397.png and /dev/null differ diff --git a/docs/pics/49e53613-46f8-4308-9ee5-c09d62367577.png b/docs/pics/49e53613-46f8-4308-9ee5-c09d62367577.png deleted file mode 100644 index e853dc13cc..0000000000 Binary files a/docs/pics/49e53613-46f8-4308-9ee5-c09d62367577.png and /dev/null differ diff --git a/docs/pics/4_2001550473915641.png b/docs/pics/4_2001550473915641.png deleted file mode 100644 index f86ccece2e..0000000000 Binary files a/docs/pics/4_2001550473915641.png and /dev/null differ diff --git a/docs/pics/4_2001550547640585.png b/docs/pics/4_2001550547640585.png deleted file mode 100644 index d3f5c798ef..0000000000 Binary files a/docs/pics/4_2001550547640585.png and /dev/null differ diff --git a/docs/pics/4_2001550810732828.png b/docs/pics/4_2001550810732828.png deleted file mode 100644 index 134cfb6b3a..0000000000 Binary files a/docs/pics/4_2001550810732828.png and /dev/null differ diff --git a/docs/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png b/docs/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png deleted file mode 100644 index 25ac4f70b3..0000000000 Binary files a/docs/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png and /dev/null differ diff --git a/docs/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png b/docs/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png deleted file mode 100644 index a4a5e7b045..0000000000 Binary files a/docs/pics/4d741402-344d-4b7c-be01-e57184bcad0e.png and /dev/null differ diff --git a/docs/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png b/docs/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png deleted file mode 100644 index a5643118d0..0000000000 Binary files a/docs/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png and /dev/null differ diff --git a/docs/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg b/docs/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg deleted file mode 100644 index febf7a179e..0000000000 Binary files a/docs/pics/4f151e62-6160-47f1-9eff-47b1f4dea4e9.jpg and /dev/null differ diff --git a/docs/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png b/docs/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png deleted file mode 100644 index 649d16cd80..0000000000 Binary files a/docs/pics/4f48e806-f90b-4c09-a55f-ac0cd641c047.png and /dev/null differ diff --git a/docs/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg b/docs/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg deleted file mode 100644 index f81e570723..0000000000 Binary files a/docs/pics/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg and /dev/null differ diff --git a/docs/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg b/docs/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg deleted file mode 100644 index 4a3f798bca..0000000000 Binary files a/docs/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg and /dev/null differ diff --git a/docs/pics/51550399426594.gif b/docs/pics/51550399426594.gif deleted file mode 100644 index 6e93179134..0000000000 Binary files a/docs/pics/51550399426594.gif and /dev/null differ diff --git a/docs/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg b/docs/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg deleted file mode 100644 index 595cdc584e..0000000000 Binary files a/docs/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg and /dev/null differ diff --git a/docs/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png b/docs/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png deleted file mode 100644 index d49b1727d8..0000000000 Binary files a/docs/pics/51fb761d-8ce0-4472-92ff-2f227ac7888a.png and /dev/null differ diff --git a/docs/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg b/docs/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg deleted file mode 100644 index 32e1f0545d..0000000000 Binary files a/docs/pics/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg and /dev/null differ diff --git a/docs/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg b/docs/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg deleted file mode 100644 index a449f38cfb..0000000000 Binary files a/docs/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg and /dev/null differ diff --git a/docs/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png b/docs/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png deleted file mode 100644 index 29bbc9de94..0000000000 Binary files a/docs/pics/536c6dfd-305a-4b95-b12c-28ca5e8aa043.png and /dev/null differ diff --git a/docs/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png b/docs/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png deleted file mode 100644 index 2666f9c209..0000000000 Binary files a/docs/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png and /dev/null differ diff --git a/docs/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg b/docs/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg deleted file mode 100644 index 0ffa2a5bc4..0000000000 Binary files a/docs/pics/54e6d499-80df-488e-aa7e-081766c41538.jpg and /dev/null differ diff --git a/docs/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png b/docs/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png deleted file mode 100644 index ad60a7e02c..0000000000 Binary files a/docs/pics/54f1e052-0596-4b5e-833c-e80d75bf3f9b.png and /dev/null differ diff --git a/docs/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png b/docs/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png deleted file mode 100644 index 525b323af1..0000000000 Binary files a/docs/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png and /dev/null differ diff --git a/docs/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg b/docs/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg deleted file mode 100644 index 28fad66e28..0000000000 Binary files a/docs/pics/56074abd-39d6-42a7-bed8-a360e81a82d8.jpg and /dev/null differ diff --git a/docs/pics/562f2844-d77c-40e0-887a-28a7128abd42.png b/docs/pics/562f2844-d77c-40e0-887a-28a7128abd42.png deleted file mode 100644 index b74f69b79a..0000000000 Binary files a/docs/pics/562f2844-d77c-40e0-887a-28a7128abd42.png and /dev/null differ diff --git a/docs/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg b/docs/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg deleted file mode 100644 index 5feb11d455..0000000000 Binary files a/docs/pics/584b05d4-a101-4bde-a758-f5388cb843c8.jpg and /dev/null differ diff --git a/docs/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg b/docs/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg deleted file mode 100644 index a695be3a4e..0000000000 Binary files a/docs/pics/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg and /dev/null differ diff --git a/docs/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg b/docs/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg deleted file mode 100644 index 62b39e4b00..0000000000 Binary files a/docs/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg and /dev/null differ diff --git a/docs/pics/5_200.png b/docs/pics/5_200.png deleted file mode 100644 index f2f98c4323..0000000000 Binary files a/docs/pics/5_200.png and /dev/null differ diff --git a/docs/pics/5_2001550474110029.png b/docs/pics/5_2001550474110029.png deleted file mode 100644 index 858a456f90..0000000000 Binary files a/docs/pics/5_2001550474110029.png and /dev/null differ diff --git a/docs/pics/5_2001550810982206.png b/docs/pics/5_2001550810982206.png deleted file mode 100644 index f2f98c4323..0000000000 Binary files a/docs/pics/5_2001550810982206.png and /dev/null differ diff --git a/docs/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg b/docs/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg deleted file mode 100644 index ba83d34211..0000000000 Binary files a/docs/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg and /dev/null differ diff --git a/docs/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png b/docs/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png deleted file mode 100644 index 110bea3581..0000000000 Binary files a/docs/pics/5d4a5181-65fb-4bf2-a9c6-899cab534b44.png and /dev/null differ diff --git a/docs/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg b/docs/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg deleted file mode 100644 index f2a6c03ae0..0000000000 Binary files a/docs/pics/5e6e05d6-1028-4f5c-b9bd-1a40b90d6070.jpg and /dev/null differ diff --git a/docs/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png b/docs/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png deleted file mode 100644 index a3ea0a24dd..0000000000 Binary files a/docs/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png and /dev/null differ diff --git a/docs/pics/600e9c75-5033-4dad-ae2b-930957db638e.png b/docs/pics/600e9c75-5033-4dad-ae2b-930957db638e.png deleted file mode 100644 index bf0834d538..0000000000 Binary files a/docs/pics/600e9c75-5033-4dad-ae2b-930957db638e.png and /dev/null differ diff --git a/docs/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png b/docs/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png deleted file mode 100644 index 900ee96329..0000000000 Binary files a/docs/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png and /dev/null differ diff --git a/docs/pics/61550402057509.gif b/docs/pics/61550402057509.gif deleted file mode 100644 index 7ba6f2807c..0000000000 Binary files a/docs/pics/61550402057509.gif and /dev/null differ diff --git a/docs/pics/61942711-45a0-4e11-bbc9-434e31436f33.png b/docs/pics/61942711-45a0-4e11-bbc9-434e31436f33.png deleted file mode 100644 index 8f093ef638..0000000000 Binary files a/docs/pics/61942711-45a0-4e11-bbc9-434e31436f33.png and /dev/null differ diff --git a/docs/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png b/docs/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png deleted file mode 100644 index 14d98b8c67..0000000000 Binary files a/docs/pics/61d39d7a-c566-40dd-91ba-c6abaefa1a24.png and /dev/null differ diff --git a/docs/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png b/docs/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png deleted file mode 100644 index 3a41cdce53..0000000000 Binary files a/docs/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png and /dev/null differ diff --git a/docs/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg b/docs/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg deleted file mode 100644 index 90e9065cde..0000000000 Binary files a/docs/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg and /dev/null differ diff --git a/docs/pics/63010737-2cb4-48f3-999f-09194481b227.png b/docs/pics/63010737-2cb4-48f3-999f-09194481b227.png deleted file mode 100644 index c9d17851ce..0000000000 Binary files a/docs/pics/63010737-2cb4-48f3-999f-09194481b227.png and /dev/null differ diff --git a/docs/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg b/docs/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg deleted file mode 100644 index d647162161..0000000000 Binary files a/docs/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg and /dev/null differ diff --git a/docs/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png b/docs/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png deleted file mode 100644 index 053a3dc69b..0000000000 Binary files a/docs/pics/6539b9a4-2b24-4d10-8c94-2eb5aba1e296.png and /dev/null differ diff --git a/docs/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png b/docs/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png deleted file mode 100644 index f488859e0e..0000000000 Binary files a/docs/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png and /dev/null differ diff --git a/docs/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg b/docs/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg deleted file mode 100644 index eec226c5f0..0000000000 Binary files a/docs/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg and /dev/null differ diff --git a/docs/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg b/docs/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg deleted file mode 100644 index fc86a2360a..0000000000 Binary files a/docs/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg and /dev/null differ diff --git a/docs/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg b/docs/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg deleted file mode 100644 index 693d7b979d..0000000000 Binary files a/docs/pics/6646db4a-7f43-45e4-96ff-0891a57a9ade.jpg and /dev/null differ diff --git a/docs/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg b/docs/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg deleted file mode 100644 index 48f3136c0f..0000000000 Binary files a/docs/pics/67173c9f-ac87-496a-bd0a-0b1a5cfa735a.jpg and /dev/null differ diff --git a/docs/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg b/docs/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg deleted file mode 100644 index 7035f00107..0000000000 Binary files a/docs/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg and /dev/null differ diff --git a/docs/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png b/docs/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png deleted file mode 100644 index b63baeb210..0000000000 Binary files a/docs/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png and /dev/null differ diff --git a/docs/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png b/docs/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png deleted file mode 100644 index 39d7ec60fa..0000000000 Binary files a/docs/pics/68778c1b-15ab-4826-99c0-3b4fd38cb9e9.png and /dev/null differ diff --git a/docs/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg b/docs/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg deleted file mode 100644 index d82f46eb69..0000000000 Binary files a/docs/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg and /dev/null differ diff --git a/docs/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png b/docs/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png deleted file mode 100644 index b61e2174cd..0000000000 Binary files a/docs/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png and /dev/null differ diff --git a/docs/pics/6_2001550474388460.png b/docs/pics/6_2001550474388460.png deleted file mode 100644 index dadfd8fcc1..0000000000 Binary files a/docs/pics/6_2001550474388460.png and /dev/null differ diff --git a/docs/pics/6_2001550476096035.png b/docs/pics/6_2001550476096035.png deleted file mode 100644 index bbf8dfd271..0000000000 Binary files a/docs/pics/6_2001550476096035.png and /dev/null differ diff --git a/docs/pics/6_2001550811175246.png b/docs/pics/6_2001550811175246.png deleted file mode 100644 index 5d124af29a..0000000000 Binary files a/docs/pics/6_2001550811175246.png and /dev/null differ diff --git a/docs/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg b/docs/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg deleted file mode 100644 index 48b1bd5db8..0000000000 Binary files a/docs/pics/6aee49d3-f6c6-4d14-a81a-080c290de875.jpg and /dev/null differ diff --git a/docs/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png b/docs/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png deleted file mode 100644 index 5b9202f6fd..0000000000 Binary files a/docs/pics/6c0cf1e8-b03f-4eff-9b1a-ab262e0c7866.png and /dev/null differ diff --git a/docs/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png b/docs/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png deleted file mode 100644 index c951760fef..0000000000 Binary files a/docs/pics/6dd28bfc-6ef7-47cb-af50-a681ebc1bbaa.png and /dev/null differ diff --git a/docs/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png b/docs/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png deleted file mode 100644 index a830a7d465..0000000000 Binary files a/docs/pics/6f1938e5-fc49-4e7b-a383-2b46f0942d70_200.png and /dev/null differ diff --git a/docs/pics/71550414107576.gif b/docs/pics/71550414107576.gif deleted file mode 100644 index fad8851319..0000000000 Binary files a/docs/pics/71550414107576.gif and /dev/null differ diff --git a/docs/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png b/docs/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png deleted file mode 100644 index 4e99159f15..0000000000 Binary files a/docs/pics/71f61bc3-582d-4c27-8bdd-dc7fb135bf8f.png and /dev/null differ diff --git a/docs/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png b/docs/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png deleted file mode 100644 index 5299728f6b..0000000000 Binary files a/docs/pics/72f0ff69-138d-4e54-b7ac-ebe025d978dc.png and /dev/null differ diff --git a/docs/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg b/docs/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg deleted file mode 100644 index dda05f945b..0000000000 Binary files a/docs/pics/731a5e8f-a2c2-43ff-b8dd-6aeb9fffbe26.jpg and /dev/null differ diff --git a/docs/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg b/docs/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg deleted file mode 100644 index b045287d5c..0000000000 Binary files a/docs/pics/73846795-675b-496b-88b0-d44233fdcfba.jpg and /dev/null differ diff --git a/docs/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg b/docs/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg deleted file mode 100644 index 5b905db167..0000000000 Binary files a/docs/pics/73c72e75-193c-4092-aa43-b9c6703c4a22.jpg and /dev/null differ diff --git a/docs/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png b/docs/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png deleted file mode 100644 index 837f995a5c..0000000000 Binary files a/docs/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png and /dev/null differ diff --git a/docs/pics/75996367-adc2-4be1-a92a-09cc4347638e.png b/docs/pics/75996367-adc2-4be1-a92a-09cc4347638e.png deleted file mode 100644 index ec381029db..0000000000 Binary files a/docs/pics/75996367-adc2-4be1-a92a-09cc4347638e.png and /dev/null differ diff --git a/docs/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png b/docs/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png deleted file mode 100644 index e8a69bff48..0000000000 Binary files a/docs/pics/766aedd0-1b00-4065-aa2b-7d31138df84f.png and /dev/null differ diff --git a/docs/pics/76a49594323247f21c9b3a69945445ee.png b/docs/pics/76a49594323247f21c9b3a69945445ee.png deleted file mode 100644 index 788ba0b121..0000000000 Binary files a/docs/pics/76a49594323247f21c9b3a69945445ee.png and /dev/null differ diff --git a/docs/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg b/docs/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg deleted file mode 100644 index 598ed2ed62..0000000000 Binary files a/docs/pics/76c7597a-8316-460d-b8fb-9752c4c43947.jpg and /dev/null differ diff --git a/docs/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg b/docs/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg deleted file mode 100644 index 642aba6bd4..0000000000 Binary files a/docs/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg and /dev/null differ diff --git a/docs/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png b/docs/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png deleted file mode 100644 index 6f8a3c52df..0000000000 Binary files a/docs/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png and /dev/null differ diff --git a/docs/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png b/docs/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png deleted file mode 100644 index d146ec06cf..0000000000 Binary files a/docs/pics/7935be3d-c2b3-4213-90c9-1e68ec4ac4e7.png and /dev/null differ diff --git a/docs/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png b/docs/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png deleted file mode 100644 index 7adddd918e..0000000000 Binary files a/docs/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png and /dev/null differ diff --git a/docs/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png b/docs/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png deleted file mode 100644 index d2a300049b..0000000000 Binary files a/docs/pics/79b12431-6d9d-4a7d-985b-1b79bc5bf5fb.png and /dev/null differ diff --git a/docs/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png b/docs/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png deleted file mode 100644 index 4938fc5865..0000000000 Binary files a/docs/pics/79e9a938-43e2-4c5a-8de9-fe55522a14c9.png and /dev/null differ diff --git a/docs/pics/7_2001550475133282.png b/docs/pics/7_2001550475133282.png deleted file mode 100644 index 884eca8319..0000000000 Binary files a/docs/pics/7_2001550475133282.png and /dev/null differ diff --git a/docs/pics/7_2001550811502556.png b/docs/pics/7_2001550811502556.png deleted file mode 100644 index 41da48f593..0000000000 Binary files a/docs/pics/7_2001550811502556.png and /dev/null differ diff --git a/docs/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg b/docs/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg deleted file mode 100644 index a048d09419..0000000000 Binary files a/docs/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg and /dev/null differ diff --git a/docs/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg b/docs/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg deleted file mode 100644 index 8d40765ea0..0000000000 Binary files a/docs/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg and /dev/null differ diff --git a/docs/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png b/docs/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png deleted file mode 100644 index 9308ecb618..0000000000 Binary files a/docs/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png and /dev/null differ diff --git a/docs/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png b/docs/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png deleted file mode 100644 index 21aafd0aa6..0000000000 Binary files a/docs/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png and /dev/null differ diff --git a/docs/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png b/docs/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png deleted file mode 100644 index 711fb45b0d..0000000000 Binary files a/docs/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png and /dev/null differ diff --git a/docs/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png b/docs/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png deleted file mode 100644 index 8b5ce20453..0000000000 Binary files a/docs/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png and /dev/null differ diff --git a/docs/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png b/docs/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png deleted file mode 100644 index cf1868b606..0000000000 Binary files a/docs/pics/7c5bcdbf-e656-4b7c-be82-b247a3589ed5_200.png and /dev/null differ diff --git a/docs/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg b/docs/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg deleted file mode 100644 index 7dcb6d1c75..0000000000 Binary files a/docs/pics/7c98e1b6-c446-4cde-8513-5c11b9f52aea.jpg and /dev/null differ diff --git a/docs/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png b/docs/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png deleted file mode 100644 index c90982c240..0000000000 Binary files a/docs/pics/7de17ad2-de6e-4496-adaf-b8bc7b2f7f7b.png and /dev/null differ diff --git a/docs/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png b/docs/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png deleted file mode 100644 index 56ca814b67..0000000000 Binary files a/docs/pics/7e6cb8fc-1b82-4135-8b03-ebcfc546d94c.png and /dev/null differ diff --git a/docs/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png b/docs/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png deleted file mode 100644 index 0741f44ee2..0000000000 Binary files a/docs/pics/7e82ce01-2afb-4c15-b720-b81049c875c2_200.png and /dev/null differ diff --git a/docs/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png b/docs/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png deleted file mode 100644 index 66e26e808a..0000000000 Binary files a/docs/pics/7e873b60-44dc-4911-b080-defd5b8f0b49.png and /dev/null differ diff --git a/docs/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg b/docs/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg deleted file mode 100644 index 5753388eeb..0000000000 Binary files a/docs/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg and /dev/null differ diff --git a/docs/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png b/docs/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png deleted file mode 100644 index 57e71b9c7b..0000000000 Binary files a/docs/pics/7f38a583-2f2e-4738-97af-510e6fb403a7.png and /dev/null differ diff --git a/docs/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg b/docs/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg deleted file mode 100644 index aad341fd41..0000000000 Binary files a/docs/pics/7f403a7a-5228-42c0-af1c-b21635c77016.jpg and /dev/null differ diff --git a/docs/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png b/docs/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png deleted file mode 100644 index b636edf5af..0000000000 Binary files a/docs/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png and /dev/null differ diff --git a/docs/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png b/docs/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png deleted file mode 100644 index ac453eeb7c..0000000000 Binary files a/docs/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png and /dev/null differ diff --git a/docs/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg b/docs/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg deleted file mode 100644 index 7491217dc8..0000000000 Binary files a/docs/pics/8036ba3d-8de9-44aa-bf5d-1f8ca18ae89b.jpg and /dev/null differ diff --git a/docs/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png b/docs/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png deleted file mode 100644 index 382cc19b1c..0000000000 Binary files a/docs/pics/805812fa-6ab5-4b8f-a0aa-3bdcadaa829d.png and /dev/null differ diff --git a/docs/pics/80646c77-1f32-484c-810e-af80ce00f902.png b/docs/pics/80646c77-1f32-484c-810e-af80ce00f902.png deleted file mode 100644 index de3f5298d9..0000000000 Binary files a/docs/pics/80646c77-1f32-484c-810e-af80ce00f902.png and /dev/null differ diff --git a/docs/pics/80804f52-8815-4096-b506-48eef3eed5c6.png b/docs/pics/80804f52-8815-4096-b506-48eef3eed5c6.png deleted file mode 100644 index 06c8760b9d..0000000000 Binary files a/docs/pics/80804f52-8815-4096-b506-48eef3eed5c6.png and /dev/null differ diff --git a/docs/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg b/docs/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg deleted file mode 100644 index 5f34004b11..0000000000 Binary files a/docs/pics/8089a19a-6239-47a0-bf69-54203117d4dc.jpg and /dev/null differ diff --git a/docs/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg b/docs/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg deleted file mode 100644 index 5446f103d0..0000000000 Binary files a/docs/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg and /dev/null differ diff --git a/docs/pics/81550405360028.gif b/docs/pics/81550405360028.gif deleted file mode 100644 index 7960c782a7..0000000000 Binary files a/docs/pics/81550405360028.gif and /dev/null differ diff --git a/docs/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png b/docs/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png deleted file mode 100644 index 2fa695823e..0000000000 Binary files a/docs/pics/8162aebb-8fd2-4620-b771-e65751ba7e41.png and /dev/null differ diff --git a/docs/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg b/docs/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg deleted file mode 100644 index 2b510c4365..0000000000 Binary files a/docs/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg and /dev/null differ diff --git a/docs/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg b/docs/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg deleted file mode 100644 index cb83edeaf3..0000000000 Binary files a/docs/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg and /dev/null differ diff --git a/docs/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png b/docs/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png deleted file mode 100644 index be8ab73787..0000000000 Binary files a/docs/pics/836a4eaf-4798-4e48-b52a-a3dab9435ace.png and /dev/null differ diff --git a/docs/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png b/docs/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png deleted file mode 100644 index 891a9a376a..0000000000 Binary files a/docs/pics/839207f5-52fd-4516-9370-956dcdf2c2b5.png and /dev/null differ diff --git a/docs/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png b/docs/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png deleted file mode 100644 index 23fc9fbd4b..0000000000 Binary files a/docs/pics/83e9c5ed-35a1-41fd-b0dd-ce571969b5f3_200.png and /dev/null differ diff --git a/docs/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png b/docs/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png deleted file mode 100644 index 5c0b076bb0..0000000000 Binary files a/docs/pics/8433fbb2-c35c-45ef-831d-e3ca42aebd51.png and /dev/null differ diff --git a/docs/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg b/docs/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg deleted file mode 100644 index c68fd3ef92..0000000000 Binary files a/docs/pics/847b9ba1-b3cd-4e52-aa72-dee0222ae09f.jpg and /dev/null differ diff --git a/docs/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png b/docs/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png deleted file mode 100644 index 5423ac8596..0000000000 Binary files a/docs/pics/84d496d7-54b0-4a9b-9499-ce232057e499_200.png and /dev/null differ diff --git a/docs/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg b/docs/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg deleted file mode 100644 index 0abcf7d6eb..0000000000 Binary files a/docs/pics/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg and /dev/null differ diff --git a/docs/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png b/docs/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png deleted file mode 100644 index f77ff34664..0000000000 Binary files a/docs/pics/8587132a-021d-4f1f-a8ec-5a9daa7157a7.png and /dev/null differ diff --git a/docs/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg b/docs/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg deleted file mode 100644 index 4b91119397..0000000000 Binary files a/docs/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg and /dev/null differ diff --git a/docs/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png b/docs/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png deleted file mode 100644 index 72cc988f12..0000000000 Binary files a/docs/pics/864bfa7d-1149-420c-a752-f9b3d4e782ec.png and /dev/null differ diff --git a/docs/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png b/docs/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png deleted file mode 100644 index 9eda1deb5f..0000000000 Binary files a/docs/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png and /dev/null differ diff --git a/docs/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png b/docs/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png deleted file mode 100644 index 26cb0153f2..0000000000 Binary files a/docs/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png and /dev/null differ diff --git a/docs/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png b/docs/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png deleted file mode 100644 index 56f83eafa3..0000000000 Binary files a/docs/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png and /dev/null differ diff --git a/docs/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png b/docs/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png deleted file mode 100644 index 14d98b8c67..0000000000 Binary files a/docs/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png and /dev/null differ diff --git a/docs/pics/8_200.png b/docs/pics/8_200.png deleted file mode 100644 index af9d663a58..0000000000 Binary files a/docs/pics/8_200.png and /dev/null differ diff --git a/docs/pics/8_2001550475451664.png b/docs/pics/8_2001550475451664.png deleted file mode 100644 index 772df79c8d..0000000000 Binary files a/docs/pics/8_2001550475451664.png and /dev/null differ diff --git a/docs/pics/8_2001550812038190.png b/docs/pics/8_2001550812038190.png deleted file mode 100644 index 966bd4e220..0000000000 Binary files a/docs/pics/8_2001550812038190.png and /dev/null differ diff --git a/docs/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png b/docs/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png deleted file mode 100644 index 31ae5e0c92..0000000000 Binary files a/docs/pics/8ae4550b-f0cb-4e4d-9e2b-c550538bf230.png and /dev/null differ diff --git a/docs/pics/8b798007-e0fb-420c-b981-ead215692417.jpg b/docs/pics/8b798007-e0fb-420c-b981-ead215692417.jpg deleted file mode 100644 index dd21813321..0000000000 Binary files a/docs/pics/8b798007-e0fb-420c-b981-ead215692417.jpg and /dev/null differ diff --git a/docs/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png b/docs/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png deleted file mode 100644 index ff810e91e7..0000000000 Binary files a/docs/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png and /dev/null differ diff --git a/docs/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png b/docs/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png deleted file mode 100644 index c90982c240..0000000000 Binary files a/docs/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png and /dev/null differ diff --git a/docs/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png b/docs/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png deleted file mode 100644 index 034205a83f..0000000000 Binary files a/docs/pics/8f0cc500-5994-4c7a-91a9-62885d658662.png and /dev/null differ diff --git a/docs/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png b/docs/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png deleted file mode 100644 index a8e36ee04f..0000000000 Binary files a/docs/pics/8f3b9519-d705-48fe-87ad-2e4052fc81d2.png and /dev/null differ diff --git a/docs/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png b/docs/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png deleted file mode 100644 index 21b8a93048..0000000000 Binary files a/docs/pics/8f6f9dc9-9ecd-47c8-b50e-2814f0219056.png and /dev/null differ diff --git a/docs/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg b/docs/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg deleted file mode 100644 index a9e33df46a..0000000000 Binary files a/docs/pics/8fdbb8f5-2cf8-41dc-b5f1-99d98abb52d9.jpg and /dev/null differ diff --git a/docs/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg b/docs/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg deleted file mode 100644 index 7b2d0143b4..0000000000 Binary files a/docs/pics/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg and /dev/null differ diff --git a/docs/pics/910f613f-514f-4534-87dd-9b4699d59d31.png b/docs/pics/910f613f-514f-4534-87dd-9b4699d59d31.png deleted file mode 100644 index 28362d4767..0000000000 Binary files a/docs/pics/910f613f-514f-4534-87dd-9b4699d59d31.png and /dev/null differ diff --git a/docs/pics/9110c1a0-8a54-4145-a814-2477d0128114.png b/docs/pics/9110c1a0-8a54-4145-a814-2477d0128114.png deleted file mode 100644 index 68aa9f9845..0000000000 Binary files a/docs/pics/9110c1a0-8a54-4145-a814-2477d0128114.png and /dev/null differ diff --git a/docs/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg b/docs/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg deleted file mode 100644 index e30c690879..0000000000 Binary files a/docs/pics/912a7886-fb1d-4a05-902d-ab17ea17007f.jpg and /dev/null differ diff --git a/docs/pics/91550405374894.gif b/docs/pics/91550405374894.gif deleted file mode 100644 index 2409a299a4..0000000000 Binary files a/docs/pics/91550405374894.gif and /dev/null differ diff --git a/docs/pics/91550414131331.gif b/docs/pics/91550414131331.gif deleted file mode 100644 index f81aa26409..0000000000 Binary files a/docs/pics/91550414131331.gif and /dev/null differ diff --git a/docs/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png b/docs/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png deleted file mode 100644 index dd90699b63..0000000000 Binary files a/docs/pics/91aa7c29-438f-4fcc-8c63-2a75899139de.png and /dev/null differ diff --git a/docs/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg b/docs/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg deleted file mode 100644 index 5bb29598b2..0000000000 Binary files a/docs/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg and /dev/null differ diff --git a/docs/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg b/docs/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg deleted file mode 100644 index 2a736557b7..0000000000 Binary files a/docs/pics/92691356-4f7a-46ec-9921-13055d3dcb12.jpg and /dev/null differ diff --git a/docs/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png b/docs/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png deleted file mode 100644 index 9064fe0e34..0000000000 Binary files a/docs/pics/938fc386-0340-47b5-aeaa-6993e3bb4fc0.png and /dev/null differ diff --git a/docs/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg b/docs/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg deleted file mode 100644 index 5c53d38bb1..0000000000 Binary files a/docs/pics/94414cd3-5db9-4aca-a2af-539140955c62.jpg and /dev/null differ diff --git a/docs/pics/952afa9a-458b-44ce-bba9-463e60162945.png b/docs/pics/952afa9a-458b-44ce-bba9-463e60162945.png deleted file mode 100644 index db93c1ac8b..0000000000 Binary files a/docs/pics/952afa9a-458b-44ce-bba9-463e60162945.png and /dev/null differ diff --git a/docs/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png b/docs/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png deleted file mode 100644 index 4983142a97..0000000000 Binary files a/docs/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png and /dev/null differ diff --git a/docs/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg b/docs/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg deleted file mode 100644 index da95c5d8f5..0000000000 Binary files a/docs/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg and /dev/null differ diff --git a/docs/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg b/docs/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg deleted file mode 100644 index 97104ded98..0000000000 Binary files a/docs/pics/963fac12-4eac-4922-8a5f-f262c11f80fb.jpg and /dev/null differ diff --git a/docs/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg b/docs/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg deleted file mode 100644 index c4e77ba196..0000000000 Binary files a/docs/pics/96706658-b3f8-4f32-8eb3-dcb7fc8d5381.jpg and /dev/null differ diff --git a/docs/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg b/docs/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg deleted file mode 100644 index 8b907d6119..0000000000 Binary files a/docs/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg and /dev/null differ diff --git a/docs/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png b/docs/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png deleted file mode 100644 index 7d87099038..0000000000 Binary files a/docs/pics/99208bd0-1454-4618-9969-0c2deb8bba0f.png and /dev/null differ diff --git a/docs/pics/9_200.png b/docs/pics/9_200.png deleted file mode 100644 index 4cf0f56d7c..0000000000 Binary files a/docs/pics/9_200.png and /dev/null differ diff --git a/docs/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg b/docs/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg deleted file mode 100644 index 77c547801f..0000000000 Binary files a/docs/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg and /dev/null differ diff --git a/docs/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png b/docs/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png deleted file mode 100644 index c7f8713859..0000000000 Binary files a/docs/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png and /dev/null differ diff --git a/docs/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg b/docs/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg deleted file mode 100644 index b985fdd887..0000000000 Binary files a/docs/pics/9bae24e9-d2f5-4e1d-af42-1a868a5847cf.jpg and /dev/null differ diff --git a/docs/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png b/docs/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png deleted file mode 100644 index 26ac536e06..0000000000 Binary files a/docs/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png and /dev/null differ diff --git a/docs/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png b/docs/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png deleted file mode 100644 index f726a4c295..0000000000 Binary files a/docs/pics/9bd12b89-f99c-49f4-ae45-410f76a713d6.png and /dev/null differ diff --git a/docs/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png b/docs/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png deleted file mode 100644 index 89c84a7353..0000000000 Binary files a/docs/pics/9c60793f-5e7f-453b-8413-35890c24abc4.png and /dev/null differ diff --git a/docs/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png b/docs/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png deleted file mode 100644 index 49da824e23..0000000000 Binary files a/docs/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png and /dev/null differ diff --git a/docs/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png b/docs/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png deleted file mode 100644 index 34a8f664b3..0000000000 Binary files a/docs/pics/9d0a637c-6a8f-4f5a-99b9-fdcfa26793ff.png and /dev/null differ diff --git a/docs/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg b/docs/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg deleted file mode 100644 index 9f4f5f0435..0000000000 Binary files a/docs/pics/9d3c6bfb-4c4c-4b77-9410-56b3f82106d1.jpg and /dev/null differ diff --git a/docs/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg b/docs/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg deleted file mode 100644 index 15d4b3c8a6..0000000000 Binary files a/docs/pics/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg and /dev/null differ diff --git a/docs/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png b/docs/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png deleted file mode 100644 index ba045e2076..0000000000 Binary files a/docs/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png and /dev/null differ diff --git a/docs/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg b/docs/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg deleted file mode 100644 index 2ab8530ac2..0000000000 Binary files a/docs/pics/9ffb82ad-9b7f-44df-b93f-fc8b3f4093a6.jpg and /dev/null differ diff --git a/docs/pics/BSD_disk.png b/docs/pics/BSD_disk.png deleted file mode 100644 index 48b0e0e82c..0000000000 Binary files a/docs/pics/BSD_disk.png and /dev/null differ diff --git a/docs/pics/ClienteServidorSockets1521731145260.jpg b/docs/pics/ClienteServidorSockets1521731145260.jpg deleted file mode 100644 index 18976fb392..0000000000 Binary files a/docs/pics/ClienteServidorSockets1521731145260.jpg and /dev/null differ diff --git a/docs/pics/CountdownLatch.png b/docs/pics/CountdownLatch.png deleted file mode 100644 index 1a581bbf34..0000000000 Binary files a/docs/pics/CountdownLatch.png and /dev/null differ diff --git a/docs/pics/CyclicBarrier.png b/docs/pics/CyclicBarrier.png deleted file mode 100644 index 5fe5b88b80..0000000000 Binary files a/docs/pics/CyclicBarrier.png and /dev/null differ diff --git a/docs/pics/DP-Decorator-java.io.png b/docs/pics/DP-Decorator-java.io.png deleted file mode 100644 index 897a510b02..0000000000 Binary files a/docs/pics/DP-Decorator-java.io.png and /dev/null differ diff --git a/docs/pics/GUID_Partition_Table_Scheme.svg.png b/docs/pics/GUID_Partition_Table_Scheme.svg.png deleted file mode 100644 index 6638ae7ab6..0000000000 Binary files a/docs/pics/GUID_Partition_Table_Scheme.svg.png and /dev/null differ diff --git a/docs/pics/HTTP1_x_Connections.png b/docs/pics/HTTP1_x_Connections.png deleted file mode 100644 index d8c18a3cb7..0000000000 Binary files a/docs/pics/HTTP1_x_Connections.png and /dev/null differ diff --git a/docs/pics/HTTP_RequestMessageExample.png b/docs/pics/HTTP_RequestMessageExample.png deleted file mode 100644 index 8fd213cbec..0000000000 Binary files a/docs/pics/HTTP_RequestMessageExample.png and /dev/null differ diff --git a/docs/pics/HTTP_ResponseMessageExample.png b/docs/pics/HTTP_ResponseMessageExample.png deleted file mode 100644 index 1adf26c539..0000000000 Binary files a/docs/pics/HTTP_ResponseMessageExample.png and /dev/null differ diff --git a/docs/pics/How-HTTPS-Works.png b/docs/pics/How-HTTPS-Works.png deleted file mode 100644 index c10605f73f..0000000000 Binary files a/docs/pics/How-HTTPS-Works.png and /dev/null differ diff --git a/docs/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png b/docs/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png deleted file mode 100644 index 188460a54e..0000000000 Binary files a/docs/pics/LOun2W9134NxVugmbJPp15d4LalxC4O.png and /dev/null differ diff --git a/docs/pics/PPjwP.png b/docs/pics/PPjwP.png deleted file mode 100644 index 8063150509..0000000000 Binary files a/docs/pics/PPjwP.png and /dev/null differ diff --git a/docs/pics/ProcessState.png b/docs/pics/ProcessState.png deleted file mode 100644 index 3926957970..0000000000 Binary files a/docs/pics/ProcessState.png and /dev/null differ diff --git a/docs/pics/Semaphore.png b/docs/pics/Semaphore.png deleted file mode 100644 index 6b8c30b134..0000000000 Binary files a/docs/pics/Semaphore.png and /dev/null differ diff --git a/docs/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png b/docs/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png deleted file mode 100644 index da66da1412..0000000000 Binary files a/docs/pics/SoWkIImgAStDuU8goIp9ILK8IatCoQn.png and /dev/null differ diff --git a/docs/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png b/docs/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png deleted file mode 100644 index e5ae523401..0000000000 Binary files a/docs/pics/SoWkIImgAStDuU8goIp9ILLmB2xEJyv.png and /dev/null differ diff --git a/docs/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png b/docs/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png deleted file mode 100644 index e31398b6fd..0000000000 Binary files a/docs/pics/SoWkIImgAStDuU8goIp9ILLmJ4ylIar.png and /dev/null differ diff --git a/docs/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png b/docs/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png deleted file mode 100644 index 055d995ea9..0000000000 Binary files a/docs/pics/SoWkIImgAStDuU8goIp9ILLmJyrBBKh.png and /dev/null differ diff --git a/docs/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png b/docs/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png deleted file mode 100644 index 37f32c4c38..0000000000 Binary files a/docs/pics/SoWkIImgAStDuU8goIp9ILLmpiyjo2_.png and /dev/null differ diff --git a/docs/pics/_u4E0B_u8F7D.png b/docs/pics/_u4E0B_u8F7D.png deleted file mode 100644 index 9da973302d..0000000000 Binary files a/docs/pics/_u4E0B_u8F7D.png and /dev/null differ diff --git a/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif b/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif deleted file mode 100644 index f769ea6804..0000000000 Binary files a/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif and /dev/null differ diff --git a/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif b/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif deleted file mode 100644 index 9941311f04..0000000000 Binary files a/docs/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif and /dev/null differ diff --git a/docs/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif b/docs/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif deleted file mode 100644 index 763fc1a39f..0000000000 Binary files a/docs/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif and /dev/null differ diff --git a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif b/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif deleted file mode 100644 index 1587890cc4..0000000000 Binary files a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif and /dev/null differ diff --git a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif b/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif deleted file mode 100644 index 312fea6075..0000000000 Binary files a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif and /dev/null differ diff --git a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif b/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif deleted file mode 100644 index 61c506c9a1..0000000000 Binary files a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif and /dev/null differ diff --git a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif b/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif deleted file mode 100644 index 8c0ba8e380..0000000000 Binary files a/docs/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif and /dev/null differ diff --git a/docs/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif b/docs/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif deleted file mode 100644 index 07f3a9102c..0000000000 Binary files a/docs/pics/_u6570_u7EC4_u4E2D_u91CD_u590D_1548260392361.gif and /dev/null differ diff --git a/docs/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif b/docs/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif deleted file mode 100644 index 9d9809a08f..0000000000 Binary files a/docs/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif and /dev/null differ diff --git a/docs/pics/_u66FF_u6362_u7A7A_u683C.gif b/docs/pics/_u66FF_u6362_u7A7A_u683C.gif deleted file mode 100644 index fe6c5503bc..0000000000 Binary files a/docs/pics/_u66FF_u6362_u7A7A_u683C.gif and /dev/null differ diff --git a/docs/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif b/docs/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif deleted file mode 100644 index dfeff15bcf..0000000000 Binary files a/docs/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif and /dev/null differ diff --git a/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif b/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif deleted file mode 100644 index a6552b51fd..0000000000 Binary files a/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif and /dev/null differ diff --git a/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif b/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif deleted file mode 100644 index d5a4eec513..0000000000 Binary files a/docs/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif and /dev/null differ diff --git a/docs/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png b/docs/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png deleted file mode 100644 index 4b9b4b485e..0000000000 Binary files a/docs/pics/a01d1516-8168-461a-a24b-620b9cfc40f4.png and /dev/null differ diff --git a/docs/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png b/docs/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png deleted file mode 100644 index 541acf31e0..0000000000 Binary files a/docs/pics/a0e90bd3-747d-4c3a-8fa0-179c59eeded0_200.png and /dev/null differ diff --git a/docs/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg b/docs/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg deleted file mode 100644 index 30e926398f..0000000000 Binary files a/docs/pics/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg and /dev/null differ diff --git a/docs/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png b/docs/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png deleted file mode 100644 index edc6840ad1..0000000000 Binary files a/docs/pics/a2d13178-f1ef-4811-a240-1fe95b55b1eb.png and /dev/null differ diff --git a/docs/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png b/docs/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png deleted file mode 100644 index 1a5a64740c..0000000000 Binary files a/docs/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png and /dev/null differ diff --git a/docs/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg b/docs/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg deleted file mode 100644 index 23258afa74..0000000000 Binary files a/docs/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg and /dev/null differ diff --git a/docs/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png b/docs/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png deleted file mode 100644 index 03b37a617b..0000000000 Binary files a/docs/pics/a3da4342-078b-43e2-b748-7e71bec50dc4.png and /dev/null differ diff --git a/docs/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg b/docs/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg deleted file mode 100644 index 7b432fd1b9..0000000000 Binary files a/docs/pics/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg and /dev/null differ diff --git a/docs/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png b/docs/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png deleted file mode 100644 index e980a2e30f..0000000000 Binary files a/docs/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png and /dev/null differ diff --git a/docs/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg b/docs/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg deleted file mode 100644 index a7050bdf00..0000000000 Binary files a/docs/pics/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg and /dev/null differ diff --git a/docs/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png b/docs/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png deleted file mode 100644 index 461fffb0eb..0000000000 Binary files a/docs/pics/a4c17d43-fa5e-4935-b74e-147e7f7e782c.png and /dev/null differ diff --git a/docs/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png b/docs/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png deleted file mode 100644 index 26b0bd034c..0000000000 Binary files a/docs/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png and /dev/null differ diff --git a/docs/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png b/docs/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png deleted file mode 100644 index 11d366536b..0000000000 Binary files a/docs/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png and /dev/null differ diff --git a/docs/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png b/docs/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png deleted file mode 100644 index 9b6bcee257..0000000000 Binary files a/docs/pics/a6ebcd0a-a44d-48c8-b29a-dfacece83301.png and /dev/null differ diff --git a/docs/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg b/docs/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg deleted file mode 100644 index 67b2264dea..0000000000 Binary files a/docs/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg and /dev/null differ diff --git a/docs/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png b/docs/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png deleted file mode 100644 index 9b93237e5b..0000000000 Binary files a/docs/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png and /dev/null differ diff --git a/docs/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png b/docs/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png deleted file mode 100644 index 6a6bf78f10..0000000000 Binary files a/docs/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png and /dev/null differ diff --git a/docs/pics/ace830df-9919-48ca-91b5-60b193f593d2.png b/docs/pics/ace830df-9919-48ca-91b5-60b193f593d2.png deleted file mode 100644 index 79efa287c3..0000000000 Binary files a/docs/pics/ace830df-9919-48ca-91b5-60b193f593d2.png and /dev/null differ diff --git a/docs/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png b/docs/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png deleted file mode 100644 index 27977740b6..0000000000 Binary files a/docs/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png and /dev/null differ diff --git a/docs/pics/af198da1-2480-4043-b07f-a3b91a88b815.png b/docs/pics/af198da1-2480-4043-b07f-a3b91a88b815.png deleted file mode 100644 index 34d7a286a5..0000000000 Binary files a/docs/pics/af198da1-2480-4043-b07f-a3b91a88b815.png and /dev/null differ diff --git a/docs/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png b/docs/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png deleted file mode 100644 index 8de4fa4d38..0000000000 Binary files a/docs/pics/af4639f9-af54-4400-aaf5-4e261d96ace7.png and /dev/null differ diff --git a/docs/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg b/docs/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg deleted file mode 100644 index cfb2586d52..0000000000 Binary files a/docs/pics/b01e12b7-6cfc-485b-a03e-a575e6f7b773.jpg and /dev/null differ diff --git a/docs/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg b/docs/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg deleted file mode 100644 index a0df23c89e..0000000000 Binary files a/docs/pics/b02fcffd-ed09-4ef9-bc5f-8f0e0383eb1a.jpg and /dev/null differ diff --git a/docs/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png b/docs/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png deleted file mode 100644 index 73dad48397..0000000000 Binary files a/docs/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png and /dev/null differ diff --git a/docs/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg b/docs/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg deleted file mode 100644 index a71d167e39..0000000000 Binary files a/docs/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg and /dev/null differ diff --git a/docs/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png b/docs/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png deleted file mode 100644 index 842b2f6cba..0000000000 Binary files a/docs/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png and /dev/null differ diff --git a/docs/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png b/docs/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png deleted file mode 100644 index 3d5adabd47..0000000000 Binary files a/docs/pics/b20a3466-44b4-445e-87c7-dd4fb9ef44b2.png and /dev/null differ diff --git a/docs/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg b/docs/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg deleted file mode 100644 index d8085fa02f..0000000000 Binary files a/docs/pics/b28a7b1e-bf5a-4e23-8be7-701eacef0111.jpg and /dev/null differ diff --git a/docs/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png b/docs/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png deleted file mode 100644 index 65f73b0e86..0000000000 Binary files a/docs/pics/b29f8971-9cb8-480d-b986-0e60c2ece069.png and /dev/null differ diff --git a/docs/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg b/docs/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg deleted file mode 100644 index a3e1a656ec..0000000000 Binary files a/docs/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg and /dev/null differ diff --git a/docs/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg b/docs/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg deleted file mode 100644 index fa79744a82..0000000000 Binary files a/docs/pics/b39a085e-e7a2-4657-b75e-ba1652a4b132.jpg and /dev/null differ diff --git a/docs/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg b/docs/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg deleted file mode 100644 index eb46d41276..0000000000 Binary files a/docs/pics/b3a421e3-41b4-4e99-b612-bc695a7f622f.jpg and /dev/null differ diff --git a/docs/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png b/docs/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png deleted file mode 100644 index 59ea02ccc4..0000000000 Binary files a/docs/pics/b4252c85-6fb0-4995-9a68-a1a5925fbdb1.png and /dev/null differ diff --git a/docs/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png b/docs/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png deleted file mode 100644 index ac9868d1eb..0000000000 Binary files a/docs/pics/b48b9a7a-f9f8-4cf9-90f1-5cddd685b782_200.png and /dev/null differ diff --git a/docs/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg b/docs/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg deleted file mode 100644 index 692a035757..0000000000 Binary files a/docs/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg and /dev/null differ diff --git a/docs/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png b/docs/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png deleted file mode 100644 index dd32da7026..0000000000 Binary files a/docs/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png and /dev/null differ diff --git a/docs/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg b/docs/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg deleted file mode 100644 index 96b80deba9..0000000000 Binary files a/docs/pics/b6a7e8af-91bf-44b2-8874-ccc6d9d52afc.jpg and /dev/null differ diff --git a/docs/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg b/docs/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg deleted file mode 100644 index 58fb0a056e..0000000000 Binary files a/docs/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg and /dev/null differ diff --git a/docs/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png b/docs/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png deleted file mode 100644 index dc44da3deb..0000000000 Binary files a/docs/pics/b84ba6fb-312b-4e69-8c77-fb6eb6fb38d4.png and /dev/null differ diff --git a/docs/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png b/docs/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png deleted file mode 100644 index c74e816e4c..0000000000 Binary files a/docs/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png and /dev/null differ diff --git a/docs/pics/b988877c-0f0a-4593-916d-de2081320628.jpg b/docs/pics/b988877c-0f0a-4593-916d-de2081320628.jpg deleted file mode 100644 index 67339134d5..0000000000 Binary files a/docs/pics/b988877c-0f0a-4593-916d-de2081320628.jpg and /dev/null differ diff --git a/docs/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg b/docs/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg deleted file mode 100644 index b750283192..0000000000 Binary files a/docs/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg and /dev/null differ diff --git a/docs/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png b/docs/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png deleted file mode 100644 index 6664d39875..0000000000 Binary files a/docs/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png and /dev/null differ diff --git a/docs/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg b/docs/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg deleted file mode 100644 index c3815f20c6..0000000000 Binary files a/docs/pics/bab0fba6-38e4-45f7-b34d-3edaad43810f.jpg and /dev/null differ diff --git a/docs/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png b/docs/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png deleted file mode 100644 index 07d8692d3c..0000000000 Binary files a/docs/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png and /dev/null differ diff --git a/docs/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg b/docs/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg deleted file mode 100644 index b27cd02ff0..0000000000 Binary files a/docs/pics/bc5826f5-014d-47b4-9a76-d86b80968643.jpg and /dev/null differ diff --git a/docs/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg b/docs/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg deleted file mode 100644 index 4cd6f60f06..0000000000 Binary files a/docs/pics/bc775758-89ab-4805-9f9c-78b8739cf780.jpg and /dev/null differ diff --git a/docs/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg b/docs/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg deleted file mode 100644 index a2965a7d77..0000000000 Binary files a/docs/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg and /dev/null differ diff --git a/docs/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png b/docs/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png deleted file mode 100644 index 2c2214ded2..0000000000 Binary files a/docs/pics/be7dca03-12ec-456b-8b54-b1b3161f5531.png and /dev/null differ diff --git a/docs/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png b/docs/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png deleted file mode 100644 index b24241d106..0000000000 Binary files a/docs/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png and /dev/null differ diff --git a/docs/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg b/docs/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg deleted file mode 100644 index a83ba2755a..0000000000 Binary files a/docs/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg and /dev/null differ diff --git a/docs/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg b/docs/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg deleted file mode 100644 index 30956ccfe4..0000000000 Binary files a/docs/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg and /dev/null differ diff --git a/docs/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png b/docs/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png deleted file mode 100644 index b9d9dba2e6..0000000000 Binary files a/docs/pics/bfbb11e2-d208-4efa-b97b-24cd40467cd8.png and /dev/null differ diff --git a/docs/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png b/docs/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png deleted file mode 100644 index 9e2feb2785..0000000000 Binary files a/docs/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png and /dev/null differ diff --git a/docs/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png b/docs/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png deleted file mode 100644 index add3c5d4d6..0000000000 Binary files a/docs/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png and /dev/null differ diff --git a/docs/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png b/docs/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png deleted file mode 100644 index cf8d36dc50..0000000000 Binary files a/docs/pics/c26e7ce3-dd9e-47e2-a208-367b5b2cddf6.png and /dev/null differ diff --git a/docs/pics/c2c2b633-c03a-426e-b436-5719a194667b.png b/docs/pics/c2c2b633-c03a-426e-b436-5719a194667b.png deleted file mode 100644 index b673d4a796..0000000000 Binary files a/docs/pics/c2c2b633-c03a-426e-b436-5719a194667b.png and /dev/null differ diff --git a/docs/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png b/docs/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png deleted file mode 100644 index 70ecc3ec67..0000000000 Binary files a/docs/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png and /dev/null differ diff --git a/docs/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png b/docs/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png deleted file mode 100644 index 84b2898b39..0000000000 Binary files a/docs/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png and /dev/null differ diff --git a/docs/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg b/docs/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg deleted file mode 100644 index 17a2e9bf65..0000000000 Binary files a/docs/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg and /dev/null differ diff --git a/docs/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg b/docs/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg deleted file mode 100644 index 2c6cc2b2a6..0000000000 Binary files a/docs/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg and /dev/null differ diff --git a/docs/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png b/docs/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png deleted file mode 100644 index 48e980fc08..0000000000 Binary files a/docs/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png and /dev/null differ diff --git a/docs/pics/c5085437-54df-4304-b62d-44b961711ba7.png b/docs/pics/c5085437-54df-4304-b62d-44b961711ba7.png deleted file mode 100644 index 36c948aad4..0000000000 Binary files a/docs/pics/c5085437-54df-4304-b62d-44b961711ba7.png and /dev/null differ diff --git a/docs/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg b/docs/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg deleted file mode 100644 index 5e392d41d0..0000000000 Binary files a/docs/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg and /dev/null differ diff --git a/docs/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg b/docs/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg deleted file mode 100644 index 895d1ed587..0000000000 Binary files a/docs/pics/c6fdb958-578d-4685-a5fa-400857be91d5.jpg and /dev/null differ diff --git a/docs/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png b/docs/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png deleted file mode 100644 index bbed020ac2..0000000000 Binary files a/docs/pics/c7875be7-fce5-43c7-ac77-d8dbe6c0ae1b.png and /dev/null differ diff --git a/docs/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png b/docs/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png deleted file mode 100644 index 91e2c14425..0000000000 Binary files a/docs/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png and /dev/null differ diff --git a/docs/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png b/docs/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png deleted file mode 100644 index 167c33bc53..0000000000 Binary files a/docs/pics/c847d6e4-3610-4f3c-a909-89a5048426e6.png and /dev/null differ diff --git a/docs/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg b/docs/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg deleted file mode 100644 index fa7ce3ae1e..0000000000 Binary files a/docs/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg and /dev/null differ diff --git a/docs/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png b/docs/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png deleted file mode 100644 index 7a7c702d9f..0000000000 Binary files a/docs/pics/cb5d2258-a60e-4364-94a7-3429a3064554_200.png and /dev/null differ diff --git a/docs/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png b/docs/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png deleted file mode 100644 index 215d2c8ddc..0000000000 Binary files a/docs/pics/cbd5f6f6-18de-4711-9e01-0f94e66f81b8_200.png and /dev/null differ diff --git a/docs/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png b/docs/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png deleted file mode 100644 index 27f2b74217..0000000000 Binary files a/docs/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png and /dev/null differ diff --git a/docs/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png b/docs/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png deleted file mode 100644 index f8550a114b..0000000000 Binary files a/docs/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png and /dev/null differ diff --git a/docs/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png b/docs/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png deleted file mode 100644 index 9b9f38382f..0000000000 Binary files a/docs/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png and /dev/null differ diff --git a/docs/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg b/docs/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg deleted file mode 100644 index b3699644c3..0000000000 Binary files a/docs/pics/cf779e26-0382-4495-8463-f1e19e2e38a0.jpg and /dev/null differ diff --git a/docs/pics/class_loader_hierarchy.png b/docs/pics/class_loader_hierarchy.png deleted file mode 100644 index 6f4baf6f10..0000000000 Binary files a/docs/pics/class_loader_hierarchy.png and /dev/null differ diff --git a/docs/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg b/docs/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg deleted file mode 100644 index 4e72308703..0000000000 Binary files a/docs/pics/d011c697-2551-4968-ac51-e5e01a954204.jpg and /dev/null differ diff --git a/docs/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png b/docs/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png deleted file mode 100644 index 58ff84fe5f..0000000000 Binary files a/docs/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png and /dev/null differ diff --git a/docs/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png b/docs/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png deleted file mode 100644 index cc971351fd..0000000000 Binary files a/docs/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png and /dev/null differ diff --git a/docs/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif b/docs/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif deleted file mode 100644 index 2c0accfc29..0000000000 Binary files a/docs/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif and /dev/null differ diff --git a/docs/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png b/docs/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png deleted file mode 100644 index ce6663bc18..0000000000 Binary files a/docs/pics/d2a12961-2b36-4463-b017-ca46a3308b8e.png and /dev/null differ diff --git a/docs/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg b/docs/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg deleted file mode 100644 index bbc7f102b5..0000000000 Binary files a/docs/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg and /dev/null differ diff --git a/docs/pics/d52270b4-9097-4667-9f18-f405fc661c99.png b/docs/pics/d52270b4-9097-4667-9f18-f405fc661c99.png deleted file mode 100644 index 347fe31617..0000000000 Binary files a/docs/pics/d52270b4-9097-4667-9f18-f405fc661c99.png and /dev/null differ diff --git a/docs/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png b/docs/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png deleted file mode 100644 index 5a138b6f17..0000000000 Binary files a/docs/pics/d5ce91a7-45f9-4560-9917-0dccd4900826.png and /dev/null differ diff --git a/docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png b/docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png deleted file mode 100644 index 2dde7dab4d..0000000000 Binary files a/docs/pics/d5d3b7ae-2712-412e-98f1-633ce6ec5955.png and /dev/null differ diff --git a/docs/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png b/docs/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png deleted file mode 100644 index fdca032441..0000000000 Binary files a/docs/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png and /dev/null differ diff --git a/docs/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png b/docs/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png deleted file mode 100644 index fdca032441..0000000000 Binary files a/docs/pics/db239936-1237-464a-bb5c-a8eefdd4c361.png and /dev/null differ diff --git a/docs/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png b/docs/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png deleted file mode 100644 index 1ee66db8b7..0000000000 Binary files a/docs/pics/dbb8516d-37ba-4e2c-b26b-eefd7de21b45.png and /dev/null differ diff --git a/docs/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg b/docs/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg deleted file mode 100644 index 8090706b41..0000000000 Binary files a/docs/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg and /dev/null differ diff --git a/docs/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg b/docs/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg deleted file mode 100644 index 0be8743c8e..0000000000 Binary files a/docs/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg and /dev/null differ diff --git a/docs/pics/dd15a984-e977-4644-b127-708cddb8ca99.png b/docs/pics/dd15a984-e977-4644-b127-708cddb8ca99.png deleted file mode 100644 index 4397e88e81..0000000000 Binary files a/docs/pics/dd15a984-e977-4644-b127-708cddb8ca99.png and /dev/null differ diff --git a/docs/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png b/docs/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png deleted file mode 100644 index 51055a2bb0..0000000000 Binary files a/docs/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png and /dev/null differ diff --git a/docs/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg b/docs/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg deleted file mode 100644 index 69f4dd050a..0000000000 Binary files a/docs/pics/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg and /dev/null differ diff --git a/docs/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png b/docs/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png deleted file mode 100644 index cfabd4d7c7..0000000000 Binary files a/docs/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png and /dev/null differ diff --git a/docs/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg b/docs/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg deleted file mode 100644 index 4a923dd802..0000000000 Binary files a/docs/pics/de9d8133-4c98-4e07-b39c-302e162784ea.jpg and /dev/null differ diff --git a/docs/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg b/docs/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg deleted file mode 100644 index 7746a71564..0000000000 Binary files a/docs/pics/deb18bdb-b3b3-4660-b778-b0823a48db12.jpg and /dev/null differ diff --git a/docs/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg b/docs/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg deleted file mode 100644 index d04e9f6fe1..0000000000 Binary files a/docs/pics/df01dbcd-4a3c-4877-86e8-5590d7589788.jpg and /dev/null differ diff --git a/docs/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png b/docs/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png deleted file mode 100644 index ef5b780fb3..0000000000 Binary files a/docs/pics/df247180-4d5c-4d4b-ab0f-1aec61d13d45.png and /dev/null differ diff --git a/docs/pics/docker-filesystems-busyboxrw.png b/docs/pics/docker-filesystems-busyboxrw.png deleted file mode 100644 index c12710afa6..0000000000 Binary files a/docs/pics/docker-filesystems-busyboxrw.png and /dev/null differ diff --git a/docs/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png b/docs/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png deleted file mode 100644 index 427cfbf82d..0000000000 Binary files a/docs/pics/e026c24d-00fa-4e7c-97a8-95a98cdc383a.png and /dev/null differ diff --git a/docs/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png b/docs/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png deleted file mode 100644 index b4c565fbde..0000000000 Binary files a/docs/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png and /dev/null differ diff --git a/docs/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg b/docs/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg deleted file mode 100644 index 0968e1bc2d..0000000000 Binary files a/docs/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg and /dev/null differ diff --git a/docs/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png b/docs/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png deleted file mode 100644 index c21e9af20f..0000000000 Binary files a/docs/pics/e19452dd-220a-4a6b-bcb0-91ad5e5c4706.png and /dev/null differ diff --git a/docs/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png b/docs/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png deleted file mode 100644 index bffe752092..0000000000 Binary files a/docs/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png and /dev/null differ diff --git a/docs/pics/e2f0d889-2330-424c-8193-198edebecff7.png b/docs/pics/e2f0d889-2330-424c-8193-198edebecff7.png deleted file mode 100644 index d747d6f30c..0000000000 Binary files a/docs/pics/e2f0d889-2330-424c-8193-198edebecff7.png and /dev/null differ diff --git a/docs/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg b/docs/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg deleted file mode 100644 index 8064365771..0000000000 Binary files a/docs/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg and /dev/null differ diff --git a/docs/pics/e31abb94-9201-4e06-9902-61101b92f475.png b/docs/pics/e31abb94-9201-4e06-9902-61101b92f475.png deleted file mode 100644 index 90833a5c5b..0000000000 Binary files a/docs/pics/e31abb94-9201-4e06-9902-61101b92f475.png and /dev/null differ diff --git a/docs/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png b/docs/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png deleted file mode 100644 index 118096b705..0000000000 Binary files a/docs/pics/e3360fa0-680e-486b-945f-09ff5d8612e4.png and /dev/null differ diff --git a/docs/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png b/docs/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png deleted file mode 100644 index a8e7e53857..0000000000 Binary files a/docs/pics/e3b53605-0c10-4a7e-be02-a9064778f8a5.png and /dev/null differ diff --git a/docs/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png b/docs/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png deleted file mode 100644 index 105916c6a6..0000000000 Binary files a/docs/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png and /dev/null differ diff --git a/docs/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg b/docs/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg deleted file mode 100644 index b28765c586..0000000000 Binary files a/docs/pics/e4047473-a274-44d7-809a-c7312f52b55f.jpg and /dev/null differ diff --git a/docs/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg b/docs/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg deleted file mode 100644 index 7fef7ba5c0..0000000000 Binary files a/docs/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg and /dev/null differ diff --git a/docs/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg b/docs/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg deleted file mode 100644 index 8eda7a0a0f..0000000000 Binary files a/docs/pics/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg and /dev/null differ diff --git a/docs/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg b/docs/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg deleted file mode 100644 index f4aa12a06d..0000000000 Binary files a/docs/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg and /dev/null differ diff --git a/docs/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png b/docs/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png deleted file mode 100644 index 1090a77959..0000000000 Binary files a/docs/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png and /dev/null differ diff --git a/docs/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png b/docs/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png deleted file mode 100644 index 59b54d2fa8..0000000000 Binary files a/docs/pics/ea2304ce-268b-4238-9486-4d8f8aea8ca4.png and /dev/null differ diff --git a/docs/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png b/docs/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png deleted file mode 100644 index 7104f0208c..0000000000 Binary files a/docs/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png and /dev/null differ diff --git a/docs/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png b/docs/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png deleted file mode 100644 index 007e9646c0..0000000000 Binary files a/docs/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png and /dev/null differ diff --git a/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png b/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png deleted file mode 100644 index 25ed74973e..0000000000 Binary files a/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png and /dev/null differ diff --git a/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png b/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png deleted file mode 100644 index 25ed74973e..0000000000 Binary files a/docs/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png and /dev/null differ diff --git a/docs/pics/ed523051-608f-4c3f-b343-383e2d194470.png b/docs/pics/ed523051-608f-4c3f-b343-383e2d194470.png deleted file mode 100644 index 1f703e2f5e..0000000000 Binary files a/docs/pics/ed523051-608f-4c3f-b343-383e2d194470.png and /dev/null differ diff --git a/docs/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png b/docs/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png deleted file mode 100644 index 1c153a8a3a..0000000000 Binary files a/docs/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png and /dev/null differ diff --git a/docs/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg b/docs/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg deleted file mode 100644 index 732e73e000..0000000000 Binary files a/docs/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg and /dev/null differ diff --git a/docs/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png b/docs/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png deleted file mode 100644 index a54c85e1f4..0000000000 Binary files a/docs/pics/ef280699-da36-4b38-9735-9b048a3c7fe0.png and /dev/null differ diff --git a/docs/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png b/docs/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png deleted file mode 100644 index f625686748..0000000000 Binary files a/docs/pics/ef8eab00-1d5e-4d99-a7c2-d6d68ea7fe92.png and /dev/null differ diff --git a/docs/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg b/docs/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg deleted file mode 100644 index 66a2ecbe20..0000000000 Binary files a/docs/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg and /dev/null differ diff --git a/docs/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg b/docs/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg deleted file mode 100644 index 4308e4c935..0000000000 Binary files a/docs/pics/f1ff65ed-bbc2-4b92-8a94-7c5c0874da0f.jpg and /dev/null differ diff --git a/docs/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png b/docs/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png deleted file mode 100644 index 7786868673..0000000000 Binary files a/docs/pics/f3080f83-6239-459b-8e9c-03b6641f7815.png and /dev/null differ diff --git a/docs/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png b/docs/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png deleted file mode 100644 index 4a5dbd1dc6..0000000000 Binary files a/docs/pics/f42443e0-208d-41ea-be44-c7fd97d2e3bf.png and /dev/null differ diff --git a/docs/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png b/docs/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png deleted file mode 100644 index f358a009ca..0000000000 Binary files a/docs/pics/f5477abd-c246-4851-89ab-6b1cde2549b1.png and /dev/null differ diff --git a/docs/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg b/docs/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg deleted file mode 100644 index 46e9f0f82b..0000000000 Binary files a/docs/pics/f5d40b01-abf2-435e-9ce7-7889c41b2fa6.jpg and /dev/null differ diff --git a/docs/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png b/docs/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png deleted file mode 100644 index dc0d4e3490..0000000000 Binary files a/docs/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png and /dev/null differ diff --git a/docs/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg b/docs/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg deleted file mode 100644 index d710e65919..0000000000 Binary files a/docs/pics/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg and /dev/null differ diff --git a/docs/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg b/docs/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg deleted file mode 100644 index 9fc2a27132..0000000000 Binary files a/docs/pics/f77f06b6-7359-4066-b85d-3f6c09ddf425.jpg and /dev/null differ diff --git a/docs/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png b/docs/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png deleted file mode 100644 index 26799762f2..0000000000 Binary files a/docs/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png and /dev/null differ diff --git a/docs/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png b/docs/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png deleted file mode 100644 index b85accf6eb..0000000000 Binary files a/docs/pics/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png and /dev/null differ diff --git a/docs/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png b/docs/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png deleted file mode 100644 index 86e2294f47..0000000000 Binary files a/docs/pics/f8047846-efd4-42be-b6b7-27a7c4998b51.png and /dev/null differ diff --git a/docs/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg b/docs/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg deleted file mode 100644 index 6a09099323..0000000000 Binary files a/docs/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg and /dev/null differ diff --git a/docs/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png b/docs/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png deleted file mode 100644 index 4d9e3e21c7..0000000000 Binary files a/docs/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png and /dev/null differ diff --git a/docs/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg b/docs/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg deleted file mode 100644 index 95b92d603a..0000000000 Binary files a/docs/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg and /dev/null differ diff --git a/docs/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png b/docs/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png deleted file mode 100644 index 56f056114d..0000000000 Binary files a/docs/pics/f944fac3-482b-4ca3-9447-17aec4a3cca0.png and /dev/null differ diff --git a/docs/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png b/docs/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png deleted file mode 100644 index 7da6d4b2e6..0000000000 Binary files a/docs/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png and /dev/null differ diff --git a/docs/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg b/docs/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg deleted file mode 100644 index 67fac8feb2..0000000000 Binary files a/docs/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg and /dev/null differ diff --git a/docs/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg b/docs/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg deleted file mode 100644 index b6a0ba7900..0000000000 Binary files a/docs/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg and /dev/null differ diff --git a/docs/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png b/docs/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png deleted file mode 100644 index 824904dbc3..0000000000 Binary files a/docs/pics/f9f9f993-8ece-4da7-b848-af9b438fad76.png and /dev/null differ diff --git a/docs/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png b/docs/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png deleted file mode 100644 index 2142bbb59f..0000000000 Binary files a/docs/pics/fa1dc552-8501-439e-b85a-3d9eac704880_200.png and /dev/null differ diff --git a/docs/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png b/docs/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png deleted file mode 100644 index 736a286164..0000000000 Binary files a/docs/pics/fa568fac-ac58-48dd-a9bb-23b3065bf2dc.png and /dev/null differ diff --git a/docs/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png b/docs/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png deleted file mode 100644 index 31ae3277cd..0000000000 Binary files a/docs/pics/fac3dfd6-1656-4329-9a80-7f6c51ef30c5_200.png and /dev/null differ diff --git a/docs/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png b/docs/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png deleted file mode 100644 index 774ecf1078..0000000000 Binary files a/docs/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png and /dev/null differ diff --git a/docs/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg b/docs/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg deleted file mode 100644 index 36c1d9b723..0000000000 Binary files a/docs/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg and /dev/null differ diff --git a/docs/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg b/docs/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg deleted file mode 100644 index e15f11db01..0000000000 Binary files a/docs/pics/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg and /dev/null differ diff --git a/docs/pics/fbe54203-c005-48f0-8883-b05e564a3173.png b/docs/pics/fbe54203-c005-48f0-8883-b05e564a3173.png deleted file mode 100644 index dee6a88dff..0000000000 Binary files a/docs/pics/fbe54203-c005-48f0-8883-b05e564a3173.png and /dev/null differ diff --git a/docs/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png b/docs/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png deleted file mode 100644 index 4eaa11ba67..0000000000 Binary files a/docs/pics/ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png and /dev/null differ diff --git a/docs/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg b/docs/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg deleted file mode 100644 index 1fa4032047..0000000000 Binary files a/docs/pics/ff5b89ac-798e-4fbc-b0ce-da2fc2358570.jpg and /dev/null differ diff --git a/docs/pics/flow.png b/docs/pics/flow.png deleted file mode 100644 index aa0492a4e8..0000000000 Binary files a/docs/pics/flow.png and /dev/null differ diff --git a/docs/pics/inode_with_signatures.jpg b/docs/pics/inode_with_signatures.jpg deleted file mode 100644 index 518ba5ac84..0000000000 Binary files a/docs/pics/inode_with_signatures.jpg and /dev/null differ diff --git a/docs/pics/linux-filesystem.png b/docs/pics/linux-filesystem.png deleted file mode 100644 index ae96529529..0000000000 Binary files a/docs/pics/linux-filesystem.png and /dev/null differ diff --git a/docs/pics/master-slave-proxy.png b/docs/pics/master-slave-proxy.png deleted file mode 100644 index 66be0d61f5..0000000000 Binary files a/docs/pics/master-slave-proxy.png and /dev/null differ diff --git a/docs/pics/master-slave.png b/docs/pics/master-slave.png deleted file mode 100644 index 594a183a4c..0000000000 Binary files a/docs/pics/master-slave.png and /dev/null differ diff --git a/docs/pics/monitor-lock-rule.png b/docs/pics/monitor-lock-rule.png deleted file mode 100644 index 6590d94bff..0000000000 Binary files a/docs/pics/monitor-lock-rule.png and /dev/null differ diff --git a/docs/pics/network-of-networks.gif b/docs/pics/network-of-networks.gif deleted file mode 100644 index 7473f913f9..0000000000 Binary files a/docs/pics/network-of-networks.gif and /dev/null differ diff --git a/docs/pics/single-thread-rule.png b/docs/pics/single-thread-rule.png deleted file mode 100644 index d6583e9e64..0000000000 Binary files a/docs/pics/single-thread-rule.png and /dev/null differ diff --git a/docs/pics/ssl-offloading.jpg b/docs/pics/ssl-offloading.jpg deleted file mode 100644 index 8f01a4182c..0000000000 Binary files a/docs/pics/ssl-offloading.jpg and /dev/null differ diff --git a/docs/pics/tGPV0.png b/docs/pics/tGPV0.png deleted file mode 100644 index 89fb7bfe75..0000000000 Binary files a/docs/pics/tGPV0.png and /dev/null differ diff --git a/docs/pics/thread-join-rule.png b/docs/pics/thread-join-rule.png deleted file mode 100644 index c17d045623..0000000000 Binary files a/docs/pics/thread-join-rule.png and /dev/null differ diff --git a/docs/pics/thread-start-rule.png b/docs/pics/thread-start-rule.png deleted file mode 100644 index 60ee78622c..0000000000 Binary files a/docs/pics/thread-start-rule.png and /dev/null differ diff --git a/docs/pics/volatile-variable-rule.png b/docs/pics/volatile-variable-rule.png deleted file mode 100644 index 1747664b01..0000000000 Binary files a/docs/pics/volatile-variable-rule.png and /dev/null differ diff --git "a/notes/10.1 \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" "b/notes/10.1 \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" new file mode 100644 index 0000000000..fd9e836bc1 --- /dev/null +++ "b/notes/10.1 \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" @@ -0,0 +1,69 @@ +# 10.1 斐波那契数列 + +## 题目链接 + +[牛客网](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。 + + + +

+ +## 解题思路 + +如果使用递归求解,会重复计算一些子问题。例如,计算 f(4) 需要计算 f(3) 和 f(2),计算 f(3) 需要计算 f(2) 和 f(1),可以看到 f(2) 被重复计算了。 + +

+ +递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。 + +```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]; + } +} +``` diff --git "a/notes/10.2 \347\237\251\345\275\242\350\246\206\347\233\226.md" "b/notes/10.2 \347\237\251\345\275\242\350\246\206\347\233\226.md" new file mode 100644 index 0000000000..01e833f709 --- /dev/null +++ "b/notes/10.2 \347\237\251\345\275\242\350\246\206\347\233\226.md" @@ -0,0 +1,42 @@ +# 10.2 矩形覆盖 + +## 题目链接 + +[牛客网](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 的大矩形,总共有多少种方法? + +

+ +## 解题思路 + +当 n 为 1 时,只有一种覆盖方法: + +

+ +当 n 为 2 时,有两种覆盖方法: + +

+ +要覆盖 2\*n 的大矩形,可以先覆盖 2\*1 的矩形,再覆盖 2\*(n-1) 的矩形;或者先覆盖 2\*2 的矩形,再覆盖 2\*(n-2) 的矩形。而覆盖 2\*(n-1) 和 2\*(n-2) 的矩形可以看成子问题。该问题的递推公式如下: + + + +

+ +```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; +} +``` diff --git "a/notes/10.3 \350\267\263\345\217\260\351\230\266.md" "b/notes/10.3 \350\267\263\345\217\260\351\230\266.md" new file mode 100644 index 0000000000..2072cb9008 --- /dev/null +++ "b/notes/10.3 \350\267\263\345\217\260\351\230\266.md" @@ -0,0 +1,40 @@ +# 10.3 跳台阶 + +## 题目链接 + +[牛客网](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 级的台阶总共有多少种跳法。 + +

+ +## 解题思路 + +当 n = 1 时,只有一种跳法: + +

+ +当 n = 2 时,有两种跳法: + +

+ +跳 n 阶台阶,可以先跳 1 阶台阶,再跳 n-1 阶台阶;或者先跳 2 阶台阶,再跳 n-2 阶台阶。而 n-1 和 n-2 阶台阶的跳法可以看成子问题,该问题的递推公式为: + +

+ +```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; +} +``` diff --git "a/notes/10.4 \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" "b/notes/10.4 \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" new file mode 100644 index 0000000000..f6099a1723 --- /dev/null +++ "b/notes/10.4 \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" @@ -0,0 +1,60 @@ +# 10.4 变态跳台阶 + +## 题目链接 + +[牛客网](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 级的台阶总共有多少种跳法。 + +

+ +## 解题思路 + +### 动态规划 + +```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); +} +``` diff --git "a/notes/11. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" "b/notes/11. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..020db3ffb0 --- /dev/null +++ "b/notes/11. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" @@ -0,0 +1,67 @@ +# 11. 旋转数组的最小数字 + +## 题目链接 + +[牛客网](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) + +## 题目描述 + +把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 + +

+ +## 解题思路 + +将旋转数组对半分可以得到一个包含最小元素的新旋转数组,以及一个非递减排序的数组。新的旋转数组的长度是原数组的一半,从而将问题规模减少了一半,这种折半性质的算法的时间复杂度为 O(log2N)。 + +

+ +此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组,哪一个是非递减数组。我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素。 + +通过修改二分查找算法进行求解(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]; +} +``` diff --git "a/notes/12. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" "b/notes/12. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000000..157785374b --- /dev/null +++ "b/notes/12. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,78 @@ +# 12. 矩阵中的路径 + +[牛客网](https://www.nowcoder.com/practice/69fe7a584f0a445da1b6652978de5c38?tpId=13&tqId=11218&tab=answerKey&from=cyc_github) + +## 题目描述 + +判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向上下左右移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 + +例如下面的矩阵包含了一条 bfce 路径。 + +

+ +## 解题思路 + +使用回溯法(backtracking)进行求解,它是一种暴力搜索方法,通过搜索所有可能的结果来求解问题。回溯法在一次搜索结束时需要进行回溯(回退),将这一次搜索过程中设置的状态进行清除,从而开始一次新的搜索过程。例如下图示例中,从 f 开始,下一步有 4 种搜索可能,如果先搜索 b,需要将 b 标记为已经使用,防止重复使用。在这一次搜索结束之后,需要将 b 的已经使用状态清除,并搜索 c。 + +

+ +本题的输入是数组而不是矩阵(二维数组),因此需要先将数组转换成矩阵。 + +```java +public class Solution { + private final static int[][] next = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}; + private int rows; + private int cols; + + public boolean hasPath (String val, int rows, int cols, String path) { + if (rows == 0 || cols == 0) return false; + this.rows = rows; + this.cols = cols; + char[] array = val.toCharArray(); + char[][] matrix = buildMatrix(array); + char[] pathList = path.toCharArray(); + boolean[][] marked = new boolean[rows][cols]; + for (int i = 0; i < rows; i++) + for (int j = 0; j < cols; j++) + if (backtracking(matrix, pathList, marked, 0, i, j)) + return true; + + return false; + } + + private boolean backtracking(char[][] matrix, char[] pathList, + boolean[][] marked, int pathLen, int r, int c) { + + if (pathLen == pathList.length) return true; + if (r < 0 || r >= rows || c < 0 || c >= cols + || matrix[r][c] != pathList[pathLen] || marked[r][c]) { + + return false; + } + marked[r][c] = true; + for (int[] n : next) + if (backtracking(matrix, pathList, 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; + } + + public static void main(String[] args) { + Solution solution = new Solution(); + String val = "ABCESFCSADEE"; + int rows = 3; + int cols = 4; + String path = "ABCCED"; + boolean res = solution.hasPath(val, rows, cols, path); + System.out.println(res); + } +} +``` diff --git "a/notes/13. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" "b/notes/13. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" new file mode 100644 index 0000000000..dd96086ed2 --- /dev/null +++ "b/notes/13. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" @@ -0,0 +1,58 @@ +# 13. 机器人的运动范围 + +[牛客网](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]; +} +``` diff --git "a/notes/14. \345\211\252\347\273\263\345\255\220.md" "b/notes/14. \345\211\252\347\273\263\345\255\220.md" new file mode 100644 index 0000000000..9108b63536 --- /dev/null +++ "b/notes/14. \345\211\252\347\273\263\345\255\220.md" @@ -0,0 +1,67 @@ +# 14. 剪绳子 + +## 题目链接 + +[牛客网](https://www.nowcoder.com/practice/57d85990ba5b440ab888fc72b0751bf8?tpId=13&tqId=33257&tab=answerKey&from=cyc_github) + +## 题目描述 + +把一根绳子剪成多段,并且使得每段的长度乘积最大。 + +```html +n = 2 +return 1 (2 = 1 + 1) + +n = 10 +return 36 (10 = 3 + 3 + 4) +``` + +## 解题思路 + +### 贪心 + +尽可能得多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。以下为证明过程。 + +将绳子拆成 1 和 n-1,则 1(n-1)-n=-1\<0,即拆开后的乘积一定更小,所以不能出现长度为 1 的绳子。 + +将绳子拆成 2 和 n-2,则 2(n-2)-n = n-4,在 n\>=4 时这样拆开能得到的乘积会比不拆更大。 + +将绳子拆成 3 和 n-3,则 3(n-3)-n = 2n-9,在 n\>=5 时效果更好。 + +将绳子拆成 4 和 n-4,因为 4=2\*2,因此效果和拆成 2 一样。 + +将绳子拆成 5 和 n-5,因为 5=2+3,而 5\<2\*3,所以不能出现 5 的绳子,而是尽可能拆成 2 和 3。 + +将绳子拆成 6 和 n-6,因为 6=3+3,而 6\<3\*3,所以不能出现 6 的绳子,而是拆成 3 和 3。这里 6 同样可以拆成 6=2+2+2,但是 3(n - 3) - 2(n - 2) = n - 5 \>= 0,在 n\>=5 的情况下将绳子拆成 3 比拆成 2 效果更好。 + +继续拆成更大的绳子可以发现都比拆成 2 和 3 的效果更差,因此我们只考虑将绳子拆成 2 和 3,并且优先拆成 3,当拆到绳子长度 n 等于 4 时,也就是出现 3+1,此时只能拆成 2+2。 + +```java +public int cutRope(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 cutRope(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]; +} +``` + diff --git "a/notes/15. \344\272\214\350\277\233\345\210\266\344\270\255 1 \347\232\204\344\270\252\346\225\260.md" "b/notes/15. \344\272\214\350\277\233\345\210\266\344\270\255 1 \347\232\204\344\270\252\346\225\260.md" new file mode 100644 index 0000000000..bea92147bb --- /dev/null +++ "b/notes/15. \344\272\214\350\277\233\345\210\266\344\270\255 1 \347\232\204\344\270\252\346\225\260.md" @@ -0,0 +1,27 @@ +# 15. 二进制中 1 的个数 + +## 题目链接 + +[牛客网](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 的位级表示中最低的那一位 1 设置为 0。不断将 1 设置为 0,直到 n 为 0。时间复杂度:O(M),其中 M 表示 1 的个数。 + +

+ + +```java +public int NumberOf1(int n) { + int cnt = 0; + while (n != 0) { + cnt++; + n &= (n - 1); + } + return cnt; +} +``` diff --git "a/notes/16. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" "b/notes/16. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" new file mode 100644 index 0000000000..b20367e239 --- /dev/null +++ "b/notes/16. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" @@ -0,0 +1,46 @@ +# 16. 数值的整数次方 + +## 题目链接 + +[牛客网](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 类型的浮点数 x和 int 类型的整数 n,求 x 的 n 次方。 + +## 解题思路 + + + +最直观的解法是将 x 重复乘 n 次,x\*x\*x...\*x,那么时间复杂度为 O(N)。因为乘法是可交换的,所以可以将上述操作拆开成两半 (x\*x..\*x)\* (x\*x..\*x),两半的计算是一样的,因此只需要计算一次。而且对于新拆开的计算,又可以继续拆开。这就是分治思想,将原问题的规模拆成多个规模较小的子问题,最后子问题的解合并起来。 + +本题中子问题是 xn/2,在将子问题合并时将子问题的解乘于自身相乘即可。但如果 n 不为偶数,那么拆成两半还会剩下一个 x,在将子问题合并时还需要需要多乘于一个 x。 + + + +

+ + +因为 (x\*x)n/2 可以通过递归求解,并且每次递归 n 都减小一半,因此整个算法的时间复杂度为 O(logN)。 + +```java +public double Power(double x, int n) { + boolean isNegative = false; + if (n < 0) { + n = -n; + isNegative = true; + } + double res = pow(x, n); + return isNegative ? 1 / res : res; +} + +private double pow(double x, int n) { + if (n == 0) return 1; + if (n == 1) return x; + double res = pow(x, n / 2); + res = res * res; + if (n % 2 != 0) res *= x; + return res; +} +``` + diff --git "a/notes/17. \346\211\223\345\215\260\344\273\216 1 \345\210\260\346\234\200\345\244\247\347\232\204 n \344\275\215\346\225\260.md" "b/notes/17. \346\211\223\345\215\260\344\273\216 1 \345\210\260\346\234\200\345\244\247\347\232\204 n \344\275\215\346\225\260.md" new file mode 100644 index 0000000000..4a05f37970 --- /dev/null +++ "b/notes/17. \346\211\223\345\215\260\344\273\216 1 \345\210\260\346\234\200\345\244\247\347\232\204 n \344\275\215\346\225\260.md" @@ -0,0 +1,40 @@ +# 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(); +} +``` diff --git "a/notes/18.1 \345\234\250 O(1) \346\227\266\351\227\264\345\206\205\345\210\240\351\231\244\351\223\276\350\241\250\350\212\202\347\202\271.md" "b/notes/18.1 \345\234\250 O(1) \346\227\266\351\227\264\345\206\205\345\210\240\351\231\244\351\223\276\350\241\250\350\212\202\347\202\271.md" new file mode 100644 index 0000000000..365e18d442 --- /dev/null +++ "b/notes/18.1 \345\234\250 O(1) \346\227\266\351\227\264\345\206\205\345\210\240\351\231\244\351\223\276\350\241\250\350\212\202\347\202\271.md" @@ -0,0 +1,37 @@ +# 18.1 在 O(1) 时间内删除链表节点 + +## 解题思路 + +① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。 + +

+ +② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。 + +

+ +综上,如果进行 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; +} +``` diff --git "a/notes/18.2 \345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\351\207\215\345\244\215\347\232\204\347\273\223\347\202\271.md" "b/notes/18.2 \345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\351\207\215\345\244\215\347\232\204\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..5d1f871ba8 --- /dev/null +++ "b/notes/18.2 \345\210\240\351\231\244\351\223\276\350\241\250\344\270\255\351\207\215\345\244\215\347\232\204\347\273\223\347\202\271.md" @@ -0,0 +1,25 @@ +# 18.2 删除链表中重复的结点 + +[牛客网](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) + +## 题目描述 + +

+ +## 解题描述 + +```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; + } +} +``` diff --git "a/notes/19. \346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\214\271\351\205\215.md" "b/notes/19. \346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\214\271\351\205\215.md" new file mode 100644 index 0000000000..a0ad0053b2 --- /dev/null +++ "b/notes/19. \346\255\243\345\210\231\350\241\250\350\276\276\345\274\217\345\214\271\351\205\215.md" @@ -0,0 +1,40 @@ +# 19. 正则表达式匹配 + +[牛客网](https://www.nowcoder.com/practice/28970c15befb4ff3a264189087b99ad4?tpId=13&tqId=11205&tab=answerKey&from=cyc_github) + +## 题目描述 + +请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 + +在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配。 + +## 解题思路 + +应该注意到,'.' 是用来当做一个任意字符,而 '\*' 是用来重复前面的字符。这两个的作用不同,不能把 '.' 的作用和 '\*' 进行类比,从而把它当成重复前面字符一次。 + +```java +public boolean match(String str, String 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.charAt(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.charAt(i - 1) == pattern.charAt(j - 1) || pattern.charAt(j - 1) == '.') + dp[i][j] = dp[i - 1][j - 1]; + else if (pattern.charAt(j - 1) == '*') + if (pattern.charAt(j - 2) == str.charAt(i - 1) || pattern.charAt(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]; +} +``` diff --git "a/notes/20. \350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.md" "b/notes/20. \350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000000..da4b4bf685 --- /dev/null +++ "b/notes/20. \350\241\250\347\244\272\346\225\260\345\200\274\347\232\204\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,49 @@ +# 20. 表示数值的字符串 + +[牛客网](https://www.nowcoder.com/practice/e69148f8528c4039ad89bb2546fd4ff8?tpId=13&tqId=11206&tab=answerKey&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 (String str) { + if (str == null || str.length() == 0) + return false; + return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?"); +} +``` diff --git "a/notes/21. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" "b/notes/21. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" new file mode 100644 index 0000000000..13ca98e5c7 --- /dev/null +++ "b/notes/21. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" @@ -0,0 +1,64 @@ +# 21. 调整数组顺序使奇数位于偶数前面 + +## 题目链接 + +[牛客网](https://www.nowcoder.com/practice/ef1f53ef31ca408cada5093c8780f44b?tpId=13&tqId=11166&tab=answerKey&from=cyc_github) + +## 题目描述 + +需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。例如对于 [1,2,3,4,5],调整后得到 [1,3,5,2,4],而不能是 {5,1,3,4,2} 这种相对位置改变的结果。 + +

+ +## 解题思路 + +方法一:创建一个新数组,时间复杂度 O(N),空间复杂度 O(N)。 + +```java +public int[] 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; + } + return nums; +} + +private boolean isEven(int x) { + return x % 2 == 0; +} +``` + +方法二:使用冒泡思想,每次都将当前偶数上浮到当前最右边。时间复杂度 O(N2),空间复杂度 O(1),时间换空间。 + +```java +public int[] 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); + } + } + } + return nums; +} + +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; +} +``` diff --git "a/notes/22. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254 K \344\270\252\347\273\223\347\202\271.md" "b/notes/22. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254 K \344\270\252\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..aa00d274af --- /dev/null +++ "b/notes/22. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254 K \344\270\252\347\273\223\347\202\271.md" @@ -0,0 +1,27 @@ +# 22. 链表中倒数第 K 个结点 + +[牛客网](https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9?tpId=13&tqId=11167&tab=answerKey&from=cyc_github) + +## 解题思路 + +设链表的长度为 N。设置两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。 + +

+ +```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; +} +``` diff --git "a/notes/23. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" "b/notes/23. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..64c6f21b49 --- /dev/null +++ "b/notes/23. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" @@ -0,0 +1,43 @@ +# 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 每次移动一个节点。因为存在环,所以两个指针必定相遇在环中的某个节点上。 + +假设环入口节点为 y1,相遇所在节点为 z1。 + +假设快指针 fast 在圈内绕了 N 圈,则总路径长度为 x+Ny+(N-1)z。z 为 (N-1) 倍是因为快慢指针最后已经在 z1 节点相遇了,后面就不需要再走了。 + +而慢指针 slow 总路径长度为 x+y。 + +因为快指针是慢指针的两倍,因此 x+Ny+(N-1)z = 2(x+y)。 + +我们要找的是环入口节点 y1,也可以看成寻找长度 x 的值,因此我们先将上面的等值分解为和 x 有关:x=(N-2)y+(N-1)z。 + +上面的等值没有很强的规律,但是我们可以发现 y+z 就是圆环的总长度,因此我们将上面的等式再分解:x=(N-2)(y+z)+z。这个等式左边是从起点x1 到环入口节点 y1 的长度,而右边是在圆环中走过 (N-2) 圈,再从相遇点 z1 再走过长度为 z 的长度。此时我们可以发现如果让两个指针同时从起点 x1 和相遇点 z1 开始,每次只走过一个距离,那么最后他们会在环入口节点相遇。 + +

+ +```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; +} +``` diff --git "a/notes/24. \345\217\215\350\275\254\351\223\276\350\241\250.md" "b/notes/24. \345\217\215\350\275\254\351\223\276\350\241\250.md" new file mode 100644 index 0000000000..05bc439f63 --- /dev/null +++ "b/notes/24. \345\217\215\350\275\254\351\223\276\350\241\250.md" @@ -0,0 +1,36 @@ +# 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; +} +``` diff --git "a/notes/25. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" "b/notes/25. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" new file mode 100644 index 0000000000..ba0e6330d2 --- /dev/null +++ "b/notes/25. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" @@ -0,0 +1,51 @@ +# 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) + +## 题目描述 + +

+ +## 解题思路 + +### 递归 + +```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; +} +``` diff --git "a/notes/26. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" "b/notes/26. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" new file mode 100644 index 0000000000..bce9fbf9f1 --- /dev/null +++ "b/notes/26. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" @@ -0,0 +1,29 @@ +# 26. 树的子结构 + +## 题目链接 + +[牛客网](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) + +## 题目描述 + +

+ +## 解题思路 + +```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); +} +``` diff --git "a/notes/27. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" "b/notes/27. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" new file mode 100644 index 0000000000..18b5f0fa18 --- /dev/null +++ "b/notes/27. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" @@ -0,0 +1,26 @@ +# 27. 二叉树的镜像 + +[牛客网](https://www.nowcoder.com/practice/a9d0ecbacef9410ca97463e4a5c83be7?tpId=13&tqId=11171&tab=answerKey&from=cyc_github) + +## 题目描述 + +

+ +## 解题思路 + +```java +public TreeNode Mirror(TreeNode root) { + if (root == null) + return root; + swap(root); + Mirror(root.left); + Mirror(root.right); + return root; +} + +private void swap(TreeNode root) { + TreeNode t = root.left; + root.left = root.right; + root.right = t; +} +``` diff --git "a/notes/28. \345\257\271\347\247\260\347\232\204\344\272\214\345\217\211\346\240\221.md" "b/notes/28. \345\257\271\347\247\260\347\232\204\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..626b6a32c2 --- /dev/null +++ "b/notes/28. \345\257\271\347\247\260\347\232\204\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,27 @@ +# 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) + +## 题目描述 + +

+ +## 解题思路 + +```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); +} +``` diff --git "a/notes/29. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" "b/notes/29. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" new file mode 100644 index 0000000000..41c05939ff --- /dev/null +++ "b/notes/29. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" @@ -0,0 +1,44 @@ +# 29. 顺时针打印矩阵 + +## 题目链接 + +[牛客网](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 + +

+ + + +## 解题思路 + +一层一层从外到里打印,观察可知每一层打印都有相同的处理步骤,唯一不同的是上下左右的边界不同了。因此使用四个变量 r1, r2, c1, c2 分别存储上下左右边界值,从而定义当前最外层。打印当前最外层的顺序:从左到右打印最上一行-\>从上到下打印最右一行-\>从右到左打印最下一行-\>从下到上打印最左一行。应当注意只有在 r1 != r2 时才打印最下一行,也就是在当前最外层的行数大于 1 时才打印最下一行,这是因为当前最外层只有一行时,继续打印最下一行,会导致重复打印。打印最左一行也要做同样处理。 + +

+ +```java +public ArrayList printMatrix(int[][] matrix) { + ArrayList 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; +} +``` diff --git "a/notes/3. \346\225\260\347\273\204\344\270\255\351\207\215\345\244\215\347\232\204\346\225\260\345\255\227.md" "b/notes/3. \346\225\260\347\273\204\344\270\255\351\207\215\345\244\215\347\232\204\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..babab09dfd --- /dev/null +++ "b/notes/3. \346\225\260\347\273\204\344\270\255\351\207\215\345\244\215\347\232\204\346\225\260\345\255\227.md" @@ -0,0 +1,51 @@ +# 3. 数组中重复的数字 + +## 题目链接 + +[牛客网](https://www.nowcoder.com/practice/6fe361ede7e54db1b84adc81d09d8524?tpId=13&tqId=11203&tab=answerKey&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 重复: + +

+ + +```java +public int duplicate(int[] nums) { + for (int i = 0; i < nums.length; i++) { + while (nums[i] != i) { + if (nums[i] == nums[nums[i]]) { + return nums[i]; + } + swap(nums, i, nums[i]); + } + swap(nums, i, nums[i]); + } + return -1; +} + +private void swap(int[] nums, int i, int j) { + int t = nums[i]; + nums[i] = nums[j]; + nums[j] = t; +} + +``` + diff --git "a/notes/30. \345\214\205\345\220\253 min \345\207\275\346\225\260\347\232\204\346\240\210.md" "b/notes/30. \345\214\205\345\220\253 min \345\207\275\346\225\260\347\232\204\346\240\210.md" new file mode 100644 index 0000000000..32c76b6e3d --- /dev/null +++ "b/notes/30. \345\214\205\345\220\253 min \345\207\275\346\225\260\347\232\204\346\240\210.md" @@ -0,0 +1,38 @@ +# 30. 包含 min 函数的栈 + +## 题目链接 + +[牛客网](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() 函数的栈,该方法返回当前栈中最小的值。 + +## 解题思路 + +使用一个额外的 minStack,栈顶元素为当前栈中最小的值。在对栈进行 push 入栈和 pop 出栈操作时,同样需要对 minStack 进行入栈出栈操作,从而使 minStack 栈顶元素一直为当前栈中最小的值。在进行 push 操作时,需要比较入栈元素和当前栈中最小值,将值较小的元素 push 到 minStack 中。 + +

+ +```java +private Stack dataStack = new Stack<>(); +private Stack 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(); +} +``` diff --git "a/notes/31. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" "b/notes/31. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" new file mode 100644 index 0000000000..69817d9693 --- /dev/null +++ "b/notes/31. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" @@ -0,0 +1,32 @@ +# 31. 栈的压入、弹出序列 + +## 题目链接 + +[牛客网](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 就不可能是该压栈序列的弹出序列。 + +## 解题思路 + +使用一个栈来模拟压入弹出操作。每次入栈一个元素后,都要判断一下栈顶元素是不是当前出栈序列 popSequence 的第一个元素,如果是的话则执行出栈操作并将 popSequence 往后移一位,继续进行判断。 + +```java +public boolean IsPopOrder(int[] pushSequence, int[] popSequence) { + int n = pushSequence.length; + Stack 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(); +} +``` + diff --git "a/notes/32.1 \344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" "b/notes/32.1 \344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..da0d6d8b5d --- /dev/null +++ "b/notes/32.1 \344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,37 @@ +# 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 + +

+ +## 解题思路 + +使用队列来进行层次遍历。 + +不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 + +```java +public ArrayList PrintFromTopToBottom(TreeNode root) { + Queue queue = new LinkedList<>(); + ArrayList 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; +} +``` diff --git "a/notes/32.2 \346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.md" "b/notes/32.2 \346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.md" new file mode 100644 index 0000000000..2366963cde --- /dev/null +++ "b/notes/32.2 \346\212\212\344\272\214\345\217\211\346\240\221\346\211\223\345\215\260\346\210\220\345\244\232\350\241\214.md" @@ -0,0 +1,32 @@ +# 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> Print(TreeNode pRoot) { + ArrayList> ret = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.add(pRoot); + while (!queue.isEmpty()) { + ArrayList 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; +} +``` diff --git "a/notes/32.3 \346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" "b/notes/32.3 \346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..640caaadd5 --- /dev/null +++ "b/notes/32.3 \346\214\211\344\271\213\345\255\227\345\275\242\351\241\272\345\272\217\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,36 @@ +# 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> Print(TreeNode pRoot) { + ArrayList> ret = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.add(pRoot); + boolean reverse = false; + while (!queue.isEmpty()) { + ArrayList 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; +} +``` diff --git "a/notes/33. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" "b/notes/33. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" new file mode 100644 index 0000000000..167f22a129 --- /dev/null +++ "b/notes/33. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" @@ -0,0 +1,34 @@ +# 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 所对应的二叉搜索树。 + +

+ +## 解题思路 + +```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); +} +``` diff --git "a/notes/34. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" "b/notes/34. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000000..44ee850d70 --- /dev/null +++ "b/notes/34. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,36 @@ +# 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 + +

+ +## 解题思路 + +```java +private ArrayList> ret = new ArrayList<>(); + +public ArrayList> FindPath(TreeNode root, int target) { + backtracking(root, target, new ArrayList<>()); + return ret; +} + +private void backtracking(TreeNode node, int target, ArrayList 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); +} +``` diff --git "a/notes/35. \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266.md" "b/notes/35. \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266.md" new file mode 100644 index 0000000000..64401bf561 --- /dev/null +++ "b/notes/35. \345\244\215\346\235\202\351\223\276\350\241\250\347\232\204\345\244\215\345\210\266.md" @@ -0,0 +1,67 @@ +# 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; + } +} +``` + +

+ +## 解题思路 + +第一步,在每个节点的后面插入复制的节点。 + +

+ +第二步,对复制节点的 random 链接进行赋值。 + +

+ +第三步,拆分。 + +

+ +```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; +} +``` diff --git "a/notes/36. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\216\345\217\214\345\220\221\351\223\276\350\241\250.md" "b/notes/36. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\216\345\217\214\345\220\221\351\223\276\350\241\250.md" new file mode 100644 index 0000000000..58b61faf84 --- /dev/null +++ "b/notes/36. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\344\270\216\345\217\214\345\220\221\351\223\276\350\241\250.md" @@ -0,0 +1,34 @@ +# 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) + +## 题目描述 + +输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 + +

+ +## 解题思路 + +```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); +} +``` diff --git "a/notes/37. \345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.md" "b/notes/37. \345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..69ca339669 --- /dev/null +++ "b/notes/37. \345\272\217\345\210\227\345\214\226\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,39 @@ +# 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; +} +``` diff --git "a/notes/38. \345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.md" "b/notes/38. \345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.md" new file mode 100644 index 0000000000..149b37720c --- /dev/null +++ "b/notes/38. \345\255\227\347\254\246\344\270\262\347\232\204\346\216\222\345\210\227.md" @@ -0,0 +1,40 @@ +# 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 ret = new ArrayList<>(); + +public ArrayList 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; + } +} +``` diff --git "a/notes/39. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" "b/notes/39. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..5087783056 --- /dev/null +++ "b/notes/39. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" @@ -0,0 +1,27 @@ +# 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; +} +``` diff --git "a/notes/4. \344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" "b/notes/4. \344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" new file mode 100644 index 0000000000..6228115473 --- /dev/null +++ "b/notes/4. \344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" @@ -0,0 +1,49 @@ +# 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 和当前元素的大小关系来快速地缩小查找区间,每次减少一行或者一列的元素。当前元素的查找区间为左下角的所有元素。 + +

+ +```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; +} +``` diff --git "a/notes/40. \346\234\200\345\260\217\347\232\204 K \344\270\252\346\225\260.md" "b/notes/40. \346\234\200\345\260\217\347\232\204 K \344\270\252\346\225\260.md" new file mode 100644 index 0000000000..50bc3e4db3 --- /dev/null +++ "b/notes/40. \346\234\200\345\260\217\347\232\204 K \344\270\252\346\225\260.md" @@ -0,0 +1,85 @@ +# 40. 最小的 K 个数 + +## 题目链接 + +[牛客网](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) + +## 解题思路 + +### 大小为 K 的最小堆 + +- 复杂度:O(NlogK) + O(K) +- 特别适合处理海量数据 + +维护一个大小为 K 的最小堆过程如下:使用大顶堆。在添加一个元素之后,如果大顶堆的大小大于 K,那么将大顶堆的堆顶元素去除,也就是将当前堆中值最大的元素去除,从而使得留在堆中的元素都比被去除的元素来得小。 + +应该使用大顶堆来维护最小堆,而不能直接创建一个小顶堆并设置一个大小,企图让小顶堆中的元素都是最小元素。 + +Java 的 PriorityQueue 实现了堆的能力,PriorityQueue 默认是小顶堆,可以在在初始化时使用 Lambda 表达式 (o1, o2) -\> o2 - o1 来实现大顶堆。其它语言也有类似的堆数据结构。 + +```java +public ArrayList GetLeastNumbers_Solution(int[] nums, int k) { + if (k > nums.length || k <= 0) + return new ArrayList<>(); + PriorityQueue maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1); + for (int num : nums) { + maxHeap.add(num); + if (maxHeap.size() > k) + maxHeap.poll(); + } + return new ArrayList<>(maxHeap); +} +``` + +### 快速选择 + +- 复杂度: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 GetLeastNumbers_Solution(int[] nums, int k) { + ArrayList 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; +} +``` diff --git "a/notes/41.1 \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.md" "b/notes/41.1 \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.md" new file mode 100644 index 0000000000..9814db6302 --- /dev/null +++ "b/notes/41.1 \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260.md" @@ -0,0 +1,42 @@ +# 41.1 数据流中的中位数 + +## 题目链接 + +[牛客网](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 left = new PriorityQueue<>((o1, o2) -> o2 - o1); +/* 小顶堆,存储右半边元素,并且右半边元素都大于左半边 */ +private PriorityQueue 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(); +} +``` diff --git "a/notes/41.2 \345\255\227\347\254\246\346\265\201\344\270\255\347\254\254\344\270\200\344\270\252\344\270\215\351\207\215\345\244\215\347\232\204\345\255\227\347\254\246.md" "b/notes/41.2 \345\255\227\347\254\246\346\265\201\344\270\255\347\254\254\344\270\200\344\270\252\344\270\215\351\207\215\345\244\215\347\232\204\345\255\227\347\254\246.md" new file mode 100644 index 0000000000..85cca0d872 --- /dev/null +++ "b/notes/41.2 \345\255\227\347\254\246\346\265\201\344\270\255\347\254\254\344\270\200\344\270\252\344\270\215\351\207\215\345\244\215\347\232\204\345\255\227\347\254\246.md" @@ -0,0 +1,31 @@ +# 41.2 字符流中第一个不重复的字符 + +## 题目描述 + +[牛客网](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"。 + +## 解题思路 + +使用统计数组来统计每个字符出现的次数,本题涉及到的字符为都为 ASCII 码,因此使用一个大小为 128 的整型数组就能完成次数统计任务。 + +使用队列来存储到达的字符,并在每次有新的字符从字符流到达时移除队列头部那些出现次数不再是一次的元素。因为队列是先进先出顺序,因此队列头部的元素为第一次只出现一次的字符。 + +```java +private int[] cnts = new int[128]; +private Queue 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(); +} +``` diff --git "a/notes/42. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" "b/notes/42. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" new file mode 100644 index 0000000000..dce17c1e5d --- /dev/null +++ "b/notes/42. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" @@ -0,0 +1,24 @@ +# 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; +} +``` + diff --git "a/notes/43. \344\273\216 1 \345\210\260 n \346\225\264\346\225\260\344\270\255 1 \345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" "b/notes/43. \344\273\216 1 \345\210\260 n \346\225\264\346\225\260\344\270\255 1 \345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" new file mode 100644 index 0000000000..89323cc8d7 --- /dev/null +++ "b/notes/43. \344\273\216 1 \345\210\260 n \346\225\264\346\225\260\344\270\255 1 \345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" @@ -0,0 +1,18 @@ +# 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) diff --git "a/notes/44. \346\225\260\345\255\227\345\272\217\345\210\227\344\270\255\347\232\204\346\237\220\344\270\200\344\275\215\346\225\260\345\255\227.md" "b/notes/44. \346\225\260\345\255\227\345\272\217\345\210\227\344\270\255\347\232\204\346\237\220\344\270\200\344\275\215\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..3d869f3a3c --- /dev/null +++ "b/notes/44. \346\225\260\345\255\227\345\272\217\345\210\227\344\270\255\347\232\204\346\237\220\344\270\200\344\275\215\346\225\260\345\255\227.md" @@ -0,0 +1,54 @@ +# 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'; +} +``` diff --git "a/notes/45. \346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.md" "b/notes/45. \346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.md" new file mode 100644 index 0000000000..a8b261849b --- /dev/null +++ "b/notes/45. \346\212\212\346\225\260\347\273\204\346\216\222\346\210\220\346\234\200\345\260\217\347\232\204\346\225\260.md" @@ -0,0 +1,29 @@ +# 45. 把数组排成最小的数 + +## 题目链接 + +[牛客网](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; +} +``` diff --git "a/notes/46. \346\212\212\346\225\260\345\255\227\347\277\273\350\257\221\346\210\220\345\255\227\347\254\246\344\270\262.md" "b/notes/46. \346\212\212\346\225\260\345\255\227\347\277\273\350\257\221\346\210\220\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000000..fcbd92fea5 --- /dev/null +++ "b/notes/46. \346\212\212\346\225\260\345\255\227\347\277\273\350\257\221\346\210\220\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,31 @@ +# 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]; +} +``` diff --git "a/notes/47. \347\244\274\347\211\251\347\232\204\346\234\200\345\244\247\344\273\267\345\200\274.md" "b/notes/47. \347\244\274\347\211\251\347\232\204\346\234\200\345\244\247\344\273\267\345\200\274.md" new file mode 100644 index 0000000000..e46fb0d082 --- /dev/null +++ "b/notes/47. \347\244\274\347\211\251\347\232\204\346\234\200\345\244\247\344\273\267\345\200\274.md" @@ -0,0 +1,35 @@ +# 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]; +} +``` diff --git "a/notes/48. \346\234\200\351\225\277\344\270\215\345\220\253\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\345\255\220\345\255\227\347\254\246\344\270\262.md" "b/notes/48. \346\234\200\351\225\277\344\270\215\345\220\253\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\345\255\220\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000000..576c3905fa --- /dev/null +++ "b/notes/48. \346\234\200\351\225\277\344\270\215\345\220\253\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\345\255\220\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,29 @@ +# 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; +} +``` diff --git "a/notes/49. \344\270\221\346\225\260.md" "b/notes/49. \344\270\221\346\225\260.md" new file mode 100644 index 0000000000..f92d4d1221 --- /dev/null +++ "b/notes/49. \344\270\221\346\225\260.md" @@ -0,0 +1,30 @@ +# 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]; +} +``` diff --git "a/notes/5. \346\233\277\346\215\242\347\251\272\346\240\274.md" "b/notes/5. \346\233\277\346\215\242\347\251\272\346\240\274.md" new file mode 100644 index 0000000000..bd8086161c --- /dev/null +++ "b/notes/5. \346\233\277\346\215\242\347\251\272\346\240\274.md" @@ -0,0 +1,52 @@ +# 5. 替换空格 + +## 题目链接 + +[牛客网](https://www.nowcoder.com/practice/0e26e5551f2b489b9f58bc83aa4b6c68?tpId=13&tqId=11155&tab=answerKey&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),退出。 + + + +

+ +```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(); +} +``` diff --git "a/notes/50. \347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246\344\275\215\347\275\256.md" "b/notes/50. \347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246\344\275\215\347\275\256.md" new file mode 100644 index 0000000000..241ed13c31 --- /dev/null +++ "b/notes/50. \347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246\344\275\215\347\275\256.md" @@ -0,0 +1,53 @@ +# 50. 第一个只出现一次的字符位置 + +## 题目链接 + +[牛客网](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) + +## 题目描述 + +在一个字符串中找到第一个只出现一次的字符,并返回它的位置。字符串只包含 ASCII 码字符。 + +``` +Input: abacc +Output: b +``` + +## 解题思路 + +最直观的解法是使用 HashMap 对出现次数进行统计:字符做为 key,出现次数作为 value,遍历字符串每次都将 key 对应的 value 加 1。最后再遍历这个 HashMap 就可以找出出现次数为 1 的字符。 + +考虑到要统计的字符范围有限,也可以使用整型数组代替 HashMap。ASCII 码只有 128 个字符,因此可以使用长度为 128 的整型数组来存储每个字符出现的次数。 + +```java +public int FirstNotRepeatingChar(String str) { + int[] cnts = new int[128]; + 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(128); + BitSet bs2 = new BitSet(128); + 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; +} +``` diff --git "a/notes/51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271.md" "b/notes/51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271.md" new file mode 100644 index 0000000000..959857d913 --- /dev/null +++ "b/notes/51. \346\225\260\347\273\204\344\270\255\347\232\204\351\200\206\345\272\217\345\257\271.md" @@ -0,0 +1,48 @@ +# 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]; +} +``` diff --git "a/notes/52. \344\270\244\344\270\252\351\223\276\350\241\250\347\232\204\347\254\254\344\270\200\344\270\252\345\205\254\345\205\261\347\273\223\347\202\271.md" "b/notes/52. \344\270\244\344\270\252\351\223\276\350\241\250\347\232\204\347\254\254\344\270\200\344\270\252\345\205\254\345\205\261\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..bb96e3d9fd --- /dev/null +++ "b/notes/52. \344\270\244\344\270\252\351\223\276\350\241\250\347\232\204\347\254\254\344\270\200\344\270\252\345\205\254\345\205\261\347\273\223\347\202\271.md" @@ -0,0 +1,24 @@ +# 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) + +## 题目描述 + +

+ +## 解题思路 + +设 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; +} +``` diff --git "a/notes/53. \346\225\260\345\255\227\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" "b/notes/53. \346\225\260\345\255\227\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" new file mode 100644 index 0000000000..4e66780423 --- /dev/null +++ "b/notes/53. \346\225\260\345\255\227\345\234\250\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\347\232\204\346\254\241\346\225\260.md" @@ -0,0 +1,73 @@ +# 53. 数字在排序数组中出现的次数 + +## 题目链接 + +[牛客网](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 +``` + +## 解题思路 + +只要能找出给定的数字 k 在有序数组第一个位置和最后一个位置,就能知道该数字出现的次数。 + +先考虑如何实现寻找数字在有序数组的第一个位置。正常的二分查找如下,在查找到给定元素 k 之后,立即返回当前索引下标。 + +```java +public int binarySearch(int[] nums, int K) { + int l = 0, h = nums.length - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (nums[m] == K) { + return m; + } else if (nums[m] > K) { + h = m - 1; + } else { + l = m + 1; + } + } + return -1; +} +``` + +但是在查找第一个位置时,找到元素之后应该继续往前找。也就是当 nums[m]\>=k 时,在左区间继续查找,左区间应该包含 m 位置。 + +```java +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; +} +``` + +查找最后一个位置可以转换成寻找 k+1 的第一个位置,并再往前移动一个位置。 + +```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; +} +``` + +需要注意以上实现的查找第一个位置的 binarySearch 方法,h 的初始值为 nums.length,而不是 nums.length - 1。先看以下示例: + +``` +nums = [2,2], k = 2 +``` + +如果 h 的取值为 nums.length - 1,那么在查找最后一个位置时,binarySearch(nums, k + 1) - 1 = 1 - 1 = 0。这是因为 binarySearch 只会返回 [0, nums.length - 1] 范围的值,对于 binarySearch([2,2], 3) ,我们希望返回 3 插入 nums 中的位置,也就是数组最后一个位置再往后一个位置,即 nums.length。所以我们需要将 h 取值为 nums.length,从而使得 binarySearch 返回的区间更大,能够覆盖 k 大于 nums 最后一个元素的情况。 diff --git "a/notes/54. \344\272\214\345\217\211\346\237\245\346\211\276\346\240\221\347\232\204\347\254\254 K \344\270\252\347\273\223\347\202\271.md" "b/notes/54. \344\272\214\345\217\211\346\237\245\346\211\276\346\240\221\347\232\204\347\254\254 K \344\270\252\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..7f932fe9ed --- /dev/null +++ "b/notes/54. \344\272\214\345\217\211\346\237\245\346\211\276\346\240\221\347\232\204\347\254\254 K \344\270\252\347\273\223\347\202\271.md" @@ -0,0 +1,27 @@ +# 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); +} +``` diff --git "a/notes/55.1 \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" "b/notes/55.1 \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" new file mode 100644 index 0000000000..81d9e3946d --- /dev/null +++ "b/notes/55.1 \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" @@ -0,0 +1,17 @@ +# 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) + +## 题目描述 + +从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +

+ +## 解题思路 + +```java +public int TreeDepth(TreeNode root) { + return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right)); +} +``` diff --git "a/notes/55.2 \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" "b/notes/55.2 \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..12c57f4250 --- /dev/null +++ "b/notes/55.2 \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,30 @@ +# 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。 + +

+ +## 解题思路 + +```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); +} +``` diff --git "a/notes/56. \346\225\260\347\273\204\344\270\255\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" "b/notes/56. \346\225\260\347\273\204\344\270\255\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..4ab74ad3c3 --- /dev/null +++ "b/notes/56. \346\225\260\347\273\204\344\270\255\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" @@ -0,0 +1,45 @@ +# 56. 数组中只出现一次的数字 + +## 题目链接 + +[牛客网](https://www.nowcoder.com/practice/389fc1c3d3be4479a154f63f495abff8?tpId=13&tqId=11193&tab=answerKey&from=cyc_github) + +## 题目描述 + +一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。 + +## 解题思路 + +两个相等的元素异或的结果为 0,而 0 与任意数 x 异或的结果都为 x。 + +对本题给的数组的所有元素执行异或操作,得到的是两个不存在重复的元素异或的结果。例如对于数组 [x,x,y,y,z,k],x^x^y^y^z^k = 0^y^y^z^k = y^y^z^k = 0^z^k = z^k。 + +两个不相等的元素在位级表示上一定会有所不同,因此这两个元素异或得到的结果 diff 一定不为 0。位运算 diff & -diff 能得到 diff 位级表示中最右侧为 1 的位,令 diff = diff & -diff。将 diff 作为区分两个元素的依据,一定有一个元素对 diff 进行异或的结果为 0,另一个结果非 0。设不相等的两个元素分别为 z 和 k,遍历数组所有元素,判断元素与 diff 的异或结果是否为 0,如果是的话将元素与 z 进行异或并赋值给 z,否则与 k 进行异或并赋值给 k。数组中相等的元素一定会同时与 z 或者与 k 进行异或操作,而不是一个与 z 进行异或,一个与 k 进行异或。而且这些相等的元素异或的结果为 0,因此最后 z 和 k 只是不相等的两个元素与 0 异或的结果,也就是不相等两个元素本身。 + +下面的解法中,num1 和 num2 数组的第一个元素是用来保持返回值的... 实际开发中不推荐这种返回值的方式。 + +```java +public int[] FindNumsAppearOnce (int[] nums) { + int[] res = new int[2]; + int diff = 0; + for (int num : nums) + diff ^= num; + diff &= -diff; + for (int num : nums) { + if ((num & diff) == 0) + res[0] ^= num; + else + res[1] ^= num; + } + if (res[0] > res[1]) { + swap(res); + } + return res; +} + +private void swap(int[] nums) { + int t = nums[0]; + nums[0] = nums[1]; + nums[1] = t; +} +``` diff --git "a/notes/57.1 \345\222\214\344\270\272 S \347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.md" "b/notes/57.1 \345\222\214\344\270\272 S \347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.md" new file mode 100644 index 0000000000..8d15f13d91 --- /dev/null +++ "b/notes/57.1 \345\222\214\344\270\272 S \347\232\204\344\270\244\344\270\252\346\225\260\345\255\227.md" @@ -0,0 +1,33 @@ +# 57.1 和为 S 的两个数字 + +## 题目链接 + +[牛客网](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,输出两个数的乘积最小的。 + +## 解题思路 + +使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 + +- 如果两个指针指向元素的和 sum == target,那么这两个元素即为所求。 +- 如果 sum \> target,移动较大的元素,使 sum 变小一些; +- 如果 sum \< target,移动较小的元素,使 sum 变大一些。 + +```java +public ArrayList FindNumbersWithSum(int[] nums, int target) { + int i = 0, j = nums.length - 1; + while (i < j) { + int cur = nums[i] + array[j]; + if (cur == target) + return new ArrayList<>(Arrays.asList(nums[i], nums[j])); + if (cur < target) + i++; + else + j--; + } + return new ArrayList<>(); +} +``` diff --git "a/notes/57.2 \345\222\214\344\270\272 S \347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.md" "b/notes/57.2 \345\222\214\344\270\272 S \347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.md" new file mode 100644 index 0000000000..8501e8e06a --- /dev/null +++ "b/notes/57.2 \345\222\214\344\270\272 S \347\232\204\350\277\236\347\273\255\346\255\243\346\225\260\345\272\217\345\210\227.md" @@ -0,0 +1,43 @@ +# 57.2 和为 S 的连续正数序列 + +## 题目描述 + +[牛客网](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> FindContinuousSequence(int sum) { + ArrayList> 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 list = new ArrayList<>(); + for (int i = start; i <= end; i++) + list.add(i); + ret.add(list); + curSum -= start; + start++; + end++; + curSum += end; + } + } + return ret; +} +``` diff --git "a/notes/58.1 \347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217\345\210\227.md" "b/notes/58.1 \347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217\345\210\227.md" new file mode 100644 index 0000000000..684a5a3a8c --- /dev/null +++ "b/notes/58.1 \347\277\273\350\275\254\345\215\225\350\257\215\351\241\272\345\272\217\345\210\227.md" @@ -0,0 +1,49 @@ +# 58.1 翻转单词顺序列 + +## 题目描述 + +[牛客网](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; +} +``` diff --git "a/notes/58.2 \345\267\246\346\227\213\350\275\254\345\255\227\347\254\246\344\270\262.md" "b/notes/58.2 \345\267\246\346\227\213\350\275\254\345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000000..576d83c9a4 --- /dev/null +++ "b/notes/58.2 \345\267\246\346\227\213\350\275\254\345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,45 @@ +# 58.2 左旋转字符串 + +## 题目链接 + +[牛客网](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) + +## 题目描述 + +将字符串 S 从第 K 位置分隔成两个子字符串,并交换这两个子字符串的位置。 + +```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; +} +``` diff --git "a/notes/59. \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.md" "b/notes/59. \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.md" new file mode 100644 index 0000000000..2b47ead0b6 --- /dev/null +++ "b/notes/59. \346\273\221\345\212\250\347\252\227\345\217\243\347\232\204\346\234\200\345\244\247\345\200\274.md" @@ -0,0 +1,37 @@ +# 59. 滑动窗口的最大值 + +## 题目链接 + +[牛客网](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}。 + +

+ +## 解题思路 + +维护一个大小为窗口大小的大顶堆,顶堆元素则为当前窗口的最大值。 + +假设窗口的大小为 M,数组的长度为 N。在窗口向右移动时,需要先在堆中删除离开窗口的元素,并将新到达的元素添加到堆中,这两个操作的时间复杂度都为 log2M,因此算法的时间复杂度为 O(Nlog2M),空间复杂度为 O(M)。 + +```java +public ArrayList maxInWindows(int[] num, int size) { + ArrayList ret = new ArrayList<>(); + if (size > num.length || size < 1) + return ret; + PriorityQueue 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; +} +``` diff --git "a/notes/6. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" "b/notes/6. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" new file mode 100644 index 0000000000..9e3f598b23 --- /dev/null +++ "b/notes/6. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" @@ -0,0 +1,89 @@ +# 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) + +## 题目描述 + +从尾到头反过来打印出每个结点的值。 + +

+ +## 解题思路 + +### 1. 使用递归 + +要逆序打印链表 1-\>2-\>3(3,2,1),可以先逆序打印链表 2-\>3(3,2),最后再打印第一个节点 1。而链表 2-\>3 可以看成一个新的链表,要逆序打印该链表可以继续使用求解函数,也就是在求解函数中调用自己,这就是递归函数。 + +```java +public ArrayList printListFromTailToHead(ListNode listNode) { + ArrayList 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; +``` + +

+ + + +为了能将一个节点插入头部,我们引入了一个叫头结点的辅助节点,该节点不存储值,只是为了方便进行插入操作。不要将头结点与第一个节点混起来,第一个节点是链表中第一个真正存储值的节点。 + +

+ +```java +public ArrayList 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 ret = new ArrayList<>(); + head = head.next; + while (head != null) { + ret.add(head.val); + head = head.next; + } + return ret; +} +``` + +### 3. 使用栈 + +栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。 + +

+ +```java +public ArrayList printListFromTailToHead(ListNode listNode) { + Stack stack = new Stack<>(); + while (listNode != null) { + stack.add(listNode.val); + listNode = listNode.next; + } + ArrayList ret = new ArrayList<>(); + while (!stack.isEmpty()) + ret.add(stack.pop()); + return ret; +} +``` diff --git "a/notes/60. n \344\270\252\351\252\260\345\255\220\347\232\204\347\202\271\346\225\260.md" "b/notes/60. n \344\270\252\351\252\260\345\255\220\347\232\204\347\202\271\346\225\260.md" new file mode 100644 index 0000000000..9da2c1c008 --- /dev/null +++ "b/notes/60. n \344\270\252\351\252\260\345\255\220\347\232\204\347\202\271\346\225\260.md" @@ -0,0 +1,74 @@ +# 60. n 个骰子的点数 + +## 题目链接 + +[Lintcode](https://www.lintcode.com/en/problem/dices-sum/) + +## 题目描述 + +把 n 个骰子扔在地上,求点数和为 s 的概率。 + +

+ +## 解题思路 + +### 动态规划 + +使用一个二维数组 dp 存储点数出现的次数,其中 dp\[i]\[j] 表示前 i 个骰子产生点数 j 的次数。 + +空间复杂度:O(N2) + +```java +public List> 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> 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> 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> ret = new ArrayList<>(); + for (int i = n; i <= pointNum; i++) + ret.add(new AbstractMap.SimpleEntry<>(i, dp[1 - flag][i] / totalNum)); + + return ret; +} +``` diff --git "a/notes/61. \346\211\221\345\205\213\347\211\214\351\241\272\345\255\220.md" "b/notes/61. \346\211\221\345\205\213\347\211\214\351\241\272\345\255\220.md" new file mode 100644 index 0000000000..9948e0dc8e --- /dev/null +++ "b/notes/61. \346\211\221\345\205\213\347\211\214\351\241\272\345\255\220.md" @@ -0,0 +1,39 @@ +# 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。判断这五张牌是否能组成顺子。 + +

+ + +## 解题思路 + +```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; +} +``` diff --git "a/notes/62. \345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260.md" "b/notes/62. \345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260.md" new file mode 100644 index 0000000000..47929d9c53 --- /dev/null +++ "b/notes/62. \345\234\206\345\234\210\344\270\255\346\234\200\345\220\216\345\211\251\344\270\213\347\232\204\346\225\260.md" @@ -0,0 +1,23 @@ +# 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; +} +``` diff --git "a/notes/63. \350\202\241\347\245\250\347\232\204\346\234\200\345\244\247\345\210\251\346\266\246.md" "b/notes/63. \350\202\241\347\245\250\347\232\204\346\234\200\345\244\247\345\210\251\346\266\246.md" new file mode 100644 index 0000000000..232d2b2f22 --- /dev/null +++ "b/notes/63. \350\202\241\347\245\250\347\232\204\346\234\200\345\244\247\345\210\251\346\266\246.md" @@ -0,0 +1,29 @@ +# 63. 股票的最大利润 + +## 题目链接 + +[Leetcode:121. Best Time to Buy and Sell Stock ](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/) + +## 题目描述 + +可以有一次买入和一次卖出,买入必须在前。求最大收益。 + +

+ +## 解题思路 + +使用贪心策略,假设第 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; +} +``` diff --git "a/notes/64. \346\261\202 1+2+3+...+n.md" "b/notes/64. \346\261\202 1+2+3+...+n.md" new file mode 100644 index 0000000000..38d7680bc0 --- /dev/null +++ "b/notes/64. \346\261\202 1+2+3+...+n.md" @@ -0,0 +1,25 @@ +# 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; +} +``` diff --git "a/notes/65. \344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.md" "b/notes/65. \344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.md" new file mode 100644 index 0000000000..54d4c53fdd --- /dev/null +++ "b/notes/65. \344\270\215\347\224\250\345\212\240\345\207\217\344\271\230\351\231\244\345\201\232\345\212\240\346\263\225.md" @@ -0,0 +1,21 @@ +# 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); +} +``` diff --git "a/notes/66. \346\236\204\345\273\272\344\271\230\347\247\257\346\225\260\347\273\204.md" "b/notes/66. \346\236\204\345\273\272\344\271\230\347\247\257\346\225\260\347\273\204.md" new file mode 100644 index 0000000000..c1d94a08af --- /dev/null +++ "b/notes/66. \346\236\204\345\273\272\344\271\230\347\247\257\346\225\260\347\273\204.md" @@ -0,0 +1,26 @@ +# 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]。要求不能使用除法。 + +

+ + +## 解题思路 + +```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; +} +``` diff --git "a/notes/67. \346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.md" "b/notes/67. \346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.md" new file mode 100644 index 0000000000..19fdd29098 --- /dev/null +++ "b/notes/67. \346\212\212\345\255\227\347\254\246\344\270\262\350\275\254\346\215\242\346\210\220\346\225\264\346\225\260.md" @@ -0,0 +1,39 @@ +# 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; +} +``` diff --git "a/notes/68. \346\240\221\344\270\255\344\270\244\344\270\252\350\212\202\347\202\271\347\232\204\346\234\200\344\275\216\345\205\254\345\205\261\347\245\226\345\205\210.md" "b/notes/68. \346\240\221\344\270\255\344\270\244\344\270\252\350\212\202\347\202\271\347\232\204\346\234\200\344\275\216\345\205\254\345\205\261\347\245\226\345\205\210.md" new file mode 100644 index 0000000000..9113ba6b13 --- /dev/null +++ "b/notes/68. \346\240\221\344\270\255\344\270\244\344\270\252\350\212\202\347\202\271\347\232\204\346\234\200\344\275\216\345\205\254\345\205\261\347\245\226\345\205\210.md" @@ -0,0 +1,48 @@ +# 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。 + +

+ +```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 分别在两个子树中,那么就说明根节点就是最低公共祖先。 + +

+ +```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; +} +``` diff --git "a/notes/7. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" "b/notes/7. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000000..76017ca347 --- /dev/null +++ "b/notes/7. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,41 @@ +# 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) + +## 题目描述 + +根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 + + + +

+ +## 解题思路 + +前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。然后分别对左右子树递归地求解。 + +

+ +```java +// 缓存中序遍历数组每个值对应的索引 +private Map 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; +} +``` diff --git "a/notes/8. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\213\344\270\200\344\270\252\347\273\223\347\202\271.md" "b/notes/8. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\213\344\270\200\344\270\252\347\273\223\347\202\271.md" new file mode 100644 index 0000000000..776db74418 --- /dev/null +++ "b/notes/8. \344\272\214\345\217\211\346\240\221\347\232\204\344\270\213\344\270\200\344\270\252\347\273\223\347\202\271.md" @@ -0,0 +1,67 @@ +# 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); +} +``` + +

+ + + +① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点; + +

+ +② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 + +

+ +```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; +} +``` diff --git "a/notes/9. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" "b/notes/9. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" new file mode 100644 index 0000000000..9d67a9759f --- /dev/null +++ "b/notes/9. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" @@ -0,0 +1,35 @@ +# 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 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。 + +

+ +```java +Stack in = new Stack(); +Stack out = new Stack(); + +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(); +} +``` diff --git a/notes/Docker.md b/notes/Docker.md new file mode 100644 index 0000000000..ecebdfc34e --- /dev/null +++ b/notes/Docker.md @@ -0,0 +1,91 @@ +# Docker + +* [Docker](#docker) + * [一、解决的问题](#一解决的问题) + * [二、与虚拟机的比较](#二与虚拟机的比较) + * [三、优势](#三优势) + * [四、使用场景](#四使用场景) + * [五、镜像与容器](#五镜像与容器) + * [参考资料](#参考资料) + + + +## 一、解决的问题 + +由于不同的机器有不同的操作系统,以及不同的库和组件,在将一个应用部署到多台机器上需要进行大量的环境配置操作。 + +Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其它机器上。 + +

+ +## 二、与虚拟机的比较 + +虚拟机也是一种虚拟化技术,它与 Docker 最大的区别在于它是通过模拟硬件,并在硬件上安装操作系统来实现。 + +

+ +### 启动速度 + +启动虚拟机需要先启动虚拟机的操作系统,再启动应用,这个过程非常慢; + +而启动 Docker 相当于启动宿主操作系统上的一个进程。 + +### 占用资源 + +虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU 资源,一台机器只能开启几十个的虚拟机。 + +而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。 + +## 三、优势 + +除了启动速度快以及占用资源少之外,Docker 具有以下优势: + +### 更容易迁移 + +提供一致性的运行环境。已经打包好的应用可以在不同的机器上进行迁移,而不用担心环境变化导致无法运行。 + +### 更容易维护 + +使用分层技术和镜像,使得应用可以更容易复用重复的部分。复用程度越高,维护工作也越容易。 + +### 更容易扩展 + +可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像可以非常容易得到我们想要的镜像。 + +## 四、使用场景 + +### 持续集成 + +持续集成指的是频繁地将代码集成到主干上,这样能够更快地发现错误。 + +Docker 具有轻量级以及隔离性的特点,在将代码集成到一个 Docker 中不会对其它 Docker 产生影响。 + +### 提供可伸缩的云服务 + +根据应用的负载情况,可以很容易地增加或者减少 Docker。 + +### 搭建微服务架构 + +Docker 轻量级的特点使得它很适合用于部署、维护、组合微服务。 + +## 五、镜像与容器 + +镜像是一种静态的结构,可以看成面向对象里面的类,而容器是镜像的一个实例。 + +镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。 + +构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 + +

+ +## 参考资料 + +- [DOCKER 101: INTRODUCTION TO DOCKER WEBINAR RECAP](https://blog.docker.com/2017/08/docker-101-introduction-docker-webinar-recap/) +- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) +- [Docker container vs Virtual machine](http://www.bogotobogo.com/DevOps/Docker/Docker_Container_vs_Virtual_Machine.php) +- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/) +- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html) +- [为什么要使用 Docker?](https://yeasy.gitbooks.io/docker_practice/introduction/why.html) +- [What is Docker](https://www.docker.com/what-docker) +- [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) + diff --git a/notes/Git.md b/notes/Git.md new file mode 100644 index 0000000000..dbc57e22a6 --- /dev/null +++ b/notes/Git.md @@ -0,0 +1,153 @@ +# Git + +* [Git](#git) + * [集中式与分布式](#集中式与分布式) + * [中心服务器](#中心服务器) + * [工作流](#工作流) + * [分支实现](#分支实现) + * [冲突](#冲突) + * [Fast forward](#fast-forward) + * [储藏(Stashing)](#储藏stashing) + * [SSH 传输设置](#ssh-传输设置) + * [.gitignore 文件](#gitignore-文件) + * [Git 命令一览](#git-命令一览) + * [参考资料](#参考资料) + + + +## 集中式与分布式 + +Git 属于分布式版本控制系统,而 SVN 属于集中式。 + +

+ +集中式版本控制只有中心服务器拥有一份代码,而分布式版本控制每个人的电脑上就有一份完整的代码。 + +集中式版本控制有安全性问题,当中心服务器挂了所有人都没办法工作了。 + +集中式版本控制需要连网才能工作,如果网速过慢,那么提交一个文件会慢的无法让人忍受。而分布式版本控制不需要连网就能工作。 + +分布式版本控制新建分支、合并分支操作速度非常快,而集中式版本控制新建一个分支相当于复制一份完整代码。 + +## 中心服务器 + +中心服务器用来交换每个用户的修改,没有中心服务器也能工作,但是中心服务器能够 24 小时保持开机状态,这样就能更方便的交换修改。 + +Github 就是一个中心服务器。 + +## 工作流 + +新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。 + +Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库,History 存储所有分支信息,使用一个 HEAD 指针指向当前分支。 + +

+ +- git add files 把文件的修改添加到暂存区 +- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了 +- git reset -- files 使用当前分支上的修改覆盖暂存区,用来撤销最后一次 git add files +- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改 + +

+ +可以跳过暂存区域直接从分支中取出修改,或者直接提交修改到分支中。 + +- git commit -a 直接把所有文件的修改添加到暂存区然后执行提交 +- git checkout HEAD -- files 取出最后一次修改,可以用来进行回滚操作 + +

+ +## 分支实现 + +使用指针将每个提交连接成一条时间线,HEAD 指针指向当前分支指针。 + +

+ +新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支,表示新分支成为当前分支。 + +

+ +每次提交只会让当前分支指针向前移动,而其它分支指针不会移动。 + +

+ +合并分支也只需要改变指针即可。 + +

+ +## 冲突 + +当两个分支都对同一个文件的同一行进行了修改,在分支合并时就会产生冲突。 + +

+ +Git 会使用 \<\<\<\<\<\<\< ,======= ,\>\>\>\>\>\>\> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。 + +``` +<<<<<<< HEAD +Creating a new branch is quick & simple. +======= +Creating a new branch is quick AND simple. +>>>>>>> feature1 +``` + +## Fast forward + +"快进式合并"(fast-farward merge),会直接将 master 分支指向合并的分支,这种模式下进行分支合并会丢失分支信息,也就不能在分支历史上看出分支信息。 + +可以在合并时加上 --no-ff 参数来禁用 Fast forward 模式,并且加上 -m 参数让合并时产生一个新的 commit。 + +``` +$ git merge --no-ff -m "merge with no-ff" dev +``` + +

+ +## 储藏(Stashing) + +在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。 + +可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈中,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。 + +``` +$ git stash +Saved working directory and index state \ "WIP on master: 049d078 added the index file" +HEAD is now at 049d078 added the index file (To restore them type "git stash apply") +``` + +该功能可以用于 bug 分支的实现。如果当前正在 dev 分支上进行开发,但是此时 master 上有个 bug 需要修复,但是 dev 分支上的开发还未完成,不想立即提交。在新建 bug 分支并切换到 bug 分支之前就需要使用 git stash 将 dev 分支的未提交修改储藏起来。 + +## SSH 传输设置 + +Git 仓库和 Github 中心仓库之间的传输是通过 SSH 加密。 + +如果工作区下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key: + +``` +$ ssh-keygen -t rsa -C "youremail@example.com" +``` + +然后把公钥 id_rsa.pub 的内容复制到 Github "Account settings" 的 SSH Keys 中。 + +## .gitignore 文件 + +忽略以下文件: + +- 操作系统自动生成的文件,比如缩略图; +- 编译生成的中间文件,比如 Java 编译产生的 .class 文件; +- 自己的敏感信息,比如存放口令的配置文件。 + +不需要全部自己编写,可以到 [https://github.com/github/gitignore](https://github.com/github/gitignore) 中进行查询。 + +## Git 命令一览 + +

+ +比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf + +## 参考资料 + +- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) +- [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) +- [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) +- [Learn Git Branching](https://learngitbranching.js.org/) diff --git a/notes/HTTP.md b/notes/HTTP.md new file mode 100644 index 0000000000..35e649948b --- /dev/null +++ b/notes/HTTP.md @@ -0,0 +1,943 @@ +# HTTP + +* [HTTP](#http) + * [一 、基础概念](#一-基础概念) + * [请求和响应报文](#请求和响应报文) + * [URL](#url) + * [二、HTTP 方法](#二http-方法) + * [GET](#get) + * [HEAD](#head) + * [POST](#post) + * [PUT](#put) + * [PATCH](#patch) + * [DELETE](#delete) + * [OPTIONS](#options) + * [CONNECT](#connect) + * [TRACE](#trace) + * [三、HTTP 状态码](#三http-状态码) + * [1XX 信息](#1xx-信息) + * [2XX 成功](#2xx-成功) + * [3XX 重定向](#3xx-重定向) + * [4XX 客户端错误](#4xx-客户端错误) + * [5XX 服务器错误](#5xx-服务器错误) + * [四、HTTP 首部](#四http-首部) + * [通用首部字段](#通用首部字段) + * [请求首部字段](#请求首部字段) + * [响应首部字段](#响应首部字段) + * [实体首部字段](#实体首部字段) + * [五、具体应用](#五具体应用) + * [连接管理](#连接管理) + * [Cookie](#cookie) + * [缓存](#缓存) + * [内容协商](#内容协商) + * [内容编码](#内容编码) + * [范围请求](#范围请求) + * [分块传输编码](#分块传输编码) + * [多部分对象集合](#多部分对象集合) + * [虚拟主机](#虚拟主机) + * [通信数据转发](#通信数据转发) + * [六、HTTPS](#六https) + * [加密](#加密) + * [认证](#认证) + * [完整性保护](#完整性保护) + * [HTTPS 的缺点](#https-的缺点) + * [七、HTTP/2.0](#七http20) + * [HTTP/1.x 缺陷](#http1x-缺陷) + * [二进制分帧层](#二进制分帧层) + * [服务端推送](#服务端推送) + * [首部压缩](#首部压缩) + * [八、HTTP/1.1 新特性](#八http11-新特性) + * [九、GET 和 POST 比较](#九get-和-post-比较) + * [作用](#作用) + * [参数](#参数) + * [安全](#安全) + * [幂等性](#幂等性) + * [可缓存](#可缓存) + * [XMLHttpRequest](#xmlhttprequest) + * [参考资料](#参考资料) + + + +## 一 、基础概念 + +### 请求和响应报文 + +客户端发送一个请求报文给服务器,服务器根据请求报文中的信息进行处理,并将处理结果放入响应报文中返回给客户端。 + +请求报文结构: + +- 第一行是包含了请求方法、URL、协议版本; +- 接下来的多行都是请求首部 Header,每个首部都有一个首部名称,以及对应的值。 +- 一个空行用来分隔首部和内容主体 Body +- 最后是请求的内容主体 + +``` +GET http://www.example.com/ HTTP/1.1 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 +Accept-Encoding: gzip, deflate +Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 +Cache-Control: max-age=0 +Host: www.example.com +If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT +If-None-Match: "3147526947+gzip" +Proxy-Connection: keep-alive +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 xxx + +param1=1¶m2=2 +``` + +响应报文结构: + +- 第一行包含协议版本、状态码以及描述,最常见的是 200 OK 表示请求成功了 +- 接下来多行也是首部内容 +- 一个空行分隔首部和内容主体 +- 最后是响应的内容主体 + +``` +HTTP/1.1 200 OK +Age: 529651 +Cache-Control: max-age=604800 +Connection: keep-alive +Content-Encoding: gzip +Content-Length: 648 +Content-Type: text/html; charset=UTF-8 +Date: Mon, 02 Nov 2020 17:53:39 GMT +Etag: "3147526947+ident+gzip" +Expires: Mon, 09 Nov 2020 17:53:39 GMT +Keep-Alive: timeout=4 +Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT +Proxy-Connection: keep-alive +Server: ECS (sjc/16DF) +Vary: Accept-Encoding +X-Cache: HIT + + + + + Example Domain + // 省略... + + + +``` + +### URL + +HTTP 使用 URL( **U** niform **R**esource **L**ocator,统一资源定位符)来定位资源,它是 URI(**U**niform **R**esource **I**dentifier,统一资源标识符)的子集,URL 在 URI 的基础上增加了定位能力。URI 除了包含 URL,还包含 URN(Uniform Resource Name,统一资源名称),它只是用来定义一个资源的名称,并不具备定位该资源的能力。例如 urn:isbn:0451450523 用来定义一个书籍名称,但是却没有表示怎么找到这本书。 + +

+ +- [wikipedia:统一资源标志符](https://zh.wikipedia.org/wiki/统一资源标志符) +- [wikipedia: URL](https://en.wikipedia.org/wiki/URL) +- [rfc2616:3.2.2 http URL](https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.2) +- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) + +## 二、HTTP 方法 + +客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 + +### GET + +> 获取资源 + +当前网络请求中,绝大部分使用的是 GET 方法。 + +### HEAD + +> 获取报文首部 + +和 GET 方法类似,但是不返回报文实体主体部分。 + +主要用于确认 URL 的有效性以及资源更新的日期时间等。 + +### POST + +> 传输实体主体 + +POST 主要用来传输数据,而 GET 主要用来获取资源。 + +更多 POST 与 GET 的比较请见第九章。 + +### PUT + +> 上传文件 + +由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般不使用该方法。 + +```html +PUT /new.html HTTP/1.1 +Host: example.com +Content-type: text/html +Content-length: 16 + +

New File

+``` + +### PATCH + +> 对资源进行部分修改 + +PUT 也可以用于修改资源,但是只能完全替代原始资源,PATCH 允许部分修改。 + +```html +PATCH /file.txt HTTP/1.1 +Host: www.example.com +Content-Type: application/example +If-Match: "e0023aa4e" +Content-Length: 100 + +[description of changes] +``` + +### DELETE + +> 删除文件 + +与 PUT 功能相反,并且同样不带验证机制。 + +```html +DELETE /file.html HTTP/1.1 +``` + +### OPTIONS + +> 查询支持的方法 + +查询指定的 URL 能够支持的方法。 + +会返回 `Allow: GET, POST, HEAD, OPTIONS` 这样的内容。 + +### CONNECT + +> 要求在与代理服务器通信时建立隧道 + +使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 + +```html +CONNECT www.example.com:443 HTTP/1.1 +``` + +

+ +### TRACE + +> 追踪路径 + +服务器会将通信路径返回给客户端。 + +发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。 + +通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)。 + +- [rfc2616:9 Method Definitions](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html) + +## 三、HTTP 状态码 + +服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 + +| 状态码 | 类别 | 含义 | +| :---: | :---: | :---: | +| 1XX | Informational(信息性状态码) | 接收的请求正在处理 | +| 2XX | Success(成功状态码) | 请求正常处理完毕 | +| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 | +| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 | +| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 | + +### 1XX 信息 + +- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 + +### 2XX 成功 + +- **200 OK** + +- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 + +- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 + +### 3XX 重定向 + +- **301 Moved Permanently** :永久性重定向 + +- **302 Found** :临时性重定向 + +- **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 状态码。 + +- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 + +### 4XX 客户端错误 + +- **400 Bad Request** :请求报文中存在语法错误。 + +- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 + +- **403 Forbidden** :请求被拒绝。 + +- **404 Not Found** + +### 5XX 服务器错误 + +- **500 Internal Server Error** :服务器正在执行请求时发生错误。 + +- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 + +## 四、HTTP 首部 + +有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。 + +各种首部字段及其含义如下(不需要全记,仅供查阅): + +### 通用首部字段 + +| 首部字段名 | 说明 | +| :--: | :--: | +| Cache-Control | 控制缓存的行为 | +| Connection | 控制不再转发给代理的首部字段、管理持久连接| +| Date | 创建报文的日期时间 | +| Pragma | 报文指令 | +| Trailer | 报文末端的首部一览 | +| Transfer-Encoding | 指定报文主体的传输编码方式 | +| Upgrade | 升级为其他协议 | +| Via | 代理服务器的相关信息 | +| Warning | 错误通知 | + +### 请求首部字段 + +| 首部字段名 | 说明 | +| :--: | :--: | +| Accept | 用户代理可处理的媒体类型 | +| Accept-Charset | 优先的字符集 | +| Accept-Encoding | 优先的内容编码 | +| Accept-Language | 优先的语言(自然语言) | +| Authorization | Web 认证信息 | +| Expect | 期待服务器的特定行为 | +| From | 用户的电子邮箱地址 | +| Host | 请求资源所在服务器 | +| If-Match | 比较实体标记(ETag) | +| If-Modified-Since | 比较资源的更新时间 | +| If-None-Match | 比较实体标记(与 If-Match 相反) | +| If-Range | 资源未更新时发送实体 Byte 的范围请求 | +| If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) | +| Max-Forwards | 最大传输逐跳数 | +| Proxy-Authorization | 代理服务器要求客户端的认证信息 | +| Range | 实体的字节范围请求 | +| Referer | 对请求中 URI 的原始获取方 | +| TE | 传输编码的优先级 | +| User-Agent | HTTP 客户端程序的信息 | + +### 响应首部字段 + +| 首部字段名 | 说明 | +| :--: | :--: | +| Accept-Ranges | 是否接受字节范围请求 | +| Age | 推算资源创建经过时间 | +| ETag | 资源的匹配信息 | +| Location | 令客户端重定向至指定 URI | +| Proxy-Authenticate | 代理服务器对客户端的认证信息 | +| Retry-After | 对再次发起请求的时机要求 | +| Server | HTTP 服务器的安装信息 | +| Vary | 代理服务器缓存的管理信息 | +| WWW-Authenticate | 服务器对客户端的认证信息 | + +### 实体首部字段 + +| 首部字段名 | 说明 | +| :--: | :--: | +| Allow | 资源可支持的 HTTP 方法 | +| Content-Encoding | 实体主体适用的编码方式 | +| Content-Language | 实体主体的自然语言 | +| Content-Length | 实体主体的大小 | +| Content-Location | 替代对应资源的 URI | +| Content-MD5 | 实体主体的报文摘要 | +| Content-Range | 实体主体的位置范围 | +| Content-Type | 实体主体的媒体类型 | +| Expires | 实体主体过期的日期时间 | +| Last-Modified | 资源的最后修改日期时间 | + +## 五、具体应用 + +### 连接管理 + +

+ +#### 1. 短连接与长连接 + +当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问的 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。 + +长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。 + +- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 `Connection : close`; +- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 `Connection : Keep-Alive`。 + +#### 2. 流水线 + +默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。 + +流水线是在同一条长连接上连续发出请求,而不用等待响应返回,这样可以减少延迟。 + +### Cookie + +HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。 + +Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。 + +Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API(本地存储和会话存储)或 IndexedDB。 + +#### 1. 用途 + +- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) +- 个性化设置(如用户自定义设置、主题等) +- 浏览器行为跟踪(如跟踪分析用户行为等) + +#### 2. 创建过程 + +服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 + +```html +HTTP/1.0 200 OK +Content-type: text/html +Set-Cookie: yummy_cookie=choco +Set-Cookie: tasty_cookie=strawberry + +[page content] +``` + +客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。 + +```html +GET /sample_page.html HTTP/1.1 +Host: www.example.org +Cookie: yummy_cookie=choco; tasty_cookie=strawberry +``` + +#### 3. 分类 + +- 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 +- 持久性 Cookie:指定过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 + +```html +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; +``` + +#### 4. 作用域 + +Domain 标识指定了哪些主机可以接受 Cookie。如果不指定,默认为当前文档的主机(不包含子域名)。如果指定了 Domain,则一般包含子域名。例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。 + +Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。例如,设置 Path=/docs,则以下地址都会匹配: + +- /docs +- /docs/Web/ +- /docs/Web/HTTP + +#### 5. JavaScript + +浏览器通过 `document.cookie` 属性可创建新的 Cookie,也可通过该属性访问非 HttpOnly 标记的 Cookie。 + +```html +document.cookie = "yummy_cookie=choco"; +document.cookie = "tasty_cookie=strawberry"; +console.log(document.cookie); +``` + +#### 6. HttpOnly + +标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。 + +```html +Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly +``` + +#### 7. Secure + +标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。 + +#### 8. Session + +除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。 + +Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。 + +使用 Session 维护用户登录状态的过程如下: + +- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; +- 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID; +- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; +- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。 + +应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。 + +#### 9. 浏览器禁用 Cookie + +此时无法使用 Cookie 来保存用户信息,只能使用 Session。除此之外,不能再将 Session ID 存放到 Cookie 中,而是使用 URL 重写技术,将 Session ID 作为 URL 的参数进行传递。 + +#### 10. Cookie 与 Session 选择 + +- Cookie 只能存储 ASCII 码字符串,而 Session 则可以存储任何类型的数据,因此在考虑数据复杂性时首选 Session; +- Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密; +- 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。 + +### 缓存 + +#### 1. 优点 + +- 缓解服务器压力; +- 降低客户端获取资源的延迟:缓存通常位于内存中,读取缓存的速度更快。并且缓存服务器在地理位置上也有可能比源服务器来得近,例如浏览器缓存。 + +#### 2. 实现方法 + +- 让代理服务器进行缓存; +- 让客户端浏览器进行缓存。 + +#### 3. Cache-Control + +HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。 + +**3.1 禁止进行缓存** + +no-store 指令规定不能对请求或响应的任何一部分进行缓存。 + +```html +Cache-Control: no-store +``` + +**3.2 强制确认缓存** + +no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效时才能使用该缓存对客户端的请求进行响应。 + +```html +Cache-Control: no-cache +``` + +**3.3 私有缓存和公共缓存** + +private 指令规定了将资源作为私有缓存,只能被单独用户使用,一般存储在用户浏览器中。 + +```html +Cache-Control: private +``` + +public 指令规定了将资源作为公共缓存,可以被多个用户使用,一般存储在代理服务器中。 + +```html +Cache-Control: public +``` + +**3.4 缓存过期机制** + +max-age 指令出现在请求报文,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。 + +max-age 指令出现在响应报文,表示缓存资源在缓存服务器中保存的时间。 + +```html +Cache-Control: max-age=31536000 +``` + +Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。 + +```html +Expires: Wed, 04 Jul 2012 08:26:05 GMT +``` + +- 在 HTTP/1.1 中,会优先处理 max-age 指令; +- 在 HTTP/1.0 中,max-age 指令会被忽略掉。 + +#### 4. 缓存验证 + +需要先了解 ETag 首部字段的含义,它是资源的唯一标识。URL 不能唯一表示资源,例如 `http://www.google.com/` 有中文和英文两个资源,只有 ETag 才能对这两个资源进行唯一标识。 + +```html +ETag: "82e22293907ce725faf67773957acd12" +``` + +可以将缓存资源的 ETag 值放入 If-None-Match 首部,服务器收到该请求后,判断缓存资源的 ETag 值和资源的最新 ETag 值是否一致,如果一致则表示缓存资源有效,返回 304 Not Modified。 + +```html +If-None-Match: "82e22293907ce725faf67773957acd12" +``` + +Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有实体主体的 304 Not Modified 响应报文。 + +```html +Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT +``` + +```html +If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT +``` + +### 内容协商 + +通过内容协商返回最合适的内容,例如根据浏览器的默认语言选择返回中文界面还是英文界面。 + +#### 1. 类型 + +**1.1 服务端驱动型** + +客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。 + +它存在以下问题: + +- 服务器很难知道客户端浏览器的全部信息; +- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术); +- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。 + +**1.2 代理驱动型** + +服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。 + +#### 2. Vary + +```html +Vary: Accept-Language +``` + +在使用内容协商的情况下,只有当缓存服务器中的缓存满足内容协商条件时,才能使用该缓存,否则应该向源服务器请求该资源。 + +例如,一个客户端发送了一个包含 Accept-Language 首部字段的请求之后,源服务器返回的响应包含 `Vary: Accept-Language` 内容,缓存服务器对这个响应进行缓存之后,在客户端下一次访问同一个 URL 资源,并且 Accept-Language 与缓存中的对应的值相同时才会返回该缓存。 + +### 内容编码 + +内容编码将实体主体进行压缩,从而减少传输的数据量。 + +常用的内容编码有:gzip、compress、deflate、identity。 + +浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,响应报文的 Vary 首部字段至少要包含 Content-Encoding。 + +### 范围请求 + +如果网络出现中断,服务器只发送了一部分数据,范围请求可以使得客户端只请求服务器未发送的那部分数据,从而避免服务器重新发送所有数据。 + +#### 1. Range + +在请求报文中添加 Range 首部字段指定请求的范围。 + +```html +GET /z4d4kWk.jpg HTTP/1.1 +Host: i.imgur.com +Range: bytes=0-1023 +``` + +请求成功的话服务器返回的响应包含 206 Partial Content 状态码。 + +```html +HTTP/1.1 206 Partial Content +Content-Range: bytes 0-1023/146515 +Content-Length: 1024 +... +(binary content) +``` + +#### 2. Accept-Ranges + +响应首部字段 Accept-Ranges 用于告知客户端是否能处理范围请求,可以处理使用 bytes,否则使用 none。 + +```html +Accept-Ranges: bytes +``` + +#### 3. 响应状态码 + +- 在请求成功的情况下,服务器会返回 206 Partial Content 状态码。 +- 在请求的范围越界的情况下,服务器会返回 416 Requested Range Not Satisfiable 状态码。 +- 在不支持范围请求的情况下,服务器会返回 200 OK 状态码。 + +### 分块传输编码 + +Chunked Transfer Encoding,可以把数据分割成多块,让浏览器逐步显示页面。 + +### 多部分对象集合 + +一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔,每个部分都可以有首部字段。 + +例如,上传多个表单时可以使用如下方式: + +```html +Content-Type: multipart/form-data; boundary=AaB03x + +--AaB03x +Content-Disposition: form-data; name="submit-name" + +Larry +--AaB03x +Content-Disposition: form-data; name="files"; filename="file1.txt" +Content-Type: text/plain + +... contents of file1.txt ... +--AaB03x-- +``` + +### 虚拟主机 + +HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。 + +### 通信数据转发 + +#### 1. 代理 + +代理服务器接受客户端的请求,并且转发给其它服务器。 + +使用代理的主要目的是: + +- 缓存 +- 负载均衡 +- 网络访问控制 +- 访问日志记录 + +代理服务器分为正向代理和反向代理两种: + +- 用户察觉得到正向代理的存在。 + +

+ +- 而反向代理一般位于内部网络中,用户察觉不到。 + +

+ +#### 2. 网关 + +与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。 + +#### 3. 隧道 + +使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路。 + +## 六、HTTPS + +HTTP 有以下安全性问题: + +- 使用明文进行通信,内容可能会被窃听; +- 不验证通信方的身份,通信方的身份有可能遭遇伪装; +- 无法证明报文的完整性,报文有可能遭篡改。 + +HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPS 使用了隧道进行通信。 + +通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 + +

+ +### 加密 + +#### 1. 对称密钥加密 + +对称密钥加密(Symmetric-Key Encryption),加密和解密使用同一密钥。 + +- 优点:运算速度快; +- 缺点:无法安全地将密钥传输给通信方。 + +

+ +#### 2.非对称密钥加密 + +非对称密钥加密,又称公开密钥加密(Public-Key Encryption),加密和解密使用不同的密钥。 + +公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。 + +非对称密钥除了用来加密,还可以用来进行签名。因为私有密钥无法被其他人获取,因此通信发送方使用其私有密钥进行签名,通信接收方使用发送方的公开密钥对签名进行解密,就能判断这个签名是否正确。 + +- 优点:可以更安全地将公开密钥传输给通信发送方; +- 缺点:运算速度慢。 + +

+ +#### 3. HTTPS 采用的加密方式 + +上面提到对称密钥加密方式的传输效率更高,但是无法安全地将密钥 Secret Key 传输给通信方。而非对称密钥加密方式可以保证传输的安全性,因此我们可以利用非对称密钥加密方式将 Secret Key 传输给通信方。HTTPS 采用混合的加密机制,正是利用了上面提到的方案: + +- 使用非对称密钥加密方式,传输对称密钥加密方式所需要的 Secret Key,从而保证安全性; +- 获取到 Secret Key 后,再使用对称密钥加密方式进行通信,从而保证效率。(下图中的 Session Key 就是 Secret Key) + +

+ +### 认证 + +通过使用 **证书** 来对通信方进行认证。 + +数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 + +服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。 + +进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。 + +

+ +### 完整性保护 + +SSL 提供报文摘要功能来进行完整性保护。 + +HTTP 也提供了 MD5 报文摘要功能,但不是安全的。例如报文内容被篡改之后,同时重新计算 MD5 的值,通信接收方是无法意识到发生了篡改。 + +HTTPS 的报文摘要功能之所以安全,是因为它结合了加密和认证这两个操作。试想一下,加密之后的报文,遭到篡改之后,也很难重新计算报文摘要,因为无法轻易获取明文。 + +### HTTPS 的缺点 + +- 因为需要进行加密解密等过程,因此速度会更慢; +- 需要支付证书授权的高额费用。 + +## 七、HTTP/2.0 + +### HTTP/1.x 缺陷 + +HTTP/1.x 实现简单是以牺牲性能为代价的: + +- 客户端需要使用多个连接才能实现并发和缩短延迟; +- 不会压缩请求和响应首部,从而导致不必要的网络流量; +- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。 + +### 二进制分帧层 + +HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。 + +

+ +在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。 + +- 一个数据流(Stream)都有一个唯一标识符和可选的优先级信息,用于承载双向信息。 +- 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。 +- 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 + +

+ +### 服务端推送 + +HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。 + +

+ +### 首部压缩 + +HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。 + +HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。 + +不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。 + +

+ +## 八、HTTP/1.1 新特性 + +详细内容请见上文 + +- 默认是长连接 +- 支持流水线 +- 支持同时打开多个 TCP 连接 +- 支持虚拟主机 +- 新增状态码 100 +- 支持分块传输编码 +- 新增缓存处理指令 max-age + +## 九、GET 和 POST 比较 + +### 作用 + +GET 用于获取资源,而 POST 用于传输实体主体。 + +### 参数 + +GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。 + +因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。例如 `中文` 会转换为 `%E4%B8%AD%E6%96%87`,而空格会转换为 `%20`。POST 参数支持标准字符集。 + +``` +GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1 +``` + +``` +POST /test/demo_form.asp HTTP/1.1 +Host: w3schools.com +name1=value1&name2=value2 +``` + +### 安全 + +安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。 + +GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。 + +安全的方法除了 GET 之外还有:HEAD、OPTIONS。 + +不安全的方法除了 POST 之外还有 PUT、DELETE。 + +### 幂等性 + +幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。 + +所有的安全方法也都是幂等的。 + +在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。 + +GET /pageX HTTP/1.1 是幂等的,连续调用多次,客户端接收到的结果都是一样的: + +``` +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +GET /pageX HTTP/1.1 +``` + +POST /add_row HTTP/1.1 不是幂等的,如果调用多次,就会增加多行记录: + +``` +POST /add_row HTTP/1.1 -> Adds a 1nd row +POST /add_row HTTP/1.1 -> Adds a 2nd row +POST /add_row HTTP/1.1 -> Adds a 3rd row +``` + +DELETE /idX/delete HTTP/1.1 是幂等的,即使不同的请求接收到的状态码不一样: + +``` +DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists +DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted +DELETE /idX/delete HTTP/1.1 -> Returns 404 +``` + +### 可缓存 + +如果要对响应进行缓存,需要满足以下条件: + +- 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。 +- 响应报文的状态码是可缓存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。 +- 响应报文的 Cache-Control 首部字段没有指定不进行缓存。 + +### XMLHttpRequest + +为了阐述 POST 和 GET 的另一个区别,需要先了解 XMLHttpRequest: + +> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。 + +- 在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。 +- 而 GET 方法 Header 和 Data 会一起发送。 + +## 参考资料 + +- 上野宣. 图解 HTTP[M]. 人民邮电出版社, 2014. +- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP) +- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) +- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php) +- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java) +- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement) +- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html) +- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/) +- [HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html) +- [Web-VPN: Secure Proxies with SPDY & Chrome](https://www.igvita.com/2011/12/01/web-vpn-secure-proxies-with-spdy-chrome/) +- [File:HTTP persistent connection.svg](http://en.wikipedia.org/wiki/File:HTTP_persistent_connection.svg) +- [Proxy server](https://en.wikipedia.org/wiki/Proxy_server) +- [What Is This HTTPS/SSL Thing And Why Should You Care?](https://www.x-cart.com/blog/what-is-https-and-ssl.html) +- [What is SSL Offloading?](https://securebox.comodo.com/ssl-sniffing/ssl-offloading/) +- [Sun Directory Server Enterprise Edition 7.0 Reference - Key Encryption](https://docs.oracle.com/cd/E19424-01/820-4811/6ng8i26bn/index.html) +- [An Introduction to Mutual SSL Authentication](https://www.codeproject.com/Articles/326574/An-Introduction-to-Mutual-SSL-Authentication) +- [The Difference Between URLs and URIs](https://danielmiessler.com/study/url-uri/) +- [Cookie 与 Session 的区别](https://juejin.im/entry/5766c29d6be3ff006a31b84e#comment) +- [COOKIE 和 SESSION 有什么区别](https://www.zhihu.com/question/19786827) +- [Cookie/Session 的机制与安全](https://harttle.land/2015/08/10/cookie-session.html) +- [HTTPS 证书原理](https://shijianan.com/2017/06/11/https/) +- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn) +- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest) +- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/) +- [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences) +- [Web 性能优化与 HTTP/2](https://www.kancloud.cn/digest/web-performance-http2) +- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) diff --git a/docs/notes/Java IO.md b/notes/Java IO.md similarity index 85% rename from docs/notes/Java IO.md rename to notes/Java IO.md index fdcae09689..d95eee4f9f 100644 --- a/docs/notes/Java IO.md +++ b/notes/Java IO.md @@ -1,37 +1,39 @@ +# Java IO -* [一、概览](#一概览) -* [二、磁盘操作](#二磁盘操作) -* [三、字节操作](#三字节操作) - * [实现文件复制](#实现文件复制) - * [装饰者模式](#装饰者模式) -* [四、字符操作](#四字符操作) - * [编码与解码](#编码与解码) - * [String 的编码方式](#string-的编码方式) - * [Reader 与 Writer](#reader-与-writer) - * [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容) -* [五、对象操作](#五对象操作) - * [序列化](#序列化) - * [Serializable](#serializable) - * [transient](#transient) -* [六、网络操作](#六网络操作) - * [InetAddress](#inetaddress) - * [URL](#url) - * [Sockets](#sockets) - * [Datagram](#datagram) -* [七、NIO](#七nio) - * [流与块](#流与块) - * [通道与缓冲区](#通道与缓冲区) - * [缓冲区状态变量](#缓冲区状态变量) - * [文件 NIO 实例](#文件-nio-实例) - * [选择器](#选择器) - * [套接字 NIO 实例](#套接字-nio-实例) - * [内存映射文件](#内存映射文件) - * [对比](#对比) -* [八、参考资料](#八参考资料) +* [Java IO](#java-io) + * [一、概览](#一概览) + * [二、磁盘操作](#二磁盘操作) + * [三、字节操作](#三字节操作) + * [实现文件复制](#实现文件复制) + * [装饰者模式](#装饰者模式) + * [四、字符操作](#四字符操作) + * [编码与解码](#编码与解码) + * [String 的编码方式](#string-的编码方式) + * [Reader 与 Writer](#reader-与-writer) + * [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容) + * [五、对象操作](#五对象操作) + * [序列化](#序列化) + * [Serializable](#serializable) + * [transient](#transient) + * [六、网络操作](#六网络操作) + * [InetAddress](#inetaddress) + * [URL](#url) + * [Sockets](#sockets) + * [Datagram](#datagram) + * [七、NIO](#七nio) + * [流与块](#流与块) + * [通道与缓冲区](#通道与缓冲区) + * [缓冲区状态变量](#缓冲区状态变量) + * [文件 NIO 实例](#文件-nio-实例) + * [选择器](#选择器) + * [套接字 NIO 实例](#套接字-nio-实例) + * [内存映射文件](#内存映射文件) + * [对比](#对比) + * [八、参考资料](#八参考资料) -# 一、概览 +## 一、概览 Java 的 I/O 大概可以分成以下几类: @@ -42,7 +44,7 @@ Java 的 I/O 大概可以分成以下几类: - 网络操作:Socket - 新的输入/输出:NIO -# 二、磁盘操作 +## 二、磁盘操作 File 类可以用于表示文件和目录的信息,但是它不表示文件的内容。 @@ -65,9 +67,9 @@ public static void listAllFiles(File dir) { 从 Java7 开始,可以使用 Paths 和 Files 代替 File。 -# 三、字节操作 +## 三、字节操作 -## 实现文件复制 +### 实现文件复制 ```java public static void copyFile(String src, String dist) throws IOException { @@ -89,7 +91,7 @@ public static void copyFile(String src, String dist) throws IOException { } ``` -## 装饰者模式 +### 装饰者模式 Java I/O 使用了装饰者模式来实现。以 InputStream 为例, @@ -97,7 +99,7 @@ Java I/O 使用了装饰者模式来实现。以 InputStream 为例, - FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; - FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 -

+

实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 @@ -108,9 +110,9 @@ BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStrea DataInputStream 装饰者提供了对更多数据类型进行输入的操作,比如 int、double 等基本类型。 -# 四、字符操作 +## 四、字符操作 -## 编码与解码 +### 编码与解码 编码就是把字符转换为字节,而解码是把字节重新组合成字符。 @@ -124,7 +126,7 @@ UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF- Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。 -## String 的编码方式 +### String 的编码方式 String 可以看成一个字符序列,可以指定一个编码方式将它编码为字节序列,也可以指定一个编码方式将一个字节序列解码为 String。 @@ -141,14 +143,14 @@ System.out.println(str2); byte[] bytes = str1.getBytes(); ``` -## Reader 与 Writer +### Reader 与 Writer 不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符。但是在程序中操作的通常是字符形式的数据,因此需要提供对字符进行操作的方法。 - InputStreamReader 实现从字节流解码成字符流; - OutputStreamWriter 实现字符流编码成为字节流。 -## 实现逐行输出文本文件的内容 +### 实现逐行输出文本文件的内容 ```java public static void readFileContent(String filePath) throws IOException { @@ -168,9 +170,9 @@ public static void readFileContent(String filePath) throws IOException { } ``` -# 五、对象操作 +## 五、对象操作 -## 序列化 +### 序列化 序列化就是将一个对象转换成字节序列,方便存储和传输。 @@ -179,7 +181,7 @@ public static void readFileContent(String filePath) throws IOException { 不会对静态变量进行序列化,因为序列化只是保存对象的状态,静态变量属于类的状态。 -## Serializable +### Serializable 序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。 @@ -216,7 +218,7 @@ private static class A implements Serializable { } ``` -## transient +### transient transient 关键字可以使一些属性不会被序列化。 @@ -226,7 +228,7 @@ ArrayList 中存储数据的数组 elementData 是用 transient 修饰的,因 private transient Object[] elementData; ``` -# 六、网络操作 +## 六、网络操作 Java 中的网络支持: @@ -235,7 +237,7 @@ Java 中的网络支持: - Sockets:使用 TCP 协议实现网络通信; - Datagram:使用 UDP 协议实现网络通信。 -## InetAddress +### InetAddress 没有公有的构造函数,只能通过静态方法来创建实例。 @@ -244,7 +246,7 @@ InetAddress.getByName(String host); InetAddress.getByAddress(byte[] address); ``` -## URL +### URL 可以直接从 URL 中读取字节流数据。 @@ -271,24 +273,24 @@ public static void main(String[] args) throws IOException { } ``` -## Sockets +### Sockets - ServerSocket:服务器端类 - Socket:客户端类 - 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。 -

+

-## Datagram +### Datagram - DatagramSocket:通信类 - DatagramPacket:数据包类 -# 七、NIO +## 七、NIO 新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。 -## 流与块 +### 流与块 I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 @@ -298,9 +300,9 @@ I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的 I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io.\* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。 -## 通道与缓冲区 +### 通道与缓冲区 -### 1. 通道 +#### 1. 通道 通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。 @@ -313,7 +315,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 - SocketChannel:通过 TCP 读写网络中数据; - ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。 -### 2. 缓冲区 +#### 2. 缓冲区 发送给一个通道的所有数据都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要先读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。 @@ -329,7 +331,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 - FloatBuffer - DoubleBuffer -## 缓冲区状态变量 +### 缓冲区状态变量 - capacity:最大容量; - position:当前已经读写的字节数; @@ -339,25 +341,25 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 ① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。 -

+

② 从输入通道中读取 5 个字节数据写入缓冲区中,此时 position 为 5,limit 保持不变。 -

+

③ 在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。 -

+

④ 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。 -

+

⑤ 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。 -

+

-## 文件 NIO 实例 +### 文件 NIO 实例 以下展示了使用 NIO 快速复制文件的实例: @@ -401,7 +403,7 @@ public static void fastCopy(String src, String dist) throws IOException { } ``` -## 选择器 +### 选择器 NIO 常常被叫做非阻塞 IO,主要是因为 NIO 在网络通信中的非阻塞特性被广泛使用。 @@ -413,15 +415,15 @@ NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用 应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。 -

+

-### 1. 创建选择器 +#### 1. 创建选择器 ```java Selector selector = Selector.open(); ``` -### 2. 将通道注册到选择器上 +#### 2. 将通道注册到选择器上 ```java ServerSocketChannel ssChannel = ServerSocketChannel.open(); @@ -453,7 +455,7 @@ public static final int OP_ACCEPT = 1 << 4; int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; ``` -### 3. 监听事件 +#### 3. 监听事件 ```java int num = selector.select(); @@ -461,7 +463,7 @@ int num = selector.select(); 使用 select() 来监听到达的事件,它会一直阻塞直到有至少一个事件到达。 -### 4. 获取到达的事件 +#### 4. 获取到达的事件 ```java Set keys = selector.selectedKeys(); @@ -477,7 +479,7 @@ while (keyIterator.hasNext()) { } ``` -### 5. 事件循环 +#### 5. 事件循环 因为一次 select() 调用不能处理完所有的事件,并且服务器端有可能需要一直监听事件,因此服务器端处理事件的代码一般会放在一个死循环内。 @@ -498,7 +500,7 @@ while (true) { } ``` -## 套接字 NIO 实例 +### 套接字 NIO 实例 ```java public class NIOServer { @@ -587,7 +589,7 @@ public class NIOClient { } ``` -## 内存映射文件 +### 内存映射文件 内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。 @@ -599,14 +601,14 @@ public class NIOClient { MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); ``` -## 对比 +### 对比 NIO 与普通 I/O 的区别主要有以下两点: - NIO 是非阻塞的; - NIO 面向块,I/O 面向流。 -# 八、参考资料 +## 八、参考资料 - Eckel B, 埃克尔, 昊鹏, 等. Java 编程思想 [M]. 机械工业出版社, 2002. - [IBM: NIO 入门](https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html) @@ -618,9 +620,3 @@ NIO 与普通 I/O 的区别主要有以下两点: - [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499) - [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document) - [Socket Multicast](http://labojava.blogspot.com/2012/12/socket-multicast.html) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/Java \345\237\272\347\241\200.md" "b/notes/Java \345\237\272\347\241\200.md" new file mode 100644 index 0000000000..b0da53a397 --- /dev/null +++ "b/notes/Java \345\237\272\347\241\200.md" @@ -0,0 +1,1463 @@ +# Java 基础 + +* [Java 基础](#java-基础) + * [一、数据类型](#一数据类型) + * [基本类型](#基本类型) + * [包装类型](#包装类型) + * [缓存池](#缓存池) + * [二、String](#二string) + * [概览](#概览) + * [不可变的好处](#不可变的好处) + * [String, StringBuffer and StringBuilder ](#string-stringbuffer-and-stringbuilder ) + * [String Pool](#string-pool) + * [new String("abc")](#new-stringabc) + * [三、运算](#三运算) + * [参数传递](#参数传递) + * [float 与 double](#float-与-double) + * [隐式类型转换](#隐式类型转换) + * [switch](#switch) + * [四、关键字](#四关键字) + * [final](#final) + * [static](#static) + * [五、Object 通用方法](#五object-通用方法) + * [概览](#概览) + * [equals()](#equals) + * [hashCode()](#hashcode) + * [toString()](#tostring) + * [clone()](#clone) + * [六、继承](#六继承) + * [访问权限](#访问权限) + * [抽象类与接口](#抽象类与接口) + * [super](#super) + * [重写与重载](#重写与重载) + * [七、反射](#七反射) + * [八、异常](#八异常) + * [九、泛型](#九泛型) + * [十、注解](#十注解) + * [十一、特性](#十一特性) + * [Java 各版本的新特性](#java-各版本的新特性) + * [Java 与 C++ 的区别](#java-与-c-的区别) + * [JRE or JDK](#jre-or-jdk) + * [参考资料](#参考资料) + + + +## 一、数据类型 + +### 基本类型 + +- byte/8 +- char/16 +- short/16 +- int/32 +- float/32 +- long/64 +- double/64 +- boolean/\~ + +boolean 只有两个值:true、false,可以使用 1 bit 来存储,但是具体大小没有明确规定。JVM 会在编译时期将 boolean 类型的数据转换为 int,使用 1 来表示 true,0 表示 false。JVM 支持 boolean 数组,但是是通过读写 byte 数组来实现的。 + +- [Primitive Data Types](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) +- [The Java® Virtual Machine Specification](https://docs.oracle.com/javase/specs/jvms/se8/jvms8.pdf) + +### 包装类型 + +基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。 + +```java +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) 的区别在于: + +- new Integer(123) 每次都会新建一个对象; +- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。 + +```java +Integer x = new Integer(123); +Integer y = new Integer(123); +System.out.println(x == y); // false +Integer z = Integer.valueOf(123); +Integer k = Integer.valueOf(123); +System.out.println(z == k); // true +``` + +valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。 + +```java +public static Integer valueOf(int i) { + if (i >= IntegerCache.low && i <= IntegerCache.high) + return IntegerCache.cache[i + (-IntegerCache.low)]; + return new Integer(i); +} +``` + +在 Java 8 中,Integer 缓存池的大小默认为 -128\~127。 + +```java +static final int low = -128; +static final int high; +static final Integer cache[]; + +static { + // high value may be configured by property + int h = 127; + String integerCacheHighPropValue = + sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); + if (integerCacheHighPropValue != null) { + try { + int i = parseInt(integerCacheHighPropValue); + i = Math.max(i, 127); + // Maximum array size is Integer.MAX_VALUE + h = Math.min(i, Integer.MAX_VALUE - (-low) -1); + } catch( NumberFormatException nfe) { + // If the property cannot be parsed into an int, ignore it. + } + } + high = h; + + cache = new Integer[(high - low) + 1]; + int j = low; + for(int k = 0; k < cache.length; k++) + cache[k] = new Integer(j++); + + // range [-128, 127] must be interned (JLS7 5.1.7) + assert IntegerCache.high >= 127; +} +``` + +编译器会在自动装箱过程调用 valueOf() 方法,因此多个值相同且值在缓存池范围内的 Integer 实例使用自动装箱来创建,那么就会引用相同的对象。 + +```java +Integer m = 123; +Integer n = 123; +System.out.println(m == n); // true +``` + +基本类型对应的缓冲池如下: + +- boolean values true and false +- all byte values +- short values between -128 and 127 +- int values between -128 and 127 +- char in the range \u0000 to \u007F + +在使用这些基本类型对应的包装类型时,如果该数值范围在缓冲池范围内,就可以直接使用缓冲池中的对象。 + +在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax=<size> 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定上界。 + +[StackOverflow : Differences between new Integer(123), Integer.valueOf(123) and just 123 +](https://stackoverflow.com/questions/9030817/differences-between-new-integer123-integer-valueof123-and-just-123) + +## 二、String + +### 概览 + +String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承) + +在 Java 8 中,String 内部使用 char 数组存储数据。 + +```java +public final class String + implements java.io.Serializable, Comparable, CharSequence { + /** The value is used for character storage. */ + private final char value[]; +} +``` + +在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 `coder` 来标识使用了哪种编码。 + +```java +public final class String + implements java.io.Serializable, Comparable, CharSequence { + /** The value is used for character storage. */ + private final byte[] value; + + /** The identifier of the encoding used to encode the bytes in {@code value}. */ + private final byte coder; +} +``` + +value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。 + +### 不可变的好处 + +**1. 可以缓存 hash 值** + +因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。 + +**2. String Pool 的需要** + +如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 + +

+ +**3. 安全性** + +String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它主机,而实际情况却不一定是。 + +**4. 线程安全** + +String 不可变性天生具备线程安全,可以在多个线程中安全地使用。 + +[Program Creek : Why String is immutable in Java?](https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/) + +### String, StringBuffer and StringBuilder + +**1. 可变性** + +- String 不可变 +- StringBuffer 和 StringBuilder 可变 + +**2. 线程安全** + +- String 不可变,因此是线程安全的 +- StringBuilder 不是线程安全的 +- StringBuffer 是线程安全的,内部使用 synchronized 进行同步 + +[StackOverflow : String, StringBuffer, and StringBuilder](https://stackoverflow.com/questions/2971315/string-stringbuffer-and-stringbuilder) + +### String Pool + +字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。 + +当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。 + +下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern() 和 s2.intern() 方法取得同一个字符串引用。intern() 首先把 "aaa" 放到 String Pool 中,然后返回这个字符串引用,因此 s3 和 s4 引用的是同一个字符串。 + +```java +String s1 = new String("aaa"); +String s2 = new String("aaa"); +System.out.println(s1 == s2); // false +String s3 = s1.intern(); +String s4 = s2.intern(); +System.out.println(s3 == s4); // true +``` + +如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。 + +```java +String s5 = "bbb"; +String s6 = "bbb"; +System.out.println(s5 == s6); // true +``` + +在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。 + +- [StackOverflow : What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning) +- [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html) + +### new String("abc") + +使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。 + +- "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量; +- 而使用 new 的方式会在堆中创建一个字符串对象。 + +创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。 + +```java +public class NewStringTest { + public static void main(String[] args) { + String s = new String("abc"); + } +} +``` + +使用 javap -verbose 进行反编译,得到以下内容: + +```java +// ... +Constant pool: +// ... + #2 = Class #18 // java/lang/String + #3 = String #19 // abc +// ... + #18 = Utf8 java/lang/String + #19 = Utf8 abc +// ... + + public static void main(java.lang.String[]); + descriptor: ([Ljava/lang/String;)V + flags: ACC_PUBLIC, ACC_STATIC + Code: + stack=3, locals=2, args_size=1 + 0: new #2 // class java/lang/String + 3: dup + 4: ldc #3 // String abc + 6: invokespecial #4 // Method java/lang/String."":(Ljava/lang/String;)V + 9: astore_1 +// ... +``` + +在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。 + +以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。 + +```java +public String(String original) { + this.value = original.value; + this.hash = original.hash; +} +``` + +## 三、运算 + +### 参数传递 + +Java 的参数是以值传递的形式传入方法中,而不是引用传递。 + +以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。 + +```java +public class Dog { + + String name; + + Dog(String name) { + this.name = name; + } + + String getName() { + return this.name; + } + + void setName(String name) { + this.name = name; + } + + String getObjectAddress() { + return super.toString(); + } +} +``` + +在方法中改变对象的字段值会改变原对象该字段值,因为引用的是同一个对象。 + +```java +class PassByValueExample { + public static void main(String[] args) { + Dog dog = new Dog("A"); + func(dog); + System.out.println(dog.getName()); // B + } + + private static void func(Dog dog) { + dog.setName("B"); + } +} +``` + +但是在方法中将指针引用了其它对象,那么此时方法里和方法外的两个指针指向了不同的对象,在一个指针改变其所指向对象的内容对另一个指针所指向的对象没有影响。 + +```java +public class PassByValueExample { + public static void main(String[] args) { + Dog dog = new Dog("A"); + System.out.println(dog.getObjectAddress()); // Dog@4554617c + func(dog); + System.out.println(dog.getObjectAddress()); // Dog@4554617c + System.out.println(dog.getName()); // A + } + + private static void func(Dog dog) { + System.out.println(dog.getObjectAddress()); // Dog@4554617c + dog = new Dog("B"); + System.out.println(dog.getObjectAddress()); // Dog@74a14482 + System.out.println(dog.getName()); // B + } +} +``` + +[StackOverflow: Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) + +### float 与 double + +Java 不能隐式执行向下转型,因为这会使得精度降低。 + +1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。 + +```java +// float f = 1.1; +``` + +1.1f 字面量才是 float 类型。 + +```java +float f = 1.1f; +``` + +### 隐式类型转换 + +因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型向下转型为 short 类型。 + +```java +short s1 = 1; +// s1 = s1 + 1; +``` + +但是使用 += 或者 ++ 运算符会执行隐式类型转换。 + +```java +s1 += 1; +s1++; +``` + +上面的语句相当于将 s1 + 1 的计算结果进行了向下转型: + +```java +s1 = (short) (s1 + 1); +``` + +[StackOverflow : Why don't Java's +=, -=, *=, /= compound assignment operators require casting?](https://stackoverflow.com/questions/8710619/why-dont-javas-compound-assignment-operators-require-casting) + +### switch + +从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。 + +```java +String s = "a"; +switch (s) { + case "a": + System.out.println("aaa"); + break; + case "b": + System.out.println("bbb"); + break; +} +``` + +switch 不支持 long、float、double,是因为 switch 的设计初衷是对那些只有少数几个值的类型进行等值判断,如果值过于复杂,那么还是用 if 比较合适。 + +```java +// long x = 111; +// switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum' +// case 111: +// System.out.println(111); +// break; +// case 222: +// System.out.println(222); +// break; +// } +``` + +[StackOverflow : Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java) + + +## 四、关键字 + +### final + +**1. 数据** + +声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 + +- 对于基本类型,final 使数值不变; +- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 + +```java +final int x = 1; +// x = 2; // cannot assign value to final variable 'x' +final A y = new A(); +y.a = 1; +``` + +**2. 方法** + +声明方法不能被子类重写。 + +private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。 + +**3. 类** + +声明类不允许被继承。 + +### static + +**1. 静态变量** + +- 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。 +- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 + +```java +public class A { + + private int x; // 实例变量 + private static int y; // 静态变量 + + public static void main(String[] args) { + // int x = A.x; // Non-static field 'x' cannot be referenced from a static context + A a = new A(); + int x = a.x; + int y = A.y; + } +} +``` + +**2. 静态方法** + +静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。 + +```java +public abstract class A { + public static void func1(){ + } + // public abstract static void func2(); // Illegal combination of modifiers: 'abstract' and 'static' +} +``` + +只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。 + +```java +public class A { + + private static int x; + private int y; + + public static void func1(){ + int a = x; + // int b = y; // Non-static field 'y' cannot be referenced from a static context + // int b = this.y; // 'A.this' cannot be referenced from a static context + } +} +``` + +**3. 静态语句块** + +静态语句块在类初始化时运行一次。 + +```java +public class A { + static { + System.out.println("123"); + } + + public static void main(String[] args) { + A a1 = new A(); + A a2 = new A(); + } +} +``` + +```html +123 +``` + +**4. 静态内部类** + +非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。而静态内部类不需要。 + +```java +public class OuterClass { + + class InnerClass { + } + + static class StaticInnerClass { + } + + public static void main(String[] args) { + // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context + OuterClass outerClass = new OuterClass(); + InnerClass innerClass = outerClass.new InnerClass(); + StaticInnerClass staticInnerClass = new StaticInnerClass(); + } +} +``` + +静态内部类不能访问外部类的非静态的变量和方法。 + +**5. 静态导包** + +在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 + +```java +import static com.xxx.ClassName.* +``` + +**6. 初始化顺序** + +静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。 + +```java +public static String staticField = "静态变量"; +``` + +```java +static { + System.out.println("静态语句块"); +} +``` + +```java +public String field = "实例变量"; +``` + +```java +{ + System.out.println("普通语句块"); +} +``` + +最后才是构造函数的初始化。 + +```java +public InitialOrderTest() { + System.out.println("构造函数"); +} +``` + +存在继承的情况下,初始化顺序为: + +- 父类(静态变量、静态语句块) +- 子类(静态变量、静态语句块) +- 父类(实例变量、普通语句块) +- 父类(构造函数) +- 子类(实例变量、普通语句块) +- 子类(构造函数) + +## 五、Object 通用方法 + +### 概览 + +```java + +public native int hashCode() + +public boolean equals(Object obj) + +protected native Object clone() throws CloneNotSupportedException + +public String toString() + +public final native Class getClass() + +protected void finalize() throws Throwable {} + +public final native void notify() + +public final native void notifyAll() + +public final native void wait(long timeout) throws InterruptedException + +public final void wait(long timeout, int nanos) throws InterruptedException + +public final void wait() throws InterruptedException +``` + +### equals() + +**1. 等价关系** + +两个对象具有等价关系,需要满足以下五个条件: + +Ⅰ 自反性 + +```java +x.equals(x); // true +``` + +Ⅱ 对称性 + +```java +x.equals(y) == y.equals(x); // true +``` + +Ⅲ 传递性 + +```java +if (x.equals(y) && y.equals(z)) + x.equals(z); // true; +``` + +Ⅳ 一致性 + +多次调用 equals() 方法结果不变 + +```java +x.equals(y) == x.equals(y); // true +``` + +Ⅴ 与 null 的比较 + +对任何不是 null 的对象 x 调用 x.equals(null) 结果都为 false + +```java +x.equals(null); // false; +``` + +**2. 等价与相等** + +- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 +- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 + +```java +Integer x = new Integer(1); +Integer y = new Integer(1); +System.out.println(x.equals(y)); // true +System.out.println(x == y); // false +``` + +**3. 实现** + +- 检查是否为同一个对象的引用,如果是直接返回 true; +- 检查是否是同一个类型,如果不是,直接返回 false; +- 将 Object 对象进行转型; +- 判断每个关键域是否相等。 + +```java +public class EqualExample { + + private int x; + private int y; + private int z; + + public EqualExample(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + EqualExample that = (EqualExample) o; + + if (x != that.x) return false; + if (y != that.y) return false; + return z == that.z; + } +} +``` + +### hashCode() + +hashCode() 返回哈希值,而 equals() 是用来判断两个对象是否等价。等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价,这是因为计算哈希值具有随机性,两个值不同的对象可能计算出相同的哈希值。 + +在覆盖 equals() 方法时应当总是覆盖 hashCode() 方法,保证等价的两个对象哈希值也相等。 + +HashSet 和 HashMap 等集合类使用了 hashCode() 方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode() 方法。 + +下面的代码中,新建了两个等价的对象,并将它们添加到 HashSet 中。我们希望将这两个对象当成一样的,只在集合中添加一个对象。但是 EqualExample 没有实现 hashCode() 方法,因此这两个对象的哈希值是不同的,最终导致集合添加了两个等价的对象。 + +```java +EqualExample e1 = new EqualExample(1, 1, 1); +EqualExample e2 = new EqualExample(1, 1, 1); +System.out.println(e1.equals(e2)); // true +HashSet set = new HashSet<>(); +set.add(e1); +set.add(e2); +System.out.println(set.size()); // 2 +``` + +理想的哈希函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的哈希值上。这就要求了哈希函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。 + +R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位,最左边的位丢失。并且一个数与 31 相乘可以转换成移位和减法:`31*x == (x<<5)-x`,编译器会自动进行这个优化。 + +```java +@Override +public int hashCode() { + int result = 17; + result = 31 * result + x; + result = 31 * result + y; + result = 31 * result + z; + return result; +} +``` + +### toString() + +默认返回 ToStringExample@4554617c 这种形式,其中 @ 后面的数值为散列码的无符号十六进制表示。 + +```java +public class ToStringExample { + + private int number; + + public ToStringExample(int number) { + this.number = number; + } +} +``` + +```java +ToStringExample example = new ToStringExample(123); +System.out.println(example.toString()); +``` + +```html +ToStringExample@4554617c +``` + +### clone() + +**1. cloneable** + +clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 + +```java +public class CloneExample { + private int a; + private int b; +} +``` + +```java +CloneExample e1 = new CloneExample(); +// CloneExample e2 = e1.clone(); // 'clone()' has protected access in 'java.lang.Object' +``` + +重写 clone() 得到以下实现: + +```java +public class CloneExample { + private int a; + private int b; + + @Override + public CloneExample clone() throws CloneNotSupportedException { + return (CloneExample)super.clone(); + } +} +``` + +```java +CloneExample e1 = new CloneExample(); +try { + CloneExample e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +``` + +```html +java.lang.CloneNotSupportedException: CloneExample +``` + +以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。 + +应该注意的是,clone() 方法并不是 Cloneable 接口的方法,而是 Object 的一个 protected 方法。Cloneable 接口只是规定,如果一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException。 + +```java +public class CloneExample implements Cloneable { + private int a; + private int b; + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} +``` + +**2. 浅拷贝** + +拷贝对象和原始对象的引用类型引用同一个对象。 + +```java +public class ShallowCloneExample implements Cloneable { + + private int[] arr; + + public ShallowCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } + + public void set(int index, int value) { + arr[index] = value; + } + + public int get(int index) { + return arr[index]; + } + + @Override + protected ShallowCloneExample clone() throws CloneNotSupportedException { + return (ShallowCloneExample) super.clone(); + } +} +``` + +```java +ShallowCloneExample e1 = new ShallowCloneExample(); +ShallowCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e2.get(2)); // 222 +``` + +**3. 深拷贝** + +拷贝对象和原始对象的引用类型引用不同对象。 + +```java +public class DeepCloneExample implements Cloneable { + + private int[] arr; + + public DeepCloneExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } + + public void set(int index, int value) { + arr[index] = value; + } + + public int get(int index) { + return arr[index]; + } + + @Override + protected DeepCloneExample clone() throws CloneNotSupportedException { + DeepCloneExample result = (DeepCloneExample) super.clone(); + result.arr = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + result.arr[i] = arr[i]; + } + return result; + } +} +``` + +```java +DeepCloneExample e1 = new DeepCloneExample(); +DeepCloneExample e2 = null; +try { + e2 = e1.clone(); +} catch (CloneNotSupportedException e) { + e.printStackTrace(); +} +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 +``` + +**4. clone() 的替代方案** + +使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 + +```java +public class CloneConstructorExample { + + private int[] arr; + + public CloneConstructorExample() { + arr = new int[10]; + for (int i = 0; i < arr.length; i++) { + arr[i] = i; + } + } + + public CloneConstructorExample(CloneConstructorExample original) { + arr = new int[original.arr.length]; + for (int i = 0; i < original.arr.length; i++) { + arr[i] = original.arr[i]; + } + } + + public void set(int index, int value) { + arr[index] = value; + } + + public int get(int index) { + return arr[index]; + } +} +``` + +```java +CloneConstructorExample e1 = new CloneConstructorExample(); +CloneConstructorExample e2 = new CloneConstructorExample(e1); +e1.set(2, 222); +System.out.println(e2.get(2)); // 2 +``` + +## 六、继承 + +### 访问权限 + +Java 中有三个访问权限修饰符:private、protected 以及 public,如果不加访问修饰符,表示包级可见。 + +可以对类或类中的成员(字段和方法)加上访问修饰符。 + +- 类可见表示其它类可以用这个类创建实例对象。 +- 成员可见表示其它类可以用这个类的实例对象访问到该成员; + +protected 用于修饰成员,表示在继承体系中成员对于子类可见,但是这个访问修饰符对于类没有意义。 + +设计良好的模块会隐藏所有的实现细节,把它的 API 与它的实现清晰地隔离开来。模块之间只通过它们的 API 进行通信,一个模块不需要知道其他模块的内部工作情况,这个概念被称为信息隐藏或封装。因此访问权限应当尽可能地使每个类或者成员不被外界访问。 + +如果子类的方法重写了父类的方法,那么子类中该方法的访问级别不允许低于父类的访问级别。这是为了确保可以使用父类实例的地方都可以使用子类实例去代替,也就是确保满足里氏替换原则。 + +字段决不能是公有的,因为这么做的话就失去了对这个字段修改行为的控制,客户端可以对其随意修改。例如下面的例子中,AccessExample 拥有 id 公有字段,如果在某个时刻,我们想要使用 int 存储 id 字段,那么就需要修改所有的客户端代码。 + +```java +public class AccessExample { + public String id; +} +``` + +可以使用公有的 getter 和 setter 方法来替换公有字段,这样的话就可以控制对字段的修改行为。 + +```java +public class AccessExample { + + private int id; + + public String getId() { + return id + ""; + } + + public void setId(String id) { + this.id = Integer.valueOf(id); + } +} +``` + +但是也有例外,如果是包级私有的类或者私有的嵌套类,那么直接暴露成员不会有特别大的影响。 + +```java +public class AccessWithInnerClassExample { + + private class InnerClass { + int x; + } + + private InnerClass innerClass; + + public AccessWithInnerClassExample() { + innerClass = new InnerClass(); + } + + public int getValue() { + return innerClass.x; // 直接访问 + } +} +``` + +### 抽象类与接口 + +**1. 抽象类** + +抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。 + +抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。 + +```java +public abstract class AbstractClassExample { + + protected int x; + private int y; + + public abstract void func1(); + + public void func2() { + System.out.println("func2"); + } +} +``` + +```java +public class AbstractExtendClassExample extends AbstractClassExample { + @Override + public void func1() { + System.out.println("func1"); + } +} +``` + +```java +// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated +AbstractClassExample ac2 = new AbstractExtendClassExample(); +ac2.func1(); +``` + +**2. 接口** + +接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。 + +从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类,让它们都实现新增的方法。 + +接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。从 Java 9 开始,允许将方法定义为 private,这样就能定义某些复用的代码又不会把方法暴露出去。 + +接口的字段默认都是 static 和 final 的。 + +```java +public interface InterfaceExample { + + void func1(); + + default void func2(){ + System.out.println("func2"); + } + + int x = 123; + // int y; // Variable 'y' might not have been initialized + public int z = 0; // Modifier 'public' is redundant for interface fields + // private int k = 0; // Modifier 'private' not allowed here + // protected int l = 0; // Modifier 'protected' not allowed here + // private void fun3(); // Modifier 'private' not allowed here +} +``` + +```java +public class InterfaceImplementExample implements InterfaceExample { + @Override + public void func1() { + System.out.println("func1"); + } +} +``` + +```java +// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated +InterfaceExample ie2 = new InterfaceImplementExample(); +ie2.func1(); +System.out.println(InterfaceExample.x); +``` + +**3. 比较** + +- 从设计层面上看,抽象类提供了一种 IS-A 关系,需要满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 +- 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 +- 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。 +- 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。 + +**4. 使用选择** + +使用接口: + +- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Comparable 接口中的 compareTo() 方法; +- 需要使用多重继承。 + +使用抽象类: + +- 需要在几个相关的类中共享代码。 +- 需要能控制继承来的成员的访问权限,而不是都为 public。 +- 需要继承非静态和非常量字段。 + +在很多情况下,接口优先于抽象类。因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。 + +- [Abstract Methods and Classes](https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html) +- [深入理解 abstract class 和 interface](https://www.ibm.com/developerworks/cn/java/l-javainterface-abstract/) +- [When to Use Abstract Class and Interface](https://dzone.com/articles/when-to-use-abstract-class-and-intreface) +- [Java 9 Private Methods in Interfaces](https://www.journaldev.com/12850/java-9-private-methods-interfaces) + + +### super + +- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。应该注意到,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它构造函数,那么就可以使用 super() 函数。 +- 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。 + +```java +public class SuperExample { + + protected int x; + protected int y; + + public SuperExample(int x, int y) { + this.x = x; + this.y = y; + } + + public void func() { + System.out.println("SuperExample.func()"); + } +} +``` + +```java +public class SuperExtendExample extends SuperExample { + + private int z; + + public SuperExtendExample(int x, int y, int z) { + super(x, y); + this.z = z; + } + + @Override + public void func() { + super.func(); + System.out.println("SuperExtendExample.func()"); + } +} +``` + +```java +SuperExample e = new SuperExtendExample(1, 2, 3); +e.func(); +``` + +```html +SuperExample.func() +SuperExtendExample.func() +``` + +[Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html) + +### 重写与重载 + +**1. 重写(Override)** + +存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。 + +为了满足里式替换原则,重写有以下三个限制: + +- 子类方法的访问权限必须大于等于父类方法; +- 子类方法的返回类型必须是父类方法返回类型或为其子类型。 +- 子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型。 + +使用 @Override 注解,可以让编译器帮忙检查是否满足上面的三个限制条件。 + +下面的示例中,SubClass 为 SuperClass 的子类,SubClass 重写了 SuperClass 的 func() 方法。其中: + +- 子类方法访问权限为 public,大于父类的 protected。 +- 子类的返回类型为 ArrayList\,是父类返回类型 List\ 的子类。 +- 子类抛出的异常类型为 Exception,是父类抛出异常 Throwable 的子类。 +- 子类重写方法使用 @Override 注解,从而让编译器自动检查是否满足限制条件。 + +```java +class SuperClass { + protected List func() throws Throwable { + return new ArrayList<>(); + } +} + +class SubClass extends SuperClass { + @Override + public ArrayList func() throws Exception { + return new ArrayList<>(); + } +} +``` + +在调用一个方法时,先从本类中查找看是否有对应的方法,如果没有再到父类中查看,看是否从父类继承来。否则就要对参数进行转型,转成父类之后看是否有对应的方法。总的来说,方法调用的优先级为: + +- this.func(this) +- super.func(this) +- this.func(super) +- super.func(super) + + +```java +/* + A + | + B + | + C + | + D + */ + + +class A { + + public void show(A obj) { + System.out.println("A.show(A)"); + } + + public void show(C obj) { + System.out.println("A.show(C)"); + } +} + +class B extends A { + + @Override + public void show(A obj) { + System.out.println("B.show(A)"); + } +} + +class C extends B { +} + +class D extends C { +} +``` + +```java +public static void main(String[] args) { + + A a = new A(); + B b = new B(); + C c = new C(); + D d = new D(); + + // 在 A 中存在 show(A obj),直接调用 + a.show(a); // A.show(A) + // 在 A 中不存在 show(B obj),将 B 转型成其父类 A + a.show(b); // A.show(A) + // 在 B 中存在从 A 继承来的 show(C obj),直接调用 + b.show(c); // A.show(C) + // 在 B 中不存在 show(D obj),但是存在从 A 继承来的 show(C obj),将 D 转型成其父类 C + b.show(d); // A.show(C) + + // 引用的还是 B 对象,所以 ba 和 b 的调用结果一样 + A ba = new B(); + ba.show(c); // A.show(C) + ba.show(d); // A.show(C) +} +``` + +**2. 重载(Overload)** + +存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。 + +应该注意的是,返回值不同,其它都相同不算是重载。 + +```java +class OverloadingExample { + public void show(int x) { + System.out.println(x); + } + + public void show(int x, String y) { + System.out.println(x + " " + y); + } +} +``` + +```java +public static void main(String[] args) { + OverloadingExample example = new OverloadingExample(); + example.show(1); + example.show(1, "2"); +} +``` + +## 七、反射 + +每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 + +类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。 + +反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 + +Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: + +- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; +- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; +- **Constructor** :可以用 Constructor 的 newInstance() 创建新的对象。 + +**反射的优点:** + +- **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 +- **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 +- **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 + +**反射的缺点:** + +尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。 + +- **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 + +- **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 + +- **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 + +- [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html) +- [深入解析 Java 反射(1)- 基础](http://www.sczyh30.com/posts/Java/java-reflection-1/) + +## 八、异常 + +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: + +- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; +- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 + +

+ +- [Java Exception Interview Questions and Answers](https://www.journaldev.com/2167/java-exception-interview-questions-and-answersl) + +- [Java提高篇——Java 异常处理](https://www.cnblogs.com/Qian123/p/5715402.html) + +## 九、泛型 + +```java +public class Box { + // T stands for "Type" + private T t; + public void set(T t) { this.t = t; } + public T get() { return t; } +} +``` + +- [Java 泛型详解](https://www.cnblogs.com/Blue-Keroro/p/8875898.html) +- [10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693) + +## 十、注解 + +Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。 + +[注解 Annotation 实现原理与自定义注解例子](https://www.cnblogs.com/acm-bingzi/p/javaAnnotation.html) + +## 十一、特性 + +### Java 各版本的新特性 + +**New highlights in Java SE 8** + +1. Lambda Expressions +2. Pipelines and Streams +3. Date and Time API +4. Default Methods +5. Type Annotations +6. Nashhorn JavaScript Engine +7. Concurrent Accumulators +8. Parallel operations +9. PermGen Error Removed + +**New highlights in Java SE 7** + +1. Strings in Switch Statement +2. Type Inference for Generic Instance Creation +3. Multiple Exception Handling +4. Support for Dynamic Languages +5. Try with Resources +6. Java nio Package +7. Binary Literals, Underscore in literals +8. Diamond Syntax + +- [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) +- [Java 8 特性](http://www.importnew.com/19345.html) + +### Java 与 C++ 的区别 + +- Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。 +- Java 通过虚拟机从而实现跨平台特性,但是 C++ 依赖于特定的平台。 +- Java 没有指针,它的引用可以理解为安全指针,而 C++ 具有和 C 一样的指针。 +- Java 支持自动垃圾回收,而 C++ 需要手动回收。 +- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。 +- Java 不支持操作符重载,虽然可以对两个 String 对象执行加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。 +- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。 + +[What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php) + +### JRE or JDK + +- JRE:Java Runtime Environment,Java 运行环境的简称,为 Java 的运行提供了所需的环境。它是一个 JVM 程序,主要包括了 JVM 的标准实现和一些 Java 基本类库。 +- JDK:Java Development Kit,Java 开发工具包,提供了 Java 的开发及运行环境。JDK 是 Java 开发的核心,集成了 JRE 以及一些其它的工具,比如编译 Java 源码的编译器 javac 等。 + +## 参考资料 + +- Eckel B. Java 编程思想[M]. 机械工业出版社, 2002. +- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. diff --git "a/docs/notes/Java \345\256\271\345\231\250.md" "b/notes/Java \345\256\271\345\231\250.md" similarity index 79% rename from "docs/notes/Java \345\256\271\345\231\250.md" rename to "notes/Java \345\256\271\345\231\250.md" index 2accf9f849..b13bf7d5ca 100644 --- "a/docs/notes/Java \345\256\271\345\231\250.md" +++ "b/notes/Java \345\256\271\345\231\250.md" @@ -1,41 +1,42 @@ +# Java 容器 -* [一、概览](#一概览) - * [Collection](#collection) - * [Map](#map) -* [二、容器中的设计模式](#二容器中的设计模式) - * [迭代器模式](#迭代器模式) - * [适配器模式](#适配器模式) -* [三、源码分析](#三源码分析) - * [ArrayList](#arraylist) - * [Vector](#vector) - * [CopyOnWriteArrayList](#copyonwritearraylist) - * [LinkedList](#linkedlist) - * [HashMap](#hashmap) - * [ConcurrentHashMap](#concurrenthashmap) - * [LinkedHashMap](#linkedhashmap) - * [WeakHashMap](#weakhashmap) -* [参考资料](#参考资料) +* [Java 容器](#java-容器) + * [一、概览](#一概览) + * [Collection](#collection) + * [Map](#map) + * [二、容器中的设计模式](#二容器中的设计模式) + * [迭代器模式](#迭代器模式) + * [适配器模式](#适配器模式) + * [三、源码分析](#三源码分析) + * [ArrayList](#arraylist) + * [Vector](#vector) + * [CopyOnWriteArrayList](#copyonwritearraylist) + * [LinkedList](#linkedlist) + * [HashMap](#hashmap) + * [ConcurrentHashMap](#concurrenthashmap) + * [LinkedHashMap](#linkedhashmap) + * [WeakHashMap](#weakhashmap) + * [参考资料](#参考资料) -# 一、概览 +## 一、概览 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。 -## Collection +### Collection -

+

- -### 1. Set +#### 1. Set - TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 - HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。 -- LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。 +- LinkedHashSet:具有 HashSet 的查找效率,并且内部使用双向链表维护元素的插入顺序。 -### 2. List +#### 2. List - ArrayList:基于动态数组实现,支持随机访问。 @@ -43,30 +44,30 @@ - LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。 -### 3. Queue +#### 3. Queue - LinkedList:可以用它来实现双向队列。 - PriorityQueue:基于堆结构实现,可以用它来实现优先队列。 -## Map +### Map -

+

- TreeMap:基于红黑树实现。 - HashMap:基于哈希表实现。 -- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。 +- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程同时写入 HashTable 不会导致数据不一致。它是遗留类,不应该去使用它,而是使用 ConcurrentHashMap 来支持线程安全,ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。 - LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。 -# 二、容器中的设计模式 +## 二、容器中的设计模式 -## 迭代器模式 +### 迭代器模式 -

+

Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。 @@ -81,7 +82,7 @@ for (String item : list) { } ``` -## 适配器模式 +### 适配器模式 java.util.Arrays#asList() 可以把数组类型转换为 List 类型。 @@ -103,18 +104,18 @@ List list = Arrays.asList(arr); List list = Arrays.asList(1, 2, 3); ``` -# 三、源码分析 +## 三、源码分析 如果没有特别说明,以下源码分析基于 JDK 1.8。 在 IDEA 中 double shift 调出 Search EveryWhere,查找源码文件,找到之后就可以阅读源码。 -## ArrayList +### ArrayList -### 1. 概览 +#### 1. 概览 -实现了 RandomAccess 接口,因此支持随机访问。这是理所当然的,因为 ArrayList 是基于数组实现的。 +因为 ArrayList 是基于数组实现的,所以支持快速随机访问。RandomAccess 接口标识着该类支持快速随机访问。 ```java public class ArrayList extends AbstractList @@ -127,12 +128,11 @@ public class ArrayList extends AbstractList private static final int DEFAULT_CAPACITY = 10; ``` -

- +

-### 2. 扩容 +#### 2. 扩容 -添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 `oldCapacity + (oldCapacity >> 1)`,也就是旧容量的 1.5 倍。 +添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量的大小为 `oldCapacity + (oldCapacity >> 1)`,即 oldCapacity+oldCapacity/2。其中 oldCapacity >> 1 需要取整,所以新容量大约是旧容量的 1.5 倍左右。(oldCapacity 为偶数就是 1.5 倍,为奇数就是 1.5 倍-0.5) 扩容操作需要调用 `Arrays.copyOf()` 把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。 @@ -170,9 +170,9 @@ private void grow(int minCapacity) { } ``` -### 3. 删除元素 +#### 3. 删除元素 -需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。 +需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看到 ArrayList 删除元素的代价是非常高的。 ```java public E remove(int index) { @@ -187,34 +187,7 @@ public E remove(int index) { } ``` -### 4. Fail-Fast - -modCount 用来记录 ArrayList 结构发生变化的次数。结构发生变化是指添加或者删除至少一个元素的所有操作,或者是调整内部数组的大小,仅仅只是设置元素的值不算结构发生变化。 - -在进行序列化或者迭代等操作时,需要比较操作前后 modCount 是否改变,如果改变了需要抛出 ConcurrentModificationException。 - -```java -private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException{ - // Write out element count, and any hidden stuff - int expectedModCount = modCount; - s.defaultWriteObject(); - - // Write out size as capacity for behavioural compatibility with clone() - s.writeInt(size); - - // Write out all elements in the proper order. - for (int i=0; i 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。 @@ -321,9 +342,9 @@ List synList = Collections.synchronizedList(list); List list = new CopyOnWriteArrayList<>(); ``` -## CopyOnWriteArrayList +### CopyOnWriteArrayList -### 读写分离 +#### 1. 读写分离 写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。 @@ -359,7 +380,7 @@ private E get(Object[] a, int index) { } ``` -### 适用场景 +#### 2. 适用场景 CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。 @@ -370,9 +391,9 @@ CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读 所以 CopyOnWriteArrayList 不适合内存敏感以及对实时性要求很高的场景。 -## LinkedList +### LinkedList -### 1. 概览 +#### 1. 概览 基于双向链表实现,使用 Node 存储链表节点信息。 @@ -391,30 +412,29 @@ transient Node first; transient Node last; ``` -

+

-### 2. 与 ArrayList 的比较 +#### 2. 与 ArrayList 的比较 -- ArrayList 基于动态数组实现,LinkedList 基于双向链表实现; -- ArrayList 支持随机访问,LinkedList 不支持; -- LinkedList 在任意位置添加删除元素更快。 +ArrayList 基于动态数组实现,LinkedList 基于双向链表实现。ArrayList 和 LinkedList 的区别可以归结为数组和链表的区别: -## HashMap +- 数组支持随机访问,但插入删除的代价很高,需要移动大量元素; +- 链表不支持随机访问,但插入删除只需要改变指针。 + +### HashMap 为了便于理解,以下源码分析以 JDK 1.7 为主。 -### 1. 存储结构 +#### 1. 存储结构 + +内部包含了一个 Entry 类型的数组 table。Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 -内部包含了一个 Entry 类型的数组 table。 +

```java transient Entry[] table; ``` -Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值相同的 Entry。 - -

- ```java static class Entry implements Map.Entry { final K key; @@ -468,7 +488,7 @@ static class Entry implements Map.Entry { } ``` -### 2. 拉链法的工作原理 +#### 2. 拉链法的工作原理 ```java HashMap map = new HashMap<>(); @@ -478,20 +498,20 @@ map.put("K3", "V3"); ``` - 新建一个 HashMap,默认大小为 16; -- 插入 <K1,V1> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到所在的桶下标 115%16=3。 -- 插入 <K2,V2> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。 -- 插入 <K3,V3> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,插在 <K2,V2> 前面。 +- 插入 <K1,V1\> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到所在的桶下标 115%16=3。 +- 插入 <K2,V2\> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。 +- 插入 <K3,V3\> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,插在 <K2,V2\> 前面。 -应该注意到链表的插入是以头插法方式进行的,例如上面的 <K3,V3> 不是插在 <K2,V2> 后面,而是插入在链表头部。 +应该注意到链表的插入是以头插法方式进行的,例如上面的 <K3,V3\> 不是插在 <K2,V2\> 后面,而是插入在链表头部。 查找需要分成两步进行: - 计算键值对所在的桶; - 在链表上顺序查找,时间复杂度显然和链表的长度成正比。 -

+

-### 3. put 操作 +#### 3. put 操作 ```java public V put(K key, V value) { @@ -570,7 +590,7 @@ Entry(int h, K k, V v, Entry n) { } ``` -### 4. 确定桶下标 +#### 4. 确定桶下标 很多操作都需要先确定一个键值对所在的桶下标。 @@ -579,7 +599,7 @@ int hash = hash(key); int i = indexFor(hash, table.length); ``` -**4.1 计算 hash 值** +**4.1 计算 hash 值** ```java final int hash(Object k) { @@ -604,9 +624,9 @@ public final int hashCode() { } ``` -**4.2 取模** +**4.2 取模** -令 x = 1<<4,即 x 为 2 的 4 次方,它具有以下性质: +令 x = 1\<\<4,即 x 为 2 的 4 次方,它具有以下性质: ``` x : 00010000 @@ -639,11 +659,11 @@ static int indexFor(int h, int length) { } ``` -### 5. 扩容-基本原理 +#### 5. 扩容-基本原理 -设 HashMap 的 table 长度为 M,需要存储的键值对数量为 N,如果哈希函数满足均匀性的要求,那么每条链表的长度大约为 N/M,因此平均查找次数的复杂度为 O(N/M)。 +设 HashMap 的 table 长度为 M,需要存储的键值对数量为 N,如果哈希函数满足均匀性的要求,那么每条链表的长度大约为 N/M,因此查找的复杂度为 O(N/M)。 -为了让查找的成本降低,应该尽可能使得 N/M 尽可能小,因此需要保证 M 尽可能大,也就是说 table 要尽可能大。HashMap 采用动态扩容来根据当前的 N 值来调整 M 值,使得空间效率和时间效率都能得到保证。 +为了让查找的成本降低,应该使 N/M 尽可能小,因此需要保证 M 尽可能大,也就是说 table 要尽可能大。HashMap 采用动态扩容来根据当前的 N 值来调整 M 值,使得空间效率和时间效率都能得到保证。 和扩容相关的参数主要有:capacity、size、threshold 和 load_factor。 @@ -652,7 +672,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)(capacity* loadFactor)。 | ```java static final int DEFAULT_INITIAL_CAPACITY = 16; @@ -718,9 +738,9 @@ void transfer(Entry[] newTable) { } ``` -### 6. 扩容-重新计算桶下标 +#### 6. 扩容-重新计算桶下标 -在进行扩容时,需要把键值对重新放到对应的桶上。HashMap 使用了一个特殊的机制,可以降低重新计算桶下标的操作。 +在进行扩容时,需要把键值对重新计算桶下标,从而放到对应的桶上。在前面提到,HashMap 使用 hash%capacity 来确定桶下标。HashMap capacity 为 2 的 n 次方这一特点能够极大降低重新计算桶下标操作的复杂度。 假设原数组长度 capacity 为 16,扩容之后 new capacity 为 32: @@ -729,12 +749,12 @@ 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. 计算数组容量 +#### 7. 计算数组容量 HashMap 构造函数允许用户传入的容量不是 2 的 n 次方,因为它可以自动地将传入的容量转换为 2 的 n 次方。 @@ -767,20 +787,22 @@ static final int tableSizeFor(int cap) { } ``` -### 8. 链表转红黑树 +#### 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 中的元素次序是不变的。 -## ConcurrentHashMap +### ConcurrentHashMap -### 1. 存储结构 +#### 1. 存储结构 + +

```java static final class HashEntry { @@ -825,9 +847,7 @@ final Segment[] segments; static final int DEFAULT_CONCURRENCY_LEVEL = 16; ``` -

- -### 2. size 操作 +#### 2. size 操作 每个 Segment 维护了一个 count 变量来统计该 Segment 中的键值对个数。 @@ -900,7 +920,7 @@ public int size() { } ``` -### 3. JDK 1.8 的改动 +#### 3. JDK 1.8 的改动 JDK 1.7 使用分段锁机制来实现并发更新操作,核心类为 Segment,它继承自重入锁 ReentrantLock,并发度与 Segment 数量相等。 @@ -908,9 +928,9 @@ JDK 1.8 使用了 CAS 操作来支持更高的并发度,在 CAS 操作失败 并且 JDK 1.8 的实现也在链表过长时会转换为红黑树。 -## LinkedHashMap +### LinkedHashMap -### 存储结构 +#### 存储结构 继承自 HashMap,因此具有和 HashMap 一样的快速查找特性。 @@ -945,7 +965,7 @@ void afterNodeAccess(Node p) { } void afterNodeInsertion(boolean evict) { } ``` -### afterNodeAccess() +#### afterNodeAccess() 当一个节点被访问时,如果 accessOrder 为 true,则会将该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。 @@ -976,7 +996,7 @@ void afterNodeAccess(Node e) { // move node to last } ``` -### afterNodeInsertion() +#### afterNodeInsertion() 在 put 等操作之后执行,当 removeEldestEntry() 方法返回 true 时会移除最晚的节点,也就是链表首部节点 first。 @@ -1000,7 +1020,7 @@ protected boolean removeEldestEntry(Map.Entry eldest) { } ``` -### LRU 缓存 +#### LRU 缓存 以下是使用 LinkedHashMap 实现的一个 LRU 缓存: @@ -1038,9 +1058,9 @@ public static void main(String[] args) { [3, 1, 4] ``` -## WeakHashMap +### WeakHashMap -### 存储结构 +#### 存储结构 WeakHashMap 的 Entry 继承自 WeakReference,被 WeakReference 关联的对象在下一次垃圾回收时会被回收。 @@ -1050,7 +1070,7 @@ WeakHashMap 主要用来实现缓存,通过使用 WeakHashMap 来引用缓存 private static class Entry extends WeakReference implements Map.Entry ``` -### ConcurrentCache +#### ConcurrentCache Tomcat 中的 ConcurrentCache 使用了 WeakHashMap 来实现缓存功能。 @@ -1097,7 +1117,7 @@ public final class ConcurrentCache { ``` -# 参考资料 +## 参考资料 - Eckel B. Java 编程思想 [M]. 机械工业出版社, 2002. - [Java Collection Framework](https://www.w3resource.com/java-tutorial/java-collections.php) @@ -1111,9 +1131,3 @@ public final class ConcurrentCache { - [Java 集合细节(二):asList 的缺陷](http://wiki.jikexueyuan.com/project/java-enhancement/java-thirtysix.html) - [Java Collection Framework – The LinkedList Class](http://javaconceptoftheday.com/java-collection-framework-linkedlist-class/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/Java \345\271\266\345\217\221.md" "b/notes/Java \345\271\266\345\217\221.md" similarity index 84% rename from "docs/notes/Java \345\271\266\345\217\221.md" rename to "notes/Java \345\271\266\345\217\221.md" index 4f1850d036..cb01195074 100644 --- "a/docs/notes/Java \345\271\266\345\217\221.md" +++ "b/notes/Java \345\271\266\345\217\221.md" @@ -1,117 +1,68 @@ +# Java 并发 -* [一、线程状态转换](#一线程状态转换) - * [新建(New)](#新建new) - * [可运行(Runnable)](#可运行runnable) - * [阻塞(Blocking)](#阻塞blocking) - * [无限期等待(Waiting)](#无限期等待waiting) - * [限期等待(Timed Waiting)](#限期等待timed-waiting) - * [死亡(Terminated)](#死亡terminated) -* [二、使用线程](#二使用线程) - * [实现 Runnable 接口](#实现-runnable-接口) - * [实现 Callable 接口](#实现-callable-接口) - * [继承 Thread 类](#继承-thread-类) - * [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread) -* [三、基础线程机制](#三基础线程机制) - * [Executor](#executor) - * [Daemon](#daemon) - * [sleep()](#sleep) - * [yield()](#yield) -* [四、中断](#四中断) - * [InterruptedException](#interruptedexception) - * [interrupted()](#interrupted) - * [Executor 的中断操作](#executor-的中断操作) -* [五、互斥同步](#五互斥同步) - * [synchronized](#synchronized) - * [ReentrantLock](#reentrantlock) - * [比较](#比较) - * [使用选择](#使用选择) -* [六、线程之间的协作](#六线程之间的协作) - * [join()](#join) - * [wait() notify() notifyAll()](#wait-notify-notifyall) - * [await() signal() signalAll()](#await-signal-signalall) -* [七、J.U.C - AQS](#七juc---aqs) - * [CountDownLatch](#countdownlatch) - * [CyclicBarrier](#cyclicbarrier) - * [Semaphore](#semaphore) -* [八、J.U.C - 其它组件](#八juc---其它组件) - * [FutureTask](#futuretask) - * [BlockingQueue](#blockingqueue) - * [ForkJoin](#forkjoin) -* [九、线程不安全示例](#九线程不安全示例) -* [十、Java 内存模型](#十java-内存模型) - * [主内存与工作内存](#主内存与工作内存) - * [内存间交互操作](#内存间交互操作) - * [内存模型三大特性](#内存模型三大特性) - * [先行发生原则](#先行发生原则) -* [十一、线程安全](#十一线程安全) - * [不可变](#不可变) - * [互斥同步](#互斥同步) - * [非阻塞同步](#非阻塞同步) - * [无同步方案](#无同步方案) -* [十二、锁优化](#十二锁优化) - * [自旋锁](#自旋锁) - * [锁消除](#锁消除) - * [锁粗化](#锁粗化) - * [轻量级锁](#轻量级锁) - * [偏向锁](#偏向锁) -* [十三、多线程开发良好的实践](#十三多线程开发良好的实践) -* [参考资料](#参考资料) +* [Java 并发](#java-并发) + * [一、使用线程](#一使用线程) + * [实现 Runnable 接口](#实现-runnable-接口) + * [实现 Callable 接口](#实现-callable-接口) + * [继承 Thread 类](#继承-thread-类) + * [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread) + * [二、基础线程机制](#二基础线程机制) + * [Executor](#executor) + * [Daemon](#daemon) + * [sleep()](#sleep) + * [yield()](#yield) + * [三、中断](#三中断) + * [InterruptedException](#interruptedexception) + * [interrupted()](#interrupted) + * [Executor 的中断操作](#executor-的中断操作) + * [四、互斥同步](#四互斥同步) + * [synchronized](#synchronized) + * [ReentrantLock](#reentrantlock) + * [比较](#比较) + * [使用选择](#使用选择) + * [五、线程之间的协作](#五线程之间的协作) + * [join()](#join) + * [wait() notify() notifyAll()](#wait-notify-notifyall) + * [await() signal() signalAll()](#await-signal-signalall) + * [六、线程状态](#六线程状态) + * [新建(NEW)](#新建new) + * [可运行(RUNABLE)](#可运行runable) + * [阻塞(BLOCKED)](#阻塞blocked) + * [无限期等待(WAITING)](#无限期等待waiting) + * [限期等待(TIMED_WAITING)](#限期等待timed_waiting) + * [死亡(TERMINATED)](#死亡terminated) + * [七、J.U.C - AQS](#七juc---aqs) + * [CountDownLatch](#countdownlatch) + * [CyclicBarrier](#cyclicbarrier) + * [Semaphore](#semaphore) + * [八、J.U.C - 其它组件](#八juc---其它组件) + * [FutureTask](#futuretask) + * [BlockingQueue](#blockingqueue) + * [ForkJoin](#forkjoin) + * [九、线程不安全示例](#九线程不安全示例) + * [十、Java 内存模型](#十java-内存模型) + * [主内存与工作内存](#主内存与工作内存) + * [内存间交互操作](#内存间交互操作) + * [内存模型三大特性](#内存模型三大特性) + * [先行发生原则](#先行发生原则) + * [十一、线程安全](#十一线程安全) + * [不可变](#不可变) + * [互斥同步](#互斥同步) + * [非阻塞同步](#非阻塞同步) + * [无同步方案](#无同步方案) + * [十二、锁优化](#十二锁优化) + * [自旋锁](#自旋锁) + * [锁消除](#锁消除) + * [锁粗化](#锁粗化) + * [轻量级锁](#轻量级锁) + * [偏向锁](#偏向锁) + * [十三、多线程开发良好的实践](#十三多线程开发良好的实践) + * [参考资料](#参考资料) -# 一、线程状态转换 -

- -## 新建(New) - -创建后尚未启动。 - -## 可运行(Runnable) - -可能正在运行,也可能正在等待 CPU 时间片。 - -包含了操作系统线程状态中的 Running 和 Ready。 - -## 阻塞(Blocking) - -等待获取一个排它锁,如果其线程释放了锁就会结束此状态。 - -## 无限期等待(Waiting) - -等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。 - -| 进入方法 | 退出方法 | -| --- | --- | -| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() | -| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 | -| LockSupport.park() 方法 | LockSupport.unpark(Thread) | - -## 限期等待(Timed Waiting) - -无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。 - -调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。 - -调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。 - -睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态。 - -阻塞和等待的区别在于,阻塞是被动的,它是在等待获取一个排它锁。而等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。 - -| 进入方法 | 退出方法 | -| --- | --- | -| Thread.sleep() 方法 | 时间结束 | -| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() | -| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 | -| LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) | -| LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) | - -## 死亡(Terminated) - -可以是线程结束任务之后自己结束,或者产生了异常而结束。 - -# 二、使用线程 +## 一、使用线程 有三种使用线程的方法: @@ -119,22 +70,23 @@ - 实现 Callable 接口; - 继承 Thread 类。 -实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。 +实现 Runnable 和 Callable 接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过 Thread 来调用。可以理解为任务是通过线程驱动从而执行的。 -## 实现 Runnable 接口 +### 实现 Runnable 接口 -需要实现 run() 方法。 - -通过 Thread 调用 start() 方法来启动线程。 +需要实现接口中的 run() 方法。 ```java public class MyRunnable implements Runnable { + @Override public void run() { // ... } } ``` +使用 Runnable 实例再创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。 + ```java public static void main(String[] args) { MyRunnable instance = new MyRunnable(); @@ -143,7 +95,7 @@ public static void main(String[] args) { } ``` -## 实现 Callable 接口 +### 实现 Callable 接口 与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 @@ -165,7 +117,7 @@ public static void main(String[] args) throws ExecutionException, InterruptedExc } ``` -## 继承 Thread 类 +### 继承 Thread 类 同样也是需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。 @@ -186,16 +138,16 @@ public static void main(String[] args) { } ``` -## 实现接口 VS 继承 Thread +### 实现接口 VS 继承 Thread 实现接口会更好一些,因为: - Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口; - 类可能只要求可执行就行,继承整个 Thread 类开销过大。 -# 三、基础线程机制 +## 二、基础线程机制 -## Executor +### Executor Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。 @@ -215,7 +167,7 @@ public static void main(String[] args) { } ``` -## Daemon +### Daemon 守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分。 @@ -223,7 +175,7 @@ public static void main(String[] args) { main() 属于非守护线程。 -使用 setDaemon() 方法将一个线程设置为守护线程。 +在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程。 ```java public static void main(String[] args) { @@ -232,7 +184,7 @@ public static void main(String[] args) { } ``` -## sleep() +### sleep() Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒。 @@ -248,7 +200,7 @@ public void run() { } ``` -## yield() +### yield() 对静态方法 Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。 @@ -258,11 +210,11 @@ public void run() { } ``` -# 四、中断 +## 三、中断 一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束。 -## InterruptedException +### InterruptedException 通过调用一个线程的 interrupt() 来中断该线程,如果该线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出 InterruptedException,从而提前结束该线程。但是不能中断 I/O 阻塞和 synchronized 锁阻塞。 @@ -303,7 +255,7 @@ java.lang.InterruptedException: sleep interrupted at java.lang.Thread.run(Thread.java:745) ``` -## interrupted() +### interrupted() 如果一个线程的 run() 方法执行一个无限循环,并且没有执行 sleep() 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt() 方法就无法使线程提前结束。 @@ -336,7 +288,7 @@ public static void main(String[] args) throws InterruptedException { Thread end ``` -## Executor 的中断操作 +### Executor 的中断操作 调用 Executor 的 shutdown() 方法会等待线程都执行完毕之后再关闭,但是如果调用的是 shutdownNow() 方法,则相当于调用每个线程的 interrupt() 方法。 @@ -369,7 +321,7 @@ java.lang.InterruptedException: sleep interrupted at java.lang.Thread.run(Thread.java:745) ``` -如果只想中断 Executor 中的一个线程,可以通过使用 submit() 方法来提交一个线程,它会返回一个 Future 对象,通过调用该对象的 cancel(true) 方法就可以中断线程。 +如果只想中断 Executor 中的一个线程,可以通过使用 submit() 方法来提交一个线程,它会返回一个 Future\ 对象,通过调用该对象的 cancel(true) 方法就可以中断线程。 ```java Future future = executorService.submit(() -> { @@ -378,13 +330,13 @@ Future future = executorService.submit(() -> { future.cancel(true); ``` -# 五、互斥同步 +## 四、互斥同步 Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。 -## synchronized +### synchronized -**1. 同步一个代码块** +**1. 同步一个代码块** ```java public void func() { @@ -441,7 +393,7 @@ public static void main(String[] args) { ``` -**2. 同步一个方法** +**2. 同步一个方法** ```java public synchronized void func () { @@ -451,7 +403,7 @@ public synchronized void func () { 它和同步代码块一样,作用于同一个对象。 -**3. 同步一个类** +**3. 同步一个类** ```java public void func() { @@ -490,7 +442,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() { @@ -500,7 +452,7 @@ public synchronized static void fun() { 作用于整个类。 -## ReentrantLock +### ReentrantLock ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁。 @@ -536,41 +488,41 @@ 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 对象。 -## 使用选择 +### 使用选择 除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。 -# 六、线程之间的协作 +## 五、线程之间的协作 当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。 -## join() +### join() 在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。 @@ -626,7 +578,7 @@ A B ``` -## wait() notify() notifyAll() +### wait() notify() notifyAll() 调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。 @@ -669,12 +621,12 @@ before after ``` -**wait() 和 sleep() 的区别** +**wait() 和 sleep() 的区别** - wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法; - wait() 会释放锁,sleep() 不会。 -## await() signal() signalAll() +### await() signal() signalAll() java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。 @@ -726,17 +678,65 @@ before after ``` -# 七、J.U.C - AQS +## 六、线程状态 + +一个线程只能处于一种状态,并且这里的线程状态特指 Java 虚拟机的线程状态,不能反映线程在特定操作系统下的状态。 + +### 新建(NEW) + +创建后尚未启动。 + +### 可运行(RUNABLE) + +正在 Java 虚拟机中运行。但是在操作系统层面,它可能处于运行状态,也可能等待资源调度(例如处理器资源),资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度。 + +### 阻塞(BLOCKED) + +请求获取 monitor lock 从而进入 synchronized 函数或者代码块,但是其它线程已经占用了该 monitor lock,所以出于阻塞状态。要结束该状态进入从而 RUNABLE 需要其他线程释放 monitor lock。 + +### 无限期等待(WAITING) + +等待其它线程显式地唤醒。 + +阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 monitor lock。而等待是主动的,通过调用 Object.wait() 等方法进入。 + +| 进入方法 | 退出方法 | +| --- | --- | +| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() | +| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 | +| LockSupport.park() 方法 | LockSupport.unpark(Thread) | + +### 限期等待(TIMED_WAITING) + +无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。 + +| 进入方法 | 退出方法 | +| --- | --- | +| Thread.sleep() 方法 | 时间结束 | +| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() | +| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 | +| LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) | +| LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) | + +调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态。 + +### 死亡(TERMINATED) + +可以是线程结束任务之后自己结束,或者产生了异常而结束。 + +[Java SE 9 Enum Thread.State](https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.State.html) + +## 七、J.U.C - AQS java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J.U.C 的核心。 -## CountDownLatch +### CountDownLatch -用来控制一个线程等待多个线程。 +用来控制一个或者多个线程等待多个线程。 维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。 -

+

```java public class CountdownLatchExample { @@ -762,7 +762,7 @@ public class CountdownLatchExample { run..run..run..run..run..run..run..run..run..run..end ``` -## CyclicBarrier +### CyclicBarrier 用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。 @@ -785,7 +785,7 @@ public CyclicBarrier(int parties) { } ``` -

+

```java public class CyclicBarrierExample { @@ -814,7 +814,7 @@ public class CyclicBarrierExample { before..before..before..before..before..before..before..before..before..before..after..after..after..after..after..after..after..after..after..after.. ``` -## Semaphore +### Semaphore Semaphore 类似于操作系统中的信号量,可以控制对互斥资源的访问线程数。 @@ -849,11 +849,11 @@ public class SemaphoreExample { 2 1 2 2 2 2 2 1 2 2 ``` -# 八、J.U.C - 其它组件 +## 八、J.U.C - 其它组件 -## FutureTask +### FutureTask -在介绍 Callable 时我们知道它可以有返回值,返回值通过 Future 进行封装。FutureTask 实现了 RunnableFuture 接口,该接口继承自 Runnable 和 Future 接口,这使得 FutureTask 既可以当做一个任务执行,也可以有返回值。 +在介绍 Callable 时我们知道它可以有返回值,返回值通过 Future\ 进行封装。FutureTask 实现了 RunnableFuture 接口,该接口继承自 Runnable 和 Future\ 接口,这使得 FutureTask 既可以当做一个任务执行,也可以有返回值。 ```java public class FutureTask implements RunnableFuture @@ -903,16 +903,16 @@ other task is running... 4950 ``` -## BlockingQueue +### BlockingQueue java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现: -- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度) -- **优先级队列** :PriorityBlockingQueue +- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度) +- **优先级队列** :PriorityBlockingQueue 提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置。 -**使用 BlockingQueue 实现生产者消费者问题** +**使用 BlockingQueue 实现生产者消费者问题** ```java public class ProducerConsumer { @@ -967,7 +967,7 @@ public static void main(String[] args) { produce..produce..consume..consume..produce..consume..produce..consume..produce..consume.. ``` -## ForkJoin +### ForkJoin 主要用于并行计算中,和 MapReduce 原理类似,都是把大的计算任务拆分成多个小任务并行计算。 @@ -1022,9 +1022,9 @@ public class ForkJoinPool extends AbstractExecutorService ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线程都维护了一个双端队列,用来存储需要执行的任务。工作窃取算法允许空闲的线程从其它线程的双端队列中窃取一个任务来执行。窃取的任务必须是最晚的任务,避免和队列所属线程发生竞争。例如下图中,Thread2 从 Thread1 的队列中拿出最晚的 Task1 任务,Thread1 会拿出 Task2 来执行,这样就避免发生竞争。但是如果队列中只有一个任务时还是会发生竞争。 -

+

-# 九、线程不安全示例 +## 九、线程不安全示例 如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。 @@ -1067,29 +1067,29 @@ public static void main(String[] args) throws InterruptedException { 997 ``` -# 十、Java 内存模型 +## 十、Java 内存模型 Java 内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。 -## 主内存与工作内存 +### 主内存与工作内存 处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。 加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。 -

+

所有的变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存或者寄存器中,保存了该线程使用的变量的主内存副本拷贝。 线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成。 -

+

-## 内存间交互操作 +### 内存间交互操作 Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互操作。 -

+

- read:把一个变量的值从主内存传输到工作内存中 - load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中 @@ -1100,9 +1100,9 @@ Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互 - lock:作用于主内存的变量 - unlock -## 内存模型三大特性 +### 内存模型三大特性 -### 1. 原子性 +#### 1. 原子性 Java 内存模型保证了 read、load、use、assign、store、write、lock 和 unlock 操作具有原子性,例如对一个 int 类型的变量执行 assign 赋值操作,这个操作就是原子性的。但是 Java 内存模型允许虚拟机将没有被 volatile 修饰的 64 位数据(long,double)的读写操作划分为两次 32 位的操作来进行,即 load、store、read 和 write 操作可以不具备原子性。 @@ -1112,11 +1112,11 @@ Java 内存模型保证了 read、load、use、assign、store、write、lock 和 下图演示了两个线程同时对 cnt 进行操作,load、assign、store 这一系列操作整体上看不具备原子性,那么在 T1 修改 cnt 并且还没有将修改后的值写入主内存,T2 依然可以读入旧值。可以看出,这两个线程虽然执行了两次自增运算,但是主内存中 cnt 的值最后为 1 而不是 2。因此对 int 类型读写操作满足原子性只是说明 load、assign、store 这些单个操作具备原子性。 -

+

AtomicInteger 能保证多个线程修改的原子性。 -

+

使用 AtomicInteger 重写之前线程不安全的代码之后得到以下线程安全实现: @@ -1194,11 +1194,11 @@ public static void main(String[] args) throws InterruptedException { 1000 ``` -### 2. 可见性 +#### 2. 可见性 可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。 -主要有有三种实现可见性的方式: +主要有三种实现可见性的方式: - volatile - synchronized,对一个变量执行 unlock 操作之前,必须把变量值同步回主内存。 @@ -1206,7 +1206,7 @@ public static void main(String[] args) throws InterruptedException { 对前面的线程不安全示例中的 cnt 变量使用 volatile 修饰,不能解决线程不安全问题,因为 volatile 并不能保证操作的原子性。 -### 3. 有序性 +#### 3. 有序性 有序性是指:在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。 @@ -1214,75 +1214,75 @@ volatile 关键字通过添加内存屏障的方式来禁止指令重排,即 也可以通过 synchronized 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。 -## 先行发生原则 +### 先行发生原则 上面提到了可以用 volatile 和 synchronized 来保证有序性。除此之外,JVM 还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。 -### 1. 单一线程原则 +#### 1. 单一线程原则 > Single Thread rule 在一个线程内,在程序前面的操作先行发生于后面的操作。 -

+

-### 2. 管程锁定规则 +#### 2. 管程锁定规则 > Monitor Lock Rule 一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 -

+

-### 3. volatile 变量规则 +#### 3. volatile 变量规则 > Volatile Variable Rule 对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。 -

+

-### 4. 线程启动规则 +#### 4. 线程启动规则 > Thread Start Rule Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。 -

+

-### 5. 线程加入规则 +#### 5. 线程加入规则 > Thread Join Rule Thread 对象的结束先行发生于 join() 方法返回。 -

+

-### 6. 线程中断规则 +#### 6. 线程中断规则 > Thread Interruption Rule 对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。 -### 7. 对象终结规则 +#### 7. 对象终结规则 > Finalizer Rule 一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。 -### 8. 传递性 +#### 8. 传递性 > Transitivity 如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。 -# 十一、线程安全 +## 十一、线程安全 多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为。 线程安全有以下几种实现方式: -## 不可变 +### 不可变 不可变(Immutable)的对象一定是线程安全的,不需要再采取任何的线程安全保障措施。只要一个不可变的对象被正确地构建出来,永远也不会看到它在多个线程之中处于不一致的状态。多线程环境下,应当尽量使对象成为不可变,来满足线程安全。 @@ -1319,23 +1319,23 @@ public V put(K key, V value) { } ``` -## 互斥同步 +### 互斥同步 synchronized 和 ReentrantLock。 -## 非阻塞同步 +### 非阻塞同步 互斥同步最主要的问题就是线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。 互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。 -### 1. CAS - 随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要将线程阻塞,因此这种同步操作称为非阻塞同步。 +#### 1. CAS + 乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是:比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。 -### 2. AtomicInteger +#### 2. AtomicInteger J.U.C 包里面的整数原子类 AtomicInteger 的方法调用了 Unsafe 类的 CAS 操作。 @@ -1372,17 +1372,17 @@ public final int getAndAddInt(Object var1, long var2, int var4) { } ``` -### 3. ABA +#### 3. ABA 如果一个变量初次读取的时候是 A 值,它的值被改成了 B,后来又被改回为 A,那 CAS 操作就会误认为它从来没有被改变过。 J.U.C 包提供了一个带有标记的原子引用类 AtomicStampedReference 来解决这个问题,它可以通过控制变量值的版本来保证 CAS 的正确性。大部分情况下 ABA 问题不会影响程序并发的正确性,如果需要解决 ABA 问题,改用传统的互斥同步可能会比原子类更高效。 -## 无同步方案 +### 无同步方案 要保证线程安全,并不是一定就要进行同步。如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性。 -### 1. 栈封闭 +#### 1. 栈封闭 多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。 @@ -1413,7 +1413,7 @@ public static void main(String[] args) { 100 ``` -### 2. 线程本地存储(Thread Local Storage) +#### 2. 线程本地存储(Thread Local Storage) 如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。 @@ -1474,7 +1474,7 @@ public class ThreadLocalExample1 { 它所对应的底层结构图为: -

+

每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。 @@ -1484,7 +1484,7 @@ public class ThreadLocalExample1 { ThreadLocal.ThreadLocalMap threadLocals = null; ``` -当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中。 +当调用一个 ThreadLocal 的 set(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal-\>value 键值对插入到该 Map 中。 ```java public void set(T value) { @@ -1519,17 +1519,17 @@ ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因 在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险。 -### 3. 可重入代码(Reentrant Code) +#### 3. 可重入代码(Reentrant Code) 这种代码也叫做纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码(包括递归调用它本身),而在控制权返回后,原来的程序不会出现任何错误。 可重入代码有一些共同的特征,例如不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数中传入、不调用非可重入的方法等。 -# 十二、锁优化 +## 十二、锁优化 这里的锁优化主要是指 JVM 对 synchronized 的优化。 -## 自旋锁 +### 自旋锁 互斥同步进入阻塞状态的开销都很大,应该尽量避免。在许多应用中,共享数据的锁定状态只会持续很短的一段时间。自旋锁的思想是让一个线程在请求一个共享数据的锁时执行忙循环(自旋)一段时间,如果在这段时间内能获得锁,就可以避免进入阻塞状态。 @@ -1537,7 +1537,7 @@ ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因 在 JDK 1.6 中引入了自适应的自旋锁。自适应意味着自旋的次数不再固定了,而是由前一次在同一个锁上的自旋次数及锁的拥有者的状态来决定。 -## 锁消除 +### 锁消除 锁消除是指对于被检测出不可能存在竞争的共享数据的锁进行消除。 @@ -1565,33 +1565,33 @@ public static String concatString(String s1, String s2, String s3) { 每个 append() 方法中都有一个同步块。虚拟机观察变量 sb,很快就会发现它的动态作用域被限制在 concatString() 方法内部。也就是说,sb 的所有引用永远不会逃逸到 concatString() 方法之外,其他线程无法访问到它,因此可以进行消除。 -## 锁粗化 +### 锁粗化 如果一系列的连续操作都对同一个对象反复加锁和解锁,频繁的加锁操作就会导致性能损耗。 上一节的示例代码中连续的 append() 方法就属于这类情况。如果虚拟机探测到由这样的一串零碎的操作都对同一个对象加锁,将会把加锁的范围扩展(粗化)到整个操作序列的外部。对于上一节的示例代码就是扩展到第一个 append() 操作之前直至最后一个 append() 操作之后,这样只需要加锁一次就可以了。 -## 轻量级锁 +### 轻量级锁 JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态:无锁状态(unlocked)、偏向锁状态(biasble)、轻量级锁状态(lightweight locked)和重量级锁状态(inflated)。 以下是 HotSpot 虚拟机对象头的内存布局,这些数据被称为 Mark Word。其中 tag bits 对应了五个状态,这些状态在右侧的 state 表格中给出。除了 marked for gc 状态,其它四个状态已经在前面介绍过了。 -

+

下图左侧是一个线程的虚拟机栈,其中有一部分称为 Lock Record 的区域,这是在轻量级锁运行过程创建的,用于存放锁对象的 Mark Word。而右侧就是一个锁对象,包含了 Mark Word 和其它信息。 -

+

轻量级锁是相对于传统的重量级锁而言,它使用 CAS 操作来避免重量级锁使用互斥量的开销。对于绝大部分的锁,在整个同步周期内都是不存在竞争的,因此也就不需要都使用互斥量进行同步,可以先采用 CAS 操作进行同步,如果 CAS 失败了再改用互斥量进行同步。 当尝试获取一个锁对象时,如果锁对象标记为 0 01,说明锁对象的锁未锁定(unlocked)状态。此时虚拟机在当前线程的虚拟机栈中创建 Lock Record,然后使用 CAS 操作将对象的 Mark Word 更新为 Lock Record 指针。如果 CAS 操作成功了,那么线程就获取了该对象上的锁,并且对象的 Mark Word 的锁标记变为 00,表示该对象处于轻量级锁状态。 -

+

如果 CAS 操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的虚拟机栈,如果是的话说明当前线程已经拥有了这个锁对象,那就可以直接进入同步块继续执行,否则说明这个锁对象已经被其他线程线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁。 -## 偏向锁 +### 偏向锁 偏向锁的思想是偏向于让第一个获取锁对象的线程,这个线程在之后获取该锁就不再需要进行同步操作,甚至连 CAS 操作也不再需要。 @@ -1599,9 +1599,9 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态: 当有另外一个线程去尝试获取这个锁对象时,偏向状态就宣告结束,此时撤销偏向(Revoke Bias)后恢复到未锁定状态或者轻量级锁状态。 -

+

-# 十三、多线程开发良好的实践 +## 十三、多线程开发良好的实践 - 给线程起个有意义的名字,这样可以方便找 Bug。 @@ -1617,7 +1617,7 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态: - 使用线程池而不是直接创建线程,这是因为创建线程代价很高,线程池可以有效地利用有限的线程来启动任务。 -# 参考资料 +## 参考资料 - BruceEckel. Java 编程思想: 第 4 版 [M]. 机械工业出版社, 2007. - 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011. @@ -1634,9 +1634,3 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态: - [JAVA FORK JOIN EXAMPLE](http://www.javacreed.com/java-fork-join-example/ "Java Fork Join Example") - [聊聊并发(八)——Fork/Join 框架介绍](http://ifeve.com/talk-concurrency-forkjoin/) - [Eliminating SynchronizationRelated Atomic Operations with Biased Locking and Bulk Rebiasing](http://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/Java \350\231\232\346\213\237\346\234\272.md" "b/notes/Java \350\231\232\346\213\237\346\234\272.md" new file mode 100644 index 0000000000..01df1073ae --- /dev/null +++ "b/notes/Java \350\231\232\346\213\237\346\234\272.md" @@ -0,0 +1,755 @@ +# Java 虚拟机 + +* [Java 虚拟机](#java-虚拟机) + * [一、运行时数据区域](#一运行时数据区域) + * [程序计数器](#程序计数器) + * [Java 虚拟机栈](#java-虚拟机栈) + * [本地方法栈](#本地方法栈) + * [堆](#堆) + * [方法区](#方法区) + * [运行时常量池](#运行时常量池) + * [直接内存](#直接内存) + * [二、垃圾收集](#二垃圾收集) + * [判断一个对象是否可被回收](#判断一个对象是否可被回收) + * [引用类型](#引用类型) + * [垃圾收集算法](#垃圾收集算法) + * [垃圾收集器](#垃圾收集器) + * [三、内存分配与回收策略](#三内存分配与回收策略) + * [Minor GC 和 Full GC](#minor-gc-和-full-gc) + * [内存分配策略](#内存分配策略) + * [Full GC 的触发条件](#full-gc-的触发条件) + * [四、类加载机制](#四类加载机制) + * [类的生命周期](#类的生命周期) + * [类加载过程](#类加载过程) + * [类初始化时机](#类初始化时机) + * [类与类加载器](#类与类加载器) + * [类加载器分类](#类加载器分类) + * [双亲委派模型](#双亲委派模型) + * [自定义类加载器实现](#自定义类加载器实现) + * [参考资料](#参考资料) + + + +本文大部分内容参考 **周志明《深入理解 Java 虚拟机》** ,想要深入学习的话请看原书。 + +## 一、运行时数据区域 + +

+ +### 程序计数器 + +记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。 + +### Java 虚拟机栈 + +每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 + +

+ +可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小,在 JDK 1.4 中默认为 256K,而在 JDK 1.5+ 默认为 1M: + +```java +java -Xss2M HackTheJava +``` + +该区域可能抛出以下异常: + +- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常; +- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。 + +### 本地方法栈 + +本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。 + +本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。 + +

+ +### 堆 + +所有对象都在这里分配内存,是垃圾收集的主要区域("GC 堆")。 + +现代的垃圾收集器基本都是采用分代收集算法,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块: + +- 新生代(Young Generation) +- 老年代(Old Generation) + +堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。 + +可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。 + +```java +java -Xms1M -Xmx2M HackTheJava +``` + +### 方法区 + +用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 + +和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。 + +对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。 + +HotSpot 虚拟机把它当成永久代来进行垃圾回收。但很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。 + +方法区是一个 JVM 规范,永久代与元空间都是其一种实现方式。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中。 + +### 运行时常量池 + +运行时常量池是方法区的一部分。 + +Class 文件中的常量池(编译器生成的字面量和符号引用)会在类加载后被放入这个区域。 + +除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。 + +### 直接内存 + +在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。 + +## 二、垃圾收集 + +垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。 + +### 判断一个对象是否可被回收 + +#### 1. 引用计数算法 + +为对象添加一个引用计数器,当对象增加一个引用时计数器加 1,引用失效时计数器减 1。引用计数为 0 的对象可被回收。 + +在两个对象出现循环引用的情况下,此时引用计数器永远不为 0,导致无法对它们进行回收。正是因为循环引用的存在,因此 Java 虚拟机不使用引用计数算法。 + +```java +public class Test { + + public Object instance = null; + + public static void main(String[] args) { + Test a = new Test(); + Test b = new Test(); + a.instance = b; + b.instance = a; + a = null; + b = null; + doSomething(); + } +} +``` + +在上述代码中,a 与 b 引用的对象实例互相持有了对象的引用,因此当我们把对 a 对象与 b 对象的引用去除之后,由于两个对象还存在互相之间的引用,导致两个 Test 对象无法被回收。 + +#### 2. 可达性分析算法 + +以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。 + +Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容: + +- 虚拟机栈中局部变量表中引用的对象 +- 本地方法栈中 JNI 中引用的对象 +- 方法区中类静态属性引用的对象 +- 方法区中的常量引用的对象 + +

+ + +#### 3. 方法区的回收 + +因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。 + +主要是对常量池的回收和对类的卸载。 + +为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。 + +类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载: + +- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。 +- 加载该类的 ClassLoader 已经被回收。 +- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。 + +#### 4. finalize() + +类似 C++ 的析构函数,用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。 + +当一个对象可被回收时,如果需要执行该对象的 finalize() 方法,那么就有可能在该方法中让对象重新被引用,从而实现自救。自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。 + +### 引用类型 + +无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。 + +Java 提供了四种强度不同的引用类型。 + +#### 1. 强引用 + +被强引用关联的对象不会被回收。 + +使用 new 一个新对象的方式来创建强引用。 + +```java +Object obj = new Object(); +``` + +#### 2. 软引用 + +被软引用关联的对象只有在内存不够的情况下才会被回收。 + +使用 SoftReference 类来创建软引用。 + +```java +Object obj = new Object(); +SoftReference sf = new SoftReference(obj); +obj = null; // 使对象只被软引用关联 +``` + +#### 3. 弱引用 + +被弱引用关联的对象一定会被回收,也就是说它只能存活到下一次垃圾回收发生之前。 + +使用 WeakReference 类来创建弱引用。 + +```java +Object obj = new Object(); +WeakReference wf = new WeakReference(obj); +obj = null; +``` + +#### 4. 虚引用 + +又称为幽灵引用或者幻影引用,一个对象是否有虚引用的存在,不会对其生存时间造成影响,也无法通过虚引用得到一个对象。 + +为一个对象设置虚引用的唯一目的是能在这个对象被回收时收到一个系统通知。 + +使用 PhantomReference 来创建虚引用。 + +```java +Object obj = new Object(); +PhantomReference pf = new PhantomReference(obj, null); +obj = null; +``` + +### 垃圾收集算法 + +#### 1. 标记 - 清除 + +

+ +在标记阶段,程序会检查每个对象是否为活动对象,如果是活动对象,则程序会在对象头部打上标记。 + +在清除阶段,会进行对象回收并取消标志位,另外,还会判断回收后的分块与前一个空闲分块是否连续,若连续,会合并这两个分块。回收对象就是把对象作为分块,连接到被称为 “空闲链表” 的单向链表,之后进行分配时只需要遍历这个空闲链表,就可以找到分块。 + +在分配时,程序会搜索空闲链表寻找空间大于等于新对象大小 size 的块 block。如果它找到的块等于 size,会直接返回这个分块;如果找到的块大于 size,会将块分割成大小为 size 与 (block - size) 的两部分,返回大小为 size 的分块,并把大小为 (block - size) 的块返回给空闲链表。 + +不足: + +- 标记和清除过程效率都不高; +- 会产生大量不连续的内存碎片,导致无法给大对象分配内存。 + +#### 2. 标记 - 整理 + +

+ +让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 + +优点: + +- 不会产生内存碎片 + +不足: + +- 需要移动大量对象,处理效率比较低。 + +#### 3. 复制 + +

+ +将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 + +主要不足是只使用了内存的一半。 + +现在的商业虚拟机都采用这种收集算法回收新生代,但是并不是划分为大小相等的两块,而是一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。在回收时,将 Eden 和 Survivor 中还存活着的对象全部复制到另一块 Survivor 上,最后清理 Eden 和使用过的那一块 Survivor。 + +HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内存的利用率达到 90%。如果每次回收有多于 10% 的对象存活,那么一块 Survivor 就不够用了,此时需要依赖于老年代进行空间分配担保,也就是借用老年代的空间存储放不下的对象。 + +#### 4. 分代收集 + +现在的商业虚拟机采用分代收集算法,它根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。 + +一般将堆分为新生代和老年代。 + +- 新生代使用:复制算法 +- 老年代使用:标记 - 清除 或者 标记 - 整理 算法 + +### 垃圾收集器 + +

+ +以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 + +- 单线程与多线程:单线程指的是垃圾收集器只使用一个线程,而多线程使用多个线程; +- 串行与并行:串行指的是垃圾收集器与用户程序交替执行,这意味着在执行垃圾收集的时候需要停顿用户程序;并行指的是垃圾收集器和用户程序同时执行。除了 CMS 和 G1 之外,其它垃圾收集器都是以串行的方式执行。 + +#### 1. Serial 收集器 + +

+ +Serial 翻译为串行,也就是说它以串行的方式执行。 + +它是单线程的收集器,只会使用一个线程进行垃圾收集工作。 + +它的优点是简单高效,在单个 CPU 环境下,由于没有线程交互的开销,因此拥有最高的单线程收集效率。 + +它是 Client 场景下的默认新生代收集器,因为在该场景下内存一般来说不会很大。它收集一两百兆垃圾的停顿时间可以控制在一百多毫秒以内,只要不是太频繁,这点停顿时间是可以接受的。 + +#### 2. ParNew 收集器 + +

+ +它是 Serial 收集器的多线程版本。 + +它是 Server 场景下默认的新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合使用。 + +#### 3. Parallel Scavenge 收集器 + +与 ParNew 一样是多线程收集器。 + +其它收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,因此它被称为“吞吐量优先”收集器。这里的吞吐量指 CPU 用于运行用户程序的时间占总时间的比值。 + +停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务,适合在后台运算而不需要太多交互的任务。 + +缩短停顿时间是以牺牲吞吐量和新生代空间来换取的:新生代空间变小,垃圾回收变得频繁,导致吞吐量下降。 + +可以通过一个开关参数打开 GC 自适应的调节策略(GC Ergonomics),就不需要手工指定新生代的大小(-Xmn)、Eden 和 Survivor 区的比例、晋升老年代对象年龄等细节参数了。虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量。 + +#### 4. Serial Old 收集器 + +

+ +是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途: + +- 在 JDK 1.5 以及之前版本(Parallel Old 诞生以前)中与 Parallel Scavenge 收集器搭配使用。 +- 作为 CMS 收集器的后备预案,在并发收集发生 Concurrent Mode Failure 时使用。 + +#### 5. Parallel Old 收集器 + +

+ +是 Parallel Scavenge 收集器的老年代版本。 + +在注重吞吐量以及 CPU 资源敏感的场合,都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。 + +#### 6. CMS 收集器 + +

+ +CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 + +分为以下四个流程: + +- 初始标记:仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。 +- 并发标记:进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。 +- 重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。 +- 并发清除:不需要停顿。 + +在整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户线程一起工作,不需要进行停顿。 + +具有以下缺点: + +- 吞吐量低:低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高。 +- 无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。 +- 标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。 + +#### 7. G1 收集器 + +G1(Garbage-First),它是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。HotSpot 开发团队赋予它的使命是未来可以替换掉 CMS 收集器。 + +堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。 + +

+ +G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。 + +

+ +通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。 + +每个 Region 都有一个 Remembered Set,用来记录该 Region 对象的引用对象所在的 Region。通过使用 Remembered Set,在做可达性分析的时候就可以避免全堆扫描。 + +

+ +如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤: + +- 初始标记 +- 并发标记 +- 最终标记:为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。 +- 筛选回收:首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。 + +具备如下特点: + +- 空间整合:整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。 +- 可预测的停顿:能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。 + +## 三、内存分配与回收策略 + +### Minor GC 和 Full GC + +- Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。 + +- Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。 + +### 内存分配策略 + +#### 1. 对象优先在 Eden 分配 + +大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。 + +#### 2. 大对象直接进入老年代 + +大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。 + +经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。 + +-XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。 + +#### 3. 长期存活的对象进入老年代 + +为对象定义年龄计数器,对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。 + +-XX:MaxTenuringThreshold 用来定义年龄的阈值。 + +#### 4. 动态对象年龄判定 + +虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。 + +#### 5. 空间分配担保 + +在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。 + +如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。 + +### Full GC 的触发条件 + +对于 Minor GC,其触发条件非常简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 则相对复杂,有以下条件: + +#### 1. 调用 System.gc() + +只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。 + +#### 2. 老年代空间不足 + +老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。 + +为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。 + +#### 3. 空间分配担保失败 + +使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。具体内容请参考上面的第 5 小节。 + +#### 4. JDK 1.7 及以前的永久代空间不足 + +在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据。 + +当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么虚拟机会抛出 java.lang.OutOfMemoryError。 + +为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。 + +#### 5. Concurrent Mode Failure + +执行 CMS GC 的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是 GC 过程中浮动垃圾过多导致暂时性的空间不足),便会报 Concurrent Mode Failure 错误,并触发 Full GC。 + +## 四、类加载机制 + +类是在运行期间第一次使用时动态加载的,而不是一次性加载所有类。因为如果一次性加载,那么会占用很多的内存。 + +### 类的生命周期 + +

+ +包括以下 7 个阶段: + +- **加载(Loading)** +- **验证(Verification)** +- **准备(Preparation)** +- **解析(Resolution)** +- **初始化(Initialization)** +- 使用(Using) +- 卸载(Unloading) + +### 类加载过程 + +包含了加载、验证、准备、解析和初始化这 5 个阶段。 + +#### 1. 加载 + +加载是类加载的一个阶段,注意不要混淆。 + +加载过程完成以下三件事: + +- 通过类的完全限定名称获取定义该类的二进制字节流。 +- 将该字节流表示的静态存储结构转换为方法区的运行时存储结构。 +- 在内存中生成一个代表该类的 Class 对象,作为方法区中该类各种数据的访问入口。 + + +其中二进制字节流可以从以下方式中获取: + +- 从 ZIP 包读取,成为 JAR、EAR、WAR 格式的基础。 +- 从网络中获取,最典型的应用是 Applet。 +- 运行时计算生成,例如动态代理技术,在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理类的二进制字节流。 +- 由其他文件生成,例如由 JSP 文件生成对应的 Class 类。 + +#### 2. 验证 + +确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。 + +#### 3. 准备 + +类变量是被 static 修饰的变量,准备阶段为类变量分配内存并设置初始值,使用的是方法区的内存。 + +实例变量不会在这阶段分配内存,它会在对象实例化时随着对象一起被分配在堆中。应该注意到,实例化不是类加载的一个过程,类加载发生在所有实例化操作之前,并且类加载只进行一次,实例化可以进行多次。 + +初始值一般为 0 值,例如下面的类变量 value 被初始化为 0 而不是 123。 + +```java +public static int value = 123; +``` + +如果类变量是常量,那么它将初始化为表达式所定义的值而不是 0。例如下面的常量 value 被初始化为 123 而不是 0。 + +```java +public static final int value = 123; +``` + +#### 4. 解析 + +将常量池的符号引用替换为直接引用的过程。 + +其中解析过程在某些情况下可以在初始化阶段之后再开始,这是为了支持 Java 的动态绑定。 + +#### 5. 初始化 + +
+初始化阶段才真正开始执行类中定义的 Java 程序代码。初始化阶段是虚拟机执行类构造器 <clinit\>() 方法的过程。在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化类变量和其它资源。 + +<clinit\>() 是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序由语句在源文件中出现的顺序决定。特别注意的是,静态语句块只能访问到定义在它之前的类变量,定义在它之后的类变量只能赋值,不能访问。例如以下代码: + +```java +public class Test { + static { + i = 0; // 给变量赋值可以正常编译通过 + System.out.print(i); // 这句编译器会提示“非法向前引用” + } + static int i = 1; +} +``` + +由于父类的 <clinit\>() 方法先执行,也就意味着父类中定义的静态语句块的执行要优先于子类。例如以下代码: + +```java +static class Parent { + public static int A = 1; + static { + A = 2; + } +} + +static class Sub extends Parent { + public static int B = A; +} + +public static void main(String[] args) { + System.out.println(Sub.B); // 2 +} +``` + +接口中不可以使用静态语句块,但仍然有类变量初始化的赋值操作,因此接口与类一样都会生成 <clinit\>() 方法。但接口与类不同的是,执行接口的 <clinit\>() 方法不需要先执行父接口的 <clinit\>() 方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的 <clinit\>() 方法。 + +虚拟机会保证一个类的 <clinit\>() 方法在多线程环境下被正确的加锁和同步,如果多个线程同时初始化一个类,只会有一个线程执行这个类的 <clinit\>() 方法,其它线程都会阻塞等待,直到活动线程执行 <clinit\>() 方法完毕。如果在一个类的 <clinit\>() 方法中有耗时的操作,就可能造成多个线程阻塞,在实际过程中此种阻塞很隐蔽。 + +### 类初始化时机 + +#### 1. 主动引用 + +虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随之发生): + +- 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。 + +- 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。 + +- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。 + +- 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main() 方法的那个类),虚拟机会先初始化这个主类; + +- 当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic, REF_putStatic, REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化; + +#### 2. 被动引用 + +以上 5 种场景中的行为称为对一个类进行主动引用。除此之外,所有引用类的方式都不会触发初始化,称为被动引用。被动引用的常见例子包括: + +- 通过子类引用父类的静态字段,不会导致子类初始化。 + +```java +System.out.println(SubClass.value); // value 字段在 SuperClass 中定义 +``` + +- 通过数组定义来引用类,不会触发此类的初始化。该过程会对数组类进行初始化,数组类是一个由虚拟机自动生成的、直接继承自 Object 的子类,其中包含了数组的属性和方法。 + +```java +SuperClass[] sca = new SuperClass[10]; +``` + +- 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。 + +```java +System.out.println(ConstClass.HELLOWORLD); +``` + +### 类与类加载器 + +两个类相等,需要类本身相等,并且使用同一个类加载器进行加载。这是因为每一个类加载器都拥有一个独立的类名称空间。 + +这里的相等,包括类的 Class 对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果为 true,也包括使用 instanceof 关键字做对象所属关系判定结果为 true。 + +### 类加载器分类 + +从 Java 虚拟机的角度来讲,只存在以下两种不同的类加载器: + +- 启动类加载器(Bootstrap ClassLoader),使用 C++ 实现,是虚拟机自身的一部分; + +- 所有其它类的加载器,使用 Java 实现,独立于虚拟机,继承自抽象类 java.lang.ClassLoader。 + +从 Java 开发人员的角度看,类加载器可以划分得更细致一些: + +- 启动类加载器(Bootstrap ClassLoader)此类加载器负责将存放在 <JRE_HOME\>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。 + +- 扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME\>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。 + +- 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。 + +### 双亲委派模型 + +应用程序是由三种类加载器互相配合从而实现类加载,除此之外还可以加入自己定义的类加载器。 + +下图展示了类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。 + +

+ +#### 1. 工作过程 + +一个类加载器首先将类加载请求转发到父类加载器,只有当父类加载器无法完成时才尝试自己加载。 + +#### 2. 好处 + +使得 Java 类随着它的类加载器一起具有一种带有优先级的层次关系,从而使得基础类得到统一。 + +例如 java.lang.Object 存放在 rt.jar 中,如果编写另外一个 java.lang.Object 并放到 ClassPath 中,程序可以编译通过。由于双亲委派模型的存在,所以在 rt.jar 中的 Object 比在 ClassPath 中的 Object 优先级更高,这是因为 rt.jar 中的 Object 使用的是启动类加载器,而 ClassPath 中的 Object 使用的是应用程序类加载器。rt.jar 中的 Object 优先级更高,那么程序中所有的 Object 都是这个 Object。 + +#### 3. 实现 + +以下是抽象类 java.lang.ClassLoader 的代码片段,其中的 loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。 + +```java +public abstract class ClassLoader { + // The parent class loader for delegation + private final ClassLoader parent; + + public Class loadClass(String name) throws ClassNotFoundException { + return loadClass(name, false); + } + + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + synchronized (getClassLoadingLock(name)) { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) { + try { + if (parent != null) { + c = parent.loadClass(name, false); + } else { + c = findBootstrapClassOrNull(name); + } + } catch (ClassNotFoundException e) { + // ClassNotFoundException thrown if class not found + // from the non-null parent class loader + } + + if (c == null) { + // If still not found, then invoke findClass in order + // to find the class. + c = findClass(name); + } + } + if (resolve) { + resolveClass(c); + } + return c; + } + } + + protected Class findClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(name); + } +} +``` + +### 自定义类加载器实现 + +以下代码中的 FileSystemClassLoader 是自定义类加载器,继承自 java.lang.ClassLoader,用于加载文件系统上的类。它首先根据类的全名在文件系统上查找类的字节代码文件(.class 文件),然后读取该文件内容,最后通过 defineClass() 方法来把这些字节代码转换成 java.lang.Class 类的实例。 + +java.lang.ClassLoader 的 loadClass() 实现了双亲委派模型的逻辑,自定义类加载器一般不去重写它,但是需要重写 findClass() 方法。 + +```java +public class FileSystemClassLoader extends ClassLoader { + + private String rootDir; + + public FileSystemClassLoader(String rootDir) { + this.rootDir = rootDir; + } + + protected Class findClass(String name) throws ClassNotFoundException { + byte[] classData = getClassData(name); + if (classData == null) { + throw new ClassNotFoundException(); + } else { + return defineClass(name, classData, 0, classData.length); + } + } + + private byte[] getClassData(String className) { + String path = classNameToPath(className); + try { + InputStream ins = new FileInputStream(path); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int bufferSize = 4096; + byte[] buffer = new byte[bufferSize]; + int bytesNumRead; + while ((bytesNumRead = ins.read(buffer)) != -1) { + baos.write(buffer, 0, bytesNumRead); + } + return baos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + private String classNameToPath(String className) { + return rootDir + File.separatorChar + + className.replace('.', File.separatorChar) + ".class"; + } +} +``` + +## 参考资料 + +- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社, 2011. +- [Chapter 2. The Structure of the Java Virtual Machine](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4) +- [Jvm memory](https://www.slideshare.net/benewu/jvm-memory) +[Getting Started with the G1 Garbage Collector](http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html) +- [JNI Part1: Java Native Interface Introduction and “Hello World” application](http://electrofriends.com/articles/jni/jni-part1-java-native-interface/) +- [Memory Architecture Of JVM(Runtime Data Areas)](https://hackthejava.wordpress.com/2015/01/09/memory-architecture-by-jvmruntime-data-areas/) +- [JVM Run-Time Data Areas](https://www.programcreek.com/2013/04/jvm-run-time-data-areas/) +- [Android on x86: Java Native Interface and the Android Native Development Kit](http://www.drdobbs.com/architecture-and-design/android-on-x86-java-native-interface-and/240166271) +- [深入理解 JVM(2)——GC 算法与内存分配策略](https://crowhawk.github.io/2017/08/10/jvm_2/) +- [深入理解 JVM(3)——7 种垃圾收集器](https://crowhawk.github.io/2017/08/15/jvm_3/) +- [JVM Internals](http://blog.jamesdbloom.com/JVMInternals.html) +- [深入探讨 Java 类加载器](https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html#code6) +- [Guide to WeakHashMap in Java](http://www.baeldung.com/java-weakhashmap) +- [Tomcat example source code file (ConcurrentCache.java)](https://alvinalexander.com/java/jwarehouse/apache-tomcat-6.0.16/java/org/apache/el/util/ConcurrentCache.java.shtml) diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" "b/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" new file mode 100644 index 0000000000..01ac357fb8 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" @@ -0,0 +1,316 @@ +# Leetcode 题解 - 二分查找 + +* [Leetcode 题解 - 二分查找](#leetcode-题解---二分查找) + * [1. 求开方](#1-求开方) + * [2. 大于给定元素的最小元素](#2-大于给定元素的最小元素) + * [3. 有序数组的 Single Element](#3-有序数组的-single-element) + * [4. 第一个错误的版本](#4-第一个错误的版本) + * [5. 旋转数组的最小数字](#5-旋转数组的最小数字) + * [6. 查找区间](#6-查找区间) + + + +**正常实现** + +```text +Input : [1,2,3,4,5] +key : 3 +return the index : 2 +``` + +```java +public int binarySearch(int[] nums, int key) { + int l = 0, h = nums.length - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (nums[m] == key) { + return m; + } else if (nums[m] > key) { + h = m - 1; + } else { + l = m + 1; + } + } + return -1; +} +``` + +**时间复杂度** + +二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。 + +**m 计算** + +有两种计算中值 m 的方式: + +- m = (l + h) / 2 +- m = l + (h - l) / 2 + +l + h 可能出现加法溢出,也就是说加法的结果大于整型能够表示的范围。但是 l 和 h 都为正数,因此 h - l 不会出现加法溢出问题。所以,最好使用第二种计算法方法。 + +**未成功查找的返回值** + +循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值: + +- -1:以一个错误码表示没有查找到 key +- l:将 key 插入到 nums 中的正确位置 + +**变种** + +二分查找可以有很多变种,实现变种要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下: + +```java +public int binarySearch(int[] nums, int key) { + int l = 0, h = nums.length; + while (l < h) { + int m = l + (h - l) / 2; + if (nums[m] >= key) { + h = m; + } else { + l = m + 1; + } + } + return l; +} +``` + +该实现和正常实现有以下不同: + +- h 的赋值表达式为 h = m +- 循环条件为 l \< h +- 最后返回 l 而不是 -1 + +在 nums[m] \>= key 的情况下,可以推导出最左 key 位于 [l, m] 区间中,这是一个闭区间。h 的赋值表达式为 h = m,因为 m 位置也可能是解。 + +在 h 的赋值表达式为 h = m 的情况下,如果循环条件为 l \<= h,那么会出现循环无法退出的情况,因此循环条件只能是 l \< h。以下演示了循环条件为 l \<= h 时循环无法退出的情况: + +```text +nums = {0, 1, 2}, key = 1 +l m h +0 1 2 nums[m] >= key +0 0 1 nums[m] < key +1 1 1 nums[m] >= key +1 1 1 nums[m] >= key +... +``` + +当循环体退出时,不表示没有查找到 key,因此最后返回的结果不应该为 -1。为了验证有没有查找到,需要在调用端判断一下返回位置上的值和 key 是否相等。 + +## 1. 求开方 + +69\. Sqrt(x) (Easy) + +[Leetcode](https://leetcode.com/problems/sqrtx/description/) / [力扣](https://leetcode-cn.com/problems/sqrtx/description/) + +```html +Input: 4 +Output: 2 + +Input: 8 +Output: 2 +Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated. +``` + +一个数 x 的开方 sqrt 一定在 0 \~ x 之间,并且满足 sqrt == x / sqrt。可以利用二分查找在 0 \~ x 之间查找 sqrt。 + +对于 x = 8,它的开方是 2.82842...,最后应该返回 2 而不是 3。在循环条件为 l \<= h 并且循环退出时,h 总是比 l 小 1,也就是说 h = 2,l = 3,因此最后的返回值应该为 h 而不是 l。 + +```java +public int mySqrt(int x) { + if (x <= 1) { + return x; + } + int l = 1, h = x; + while (l <= h) { + int mid = l + (h - l) / 2; + int sqrt = x / mid; + if (sqrt == mid) { + return mid; + } else if (mid > sqrt) { + h = mid - 1; + } else { + l = mid + 1; + } + } + return h; +} +``` + +## 2. 大于给定元素的最小元素 + +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: +letters = ["c", "f", "j"] +target = "d" +Output: "f" + +Input: +letters = ["c", "f", "j"] +target = "k" +Output: "c" +``` + +题目描述:给定一个有序的字符数组 letters 和一个字符 target,要求找出 letters 中大于 target 的最小字符,如果找不到就返回第 1 个字符。 + +```java +public char nextGreatestLetter(char[] letters, char target) { + int n = letters.length; + int l = 0, h = n - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (letters[m] <= target) { + l = m + 1; + } else { + h = m - 1; + } + } + return l < n ? letters[l] : letters[0]; +} +``` + +## 3. 有序数组的 Single Element + +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] +Output: 2 +``` + +题目描述:一个有序数组只有一个数不出现两次,找出这个数。 + +要求以 O(logN) 时间复杂度进行求解,因此不能遍历数组并进行异或操作来求解,这么做的时间复杂度为 O(N)。 + +令 index 为 Single Element 在数组中的位置。在 index 之后,数组中原来存在的成对状态被改变。如果 m 为偶数,并且 m + 1 \< index,那么 nums[m] == nums[m + 1];m + 1 \>= index,那么 nums[m] != nums[m + 1]。 + +从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 index 所在的数组位置为 [m + 2, h],此时令 l = m + 2;如果 nums[m] != nums[m + 1],那么 index 所在的数组位置为 [l, m],此时令 h = m。 + +因为 h 的赋值表达式为 h = m,那么循环条件也就只能使用 l \< h 这种形式。 + +```java +public int singleNonDuplicate(int[] nums) { + int l = 0, h = nums.length - 1; + while (l < h) { + int m = l + (h - l) / 2; + if (m % 2 == 1) { + m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数 + } + if (nums[m] == nums[m + 1]) { + l = m + 2; + } else { + h = m; + } + } + return nums[l]; +} +``` + +## 4. 第一个错误的版本 + +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) 知道某个版本是否错误,要求找到第一个错误的版本。 + +如果第 m 个版本出错,则表示第一个错误的版本在 [l, m] 之间,令 h = m;否则第一个错误的版本在 [m + 1, h] 之间,令 l = m + 1。 + +因为 h 的赋值表达式为 h = m,因此循环条件为 l \< h。 + +```java +public int firstBadVersion(int n) { + int l = 1, h = n; + while (l < h) { + int mid = l + (h - l) / 2; + if (isBadVersion(mid)) { + h = mid; + } else { + l = mid + 1; + } + } + return l; +} +``` + +## 5. 旋转数组的最小数字 + +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], +Output: 1 +``` + +```java +public int findMin(int[] nums) { + 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]; +} +``` + +## 6. 查找区间 + +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 +Output: [3,4] + +Input: nums = [5,7,7,8,8,10], target = 6 +Output: [-1,-1] +``` + +题目描述:给定一个有序数组 nums 和一个目标 target,要求找到 target 在 nums 中的第一个位置和最后一个位置。 + +可以用二分查找找出第一个位置和最后一个位置,但是寻找的方法有所不同,需要实现两个二分查找。我们将寻找 target 最后一个位置,转换成寻找 target+1 第一个位置,再往前移动一个位置。这样我们只需要实现一个二分查找代码即可。 + +```java +public int[] searchRange(int[] nums, int target) { + int first = findFirst(nums, target); + int last = findFirst(nums, target + 1) - 1; + if (first == nums.length || nums[first] != target) { + return new int[]{-1, -1}; + } else { + return new int[]{first, Math.max(first, last)}; + } +} + +private int findFirst(int[] nums, int target) { + int l = 0, h = nums.length; // 注意 h 的初始值 + while (l < h) { + int m = l + (h - l) / 2; + if (nums[m] >= target) { + h = m; + } else { + l = m + 1; + } + } + return l; +} +``` + +在寻找第一个位置的二分查找代码中,需要注意 h 的取值为 nums.length,而不是 nums.length - 1。先看以下示例: + +``` +nums = [2,2], target = 2 +``` + +如果 h 的取值为 nums.length - 1,那么 last = findFirst(nums, target + 1) - 1 = 1 - 1 = 0。这是因为 findLeft 只会返回 [0, nums.length - 1] 范围的值,对于 findFirst([2,2], 3) ,我们希望返回 3 插入 nums 中的位置,也就是数组最后一个位置再往后一个位置,即 nums.length。所以我们需要将 h 取值为 nums.length,从而使得 findFirst返回的区间更大,能够覆盖 target 大于 nums 最后一个元素的情况。 diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" "b/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" new file mode 100644 index 0000000000..a097b75dcf --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \344\275\215\350\277\220\347\256\227.md" @@ -0,0 +1,503 @@ +# Leetcode 题解 - 位运算 + +* [Leetcode 题解 - 位运算](#leetcode-题解---位运算) + * [0. 原理](#0-原理) + * [1. 统计两个数的二进制表示有多少位不同](#1-统计两个数的二进制表示有多少位不同) + * [2. 数组中唯一一个不重复的元素](#2-数组中唯一一个不重复的元素) + * [3. 找出数组中缺失的那个数](#3-找出数组中缺失的那个数) + * [4. 数组中不重复的两个元素](#4-数组中不重复的两个元素) + * [5. 翻转一个数的比特位](#5-翻转一个数的比特位) + * [6. 不用额外变量交换两个整数](#6-不用额外变量交换两个整数) + * [7. 判断一个数是不是 2 的 n 次方](#7-判断一个数是不是-2-的-n-次方) + * [8. 判断一个数是不是 4 的 n 次方](#8--判断一个数是不是-4-的-n-次方) + * [9. 判断一个数的位级表示是否不会出现连续的 0 和 1](#9-判断一个数的位级表示是否不会出现连续的-0-和-1) + * [10. 求一个数的补码](#10-求一个数的补码) + * [11. 实现整数的加法](#11-实现整数的加法) + * [12. 字符串数组最大乘积](#12-字符串数组最大乘积) + * [13. 统计从 0 \~ n 每个数的二进制表示中 1 的个数](#13-统计从-0-\~-n-每个数的二进制表示中-1-的个数) + + + +## 0. 原理 + +**基本原理** + +0s 表示一串 0,1s 表示一串 1。 + +``` +x ^ 0s = x x & 0s = 0 x | 0s = x +x ^ 1s = ~x x & 1s = x x | 1s = 1s +x ^ x = 0 x & x = x x | x = x +``` + +利用 x ^ 1s = \~x 的特点,可以将一个数的位级表示翻转;利用 x ^ x = 0 的特点,可以将三个数中重复的两个数去除,只留下另一个数。 + +``` +1^1^2 = 2 +``` + +利用 x & 0s = 0 和 x & 1s = x 的特点,可以实现掩码操作。一个数 num 与 mask:00111100 进行位与操作,只保留 num 中与 mask 的 1 部分相对应的位。 + +``` +01011011 & +00111100 +-------- +00011000 +``` + +利用 x | 0s = x 和 x | 1s = 1s 的特点,可以实现设值操作。一个数 num 与 mask:00111100 进行位或操作,将 num 中与 mask 的 1 部分相对应的位都设置为 1。 + +``` +01011011 | +00111100 +-------- +01111111 +``` + +**位与运算技巧** + +n&(n-1) 去除 n 的位级表示中最低的那一位 1。例如对于二进制表示 01011011,减去 1 得到 01011010,这两个数相与得到 01011010。 + +``` +01011011 & +01011010 +-------- +01011010 +``` + +n&(-n) 得到 n 的位级表示中最低的那一位 1。-n 得到 n 的反码加 1,也就是 -n=\~n+1。例如对于二进制表示 10110100,-n 得到 01001100,相与得到 00000100。 + +``` +10110100 & +01001100 +-------- +00000100 +``` + +n-(n&(-n)) 则可以去除 n 的位级表示中最低的那一位 1,和 n&(n-1) 效果一样。 + +**移位运算** + +\\>\\> n 为算术右移,相当于除以 2n,例如 -7 \\>\\> 2 = -2。 + +``` +11111111111111111111111111111001 >> 2 +-------- +11111111111111111111111111111110 +``` + +\\>\\>\\> n 为无符号右移,左边会补上 0。例如 -7 \\>\\>\\> 2 = 1073741822。 + +``` +11111111111111111111111111111001 >>> 2 +-------- +00111111111111111111111111111111 +``` + +\<\< n 为算术左移,相当于乘以 2n。-7 \<\< 2 = -28。 + +``` +11111111111111111111111111111001 << 2 +-------- +11111111111111111111111111100100 +``` + +**mask 计算** + +要获取 111111111,将 0 取反即可,\~0。 + +要得到只有第 i 位为 1 的 mask,将 1 向左移动 i-1 位即可,1\<\<(i-1) 。例如 1\<\<4 得到只有第 5 位为 1 的 mask :00010000。 + +要得到 1 到 i 位为 1 的 mask,(1\<\> 1; + } + return cnt; +} +``` + +使用 z&(z-1) 去除 z 位级表示最低的那一位。 + +```java +public int hammingDistance(int x, int y) { + int z = x ^ y; + int cnt = 0; + while (z != 0) { + z &= (z - 1); + cnt++; + } + return cnt; +} +``` + +可以使用 Integer.bitcount() 来统计 1 个的个数。 + +```java +public int hammingDistance(int x, int y) { + return Integer.bitCount(x ^ y); +} +``` + +## 2. 数组中唯一一个不重复的元素 + +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] +Output: 4 +``` + +两个相同的数异或的结果为 0,对所有数进行异或操作,最后的结果就是单独出现的那个数。 + +```java +public int singleNumber(int[] nums) { + int ret = 0; + for (int n : nums) ret = ret ^ n; + return ret; +} +``` + +## 3. 找出数组中缺失的那个数 + +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] +Output: 2 +``` + +题目描述:数组元素在 0-n 之间,但是有一个数是缺失的,要求找到这个缺失的数。 + +```java +public int missingNumber(int[] nums) { + int ret = 0; + for (int i = 0; i < nums.length; i++) { + ret = ret ^ i ^ nums[i]; + } + return ret ^ nums.length; +} +``` + +## 4. 数组中不重复的两个元素 + +260\. Single Number III (Medium) + +[Leetcode](https://leetcode.com/problems/single-number-iii/description/) / [力扣](https://leetcode-cn.com/problems/single-number-iii/description/) + +两个不相等的元素在位级表示上必定会有一位存在不同。 + +将数组的所有元素异或得到的结果为不存在重复的两个元素异或的结果。 + +diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。 + +```java +public int[] singleNumber(int[] nums) { + int diff = 0; + for (int num : nums) diff ^= num; + diff &= -diff; // 得到最右一位 + int[] ret = new int[2]; + for (int num : nums) { + if ((num & diff) == 0) ret[0] ^= num; + else ret[1] ^= num; + } + return ret; +} +``` + +## 5. 翻转一个数的比特位 + +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) { + int ret = 0; + for (int i = 0; i < 32; i++) { + ret <<= 1; + ret |= (n & 1); + n >>>= 1; + } + return ret; +} +``` + +如果该函数需要被调用很多次,可以将 int 拆成 4 个 byte,然后缓存 byte 对应的比特位翻转,最后再拼接起来。 + +```java +private static Map cache = new HashMap<>(); + +public int reverseBits(int n) { + int ret = 0; + for (int i = 0; i < 4; i++) { + ret <<= 8; + ret |= reverseByte((byte) (n & 0b11111111)); + n >>= 8; + } + return ret; +} + +private int reverseByte(byte b) { + if (cache.containsKey(b)) return cache.get(b); + int ret = 0; + byte t = b; + for (int i = 0; i < 8; i++) { + ret <<= 1; + ret |= t & 1; + t >>= 1; + } + cache.put(b, ret); + return ret; +} +``` + +## 6. 不用额外变量交换两个整数 + +[程序员代码面试指南 :P317](#) + +```java +a = a ^ b; +b = a ^ b; +a = a ^ b; +``` + +## 7. 判断一个数是不是 2 的 n 次方 + +231\. Power of Two (Easy) + +[Leetcode](https://leetcode.com/problems/power-of-two/description/) / [力扣](https://leetcode-cn.com/problems/power-of-two/description/) + +二进制表示只有一个 1 存在。 + +```java +public boolean isPowerOfTwo(int n) { + return n > 0 && Integer.bitCount(n) == 1; +} +``` + +利用 1000 & 0111 == 0 这种性质,得到以下解法: + +```java +public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; +} +``` + +## 8. 判断一个数是不是 4 的 n 次方 + +342\. Power of Four (Easy) + +[Leetcode](https://leetcode.com/problems/power-of-four/) / [力扣](https://leetcode-cn.com/problems/power-of-four/) + +这种数在二进制表示中有且只有一个奇数位为 1,例如 16(10000)。 + +```java +public boolean isPowerOfFour(int num) { + return num > 0 && (num & (num - 1)) == 0 && (num & 0b01010101010101010101010101010101) != 0; +} +``` + +也可以使用正则表达式进行匹配。 + +```java +public boolean isPowerOfFour(int num) { + return Integer.toString(num, 4).matches("10*"); +} +``` + +## 9. 判断一个数的位级表示是否不会出现连续的 0 和 1 + +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 +Output: True +Explanation: +The binary representation of 10 is: 1010. + +Input: 11 +Output: False +Explanation: +The binary representation of 11 is: 1011. +``` + +对于 1010 这种位级表示的数,把它向右移动 1 位得到 101,这两个数每个位都不同,因此异或得到的结果为 1111。 + +```java +public boolean hasAlternatingBits(int n) { + int a = (n ^ (n >> 1)); + return (a & (a + 1)) == 0; +} +``` + +## 10. 求一个数的补码 + +476\. Number Complement (Easy) + +[Leetcode](https://leetcode.com/problems/number-complement/description/) / [力扣](https://leetcode-cn.com/problems/number-complement/description/) + +```html +Input: 5 +Output: 2 +Explanation: The binary representation of 5 is 101 (no leading zero bits), and its complement is 010. So you need to output 2. +``` + +题目描述:不考虑二进制表示中的首 0 部分。 + +对于 00000101,要求补码可以将它与 00000111 进行异或操作。那么问题就转换为求掩码 00000111。 + +```java +public int findComplement(int num) { + if (num == 0) return 1; + int mask = 1 << 30; + while ((num & mask) == 0) mask >>= 1; + mask = (mask << 1) - 1; + return num ^ mask; +} +``` + +可以利用 Java 的 Integer.highestOneBit() 方法来获得含有首 1 的数。 + +```java +public int findComplement(int num) { + if (num == 0) return 1; + int mask = Integer.highestOneBit(num); + mask = (mask << 1) - 1; + return num ^ mask; +} +``` + +对于 10000000 这样的数要扩展成 11111111,可以利用以下方法: + +```html +mask |= mask >> 1 11000000 +mask |= mask >> 2 11110000 +mask |= mask >> 4 11111111 +``` + +```java +public int findComplement(int num) { + int mask = num; + mask |= mask >> 1; + mask |= mask >> 2; + mask |= mask >> 4; + mask |= mask >> 8; + mask |= mask >> 16; + return (mask ^ num); +} +``` + +## 11. 实现整数的加法 + +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 就是进位。 + +递归会终止的原因是 (a & b) \<\< 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 + +```java +public int getSum(int a, int b) { + return b == 0 ? a : getSum((a ^ b), (a & b) << 1); +} +``` + +## 12. 字符串数组最大乘积 + +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"] +Return 16 +The two words can be "abcw", "xtfn". +``` + +题目描述:字符串数组的字符串只含有小写字符。求解字符串数组中两个字符串长度的最大乘积,要求这两个字符串不能含有相同字符。 + +本题主要问题是判断两个字符串是否含相同字符,由于字符串只含有小写字符,总共 26 位,因此可以用一个 32 位的整数来存储每个字符是否出现过。 + +```java +public int maxProduct(String[] words) { + int n = words.length; + int[] val = new int[n]; + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + val[i] |= 1 << (c - 'a'); + } + } + int ret = 0; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if ((val[i] & val[j]) == 0) { + ret = Math.max(ret, words[i].length() * words[j].length()); + } + } + } + return ret; +} +``` + +## 13. 统计从 0 \~ n 每个数的二进制表示中 1 的个数 + +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; + +```java +public int[] countBits(int num) { + int[] ret = new int[num + 1]; + for(int i = 1; i <= num; i++){ + ret[i] = ret[i&(i-1)] + 1; + } + return ret; +} +``` + diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" new file mode 100644 index 0000000000..3fefbb4788 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\210\206\346\262\273.md" @@ -0,0 +1,112 @@ +# Leetcode 题解 - 分治 + +* [Leetcode 题解 - 分治](#leetcode-题解---分治) + * [1. 给表达式加括号](#1-给表达式加括号) + * [2. 不同的二叉搜索树](#2-不同的二叉搜索树) + + + +## 1. 给表达式加括号 + +241\. Different Ways to Add Parentheses (Medium) + +[Leetcode](https://leetcode.com/problems/different-ways-to-add-parentheses/description/) / [力扣](https://leetcode-cn.com/problems/different-ways-to-add-parentheses/description/) + +```html +Input: "2-1-1". + +((2-1)-1) = 0 +(2-(1-1)) = 2 + +Output : [0, 2] +``` + +```java +public List diffWaysToCompute(String input) { + List ways = new ArrayList<>(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '+' || c == '-' || c == '*') { + List left = diffWaysToCompute(input.substring(0, i)); + List right = diffWaysToCompute(input.substring(i + 1)); + for (int l : left) { + for (int r : right) { + switch (c) { + case '+': + ways.add(l + r); + break; + case '-': + ways.add(l - r); + break; + case '*': + ways.add(l * r); + break; + } + } + } + } + } + if (ways.size() == 0) { + ways.add(Integer.valueOf(input)); + } + return ways; +} +``` + +## 2. 不同的二叉搜索树 + +95\. Unique Binary Search Trees II (Medium) + +[Leetcode](https://leetcode.com/problems/unique-binary-search-trees-ii/description/) / [力扣](https://leetcode-cn.com/problems/unique-binary-search-trees-ii/description/) + +给定一个数字 n,要求生成所有值为 1...n 的二叉搜索树。 + +```html +Input: 3 +Output: +[ + [1,null,3,2], + [3,2,null,1], + [3,1,null,null,2], + [2,1,3], + [1,null,2,null,3] +] +Explanation: +The above output corresponds to the 5 unique BST's shown below: + + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 +``` + +```java +public List generateTrees(int n) { + if (n < 1) { + return new LinkedList(); + } + return generateSubtrees(1, n); +} + +private List generateSubtrees(int s, int e) { + List res = new LinkedList(); + if (s > e) { + res.add(null); + return res; + } + for (int i = s; i <= e; ++i) { + List leftSubtrees = generateSubtrees(s, i - 1); + List rightSubtrees = generateSubtrees(i + 1, e); + for (TreeNode left : leftSubtrees) { + for (TreeNode right : rightSubtrees) { + TreeNode root = new TreeNode(i); + root.left = left; + root.right = right; + res.add(root); + } + } + } + return res; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" new file mode 100644 index 0000000000..b50a12e694 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" @@ -0,0 +1,1313 @@ +# Leetcode 题解 - 动态规划 + +* [Leetcode 题解 - 动态规划](#leetcode-题解---动态规划) + * [斐波那契数列](#斐波那契数列) + * [1. 爬楼梯](#1-爬楼梯) + * [2. 强盗抢劫](#2-强盗抢劫) + * [3. 强盗在环形街区抢劫](#3-强盗在环形街区抢劫) + * [4. 信件错排](#4-信件错排) + * [5. 母牛生产](#5-母牛生产) + * [矩阵路径](#矩阵路径) + * [1. 矩阵的最小路径和](#1-矩阵的最小路径和) + * [2. 矩阵的总路径数](#2-矩阵的总路径数) + * [数组区间](#数组区间) + * [1. 数组区间和](#1-数组区间和) + * [2. 数组中等差递增子区间的个数](#2-数组中等差递增子区间的个数) + * [分割整数](#分割整数) + * [1. 分割整数的最大乘积](#1-分割整数的最大乘积) + * [2. 按平方数来分割整数](#2-按平方数来分割整数) + * [3. 分割整数构成字母字符串](#3-分割整数构成字母字符串) + * [最长递增子序列](#最长递增子序列) + * [1. 最长递增子序列](#1-最长递增子序列) + * [2. 一组整数对能够构成的最长链](#2-一组整数对能够构成的最长链) + * [3. 最长摆动子序列](#3-最长摆动子序列) + * [最长公共子序列](#最长公共子序列) + * [1. 最长公共子序列](#1-最长公共子序列) + * [0-1 背包](#0-1-背包) + * [1. 划分数组为和相等的两部分](#1-划分数组为和相等的两部分) + * [2. 改变一组数的正负号使得它们的和为一给定数](#2-改变一组数的正负号使得它们的和为一给定数) + * [3. 01 字符构成最多的字符串](#3-01-字符构成最多的字符串) + * [4. 找零钱的最少硬币数](#4-找零钱的最少硬币数) + * [5. 找零钱的硬币数组合](#5-找零钱的硬币数组合) + * [6. 字符串按单词列表分割](#6-字符串按单词列表分割) + * [7. 组合总和](#7-组合总和) + * [股票交易](#股票交易) + * [1. 需要冷却期的股票交易](#1-需要冷却期的股票交易) + * [2. 需要交易费用的股票交易](#2-需要交易费用的股票交易) + * [3. 只能进行两次的股票交易](#3-只能进行两次的股票交易) + * [4. 只能进行 k 次的股票交易](#4-只能进行-k-次的股票交易) + * [字符串编辑](#字符串编辑) + * [1. 删除两个字符串的字符使它们相等](#1-删除两个字符串的字符使它们相等) + * [2. 编辑距离](#2-编辑距离) + * [3. 复制粘贴字符](#3-复制粘贴字符) + + + +递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。 + +## 斐波那契数列 + +### 1. 爬楼梯 + +70\. Climbing Stairs (Easy) + +[Leetcode](https://leetcode.com/problems/climbing-stairs/description/) / [力扣](https://leetcode-cn.com/problems/climbing-stairs/description/) + +题目描述:有 N 阶楼梯,每次可以上一阶或者两阶,求有多少种上楼梯的方法。 + +定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。 + +第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。 + + + +

+ +考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。 + +```java +public int climbStairs(int n) { + if (n <= 2) { + return n; + } + int pre2 = 1, pre1 = 2; + for (int i = 2; i < n; i++) { + int cur = pre1 + pre2; + pre2 = pre1; + pre1 = cur; + } + return pre1; +} +``` + +### 2. 强盗抢劫 + +198\. House Robber (Easy) + +[Leetcode](https://leetcode.com/problems/house-robber/description/) / [力扣](https://leetcode-cn.com/problems/house-robber/description/) + +题目描述:抢劫一排住户,但是不能抢邻近的住户,求最大抢劫量。 + +定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。 + +由于不能抢劫邻近住户,如果抢劫了第 i -1 个住户,那么就不能再抢劫第 i 个住户,所以 + + + +

+ +```java +public int rob(int[] nums) { + int pre2 = 0, pre1 = 0; + for (int i = 0; i < nums.length; i++) { + int cur = Math.max(pre2 + nums[i], pre1); + pre2 = pre1; + pre1 = cur; + } + return pre1; +} +``` + +### 3. 强盗在环形街区抢劫 + +213\. House Robber II (Medium) + +[Leetcode](https://leetcode.com/problems/house-robber-ii/description/) / [力扣](https://leetcode-cn.com/problems/house-robber-ii/description/) + +```java +public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + if (n == 1) { + return nums[0]; + } + return Math.max(rob(nums, 0, n - 2), rob(nums, 1, n - 1)); +} + +private int rob(int[] nums, int first, int last) { + int pre2 = 0, pre1 = 0; + for (int i = first; i <= last; i++) { + int cur = Math.max(pre1, pre2 + nums[i]); + pre2 = pre1; + pre1 = cur; + } + return pre1; +} +``` + +### 4. 信件错排 + +题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信方式的数量。 + +定义一个数组 dp 存储错误方式数量,dp[i] 表示前 i 个信和信封的错误方式数量。假设第 i 个信装到第 j 个信封里面,而第 j 个信装到第 k 个信封里面。根据 i 和 k 是否相等,有两种情况: + +- i==k,交换 i 和 j 的信后,它们的信和信封在正确的位置,但是其余 i-2 封信有 dp[i-2] 种错误装信的方式。由于 j 有 i-1 种取值,因此共有 (i-1)\*dp[i-2] 种错误装信方式。 +- i != k,交换 i 和 j 的信后,第 i 个信和信封在正确的位置,其余 i-1 封信有 dp[i-1] 种错误装信方式。由于 j 有 i-1 种取值,因此共有 (i-1)\*dp[i-1] 种错误装信方式。 + +综上所述,错误装信数量方式数量为: + + + +

+ +### 5. 母牛生产 + +[程序员代码面试指南-P181](#) + +题目描述:假设农场中成熟的母牛每年都会生 1 头小母牛,并且永远不会死。第一年有 1 只小母牛,从第二年开始,母牛开始生小母牛。每只小母牛 3 年之后成熟又可以生小母牛。给定整数 N,求 N 年后牛的数量。 + +第 i 年成熟的牛的数量为: + + + +

+ +## 矩阵路径 + +### 1. 矩阵的最小路径和 + +64\. Minimum Path Sum (Medium) + +[Leetcode](https://leetcode.com/problems/minimum-path-sum/description/) / [力扣](https://leetcode-cn.com/problems/minimum-path-sum/description/) + +```html +[[1,3,1], + [1,5,1], + [4,2,1]] +Given the above grid map, return 7. Because the path 1→3→1→1→1 minimizes the sum. +``` + +题目描述:求从矩阵的左上角到右下角的最小路径和,每次只能向右和向下移动。 + +```java +public int minPathSum(int[][] grid) { + if (grid.length == 0 || grid[0].length == 0) { + return 0; + } + int m = grid.length, n = grid[0].length; + int[] dp = new int[n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (j == 0) { + dp[j] = dp[j]; // 只能从上侧走到该位置 + } else if (i == 0) { + dp[j] = dp[j - 1]; // 只能从左侧走到该位置 + } else { + dp[j] = Math.min(dp[j - 1], dp[j]); + } + dp[j] += grid[i][j]; + } + } + return dp[n - 1]; +} +``` + +### 2. 矩阵的总路径数 + +62\. Unique Paths (Medium) + +[Leetcode](https://leetcode.com/problems/unique-paths/description/) / [力扣](https://leetcode-cn.com/problems/unique-paths/description/) + +题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。 + +

+ +```java +public int uniquePaths(int m, int n) { + int[] dp = new int[n]; + Arrays.fill(dp, 1); + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[j] = dp[j] + dp[j - 1]; + } + } + return dp[n - 1]; +} +``` + +也可以直接用数学公式求解,这是一个组合问题。机器人总共移动的次数 S=m+n-2,向下移动的次数 D=m-1,那么问题可以看成从 S 中取出 D 个位置的组合数量,这个问题的解为 C(S, D)。 + +```java +public int uniquePaths(int m, int n) { + int S = m + n - 2; // 总共的移动次数 + int D = m - 1; // 向下的移动次数 + long ret = 1; + for (int i = 1; i <= D; i++) { + ret = ret * (S - D + i) / i; + } + return (int) ret; +} +``` + +## 数组区间 + +### 1. 数组区间和 + +303\. Range Sum Query - Immutable (Easy) + +[Leetcode](https://leetcode.com/problems/range-sum-query-immutable/description/) / [力扣](https://leetcode-cn.com/problems/range-sum-query-immutable/description/) + +```html +Given nums = [-2, 0, 3, -5, 2, -1] + +sumRange(0, 2) -> 1 +sumRange(2, 5) -> -1 +sumRange(0, 5) -> -3 +``` + +求区间 i \~ j 的和,可以转换为 sum[j + 1] - sum[i],其中 sum[i] 为 0 \~ i - 1 的和。 + +```java +class NumArray { + + private int[] sums; + + public NumArray(int[] nums) { + sums = new int[nums.length + 1]; + for (int i = 1; i <= nums.length; i++) { + sums[i] = sums[i - 1] + nums[i - 1]; + } + } + + public int sumRange(int i, int j) { + return sums[j + 1] - sums[i]; + } +} +``` + +### 2. 数组中等差递增子区间的个数 + +413\. Arithmetic Slices (Medium) + +[Leetcode](https://leetcode.com/problems/arithmetic-slices/description/) / [力扣](https://leetcode-cn.com/problems/arithmetic-slices/description/) + +```html +A = [0, 1, 2, 3, 4] + +return: 6, for 3 arithmetic slices in A: + +[0, 1, 2], +[1, 2, 3], +[0, 1, 2, 3], +[0, 1, 2, 3, 4], +[ 1, 2, 3, 4], +[2, 3, 4] +``` + +dp[i] 表示以 A[i] 为结尾的等差递增子区间的个数。 + +当 A[i] - A[i-1] == A[i-1] - A[i-2],那么 [A[i-2], A[i-1], A[i]] 构成一个等差递增子区间。而且在以 A[i-1] 为结尾的递增子区间的后面再加上一个 A[i],一样可以构成新的递增子区间。 + +```html +dp[2] = 1 + [0, 1, 2] +dp[3] = dp[2] + 1 = 2 + [0, 1, 2, 3], // [0, 1, 2] 之后加一个 3 + [1, 2, 3] // 新的递增子区间 +dp[4] = dp[3] + 1 = 3 + [0, 1, 2, 3, 4], // [0, 1, 2, 3] 之后加一个 4 + [1, 2, 3, 4], // [1, 2, 3] 之后加一个 4 + [2, 3, 4] // 新的递增子区间 +``` + +综上,在 A[i] - A[i-1] == A[i-1] - A[i-2] 时,dp[i] = dp[i-1] + 1。 + +因为递增子区间不一定以最后一个元素为结尾,可以是任意一个元素结尾,因此需要返回 dp 数组累加的结果。 + +```java +public int numberOfArithmeticSlices(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int n = A.length; + int[] dp = new int[n]; + for (int i = 2; i < n; i++) { + if (A[i] - A[i - 1] == A[i - 1] - A[i - 2]) { + dp[i] = dp[i - 1] + 1; + } + } + int total = 0; + for (int cnt : dp) { + total += cnt; + } + return total; +} +``` + +## 分割整数 + +### 1. 分割整数的最大乘积 + +343\. Integer Break (Medim) + +[Leetcode](https://leetcode.com/problems/integer-break/description/) / [力扣](https://leetcode-cn.com/problems/integer-break/description/) + +题目描述:For example, given n = 2, return 1 (2 = 1 + 1); given n = 10, return 36 (10 = 3 + 3 + 4). + +```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 - 1; j++) { + dp[i] = Math.max(dp[i], Math.max(j * dp[i - j], j * (i - j))); + } + } + return dp[n]; +} +``` + +### 2. 按平方数来分割整数 + +279\. Perfect Squares(Medium) + +[Leetcode](https://leetcode.com/problems/perfect-squares/description/) / [力扣](https://leetcode-cn.com/problems/perfect-squares/description/) + +题目描述:For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. + +```java +public int numSquares(int n) { + List squareList = generateSquareList(n); + int[] dp = new int[n + 1]; + for (int i = 1; i <= n; i++) { + int min = Integer.MAX_VALUE; + for (int square : squareList) { + if (square > i) { + break; + } + min = Math.min(min, dp[i - square] + 1); + } + dp[i] = min; + } + return dp[n]; +} + +private List generateSquareList(int n) { + List squareList = new ArrayList<>(); + int diff = 3; + int square = 1; + while (square <= n) { + squareList.add(square); + square += diff; + diff += 2; + } + return squareList; +} +``` + +### 3. 分割整数构成字母字符串 + +91\. Decode Ways (Medium) + +[Leetcode](https://leetcode.com/problems/decode-ways/description/) / [力扣](https://leetcode-cn.com/problems/decode-ways/description/) + +题目描述:Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). + +```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]; +} +``` + +## 最长递增子序列 + +已知一个序列 {S1, S2,...,Sn},取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个 **子序列** 。 + +如果在子序列中,当下标 ix > iy 时,Six > Siy,称子序列为原序列的一个 **递增子序列** 。 + +定义一个数组 dp 存储最长递增子序列的长度,dp[n] 表示以 Sn 结尾的序列的最长递增子序列长度。对于一个递增子序列 {Si1, Si2,...,Sim},如果 im < n 并且 Sim < Sn,此时 {Si1, Si2,..., Sim, Sn} 为一个递增子序列,递增子序列的长度增加 1。满足上述条件的递增子序列中,长度最长的那个递增子序列就是要找的,在长度最长的递增子序列上加上 Sn 就构成了以 Sn 为结尾的最长递增子序列。因此 dp[n] = max{ dp[i]+1 | Si < Sn && i < n} 。 + +因为在求 dp[n] 时可能无法找到一个满足条件的递增子序列,此时 {Sn} 就构成了递增子序列,需要对前面的求解方程做修改,令 dp[n] 最小为 1,即: + + + +

+ +对于一个长度为 N 的序列,最长递增子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,max{ dp[i] | 1 <= i <= N} 即为所求。 + +### 1. 最长递增子序列 + +300\. Longest Increasing Subsequence (Medium) + +[Leetcode](https://leetcode.com/problems/longest-increasing-subsequence/description/) / [力扣](https://leetcode-cn.com/problems/longest-increasing-subsequence/description/) + +```java +public int lengthOfLIS(int[] nums) { + int n = nums.length; + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + int max = 1; + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + max = Math.max(max, dp[j] + 1); + } + } + dp[i] = max; + } + return Arrays.stream(dp).max().orElse(0); +} +``` + +使用 Stream 求最大值会导致运行时间过长,可以改成以下形式: + +```java +int ret = 0; +for (int i = 0; i < n; i++) { + ret = Math.max(ret, dp[i]); +} +return ret; +``` + +以上解法的时间复杂度为 O(N2),可以使用二分查找将时间复杂度降低为 O(NlogN)。 + +定义一个 tails 数组,其中 tails[i] 存储长度为 i + 1 的最长递增子序列的最后一个元素。对于一个元素 x, + +- 如果它大于 tails 数组所有的值,那么把它添加到 tails 后面,表示最长递增子序列长度加 1; +- 如果 tails[i-1] \< x \<= tails[i],那么更新 tails[i] = x。 + +例如对于数组 [4,3,6,5],有: + +```html +tails len num +[] 0 4 +[4] 1 3 +[3] 1 6 +[3,6] 2 5 +[3,5] 2 null +``` + +可以看出 tails 数组保持有序,因此在查找 Si 位于 tails 数组的位置时就可以使用二分查找。 + +```java +public int lengthOfLIS(int[] nums) { + int n = nums.length; + int[] tails = new int[n]; + int len = 0; + for (int num : nums) { + int index = binarySearch(tails, len, num); + tails[index] = num; + if (index == len) { + len++; + } + } + return len; +} + +private int binarySearch(int[] tails, int len, int key) { + int l = 0, h = len; + while (l < h) { + int mid = l + (h - l) / 2; + if (tails[mid] == key) { + return mid; + } else if (tails[mid] > key) { + h = mid; + } else { + l = mid + 1; + } + } + return l; +} +``` + +### 2. 一组整数对能够构成的最长链 + +646\. Maximum Length of Pair Chain (Medium) + +[Leetcode](https://leetcode.com/problems/maximum-length-of-pair-chain/description/) / [力扣](https://leetcode-cn.com/problems/maximum-length-of-pair-chain/description/) + +```html +Input: [[1,2], [2,3], [3,4]] +Output: 2 +Explanation: The longest chain is [1,2] -> [3,4] +``` + +题目描述:对于 (a, b) 和 (c, d) ,如果 b \< c,则它们可以构成一条链。 + +```java +public int findLongestChain(int[][] pairs) { + if (pairs == null || pairs.length == 0) { + return 0; + } + Arrays.sort(pairs, (a, b) -> (a[0] - b[0])); + int n = pairs.length; + int[] dp = new int[n]; + Arrays.fill(dp, 1); + for (int i = 1; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + return Arrays.stream(dp).max().orElse(0); +} +``` + +### 3. 最长摆动子序列 + +376\. Wiggle Subsequence (Medium) + +[Leetcode](https://leetcode.com/problems/wiggle-subsequence/description/) / [力扣](https://leetcode-cn.com/problems/wiggle-subsequence/description/) + +```html +Input: [1,7,4,9,2,5] +Output: 6 +The entire sequence is a wiggle sequence. + +Input: [1,17,5,10,13,15,10,5,16,8] +Output: 7 +There are several subsequences that achieve this length. One is [1,17,10,13,10,16,8]. + +Input: [1,2,3,4,5,6,7,8,9] +Output: 2 +``` + +要求:使用 O(N) 时间复杂度求解。 + +```java +public int wiggleMaxLength(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int up = 1, down = 1; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > nums[i - 1]) { + up = down + 1; + } else if (nums[i] < nums[i - 1]) { + down = up + 1; + } + } + return Math.max(up, down); +} +``` + +## 最长公共子序列 + +对于两个子序列 S1 和 S2,找出它们最长的公共子序列。 + +定义一个二维数组 dp 用来存储最长公共子序列的长度,其中 dp[i][j] 表示 S1 的前 i 个字符与 S2 的前 j 个字符最长公共子序列的长度。考虑 S1i 与 S2j 值是否相等,分为两种情况: + +- 当 S1i==S2j 时,那么就能在 S1 的前 i-1 个字符与 S2 的前 j-1 个字符最长公共子序列的基础上再加上 S1i 这个值,最长公共子序列长度加 1,即 dp[i][j] = dp[i-1][j-1] + 1。 +- 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,或者 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,取它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。 + +综上,最长公共子序列的状态转移方程为: + + + +

+ +对于长度为 N 的序列 S1 和长度为 M 的序列 S2,dp[N][M] 就是序列 S1 和序列 S2 的最长公共子序列长度。 + +与最长递增子序列相比,最长公共子序列有以下不同点: + +- 针对的是两个序列,求它们的最长公共子序列。 +- 在最长递增子序列中,dp[i] 表示以 Si 为结尾的最长递增子序列长度,子序列必须包含 Si ;在最长公共子序列中,dp[i][j] 表示 S1 中前 i 个字符与 S2 中前 j 个字符的最长公共子序列长度,不一定包含 S1i 和 S2j。 +- 在求最终解时,最长公共子序列中 dp[N][M] 就是最终解,而最长递增子序列中 dp[N] 不是最终解,因为以 SN 为结尾的最长递增子序列不一定是整个序列最长递增子序列,需要遍历一遍 dp 数组找到最大者。 + +### 1. 最长公共子序列 + +1143\. Longest Common Subsequence + +[Leetcode](https://leetcode.com/problems/longest-common-subsequence/) / [力扣](https://leetcode-cn.com/problems/longest-common-subsequence/) + +```java + public int longestCommonSubsequence(String text1, String text2) { + int n1 = text1.length(), n2 = text2.length(); + int[][] dp = new int[n1 + 1][n2 + 1]; + for (int i = 1; i <= n1; i++) { + for (int j = 1; j <= n2; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[n1][n2]; + } +``` + +## 0-1 背包 + +有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。 + +定义一个二维数组 dp 存储最大价值,其中 dp[i][j] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论: + +- 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i-1 件物品的最大价值,dp[i][j] = dp[i-1][j]。 +- 第 i 件物品添加到背包中,dp[i][j] = dp[i-1][j-w] + v。 + +第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为: + + + +

+ +```java +// W 为背包总体积 +// N 为物品数量 +// weights 数组存储 N 个物品的重量 +// values 数组存储 N 个物品的价值 +public int knapsack(int W, int N, int[] weights, int[] values) { + int[][] dp = new int[N + 1][W + 1]; + for (int i = 1; i <= N; i++) { + int w = weights[i - 1], v = values[i - 1]; + for (int j = 1; j <= W; j++) { + if (j >= w) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v); + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + return dp[N][W]; +} +``` + +**空间优化** + +在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅与前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时, + + + +

+ +因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],防止将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 + +```java +public int knapsack(int W, int N, int[] weights, int[] values) { + int[] dp = new int[W + 1]; + for (int i = 1; i <= N; i++) { + int w = weights[i - 1], v = values[i - 1]; + for (int j = W; j >= 1; j--) { + if (j >= w) { + dp[j] = Math.max(dp[j], dp[j - w] + v); + } + } + } + return dp[W]; +} +``` + +**无法使用贪心算法的解释** + +0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优。考虑下面的物品和一个容量为 5 的背包,如果先添加物品 0 再添加物品 1,那么只能存放的价值为 16,浪费了大小为 2 的空间。最优的方式是存放物品 1 和物品 2,价值为 22. + +| id | w | v | v/w | +| --- | --- | --- | --- | +| 0 | 1 | 6 | 6 | +| 1 | 2 | 10 | 5 | +| 2 | 3 | 12 | 4 | + +**变种** + +- 完全背包:物品数量为无限个 + +- 多重背包:物品数量有限制 + +- 多维费用背包:物品不仅有重量,还有体积,同时考虑这两种限制 + +- 其它:物品之间相互约束或者依赖 + +### 1. 划分数组为和相等的两部分 + +416\. Partition Equal Subset Sum (Medium) + +[Leetcode](https://leetcode.com/problems/partition-equal-subset-sum/description/) / [力扣](https://leetcode-cn.com/problems/partition-equal-subset-sum/description/) + +```html +Input: [1, 5, 11, 5] + +Output: true + +Explanation: The array can be partitioned as [1, 5, 5] and [11]. +``` + +可以看成一个背包大小为 sum/2 的 0-1 背包问题。 + +```java +public boolean canPartition(int[] nums) { + int sum = computeArraySum(nums); + if (sum % 2 != 0) { + return false; + } + int W = sum / 2; + boolean[] dp = new boolean[W + 1]; + dp[0] = true; + for (int num : nums) { // 0-1 背包一个物品只能用一次 + for (int i = W; i >= num; i--) { // 从后往前,先计算 dp[i] 再计算 dp[i-num] + dp[i] = dp[i] || dp[i - num]; + } + } + return dp[W]; +} + +private int computeArraySum(int[] nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + return sum; +} +``` + +### 2. 改变一组数的正负号使得它们的和为一给定数 + +494\. Target Sum (Medium) + +[Leetcode](https://leetcode.com/problems/target-sum/description/) / [力扣](https://leetcode-cn.com/problems/target-sum/description/) + +```html +Input: nums is [1, 1, 1, 1, 1], S is 3. +Output: 5 +Explanation: + +-1+1+1+1+1 = 3 ++1-1+1+1+1 = 3 ++1+1-1+1+1 = 3 ++1+1+1-1+1 = 3 ++1+1+1+1-1 = 3 + +There are 5 ways to assign symbols to make the sum of nums be target 3. +``` + +该问题可以转换为 Subset Sum 问题,从而使用 0-1 背包的方法来求解。 + +可以将这组数看成两部分,P 和 N,其中 P 使用正号,N 使用负号,有以下推导: + +```html + sum(P) - sum(N) = target +sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N) + 2 * sum(P) = target + sum(nums) +``` + +因此只要找到一个子集,令它们都取正号,并且和等于 (target + sum(nums))/2,就证明存在解。 + +```java +public int findTargetSumWays(int[] nums, int S) { + int sum = computeArraySum(nums); + if (sum < S || (sum + S) % 2 == 1) { + return 0; + } + int W = (sum + S) / 2; + int[] dp = new int[W + 1]; + dp[0] = 1; + for (int num : nums) { + for (int i = W; i >= num; i--) { + dp[i] = dp[i] + dp[i - num]; + } + } + return dp[W]; +} + +private int computeArraySum(int[] nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + return sum; +} +``` + +DFS 解法: + +```java +public int findTargetSumWays(int[] nums, int S) { + return findTargetSumWays(nums, 0, S); +} + +private int findTargetSumWays(int[] nums, int start, int S) { + if (start == nums.length) { + return S == 0 ? 1 : 0; + } + return findTargetSumWays(nums, start + 1, S + nums[start]) + + findTargetSumWays(nums, start + 1, S - nums[start]); +} +``` + +### 3. 01 字符构成最多的字符串 + +474\. Ones and Zeroes (Medium) + +[Leetcode](https://leetcode.com/problems/ones-and-zeroes/description/) / [力扣](https://leetcode-cn.com/problems/ones-and-zeroes/description/) + +```html +Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 +Output: 4 + +Explanation: There are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are "10","0001","1","0" +``` + +这是一个多维费用的 0-1 背包问题,有两个背包大小,0 的数量和 1 的数量。 + +```java +public int findMaxForm(String[] strs, int m, int n) { + if (strs == null || strs.length == 0) { + return 0; + } + int[][] dp = new int[m + 1][n + 1]; + for (String s : strs) { // 每个字符串只能用一次 + int ones = 0, zeros = 0; + for (char c : s.toCharArray()) { + if (c == '0') { + zeros++; + } else { + ones++; + } + } + for (int i = m; i >= zeros; i--) { + for (int j = n; j >= ones; j--) { + dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1); + } + } + } + return dp[m][n]; +} +``` + +### 4. 找零钱的最少硬币数 + +322\. Coin Change (Medium) + +[Leetcode](https://leetcode.com/problems/coin-change/description/) / [力扣](https://leetcode-cn.com/problems/coin-change/description/) + +```html +Example 1: +coins = [1, 2, 5], amount = 11 +return 3 (11 = 5 + 5 + 1) + +Example 2: +coins = [2], amount = 3 +return -1. +``` + +题目描述:给一些面额的硬币,要求用这些硬币来组成给定面额的钱数,并且使得硬币数量最少。硬币可以重复使用。 + +- 物品:硬币 +- 物品大小:面额 +- 物品价值:数量 + +因为硬币可以重复使用,因此这是一个完全背包问题。完全背包只需要将 0-1 背包的逆序遍历 dp 数组改为正序遍历即可。 + +```java +public int coinChange(int[] coins, int amount) { + if (amount == 0 || coins == null) return 0; + int[] dp = new int[amount + 1]; + for (int coin : coins) { + for (int i = coin; i <= amount; i++) { //将逆序遍历改为正序遍历 + if (i == coin) { + dp[i] = 1; + } else if (dp[i] == 0 && dp[i - coin] != 0) { + dp[i] = dp[i - coin] + 1; + + } else if (dp[i - coin] != 0) { + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + } + return dp[amount] == 0 ? -1 : dp[amount]; +} +``` + +### 5. 找零钱的硬币数组合 + +518\. Coin Change 2 (Medium) + +[Leetcode](https://leetcode.com/problems/coin-change-2/description/) / [力扣](https://leetcode-cn.com/problems/coin-change-2/description/) + +```text-html-basic +Input: amount = 5, coins = [1, 2, 5] +Output: 4 +Explanation: there are four ways to make up the amount: +5=5 +5=2+2+1 +5=2+1+1+1 +5=1+1+1+1+1 +``` + +完全背包问题,使用 dp 记录可达成目标的组合数目。 + +```java +public int change(int amount, int[] coins) { + if (coins == null) { + return 0; + } + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int coin : coins) { + for (int i = coin; i <= amount; i++) { + dp[i] += dp[i - coin]; + } + } + return dp[amount]; +} +``` + +### 6. 字符串按单词列表分割 + +139\. Word Break (Medium) + +[Leetcode](https://leetcode.com/problems/word-break/description/) / [力扣](https://leetcode-cn.com/problems/word-break/description/) + +```html +s = "leetcode", +dict = ["leet", "code"]. +Return true because "leetcode" can be segmented as "leet code". +``` + +dict 中的单词没有使用次数的限制,因此这是一个完全背包问题。 + +该问题涉及到字典中单词的使用顺序,也就是说物品必须按一定顺序放入背包中,例如下面的 dict 就不够组成字符串 "leetcode": + +```html +["lee", "tc", "cod"] +``` + +求解顺序的完全背包问题时,对物品的迭代应该放在最里层,对背包的迭代放在外层,只有这样才能让物品按一定顺序放入背包中。 + +```java +public boolean wordBreak(String s, List wordDict) { + int n = s.length(); + boolean[] dp = new boolean[n + 1]; + dp[0] = true; + for (int i = 1; i <= n; i++) { + for (String word : wordDict) { // 对物品的迭代应该放在最里层 + int len = word.length(); + if (len <= i && word.equals(s.substring(i - len, i))) { + dp[i] = dp[i] || dp[i - len]; + } + } + } + return dp[n]; +} +``` + +### 7. 组合总和 + +377\. Combination Sum IV (Medium) + +[Leetcode](https://leetcode.com/problems/combination-sum-iv/description/) / [力扣](https://leetcode-cn.com/problems/combination-sum-iv/description/) + +```html +nums = [1, 2, 3] +target = 4 + +The possible combination ways are: +(1, 1, 1, 1) +(1, 1, 2) +(1, 2, 1) +(1, 3) +(2, 1, 1) +(2, 2) +(3, 1) + +Note that different sequences are counted as different combinations. + +Therefore the output is 7. +``` + +涉及顺序的完全背包。 + +```java +public int combinationSum4(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int[] maximum = new int[target + 1]; + maximum[0] = 1; + Arrays.sort(nums); + for (int i = 1; i <= target; i++) { + for (int j = 0; j < nums.length && nums[j] <= i; j++) { + maximum[i] += maximum[i - nums[j]]; + } + } + return maximum[target]; +} +``` + +## 股票交易 + +### 1. 需要冷却期的股票交易 + +309\. Best Time to Buy and Sell Stock with Cooldown(Medium) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/description/) + +题目描述:交易之后需要有一天的冷却时间。 + +

+ +```java +public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int N = prices.length; + int[] buy = new int[N]; + int[] s1 = new int[N]; + int[] sell = new int[N]; + int[] s2 = new int[N]; + s1[0] = buy[0] = -prices[0]; + sell[0] = s2[0] = 0; + for (int i = 1; i < N; i++) { + buy[i] = s2[i - 1] - prices[i]; + s1[i] = Math.max(buy[i - 1], s1[i - 1]); + sell[i] = Math.max(buy[i - 1], s1[i - 1]) + prices[i]; + s2[i] = Math.max(s2[i - 1], sell[i - 1]); + } + return Math.max(sell[N - 1], s2[N - 1]); +} +``` + +### 2. 需要交易费用的股票交易 + +714\. Best Time to Buy and Sell Stock with Transaction Fee (Medium) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/description/) + +```html +Input: prices = [1, 3, 2, 8, 4, 9], fee = 2 +Output: 8 +Explanation: The maximum profit can be achieved by: +Buying at prices[0] = 1 +Selling at prices[3] = 8 +Buying at prices[4] = 4 +Selling at prices[5] = 9 +The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. +``` + +题目描述:每交易一次,都要支付一定的费用。 + +

+ +```java +public int maxProfit(int[] prices, int fee) { + int N = prices.length; + int[] buy = new int[N]; + int[] s1 = new int[N]; + int[] sell = new int[N]; + int[] s2 = new int[N]; + s1[0] = buy[0] = -prices[0]; + sell[0] = s2[0] = 0; + for (int i = 1; i < N; i++) { + buy[i] = Math.max(sell[i - 1], s2[i - 1]) - prices[i]; + s1[i] = Math.max(buy[i - 1], s1[i - 1]); + sell[i] = Math.max(buy[i - 1], s1[i - 1]) - fee + prices[i]; + s2[i] = Math.max(s2[i - 1], sell[i - 1]); + } + return Math.max(sell[N - 1], s2[N - 1]); +} +``` + + +### 3. 只能进行两次的股票交易 + +123\. Best Time to Buy and Sell Stock III (Hard) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/description/) + +```java +public int maxProfit(int[] prices) { + int firstBuy = Integer.MIN_VALUE, firstSell = 0; + int secondBuy = Integer.MIN_VALUE, secondSell = 0; + for (int curPrice : prices) { + if (firstBuy < -curPrice) { + firstBuy = -curPrice; + } + if (firstSell < firstBuy + curPrice) { + firstSell = firstBuy + curPrice; + } + if (secondBuy < firstSell - curPrice) { + secondBuy = firstSell - curPrice; + } + if (secondSell < secondBuy + curPrice) { + secondSell = secondBuy + curPrice; + } + } + return secondSell; +} +``` + +### 4. 只能进行 k 次的股票交易 + +188\. Best Time to Buy and Sell Stock IV (Hard) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/description/) + +```java +public int maxProfit(int k, int[] prices) { + int n = prices.length; + if (k >= n / 2) { // 这种情况下该问题退化为普通的股票交易问题 + int maxProfit = 0; + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + maxProfit += prices[i] - prices[i - 1]; + } + } + return maxProfit; + } + int[][] maxProfit = new int[k + 1][n]; + for (int i = 1; i <= k; i++) { + int localMax = maxProfit[i - 1][0] - prices[0]; + for (int j = 1; j < n; j++) { + maxProfit[i][j] = Math.max(maxProfit[i][j - 1], prices[j] + localMax); + localMax = Math.max(localMax, maxProfit[i - 1][j] - prices[j]); + } + } + return maxProfit[k][n - 1]; +} +``` + +## 字符串编辑 + +### 1. 删除两个字符串的字符使它们相等 + +583\. Delete Operation for Two Strings (Medium) + +[Leetcode](https://leetcode.com/problems/delete-operation-for-two-strings/description/) / [力扣](https://leetcode-cn.com/problems/delete-operation-for-two-strings/description/) + +```html +Input: "sea", "eat" +Output: 2 +Explanation: You need one step to make "sea" to "ea" and another step to make "eat" to "ea". +``` + +可以转换为求两个字符串的最长公共子序列问题。 + +```java +public int minDistance(String word1, String word2) { + int m = word1.length(), n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + } + } + } + return m + n - 2 * dp[m][n]; +} +``` + +### 2. 编辑距离 + +72\. Edit Distance (Hard) + +[Leetcode](https://leetcode.com/problems/edit-distance/description/) / [力扣](https://leetcode-cn.com/problems/edit-distance/description/) + +```html +Example 1: + +Input: word1 = "horse", word2 = "ros" +Output: 3 +Explanation: +horse -> rorse (replace 'h' with 'r') +rorse -> rose (remove 'r') +rose -> ros (remove 'e') +Example 2: + +Input: word1 = "intention", word2 = "execution" +Output: 5 +Explanation: +intention -> inention (remove 't') +inention -> enention (replace 'i' with 'e') +enention -> exention (replace 'n' with 'x') +exention -> exection (replace 'n' with 'c') +exection -> execution (insert 'u') +``` + +题目描述:修改一个字符串成为另一个字符串,使得修改次数最少。一次修改操作包括:插入一个字符、删除一个字符、替换一个字符。 + +```java +public int minDistance(String word1, String word2) { + if (word1 == null || word2 == null) { + return 0; + } + int m = word1.length(), n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 1; i <= m; i++) { + dp[i][0] = i; + } + for (int i = 1; i <= n; i++) { + dp[0][i] = i; + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; + } + } + } + return dp[m][n]; +} +``` + +### 3. 复制粘贴字符 + +650\. 2 Keys Keyboard (Medium) + +[Leetcode](https://leetcode.com/problems/2-keys-keyboard/description/) / [力扣](https://leetcode-cn.com/problems/2-keys-keyboard/description/) + +题目描述:最开始只有一个字符 A,问需要多少次操作能够得到 n 个字符 A,每次操作可以复制当前所有的字符,或者粘贴。 + +``` +Input: 3 +Output: 3 +Explanation: +Intitally, we have one character 'A'. +In step 1, we use Copy All operation. +In step 2, we use Paste operation to get 'AA'. +In step 3, we use Paste operation to get 'AAA'. +``` + +```java +public int minSteps(int n) { + if (n == 1) return 0; + for (int i = 2; i <= Math.sqrt(n); i++) { + if (n % i == 0) return i + minSteps(n / i); + } + return n; +} +``` + +```java +public int minSteps(int n) { + int[] dp = new int[n + 1]; + int h = (int) Math.sqrt(n); + for (int i = 2; i <= n; i++) { + dp[i] = i; + for (int j = 2; j <= h; j++) { + if (i % j == 0) { + dp[i] = dp[j] + dp[i / j]; + break; + } + } + } + return dp[n]; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" new file mode 100644 index 0000000000..1dbd48df9c --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" @@ -0,0 +1,294 @@ +# Leetcode 题解 - 双指针 + +* [Leetcode 题解 - 双指针](#leetcode-题解---双指针) + * [1. 有序数组的 Two Sum](#1-有序数组的-two-sum) + * [2. 两数平方和](#2-两数平方和) + * [3. 反转字符串中的元音字符](#3-反转字符串中的元音字符) + * [4. 回文字符串](#4-回文字符串) + * [5. 归并两个有序数组](#5-归并两个有序数组) + * [6. 判断链表是否存在环](#6-判断链表是否存在环) + * [7. 最长子序列](#7-最长子序列) + + + +双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 + +## 1. 有序数组的 Two Sum + +167\. Two Sum II - Input array is sorted (Easy) + +[Leetcode](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) / [力扣](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/description/) + +```html +Input: numbers={2, 7, 11, 15}, target=9 +Output: index1=1, index2=2 +``` + +题目描述:在有序数组中找出两个数,使它们的和为 target。 + +使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 + +- 如果两个指针指向元素的和 sum == target,那么得到要求的结果; +- 如果 sum \> target,移动较大的元素,使 sum 变小一些; +- 如果 sum \< target,移动较小的元素,使 sum 变大一些。 + +数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。 + +

+ +```java +public int[] twoSum(int[] numbers, int target) { + if (numbers == null) return null; + int i = 0, j = numbers.length - 1; + while (i < j) { + int sum = numbers[i] + numbers[j]; + if (sum == target) { + return new int[]{i + 1, j + 1}; + } else if (sum < target) { + i++; + } else { + j--; + } + } + return null; +} +``` + +## 2. 两数平方和 + +633\. Sum of Square Numbers (Easy) + +[Leetcode](https://leetcode.com/problems/sum-of-square-numbers/description/) / [力扣](https://leetcode-cn.com/problems/sum-of-square-numbers/description/) + +```html +Input: 5 +Output: True +Explanation: 1 * 1 + 2 * 2 = 5 +``` + +题目描述:判断一个非负整数是否为两个整数的平方和。 + +可以看成是在元素为 0\~target 的有序数组中查找两个数,使得这两个数的平方和为 target,如果能找到,则返回 true,表示 target 是两个整数的平方和。 + +本题和 167\. Two Sum II - Input array is sorted 类似,只有一个明显区别:一个是和为 target,一个是平方和为 target。本题同样可以使用双指针得到两个数,使其平方和为 target。 + +本题的关键是右指针的初始化,实现剪枝,从而降低时间复杂度。设右指针为 x,左指针固定为 0,为了使 02 + x2 的值尽可能接近 target,我们可以将 x 取为 sqrt(target)。 + +因为最多只需要遍历一次 0\~sqrt(target),所以时间复杂度为 O(sqrt(target))。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。 + +```java + public boolean judgeSquareSum(int target) { + if (target < 0) return false; + int i = 0, j = (int) Math.sqrt(target); + while (i <= j) { + int powSum = i * i + j * j; + if (powSum == target) { + return true; + } else if (powSum > target) { + j--; + } else { + i++; + } + } + return false; + } +``` + +## 3. 反转字符串中的元音字符 + +345\. Reverse Vowels of a String (Easy) + +[Leetcode](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) / [力扣](https://leetcode-cn.com/problems/reverse-vowels-of-a-string/description/) + +```html +Given s = "leetcode", return "leotcede". +``` + +

+ +使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。 + +为了快速判断一个字符是不是元音字符,我们将全部元音字符添加到集合 HashSet 中,从而以 O(1) 的时间复杂度进行该操作。 + +- 时间复杂度为 O(N):只需要遍历所有元素一次 +- 空间复杂度 O(1):只需要使用两个额外变量 + +

+ +```java +private final static HashSet vowels = new HashSet<>( + Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U')); + +public String reverseVowels(String s) { + if (s == null) return null; + int i = 0, j = s.length() - 1; + char[] result = new char[s.length()]; + while (i <= j) { + char ci = s.charAt(i); + char cj = s.charAt(j); + if (!vowels.contains(ci)) { + result[i++] = ci; + } else if (!vowels.contains(cj)) { + result[j--] = cj; + } else { + result[i++] = cj; + result[j--] = ci; + } + } + return new String(result); +} +``` + +## 4. 回文字符串 + +680\. Valid Palindrome II (Easy) + +[Leetcode](https://leetcode.com/problems/valid-palindrome-ii/description/) / [力扣](https://leetcode-cn.com/problems/valid-palindrome-ii/description/) + +```html +Input: "abca" +Output: True +Explanation: You could delete the character 'c'. +``` + +题目描述:可以删除一个字符,判断是否能构成回文字符串。 + +所谓的回文字符串,是指具有左右对称特点的字符串,例如 "abcba" 就是一个回文字符串。 + +使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。 + +

+ +本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。 + +在判断是否为回文字符串时,我们不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。 + +在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。 + +

+ +```java +public boolean validPalindrome(String s) { + for (int i = 0, j = s.length() - 1; i < j; i++, j--) { + if (s.charAt(i) != s.charAt(j)) { + return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j); + } + } + return true; +} + +private boolean isPalindrome(String s, int i, int j) { + while (i < j) { + if (s.charAt(i++) != s.charAt(j--)) { + return false; + } + } + return true; +} +``` + +## 5. 归并两个有序数组 + +88\. Merge Sorted Array (Easy) + +[Leetcode](https://leetcode.com/problems/merge-sorted-array/description/) / [力扣](https://leetcode-cn.com/problems/merge-sorted-array/description/) + +```html +Input: +nums1 = [1,2,3,0,0,0], m = 3 +nums2 = [2,5,6], n = 3 + +Output: [1,2,2,3,5,6] +``` + +题目描述:把归并结果存到第一个数组上。 + +需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。 + +```java +public void merge(int[] nums1, int m, int[] nums2, int n) { + int index1 = m - 1, index2 = n - 1; + int indexMerge = m + n - 1; + while (index2 >= 0) { + if (index1 < 0) { + nums1[indexMerge--] = nums2[index2--]; + } else if (index2 < 0) { + nums1[indexMerge--] = nums1[index1--]; + } else if (nums1[index1] > nums2[index2]) { + nums1[indexMerge--] = nums1[index1--]; + } else { + nums1[indexMerge--] = nums2[index2--]; + } + } +} +``` + +## 6. 判断链表是否存在环 + +141\. Linked List Cycle (Easy) + +[Leetcode](https://leetcode.com/problems/linked-list-cycle/description/) / [力扣](https://leetcode-cn.com/problems/linked-list-cycle/description/) + +使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。 + +```java +public boolean hasCycle(ListNode head) { + if (head == null) { + return false; + } + ListNode l1 = head, l2 = head.next; + while (l1 != null && l2 != null && l2.next != null) { + if (l1 == l2) { + return true; + } + l1 = l1.next; + l2 = l2.next.next; + } + return false; +} +``` + +## 7. 最长子序列 + +524\. Longest Word in Dictionary through Deleting (Medium) + +[Leetcode](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) / [力扣](https://leetcode-cn.com/problems/longest-word-in-dictionary-through-deleting/description/) + +``` +Input: +s = "abpcplea", d = ["ale","apple","monkey","plea"] + +Output: +"apple" +``` + +题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。 + +通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。 + +```java +public String findLongestWord(String s, List d) { + String longestWord = ""; + for (String target : d) { + int l1 = longestWord.length(), l2 = target.length(); + if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) { + continue; + } + if (isSubstr(s, target)) { + longestWord = target; + } + } + return longestWord; +} + +private boolean isSubstr(String s, String target) { + int i = 0, j = 0; + while (i < s.length() && j < target.length()) { + if (s.charAt(i) == target.charAt(j)) { + j++; + } + i++; + } + return j == target.length(); +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" new file mode 100644 index 0000000000..c8565bbf3a --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" @@ -0,0 +1,135 @@ +# Leetcode 题解 - 哈希表 + +* [Leetcode 题解 - 哈希表](#leetcode-题解---哈希表) + * [1. 数组中两个数的和为给定值](#1-数组中两个数的和为给定值) + * [2. 判断数组是否含有重复元素](#2-判断数组是否含有重复元素) + * [3. 最长和谐序列](#3-最长和谐序列) + * [4. 最长连续序列](#4-最长连续序列) + + + +哈希表使用 O(N) 空间复杂度存储数据,并且以 O(1) 时间复杂度求解问题。 + +- Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。 + + Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中 [Leetcdoe : 535. Encode and Decode TinyURL (Medium) + +[Leetcode](https://leetcode.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源�) / [力扣](https://leetcode-cn.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源�) + + +## 1. 数组中两个数的和为给定值 + +1\. Two Sum (Easy) + +[Leetcode](https://leetcode.com/problems/two-sum/description/) / [力扣](https://leetcode-cn.com/problems/two-sum/description/) + +可以先对数组进行排序,然后使用双指针方法或者二分查找方法。这样做的时间复杂度为 O(NlogN),空间复杂度为 O(1)。 + +用 HashMap 存储数组元素和索引的映射,在访问到 nums[i] 时,判断 HashMap 中是否存在 target - nums[i],如果存在说明 target - nums[i] 所在的索引和 i 就是要找的两个数。该方法的时间复杂度为 O(N),空间复杂度为 O(N),使用空间来换取时间。 + +```java +public int[] twoSum(int[] nums, int target) { + HashMap indexForNum = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (indexForNum.containsKey(target - nums[i])) { + return new int[]{indexForNum.get(target - nums[i]), i}; + } else { + indexForNum.put(nums[i], i); + } + } + return null; +} +``` + +## 2. 判断数组是否含有重复元素 + +217\. Contains Duplicate (Easy) + +[Leetcode](https://leetcode.com/problems/contains-duplicate/description/) / [力扣](https://leetcode-cn.com/problems/contains-duplicate/description/) + +```java +public boolean containsDuplicate(int[] nums) { + Set set = new HashSet<>(); + for (int num : nums) { + set.add(num); + } + return set.size() < nums.length; +} +``` + +## 3. 最长和谐序列 + +594\. Longest Harmonious Subsequence (Easy) + +[Leetcode](https://leetcode.com/problems/longest-harmonious-subsequence/description/) / [力扣](https://leetcode-cn.com/problems/longest-harmonious-subsequence/description/) + +```html +Input: [1,3,2,2,5,2,3,7] +Output: 5 +Explanation: The longest harmonious subsequence is [3,2,2,2,3]. +``` + +和谐序列中最大数和最小数之差正好为 1,应该注意的是序列的元素不一定是数组的连续元素。 + +```java +public int findLHS(int[] nums) { + Map countForNum = new HashMap<>(); + for (int num : nums) { + countForNum.put(num, countForNum.getOrDefault(num, 0) + 1); + } + int longest = 0; + for (int num : countForNum.keySet()) { + if (countForNum.containsKey(num + 1)) { + longest = Math.max(longest, countForNum.get(num + 1) + countForNum.get(num)); + } + } + return longest; +} +``` + +## 4. 最长连续序列 + +128\. Longest Consecutive Sequence (Hard) + +[Leetcode](https://leetcode.com/problems/longest-consecutive-sequence/description/) / [力扣](https://leetcode-cn.com/problems/longest-consecutive-sequence/description/) + +```html +Given [100, 4, 200, 1, 3, 2], +The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. +``` + +要求以 O(N) 的时间复杂度求解。 + +```java +public int longestConsecutive(int[] nums) { + Map countForNum = new HashMap<>(); + for (int num : nums) { + countForNum.put(num, 1); + } + for (int num : nums) { + forward(countForNum, num); + } + return maxCount(countForNum); +} + +private int forward(Map countForNum, int num) { + if (!countForNum.containsKey(num)) { + return 0; + } + int cnt = countForNum.get(num); + if (cnt > 1) { + return cnt; + } + cnt = forward(countForNum, num + 1) + 1; + countForNum.put(num, cnt); + return cnt; +} + +private int maxCount(Map countForNum) { + int max = 0; + for (int num : countForNum.keySet()) { + max = Math.max(max, countForNum.get(num)); + } + return max; +} +``` diff --git "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" similarity index 79% rename from "docs/notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" rename to "notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" index f660c2c75f..4fe635d5eb 100644 --- "a/docs/notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\233\276.md" @@ -1,21 +1,25 @@ +# Leetcode 题解 - 图 -* [二分图](#二分图) - * [判断是否为二分图](#判断是否为二分图) -* [拓扑排序](#拓扑排序) - * [课程安排的合法性](#课程安排的合法性) - * [课程安排的顺序](#课程安排的顺序) -* [并查集](#并查集) - * [冗余连接](#冗余连接) +* [Leetcode 题解 - 图](#leetcode-题解---图) + * [二分图](#二分图) + * [1. 判断是否为二分图](#1-判断是否为二分图) + * [拓扑排序](#拓扑排序) + * [1. 课程安排的合法性](#1-课程安排的合法性) + * [2. 课程安排的顺序](#2-课程安排的顺序) + * [并查集](#并查集) + * [1. 冗余连接](#1-冗余连接) -# 二分图 +## 二分图 如果可以用两种颜色对图中的节点进行着色,并且保证相邻的节点颜色不同,那么这个图就是二分图。 -## 判断是否为二分图 +### 1. 判断是否为二分图 -[785. Is Graph Bipartite? (Medium)](https://leetcode.com/problems/is-graph-bipartite/description/) +785\. Is Graph Bipartite? (Medium) + +[Leetcode](https://leetcode.com/problems/is-graph-bipartite/description/) / [力扣](https://leetcode-cn.com/problems/is-graph-bipartite/description/) ```html Input: [[1,3], [0,2], [1,3], [0,2]] @@ -68,13 +72,15 @@ private boolean isBipartite(int curNode, int curColor, int[] colors, int[][] gra } ``` -# 拓扑排序 +## 拓扑排序 常用于在具有先序关系的任务规划中。 -## 课程安排的合法性 +### 1. 课程安排的合法性 + +207\. Course Schedule (Medium) -[207. Course Schedule (Medium)](https://leetcode.com/problems/course-schedule/description/) +[Leetcode](https://leetcode.com/problems/course-schedule/description/) / [力扣](https://leetcode-cn.com/problems/course-schedule/description/) ```html 2, [[1,0]] @@ -130,9 +136,11 @@ private boolean hasCycle(boolean[] globalMarked, boolean[] localMarked, } ``` -## 课程安排的顺序 +### 2. 课程安排的顺序 -[210. Course Schedule II (Medium)](https://leetcode.com/problems/course-schedule-ii/description/) +210\. Course Schedule II (Medium) + +[Leetcode](https://leetcode.com/problems/course-schedule-ii/description/) / [力扣](https://leetcode-cn.com/problems/course-schedule-ii/description/) ```html 4, [[1,0],[2,0],[3,1],[3,2]] @@ -141,7 +149,7 @@ There are a total of 4 courses to take. To take course 3 you should have finishe 使用 DFS 来实现拓扑排序,使用一个栈存储后序遍历结果,这个栈的逆序结果就是拓扑排序结果。 -证明:对于任何先序关系:v->w,后序遍历结果可以保证 w 先进入栈中,因此栈的逆序结果中 v 会在 w 之前。 +证明:对于任何先序关系:v-\>w,后序遍历结果可以保证 w 先进入栈中,因此栈的逆序结果中 v 会在 w 之前。 ```java public int[] findOrder(int numCourses, int[][] prerequisites) { @@ -189,13 +197,15 @@ private boolean hasCycle(boolean[] globalMarked, boolean[] localMarked, List
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" new file mode 100644 index 0000000000..7c40f94925 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" @@ -0,0 +1,239 @@ +# Leetcode 题解 - 字符串 + +* [Leetcode 题解 - 字符串](#leetcode-题解---字符串) + * [1. 字符串循环移位包含](#1-字符串循环移位包含) + * [2. 字符串循环移位](#2-字符串循环移位) + * [3. 字符串中单词的翻转](#3-字符串中单词的翻转) + * [4. 两个字符串包含的字符是否完全相同](#4-两个字符串包含的字符是否完全相同) + * [5. 计算一组字符集合可以组成的回文字符串的最大长度](#5-计算一组字符集合可以组成的回文字符串的最大长度) + * [6. 字符串同构](#6-字符串同构) + * [7. 回文子字符串个数](#7-回文子字符串个数) + * [8. 判断一个整数是否是回文数](#8-判断一个整数是否是回文数) + * [9. 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数](#9-统计二进制字符串中连续-1-和连续-0-数量相同的子字符串个数) + + + +## 1. 字符串循环移位包含 + +[编程之美 3.1](#) + +```html +s1 = AABCD, s2 = CDAA +Return : true +``` + +给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。 + +s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。 + +## 2. 字符串循环移位 + +[编程之美 2.17](#) + +```html +s = "abcd123" k = 3 +Return "123abcd" +``` + +将字符串向右循环移动 k 位。 + +将 abcd123 中的 abcd 和 123 单独翻转,得到 dcba321,然后对整个字符串进行翻转,得到 123abcd。 + +## 3. 字符串中单词的翻转 + +[程序员代码面试指南](#) + +```html +s = "I am a student" +Return "student a am I" +``` + +将每个单词翻转,然后将整个字符串翻转。 + +## 4. 两个字符串包含的字符是否完全相同 + +242\. Valid Anagram (Easy) + +[Leetcode](https://leetcode.com/problems/valid-anagram/description/) / [力扣](https://leetcode-cn.com/problems/valid-anagram/description/) + +```html +s = "anagram", t = "nagaram", return true. +s = "rat", t = "car", return false. +``` + +可以用 HashMap 来映射字符与出现次数,然后比较两个字符串出现的字符数量是否相同。 + +由于本题的字符串只包含 26 个小写字符,因此可以使用长度为 26 的整型数组对字符串出现的字符进行统计,不再使用 HashMap。 + +```java +public boolean isAnagram(String s, String t) { + int[] cnts = new int[26]; + for (char c : s.toCharArray()) { + cnts[c - 'a']++; + } + for (char c : t.toCharArray()) { + cnts[c - 'a']--; + } + for (int cnt : cnts) { + if (cnt != 0) { + return false; + } + } + return true; +} +``` + +## 5. 计算一组字符集合可以组成的回文字符串的最大长度 + +409\. Longest Palindrome (Easy) + +[Leetcode](https://leetcode.com/problems/longest-palindrome/description/) / [力扣](https://leetcode-cn.com/problems/longest-palindrome/description/) + +```html +Input : "abccccdd" +Output : 7 +Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7. +``` + +使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。 + +因为回文字符串最中间的那个字符可以单独出现,所以如果有单独的字符就把它放到最中间。 + +```java +public int longestPalindrome(String s) { + int[] cnts = new int[256]; + for (char c : s.toCharArray()) { + cnts[c]++; + } + int palindrome = 0; + for (int cnt : cnts) { + palindrome += (cnt / 2) * 2; + } + if (palindrome < s.length()) { + palindrome++; // 这个条件下 s 中一定有单个未使用的字符存在,可以把这个字符放到回文的最中间 + } + return palindrome; +} +``` + +## 6. 字符串同构 + +205\. Isomorphic Strings (Easy) + +[Leetcode](https://leetcode.com/problems/isomorphic-strings/description/) / [力扣](https://leetcode-cn.com/problems/isomorphic-strings/description/) + +```html +Given "egg", "add", return true. +Given "foo", "bar", return false. +Given "paper", "title", return true. +``` + +记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。 + +```java +public boolean isIsomorphic(String s, String t) { + int[] preIndexOfS = new int[256]; + int[] preIndexOfT = new int[256]; + for (int i = 0; i < s.length(); i++) { + char sc = s.charAt(i), tc = t.charAt(i); + if (preIndexOfS[sc] != preIndexOfT[tc]) { + return false; + } + preIndexOfS[sc] = i + 1; + preIndexOfT[tc] = i + 1; + } + return true; +} +``` + +## 7. 回文子字符串个数 + +647\. Palindromic Substrings (Medium) + +[Leetcode](https://leetcode.com/problems/palindromic-substrings/description/) / [力扣](https://leetcode-cn.com/problems/palindromic-substrings/description/) + +```html +Input: "aaa" +Output: 6 +Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". +``` + +从字符串的某一位开始,尝试着去扩展子字符串。 + +```java +private int cnt = 0; + +public int countSubstrings(String s) { + for (int i = 0; i < s.length(); i++) { + extendSubstrings(s, i, i); // 奇数长度 + extendSubstrings(s, i, i + 1); // 偶数长度 + } + return cnt; +} + +private void extendSubstrings(String s, int start, int end) { + while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) { + start--; + end++; + cnt++; + } +} +``` + +## 8. 判断一个整数是否是回文数 + +9\. Palindrome Number (Easy) + +[Leetcode](https://leetcode.com/problems/palindrome-number/description/) / [力扣](https://leetcode-cn.com/problems/palindrome-number/description/) + +要求不能使用额外空间,也就不能将整数转换为字符串进行判断。 + +将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。 + +```java +public boolean isPalindrome(int x) { + if (x == 0) { + return true; + } + if (x < 0 || x % 10 == 0) { + return false; + } + int right = 0; + while (x > right) { + right = right * 10 + x % 10; + x /= 10; + } + return x == right || x == right / 10; +} +``` + +## 9. 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数 + +696\. Count Binary Substrings (Easy) + +[Leetcode](https://leetcode.com/problems/count-binary-substrings/description/) / [力扣](https://leetcode-cn.com/problems/count-binary-substrings/description/) + +```html +Input: "00110011" +Output: 6 +Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01". +``` + +```java +public int countBinarySubstrings(String s) { + int preLen = 0, curLen = 1, count = 0; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) == s.charAt(i - 1)) { + curLen++; + } else { + preLen = curLen; + curLen = 1; + } + + if (preLen >= curLen) { + count++; + } + } + return count; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" new file mode 100644 index 0000000000..36a31e3d9b --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\216\222\345\272\217.md" @@ -0,0 +1,244 @@ +# Leetcode 题解 - 排序 + +* [Leetcode 题解 - 排序](#leetcode-题解---排序) + * [快速选择](#快速选择) + * [堆](#堆) + * [1. Kth Element](#1-kth-element) + * [桶排序](#桶排序) + * [1. 出现频率最多的 k 个元素](#1-出现频率最多的-k-个元素) + * [2. 按照字符出现次数对字符串排序](#2-按照字符出现次数对字符串排序) + * [荷兰国旗问题](#荷兰国旗问题) + * [1. 按颜色进行排序](#1-按颜色进行排序) + + + +## 快速选择 + +用于求解 **Kth Element** 问题,也就是第 K 个元素的问题。 + +可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 + +## 堆 + +用于求解 **TopK Elements** 问题,也就是 K 个最小元素的问题。使用最小堆来实现 TopK 问题,最小堆使用大顶堆来实现,大顶堆的堆顶元素为当前堆的最大元素。实现过程:不断地往大顶堆中插入新元素,当堆中元素的数量大于 k 时,移除堆顶元素,也就是当前堆中最大的元素,剩下的元素都为当前添加过的元素中最小的 K 个元素。插入和移除堆顶元素的时间复杂度都为 log2N。 + +堆也可以用于求解 Kth Element 问题,得到了大小为 K 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 K 大的元素。 + +快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。 + +可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 + +### 1. Kth Element + +215\. Kth Largest Element in an Array (Medium) + +[Leetcode](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) / [力扣](https://leetcode-cn.com/problems/kth-largest-element-in-an-array/description/) + +```text +Input: [3,2,1,5,6,4] and k = 2 +Output: 5 +``` + +题目描述:找到倒数第 k 个的元素。 + +**排序** :时间复杂度 O(NlogN),空间复杂度 O(1) + +```java +public int findKthLargest(int[] nums, int k) { + Arrays.sort(nums); + return nums[nums.length - k]; +} +``` + +**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。 + +```java +public int findKthLargest(int[] nums, int k) { + PriorityQueue pq = new PriorityQueue<>(); // 小顶堆 + for (int val : nums) { + pq.add(val); + if (pq.size() > k) // 维护堆的大小为 K + pq.poll(); + } + return pq.peek(); +} +``` + +**快速选择** :时间复杂度 O(N),空间复杂度 O(1) + +```java +public int findKthLargest(int[] nums, int k) { + k = nums.length - k; + int l = 0, h = nums.length - 1; + while (l < h) { + int j = partition(nums, l, h); + if (j == k) { + break; + } else if (j < k) { + l = j + 1; + } else { + h = j - 1; + } + } + return nums[k]; +} + +private int partition(int[] a, int l, int h) { + int i = l, j = h + 1; + while (true) { + while (a[++i] < a[l] && i < h) ; + while (a[--j] > a[l] && j > l) ; + if (i >= j) { + break; + } + swap(a, i, j); + } + swap(a, l, j); + return j; +} + +private void swap(int[] a, int i, int j) { + int t = a[i]; + a[i] = a[j]; + a[j] = t; +} +``` + +## 桶排序 + +### 1. 出现频率最多的 k 个元素 + +347\. Top K Frequent Elements (Medium) + +[Leetcode](https://leetcode.com/problems/top-k-frequent-elements/description/) / [力扣](https://leetcode-cn.com/problems/top-k-frequent-elements/description/) + +```html +Given [1,1,1,2,2,3] and k = 2, return [1,2]. +``` + +设置若干个桶,每个桶存储出现频率相同的数。桶的下标表示数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 + +把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 + +```java +public int[] topKFrequent(int[] nums, int k) { + Map frequencyForNum = new HashMap<>(); + for (int num : nums) { + frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1); + } + List[] buckets = new ArrayList[nums.length + 1]; + for (int key : frequencyForNum.keySet()) { + int frequency = frequencyForNum.get(key); + if (buckets[frequency] == null) { + buckets[frequency] = new ArrayList<>(); + } + buckets[frequency].add(key); + } + List topK = new ArrayList<>(); + for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) { + if (buckets[i] == null) { + continue; + } + if (buckets[i].size() <= (k - topK.size())) { + topK.addAll(buckets[i]); + } else { + topK.addAll(buckets[i].subList(0, k - topK.size())); + } + } + int[] res = new int[k]; + for (int i = 0; i < k; i++) { + res[i] = topK.get(i); + } + return res; +} +``` + +### 2. 按照字符出现次数对字符串排序 + +451\. Sort Characters By Frequency (Medium) + +[Leetcode](https://leetcode.com/problems/sort-characters-by-frequency/description/) / [力扣](https://leetcode-cn.com/problems/sort-characters-by-frequency/description/) + +```html +Input: +"tree" + +Output: +"eert" + +Explanation: +'e' appears twice while 'r' and 't' both appear once. +So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer. +``` + +```java +public String frequencySort(String s) { + Map frequencyForNum = new HashMap<>(); + for (char c : s.toCharArray()) + frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1); + + List[] frequencyBucket = new ArrayList[s.length() + 1]; + for (char c : frequencyForNum.keySet()) { + int f = frequencyForNum.get(c); + if (frequencyBucket[f] == null) { + frequencyBucket[f] = new ArrayList<>(); + } + frequencyBucket[f].add(c); + } + StringBuilder str = new StringBuilder(); + for (int i = frequencyBucket.length - 1; i >= 0; i--) { + if (frequencyBucket[i] == null) { + continue; + } + for (char c : frequencyBucket[i]) { + for (int j = 0; j < i; j++) { + str.append(c); + } + } + } + return str.toString(); +} +``` + +## 荷兰国旗问题 + +荷兰国旗包含三种颜色:红、白、蓝。 + +有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 + +

+ + +### 1. 按颜色进行排序 + +75\. Sort Colors (Medium) + +[Leetcode](https://leetcode.com/problems/sort-colors/description/) / [力扣](https://leetcode-cn.com/problems/sort-colors/description/) + +```html +Input: [2,0,2,1,1,0] +Output: [0,0,1,1,2,2] +``` + +题目描述:只有 0/1/2 三种颜色。 + +```java +public void sortColors(int[] nums) { + int zero = -1, one = 0, two = nums.length; + while (one < two) { + if (nums[one] == 0) { + swap(nums, ++zero, one++); + } else if (nums[one] == 2) { + swap(nums, --two, one); + } else { + ++one; + } + } +} + +private void swap(int[] nums, int i, int j) { + int t = nums[i]; + nums[i] = nums[j]; + nums[j] = t; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" new file mode 100644 index 0000000000..83d1c85ebc --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\220\234\347\264\242.md" @@ -0,0 +1,1324 @@ +# Leetcode 题解 - 搜索 + +* [Leetcode 题解 - 搜索](#leetcode-题解---搜索) + * [BFS](#bfs) + * [1. 计算在网格中从原点到特定点的最短路径长度](#1-计算在网格中从原点到特定点的最短路径长度) + * [2. 组成整数的最小平方数数量](#2-组成整数的最小平方数数量) + * [3. 最短单词路径](#3-最短单词路径) + * [DFS](#dfs) + * [1. 查找最大的连通面积](#1-查找最大的连通面积) + * [2. 矩阵中的连通分量数目](#2-矩阵中的连通分量数目) + * [3. 好友关系的连通分量数目](#3-好友关系的连通分量数目) + * [4. 填充封闭区域](#4-填充封闭区域) + * [5. 能到达的太平洋和大西洋的区域](#5-能到达的太平洋和大西洋的区域) + * [Backtracking](#backtracking) + * [1. 数字键盘组合](#1-数字键盘组合) + * [2. IP 地址划分](#2-ip-地址划分) + * [3. 在矩阵中寻找字符串](#3-在矩阵中寻找字符串) + * [4. 输出二叉树中所有从根到叶子的路径](#4-输出二叉树中所有从根到叶子的路径) + * [5. 排列](#5-排列) + * [6. 含有相同元素求排列](#6-含有相同元素求排列) + * [7. 组合](#7-组合) + * [8. 组合求和](#8-组合求和) + * [9. 含有相同元素的组合求和](#9-含有相同元素的组合求和) + * [10. 1-9 数字的组合求和](#10-1-9-数字的组合求和) + * [11. 子集](#11-子集) + * [12. 含有相同元素求子集](#12-含有相同元素求子集) + * [13. 分割字符串使得每个部分都是回文数](#13-分割字符串使得每个部分都是回文数) + * [14. 数独](#14-数独) + * [15. N 皇后](#15-n-皇后) + + + +深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。 + +## BFS + +

+ +广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。 + +第一层: + +- 0 -\> {6,2,1,5} + +第二层: + +- 6 -\> {4} +- 2 -\> {} +- 1 -\> {} +- 5 -\> {3} + +第三层: + +- 4 -\> {} +- 3 -\> {} + +每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。 + +在程序实现 BFS 时需要考虑以下问题: + +- 队列:用来存储每一轮遍历得到的节点; +- 标记:对于遍历过的节点,应该将它标记,防止重复遍历。 + +### 1. 计算在网格中从原点到特定点的最短路径长度 + +1091\. Shortest Path in Binary Matrix(Medium) + +[Leetcode](https://leetcode.com/problems/shortest-path-in-binary-matrix/) / [力扣](https://leetcode-cn.com/problems/shortest-path-in-binary-matrix/) + +```html +[[1,1,0,1], + [1,0,1,0], + [1,1,1,1], + [1,0,1,1]] +``` + +题目描述:0 表示可以经过某个位置,求解从左上角到右下角的最短路径长度。 + +```java +public int shortestPathBinaryMatrix(int[][] grids) { + if (grids == null || grids.length == 0 || grids[0].length == 0) { + return -1; + } + int[][] direction = {{1, -1}, {1, 0}, {1, 1}, {0, -1}, {0, 1}, {-1, -1}, {-1, 0}, {-1, 1}}; + int m = grids.length, n = grids[0].length; + Queue> queue = new LinkedList<>(); + queue.add(new Pair<>(0, 0)); + int pathLength = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + pathLength++; + while (size-- > 0) { + Pair cur = queue.poll(); + int cr = cur.getKey(), cc = cur.getValue(); + if (grids[cr][cc] == 1) { + continue; + } + if (cr == m - 1 && cc == n - 1) { + return pathLength; + } + grids[cr][cc] = 1; // 标记 + for (int[] d : direction) { + int nr = cr + d[0], nc = cc + d[1]; + if (nr < 0 || nr >= m || nc < 0 || nc >= n) { + continue; + } + queue.add(new Pair<>(nr, nc)); + } + } + } + return -1; + } +``` + +### 2. 组成整数的最小平方数数量 + +279\. Perfect Squares (Medium) + +[Leetcode](https://leetcode.com/problems/perfect-squares/description/) / [力扣](https://leetcode-cn.com/problems/perfect-squares/description/) + +```html +For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. +``` + +可以将每个整数看成图中的一个节点,如果两个整数之差为一个平方数,那么这两个整数所在的节点就有一条边。 + +要求解最小的平方数数量,就是求解从节点 n 到节点 0 的最短路径。 + +本题也可以用动态规划求解,在之后动态规划部分中会再次出现。 + +```java +public int numSquares(int n) { + List squares = generateSquares(n); + Queue queue = new LinkedList<>(); + boolean[] marked = new boolean[n + 1]; + queue.add(n); + marked[n] = true; + int level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + level++; + while (size-- > 0) { + int cur = queue.poll(); + for (int s : squares) { + int next = cur - s; + if (next < 0) { + break; + } + if (next == 0) { + return level; + } + if (marked[next]) { + continue; + } + marked[next] = true; + queue.add(next); + } + } + } + return n; +} + +/** + * 生成小于 n 的平方数序列 + * @return 1,4,9,... + */ +private List generateSquares(int n) { + List squares = new ArrayList<>(); + int square = 1; + int diff = 3; + while (square <= n) { + squares.add(square); + square += diff; + diff += 2; + } + return squares; +} +``` + +### 3. 最短单词路径 + +127\. Word Ladder (Medium) + +[Leetcode](https://leetcode.com/problems/word-ladder/description/) / [力扣](https://leetcode-cn.com/problems/word-ladder/description/) + +```html +Input: +beginWord = "hit", +endWord = "cog", +wordList = ["hot","dot","dog","lot","log","cog"] + +Output: 5 + +Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", +return its length 5. +``` + +```html +Input: +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log"] + +Output: 0 + +Explanation: The endWord "cog" is not in wordList, therefore no possible transformation. +``` + +题目描述:找出一条从 beginWord 到 endWord 的最短路径,每次移动规定为改变一个字符,并且改变之后的字符串必须在 wordList 中。 + +```java +public int ladderLength(String beginWord, String endWord, List wordList) { + wordList.add(beginWord); + int N = wordList.size(); + int start = N - 1; + int end = 0; + while (end < N && !wordList.get(end).equals(endWord)) { + end++; + } + if (end == N) { + return 0; + } + List[] graphic = buildGraphic(wordList); + return getShortestPath(graphic, start, end); +} + +private List[] buildGraphic(List wordList) { + int N = wordList.size(); + List[] graphic = new List[N]; + for (int i = 0; i < N; i++) { + graphic[i] = new ArrayList<>(); + for (int j = 0; j < N; j++) { + if (isConnect(wordList.get(i), wordList.get(j))) { + graphic[i].add(j); + } + } + } + return graphic; +} + +private boolean isConnect(String s1, String s2) { + int diffCnt = 0; + for (int i = 0; i < s1.length() && diffCnt <= 1; i++) { + if (s1.charAt(i) != s2.charAt(i)) { + diffCnt++; + } + } + return diffCnt == 1; +} + +private int getShortestPath(List[] graphic, int start, int end) { + Queue queue = new LinkedList<>(); + boolean[] marked = new boolean[graphic.length]; + queue.add(start); + marked[start] = true; + int path = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + path++; + while (size-- > 0) { + int cur = queue.poll(); + for (int next : graphic[cur]) { + if (next == end) { + return path; + } + if (marked[next]) { + continue; + } + marked[next] = true; + queue.add(next); + } + } + } + return 0; +} +``` + +## DFS + +

+ +广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。 + +而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 + +从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 + +在程序实现 DFS 时需要考虑以下问题: + +- 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。可以使用递归栈。 +- 标记:和 BFS 一样同样需要对已经遍历过的节点进行标记。 + +### 1. 查找最大的连通面积 + +695\. Max Area of Island (Medium) + +[Leetcode](https://leetcode.com/problems/max-area-of-island/description/) / [力扣](https://leetcode-cn.com/problems/max-area-of-island/description/) + +```html +[[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] +``` + +```java +private int m, n; +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public int maxAreaOfIsland(int[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + m = grid.length; + n = grid[0].length; + int maxArea = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + maxArea = Math.max(maxArea, dfs(grid, i, j)); + } + } + return maxArea; +} + +private int dfs(int[][] grid, int r, int c) { + if (r < 0 || r >= m || c < 0 || c >= n || grid[r][c] == 0) { + return 0; + } + grid[r][c] = 0; + int area = 1; + for (int[] d : direction) { + area += dfs(grid, r + d[0], c + d[1]); + } + return area; +} +``` + +### 2. 矩阵中的连通分量数目 + +200\. Number of Islands (Medium) + +[Leetcode](https://leetcode.com/problems/number-of-islands/description/) / [力扣](https://leetcode-cn.com/problems/number-of-islands/description/) + +```html +Input: +11000 +11000 +00100 +00011 + +Output: 3 +``` + +可以将矩阵表示看成一张有向图。 + +```java +private int m, n; +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0) { + return 0; + } + m = grid.length; + n = grid[0].length; + int islandsNum = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] != '0') { + dfs(grid, i, j); + islandsNum++; + } + } + } + return islandsNum; +} + +private void dfs(char[][] grid, int i, int j) { + if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') { + return; + } + grid[i][j] = '0'; + for (int[] d : direction) { + dfs(grid, i + d[0], j + d[1]); + } +} +``` + +### 3. 好友关系的连通分量数目 + +547\. Friend Circles (Medium) + +[Leetcode](https://leetcode.com/problems/friend-circles/description/) / [力扣](https://leetcode-cn.com/problems/friend-circles/description/) + +```html +Input: +[[1,1,0], + [1,1,0], + [0,0,1]] + +Output: 2 + +Explanation:The 0th and 1st students are direct friends, so they are in a friend circle. +The 2nd student himself is in a friend circle. So return 2. +``` + +题目描述:好友关系可以看成是一个无向图,例如第 0 个人与第 1 个人是好友,那么 M[0][1] 和 M[1][0] 的值都为 1。 + +```java +private int n; + +public int findCircleNum(int[][] M) { + n = M.length; + int circleNum = 0; + boolean[] hasVisited = new boolean[n]; + for (int i = 0; i < n; i++) { + if (!hasVisited[i]) { + dfs(M, i, hasVisited); + circleNum++; + } + } + return circleNum; +} + +private void dfs(int[][] M, int i, boolean[] hasVisited) { + hasVisited[i] = true; + for (int k = 0; k < n; k++) { + if (M[i][k] == 1 && !hasVisited[k]) { + dfs(M, k, hasVisited); + } + } +} +``` + +### 4. 填充封闭区域 + +130\. Surrounded Regions (Medium) + +[Leetcode](https://leetcode.com/problems/surrounded-regions/description/) / [力扣](https://leetcode-cn.com/problems/surrounded-regions/description/) + +```html +For example, +X X X X +X O O X +X X O X +X O X X + +After running your function, the board should be: +X X X X +X X X X +X X X X +X O X X +``` + +题目描述:使被 'X' 包围的 'O' 转换为 'X'。 + +先填充最外侧,剩下的就是里侧了。 + +```java +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; +private int m, n; + +public void solve(char[][] board) { + if (board == null || board.length == 0) { + return; + } + + m = board.length; + n = board[0].length; + + for (int i = 0; i < m; i++) { + dfs(board, i, 0); + dfs(board, i, n - 1); + } + for (int i = 0; i < n; i++) { + dfs(board, 0, i); + dfs(board, m - 1, i); + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'T') { + board[i][j] = 'O'; + } else if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + } + } +} + +private void dfs(char[][] board, int r, int c) { + if (r < 0 || r >= m || c < 0 || c >= n || board[r][c] != 'O') { + return; + } + board[r][c] = 'T'; + for (int[] d : direction) { + dfs(board, r + d[0], c + d[1]); + } +} +``` + +### 5. 能到达的太平洋和大西洋的区域 + +417\. Pacific Atlantic Water Flow (Medium) + +[Leetcode](https://leetcode.com/problems/pacific-atlantic-water-flow/description/) / [力扣](https://leetcode-cn.com/problems/pacific-atlantic-water-flow/description/) + +```html +Given the following 5x5 matrix: + + Pacific ~ ~ ~ ~ ~ + ~ 1 2 2 3 (5) * + ~ 3 2 3 (4) (4) * + ~ 2 4 (5) 3 1 * + ~ (6) (7) 1 4 5 * + ~ (5) 1 1 2 4 * + * * * * * Atlantic + +Return: +[[0, 4], [1, 3], [1, 4], [2, 2], [3, 0], [3, 1], [4, 0]] (positions with parentheses in above matrix). +``` + +左边和上边是太平洋,右边和下边是大西洋,内部的数字代表海拔,海拔高的地方的水能够流到低的地方,求解水能够流到太平洋和大西洋的所有位置。 + +```java +private int m, n; +private int[][] matrix; +private int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public List> pacificAtlantic(int[][] matrix) { + List> ret = new ArrayList<>(); + if (matrix == null || matrix.length == 0) { + return ret; + } + + m = matrix.length; + n = matrix[0].length; + this.matrix = matrix; + boolean[][] canReachP = new boolean[m][n]; + boolean[][] canReachA = new boolean[m][n]; + + for (int i = 0; i < m; i++) { + dfs(i, 0, canReachP); + dfs(i, n - 1, canReachA); + } + for (int i = 0; i < n; i++) { + dfs(0, i, canReachP); + dfs(m - 1, i, canReachA); + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (canReachP[i][j] && canReachA[i][j]) { + ret.add(Arrays.asList(i, j)); + } + } + } + + return ret; +} + +private void dfs(int r, int c, boolean[][] canReach) { + if (canReach[r][c]) { + return; + } + canReach[r][c] = true; + for (int[] d : direction) { + int nextR = d[0] + r; + int nextC = d[1] + c; + if (nextR < 0 || nextR >= m || nextC < 0 || nextC >= n + || matrix[r][c] > matrix[nextR][nextC]) { + + continue; + } + dfs(nextR, nextC, canReach); + } +} +``` + +## Backtracking + +Backtracking(回溯)属于 DFS。 + +- 普通 DFS 主要用在 **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。 +- 而 Backtracking 主要用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。 + +因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题: + +- 在访问一个新元素进入新的递归调用时,需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素; +- 但是在递归返回时,需要将元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,可以访问已经访问过但是不在当前递归链中的元素。 + +### 1. 数字键盘组合 + +17\. Letter Combinations of a Phone Number (Medium) + +[Leetcode](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/) / [力扣](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/description/) + +

+ +```html +Input:Digit string "23" +Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. +``` + +```java +private static final String[] KEYS = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + +public List letterCombinations(String digits) { + List combinations = new ArrayList<>(); + if (digits == null || digits.length() == 0) { + return combinations; + } + doCombination(new StringBuilder(), combinations, digits); + return combinations; +} + +private void doCombination(StringBuilder prefix, List combinations, final String digits) { + if (prefix.length() == digits.length()) { + combinations.add(prefix.toString()); + return; + } + int curDigits = digits.charAt(prefix.length()) - '0'; + String letters = KEYS[curDigits]; + for (char c : letters.toCharArray()) { + prefix.append(c); // 添加 + doCombination(prefix, combinations, digits); + prefix.deleteCharAt(prefix.length() - 1); // 删除 + } +} +``` + +### 2. IP 地址划分 + +93\. Restore IP Addresses(Medium) + +[Leetcode](https://leetcode.com/problems/restore-ip-addresses/description/) / [力扣](https://leetcode-cn.com/problems/restore-ip-addresses/description/) + +```html +Given "25525511135", +return ["255.255.11.135", "255.255.111.35"]. +``` + +```java +public List restoreIpAddresses(String s) { + List addresses = new ArrayList<>(); + StringBuilder tempAddress = new StringBuilder(); + doRestore(0, tempAddress, addresses, s); + return addresses; +} + +private void doRestore(int k, StringBuilder tempAddress, List addresses, String s) { + if (k == 4 || s.length() == 0) { + if (k == 4 && s.length() == 0) { + addresses.add(tempAddress.toString()); + } + return; + } + for (int i = 0; i < s.length() && i <= 2; i++) { + if (i != 0 && s.charAt(0) == '0') { + break; + } + String part = s.substring(0, i + 1); + if (Integer.valueOf(part) <= 255) { + if (tempAddress.length() != 0) { + part = "." + part; + } + tempAddress.append(part); + doRestore(k + 1, tempAddress, addresses, s.substring(i + 1)); + tempAddress.delete(tempAddress.length() - part.length(), tempAddress.length()); + } + } +} +``` + +### 3. 在矩阵中寻找字符串 + +79\. Word Search (Medium) + +[Leetcode](https://leetcode.com/problems/word-search/description/) / [力扣](https://leetcode-cn.com/problems/word-search/description/) + +```html +For example, +Given board = +[ + ['A','B','C','E'], + ['S','F','C','S'], + ['A','D','E','E'] +] +word = "ABCCED", -> returns true, +word = "SEE", -> returns true, +word = "ABCB", -> returns false. +``` + +```java +private final static int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; +private int m; +private int n; + +public boolean exist(char[][] board, String word) { + if (word == null || word.length() == 0) { + return true; + } + if (board == null || board.length == 0 || board[0].length == 0) { + return false; + } + + m = board.length; + n = board[0].length; + boolean[][] hasVisited = new boolean[m][n]; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (backtracking(0, r, c, hasVisited, board, word)) { + return true; + } + } + } + + return false; +} + +private boolean backtracking(int curLen, int r, int c, boolean[][] visited, final char[][] board, final String word) { + if (curLen == word.length()) { + return true; + } + if (r < 0 || r >= m || c < 0 || c >= n + || board[r][c] != word.charAt(curLen) || visited[r][c]) { + + return false; + } + + visited[r][c] = true; + + for (int[] d : direction) { + if (backtracking(curLen + 1, r + d[0], c + d[1], visited, board, word)) { + return true; + } + } + + visited[r][c] = false; + + return false; +} +``` + +### 4. 输出二叉树中所有从根到叶子的路径 + +257\. Binary Tree Paths (Easy) + +[Leetcode](https://leetcode.com/problems/binary-tree-paths/description/) / [力扣](https://leetcode-cn.com/problems/binary-tree-paths/description/) + +```html + 1 + / \ +2 3 + \ + 5 +``` + +```html +["1->2->5", "1->3"] +``` + +```java + +public List binaryTreePaths(TreeNode root) { + List paths = new ArrayList<>(); + if (root == null) { + return paths; + } + List values = new ArrayList<>(); + backtracking(root, values, paths); + return paths; +} + +private void backtracking(TreeNode node, List values, List paths) { + if (node == null) { + return; + } + values.add(node.val); + if (isLeaf(node)) { + paths.add(buildPath(values)); + } else { + backtracking(node.left, values, paths); + backtracking(node.right, values, paths); + } + values.remove(values.size() - 1); +} + +private boolean isLeaf(TreeNode node) { + return node.left == null && node.right == null; +} + +private String buildPath(List values) { + StringBuilder str = new StringBuilder(); + for (int i = 0; i < values.size(); i++) { + str.append(values.get(i)); + if (i != values.size() - 1) { + str.append("->"); + } + } + return str.toString(); +} +``` + +### 5. 排列 + +46\. Permutations (Medium) + +[Leetcode](https://leetcode.com/problems/permutations/description/) / [力扣](https://leetcode-cn.com/problems/permutations/description/) + +```html +[1,2,3] have the following permutations: +[ + [1,2,3], + [1,3,2], + [2,1,3], + [2,3,1], + [3,1,2], + [3,2,1] +] +``` + +```java +public List> permute(int[] nums) { + List> permutes = new ArrayList<>(); + List permuteList = new ArrayList<>(); + boolean[] hasVisited = new boolean[nums.length]; + backtracking(permuteList, permutes, hasVisited, nums); + return permutes; +} + +private void backtracking(List permuteList, List> permutes, boolean[] visited, final int[] nums) { + if (permuteList.size() == nums.length) { + permutes.add(new ArrayList<>(permuteList)); // 重新构造一个 List + return; + } + for (int i = 0; i < visited.length; i++) { + if (visited[i]) { + continue; + } + visited[i] = true; + permuteList.add(nums[i]); + backtracking(permuteList, permutes, visited, nums); + permuteList.remove(permuteList.size() - 1); + visited[i] = false; + } +} +``` + +### 6. 含有相同元素求排列 + +47\. Permutations II (Medium) + +[Leetcode](https://leetcode.com/problems/permutations-ii/description/) / [力扣](https://leetcode-cn.com/problems/permutations-ii/description/) + +```html +[1,1,2] have the following unique permutations: +[[1,1,2], [1,2,1], [2,1,1]] +``` + +数组元素可能含有相同的元素,进行排列时就有可能出现重复的排列,要求重复的排列只返回一个。 + +在实现上,和 Permutations 不同的是要先排序,然后在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。 + +```java +public List> permuteUnique(int[] nums) { + List> permutes = new ArrayList<>(); + List permuteList = new ArrayList<>(); + Arrays.sort(nums); // 排序 + boolean[] hasVisited = new boolean[nums.length]; + backtracking(permuteList, permutes, hasVisited, nums); + return permutes; +} + +private void backtracking(List permuteList, List> permutes, boolean[] visited, final int[] nums) { + if (permuteList.size() == nums.length) { + permutes.add(new ArrayList<>(permuteList)); + return; + } + + for (int i = 0; i < visited.length; i++) { + if (i != 0 && nums[i] == nums[i - 1] && !visited[i - 1]) { + continue; // 防止重复 + } + if (visited[i]){ + continue; + } + visited[i] = true; + permuteList.add(nums[i]); + backtracking(permuteList, permutes, visited, nums); + permuteList.remove(permuteList.size() - 1); + visited[i] = false; + } +} +``` + +### 7. 组合 + +77\. Combinations (Medium) + +[Leetcode](https://leetcode.com/problems/combinations/description/) / [力扣](https://leetcode-cn.com/problems/combinations/description/) + +```html +If n = 4 and k = 2, a solution is: +[ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], +] +``` + +```java +public List> combine(int n, int k) { + List> combinations = new ArrayList<>(); + List combineList = new ArrayList<>(); + backtracking(combineList, combinations, 1, k, n); + return combinations; +} + +private void backtracking(List combineList, List> combinations, int start, int k, final int n) { + if (k == 0) { + combinations.add(new ArrayList<>(combineList)); + return; + } + for (int i = start; i <= n - k + 1; i++) { // 剪枝 + combineList.add(i); + backtracking(combineList, combinations, i + 1, k - 1, n); + combineList.remove(combineList.size() - 1); + } +} +``` + +### 8. 组合求和 + +39\. Combination Sum (Medium) + +[Leetcode](https://leetcode.com/problems/combination-sum/description/) / [力扣](https://leetcode-cn.com/problems/combination-sum/description/) + +```html +given candidate set [2, 3, 6, 7] and target 7, +A solution set is: +[[7],[2, 2, 3]] +``` + +```java +public List> combinationSum(int[] candidates, int target) { + List> combinations = new ArrayList<>(); + backtracking(new ArrayList<>(), combinations, 0, target, candidates); + return combinations; +} + +private void backtracking(List tempCombination, List> combinations, + int start, int target, final int[] candidates) { + + if (target == 0) { + combinations.add(new ArrayList<>(tempCombination)); + return; + } + for (int i = start; i < candidates.length; i++) { + if (candidates[i] <= target) { + tempCombination.add(candidates[i]); + backtracking(tempCombination, combinations, i, target - candidates[i], candidates); + tempCombination.remove(tempCombination.size() - 1); + } + } +} +``` + +### 9. 含有相同元素的组合求和 + +40\. Combination Sum II (Medium) + +[Leetcode](https://leetcode.com/problems/combination-sum-ii/description/) / [力扣](https://leetcode-cn.com/problems/combination-sum-ii/description/) + +```html +For example, given candidate set [10, 1, 2, 7, 6, 1, 5] and target 8, +A solution set is: +[ + [1, 7], + [1, 2, 5], + [2, 6], + [1, 1, 6] +] +``` + +```java +public List> combinationSum2(int[] candidates, int target) { + List> combinations = new ArrayList<>(); + Arrays.sort(candidates); + backtracking(new ArrayList<>(), combinations, new boolean[candidates.length], 0, target, candidates); + return combinations; +} + +private void backtracking(List tempCombination, List> combinations, + boolean[] hasVisited, int start, int target, final int[] candidates) { + + if (target == 0) { + combinations.add(new ArrayList<>(tempCombination)); + return; + } + for (int i = start; i < candidates.length; i++) { + if (i != 0 && candidates[i] == candidates[i - 1] && !hasVisited[i - 1]) { + continue; + } + if (candidates[i] <= target) { + tempCombination.add(candidates[i]); + hasVisited[i] = true; + backtracking(tempCombination, combinations, hasVisited, i + 1, target - candidates[i], candidates); + hasVisited[i] = false; + tempCombination.remove(tempCombination.size() - 1); + } + } +} +``` + +### 10. 1-9 数字的组合求和 + +216\. Combination Sum III (Medium) + +[Leetcode](https://leetcode.com/problems/combination-sum-iii/description/) / [力扣](https://leetcode-cn.com/problems/combination-sum-iii/description/) + +```html +Input: k = 3, n = 9 + +Output: + +[[1,2,6], [1,3,5], [2,3,4]] +``` + +从 1-9 数字中选出 k 个数不重复的数,使得它们的和为 n。 + +```java +public List> combinationSum3(int k, int n) { + List> combinations = new ArrayList<>(); + List path = new ArrayList<>(); + backtracking(k, n, 1, path, combinations); + return combinations; +} + +private void backtracking(int k, int n, int start, + List tempCombination, List> combinations) { + + if (k == 0 && n == 0) { + combinations.add(new ArrayList<>(tempCombination)); + return; + } + if (k == 0 || n == 0) { + return; + } + for (int i = start; i <= 9; i++) { + tempCombination.add(i); + backtracking(k - 1, n - i, i + 1, tempCombination, combinations); + tempCombination.remove(tempCombination.size() - 1); + } +} +``` + +### 11. 子集 + +78\. Subsets (Medium) + +[Leetcode](https://leetcode.com/problems/subsets/description/) / [力扣](https://leetcode-cn.com/problems/subsets/description/) + +找出集合的所有子集,子集不能重复,[1, 2] 和 [2, 1] 这种子集算重复 + +```java +public List> subsets(int[] nums) { + List> subsets = new ArrayList<>(); + List tempSubset = new ArrayList<>(); + for (int size = 0; size <= nums.length; size++) { + backtracking(0, tempSubset, subsets, size, nums); // 不同的子集大小 + } + return subsets; +} + +private void backtracking(int start, List tempSubset, List> subsets, + final int size, final int[] nums) { + + if (tempSubset.size() == size) { + subsets.add(new ArrayList<>(tempSubset)); + return; + } + for (int i = start; i < nums.length; i++) { + tempSubset.add(nums[i]); + backtracking(i + 1, tempSubset, subsets, size, nums); + tempSubset.remove(tempSubset.size() - 1); + } +} +``` + +### 12. 含有相同元素求子集 + +90\. Subsets II (Medium) + +[Leetcode](https://leetcode.com/problems/subsets-ii/description/) / [力扣](https://leetcode-cn.com/problems/subsets-ii/description/) + +```html +For example, +If nums = [1,2,2], a solution is: + +[ + [2], + [1], + [1,2,2], + [2,2], + [1,2], + [] +] +``` + +```java +public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + List> subsets = new ArrayList<>(); + List tempSubset = new ArrayList<>(); + boolean[] hasVisited = new boolean[nums.length]; + for (int size = 0; size <= nums.length; size++) { + backtracking(0, tempSubset, subsets, hasVisited, size, nums); // 不同的子集大小 + } + return subsets; +} + +private void backtracking(int start, List tempSubset, List> subsets, boolean[] hasVisited, + final int size, final int[] nums) { + + if (tempSubset.size() == size) { + subsets.add(new ArrayList<>(tempSubset)); + return; + } + for (int i = start; i < nums.length; i++) { + if (i != 0 && nums[i] == nums[i - 1] && !hasVisited[i - 1]) { + continue; + } + tempSubset.add(nums[i]); + hasVisited[i] = true; + backtracking(i + 1, tempSubset, subsets, hasVisited, size, nums); + hasVisited[i] = false; + tempSubset.remove(tempSubset.size() - 1); + } +} +``` + +### 13. 分割字符串使得每个部分都是回文数 + +131\. Palindrome Partitioning (Medium) + +[Leetcode](https://leetcode.com/problems/palindrome-partitioning/description/) / [力扣](https://leetcode-cn.com/problems/palindrome-partitioning/description/) + +```html +For example, given s = "aab", +Return + +[ + ["aa","b"], + ["a","a","b"] +] +``` + +```java +public List> partition(String s) { + List> partitions = new ArrayList<>(); + List tempPartition = new ArrayList<>(); + doPartition(s, partitions, tempPartition); + return partitions; +} + +private void doPartition(String s, List> partitions, List tempPartition) { + if (s.length() == 0) { + partitions.add(new ArrayList<>(tempPartition)); + return; + } + for (int i = 0; i < s.length(); i++) { + if (isPalindrome(s, 0, i)) { + tempPartition.add(s.substring(0, i + 1)); + doPartition(s.substring(i + 1), partitions, tempPartition); + tempPartition.remove(tempPartition.size() - 1); + } + } +} + +private boolean isPalindrome(String s, int begin, int end) { + while (begin < end) { + if (s.charAt(begin++) != s.charAt(end--)) { + return false; + } + } + return true; +} +``` + +### 14. 数独 + +37\. Sudoku Solver (Hard) + +[Leetcode](https://leetcode.com/problems/sudoku-solver/description/) / [力扣](https://leetcode-cn.com/problems/sudoku-solver/description/) + +

+ +```java +private boolean[][] rowsUsed = new boolean[9][10]; +private boolean[][] colsUsed = new boolean[9][10]; +private boolean[][] cubesUsed = new boolean[9][10]; +private char[][] board; + +public void solveSudoku(char[][] board) { + this.board = board; + for (int i = 0; i < 9; i++) + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') { + continue; + } + int num = board[i][j] - '0'; + rowsUsed[i][num] = true; + colsUsed[j][num] = true; + cubesUsed[cubeNum(i, j)][num] = true; + } + backtracking(0, 0); +} + +private boolean backtracking(int row, int col) { + while (row < 9 && board[row][col] != '.') { + row = col == 8 ? row + 1 : row; + col = col == 8 ? 0 : col + 1; + } + if (row == 9) { + return true; + } + for (int num = 1; num <= 9; num++) { + if (rowsUsed[row][num] || colsUsed[col][num] || cubesUsed[cubeNum(row, col)][num]) { + continue; + } + rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = true; + board[row][col] = (char) (num + '0'); + if (backtracking(row, col)) { + return true; + } + board[row][col] = '.'; + rowsUsed[row][num] = colsUsed[col][num] = cubesUsed[cubeNum(row, col)][num] = false; + } + return false; +} + +private int cubeNum(int i, int j) { + int r = i / 3; + int c = j / 3; + return r * 3 + c; +} +``` + +### 15. N 皇后 + +51\. N-Queens (Hard) + +[Leetcode](https://leetcode.com/problems/n-queens/description/) / [力扣](https://leetcode-cn.com/problems/n-queens/description/) + +

+ +在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,求所有的 n 皇后的解。 + +一行一行地摆放,在确定一行中的那个皇后应该摆在哪一列时,需要用三个标记数组来确定某一列是否合法,这三个标记数组分别为:列标记数组、45 度对角线标记数组和 135 度对角线标记数组。 + +45 度对角线标记数组的长度为 2 \* n - 1,通过下图可以明确 (r, c) 的位置所在的数组下标为 r + c。 + +

+ + +135 度对角线标记数组的长度也是 2 \* n - 1,(r, c) 的位置所在的数组下标为 n - 1 - (r - c)。 + +

+ +```java +private List> solutions; +private char[][] nQueens; +private boolean[] colUsed; +private boolean[] diagonals45Used; +private boolean[] diagonals135Used; +private int n; + +public List> solveNQueens(int n) { + solutions = new ArrayList<>(); + nQueens = new char[n][n]; + for (int i = 0; i < n; i++) { + Arrays.fill(nQueens[i], '.'); + } + colUsed = new boolean[n]; + diagonals45Used = new boolean[2 * n - 1]; + diagonals135Used = new boolean[2 * n - 1]; + this.n = n; + backtracking(0); + return solutions; +} + +private void backtracking(int row) { + if (row == n) { + List list = new ArrayList<>(); + for (char[] chars : nQueens) { + list.add(new String(chars)); + } + solutions.add(list); + return; + } + + for (int col = 0; col < n; col++) { + int diagonals45Idx = row + col; + int diagonals135Idx = n - 1 - (row - col); + if (colUsed[col] || diagonals45Used[diagonals45Idx] || diagonals135Used[diagonals135Idx]) { + continue; + } + nQueens[row][col] = 'Q'; + colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = true; + backtracking(row + 1); + colUsed[col] = diagonals45Used[diagonals45Idx] = diagonals135Used[diagonals135Idx] = false; + nQueens[row][col] = '.'; + } +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" new file mode 100644 index 0000000000..d41ad654c6 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" @@ -0,0 +1,535 @@ +# Leetcode 题解 - 数学 + +* [Leetcode 题解 - 数学](#leetcode-题解---数学) + * [素数分解](#素数分解) + * [整除](#整除) + * [最大公约数最小公倍数](#最大公约数最小公倍数) + * [1. 生成素数序列](#1-生成素数序列) + * [2. 最大公约数](#2-最大公约数) + * [3. 使用位操作和减法求解最大公约数](#3-使用位操作和减法求解最大公约数) + * [进制转换](#进制转换) + * [1. 7 进制](#1-7-进制) + * [2. 16 进制](#2-16-进制) + * [3. 26 进制](#3-26-进制) + * [阶乘](#阶乘) + * [1. 统计阶乘尾部有多少个 0](#1-统计阶乘尾部有多少个-0) + * [字符串加法减法](#字符串加法减法) + * [1. 二进制加法](#1-二进制加法) + * [2. 字符串加法](#2-字符串加法) + * [相遇问题](#相遇问题) + * [1. 改变数组元素使所有的数组元素都相等](#1-改变数组元素使所有的数组元素都相等) + * [多数投票问题](#多数投票问题) + * [1. 数组中出现次数多于 n / 2 的元素](#1-数组中出现次数多于-n--2-的元素) + * [其它](#其它) + * [1. 平方数](#1-平方数) + * [2. 3 的 n 次方](#2-3-的-n-次方) + * [3. 乘积数组](#3-乘积数组) + * [4. 找出数组中的乘积最大的三个数](#4-找出数组中的乘积最大的三个数) + + + +## 素数分解 + +每一个数都可以分解成素数的乘积,例如 84 = 22 \* 31 \* 50 \* 71 \* 110 \* 130 \* 170 \* … + +## 整除 + +令 x = 2m0 \* 3m1 \* 5m2 \* 7m3 \* 11m4 \* … + +令 y = 2n0 \* 3n1 \* 5n2 \* 7n3 \* 11n4 \* … + +如果 x 整除 y(y mod x == 0),则对于所有 i,mi \<= ni。 + +## 最大公约数最小公倍数 + +x 和 y 的最大公约数为:gcd(x,y) = 2min(m0,n0) \* 3min(m1,n1) \* 5min(m2,n2) \* ... + +x 和 y 的最小公倍数为:lcm(x,y) = 2max(m0,n0) \* 3max(m1,n1) \* 5max(m2,n2) \* ... + +### 1. 生成素数序列 + +204\. Count Primes (Easy) + +[Leetcode](https://leetcode.com/problems/count-primes/description/) / [力扣](https://leetcode-cn.com/problems/count-primes/description/) + +埃拉托斯特尼筛法在每次找到一个素数时,将能被素数整除的数排除掉。 + +```java +public int countPrimes(int n) { + boolean[] notPrimes = new boolean[n + 1]; + int count = 0; + for (int i = 2; i < n; i++) { + if (notPrimes[i]) { + continue; + } + count++; + // 从 i * i 开始,因为如果 k < i,那么 k * i 在之前就已经被去除过了 + for (long j = (long) (i) * i; j < n; j += i) { + notPrimes[(int) j] = true; + } + } + return count; +} +``` + +### 2. 最大公约数 + +```java +int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); +} +``` + +最小公倍数为两数的乘积除以最大公约数。 + +```java +int lcm(int a, int b) { + return a * b / gcd(a, b); +} +``` + +### 3. 使用位操作和减法求解最大公约数 + +[编程之美:2.7](#) + +对于 a 和 b 的最大公约数 f(a, b),有: + +- 如果 a 和 b 均为偶数,f(a, b) = 2\*f(a/2, b/2); +- 如果 a 是偶数 b 是奇数,f(a, b) = f(a/2, b); +- 如果 b 是偶数 a 是奇数,f(a, b) = f(a, b/2); +- 如果 a 和 b 均为奇数,f(a, b) = f(b, a-b); + +乘 2 和除 2 都可以转换为移位操作。 + +```java +public int gcd(int a, int b) { + if (a < b) { + return gcd(b, a); + } + if (b == 0) { + return a; + } + boolean isAEven = isEven(a), isBEven = isEven(b); + if (isAEven && isBEven) { + return 2 * gcd(a >> 1, b >> 1); + } else if (isAEven && !isBEven) { + return gcd(a >> 1, b); + } else if (!isAEven && isBEven) { + return gcd(a, b >> 1); + } else { + return gcd(b, a - b); + } +} +``` + +## 进制转换 + +### 1. 7 进制 + +504\. Base 7 (Easy) + +[Leetcode](https://leetcode.com/problems/base-7/description/) / [力扣](https://leetcode-cn.com/problems/base-7/description/) + +```java +public String convertToBase7(int num) { + if (num == 0) { + return "0"; + } + StringBuilder sb = new StringBuilder(); + boolean isNegative = num < 0; + if (isNegative) { + num = -num; + } + while (num > 0) { + sb.append(num % 7); + num /= 7; + } + String ret = sb.reverse().toString(); + return isNegative ? "-" + ret : ret; +} +``` + +Java 中 static String toString(int num, int radix) 可以将一个整数转换为 radix 进制表示的字符串。 + +```java +public String convertToBase7(int num) { + return Integer.toString(num, 7); +} +``` + +### 2. 16 进制 + +405\. Convert a Number to Hexadecimal (Easy) + +[Leetcode](https://leetcode.com/problems/convert-a-number-to-hexadecimal/description/) / [力扣](https://leetcode-cn.com/problems/convert-a-number-to-hexadecimal/description/) + +```html +Input: +26 + +Output: +"1a" + +Input: +-1 + +Output: +"ffffffff" +``` + +负数要用它的补码形式。 + +```java +public String toHex(int num) { + char[] map = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + if (num == 0) return "0"; + StringBuilder sb = new StringBuilder(); + while (num != 0) { + sb.append(map[num & 0b1111]); + num >>>= 4; // 因为考虑的是补码形式,因此符号位就不能有特殊的意义,需要使用无符号右移,左边填 0 + } + return sb.reverse().toString(); +} +``` + +### 3. 26 进制 + +168\. Excel Sheet Column Title (Easy) + +[Leetcode](https://leetcode.com/problems/excel-sheet-column-title/description/) / [力扣](https://leetcode-cn.com/problems/excel-sheet-column-title/description/) + +```html +1 -> A +2 -> B +3 -> C +... +26 -> Z +27 -> AA +28 -> AB +``` + +因为是从 1 开始计算的,而不是从 0 开始,因此需要对 n 执行 -1 操作。 + +```java +public String convertToTitle(int n) { + if (n == 0) { + return ""; + } + n--; + return convertToTitle(n / 26) + (char) (n % 26 + 'A'); +} +``` + +## 阶乘 + +### 1. 统计阶乘尾部有多少个 0 + +172\. Factorial Trailing Zeroes (Easy) + +[Leetcode](https://leetcode.com/problems/factorial-trailing-zeroes/description/) / [力扣](https://leetcode-cn.com/problems/factorial-trailing-zeroes/description/) + +尾部的 0 由 2 * 5 得来,2 的数量明显多于 5 的数量,因此只要统计有多少个 5 即可。 + +对于一个数 N,它所包含 5 的个数为:N/5 + N/52 + N/53 + ...,其中 N/5 表示不大于 N 的数中 5 的倍数贡献一个 5,N/52 表示不大于 N 的数中 52 的倍数再贡献一个 5 ...。 + +```java +public int trailingZeroes(int n) { + return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5); +} +``` + +如果统计的是 N! 的二进制表示中最低位 1 的位置,只要统计有多少个 2 即可,该题目出自 [编程之美:2.2](#) 。和求解有多少个 5 一样,2 的个数为 N/2 + N/22 + N/23 + ... + +## 字符串加法减法 + +### 1. 二进制加法 + +67\. Add Binary (Easy) + +[Leetcode](https://leetcode.com/problems/add-binary/description/) / [力扣](https://leetcode-cn.com/problems/add-binary/description/) + +```html +a = "11" +b = "1" +Return "100". +``` + +```java +public String addBinary(String a, String b) { + int i = a.length() - 1, j = b.length() - 1, carry = 0; + StringBuilder str = new StringBuilder(); + while (carry == 1 || i >= 0 || j >= 0) { + if (i >= 0 && a.charAt(i--) == '1') { + carry++; + } + if (j >= 0 && b.charAt(j--) == '1') { + carry++; + } + str.append(carry % 2); + carry /= 2; + } + return str.reverse().toString(); +} +``` + +### 2. 字符串加法 + +415\. Add Strings (Easy) + +[Leetcode](https://leetcode.com/problems/add-strings/description/) / [力扣](https://leetcode-cn.com/problems/add-strings/description/) + +字符串的值为非负整数。 + +```java +public String addStrings(String num1, String num2) { + StringBuilder str = new StringBuilder(); + int carry = 0, i = num1.length() - 1, j = num2.length() - 1; + while (carry == 1 || i >= 0 || j >= 0) { + int x = i < 0 ? 0 : num1.charAt(i--) - '0'; + int y = j < 0 ? 0 : num2.charAt(j--) - '0'; + str.append((x + y + carry) % 10); + carry = (x + y + carry) / 10; + } + return str.reverse().toString(); +} +``` + +## 相遇问题 + +### 1. 改变数组元素使所有的数组元素都相等 + +462\. Minimum Moves to Equal Array Elements II (Medium) + +[Leetcode](https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/description/) / [力扣](https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements-ii/description/) + +```html +Input: +[1,2,3] + +Output: +2 + +Explanation: +Only two moves are needed (remember each move increments or decrements one element): + +[1,2,3] => [2,2,3] => [2,2,2] +``` + +每次可以对一个数组元素加一或者减一,求最小的改变次数。 + +这是个典型的相遇问题,移动距离最小的方式是所有元素都移动到中位数。理由如下: + +设 m 为中位数。a 和 b 是 m 两边的两个元素,且 b \> a。要使 a 和 b 相等,它们总共移动的次数为 b - a,这个值等于 (b - m) + (m - a),也就是把这两个数移动到中位数的移动次数。 + +设数组长度为 N,则可以找到 N/2 对 a 和 b 的组合,使它们都移动到 m 的位置。 + +**解法 1** + +先排序,时间复杂度:O(NlogN) + +```java +public int minMoves2(int[] nums) { + Arrays.sort(nums); + int move = 0; + int l = 0, h = nums.length - 1; + while (l <= h) { + move += nums[h] - nums[l]; + l++; + h--; + } + return move; +} +``` + +**解法 2** + +使用快速选择找到中位数,时间复杂度 O(N) + +```java +public int minMoves2(int[] nums) { + int move = 0; + int median = findKthSmallest(nums, nums.length / 2); + for (int num : nums) { + move += Math.abs(num - median); + } + return move; +} + +private int 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) { + l = j + 1; + } else { + h = j - 1; + } + } + return nums[k]; +} + +private int partition(int[] nums, int l, int h) { + int i = l, j = h + 1; + while (true) { + while (nums[++i] < nums[l] && i < h) ; + while (nums[--j] > nums[l] && j > l) ; + if (i >= j) { + break; + } + swap(nums, i, j); + } + swap(nums, l, j); + return j; +} + +private void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} +``` + +## 多数投票问题 + +### 1. 数组中出现次数多于 n / 2 的元素 + +169\. Majority Element (Easy) + +[Leetcode](https://leetcode.com/problems/majority-element/description/) / [力扣](https://leetcode-cn.com/problems/majority-element/description/) + +先对数组排序,最中间那个数出现次数一定多于 n / 2。 + +```java +public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length / 2]; +} +``` + +可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。可以这么理解该算法:使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2,因为如果多于 i / 2 的话 cnt 就一定不会为 0。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。 + +```java +public int majorityElement(int[] nums) { + int cnt = 0, majority = nums[0]; + for (int num : nums) { + majority = (cnt == 0) ? num : majority; + cnt = (majority == num) ? cnt + 1 : cnt - 1; + } + return majority; +} +``` + +## 其它 + +### 1. 平方数 + +367\. Valid Perfect Square (Easy) + +[Leetcode](https://leetcode.com/problems/valid-perfect-square/description/) / [力扣](https://leetcode-cn.com/problems/valid-perfect-square/description/) + +```html +Input: 16 +Returns: True +``` + +平方序列:1,4,9,16,.. + +间隔:3,5,7,... + +间隔为等差数列,使用这个特性可以得到从 1 开始的平方序列。 + +```java +public boolean isPerfectSquare(int num) { + int subNum = 1; + while (num > 0) { + num -= subNum; + subNum += 2; + } + return num == 0; +} +``` + +### 2. 3 的 n 次方 + +326\. Power of Three (Easy) + +[Leetcode](https://leetcode.com/problems/power-of-three/description/) / [力扣](https://leetcode-cn.com/problems/power-of-three/description/) + +```java +public boolean isPowerOfThree(int n) { + return n > 0 && (1162261467 % n == 0); +} +``` + +### 3. 乘积数组 + +238\. Product of Array Except Self (Medium) + +[Leetcode](https://leetcode.com/problems/product-of-array-except-self/description/) / [力扣](https://leetcode-cn.com/problems/product-of-array-except-self/description/) + +```html +For example, given [1,2,3,4], return [24,12,8,6]. +``` + +给定一个数组,创建一个新数组,新数组的每个元素为原始数组中除了该位置上的元素之外所有元素的乘积。 + +要求时间复杂度为 O(N),并且不能使用除法。 + +```java +public int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] products = new int[n]; + Arrays.fill(products, 1); + int left = 1; + for (int i = 1; i < n; i++) { + left *= nums[i - 1]; + products[i] *= left; + } + int right = 1; + for (int i = n - 2; i >= 0; i--) { + right *= nums[i + 1]; + products[i] *= right; + } + return products; +} +``` + +### 4. 找出数组中的乘积最大的三个数 + +628\. Maximum Product of Three Numbers (Easy) + +[Leetcode](https://leetcode.com/problems/maximum-product-of-three-numbers/description/) / [力扣](https://leetcode-cn.com/problems/maximum-product-of-three-numbers/description/) + +```html +Input: [1,2,3,4] +Output: 24 +``` + +```java +public int maximumProduct(int[] nums) { + int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE, max3 = Integer.MIN_VALUE, min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + for (int n : nums) { + if (n > max1) { + max3 = max2; + max2 = max1; + max1 = n; + } else if (n > max2) { + max3 = max2; + max2 = n; + } else if (n > max3) { + max3 = n; + } + + if (n < min1) { + min2 = min1; + min1 = n; + } else if (n < min2) { + min2 = n; + } + } + return Math.max(max1*max2*max3, max1*min1*min2); +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" new file mode 100644 index 0000000000..b88866a87f --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" @@ -0,0 +1,454 @@ +# Leetcode 题解 - 数组与矩阵 + +* [Leetcode 题解 - 数组与矩阵](#leetcode-题解---数组与矩阵) + * [1. 把数组中的 0 移到末尾](#1-把数组中的-0-移到末尾) + * [2. 改变矩阵维度](#2-改变矩阵维度) + * [3. 找出数组中最长的连续 1](#3-找出数组中最长的连续-1) + * [4. 有序矩阵查找](#4-有序矩阵查找) + * [5. 有序矩阵的 Kth Element](#5-有序矩阵的-kth-element) + * [6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数](#6-一个数组元素在-[1-n]-之间,其中一个数被替换为另一个数,找出重复的数和丢失的数) + * [7. 找出数组中重复的数,数组值在 [1, n] 之间](#7-找出数组中重复的数,数组值在-[1-n]-之间) + * [8. 数组相邻差值的个数](#8-数组相邻差值的个数) + * [9. 数组的度](#9-数组的度) + * [10. 对角元素相等的矩阵](#10-对角元素相等的矩阵) + * [11. 嵌套数组](#11-嵌套数组) + * [12. 分隔数组](#12-分隔数组) + + + +## 1. 把数组中的 0 移到末尾 + +283\. Move Zeroes (Easy) + +[Leetcode](https://leetcode.com/problems/move-zeroes/description/) / [力扣](https://leetcode-cn.com/problems/move-zeroes/description/) + +```html +For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0]. +``` + +```java +public void moveZeroes(int[] nums) { + int idx = 0; + for (int num : nums) { + if (num != 0) { + nums[idx++] = num; + } + } + while (idx < nums.length) { + nums[idx++] = 0; + } +} +``` + +## 2. 改变矩阵维度 + +566\. Reshape the Matrix (Easy) + +[Leetcode](https://leetcode.com/problems/reshape-the-matrix/description/) / [力扣](https://leetcode-cn.com/problems/reshape-the-matrix/description/) + +```html +Input: +nums = +[[1,2], + [3,4]] +r = 1, c = 4 + +Output: +[[1,2,3,4]] + +Explanation: +The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list. +``` + +```java +public int[][] matrixReshape(int[][] nums, int r, int c) { + int m = nums.length, n = nums[0].length; + if (m * n != r * c) { + return nums; + } + int[][] reshapedNums = new int[r][c]; + int index = 0; + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + reshapedNums[i][j] = nums[index / n][index % n]; + index++; + } + } + return reshapedNums; +} +``` + +## 3. 找出数组中最长的连续 1 + +485\. Max Consecutive Ones (Easy) + +[Leetcode](https://leetcode.com/problems/max-consecutive-ones/description/) / [力扣](https://leetcode-cn.com/problems/max-consecutive-ones/description/) + +```java +public int findMaxConsecutiveOnes(int[] nums) { + int max = 0, cur = 0; + for (int x : nums) { + cur = x == 0 ? 0 : cur + 1; + max = Math.max(max, cur); + } + return max; +} +``` + +## 4. 有序矩阵查找 + +240\. Search a 2D Matrix II (Medium) + +[Leetcode](https://leetcode.com/problems/search-a-2d-matrix-ii/description/) / [力扣](https://leetcode-cn.com/problems/search-a-2d-matrix-ii/description/) + +```html +[ + [ 1, 5, 9], + [10, 11, 13], + [12, 13, 15] +] +``` + +```java +public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false; + int m = matrix.length, n = matrix[0].length; + int row = 0, col = n - 1; + while (row < m && col >= 0) { + if (target == matrix[row][col]) return true; + else if (target < matrix[row][col]) col--; + else row++; + } + return false; +} +``` + +## 5. 有序矩阵的 Kth Element + +378\. Kth Smallest Element in a Sorted Matrix ((Medium)) + +[Leetcode](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/) / [力扣](https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix/description/) + +```html +matrix = [ + [ 1, 5, 9], + [10, 11, 13], + [12, 13, 15] +], +k = 8, + +return 13. +``` + +解题参考:[Share my thoughts and Clean Java Code](https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix/discuss/85173) + +二分查找解法: + +```java +public int kthSmallest(int[][] matrix, int k) { + int m = matrix.length, n = matrix[0].length; + int lo = matrix[0][0], hi = matrix[m - 1][n - 1]; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + int cnt = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n && matrix[i][j] <= mid; j++) { + cnt++; + } + } + if (cnt < k) lo = mid + 1; + else hi = mid - 1; + } + return lo; +} +``` + +堆解法: + +```java +public int kthSmallest(int[][] matrix, int k) { + int m = matrix.length, n = matrix[0].length; + PriorityQueue pq = new PriorityQueue(); + for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j])); + for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数 + Tuple t = pq.poll(); + if(t.x == m - 1) continue; + pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y])); + } + return pq.poll().val; +} + +class Tuple implements Comparable { + int x, y, val; + public Tuple(int x, int y, int val) { + this.x = x; this.y = y; this.val = val; + } + + @Override + public int compareTo(Tuple that) { + return this.val - that.val; + } +} +``` + +## 6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数 + +645\. Set Mismatch (Easy) + +[Leetcode](https://leetcode.com/problems/set-mismatch/description/) / [力扣](https://leetcode-cn.com/problems/set-mismatch/description/) + +```html +Input: nums = [1,2,2,4] +Output: [2,3] +``` + +```html +Input: nums = [1,2,2,4] +Output: [2,3] +``` + +最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(NlogN)。本题可以以 O(N) 的时间复杂度、O(1) 空间复杂度来求解。 + +主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。 + +```java +public int[] findErrorNums(int[] nums) { + for (int i = 0; i < nums.length; i++) { + while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) { + swap(nums, i, nums[i] - 1); + } + } + for (int i = 0; i < nums.length; i++) { + if (nums[i] != i + 1) { + return new int[]{nums[i], i + 1}; + } + } + return null; +} + +private void swap(int[] nums, int i, int j) { + int tmp = nums[i]; + nums[i] = nums[j]; + nums[j] = tmp; +} +``` + +## 7. 找出数组中重复的数,数组值在 [1, n] 之间 + +287\. Find the Duplicate Number (Medium) + +[Leetcode](https://leetcode.com/problems/find-the-duplicate-number/description/) / [力扣](https://leetcode-cn.com/problems/find-the-duplicate-number/description/) + +要求不能修改数组,也不能使用额外的空间。 + +二分查找解法: + +```java +public int findDuplicate(int[] nums) { + int l = 1, h = nums.length - 1; + while (l <= h) { + int mid = l + (h - l) / 2; + int cnt = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] <= mid) cnt++; + } + if (cnt > mid) h = mid - 1; + else l = mid + 1; + } + return l; +} +``` + +双指针解法,类似于有环链表中找出环的入口: + +```java +public int findDuplicate(int[] nums) { + int slow = nums[0], fast = nums[nums[0]]; + while (slow != fast) { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + fast = 0; + while (slow != fast) { + slow = nums[slow]; + fast = nums[fast]; + } + return slow; +} +``` + +## 8. 数组相邻差值的个数 + +667\. Beautiful Arrangement II (Medium) + +[Leetcode](https://leetcode.com/problems/beautiful-arrangement-ii/description/) / [力扣](https://leetcode-cn.com/problems/beautiful-arrangement-ii/description/) + +```html +Input: n = 3, k = 2 +Output: [1, 3, 2] +Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2. +``` + +题目描述:数组元素为 1\~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。 + +让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 ... k/2 k/2+1. + +```java +public int[] constructArray(int n, int k) { + int[] ret = new int[n]; + ret[0] = 1; + for (int i = 1, interval = k; i <= k; i++, interval--) { + ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval; + } + for (int i = k + 1; i < n; i++) { + ret[i] = i + 1; + } + return ret; +} +``` + +## 9. 数组的度 + +697\. Degree of an Array (Easy) + +[Leetcode](https://leetcode.com/problems/degree-of-an-array/description/) / [力扣](https://leetcode-cn.com/problems/degree-of-an-array/description/) + +```html +Input: [1,2,2,3,1,4,2] +Output: 6 +``` + +题目描述:数组的度定义为元素出现的最高频率,例如上面的数组度为 3。要求找到一个最小的子数组,这个子数组的度和原数组一样。 + +```java +public int findShortestSubArray(int[] nums) { + Map numsCnt = new HashMap<>(); + Map numsLastIndex = new HashMap<>(); + Map numsFirstIndex = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1); + numsLastIndex.put(num, i); + if (!numsFirstIndex.containsKey(num)) { + numsFirstIndex.put(num, i); + } + } + int maxCnt = 0; + for (int num : nums) { + maxCnt = Math.max(maxCnt, numsCnt.get(num)); + } + int ret = nums.length; + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + int cnt = numsCnt.get(num); + if (cnt != maxCnt) continue; + ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1); + } + return ret; +} +``` + +## 10. 对角元素相等的矩阵 + +766\. Toeplitz Matrix (Easy) + +[Leetcode](https://leetcode.com/problems/toeplitz-matrix/description/) / [力扣](https://leetcode-cn.com/problems/toeplitz-matrix/description/) + +```html +1234 +5123 +9512 + +In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", and in each diagonal all elements are the same, so the answer is True. +``` + +```java +public boolean isToeplitzMatrix(int[][] matrix) { + for (int i = 0; i < matrix[0].length; i++) { + if (!check(matrix, matrix[0][i], 0, i)) { + return false; + } + } + for (int i = 0; i < matrix.length; i++) { + if (!check(matrix, matrix[i][0], i, 0)) { + return false; + } + } + return true; +} + +private boolean check(int[][] matrix, int expectValue, int row, int col) { + if (row >= matrix.length || col >= matrix[0].length) { + return true; + } + if (matrix[row][col] != expectValue) { + return false; + } + return check(matrix, expectValue, row + 1, col + 1); +} +``` + +## 11. 嵌套数组 + +565\. Array Nesting (Medium) + +[Leetcode](https://leetcode.com/problems/array-nesting/description/) / [力扣](https://leetcode-cn.com/problems/array-nesting/description/) + +```html +Input: A = [5,4,0,3,1,6,2] +Output: 4 +Explanation: +A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2. + +One of the longest S[K]: +S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0} +``` + +题目描述:S[i] 表示一个集合,集合的第一个元素是 A[i],第二个元素是 A[A[i]],如此嵌套下去。求最大的 S[i]。 + +```java +public int arrayNesting(int[] nums) { + int max = 0; + for (int i = 0; i < nums.length; i++) { + int cnt = 0; + for (int j = i; nums[j] != -1; ) { + cnt++; + int t = nums[j]; + nums[j] = -1; // 标记该位置已经被访问 + j = t; + + } + max = Math.max(max, cnt); + } + return max; +} +``` + +## 12. 分隔数组 + +769\. Max Chunks To Make Sorted (Medium) + +[Leetcode](https://leetcode.com/problems/max-chunks-to-make-sorted/description/) / [力扣](https://leetcode-cn.com/problems/max-chunks-to-make-sorted/description/) + +```html +Input: arr = [1,0,2,3,4] +Output: 4 +Explanation: +We can split into two chunks, such as [1, 0], [2, 3, 4]. +However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible. +``` + +题目描述:分隔数组,使得对每部分排序后数组就为有序。 + +```java +public int maxChunksToSorted(int[] arr) { + if (arr == null) return 0; + int ret = 0; + int right = arr[0]; + for (int i = 0; i < arr.length; i++) { + right = Math.max(right, arr[i]); + if (right == i) ret++; + } + return ret; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" new file mode 100644 index 0000000000..c88465e75c --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" @@ -0,0 +1,234 @@ +# Leetcode 题解 - 栈和队列 + +* [Leetcode 题解 - 栈和队列](#leetcode-题解---栈和队列) + * [1. 用栈实现队列](#1-用栈实现队列) + * [2. 用队列实现栈](#2-用队列实现栈) + * [3. 最小值栈](#3-最小值栈) + * [4. 用栈实现括号匹配](#4-用栈实现括号匹配) + * [5. 数组中元素与下一个比它大的元素之间的距离](#5-数组中元素与下一个比它大的元素之间的距离) + * [6. 循环数组中比当前元素大的下一个元素](#6-循环数组中比当前元素大的下一个元素) + + + +## 1. 用栈实现队列 + +232\. Implement Queue using Stacks (Easy) + +[Leetcode](https://leetcode.com/problems/implement-queue-using-stacks/description/) / [力扣](https://leetcode-cn.com/problems/implement-queue-using-stacks/description/) + +栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。 + +```java +class MyQueue { + + private Stack in = new Stack<>(); + private Stack out = new Stack<>(); + + public void push(int x) { + in.push(x); + } + + public int pop() { + in2out(); + return out.pop(); + } + + public int peek() { + in2out(); + return out.peek(); + } + + private void in2out() { + if (out.isEmpty()) { + while (!in.isEmpty()) { + out.push(in.pop()); + } + } + } + + public boolean empty() { + return in.isEmpty() && out.isEmpty(); + } +} +``` + +## 2. 用队列实现栈 + +225\. Implement Stack using Queues (Easy) + +[Leetcode](https://leetcode.com/problems/implement-stack-using-queues/description/) / [力扣](https://leetcode-cn.com/problems/implement-stack-using-queues/description/) + +在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。 + +```java +class MyStack { + + private Queue queue; + + public MyStack() { + queue = new LinkedList<>(); + } + + public void push(int x) { + queue.add(x); + int cnt = queue.size(); + while (cnt-- > 1) { + queue.add(queue.poll()); + } + } + + public int pop() { + return queue.remove(); + } + + public int top() { + return queue.peek(); + } + + public boolean empty() { + return queue.isEmpty(); + } +} +``` + +## 3. 最小值栈 + +155\. Min Stack (Easy) + +[Leetcode](https://leetcode.com/problems/min-stack/description/) / [力扣](https://leetcode-cn.com/problems/min-stack/description/) + +```java +class MinStack { + + private Stack dataStack; + private Stack minStack; + private int min; + + public MinStack() { + dataStack = new Stack<>(); + minStack = new Stack<>(); + min = Integer.MAX_VALUE; + } + + public void push(int x) { + dataStack.add(x); + min = Math.min(min, x); + minStack.add(min); + } + + public void pop() { + dataStack.pop(); + minStack.pop(); + min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek(); + } + + public int top() { + return dataStack.peek(); + } + + public int getMin() { + return minStack.peek(); + } +} +``` + +对于实现最小值队列问题,可以先将队列使用栈来实现,然后就将问题转换为最小值栈,这个问题出现在 编程之美:3.7。 + +## 4. 用栈实现括号匹配 + +20\. Valid Parentheses (Easy) + +[Leetcode](https://leetcode.com/problems/valid-parentheses/description/) / [力扣](https://leetcode-cn.com/problems/valid-parentheses/description/) + +```html +"()[]{}" + +Output : true +``` + +```java +public boolean isValid(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '(' || c == '{' || c == '[') { + stack.push(c); + } else { + if (stack.isEmpty()) { + return false; + } + char cStack = stack.pop(); + boolean b1 = c == ')' && cStack != '('; + boolean b2 = c == ']' && cStack != '['; + boolean b3 = c == '}' && cStack != '{'; + if (b1 || b2 || b3) { + return false; + } + } + } + return stack.isEmpty(); +} +``` + +## 5. 数组中元素与下一个比它大的元素之间的距离 + +739\. Daily Temperatures (Medium) + +[Leetcode](https://leetcode.com/problems/daily-temperatures/description/) / [力扣](https://leetcode-cn.com/problems/daily-temperatures/description/) + +```html +Input: [73, 74, 75, 71, 69, 72, 76, 73] +Output: [1, 1, 4, 2, 1, 1, 0, 0] +``` + +在遍历数组时用栈把数组中的数存起来,如果当前遍历的数比栈顶元素来的大,说明栈顶元素的下一个比它大的数就是当前元素。 + +```java +public int[] dailyTemperatures(int[] temperatures) { + int n = temperatures.length; + int[] dist = new int[n]; + Stack indexs = new Stack<>(); + for (int curIndex = 0; curIndex < n; curIndex++) { + while (!indexs.isEmpty() && temperatures[curIndex] > temperatures[indexs.peek()]) { + int preIndex = indexs.pop(); + dist[preIndex] = curIndex - preIndex; + } + indexs.add(curIndex); + } + return dist; +} +``` + +## 6. 循环数组中比当前元素大的下一个元素 + +503\. Next Greater Element II (Medium) + +[Leetcode](https://leetcode.com/problems/next-greater-element-ii/description/) / [力扣](https://leetcode-cn.com/problems/next-greater-element-ii/description/) + +```text +Input: [1,2,1] +Output: [2,-1,2] +Explanation: The first 1's next greater number is 2; +The number 2 can't find next greater number; +The second 1's next greater number needs to search circularly, which is also 2. +``` + +与 739. Daily Temperatures (Medium) 不同的是,数组是循环数组,并且最后要求的不是距离而是下一个元素。 + +```java +public int[] nextGreaterElements(int[] nums) { + int n = nums.length; + int[] next = new int[n]; + Arrays.fill(next, -1); + Stack pre = new Stack<>(); + for (int i = 0; i < n * 2; i++) { + int num = nums[i % n]; + while (!pre.isEmpty() && nums[pre.peek()] < num) { + next[pre.pop()] = num; + } + if (i < n){ + pre.push(i); + } + } + return next; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" new file mode 100644 index 0000000000..c024393961 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\221.md" @@ -0,0 +1,1189 @@ +# Leetcode 题解 - 树 + +* [Leetcode 题解 - 树](#leetcode-题解---树) + * [递归](#递归) + * [1. 树的高度](#1-树的高度) + * [2. 平衡树](#2-平衡树) + * [3. 两节点的最长路径](#3-两节点的最长路径) + * [4. 翻转树](#4-翻转树) + * [5. 归并两棵树](#5-归并两棵树) + * [6. 判断路径和是否等于一个数](#6-判断路径和是否等于一个数) + * [7. 统计路径和等于一个数的路径数量](#7-统计路径和等于一个数的路径数量) + * [8. 子树](#8-子树) + * [9. 树的对称](#9-树的对称) + * [10. 最小路径](#10-最小路径) + * [11. 统计左叶子节点的和](#11-统计左叶子节点的和) + * [12. 相同节点值的最大路径长度](#12-相同节点值的最大路径长度) + * [13. 间隔遍历](#13-间隔遍历) + * [14. 找出二叉树中第二小的节点](#14-找出二叉树中第二小的节点) + * [层次遍历](#层次遍历) + * [1. 一棵树每层节点的平均数](#1-一棵树每层节点的平均数) + * [2. 得到左下角的节点](#2-得到左下角的节点) + * [前中后序遍历](#前中后序遍历) + * [1. 非递归实现二叉树的前序遍历](#1-非递归实现二叉树的前序遍历) + * [2. 非递归实现二叉树的后序遍历](#2-非递归实现二叉树的后序遍历) + * [3. 非递归实现二叉树的中序遍历](#3-非递归实现二叉树的中序遍历) + * [BST](#bst) + * [1. 修剪二叉查找树](#1-修剪二叉查找树) + * [2. 寻找二叉查找树的第 k 个元素](#2-寻找二叉查找树的第-k-个元素) + * [3. 把二叉查找树每个节点的值都加上比它大的节点的值](#3-把二叉查找树每个节点的值都加上比它大的节点的值) + * [4. 二叉查找树的最近公共祖先](#4-二叉查找树的最近公共祖先) + * [5. 二叉树的最近公共祖先](#5-二叉树的最近公共祖先) + * [6. 从有序数组中构造二叉查找树](#6-从有序数组中构造二叉查找树) + * [7. 根据有序链表构造平衡的二叉查找树](#7-根据有序链表构造平衡的二叉查找树) + * [8. 在二叉查找树中寻找两个节点,使它们的和为一个给定值](#8-在二叉查找树中寻找两个节点,使它们的和为一个给定值) + * [9. 在二叉查找树中查找两个节点之差的最小绝对值](#9-在二叉查找树中查找两个节点之差的最小绝对值) + * [10. 寻找二叉查找树中出现次数最多的值](#10-寻找二叉查找树中出现次数最多的值) + * [Trie](#trie) + * [1. 实现一个 Trie](#1-实现一个-trie) + * [2. 实现一个 Trie,用来求前缀和](#2-实现一个-trie,用来求前缀和) + + + +## 递归 + +一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。 + +### 1. 树的高度 + +104\. Maximum Depth of Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/maximum-depth-of-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/description/) + +```java +public int maxDepth(TreeNode root) { + if (root == null) return 0; + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +} +``` + +### 2. 平衡树 + +110\. Balanced Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/balanced-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/balanced-binary-tree/description/) + +```html + 3 + / \ + 9 20 + / \ + 15 7 +``` + +平衡树左右子树高度差都小于等于 1 + +```java +private boolean result = true; + +public boolean isBalanced(TreeNode root) { + maxDepth(root); + return result; +} + +public int maxDepth(TreeNode root) { + if (root == null) return 0; + int l = maxDepth(root.left); + int r = maxDepth(root.right); + if (Math.abs(l - r) > 1) result = false; + return 1 + Math.max(l, r); +} +``` + +### 3. 两节点的最长路径 + +543\. Diameter of Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/diameter-of-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/diameter-of-binary-tree/description/) + +```html +Input: + + 1 + / \ + 2 3 + / \ + 4 5 + +Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. +``` + +```java +private int max = 0; + +public int diameterOfBinaryTree(TreeNode root) { + depth(root); + return max; +} + +private int depth(TreeNode root) { + if (root == null) return 0; + int leftDepth = depth(root.left); + int rightDepth = depth(root.right); + max = Math.max(max, leftDepth + rightDepth); + return Math.max(leftDepth, rightDepth) + 1; +} +``` + +### 4. 翻转树 + +226\. Invert Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/invert-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/invert-binary-tree/description/) + +```java +public TreeNode invertTree(TreeNode root) { + if (root == null) return null; + TreeNode left = root.left; // 后面的操作会改变 left 指针,因此先保存下来 + root.left = invertTree(root.right); + root.right = invertTree(left); + return root; +} +``` + +### 5. 归并两棵树 + +617\. Merge Two Binary Trees (Easy) + +[Leetcode](https://leetcode.com/problems/merge-two-binary-trees/description/) / [力扣](https://leetcode-cn.com/problems/merge-two-binary-trees/description/) + +```html +Input: + Tree 1 Tree 2 + 1 2 + / \ / \ + 3 2 1 3 + / \ \ + 5 4 7 + +Output: + 3 + / \ + 4 5 + / \ \ + 5 4 7 +``` + +```java +public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { + if (t1 == null && t2 == null) return null; + if (t1 == null) return t2; + if (t2 == null) return t1; + TreeNode root = new TreeNode(t1.val + t2.val); + root.left = mergeTrees(t1.left, t2.left); + root.right = mergeTrees(t1.right, t2.right); + return root; +} +``` + +### 6. 判断路径和是否等于一个数 + +Leetcdoe : 112. Path Sum (Easy) + +[Leetcode](https://leetcode.com/problems/path-sum/description/) / [力扣](https://leetcode-cn.com/problems/path-sum/description/) + +```html +Given the below binary tree and sum = 22, + + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ \ + 7 2 1 + +return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22. +``` + +路径和定义为从 root 到 leaf 的所有节点的和。 + +```java +public boolean hasPathSum(TreeNode root, int sum) { + if (root == null) return false; + if (root.left == null && root.right == null && root.val == sum) return true; + return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); +} +``` + +### 7. 统计路径和等于一个数的路径数量 + +437\. Path Sum III (Easy) + +[Leetcode](https://leetcode.com/problems/path-sum-iii/description/) / [力扣](https://leetcode-cn.com/problems/path-sum-iii/description/) + +```html +root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 + + 10 + / \ + 5 -3 + / \ \ + 3 2 11 + / \ \ +3 -2 1 + +Return 3. The paths that sum to 8 are: + +1. 5 -> 3 +2. 5 -> 2 -> 1 +3. -3 -> 11 +``` + +路径不一定以 root 开头,也不一定以 leaf 结尾,但是必须连续。 + +```java +public int pathSum(TreeNode root, int sum) { + if (root == null) return 0; + int ret = pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum); + return ret; +} + +private int pathSumStartWithRoot(TreeNode root, int sum) { + if (root == null) return 0; + int ret = 0; + if (root.val == sum) ret++; + ret += pathSumStartWithRoot(root.left, sum - root.val) + pathSumStartWithRoot(root.right, sum - root.val); + return ret; +} +``` + +### 8. 子树 + +572\. Subtree of Another Tree (Easy) + +[Leetcode](https://leetcode.com/problems/subtree-of-another-tree/description/) / [力扣](https://leetcode-cn.com/problems/subtree-of-another-tree/description/) + +```html +Given tree s: + 3 + / \ + 4 5 + / \ + 1 2 + +Given tree t: + 4 + / \ + 1 2 + +Return true, because t has the same structure and node values with a subtree of s. + +Given tree s: + + 3 + / \ + 4 5 + / \ + 1 2 + / + 0 + +Given tree t: + 4 + / \ + 1 2 + +Return false. +``` + +```java +public boolean isSubtree(TreeNode s, TreeNode t) { + if (s == null) return false; + return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); +} + +private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) { + if (t == null && s == null) return true; + if (t == null || s == null) return false; + if (t.val != s.val) return false; + return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right); +} +``` + +### 9. 树的对称 + +101\. Symmetric Tree (Easy) + +[Leetcode](https://leetcode.com/problems/symmetric-tree/description/) / [力扣](https://leetcode-cn.com/problems/symmetric-tree/description/) + +```html + 1 + / \ + 2 2 + / \ / \ +3 4 4 3 +``` + +```java +public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + return isSymmetric(root.left, root.right); +} + +private boolean isSymmetric(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 isSymmetric(t1.left, t2.right) && isSymmetric(t1.right, t2.left); +} +``` + +### 10. 最小路径 + +111\. Minimum Depth of Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/minimum-depth-of-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/description/) + +树的根节点到叶子节点的最小路径长度 + +```java +public int minDepth(TreeNode root) { + if (root == null) return 0; + int left = minDepth(root.left); + int right = minDepth(root.right); + if (left == 0 || right == 0) return left + right + 1; + return Math.min(left, right) + 1; +} +``` + +### 11. 统计左叶子节点的和 + +404\. Sum of Left Leaves (Easy) + +[Leetcode](https://leetcode.com/problems/sum-of-left-leaves/description/) / [力扣](https://leetcode-cn.com/problems/sum-of-left-leaves/description/) + +```html + 3 + / \ + 9 20 + / \ + 15 7 + +There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24. +``` + +```java +public int sumOfLeftLeaves(TreeNode root) { + if (root == null) return 0; + if (isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right); + return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right); +} + +private boolean isLeaf(TreeNode node){ + if (node == null) return false; + return node.left == null && node.right == null; +} +``` + +### 12. 相同节点值的最大路径长度 + +687\. Longest Univalue Path (Easy) + +[Leetcode](https://leetcode.com/problems/longest-univalue-path/) / [力扣](https://leetcode-cn.com/problems/longest-univalue-path/) + +```html + 1 + / \ + 4 5 + / \ \ + 4 4 5 + +Output : 2 +``` + +```java +private int path = 0; + +public int longestUnivaluePath(TreeNode root) { + dfs(root); + return path; +} + +private int dfs(TreeNode root){ + if (root == null) return 0; + int left = dfs(root.left); + int right = dfs(root.right); + int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0; + int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0; + path = Math.max(path, leftPath + rightPath); + return Math.max(leftPath, rightPath); +} +``` + +### 13. 间隔遍历 + +337\. House Robber III (Medium) + +[Leetcode](https://leetcode.com/problems/house-robber-iii/description/) / [力扣](https://leetcode-cn.com/problems/house-robber-iii/description/) + +```html + 3 + / \ + 2 3 + \ \ + 3 1 +Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. +``` + +```java +Map cache = new HashMap<>(); + +public int rob(TreeNode root) { + if (root == null) return 0; + if (cache.containsKey(root)) return cache.get(root); + int val1 = root.val; + if (root.left != null) val1 += rob(root.left.left) + rob(root.left.right); + if (root.right != null) val1 += rob(root.right.left) + rob(root.right.right); + int val2 = rob(root.left) + rob(root.right); + int res = Math.max(val1, val2); + cache.put(root, res); + return res; +} +``` + +### 14. 找出二叉树中第二小的节点 + +671\. Second Minimum Node In a Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/second-minimum-node-in-a-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree/description/) + +```html +Input: + 2 + / \ + 2 5 + / \ + 5 7 + +Output: 5 +``` + +一个节点要么具有 0 个或 2 个子节点,如果有子节点,那么根节点是最小的节点。 + +```java +public int findSecondMinimumValue(TreeNode root) { + if (root == null) return -1; + if (root.left == null && root.right == null) return -1; + int leftVal = root.left.val; + int rightVal = root.right.val; + if (leftVal == root.val) leftVal = findSecondMinimumValue(root.left); + if (rightVal == root.val) rightVal = findSecondMinimumValue(root.right); + if (leftVal != -1 && rightVal != -1) return Math.min(leftVal, rightVal); + if (leftVal != -1) return leftVal; + return rightVal; +} +``` + +## 层次遍历 + +使用 BFS 进行层次遍历。不需要使用两个队列来分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 + +### 1. 一棵树每层节点的平均数 + +637\. Average of Levels in Binary Tree (Easy) + +[Leetcode](https://leetcode.com/problems/average-of-levels-in-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/description/) + +```java +public List averageOfLevels(TreeNode root) { + List ret = new ArrayList<>(); + if (root == null) return ret; + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + int cnt = queue.size(); + double sum = 0; + for (int i = 0; i < cnt; i++) { + TreeNode node = queue.poll(); + sum += node.val; + if (node.left != null) queue.add(node.left); + if (node.right != null) queue.add(node.right); + } + ret.add(sum / cnt); + } + return ret; +} +``` + +### 2. 得到左下角的节点 + +513\. Find Bottom Left Tree Value (Easy) + +[Leetcode](https://leetcode.com/problems/find-bottom-left-tree-value/description/) / [力扣](https://leetcode-cn.com/problems/find-bottom-left-tree-value/description/) + +```html +Input: + + 1 + / \ + 2 3 + / / \ + 4 5 6 + / + 7 + +Output: +7 +``` + +```java +public int findBottomLeftValue(TreeNode root) { + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + root = queue.poll(); + if (root.right != null) queue.add(root.right); + if (root.left != null) queue.add(root.left); + } + return root.val; +} +``` + +## 前中后序遍历 + +```html + 1 + / \ + 2 3 + / \ \ +4 5 6 +``` + +- 层次遍历顺序:[1 2 3 4 5 6] +- 前序遍历顺序:[1 2 4 5 3 6] +- 中序遍历顺序:[4 2 5 1 3 6] +- 后序遍历顺序:[4 5 2 6 3 1] + +层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;而前序、中序、后序遍历利用了 DFS 实现。 + +前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。 + +① 前序 + +```java +void dfs(TreeNode root) { + visit(root); + dfs(root.left); + dfs(root.right); +} +``` + +② 中序 + +```java +void dfs(TreeNode root) { + dfs(root.left); + visit(root); + dfs(root.right); +} +``` + +③ 后序 + +```java +void dfs(TreeNode root) { + dfs(root.left); + dfs(root.right); + visit(root); +} +``` + +### 1. 非递归实现二叉树的前序遍历 + +144\. Binary Tree Preorder Traversal (Medium) + +[Leetcode](https://leetcode.com/problems/binary-tree-preorder-traversal/description/) / [力扣](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/description/) + +```java +public List preorderTraversal(TreeNode root) { + List ret = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + ret.add(node.val); + stack.push(node.right); // 先右后左,保证左子树先遍历 + stack.push(node.left); + } + return ret; +} +``` + +### 2. 非递归实现二叉树的后序遍历 + +145\. Binary Tree Postorder Traversal (Medium) + +[Leetcode](https://leetcode.com/problems/binary-tree-postorder-traversal/description/) / [力扣](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/description/) + +前序遍历为 root -\> left -\> right,后序遍历为 left -\> right -\> root。可以修改前序遍历成为 root -\> right -\> left,那么这个顺序就和后序遍历正好相反。 + +```java +public List postorderTraversal(TreeNode root) { + List ret = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + ret.add(node.val); + stack.push(node.left); + stack.push(node.right); + } + Collections.reverse(ret); + return ret; +} +``` + +### 3. 非递归实现二叉树的中序遍历 + +94\. Binary Tree Inorder Traversal (Medium) + +[Leetcode](https://leetcode.com/problems/binary-tree-inorder-traversal/description/) / [力扣](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/description/) + +```java +public List inorderTraversal(TreeNode root) { + List ret = new ArrayList<>(); + if (root == null) return ret; + Stack stack = new Stack<>(); + TreeNode cur = root; + while (cur != null || !stack.isEmpty()) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + TreeNode node = stack.pop(); + ret.add(node.val); + cur = node.right; + } + return ret; +} +``` + +## BST + +二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。 + +二叉查找树中序遍历有序。 + +### 1. 修剪二叉查找树 + +669\. Trim a Binary Search Tree (Easy) + +[Leetcode](https://leetcode.com/problems/trim-a-binary-search-tree/description/) / [力扣](https://leetcode-cn.com/problems/trim-a-binary-search-tree/description/) + +```html +Input: + + 3 + / \ + 0 4 + \ + 2 + / + 1 + + L = 1 + R = 3 + +Output: + + 3 + / + 2 + / + 1 +``` + +题目描述:只保留值在 L \~ R 之间的节点 + +```java +public TreeNode trimBST(TreeNode root, int L, int R) { + if (root == null) return null; + if (root.val > R) return trimBST(root.left, L, R); + if (root.val < L) return trimBST(root.right, L, R); + root.left = trimBST(root.left, L, R); + root.right = trimBST(root.right, L, R); + return root; +} +``` + +### 2. 寻找二叉查找树的第 k 个元素 + +230\. Kth Smallest Element in a BST (Medium) + +[Leetcode](https://leetcode.com/problems/kth-smallest-element-in-a-bst/description/) / [力扣](https://leetcode-cn.com/problems/kth-smallest-element-in-a-bst/description/) + + +中序遍历解法: + +```java +private int cnt = 0; +private int val; + +public int kthSmallest(TreeNode root, int k) { + inOrder(root, k); + return val; +} + +private void inOrder(TreeNode node, int k) { + if (node == null) return; + inOrder(node.left, k); + cnt++; + if (cnt == k) { + val = node.val; + return; + } + inOrder(node.right, k); +} +``` + +递归解法: + +```java +public int kthSmallest(TreeNode root, int k) { + int leftCnt = count(root.left); + if (leftCnt == k - 1) return root.val; + if (leftCnt > k - 1) return kthSmallest(root.left, k); + return kthSmallest(root.right, k - leftCnt - 1); +} + +private int count(TreeNode node) { + if (node == null) return 0; + return 1 + count(node.left) + count(node.right); +} +``` + +### 3. 把二叉查找树每个节点的值都加上比它大的节点的值 + +Convert BST to Greater Tree (Easy) + +[Leetcode](https://leetcode.com/problems/convert-bst-to-greater-tree/description/) / [力扣](https://leetcode-cn.com/problems/convert-bst-to-greater-tree/description/) + +```html +Input: The root of a Binary Search Tree like this: + + 5 + / \ + 2 13 + +Output: The root of a Greater Tree like this: + + 18 + / \ + 20 13 +``` + +先遍历右子树。 + +```java +private int sum = 0; + +public TreeNode convertBST(TreeNode root) { + traver(root); + return root; +} + +private void traver(TreeNode node) { + if (node == null) return; + traver(node.right); + sum += node.val; + node.val = sum; + traver(node.left); +} +``` + +### 4. 二叉查找树的最近公共祖先 + +235\. Lowest Common Ancestor of a Binary Search Tree (Easy) + +[Leetcode](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/) / [力扣](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/) + +```html + _______6______ + / \ + ___2__ ___8__ + / \ / \ +0 4 7 9 + / \ + 3 5 + +For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. +``` + +```java +public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + 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; +} +``` + +### 5. 二叉树的最近公共祖先 + +236\. Lowest Common Ancestor of a Binary Tree (Medium) + +[Leetcode](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/) / [力扣](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/description/) + +```html + _______3______ + / \ + ___5__ ___1__ + / \ / \ +6 2 0 8 + / \ + 7 4 + +For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition. +``` + +```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; +} +``` + +### 6. 从有序数组中构造二叉查找树 + +108\. Convert Sorted Array to Binary Search Tree (Easy) + +[Leetcode](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/) / [力扣](https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/description/) + +```java +public TreeNode sortedArrayToBST(int[] nums) { + return toBST(nums, 0, nums.length - 1); +} + +private TreeNode toBST(int[] nums, int sIdx, int eIdx){ + if (sIdx > eIdx) return null; + int mIdx = (sIdx + eIdx) / 2; + TreeNode root = new TreeNode(nums[mIdx]); + root.left = toBST(nums, sIdx, mIdx - 1); + root.right = toBST(nums, mIdx + 1, eIdx); + return root; +} +``` + +### 7. 根据有序链表构造平衡的二叉查找树 + +109\. Convert Sorted List to Binary Search Tree (Medium) + +[Leetcode](https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/description/) / [力扣](https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/description/) + +```html +Given the sorted linked list: [-10,-3,0,5,9], + +One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: + + 0 + / \ + -3 9 + / / + -10 5 +``` + +```java +public TreeNode sortedListToBST(ListNode head) { + if (head == null) return null; + if (head.next == null) return new TreeNode(head.val); + ListNode preMid = preMid(head); + ListNode mid = preMid.next; + preMid.next = null; // 断开链表 + TreeNode t = new TreeNode(mid.val); + t.left = sortedListToBST(head); + t.right = sortedListToBST(mid.next); + return t; +} + +private ListNode preMid(ListNode head) { + ListNode slow = head, fast = head.next; + ListNode pre = head; + while (fast != null && fast.next != null) { + pre = slow; + slow = slow.next; + fast = fast.next.next; + } + return pre; +} +``` + +### 8. 在二叉查找树中寻找两个节点,使它们的和为一个给定值 + +653\. Two Sum IV - Input is a BST (Easy) + +[Leetcode](https://leetcode.com/problems/two-sum-iv-input-is-a-bst/description/) / [力扣](https://leetcode-cn.com/problems/two-sum-iv-input-is-a-bst/description/) + +```html +Input: + + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Target = 9 + +Output: True +``` + +使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。 + +应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。 + +```java +public boolean findTarget(TreeNode root, int k) { + List nums = new ArrayList<>(); + inOrder(root, nums); + int i = 0, j = nums.size() - 1; + while (i < j) { + int sum = nums.get(i) + nums.get(j); + if (sum == k) return true; + if (sum < k) i++; + else j--; + } + return false; +} + +private void inOrder(TreeNode root, List nums) { + if (root == null) return; + inOrder(root.left, nums); + nums.add(root.val); + inOrder(root.right, nums); +} +``` + +### 9. 在二叉查找树中查找两个节点之差的最小绝对值 + +530\. Minimum Absolute Difference in BST (Easy) + +[Leetcode](https://leetcode.com/problems/minimum-absolute-difference-in-bst/description/) / [力扣](https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/description/) + +```html +Input: + + 1 + \ + 3 + / + 2 + +Output: + +1 +``` + +利用二叉查找树的中序遍历为有序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。 + +```java +private int minDiff = Integer.MAX_VALUE; +private TreeNode preNode = null; + +public int getMinimumDifference(TreeNode root) { + inOrder(root); + return minDiff; +} + +private void inOrder(TreeNode node) { + if (node == null) return; + inOrder(node.left); + if (preNode != null) minDiff = Math.min(minDiff, node.val - preNode.val); + preNode = node; + inOrder(node.right); +} +``` + +### 10. 寻找二叉查找树中出现次数最多的值 + +501\. Find Mode in Binary Search Tree (Easy) + +[Leetcode](https://leetcode.com/problems/find-mode-in-binary-search-tree/description/) / [力扣](https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/description/) + +```html + 1 + \ + 2 + / + 2 + +return [2]. +``` + +答案可能不止一个,也就是有多个值出现的次数一样多。 + +```java +private int curCnt = 1; +private int maxCnt = 1; +private TreeNode preNode = null; + +public int[] findMode(TreeNode root) { + List maxCntNums = new ArrayList<>(); + inOrder(root, maxCntNums); + int[] ret = new int[maxCntNums.size()]; + int idx = 0; + for (int num : maxCntNums) { + ret[idx++] = num; + } + return ret; +} + +private void inOrder(TreeNode node, List nums) { + if (node == null) return; + inOrder(node.left, nums); + if (preNode != null) { + if (preNode.val == node.val) curCnt++; + else curCnt = 1; + } + if (curCnt > maxCnt) { + maxCnt = curCnt; + nums.clear(); + nums.add(node.val); + } else if (curCnt == maxCnt) { + nums.add(node.val); + } + preNode = node; + inOrder(node.right, nums); +} +``` + +## Trie + +

+ +Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 + +### 1. 实现一个 Trie + +208\. Implement Trie (Prefix Tree) (Medium) + +[Leetcode](https://leetcode.com/problems/implement-trie-prefix-tree/description/) / [力扣](https://leetcode-cn.com/problems/implement-trie-prefix-tree/description/) + +```java +class Trie { + + private class Node { + Node[] childs = new Node[26]; + boolean isLeaf; + } + + private Node root = new Node(); + + public Trie() { + } + + public void insert(String word) { + insert(word, root); + } + + private void insert(String word, Node node) { + if (node == null) return; + if (word.length() == 0) { + node.isLeaf = true; + return; + } + int index = indexForChar(word.charAt(0)); + if (node.childs[index] == null) { + node.childs[index] = new Node(); + } + insert(word.substring(1), node.childs[index]); + } + + public boolean search(String word) { + return search(word, root); + } + + private boolean search(String word, Node node) { + if (node == null) return false; + if (word.length() == 0) return node.isLeaf; + int index = indexForChar(word.charAt(0)); + return search(word.substring(1), node.childs[index]); + } + + public boolean startsWith(String prefix) { + return startWith(prefix, root); + } + + private boolean startWith(String prefix, Node node) { + if (node == null) return false; + if (prefix.length() == 0) return true; + int index = indexForChar(prefix.charAt(0)); + return startWith(prefix.substring(1), node.childs[index]); + } + + private int indexForChar(char c) { + return c - 'a'; + } +} +``` + +### 2. 实现一个 Trie,用来求前缀和 + +677\. Map Sum Pairs (Medium) + +[Leetcode](https://leetcode.com/problems/map-sum-pairs/description/) / [力扣](https://leetcode-cn.com/problems/map-sum-pairs/description/) + +```html +Input: insert("apple", 3), Output: Null +Input: sum("ap"), Output: 3 +Input: insert("app", 2), Output: Null +Input: sum("ap"), Output: 5 +``` + +```java +class MapSum { + + private class Node { + Node[] child = new Node[26]; + int value; + } + + private Node root = new Node(); + + public MapSum() { + + } + + public void insert(String key, int val) { + insert(key, root, val); + } + + private void insert(String key, Node node, int val) { + if (node == null) return; + if (key.length() == 0) { + node.value = val; + return; + } + int index = indexForChar(key.charAt(0)); + if (node.child[index] == null) { + node.child[index] = new Node(); + } + insert(key.substring(1), node.child[index], val); + } + + public int sum(String prefix) { + return sum(prefix, root); + } + + private int sum(String prefix, Node node) { + if (node == null) return 0; + if (prefix.length() != 0) { + int index = indexForChar(prefix.charAt(0)); + return sum(prefix.substring(1), node.child[index]); + } + int sum = node.value; + for (Node child : node.child) { + sum += sum(prefix, child); + } + return sum; + } + + private int indexForChar(char c) { + return c - 'a'; + } +} +``` + diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" "b/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" new file mode 100644 index 0000000000..dd718840fd --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \347\233\256\345\275\225.md" @@ -0,0 +1,36 @@ +# Leetcode 题解 + +## 前言 + +本文从 Leetcode 中精选大概 200 左右的题目,去除了某些繁杂但是没有多少算法思想的题目,同时保留了面试中经常被问到的经典题目。 + +## 算法思想 + +- [双指针](Leetcode%20题解%20-%20双指针.md) +- [排序](Leetcode%20题解%20-%20排序.md) +- [贪心思想](Leetcode%20题解%20-%20贪心思想.md) +- [二分查找](Leetcode%20题解%20-%20二分查找.md) +- [分治](Leetcode%20题解%20-%20分治.md) +- [搜索](Leetcode%20题解%20-%20搜索.md) +- [动态规划](Leetcode%20题解%20-%20动态规划.md) +- [数学](Leetcode%20题解%20-%20数学.md) + +## 数据结构相关 + +- [链表](Leetcode%20题解%20-%20链表.md) +- [树](Leetcode%20题解%20-%20树.md) +- [栈和队列](Leetcode%20题解%20-%20栈和队列.md) +- [哈希表](Leetcode%20题解%20-%20哈希表.md) +- [字符串](Leetcode%20题解%20-%20字符串.md) +- [数组与矩阵](Leetcode%20题解%20-%20数组与矩阵.md) +- [图](Leetcode%20题解%20-%20图.md) +- [位运算](Leetcode%20题解%20-%20位运算.md) + +## 参考资料 + +- Leetcode +- Weiss M A, 冯舜玺. 数据结构与算法分析——C 语言描述[J]. 2004. +- Sedgewick R. Algorithms[M]. Pearson Education India, 1988. +- 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014. +- 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008. +- 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015. diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" "b/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" new file mode 100644 index 0000000000..6fd7ed282b --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \350\264\252\345\277\203\346\200\235\346\203\263.md" @@ -0,0 +1,396 @@ +# Leetcode 题解 - 贪心思想 + +* [Leetcode 题解 - 贪心思想](#leetcode-题解---贪心思想) + * [1. 分配饼干](#1-分配饼干) + * [2. 不重叠的区间个数](#2-不重叠的区间个数) + * [3. 投飞镖刺破气球](#3-投飞镖刺破气球) + * [4. 根据身高和序号重组队列](#4-根据身高和序号重组队列) + * [5. 买卖股票最大的收益](#5-买卖股票最大的收益) + * [6. 买卖股票的最大收益 II](#6-买卖股票的最大收益-ii) + * [7. 种植花朵](#7-种植花朵) + * [8. 判断是否为子序列](#8-判断是否为子序列) + * [9. 修改一个数成为非递减数组](#9-修改一个数成为非递减数组) + * [10. 子数组最大的和](#10-子数组最大的和) + * [11. 分隔字符串使同种字符出现在一起](#11-分隔字符串使同种字符出现在一起) + + + +保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。 + +## 1. 分配饼干 + +455\. Assign Cookies (Easy) + +[Leetcode](https://leetcode.com/problems/assign-cookies/description/) / [力扣](https://leetcode-cn.com/problems/assign-cookies/description/) + +```html +Input: grid[1,3], size[1,2,4] +Output: 2 +``` + +题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。 + +1. 给一个孩子的饼干应当尽量小并且又能满足该孩子,这样大饼干才能拿来给满足度比较大的孩子。 +2. 因为满足度最小的孩子最容易得到满足,所以先满足满足度最小的孩子。 + +在以上的解法中,我们只在每次分配时饼干时选择一种看起来是当前最优的分配方法,但无法保证这种局部最优的分配方法最后能得到全局最优解。我们假设能得到全局最优解,并使用反证法进行证明,即假设存在一种比我们使用的贪心策略更优的最优策略。如果不存在这种最优策略,表示贪心策略就是最优策略,得到的解也就是全局最优解。 + +证明:假设在某次选择中,贪心策略选择给当前满足度最小的孩子分配第 m 个饼干,第 m 个饼干为可以满足该孩子的最小饼干。假设存在一种最优策略,可以给该孩子分配第 n 个饼干,并且 m \< n。我们可以发现,经过这一轮分配,贪心策略分配后剩下的饼干一定有一个比最优策略来得大。因此在后续的分配中,贪心策略一定能满足更多的孩子。也就是说不存在比贪心策略更优的策略,即贪心策略就是最优策略。 + +

+ +```java +public int findContentChildren(int[] grid, int[] size) { + if (grid == null || size == null) return 0; + Arrays.sort(grid); + Arrays.sort(size); + int gi = 0, si = 0; + while (gi < grid.length && si < size.length) { + if (grid[gi] <= size[si]) { + gi++; + } + si++; + } + return gi; +} +``` + +## 2. 不重叠的区间个数 + +435\. Non-overlapping Intervals (Medium) + +[Leetcode](https://leetcode.com/problems/non-overlapping-intervals/description/) / [力扣](https://leetcode-cn.com/problems/non-overlapping-intervals/description/) + +```html +Input: [ [1,2], [1,2], [1,2] ] + +Output: 2 + +Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping. +``` + +```html +Input: [ [1,2], [2,3] ] + +Output: 0 + +Explanation: You don't need to remove any of the intervals since they're already non-overlapping. +``` + +题目描述:计算让一组区间不重叠所需要移除的区间个数。 + +先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。 + +在每次选择中,区间的结尾最为重要,选择的区间结尾越小,留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。 + +按区间的结尾进行排序,每次选择结尾最小,并且和前一个区间不重叠的区间。 + +```java +public int eraseOverlapIntervals(int[][] intervals) { + if (intervals.length == 0) { + return 0; + } + Arrays.sort(intervals, Comparator.comparingInt(o -> o[1])); + int cnt = 1; + int end = intervals[0][1]; + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] < end) { + continue; + } + end = intervals[i][1]; + cnt++; + } + return intervals.length - cnt; +} +``` + +使用 lambda 表示式创建 Comparator 会导致算法运行时间过长,如果注重运行时间,可以修改为普通创建 Comparator 语句: + +```java +Arrays.sort(intervals, new Comparator() { + @Override + public int compare(int[] o1, int[] o2) { + return (o1[1] < o2[1]) ? -1 : ((o1[1] == o2[1]) ? 0 : 1); + } +}); +``` + +实现 compare() 函数时避免使用 `return o1[1] - o2[1];` 这种减法操作,防止溢出。 + +## 3. 投飞镖刺破气球 + +452\. Minimum Number of Arrows to Burst Balloons (Medium) + +[Leetcode](https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/description/) / [力扣](https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/description/) + +``` +Input: +[[10,16], [2,8], [1,6], [7,12]] + +Output: +2 +``` + +题目描述:气球在一个水平数轴上摆放,可以重叠,飞镖垂直投向坐标轴,使得路径上的气球都被刺破。求解最小的投飞镖次数使所有气球都被刺破。 + +也是计算不重叠的区间个数,不过和 Non-overlapping Intervals 的区别在于,[1, 2] 和 [2, 3] 在本题中算是重叠区间。 + +```java +public int findMinArrowShots(int[][] points) { + if (points.length == 0) { + return 0; + } + Arrays.sort(points, Comparator.comparingInt(o -> o[1])); + int cnt = 1, end = points[0][1]; + for (int i = 1; i < points.length; i++) { + if (points[i][0] <= end) { + continue; + } + cnt++; + end = points[i][1]; + } + return cnt; +} +``` + +## 4. 根据身高和序号重组队列 + +406\. Queue Reconstruction by Height(Medium) + +[Leetcode](https://leetcode.com/problems/queue-reconstruction-by-height/description/) / [力扣](https://leetcode-cn.com/problems/queue-reconstruction-by-height/description/) + +```html +Input: +[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] + +Output: +[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] +``` + +题目描述:一个学生用两个分量 (h, k) 描述,h 表示身高,k 表示排在前面的有 k 个学生的身高比他高或者和他一样高。 + +为了使插入操作不影响后续的操作,身高较高的学生应该先做插入操作,否则身高较小的学生原先正确插入的第 k 个位置可能会变成第 k+1 个位置。 + +身高 h 降序、个数 k 值升序,然后将某个学生插入队列的第 k 个位置中。 + +```java +public int[][] reconstructQueue(int[][] people) { + if (people == null || people.length == 0 || people[0].length == 0) { + return new int[0][0]; + } + Arrays.sort(people, (a, b) -> (a[0] == b[0] ? a[1] - b[1] : b[0] - a[0])); + List queue = new ArrayList<>(); + for (int[] p : people) { + queue.add(p[1], p); + } + return queue.toArray(new int[queue.size()][]); +} +``` + +## 5. 买卖股票最大的收益 + +121\. Best Time to Buy and Sell Stock (Easy) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/description/) + +题目描述:一次股票交易包含买入和卖出,只进行一次交易,求最大收益。 + +只要记录前面的最小价格,将这个最小价格作为买入价格,然后将当前的价格作为售出价格,查看当前收益是不是最大收益。 + +```java +public int maxProfit(int[] prices) { + int n = prices.length; + if (n == 0) return 0; + int soFarMin = prices[0]; + int max = 0; + for (int i = 1; i < n; i++) { + if (soFarMin > prices[i]) soFarMin = prices[i]; + else max = Math.max(max, prices[i] - soFarMin); + } + return max; +} +``` + + +## 6. 买卖股票的最大收益 II + +122\. Best Time to Buy and Sell Stock II (Easy) + +[Leetcode](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/) / [力扣](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/description/) + +题目描述:可以进行多次交易,多次交易之间不能交叉进行,可以进行多次交易。 + +对于 [a, b, c, d],如果有 a \<= b \<= c \<= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] \> 0,那么就把 prices[i] - prices[i-1] 添加到收益中。 + +```java +public int maxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; +} +``` + + +## 7. 种植花朵 + +605\. Can Place Flowers (Easy) + +[Leetcode](https://leetcode.com/problems/can-place-flowers/description/) / [力扣](https://leetcode-cn.com/problems/can-place-flowers/description/) + +```html +Input: flowerbed = [1,0,0,0,1], n = 1 +Output: True +``` + +题目描述:flowerbed 数组中 1 表示已经种下了花朵。花朵之间至少需要一个单位的间隔,求解是否能种下 n 朵花。 + +```java +public boolean canPlaceFlowers(int[] flowerbed, int n) { + int len = flowerbed.length; + int cnt = 0; + for (int i = 0; i < len && cnt < n; i++) { + if (flowerbed[i] == 1) { + continue; + } + int pre = i == 0 ? 0 : flowerbed[i - 1]; + int next = i == len - 1 ? 0 : flowerbed[i + 1]; + if (pre == 0 && next == 0) { + cnt++; + flowerbed[i] = 1; + } + } + return cnt >= n; +} +``` + +## 8. 判断是否为子序列 + +392\. Is Subsequence (Medium) + +[Leetcode](https://leetcode.com/problems/is-subsequence/description/) / [力扣](https://leetcode-cn.com/problems/is-subsequence/description/) + +```html +s = "abc", t = "ahbgdc" +Return true. +``` + +```java +public boolean isSubsequence(String s, String t) { + int index = -1; + for (char c : s.toCharArray()) { + index = t.indexOf(c, index + 1); + if (index == -1) { + return false; + } + } + return true; +} +``` + +## 9. 修改一个数成为非递减数组 + +665\. Non-decreasing Array (Easy) + +[Leetcode](https://leetcode.com/problems/non-decreasing-array/description/) / [力扣](https://leetcode-cn.com/problems/non-decreasing-array/description/) + +```html +Input: [4,2,3] +Output: True +Explanation: You could modify the first 4 to 1 to get a non-decreasing array. +``` + +题目描述:判断一个数组是否能只修改一个数就成为非递减数组。 + +在出现 nums[i] \< nums[i - 1] 时,需要考虑的是应该修改数组的哪个数,使得本次修改能使 i 之前的数组成为非递减数组,并且 **不影响后续的操作** 。优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。还有一个比较特别的情况就是 nums[i] \< nums[i - 2],修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]。 + +```java +public boolean checkPossibility(int[] nums) { + int cnt = 0; + for (int i = 1; i < nums.length && cnt < 2; i++) { + if (nums[i] >= nums[i - 1]) { + continue; + } + cnt++; + if (i - 2 >= 0 && nums[i - 2] > nums[i]) { + nums[i] = nums[i - 1]; + } else { + nums[i - 1] = nums[i]; + } + } + return cnt <= 1; +} +``` + + + +## 10. 子数组最大的和 + +53\. Maximum Subarray (Easy) + +[Leetcode](https://leetcode.com/problems/maximum-subarray/description/) / [力扣](https://leetcode-cn.com/problems/maximum-subarray/description/) + +```html +For example, given the array [-2,1,-3,4,-1,2,1,-5,4], +the contiguous subarray [4,-1,2,1] has the largest sum = 6. +``` + +```java +public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int preSum = nums[0]; + int maxSum = preSum; + for (int i = 1; i < nums.length; i++) { + preSum = preSum > 0 ? preSum + nums[i] : nums[i]; + maxSum = Math.max(maxSum, preSum); + } + return maxSum; +} +``` + +## 11. 分隔字符串使同种字符出现在一起 + +763\. Partition Labels (Medium) + +[Leetcode](https://leetcode.com/problems/partition-labels/description/) / [力扣](https://leetcode-cn.com/problems/partition-labels/description/) + +```html +Input: S = "ababcbacadefegdehijhklij" +Output: [9,7,8] +Explanation: +The partition is "ababcbaca", "defegde", "hijhklij". +This is a partition so that each letter appears in at most one part. +A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. +``` + +```java +public List partitionLabels(String S) { + int[] lastIndexsOfChar = new int[26]; + for (int i = 0; i < S.length(); i++) { + lastIndexsOfChar[char2Index(S.charAt(i))] = i; + } + List partitions = new ArrayList<>(); + int firstIndex = 0; + while (firstIndex < S.length()) { + int lastIndex = firstIndex; + for (int i = firstIndex; i < S.length() && i <= lastIndex; i++) { + int index = lastIndexsOfChar[char2Index(S.charAt(i))]; + if (index > lastIndex) { + lastIndex = index; + } + } + partitions.add(lastIndex - firstIndex + 1); + firstIndex = lastIndex + 1; + } + return partitions; +} + +private int char2Index(char c) { + return c - 'a'; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" "b/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" new file mode 100644 index 0000000000..dd125c4875 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" @@ -0,0 +1,365 @@ +# Leetcode 题解 - 链表 + +* [Leetcode 题解 - 链表](#leetcode-题解---链表) + * [1. 找出两个链表的交点](#1-找出两个链表的交点) + * [2. 链表反转](#2-链表反转) + * [3. 归并两个有序的链表](#3-归并两个有序的链表) + * [4. 从有序链表中删除重复节点](#4-从有序链表中删除重复节点) + * [5. 删除链表的倒数第 n 个节点](#5-删除链表的倒数第-n-个节点) + * [6. 交换链表中的相邻结点](#6-交换链表中的相邻结点) + * [7. 链表求和](#7-链表求和) + * [8. 回文链表](#8-回文链表) + * [9. 分隔链表](#9-分隔链表) + * [10. 链表元素按奇偶聚集](#10-链表元素按奇偶聚集) + + + +链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。 + +## 1. 找出两个链表的交点 + +160\. Intersection of Two Linked Lists (Easy) + +[Leetcode](https://leetcode.com/problems/intersection-of-two-linked-lists/description/) / [力扣](https://leetcode-cn.com/problems/intersection-of-two-linked-lists/description/) + +例如以下示例中 A 和 B 两个链表相交于 c1: + +```html +A: a1 → a2 + ↘ + c1 → c2 → c3 + ↗ +B: b1 → b2 → b3 +``` + +但是不会出现以下相交的情况,因为每个节点只有一个 next 指针,也就只能有一个后继节点,而以下示例中节点 c 有两个后继节点。 + +```html +A: a1 → a2 d1 → d2 + ↘ ↗ + c + ↗ ↘ +B: b1 → b2 → b3 e1 → e2 +``` + + + +要求时间复杂度为 O(N),空间复杂度为 O(1)。如果不存在交点则返回 null。 + +设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 + +当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。 + +如果不存在交点,那么 a + b = b + a,以下实现代码中 l1 和 l2 会同时为 null,从而退出循环。 + +```java +public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + ListNode l1 = headA, l2 = headB; + while (l1 != l2) { + l1 = (l1 == null) ? headB : l1.next; + l2 = (l2 == null) ? headA : l2.next; + } + return l1; +} +``` + +如果只是判断是否存在交点,那么就是另一个问题,即 [编程之美 3.6]() 的问题。有两种解法: + +- 把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环; +- 或者直接比较两个链表的最后一个节点是否相同。 + +## 2. 链表反转 + +206\. Reverse Linked List (Easy) + +[Leetcode](https://leetcode.com/problems/reverse-linked-list/description/) / [力扣](https://leetcode-cn.com/problems/reverse-linked-list/description/) + +递归 + +```java +public ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode next = head.next; + ListNode newHead = reverseList(next); + next.next = head; + head.next = null; + return newHead; +} +``` + +头插法 + +```java +public ListNode reverseList(ListNode head) { + ListNode newHead = new ListNode(-1); + while (head != null) { + ListNode next = head.next; + head.next = newHead.next; + newHead.next = head; + head = next; + } + return newHead.next; +} +``` + +## 3. 归并两个有序的链表 + +21\. Merge Two Sorted Lists (Easy) + +[Leetcode](https://leetcode.com/problems/merge-two-sorted-lists/description/) / [力扣](https://leetcode-cn.com/problems/merge-two-sorted-lists/description/) + +```java +public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) return l2; + if (l2 == null) return l1; + if (l1.val < l2.val) { + l1.next = mergeTwoLists(l1.next, l2); + return l1; + } else { + l2.next = mergeTwoLists(l1, l2.next); + return l2; + } +} +``` + +## 4. 从有序链表中删除重复节点 + +83\. Remove Duplicates from Sorted List (Easy) + +[Leetcode](https://leetcode.com/problems/remove-duplicates-from-sorted-list/description/) / [力扣](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/description/) + +```html +Given 1->1->2, return 1->2. +Given 1->1->2->3->3, return 1->2->3. +``` + +```java +public ListNode deleteDuplicates(ListNode head) { + if (head == null || head.next == null) return head; + head.next = deleteDuplicates(head.next); + return head.val == head.next.val ? head.next : head; +} +``` + +## 5. 删除链表的倒数第 n 个节点 + +19\. Remove Nth Node From End of List (Medium) + +[Leetcode](https://leetcode.com/problems/remove-nth-node-from-end-of-list/description/) / [力扣](https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/description/) + +```html +Given linked list: 1->2->3->4->5, and n = 2. +After removing the second node from the end, the linked list becomes 1->2->3->5. +``` + +```java +public ListNode removeNthFromEnd(ListNode head, int n) { + ListNode fast = head; + while (n-- > 0) { + fast = fast.next; + } + if (fast == null) return head.next; + ListNode slow = head; + while (fast.next != null) { + fast = fast.next; + slow = slow.next; + } + slow.next = slow.next.next; + return head; +} +``` + +## 6. 交换链表中的相邻结点 + +24\. Swap Nodes in Pairs (Medium) + +[Leetcode](https://leetcode.com/problems/swap-nodes-in-pairs/description/) / [力扣](https://leetcode-cn.com/problems/swap-nodes-in-pairs/description/) + +```html +Given 1->2->3->4, you should return the list as 2->1->4->3. +``` + +题目要求:不能修改结点的 val 值,O(1) 空间复杂度。 + +```java +public ListNode swapPairs(ListNode head) { + ListNode node = new ListNode(-1); + node.next = head; + ListNode pre = node; + while (pre.next != null && pre.next.next != null) { + ListNode l1 = pre.next, l2 = pre.next.next; + ListNode next = l2.next; + l1.next = next; + l2.next = l1; + pre.next = l2; + + pre = l1; + } + return node.next; +} +``` + +## 7. 链表求和 + +445\. Add Two Numbers II (Medium) + +[Leetcode](https://leetcode.com/problems/add-two-numbers-ii/description/) / [力扣](https://leetcode-cn.com/problems/add-two-numbers-ii/description/) + +```html +Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) +Output: 7 -> 8 -> 0 -> 7 +``` + +题目要求:不能修改原始链表。 + +```java +public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + Stack l1Stack = buildStack(l1); + Stack l2Stack = buildStack(l2); + ListNode head = new ListNode(-1); + int carry = 0; + while (!l1Stack.isEmpty() || !l2Stack.isEmpty() || carry != 0) { + int x = l1Stack.isEmpty() ? 0 : l1Stack.pop(); + int y = l2Stack.isEmpty() ? 0 : l2Stack.pop(); + int sum = x + y + carry; + ListNode node = new ListNode(sum % 10); + node.next = head.next; + head.next = node; + carry = sum / 10; + } + return head.next; +} + +private Stack buildStack(ListNode l) { + Stack stack = new Stack<>(); + while (l != null) { + stack.push(l.val); + l = l.next; + } + return stack; +} +``` + +## 8. 回文链表 + +234\. Palindrome Linked List (Easy) + +[Leetcode](https://leetcode.com/problems/palindrome-linked-list/description/) / [力扣](https://leetcode-cn.com/problems/palindrome-linked-list/description/) + +题目要求:以 O(1) 的空间复杂度来求解。 + +切成两半,把后半段反转,然后比较两半是否相等。 + +```java +public boolean isPalindrome(ListNode head) { + if (head == null || head.next == null) return true; + ListNode slow = head, fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + if (fast != null) slow = slow.next; // 偶数节点,让 slow 指向下一个节点 + cut(head, slow); // 切成两个链表 + return isEqual(head, reverse(slow)); +} + +private void cut(ListNode head, ListNode cutNode) { + while (head.next != cutNode) { + head = head.next; + } + head.next = null; +} + +private ListNode reverse(ListNode head) { + ListNode newHead = null; + while (head != null) { + ListNode nextNode = head.next; + head.next = newHead; + newHead = head; + head = nextNode; + } + return newHead; +} + +private boolean isEqual(ListNode l1, ListNode l2) { + while (l1 != null && l2 != null) { + if (l1.val != l2.val) return false; + l1 = l1.next; + l2 = l2.next; + } + return true; +} +``` + +## 9. 分隔链表 + +725\. Split Linked List in Parts(Medium) + +[Leetcode](https://leetcode.com/problems/split-linked-list-in-parts/description/) / [力扣](https://leetcode-cn.com/problems/split-linked-list-in-parts/description/) + +```html +Input: +root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3 +Output: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] +Explanation: +The input has been split into consecutive parts with size difference at most 1, and earlier parts are a larger size than the later parts. +``` + +题目描述:把链表分隔成 k 部分,每部分的长度都应该尽可能相同,排在前面的长度应该大于等于后面的。 + +```java +public ListNode[] splitListToParts(ListNode root, int k) { + int N = 0; + ListNode cur = root; + while (cur != null) { + N++; + cur = cur.next; + } + int mod = N % k; + int size = N / k; + ListNode[] ret = new ListNode[k]; + cur = root; + for (int i = 0; cur != null && i < k; i++) { + ret[i] = cur; + int curSize = size + (mod-- > 0 ? 1 : 0); + for (int j = 0; j < curSize - 1; j++) { + cur = cur.next; + } + ListNode next = cur.next; + cur.next = null; + cur = next; + } + return ret; +} +``` + +## 10. 链表元素按奇偶聚集 + +328\. Odd Even Linked List (Medium) + +[Leetcode](https://leetcode.com/problems/odd-even-linked-list/description/) / [力扣](https://leetcode-cn.com/problems/odd-even-linked-list/description/) + +```html +Example: +Given 1->2->3->4->5->NULL, +return 1->3->5->2->4->NULL. +``` + +```java +public ListNode oddEvenList(ListNode head) { + if (head == null) { + return head; + } + ListNode odd = head, even = head.next, evenHead = even; + while (even != null && even.next != null) { + odd.next = odd.next.next; + odd = odd.next; + even.next = even.next.next; + even = even.next; + } + odd.next = evenHead; + return head; +} +``` diff --git "a/notes/Leetcode \351\242\230\350\247\243.md" "b/notes/Leetcode \351\242\230\350\247\243.md" new file mode 100644 index 0000000000..5df5d8b7f4 --- /dev/null +++ "b/notes/Leetcode \351\242\230\350\247\243.md" @@ -0,0 +1 @@ +[Leetcode 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md) diff --git a/notes/Linux.md b/notes/Linux.md new file mode 100644 index 0000000000..c11905d24d --- /dev/null +++ b/notes/Linux.md @@ -0,0 +1,1264 @@ +# Linux + +* [Linux](#linux) + * [前言](#前言) + * [一、常用操作以及概念](#一常用操作以及概念) + * [快捷键](#快捷键) + * [求助](#求助) + * [关机](#关机) + * [PATH](#path) + * [sudo](#sudo) + * [包管理工具](#包管理工具) + * [发行版](#发行版) + * [VIM 三个模式](#vim-三个模式) + * [GNU](#gnu) + * [开源协议](#开源协议) + * [二、磁盘](#二磁盘) + * [磁盘接口](#磁盘接口) + * [磁盘的文件名](#磁盘的文件名) + * [三、分区](#三分区) + * [分区表](#分区表) + * [开机检测程序](#开机检测程序) + * [四、文件系统](#四文件系统) + * [分区与文件系统](#分区与文件系统) + * [组成](#组成) + * [文件读取](#文件读取) + * [磁盘碎片](#磁盘碎片) + * [block](#block) + * [inode](#inode) + * [目录](#目录) + * [日志](#日志) + * [挂载](#挂载) + * [目录配置](#目录配置) + * [五、文件](#五文件) + * [文件属性](#文件属性) + * [文件与目录的基本操作](#文件与目录的基本操作) + * [修改权限](#修改权限) + * [默认权限](#默认权限) + * [目录的权限](#目录的权限) + * [链接](#链接) + * [获取文件内容](#获取文件内容) + * [指令与文件搜索](#指令与文件搜索) + * [六、压缩与打包](#六压缩与打包) + * [压缩文件名](#压缩文件名) + * [压缩指令](#压缩指令) + * [打包](#打包) + * [七、Bash](#七bash) + * [特性](#特性) + * [变量操作](#变量操作) + * [指令搜索顺序](#指令搜索顺序) + * [数据流重定向](#数据流重定向) + * [八、管道指令](#八管道指令) + * [提取指令](#提取指令) + * [排序指令](#排序指令) + * [双向输出重定向](#双向输出重定向) + * [字符转换指令](#字符转换指令) + * [分区指令](#分区指令) + * [九、正则表达式](#九正则表达式) + * [grep](#grep) + * [printf](#printf) + * [awk](#awk) + * [十、进程管理](#十进程管理) + * [查看进程](#查看进程) + * [进程状态](#进程状态) + * [SIGCHLD](#sigchld) + * [wait()](#wait) + * [waitpid()](#waitpid) + * [孤儿进程](#孤儿进程) + * [僵尸进程](#僵尸进程) + * [参考资料](#参考资料) + + + +## 前言 + +为了便于理解,本文从常用操作和概念开始讲起。虽然已经尽量做到简化,但是涉及到的内容还是有点多。在面试中,Linux 知识点相对于网络和操作系统等知识点而言不是那么重要,只需要重点掌握一些原理和命令即可。为了方便大家准备面试,在此先将一些比较重要的知识点列出来: + +- 能简单使用 cat,grep,cut 等命令进行一些操作; +- 文件系统相关的原理,inode 和 block 等概念,数据恢复; +- 硬链接与软链接; +- 进程管理相关,僵尸进程与孤儿进程,SIGCHLD 。 + +## 一、常用操作以及概念 + +### 快捷键 + +- Tab:命令和文件名补全; +- Ctrl+C:中断正在运行的程序; +- Ctrl+D:结束键盘输入(End Of File,EOF) + +### 求助 + +#### 1. --help + +指令的基本用法与选项介绍。 + +#### 2. man + +man 是 manual 的缩写,将指令的具体信息显示出来。 + +当执行 `man date` 时,有 DATE(1) 出现,其中的数字代表指令的类型,常用的数字及其类型如下: + +| 代号 | 类型 | +| :--: | -- | +| 1 | 用户在 shell 环境中可以操作的指令或者可执行文件 | +| 5 | 配置文件 | +| 8 | 系统管理员可以使用的管理指令 | + +#### 3. info + +info 与 man 类似,但是 info 将文档分成一个个页面,每个页面可以跳转。 + +#### 4. doc + +/usr/share/doc 存放着软件的一整套说明文件。 + +### 关机 + +#### 1. who + +在关机前需要先使用 who 命令查看有没有其它用户在线。 + +#### 2. sync + +为了加快对磁盘文件的读写速度,位于内存中的文件数据不会立即同步到磁盘,因此关机之前需要先进行 sync 同步操作。 + +#### 3. shutdown + +```html +## shutdown [-krhc] 时间 [信息] +-k : 不会关机,只是发送警告信息,通知所有在线的用户 +-r : 将系统的服务停掉后就重新启动 +-h : 将系统的服务停掉后就立即关机 +-c : 取消已经在进行的 shutdown +``` + +### PATH + +可以在环境变量 PATH 中声明可执行文件的路径,路径之间用 : 分隔。 + +```html +/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin +``` + +### sudo + +sudo 允许一般用户使用 root 可执行的命令,不过只有在 /etc/sudoers 配置文件中添加的用户才能使用该指令。 + +### 包管理工具 + +RPM 和 DPKG 为最常见的两类软件包管理工具: + +- RPM 全称为 Redhat Package Manager,最早由 Red Hat 公司制定实施,随后被 GNU 开源操作系统接受并成为许多 Linux 系统的既定软件标准。YUM 基于 RPM,具有依赖管理和软件升级功能。 +- 与 RPM 竞争的是基于 Debian 操作系统的 DEB 软件包管理工具 DPKG,全称为 Debian Package,功能方面与 RPM 相似。 + +### 发行版 + +Linux 发行版是 Linux 内核及各种应用软件的集成版本。 + +| 基于的包管理工具 | 商业发行版 | 社区发行版 | +| :--: | :--: | :--: | +| RPM | Red Hat | Fedora / CentOS | +| DPKG | Ubuntu | Debian | + +### VIM 三个模式 + +

+ + + +- 一般指令模式(Command mode):VIM 的默认模式,可以用于移动游标查看内容; +- 编辑模式(Insert mode):按下 "i" 等按键之后进入,可以对文本进行编辑; +- 指令列模式(Bottom-line mode):按下 ":" 按键之后进入,用于保存退出等操作。 + +在指令列模式下,有以下命令用于离开或者保存文件。 + +| 命令 | 作用 | +| :--: | :--: | +| :w | 写入磁盘| +| :w! | 当文件为只读时,强制写入磁盘。到底能不能写入,与用户对该文件的权限有关 | +| :q | 离开 | +| :q! | 强制离开不保存 | +| :wq | 写入磁盘后离开 | +| :wq!| 强制写入磁盘后离开 | + +### GNU + +GNU 计划,译为革奴计划,它的目标是创建一套完全自由的操作系统,称为 GNU,其内容软件完全以 GPL 方式发布。其中 GPL 全称为 GNU 通用公共许可协议(GNU General Public License),包含了以下内容: + +- 以任何目的运行此程序的自由; +- 再复制的自由; +- 改进此程序,并公开发布改进的自由。 + +### 开源协议 + +- [Choose an open source license](https://choosealicense.com/) +- [如何选择开源许可证?](http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html) + +## 二、磁盘 + +### 磁盘接口 + +#### 1. IDE + +IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/s,因为并口线的抗干扰性太差,且排线占用空间较大,不利电脑内部散热,已逐渐被 SATA 所取代。 + +

+ +#### 2. SATA + +SATA 全称 Serial ATA,也就是使用串口的 ATA 接口,抗干扰性强,且对数据线的长度要求比 ATA 低很多,支持热插拔等功能。SATA-II 的接口速度为 300MB/s,而 SATA-III 标准可达到 600MB/s 的传输速度。SATA 的数据线也比 ATA 的细得多,有利于机箱内的空气流通,整理线材也比较方便。 + +

+ +#### 3. SCSI + +SCSI 全称是 Small Computer System Interface(小型机系统接口),SCSI 硬盘广为工作站以及个人电脑以及服务器所使用,因此会使用较为先进的技术,如碟片转速 15000rpm 的高转速,且传输时 CPU 占用率较低,但是单价也比相同容量的 ATA 及 SATA 硬盘更加昂贵。 + +

+ +#### 4. SAS + +SAS(Serial Attached SCSI)是新一代的 SCSI 技术,和 SATA 硬盘相同,都是采取序列式技术以获得更高的传输速度,可达到 6Gb/s。此外也通过缩小连接线改善系统内部空间等。 + +

+ +### 磁盘的文件名 + +Linux 中每个硬件都被当做一个文件,包括磁盘。磁盘以磁盘接口类型进行命名,常见磁盘的文件名如下: + +- IDE 磁盘:/dev/hd[a-d] +- SATA/SCSI/SAS 磁盘:/dev/sd[a-p] + +其中文件名后面的序号的确定与系统检测到磁盘的顺序有关,而与磁盘所插入的插槽位置无关。 + +## 三、分区 + +### 分区表 + +磁盘分区表主要有两种格式,一种是限制较多的 MBR 分区表,一种是较新且限制较少的 GPT 分区表。 + +#### 1. MBR + +MBR 中,第一个扇区最重要,里面有主要开机记录(Master boot record, MBR)及分区表(partition table),其中主要开机记录占 446 bytes,分区表占 64 bytes。 + +分区表只有 64 bytes,最多只能存储 4 个分区,这 4 个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它使用其它扇区来记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。 + +Linux 也把分区当成文件,分区文件的命名方式为:磁盘文件名 + 编号,例如 /dev/sda1。注意,逻辑分区的编号从 5 开始。 + +#### 2. GPT + +扇区是磁盘的最小存储单位,旧磁盘的扇区大小通常为 512 bytes,而最新的磁盘支持 4 k。GPT 为了兼容所有磁盘,在定义扇区上使用逻辑区块地址(Logical Block Address, LBA),LBA 默认大小为 512 bytes。 + +GPT 第 1 个区块记录了主要开机记录(MBR),紧接着是 33 个区块记录分区信息,并把最后的 33 个区块用于对分区信息进行备份。这 33 个区块第一个为 GPT 表头纪录,这个部份纪录了分区表本身的位置与大小和备份分区的位置,同时放置了分区表的校验码 (CRC32),操作系统可以根据这个校验码来判断 GPT 是否正确。若有错误,可以使用备份分区进行恢复。 + +GPT 没有扩展分区概念,都是主分区,每个 LBA 可以分 4 个分区,因此总共可以分 4 * 32 = 128 个分区。 + +MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB = 8 ZB。 + +

+ +### 开机检测程序 + +#### 1. BIOS + +BIOS(Basic Input/Output System,基本输入输出系统),它是一个固件(嵌入在硬件中的软件),BIOS 程序存放在断电后内容不会丢失的只读内存中。 + +

+ +BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。 + +主要开机记录(MBR)中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动开机管理程序时,就可以通过选单选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。 + +下图中,第一扇区的主要开机记录(MBR)中的开机管理程序提供了两个选单:M1、M2,M1 指向了 Windows 操作系统,而 M2 指向其它分区的启动扇区,里面包含了另外一个开机管理程序,提供了一个指向 Linux 的选单。 + +

+ +安装多重引导,最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉主要开机记录(MBR),而 Linux 可以选择将开机管理程序安装在主要开机记录(MBR)或者其它分区的启动扇区,并且可以设置开机管理程序的选单。 + +#### 2. UEFI + +BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 + +## 四、文件系统 + +### 分区与文件系统 + +对分区进行格式化是为了在分区上建立文件系统。一个分区通常只能格式化为一个文件系统,但是磁盘阵列等技术可以将一个分区格式化为多个文件系统。 + +### 组成 + +最主要的几个组成部分如下: + +- inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号; +- block:记录文件的内容,文件太大时,会占用多个 block。 + +除此之外还包括: + +- superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等; +- block bitmap:记录 block 是否被使用的位图。 + +

+ +### 文件读取 + +对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中查找文件内容所在的所有 block,然后把所有 block 的内容读出来。 + +

+ +而对于 FAT 文件系统,它没有 inode,每个 block 中存储着下一个 block 的编号。 + +

+ +### 磁盘碎片 + +指一个文件内容所在的 block 过于分散,导致磁盘磁头移动距离过大,从而降低磁盘读写性能。 + +### block + +在 Ext2 文件系统中所支持的 block 大小有 1K,2K 及 4K 三种,不同的大小限制了单个文件和文件系统的最大大小。 + +| 大小 | 1KB | 2KB | 4KB | +| :---: | :---: | :---: | :---: | +| 最大单一文件 | 16GB | 256GB | 2TB | +| 最大文件系统 | 2TB | 8TB | 16TB | + +一个 block 只能被一个文件所使用,未使用的部分直接浪费了。因此如果需要存储大量的小文件,那么最好选用比较小的 block。 + +### inode + +inode 具体包含以下信息: + +- 权限 (read/write/excute); +- 拥有者与群组 (owner/group); +- 容量; +- 建立或状态改变的时间 (ctime); +- 最近读取时间 (atime); +- 最近修改时间 (mtime); +- 定义文件特性的旗标 (flag),如 SetUID...; +- 该文件真正内容的指向 (pointer)。 + +inode 具有以下特点: + +- 每个 inode 大小均固定为 128 bytes (新的 ext4 与 xfs 可设定到 256 bytes); +- 每个文件都仅会占用一个 inode。 + +inode 中记录了文件内容所在的 block 编号,但是每个 block 非常小,一个大文件随便都需要几十万的 block。而一个 inode 大小有限,无法直接引用这么多 block 编号。因此引入了间接、双间接、三间接引用。间接引用让 inode 记录的引用 block 块记录引用信息。 + +

+ +### 目录 + +建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。 + +可以看到文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的写权限有关。 + +### 日志 + +如果突然断电,那么文件系统会发生错误,例如断电前只修改了 block bitmap,而还没有将数据真正写入 block 中。 + +ext3/ext4 文件系统引入了日志功能,可以利用日志来修复文件系统。 + +### 挂载 + +挂载利用目录作为文件系统的进入点,也就是说,进入目录之后就可以读取文件系统的数据。 + +### 目录配置 + +为了使不同 Linux 发行版本的目录结构保持一致性,Filesystem Hierarchy Standard (FHS) 规定了 Linux 的目录结构。最基础的三个目录如下: + +- / (root, 根目录) +- /usr (unix software resource):所有系统默认软件都会安装到这个目录; +- /var (variable):存放系统或程序运行过程中的数据文件。 + +

+ +## 五、文件 + +### 文件属性 + +用户分为三种:文件拥有者、群组以及其它人,对不同的用户有不同的文件权限。 + +使用 ls 查看一个文件时,会显示一个文件的信息,例如 `drwxr-xr-x 3 root root 17 May 6 00:14 .config`,对这个信息的解释如下: + +- drwxr-xr-x:文件类型以及权限,第 1 位为文件类型字段,后 9 位为文件权限字段 +- 3:链接数 +- root:文件拥有者 +- root:所属群组 +- 17:文件大小 +- May 6 00:14:文件最后被修改的时间 +- .config:文件名 + +常见的文件类型及其含义有: + +- d:目录 +- -:文件 +- l:链接文件 + +9 位的文件权限字段中,每 3 个为一组,共 3 组,每一组分别代表对文件拥有者、所属群组以及其它人的文件权限。一组权限中的 3 位分别为 r、w、x 权限,表示可读、可写、可执行。 + +文件时间有以下三种: + +- modification time (mtime):文件的内容更新就会更新; +- status time (ctime):文件的状态(权限、属性)更新就会更新; +- access time (atime):读取文件时就会更新。 + +### 文件与目录的基本操作 + +#### 1. ls + +列出文件或者目录的信息,目录的信息就是其中包含的文件。 + +```html +## ls [-aAdfFhilnrRSt] file|dir +-a :列出全部的文件 +-d :仅列出目录本身 +-l :以长数据串行列出,包含文件的属性与权限等等数据 +``` + +#### 2. cd + +更换当前目录。 + +``` +cd [相对路径或绝对路径] +``` + +#### 3. mkdir + +创建目录。 + +``` +## mkdir [-mp] 目录名称 +-m :配置目录权限 +-p :递归创建目录 +``` + +#### 4. rmdir + +删除目录,目录必须为空。 + +```html +rmdir [-p] 目录名称 +-p :递归删除目录 +``` + +#### 5. touch + +更新文件时间或者建立新文件。 + +```html +## touch [-acdmt] filename +-a : 更新 atime +-c : 更新 ctime,若该文件不存在则不建立新文件 +-m : 更新 mtime +-d : 后面可以接更新日期而不使用当前日期,也可以使用 --date="日期或时间" +-t : 后面可以接更新时间而不使用当前时间,格式为[YYYYMMDDhhmm] +``` + +#### 6. cp + +复制文件。如果源文件有两个以上,则目的文件一定要是目录才行。 + +```html +cp [-adfilprsu] source destination +-a :相当于 -dr --preserve=all +-d :若来源文件为链接文件,则复制链接文件属性而非文件本身 +-i :若目标文件已经存在时,在覆盖前会先询问 +-p :连同文件的属性一起复制过去 +-r :递归复制 +-u :destination 比 source 旧才更新 destination,或 destination 不存在的情况下才复制 +--preserve=all :除了 -p 的权限相关参数外,还加入 SELinux 的属性, links, xattr 等也复制了 +``` + +#### 7. rm + +删除文件。 + +```html +## rm [-fir] 文件或目录 +-r :递归删除 +``` + +#### 8. mv + +移动文件。 + +```html +## mv [-fiu] source destination +## mv [options] source1 source2 source3 .... directory +-f : force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖 +``` + +### 修改权限 + +可以将一组权限用数字来表示,此时一组权限的 3 个位当做二进制数字的位,从左到右每个位的权值为 4、2、1,即每个权限对应的数字权值为 r : 4、w : 2、x : 1。 + +```html +## chmod [-R] xyz dirname/filename +``` + +示例:将 .bashrc 文件的权限修改为 -rwxr-xr--。 + +```html +## chmod 754 .bashrc +``` + +也可以使用符号来设定权限。 + +```html +## chmod [ugoa] [+-=] [rwx] dirname/filename +- u:拥有者 +- g:所属群组 +- o:其他人 +- a:所有人 +- +:添加权限 +- -:移除权限 +- =:设定权限 +``` + +示例:为 .bashrc 文件的所有用户添加写权限。 + +```html +## chmod a+w .bashrc +``` + +### 默认权限 + +- 文件默认权限:文件默认没有可执行权限,因此为 666,也就是 -rw-rw-rw- 。 +- 目录默认权限:目录必须要能够进入,也就是必须拥有可执行权限,因此为 777 ,也就是 drwxrwxrwx。 + +可以通过 umask 设置或者查看默认权限,通常以掩码的形式来表示,例如 002 表示其它用户的权限去除了一个 2 的权限,也就是写权限,因此建立新文件时默认的权限为 -rw-rw-r--。 + +### 目录的权限 + +文件名不是存储在一个文件的内容中,而是存储在一个文件所在的目录中。因此,拥有文件的 w 权限并不能对文件名进行修改。 + +目录存储文件列表,一个目录的权限也就是对其文件列表的权限。因此,目录的 r 权限表示可以读取文件列表;w 权限表示可以修改文件列表,具体来说,就是添加删除文件,对文件名进行修改;x 权限可以让该目录成为工作目录,x 权限是 r 和 w 权限的基础,如果不能使一个目录成为工作目录,也就没办法读取文件列表以及对文件列表进行修改了。 + +### 链接 + +

+ + +```html +## ln [-sf] source_filename dist_filename +-s :默认是实体链接,加 -s 为符号链接 +-f :如果目标文件存在时,先删除目标文件 +``` + +#### 1. 实体链接 + +在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。 + +删除任意一个条目,文件还是存在,只要引用数量不为 0。 + +有以下限制:不能跨越文件系统、不能对目录进行链接。 + +```html +## ln /etc/crontab . +## ll -i /etc/crontab crontab +34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 crontab +34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab +``` + +#### 2. 符号链接 + +符号链接文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式。 + +当源文件被删除了,链接文件就打不开了。 + +因为记录的是路径,所以可以为目录建立符号链接。 + +```html +## ll -i /etc/crontab /root/crontab2 +34474855 -rw-r--r--. 2 root root 451 Jun 10 2014 /etc/crontab +53745909 lrwxrwxrwx. 1 root root 12 Jun 23 22:31 /root/crontab2 -> /etc/crontab +``` + +### 获取文件内容 + +#### 1. cat + +取得文件内容。 + +```html +## cat [-AbEnTv] filename +-n :打印出行号,连同空白行也会有行号,-b 不会 +``` + +#### 2. tac + +是 cat 的反向操作,从最后一行开始打印。 + +#### 3. more + +和 cat 不同的是它可以一页一页查看文件内容,比较适合大文件的查看。 + +#### 4. less + +和 more 类似,但是多了一个向前翻页的功能。 + +#### 5. head + +取得文件前几行。 + +```html +## head [-n number] filename +-n :后面接数字,代表显示几行的意思 +``` + +#### 6. tail + +是 head 的反向操作,只是取得是后几行。 + +#### 7. od + +以字符或者十六进制的形式显示二进制文件。 + +### 指令与文件搜索 + +#### 1. which + +指令搜索。 + +```html +## which [-a] command +-a :将所有指令列出,而不是只列第一个 +``` + +#### 2. whereis + +文件搜索。速度比较快,因为它只搜索几个特定的目录。 + +```html +## whereis [-bmsu] dirname/filename +``` + +#### 3. locate + +文件搜索。可以用关键字或者正则表达式进行搜索。 + +locate 使用 /var/lib/mlocate/ 这个数据库来进行搜索,它存储在内存中,并且每天更新一次,所以无法用 locate 搜索新建的文件。可以使用 updatedb 来立即更新数据库。 + +```html +## locate [-ir] keyword +-r:正则表达式 +``` + +#### 4. find + +文件搜索。可以使用文件的属性和权限进行搜索。 + +```html +## find [basedir] [option] +example: find . -name "shadow*" +``` + +**① 与时间有关的选项** + +```html +-mtime n :列出在 n 天前的那一天修改过内容的文件 +-mtime +n :列出在 n 天之前 (不含 n 天本身) 修改过内容的文件 +-mtime -n :列出在 n 天之内 (含 n 天本身) 修改过内容的文件 +-newer file : 列出比 file 更新的文件 +``` + ++4、4 和 -4 的指示的时间范围如下: + +

+ +**② 与文件拥有者和所属群组有关的选项** + +```html +-uid n +-gid n +-user name +-group name +-nouser :搜索拥有者不存在 /etc/passwd 的文件 +-nogroup:搜索所属群组不存在于 /etc/group 的文件 +``` + +**③ 与文件权限和名称有关的选项** + +```html +-name filename +-size [+-]SIZE:搜寻比 SIZE 还要大 (+) 或小 (-) 的文件。这个 SIZE 的规格有:c: 代表 byte,k: 代表 1024bytes。所以,要找比 50KB 还要大的文件,就是 -size +50k +-type TYPE +-perm mode :搜索权限等于 mode 的文件 +-perm -mode :搜索权限包含 mode 的文件 +-perm /mode :搜索权限包含任一 mode 的文件 +``` + +## 六、压缩与打包 + +### 压缩文件名 + +Linux 底下有很多压缩文件名,常见的如下: + +| 扩展名 | 压缩程序 | +| -- | -- | +| \*.Z | compress | +|\*.zip | zip | +|\*.gz | gzip| +|\*.bz2 | bzip2 | +|\*.xz | xz | +|\*.tar | tar 程序打包的数据,没有经过压缩 | +|\*.tar.gz | tar 程序打包的文件,经过 gzip 的压缩 | +|\*.tar.bz2 | tar 程序打包的文件,经过 bzip2 的压缩 | +|\*.tar.xz | tar 程序打包的文件,经过 xz 的压缩 | + +### 压缩指令 + +#### 1. gzip + +gzip 是 Linux 使用最广的压缩指令,可以解开 compress、zip 与 gzip 所压缩的文件。 + +经过 gzip 压缩过,源文件就不存在了。 + +有 9 个不同的压缩等级可以使用。 + +可以使用 zcat、zmore、zless 来读取压缩文件的内容。 + +```html +$ gzip [-cdtv#] filename +-c :将压缩的数据输出到屏幕上 +-d :解压缩 +-t :检验压缩文件是否出错 +-v :显示压缩比等信息 +-# : # 为数字的意思,代表压缩等级,数字越大压缩比越高,默认为 6 +``` + +#### 2. bzip2 + +提供比 gzip 更高的压缩比。 + +查看命令:bzcat、bzmore、bzless、bzgrep。 + +```html +$ bzip2 [-cdkzv#] filename +-k :保留源文件 +``` + +#### 3. xz + +提供比 bzip2 更佳的压缩比。 + +可以看到,gzip、bzip2、xz 的压缩比不断优化。不过要注意的是,压缩比越高,压缩的时间也越长。 + +查看命令:xzcat、xzmore、xzless、xzgrep。 + +```html +$ xz [-dtlkc#] filename +``` + +### 打包 + +压缩指令只能对一个文件进行压缩,而打包能够将多个文件打包成一个大文件。tar 不仅可以用于打包,也可以使用 gzip、bzip2、xz 将打包文件进行压缩。 + +```html +$ tar [-z|-j|-J] [cv] [-f 新建的 tar 文件] filename... ==打包压缩 +$ tar [-z|-j|-J] [tv] [-f 已有的 tar 文件] ==查看 +$ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录] ==解压缩 +-z :使用 zip; +-j :使用 bzip2; +-J :使用 xz; +-c :新建打包文件; +-t :查看打包文件里面有哪些文件; +-x :解打包或解压缩的功能; +-v :在压缩/解压缩的过程中,显示正在处理的文件名; +-f : filename:要处理的文件; +-C 目录 : 在特定目录解压缩。 +``` + +| 使用方式 | 命令 | +| :---: | --- | +| 打包压缩 | tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称 | +| 查 看 | tar -jtv -f filename.tar.bz2 | +| 解压缩 | tar -jxv -f filename.tar.bz2 -C 要解压缩的目录 | + +## 七、Bash + +可以通过 Shell 请求内核提供服务,Bash 正是 Shell 的一种。 + +### 特性 + +- 命令历史:记录使用过的命令 +- 命令与文件补全:快捷键:tab +- 命名别名:例如 ll 是 ls -al 的别名 +- shell scripts +- 通配符:例如 ls -l /usr/bin/X\* 列出 /usr/bin 下面所有以 X 开头的文件 + +### 变量操作 + +对一个变量赋值直接使用 =。 + +对变量取用需要在变量前加上 \$ ,也可以用 \${} 的形式; + +输出变量使用 echo 命令。 + +```bash +$ x=abc +$ echo $x +$ echo ${x} +``` + +变量内容如果有空格,必须使用双引号或者单引号。 + +- 双引号内的特殊字符可以保留原本特性,例如 x="lang is \$LANG",则 x 的值为 lang is zh_TW.UTF-8; +- 单引号内的特殊字符就是特殊字符本身,例如 x='lang is \$LANG',则 x 的值为 lang is \$LANG。 + +可以使用 \`指令\` 或者 \$(指令) 的方式将指令的执行结果赋值给变量。例如 version=\$(uname -r),则 version 的值为 4.15.0-22-generic。 + +可以使用 export 命令将自定义变量转成环境变量,环境变量可以在子程序中使用,所谓子程序就是由当前 Bash 而产生的子 Bash。 + +Bash 的变量可以声明为数组和整数数字。注意数字类型没有浮点数。如果不进行声明,默认是字符串类型。变量的声明使用 declare 命令: + +```html +$ declare [-aixr] variable +-a : 定义为数组类型 +-i : 定义为整数类型 +-x : 定义为环境变量 +-r : 定义为 readonly 类型 +``` + +使用 [ ] 来对数组进行索引操作: + +```bash +$ array[1]=a +$ array[2]=b +$ echo ${array[1]} +``` + +### 指令搜索顺序 + +- 以绝对或相对路径来执行指令,例如 /bin/ls 或者 ./ls ; +- 由别名找到该指令来执行; +- 由 Bash 内置的指令来执行; +- 按 \$PATH 变量指定的搜索路径的顺序找到第一个指令来执行。 + +### 数据流重定向 + +重定向指的是使用文件代替标准输入、标准输出和标准错误输出。 + +| 1 | 代码 | 运算符 | +| :---: | :---: | :---:| +| 标准输入 (stdin) | 0 | \< 或 \<\< | +| 标准输出 (stdout) | 1 | > 或 \>\> | +| 标准错误输出 (stderr) | 2 | 2\> 或 2\>\> | + +其中,有一个箭头的表示以覆盖的方式重定向,而有两个箭头的表示以追加的方式重定向。 + +可以将不需要的标准输出以及标准错误输出重定向到 /dev/null,相当于扔进垃圾箱。 + +如果需要将标准输出以及标准错误输出同时重定向到一个文件,需要将某个输出转换为另一个输出,例如 2\>&1 表示将标准错误输出转换为标准输出。 + +```bash +$ find /home -name .bashrc > list 2>&1 +``` + +## 八、管道指令 + +管道是将一个命令的标准输出作为另一个命令的标准输入,在数据需要经过多个步骤的处理之后才能得到我们想要的内容时就可以使用管道。 + +在命令之间使用 | 分隔各个管道命令。 + +```bash +$ ls -al /etc | less +``` + +### 提取指令 + +cut 对数据进行切分,取出想要的部分。 + +切分过程一行一行地进行。 + +```html +$ cut +-d :分隔符 +-f :经过 -d 分隔后,使用 -f n 取出第 n 个区间 +-c :以字符为单位取出区间 +``` + +示例 1:last 显示登入者的信息,取出用户名。 + +```html +$ last +root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in +root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33) +root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16) + +$ last | cut -d ' ' -f 1 +``` + +示例 2:将 export 输出的信息,取出第 12 字符以后的所有字符串。 + +```html +$ export +declare -x HISTCONTROL="ignoredups" +declare -x HISTSIZE="1000" +declare -x HOME="/home/dmtsai" +declare -x HOSTNAME="study.centos.vbird" +.....(其他省略)..... + +$ export | cut -c 12- +``` + +### 排序指令 + +**sort** 用于排序。 + +```html +$ sort [-fbMnrtuk] [file or stdin] +-f :忽略大小写 +-b :忽略最前面的空格 +-M :以月份的名字来排序,例如 JAN,DEC +-n :使用数字 +-r :反向排序 +-u :相当于 unique,重复的内容只出现一次 +-t :分隔符,默认为 tab +-k :指定排序的区间 +``` + +示例:/etc/passwd 文件内容以 : 来分隔,要求以第三列进行排序。 + +```html +$ cat /etc/passwd | sort -t ':' -k 3 +root:x:0:0:root:/root:/bin/bash +dmtsai:x:1000:1000:dmtsai:/home/dmtsai:/bin/bash +alex:x:1001:1002::/home/alex:/bin/bash +arod:x:1002:1003::/home/arod:/bin/bash +``` + +**uniq** 可以将重复的数据只取一个。 + +```html +$ uniq [-ic] +-i :忽略大小写 +-c :进行计数 +``` + +示例:取得每个人的登录总次数 + +```html +$ last | cut -d ' ' -f 1 | sort | uniq -c +1 +6 (unknown +47 dmtsai +4 reboot +7 root +1 wtmp +``` + +### 双向输出重定向 + +输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 + +```html +$ tee [-a] file +``` + +### 字符转换指令 + +**tr** 用来删除一行中的字符,或者对字符进行替换。 + +```html +$ tr [-ds] SET1 ... +-d : 删除行中 SET1 这个字符串 +``` + +示例,将 last 输出的信息所有小写转换为大写。 + +```html +$ last | tr '[a-z]' '[A-Z]' +``` + + **col** 将 tab 字符转为空格字符。 + +```html +$ col [-xb] +-x : 将 tab 键转换成对等的空格键 +``` + +**expand** 将 tab 转换一定数量的空格,默认是 8 个。 + +```html +$ expand [-t] file +-t :tab 转为空格的数量 +``` + +**join** 将有相同数据的那一行合并在一起。 + +```html +$ join [-ti12] file1 file2 +-t :分隔符,默认为空格 +-i :忽略大小写的差异 +-1 :第一个文件所用的比较字段 +-2 :第二个文件所用的比较字段 +``` + +**paste** 直接将两行粘贴在一起。 + +```html +$ paste [-d] file1 file2 +-d :分隔符,默认为 tab +``` + +### 分区指令 + +**split** 将一个文件划分成多个文件。 + +```html +$ split [-bl] file PREFIX +-b :以大小来进行分区,可加单位,例如 b, k, m 等 +-l :以行数来进行分区。 +- PREFIX :分区文件的前导名称 +``` + +## 九、正则表达式 + +### grep + +g/re/p(globally search a regular expression and print),使用正则表示式进行全局查找并打印。 + +```html +$ grep [-acinv] [--color=auto] 搜寻字符串 filename +-c : 统计匹配到行的个数 +-i : 忽略大小写 +-n : 输出行号 +-v : 反向选择,也就是显示出没有 搜寻字符串 内容的那一行 +--color=auto :找到的关键字加颜色显示 +``` + +示例:把含有 the 字符串的行提取出来(注意默认会有 --color=auto 选项,因此以下内容在 Linux 中有颜色显示 the 字符串) + +```html +$ grep -n 'the' regular_express.txt +8:I can't finish the test. +12:the symbol '*' is represented as start. +15:You are the best is mean you are the no. 1. +16:The world Happy is the same with "glad". +18:google is the best tools for search keyword +``` + +示例:正则表达式 a{m,n} 用来匹配字符 a m\~n 次,这里需要将 { 和 } 进行转义,因为它们在 shell 是有特殊意义的。 + +```html +$ grep -n 'a\{2,5\}' regular_express.txt +``` + +### printf + +用于格式化输出。它不属于管道命令,在给 printf 传数据时需要使用 $( ) 形式。 + +```html +$ printf '%10s %5i %5i %5i %8.2f \n' $(cat printf.txt) + DmTsai 80 60 92 77.33 + VBird 75 55 80 70.00 + Ken 60 90 70 73.33 +``` + +### awk + +是由 Alfred Aho,Peter Weinberger 和 Brian Kernighan 创造,awk 这个名字就是这三个创始人名字的首字母。 + +awk 每次处理一行,处理的最小单位是字段,每个字段的命名方式为:\$n,n 为字段号,从 1 开始,\$0 表示一整行。 + +示例:取出最近五个登录用户的用户名和 IP。首先用 last -n 5 取出用最近五个登录用户的所有信息,可以看到用户名和 IP 分别在第 1 列和第 3 列,我们用 \$1 和 \$3 就能取出这两个字段,然后用 print 进行打印。 + +```html +$ last -n 5 +dmtsai pts/0 192.168.1.100 Tue Jul 14 17:32 still logged in +dmtsai pts/0 192.168.1.100 Thu Jul 9 23:36 - 02:58 (03:22) +dmtsai pts/0 192.168.1.100 Thu Jul 9 17:23 - 23:36 (06:12) +dmtsai pts/0 192.168.1.100 Thu Jul 9 08:02 - 08:17 (00:14) +dmtsai tty1 Fri May 29 11:55 - 12:11 (00:15) +``` + +```html +$ last -n 5 | awk '{print $1 "\t" $3}' +dmtsai 192.168.1.100 +dmtsai 192.168.1.100 +dmtsai 192.168.1.100 +dmtsai 192.168.1.100 +dmtsai Fri +``` + +可以根据字段的某些条件进行匹配,例如匹配字段小于某个值的那一行数据。 + +```html +$ awk '条件类型 1 {动作 1} 条件类型 2 {动作 2} ...' filename +``` + +示例:/etc/passwd 文件第三个字段为 UID,对 UID 小于 10 的数据进行处理。 + +```text +$ cat /etc/passwd | awk 'BEGIN {FS=":"} $3 < 10 {print $1 "\t " $3}' +root 0 +bin 1 +daemon 2 +``` + +awk 变量: + +| 变量名称 | 代表意义 | +| :--: | -- | +| NF | 每一行拥有的字段总数 | +| NR | 目前所处理的是第几行数据 | +| FS | 目前的分隔字符,默认是空格键 | + +示例:显示正在处理的行号以及每一行有多少字段 + +```html +$ last -n 5 | awk '{print $1 "\t lines: " NR "\t columns: " NF}' +dmtsai lines: 1 columns: 10 +dmtsai lines: 2 columns: 10 +dmtsai lines: 3 columns: 10 +dmtsai lines: 4 columns: 10 +dmtsai lines: 5 columns: 9 +``` + +## 十、进程管理 + +### 查看进程 + +#### 1. ps + +查看某个时间点的进程信息。 + +示例:查看自己的进程 + +```sh +## ps -l +``` + +示例:查看系统所有进程 + +```sh +## ps aux +``` + +示例:查看特定的进程 + +```sh +## ps aux | grep threadx +``` + +#### 2. pstree + +查看进程树。 + +示例:查看所有进程树 + +```sh +## pstree -A +``` + +#### 3. top + +实时显示进程信息。 + +示例:两秒钟刷新一次 + +```sh +## top -d 2 +``` + +#### 4. netstat + +查看占用端口的进程 + +示例:查看特定端口的进程 + +```sh +## netstat -anp | grep port +``` + +### 进程状态 + +| 状态 | 说明 | +| :---: | --- | +| R | running or runnable (on run queue)
正在执行或者可执行,此时进程位于执行队列中。| +| D | uninterruptible sleep (usually I/O)
不可中断阻塞,通常为 IO 阻塞。 | +| S | interruptible sleep (waiting for an event to complete)
可中断阻塞,此时进程正在等待某个事件完成。| +| Z | zombie (terminated but not reaped by its parent)
僵死,进程已经终止但是尚未被其父进程获取信息。| +| T | stopped (either by a job control signal or because it is being traced)
结束,进程既可以被作业控制信号结束,也可能是正在被追踪。| +
+ +

+ +### SIGCHLD + +当一个子进程改变了它的状态时(停止运行,继续运行或者退出),有两件事会发生在父进程中: + +- 得到 SIGCHLD 信号; +- waitpid() 或者 wait() 调用会返回。 + +其中子进程发送的 SIGCHLD 信号包含了子进程的信息,比如进程 ID、进程状态、进程使用 CPU 的时间等。 + +在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。 + +

+ +### wait() + +```c +pid_t wait(int *status) +``` + +父进程调用 wait() 会一直阻塞,直到收到一个子进程退出的 SIGCHLD 信号,之后 wait() 函数会销毁子进程并返回。 + +如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1,同时 errno 被置为 ECHILD。 + +参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL。 + +### waitpid() + +```c +pid_t waitpid(pid_t pid, int *status, int options) +``` + +作用和 wait() 完全相同,但是多了两个可由用户控制的参数 pid 和 options。 + +pid 参数指示一个子进程的 ID,表示只关心这个子进程退出的 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号。 + +options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 waitpid() 调用变成非阻塞的,也就是说它会立即返回,父进程可以继续执行其它任务。 + +### 孤儿进程 + +一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。 + +孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。 + +由于孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。 + +### 僵尸进程 + +一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。 + +僵尸进程通过 ps 命令显示出来的状态为 Z(zombie)。 + +系统所能使用的进程号是有限的,如果产生大量僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。 + +要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。 + +## 参考资料 + +- 鸟哥. 鸟 哥 的 Linux 私 房 菜 基 础 篇 第 三 版[J]. 2009. +- [Linux 平台上的软件包管理](https://www.ibm.com/developerworks/cn/linux/l-cn-rpmdpkg/index.html) +- [Linux 之守护进程、僵死进程与孤儿进程](http://liubigbin.github.io/2016/03/11/Linux-%E4%B9%8B%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E3%80%81%E5%83%B5%E6%AD%BB%E8%BF%9B%E7%A8%8B%E4%B8%8E%E5%AD%A4%E5%84%BF%E8%BF%9B%E7%A8%8B/) +- [What is the difference between a symbolic link and a hard link?](https://stackoverflow.com/questions/185899/what-is-the-difference-between-a-symbolic-link-and-a-hard-link) +- [Linux process states](https://idea.popcount.org/2012-12-11-linux-process-states/) +- [GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table) +- [详解 wait 和 waitpid 函数](https://blog.csdn.net/kevinhg/article/details/7001719) +- [IDE、SATA、SCSI、SAS、FC、SSD 硬盘类型介绍](https://blog.csdn.net/tianlesoftware/article/details/6009110) +- [Akai IB-301S SCSI Interface for S2800,S3000](http://www.mpchunter.com/s3000/akai-ib-301s-scsi-interface-for-s2800s3000/) +- [Parallel ATA](https://en.wikipedia.org/wiki/Parallel_ATA) +- [ADATA XPG SX900 256GB SATA 3 SSD Review – Expanded Capacity and SandForce Driven Speed](http://www.thessdreview.com/our-reviews/adata-xpg-sx900-256gb-sata-3-ssd-review-expanded-capacity-and-sandforce-driven-speed/4/) +- [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) +- [硬盘](https://zh.wikipedia.org/wiki/%E7%A1%AC%E7%9B%98) +- [Difference between SAS and SATA](http://www.differencebetween.info/difference-between-sas-and-sata) +- [BIOS](https://zh.wikipedia.org/wiki/BIOS) +- [File system design case studies](https://www.cs.rutgers.edu/\~pxk/416/notes/13-fs-studies.html) +- [Programming Project #4](https://classes.soe.ucsc.edu/cmps111/Fall08/proj4.shtml) +- [FILE SYSTEM DESIGN](http://web.cs.ucla.edu/classes/fall14/cs111/scribe/11a/index.html) diff --git a/docs/notes/MySQL.md b/notes/MySQL.md similarity index 75% rename from docs/notes/MySQL.md rename to notes/MySQL.md index ef17c89da9..9b198fd43b 100644 --- a/docs/notes/MySQL.md +++ b/notes/MySQL.md @@ -1,40 +1,42 @@ +# MySQL -* [一、索引](#一索引) - * [B+ Tree 原理](#b-tree-原理) - * [MySQL 索引](#mysql-索引) - * [索引优化](#索引优化) - * [索引的优点](#索引的优点) - * [索引的使用条件](#索引的使用条件) -* [二、查询性能优化](#二查询性能优化) - * [使用 Explain 进行分析](#使用-explain-进行分析) - * [优化数据访问](#优化数据访问) - * [重构查询方式](#重构查询方式) -* [三、存储引擎](#三存储引擎) - * [InnoDB](#innodb) - * [MyISAM](#myisam) - * [比较](#比较) -* [四、数据类型](#四数据类型) - * [整型](#整型) - * [浮点数](#浮点数) - * [字符串](#字符串) - * [时间和日期](#时间和日期) -* [五、切分](#五切分) - * [水平切分](#水平切分) - * [垂直切分](#垂直切分) - * [Sharding 策略](#sharding-策略) - * [Sharding 存在的问题](#sharding-存在的问题) -* [六、复制](#六复制) - * [主从复制](#主从复制) - * [读写分离](#读写分离) -* [参考资料](#参考资料) +* [MySQL](#mysql) + * [一、索引](#一索引) + * [B+ Tree 原理](#b-tree-原理) + * [MySQL 索引](#mysql-索引) + * [索引优化](#索引优化) + * [索引的优点](#索引的优点) + * [索引的使用条件](#索引的使用条件) + * [二、查询性能优化](#二查询性能优化) + * [使用 Explain 进行分析](#使用-explain-进行分析) + * [优化数据访问](#优化数据访问) + * [重构查询方式](#重构查询方式) + * [三、存储引擎](#三存储引擎) + * [InnoDB](#innodb) + * [MyISAM](#myisam) + * [比较](#比较) + * [四、数据类型](#四数据类型) + * [整型](#整型) + * [浮点数](#浮点数) + * [字符串](#字符串) + * [时间和日期](#时间和日期) + * [五、切分](#五切分) + * [水平切分](#水平切分) + * [垂直切分](#垂直切分) + * [Sharding 策略](#sharding-策略) + * [Sharding 存在的问题](#sharding-存在的问题) + * [六、复制](#六复制) + * [主从复制](#主从复制) + * [读写分离](#读写分离) + * [参考资料](#参考资料) -# 一、索引 +## 一、索引 -## B+ Tree 原理 +### B+ Tree 原理 -### 1. 数据结构 +#### 1. 数据结构 B Tree 指的是 Balance Tree,也就是平衡树。平衡树是一颗查找树,并且所有叶子节点位于同一层。 @@ -42,41 +44,43 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。 -

+

-### 2. 操作 +#### 2. 操作 进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。 -插入删除操作会破坏平衡树的平衡性,因此在插入删除操作之后,需要对树进行一个分裂、合并、旋转等操作来维护平衡性。 +插入删除操作会破坏平衡树的平衡性,因此在进行插入删除操作之后,需要对树进行分裂、合并、旋转等操作来维护平衡性。 -### 3. 与红黑树的比较 +#### 3. 与红黑树的比较 -红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,主要有以下两个原因: +红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,这是因为使用 B+ 树访问磁盘数据有更高的性能。 -(一)更少的查找次数 +(一)B+ 树有更低的树高 -平衡树查找操作的时间复杂度和树高 h 相关,O(h)=O(logdN),其中 d 为每个节点的出度。 +平衡树的树高 O(h)=O(logdN),其中 d 为每个节点的出度。红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多。 -红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多,查找的次数也就更多。 +(二)磁盘访问原理 -(二)利用磁盘预读特性 +操作系统一般将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点。 -为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,速度会非常快。 +如果数据不在同一个磁盘块上,那么通常需要移动制动手臂进行寻道,而制动手臂因为其物理结构导致了移动效率低下,从而增加磁盘数据读取时间。B+ 树相对于红黑树有更低的树高,进行寻道的次数与树高成正比,在同一个磁盘块上进行访问只需要很短的磁盘旋转时间,所以 B+ 树更适合磁盘数据的读取。 -操作系统一般将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点。并且可以利用预读特性,相邻的节点也能够被预先载入。 +(三)磁盘预读特性 -## MySQL 索引 +为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的磁盘旋转时间,速度会非常快。并且可以利用预读特性,相邻的节点也能够被预先载入。 + +### MySQL 索引 索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。 -### 1. B+Tree 索引 +#### 1. B+Tree 索引 是大多数 MySQL 存储引擎的默认索引类型。 因为不再需要进行全表扫描,只需要对树进行搜索即可,所以查找速度快很多。 -除了用于查找,还可以用于排序和分组。 +因为 B+ Tree 的有序性,所以除了用于查找,还可以用于排序和分组。 可以指定多个列作为索引列,多个索引列共同组成键。 @@ -84,13 +88,13 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。 -

+

辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。 -

+

-### 2. 哈希索引 +#### 2. 哈希索引 哈希索引能以 O(1) 时间进行查找,但是失去了有序性: @@ -99,7 +103,7 @@ InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节 InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。 -### 3. 全文索引 +#### 3. 全文索引 MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。 @@ -109,15 +113,15 @@ MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而 InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。 -### 4. 空间数据索引 +#### 4. 空间数据索引 MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 必须使用 GIS 相关的函数来维护数据。 -## 索引优化 +### 索引优化 -### 1. 独立的列 +#### 1. 独立的列 在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。 @@ -127,7 +131,7 @@ MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数 SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5; ``` -### 2. 多列索引 +#### 2. 多列索引 在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。 @@ -136,11 +140,11 @@ SELECT film_id, actor_ id FROM sakila.film_actor WHERE actor_id = 1 AND film_id = 1; ``` -### 3. 索引列的顺序 +#### 3. 索引列的顺序 让选择性最强的索引列放在前面。 -索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,查询效率也越高。 +索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。 例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。 @@ -157,13 +161,13 @@ customer_id_selectivity: 0.0373 COUNT(*): 16049 ``` -### 4. 前缀索引 +#### 4. 前缀索引 对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。 -对于前缀长度的选取需要根据索引选择性来确定。 +前缀长度的选取需要根据索引选择性来确定。 -### 5. 覆盖索引 +#### 5. 覆盖索引 索引包含所有需要查询的字段的值。 @@ -173,15 +177,15 @@ customer_id_selectivity: 0.0373 - 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。 - 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。 -## 索引的优点 +### 索引的优点 - 大大减少了服务器需要扫描的数据行数。 -- 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,因为不需要排序和分组,也就不需要创建临时表)。 +- 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,不需要排序和分组,也就不需要创建临时表)。 - 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。 -## 索引的使用条件 +### 索引的使用条件 - 对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效; @@ -189,9 +193,9 @@ customer_id_selectivity: 0.0373 - 但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。 -# 二、查询性能优化 +## 二、查询性能优化 -## 使用 Explain 进行分析 +### 使用 Explain 进行分析 Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。 @@ -201,21 +205,21 @@ Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explai - key : 使用的索引 - rows : 扫描的行数 -## 优化数据访问 +### 优化数据访问 -### 1. 减少请求的数据量 +#### 1. 减少请求的数据量 - 只返回必要的列:最好不要使用 SELECT * 语句。 - 只返回必要的行:使用 LIMIT 语句来限制返回的数据。 - 缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。 -### 2. 减少服务器端扫描的行数 +#### 2. 减少服务器端扫描的行数 最有效的方式是使用索引来覆盖查询。 -## 重构查询方式 +### 重构查询方式 -### 1. 切分大查询 +#### 1. 切分大查询 一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。 @@ -231,7 +235,7 @@ do { } while rows_affected > 0 ``` -### 2. 分解大连接查询 +#### 2. 分解大连接查询 将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有: @@ -242,7 +246,7 @@ do { - 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。 ```sql -SELECT * FROM tab +SELECT * FROM tag JOIN tag_post ON tag_post.tag_id=tag.id JOIN post ON tag_post.post_id=post.id WHERE tag.tag='mysql'; @@ -254,13 +258,13 @@ SELECT * FROM tag_post WHERE tag_id=1234; SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); ``` -# 三、存储引擎 +## 三、存储引擎 -## InnoDB +### InnoDB 是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。 -实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ 间隙锁(Next-Key Locking)防止幻影读。 +实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。 主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。 @@ -268,7 +272,7 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。 -## MyISAM +### MyISAM 设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。 @@ -282,7 +286,7 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); 如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。 -## 比较 +### 比较 - 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。 @@ -296,21 +300,21 @@ SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904); - 其它特性:MyISAM 支持压缩表和空间数据索引。 -# 四、数据类型 +## 四、数据类型 -## 整型 +### 整型 TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。 INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。 -## 浮点数 +### 浮点数 FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。 FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。 -## 字符串 +### 字符串 主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。 @@ -318,19 +322,19 @@ VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内 在进行存储和检索时,会保留 VARCHAR 末尾的空格,而会删除 CHAR 末尾的空格。 -## 时间和日期 +### 时间和日期 MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。 -### 1. DATETIME +#### 1. DATETIME -能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 +能够保存从 1000 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 它与时区无关。 默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。 -### 2. TIMESTAMP +#### 2. TIMESTAMP 和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年到 2038 年。 @@ -342,59 +346,59 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。 -# 五、切分 +## 五、切分 -## 水平切分 +### 水平切分 水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。 当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。 -

+

-## 垂直切分 +### 垂直切分 垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。 在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。 -

+

-## Sharding 策略 +### Sharding 策略 - 哈希取模:hash(key) % N; - 范围:可以是 ID 范围也可以是时间范围; - 映射表:使用单独的一个数据库来存储映射关系。 -## Sharding 存在的问题 +### Sharding 存在的问题 -### 1. 事务问题 +#### 1. 事务问题 使用分布式事务来解决,比如 XA 接口。 -### 2. 连接 +#### 2. 连接 可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。 -### 3. ID 唯一性 +#### 3. ID 唯一性 - 使用全局唯一 ID(GUID) - 为每个分片指定一个 ID 范围 - 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法) -# 六、复制 +## 六、复制 -## 主从复制 +### 主从复制 主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。 -- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志(Binary log)中。 -- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的重放日志(Replay log)中。 -- **SQL 线程** :负责读取重放日志并重放其中的 SQL 语句。 +- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志(Binary log)中。 +- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。 +- **SQL 线程** :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。 -

+

-## 读写分离 +### 读写分离 主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。 @@ -406,9 +410,9 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。 -

+

-# 参考资料 +## 参考资料 - BaronScbwartz, PeterZaitsev, VadimTkacbenko, 等. 高性能 MySQL[M]. 电子工业出版社, 2013. - 姜承尧. MySQL 技术内幕: InnoDB 存储引擎 [M]. 机械工业出版社, 2011. @@ -421,9 +425,3 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 - [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6) - [大众点评订单系统分库分表实践](https://tech.meituan.com/dianping_order_db_sharding.html) - [B + 树](https://zh.wikipedia.org/wiki/B%2B%E6%A0%91) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/docs/notes/Redis.md b/notes/Redis.md similarity index 76% rename from docs/notes/Redis.md rename to notes/Redis.md index 67837c438f..b41f5d8c57 100644 --- a/docs/notes/Redis.md +++ b/notes/Redis.md @@ -1,51 +1,53 @@ +# Redis -* [一、概述](#一概述) -* [二、数据类型](#二数据类型) - * [STRING](#string) - * [LIST](#list) - * [SET](#set) - * [HASH](#hash) - * [ZSET](#zset) -* [三、数据结构](#三数据结构) - * [字典](#字典) - * [跳跃表](#跳跃表) -* [四、使用场景](#四使用场景) - * [计数器](#计数器) - * [缓存](#缓存) - * [查找表](#查找表) - * [消息队列](#消息队列) - * [会话缓存](#会话缓存) - * [分布式锁实现](#分布式锁实现) - * [其它](#其它) -* [五、Redis 与 Memcached](#五redis-与-memcached) - * [数据类型](#数据类型) - * [数据持久化](#数据持久化) - * [分布式](#分布式) - * [内存管理机制](#内存管理机制) -* [六、键的过期时间](#六键的过期时间) -* [七、数据淘汰策略](#七数据淘汰策略) -* [八、持久化](#八持久化) - * [RDB 持久化](#rdb-持久化) - * [AOF 持久化](#aof-持久化) -* [九、事务](#九事务) -* [十、事件](#十事件) - * [文件事件](#文件事件) - * [时间事件](#时间事件) - * [事件的调度与执行](#事件的调度与执行) -* [十一、复制](#十一复制) - * [连接过程](#连接过程) - * [主从链](#主从链) -* [十二、Sentinel](#十二sentinel) -* [十三、分片](#十三分片) -* [十四、一个简单的论坛系统分析](#十四一个简单的论坛系统分析) - * [文章信息](#文章信息) - * [点赞功能](#点赞功能) - * [对文章进行排序](#对文章进行排序) -* [参考资料](#参考资料) +* [Redis](#redis) + * [一、概述](#一概述) + * [二、数据类型](#二数据类型) + * [STRING](#string) + * [LIST](#list) + * [SET](#set) + * [HASH](#hash) + * [ZSET](#zset) + * [三、数据结构](#三数据结构) + * [字典](#字典) + * [跳跃表](#跳跃表) + * [四、使用场景](#四使用场景) + * [计数器](#计数器) + * [缓存](#缓存) + * [查找表](#查找表) + * [消息队列](#消息队列) + * [会话缓存](#会话缓存) + * [分布式锁实现](#分布式锁实现) + * [其它](#其它) + * [五、Redis 与 Memcached](#五redis-与-memcached) + * [数据类型](#数据类型) + * [数据持久化](#数据持久化) + * [分布式](#分布式) + * [内存管理机制](#内存管理机制) + * [六、键的过期时间](#六键的过期时间) + * [七、数据淘汰策略](#七数据淘汰策略) + * [八、持久化](#八持久化) + * [RDB 持久化](#rdb-持久化) + * [AOF 持久化](#aof-持久化) + * [九、事务](#九事务) + * [十、事件](#十事件) + * [文件事件](#文件事件) + * [时间事件](#时间事件) + * [事件的调度与执行](#事件的调度与执行) + * [十一、复制](#十一复制) + * [连接过程](#连接过程) + * [主从链](#主从链) + * [十二、Sentinel](#十二sentinel) + * [十三、分片](#十三分片) + * [十四、一个简单的论坛系统分析](#十四一个简单的论坛系统分析) + * [文章信息](#文章信息) + * [点赞功能](#点赞功能) + * [对文章进行排序](#对文章进行排序) + * [参考资料](#参考资料) -# 一、概述 +## 一、概述 Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。 @@ -53,21 +55,21 @@ Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以 Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。 -# 二、数据类型 +## 二、数据类型 | 数据类型 | 可以存储的值 | 操作 | | :--: | :--: | :--: | -| STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作 | -| LIST | 列表 | 从两端压入或者弹出元素
对单个或者多个元素
进行修剪,只保留一个范围内的元素 | -| SET | 无序集合 | 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素 | -| HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在| -| ZSET | 有序集合 | 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名 | +| STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作\ 对整数和浮点数执行自增或者自减操作 | +| LIST | 列表 | 从两端压入或者弹出元素 \ 对单个或者多个元素进行修剪,\ 只保留一个范围内的元素 | +| SET | 无序集合 | 添加、获取、移除单个元素\ 检查一个元素是否存在于集合中\ 计算交集、并集、差集\ 从集合里面随机获取元素 | +| HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对\ 获取所有键值对\ 检查某个键是否存在| +| ZSET | 有序集合 | 添加、获取、删除元素\ 根据分值范围或者成员来获取元素\ 计算一个键的排名 | > [What Redis data structures look like](https://redislabs.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/) -## STRING +### STRING -

+

```html > set hello world @@ -80,9 +82,9 @@ OK (nil) ``` -## LIST +### LIST -

+

```html > rpush list-key item @@ -108,9 +110,9 @@ OK 2) "item" ``` -## SET +### SET -

+

```html > sadd set-key item @@ -142,9 +144,9 @@ OK 2) "item3" ``` -## HASH +### HASH -

+

```html > hset hash-key sub-key1 value1 @@ -173,9 +175,9 @@ OK 2) "value1" ``` -## ZSET +### ZSET -

+

```html > zadd zset-key 728 member1 @@ -205,11 +207,11 @@ OK 2) "982" ``` -# 三、数据结构 +## 三、数据结构 -## 字典 +### 字典 -dictht 是一个散列表结构,使用拉链法保存哈希冲突。 +dictht 是一个散列表结构,使用拉链法解决哈希冲突。 ```c /* This is our hash table structure. Every dictionary has two of this as we @@ -311,17 +313,17 @@ int dictRehash(dict *d, int n) { } ``` -## 跳跃表 +### 跳跃表 是有序集合的底层实现之一。 跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。 -

+

在查找时,从上层指针开始查找,找到对应的区间之后再到下一层去查找。下图演示了查找 22 的过程。 -

+

与红黑树等平衡树相比,跳跃表具有以下优点: @@ -329,83 +331,83 @@ int dictRehash(dict *d, int n) { - 更容易实现; - 支持无锁操作。 -# 四、使用场景 +## 四、使用场景 -## 计数器 +### 计数器 可以对 String 进行自增自减运算,从而实现计数器功能。 Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。 -## 缓存 +### 缓存 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。 -## 查找表 +### 查找表 例如 DNS 记录就很适合使用 Redis 进行存储。 查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。 -## 消息队列 +### 消息队列 -List 是一个双向链表,可以通过 lpop 和 lpush 写入和读取消息。 +List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息 不过最好使用 Kafka、RabbitMQ 等消息中间件。 -## 会话缓存 +### 会话缓存 可以使用 Redis 来统一存储多台应用服务器的会话信息。 当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。 -## 分布式锁实现 +### 分布式锁实现 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。 -可以使用 Reids 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。 +可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。 -## 其它 +### 其它 Set 可以实现交集、并集等操作,从而实现共同好友等功能。 ZSet 可以实现有序性操作,从而实现排行榜等功能。 -# 五、Redis 与 Memcached +## 五、Redis 与 Memcached 两者都是非关系型内存键值数据库,主要有以下不同: -## 数据类型 +### 数据类型 Memcached 仅支持字符串类型,而 Redis 支持五种不同的数据类型,可以更灵活地解决问题。 -## 数据持久化 +### 数据持久化 Redis 支持两种持久化策略:RDB 快照和 AOF 日志,而 Memcached 不支持持久化。 -## 分布式 +### 分布式 Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。 Redis Cluster 实现了分布式的支持。 -## 内存管理机制 +### 内存管理机制 - 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中。 - Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了。 -# 六、键的过期时间 +## 六、键的过期时间 Redis 可以为每个键设置过期时间,当键过期时,会自动删除该键。 对于散列表这种容器,只能为整个键设置过期时间(整个散列表),而不能为键里面的单个元素设置过期时间。 -# 七、数据淘汰策略 +## 七、数据淘汰策略 可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。 -Reids 具体有 6 种淘汰策略: +Redis 具体有 6 种淘汰策略: | 策略 | 描述 | | :--: | :--: | @@ -422,11 +424,11 @@ Reids 具体有 6 种淘汰策略: Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰。 -# 八、持久化 +## 八、持久化 Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。 -## RDB 持久化 +### RDB 持久化 将某个时间点的所有数据都存放到硬盘上。 @@ -436,11 +438,11 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需 如果数据量很大,保存快照的时间会很长。 -## AOF 持久化 +### AOF 持久化 将写命令添加到 AOF 文件(Append Only File)的末尾。 -使用 AOF 持久化需要设置同步选项,从而确保写命令什么时候会同步到磁盘文件上。这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。有以下同步选项: +使用 AOF 持久化需要设置同步选项,从而确保写命令同步到磁盘文件上的时机。这是因为对文件进行写入并不会马上将内容同步到磁盘上,而是先存储到缓冲区,然后由操作系统决定什么时候同步到磁盘。有以下同步选项: | 选项 | 同步频率 | | :--: | :--: | @@ -454,7 +456,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需 随着服务器写请求的增多,AOF 文件会越来越大。Redis 提供了一种将 AOF 重写的特性,能够去除 AOF 文件中的冗余写命令。 -# 九、事务 +## 九、事务 一个事务包含了多个命令,服务器在执行事务期间,不会改去执行其它客户端的命令请求。 @@ -462,19 +464,19 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需 Redis 最简单的事务实现方式是使用 MULTI 和 EXEC 命令将事务操作包围起来。 -# 十、事件 +## 十、事件 Redis 服务器是一个事件驱动程序。 -## 文件事件 +### 文件事件 服务器通过套接字与客户端或者其它服务器进行通信,文件事件就是对套接字操作的抽象。 Redis 基于 Reactor 模式开发了自己的网络事件处理器,使用 I/O 多路复用程序来同时监听多个套接字,并将到达的事件传送给文件事件分派器,分派器会根据套接字产生的事件类型调用相应的事件处理器。 -

+

-## 时间事件 +### 时间事件 服务器有一些操作需要在给定的时间点执行,时间事件是对这类定时操作的抽象。 @@ -485,7 +487,7 @@ Redis 基于 Reactor 模式开发了自己的网络事件处理器,使用 I/O Redis 将所有时间事件都放在一个无序链表中,通过遍历整个链表查找出已到达的时间事件,并调用相应的事件处理器。 -## 事件的调度与执行 +### 事件的调度与执行 服务器需要不断监听文件事件的套接字才能得到待处理的文件事件,但是不能一直监听,否则时间事件无法在规定的时间内执行,因此监听时间应该根据距离现在最近的时间事件来决定。 @@ -525,15 +527,15 @@ def main(): 从事件处理的角度来看,服务器运行流程如下: -

+

-# 十一、复制 +## 十一、复制 通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。 一个从服务器只能有一个主服务器,并且不支持主主复制。 -## 连接过程 +### 连接过程 1. 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令; @@ -541,21 +543,21 @@ def main(): 3. 主服务器每执行一次写命令,就向从服务器发送相同的写命令。 -## 主从链 +### 主从链 随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。 -

+

-# 十二、Sentinel +## 十二、Sentinel Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。 -# 十三、分片 +## 十三、分片 分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,这种方法在解决某些问题时可以获得线性级别的性能提升。 -假设有 4 个 Reids 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,... ,有不同的方式来选择一个指定的键存储在哪个实例中。 +假设有 4 个 Redis 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,... ,有不同的方式来选择一个指定的键存储在哪个实例中。 - 最简单的方式是范围分片,例如用户 id 从 0\~1000 的存储到实例 R0 中,用户 id 从 1001\~2000 的存储到实例 R1 中,等等。但是这样需要维护一张映射范围表,维护操作代价很高。 - 还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。 @@ -566,7 +568,7 @@ Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入 - 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。 - 服务器分片:Redis Cluster。 -# 十四、一个简单的论坛系统分析 +## 十四、一个简单的论坛系统分析 该论坛系统功能如下: @@ -574,29 +576,29 @@ Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入 - 可以对文章进行点赞; - 在首页可以按文章的发布时间或者文章的点赞数进行排序显示。 -## 文章信息 +### 文章信息 文章包括标题、作者、赞数等信息,在关系型数据库中很容易构建一张表来存储这些信息,在 Redis 中可以使用 HASH 来存储每种信息以及其对应的值的映射。 Redis 没有关系型数据库中的表这一概念来将同种类型的数据存放在一起,而是使用命名空间的方式来实现这一功能。键名的前面部分存储命名空间,后面部分的内容存储 ID,通常使用 : 来进行分隔。例如下面的 HASH 的键名为 article:92617,其中 article 为命名空间,ID 为 92617。 -

+

-## 点赞功能 +### 点赞功能 当有用户为一篇文章点赞时,除了要对该文章的 votes 字段进行加 1 操作,还必须记录该用户已经对该文章进行了点赞,防止用户点赞次数超过 1。可以建立文章的已投票用户集合来进行记录。 为了节约内存,规定一篇文章发布满一周之后,就不能再对它进行投票,而文章的已投票集合也会被删除,可以为文章的已投票集合设置一个一周的过期时间就能实现这个规定。 -

+

-## 对文章进行排序 +### 对文章进行排序 为了按发布时间和点赞数进行排序,可以建立一个文章发布时间的有序集合和一个文章点赞数的有序集合。(下图中的 score 就是这里所说的点赞数;下面所示的有序集合分值并不直接是时间和点赞数,而是根据时间和点赞数间接计算出来的) -

+

-# 参考资料 +## 参考资料 - Carlson J L. Redis in Action[J]. Media.johnwiley.com.au, 2013. - [黄健宏. Redis 设计与实现 [M]. 机械工业出版社, 2014.](http://redisbook.com/index.html) @@ -606,9 +608,3 @@ Redis 没有关系型数据库中的表这一概念来将同种类型的数据 - [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide) - [Redis 应用场景](http://www.scienjus.com/redis-use-case/) - [Using Redis as an LRU cache](https://redis.io/topics/lru-cache) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/SQL \347\273\203\344\271\240.md" "b/notes/SQL \347\273\203\344\271\240.md" new file mode 100644 index 0000000000..97421e1db9 --- /dev/null +++ "b/notes/SQL \347\273\203\344\271\240.md" @@ -0,0 +1,1093 @@ +# SQL 练习 + +* [SQL 练习](#sql-练习) + * [595. Big Countries](#595-big-countries) + * [627. Swap Salary](#627-swap-salary) + * [620. Not Boring Movies](#620-not-boring-movies) + * [596. Classes More Than 5 Students](#596-classes-more-than-5-students) + * [182. Duplicate Emails](#182-duplicate-emails) + * [196. Delete Duplicate Emails](#196-delete-duplicate-emails) + * [175. Combine Two Tables](#175-combine-two-tables) + * [181. Employees Earning More Than Their Managers](#181-employees-earning-more-than-their-managers) + * [183. Customers Who Never Order](#183-customers-who-never-order) + * [184. Department Highest Salary](#184-department-highest-salary) + * [176. Second Highest Salary](#176-second-highest-salary) + * [177. Nth Highest Salary](#177-nth-highest-salary) + * [178. Rank Scores](#178-rank-scores) + * [180. Consecutive Numbers](#180-consecutive-numbers) + * [626. Exchange Seats](#626-exchange-seats) + + + +## 595. Big Countries + +https://leetcode.com/problems/big-countries/description/ + +### Description + +```html ++-----------------+------------+------------+--------------+---------------+ +| name | continent | area | population | gdp | ++-----------------+------------+------------+--------------+---------------+ +| Afghanistan | Asia | 652230 | 25500100 | 20343000 | +| Albania | Europe | 28748 | 2831741 | 12960000 | +| Algeria | Africa | 2381741 | 37100000 | 188681000 | +| Andorra | Europe | 468 | 78115 | 3712000 | +| Angola | Africa | 1246700 | 20609294 | 100990000 | ++-----------------+------------+------------+--------------+---------------+ +``` + +查找面积超过 3,000,000 或者人口数超过 25,000,000 的国家。 + +```html ++--------------+-------------+--------------+ +| name | population | area | ++--------------+-------------+--------------+ +| Afghanistan | 25500100 | 652230 | +| Algeria | 37100000 | 2381741 | ++--------------+-------------+--------------+ +``` + +### Solution + +```sql +SELECT name, + population, + area +FROM + World +WHERE + area > 3000000 + OR population > 25000000; +``` + +### SQL Schema + +SQL Schema 用于在本地环境下创建表结构并导入数据,从而方便在本地环境调试。 + +```sql +DROP TABLE +IF + EXISTS World; +CREATE TABLE World ( NAME VARCHAR ( 255 ), continent VARCHAR ( 255 ), area INT, population INT, gdp INT ); +INSERT INTO World ( NAME, continent, area, population, gdp ) +VALUES + ( 'Afghanistan', 'Asia', '652230', '25500100', '203430000' ), + ( 'Albania', 'Europe', '28748', '2831741', '129600000' ), + ( 'Algeria', 'Africa', '2381741', '37100000', '1886810000' ), + ( 'Andorra', 'Europe', '468', '78115', '37120000' ), + ( 'Angola', 'Africa', '1246700', '20609294', '1009900000' ); +``` + +## 627. Swap Salary + +https://leetcode.com/problems/swap-salary/description/ + +### Description + +```html +| id | name | sex | salary | +|----|------|-----|--------| +| 1 | A | m | 2500 | +| 2 | B | f | 1500 | +| 3 | C | m | 5500 | +| 4 | D | f | 500 | +``` + +只用一个 SQL 查询,将 sex 字段反转。 + +```html +| id | name | sex | salary | +|----|------|-----|--------| +| 1 | A | f | 2500 | +| 2 | B | m | 1500 | +| 3 | C | f | 5500 | +| 4 | D | m | 500 | +``` + +### Solution + +两个相等的数异或的结果为 0,而 0 与任何一个数异或的结果为这个数。 + +sex 字段只有两个取值:'f' 和 'm',并且有以下规律: + +``` +'f' ^ ('m' ^ 'f') = 'm' ^ ('f' ^ 'f') = 'm' +'m' ^ ('m' ^ 'f') = 'f' ^ ('m' ^ 'm') = 'f' +``` + +因此将 sex 字段和 'm' ^ 'f' 进行异或操作,最后就能反转 sex 字段。 + +```sql +UPDATE salary +SET sex = CHAR ( ASCII(sex) ^ ASCII( 'm' ) ^ ASCII( 'f' ) ); +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS salary; +CREATE TABLE salary ( id INT, NAME VARCHAR ( 100 ), sex CHAR ( 1 ), salary INT ); +INSERT INTO salary ( id, NAME, sex, salary ) +VALUES + ( '1', 'A', 'm', '2500' ), + ( '2', 'B', 'f', '1500' ), + ( '3', 'C', 'm', '5500' ), + ( '4', 'D', 'f', '500' ); +``` + +## 620. Not Boring Movies + +https://leetcode.com/problems/not-boring-movies/description/ + +### Description + + +```html ++---------+-----------+--------------+-----------+ +| id | movie | description | rating | ++---------+-----------+--------------+-----------+ +| 1 | War | great 3D | 8.9 | +| 2 | Science | fiction | 8.5 | +| 3 | irish | boring | 6.2 | +| 4 | Ice song | Fantacy | 8.6 | +| 5 | House card| Interesting| 9.1 | ++---------+-----------+--------------+-----------+ +``` + +查找 id 为奇数,并且 description 不是 boring 的电影,按 rating 降序。 + +```html ++---------+-----------+--------------+-----------+ +| id | movie | description | rating | ++---------+-----------+--------------+-----------+ +| 5 | House card| Interesting| 9.1 | +| 1 | War | great 3D | 8.9 | ++---------+-----------+--------------+-----------+ +``` + +### Solution + +```sql +SELECT + * +FROM + cinema +WHERE + id % 2 = 1 + AND description != 'boring' +ORDER BY + rating DESC; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS cinema; +CREATE TABLE cinema ( id INT, movie VARCHAR ( 255 ), description VARCHAR ( 255 ), rating FLOAT ( 2, 1 ) ); +INSERT INTO cinema ( id, movie, description, rating ) +VALUES + ( 1, 'War', 'great 3D', 8.9 ), + ( 2, 'Science', 'fiction', 8.5 ), + ( 3, 'irish', 'boring', 6.2 ), + ( 4, 'Ice song', 'Fantacy', 8.6 ), + ( 5, 'House card', 'Interesting', 9.1 ); +``` + +## 596. Classes More Than 5 Students + +https://leetcode.com/problems/classes-more-than-5-students/description/ + +### Description + +```html ++---------+------------+ +| student | class | ++---------+------------+ +| A | Math | +| B | English | +| C | Math | +| D | Biology | +| E | Math | +| F | Computer | +| G | Math | +| H | Math | +| I | Math | ++---------+------------+ +``` + +查找有五名及以上 student 的 class。 + +```html ++---------+ +| class | ++---------+ +| Math | ++---------+ +``` + +### Solution + +对 class 列进行分组之后,再使用 count 汇总函数统计每个分组的记录个数,之后使用 HAVING 进行筛选。HAVING 针对分组进行筛选,而 WHERE 针对每个记录(行)进行筛选。 + +```sql +SELECT + class +FROM + courses +GROUP BY + class +HAVING + count( DISTINCT student ) >= 5; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS courses; +CREATE TABLE courses ( student VARCHAR ( 255 ), class VARCHAR ( 255 ) ); +INSERT INTO courses ( student, class ) +VALUES + ( 'A', 'Math' ), + ( 'B', 'English' ), + ( 'C', 'Math' ), + ( 'D', 'Biology' ), + ( 'E', 'Math' ), + ( 'F', 'Computer' ), + ( 'G', 'Math' ), + ( 'H', 'Math' ), + ( 'I', 'Math' ); +``` + +## 182. Duplicate Emails + +https://leetcode.com/problems/duplicate-emails/description/ + +### Description + +邮件地址表: + +```html ++----+---------+ +| Id | Email | ++----+---------+ +| 1 | a@b.com | +| 2 | c@d.com | +| 3 | a@b.com | ++----+---------+ +``` + +查找重复的邮件地址: + +```html ++---------+ +| Email | ++---------+ +| a@b.com | ++---------+ +``` + +### Solution + +对 Email 进行分组,如果并使用 COUNT 进行计数统计,结果大于等于 2 的表示 Email 重复。 + +```sql +SELECT + Email +FROM + Person +GROUP BY + Email +HAVING + COUNT( * ) >= 2; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Person; +CREATE TABLE Person ( Id INT, Email VARCHAR ( 255 ) ); +INSERT INTO Person ( Id, Email ) +VALUES + ( 1, 'a@b.com' ), + ( 2, 'c@d.com' ), + ( 3, 'a@b.com' ); +``` + + +## 196. Delete Duplicate Emails + +https://leetcode.com/problems/delete-duplicate-emails/description/ + +### Description + +邮件地址表: + +```html ++----+---------+ +| Id | Email | ++----+---------+ +| 1 | john@example.com | +| 2 | bob@example.com | +| 3 | john@example.com | ++----+---------+ +``` + +删除重复的邮件地址: + +```html ++----+------------------+ +| Id | Email | ++----+------------------+ +| 1 | john@example.com | +| 2 | bob@example.com | ++----+------------------+ +``` + +### Solution + +只保留相同 Email 中 Id 最小的那一个,然后删除其它的。 + +连接查询: + +```sql +DELETE p1 +FROM + Person p1, + Person p2 +WHERE + p1.Email = p2.Email + AND p1.Id > p2.Id +``` + +子查询: + +```sql +DELETE +FROM + Person +WHERE + id NOT IN ( + SELECT id + FROM ( + SELECT min( id ) AS id + FROM Person + GROUP BY email + ) AS m + ); +``` + +应该注意的是上述解法额外嵌套了一个 SELECT 语句,如果不这么做,会出现错误:You can't specify target table 'Person' for update in FROM clause。以下演示了这种错误解法。 + +```sql +DELETE +FROM + Person +WHERE + id NOT IN ( + SELECT min( id ) AS id + FROM Person + GROUP BY email + ); +``` + +参考:[pMySQL Error 1093 - Can't specify target table for update in FROM clause](https://stackoverflow.com/questions/45494/mysql-error-1093-cant-specify-target-table-for-update-in-from-clause) + +### SQL Schema + +与 182 相同。 + +## 175. Combine Two Tables + +https://leetcode.com/problems/combine-two-tables/description/ + +### Description + +Person 表: + +```html ++-------------+---------+ +| Column Name | Type | ++-------------+---------+ +| PersonId | int | +| FirstName | varchar | +| LastName | varchar | ++-------------+---------+ +PersonId is the primary key column for this table. +``` + +Address 表: + +```html ++-------------+---------+ +| Column Name | Type | ++-------------+---------+ +| AddressId | int | +| PersonId | int | +| City | varchar | +| State | varchar | ++-------------+---------+ +AddressId is the primary key column for this table. +``` + +查找 FirstName, LastName, City, State 数据,而不管一个用户有没有填地址信息。 + +### Solution + +涉及到 Person 和 Address 两个表,在对这两个表执行连接操作时,因为要保留 Person 表中的信息,即使在 Address 表中没有关联的信息也要保留。此时可以用左外连接,将 Person 表放在 LEFT JOIN 的左边。 + +```sql +SELECT + FirstName, + LastName, + City, + State +FROM + Person P + LEFT JOIN Address A + ON P.PersonId = A.PersonId; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Person; +CREATE TABLE Person ( PersonId INT, FirstName VARCHAR ( 255 ), LastName VARCHAR ( 255 ) ); +DROP TABLE +IF + EXISTS Address; +CREATE TABLE Address ( AddressId INT, PersonId INT, City VARCHAR ( 255 ), State VARCHAR ( 255 ) ); +INSERT INTO Person ( PersonId, LastName, FirstName ) +VALUES + ( 1, 'Wang', 'Allen' ); +INSERT INTO Address ( AddressId, PersonId, City, State ) +VALUES + ( 1, 2, 'New York City', 'New York' ); +``` + +## 181. Employees Earning More Than Their Managers + +https://leetcode.com/problems/employees-earning-more-than-their-managers/description/ + +### Description + +Employee 表: + +```html ++----+-------+--------+-----------+ +| Id | Name | Salary | ManagerId | ++----+-------+--------+-----------+ +| 1 | Joe | 70000 | 3 | +| 2 | Henry | 80000 | 4 | +| 3 | Sam | 60000 | NULL | +| 4 | Max | 90000 | NULL | ++----+-------+--------+-----------+ +``` + +查找薪资大于其经理薪资的员工信息。 + +### Solution + +```sql +SELECT + E1.NAME AS Employee +FROM + Employee E1 + INNER JOIN Employee E2 + ON E1.ManagerId = E2.Id + AND E1.Salary > E2.Salary; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Employee; +CREATE TABLE Employee ( Id INT, NAME VARCHAR ( 255 ), Salary INT, ManagerId INT ); +INSERT INTO Employee ( Id, NAME, Salary, ManagerId ) +VALUES + ( 1, 'Joe', 70000, 3 ), + ( 2, 'Henry', 80000, 4 ), + ( 3, 'Sam', 60000, NULL ), + ( 4, 'Max', 90000, NULL ); +``` + +## 183. Customers Who Never Order + +https://leetcode.com/problems/customers-who-never-order/description/ + +### Description + +Customers 表: + +```html ++----+-------+ +| Id | Name | ++----+-------+ +| 1 | Joe | +| 2 | Henry | +| 3 | Sam | +| 4 | Max | ++----+-------+ +``` + +Orders 表: + +```html ++----+------------+ +| Id | CustomerId | ++----+------------+ +| 1 | 3 | +| 2 | 1 | ++----+------------+ +``` + +查找没有订单的顾客信息: + +```html ++-----------+ +| Customers | ++-----------+ +| Henry | +| Max | ++-----------+ +``` + +### Solution + +左外链接 + +```sql +SELECT + C.Name AS Customers +FROM + Customers C + LEFT JOIN Orders O + ON C.Id = O.CustomerId +WHERE + O.CustomerId IS NULL; +``` + +子查询 + +```sql +SELECT + Name AS Customers +FROM + Customers +WHERE + Id NOT IN ( + SELECT CustomerId + FROM Orders + ); +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Customers; +CREATE TABLE Customers ( Id INT, NAME VARCHAR ( 255 ) ); +DROP TABLE +IF + EXISTS Orders; +CREATE TABLE Orders ( Id INT, CustomerId INT ); +INSERT INTO Customers ( Id, NAME ) +VALUES + ( 1, 'Joe' ), + ( 2, 'Henry' ), + ( 3, 'Sam' ), + ( 4, 'Max' ); +INSERT INTO Orders ( Id, CustomerId ) +VALUES + ( 1, 3 ), + ( 2, 1 ); +``` + +## 184. Department Highest Salary + +https://leetcode.com/problems/department-highest-salary/description/ + +### Description + +Employee 表: + +```html ++----+-------+--------+--------------+ +| Id | Name | Salary | DepartmentId | ++----+-------+--------+--------------+ +| 1 | Joe | 70000 | 1 | +| 2 | Henry | 80000 | 2 | +| 3 | Sam | 60000 | 2 | +| 4 | Max | 90000 | 1 | ++----+-------+--------+--------------+ +``` + +Department 表: + +```html ++----+----------+ +| Id | Name | ++----+----------+ +| 1 | IT | +| 2 | Sales | ++----+----------+ +``` + +查找一个 Department 中收入最高者的信息: + +```html ++------------+----------+--------+ +| Department | Employee | Salary | ++------------+----------+--------+ +| IT | Max | 90000 | +| Sales | Henry | 80000 | ++------------+----------+--------+ +``` + +### Solution + +创建一个临时表,包含了部门员工的最大薪资。可以对部门进行分组,然后使用 MAX() 汇总函数取得最大薪资。 + +之后使用连接找到一个部门中薪资等于临时表中最大薪资的员工。 + +```sql +SELECT + D.NAME Department, + E.NAME Employee, + E.Salary +FROM + Employee E, + Department D, + ( SELECT DepartmentId, MAX( Salary ) Salary + FROM Employee + GROUP BY DepartmentId ) M +WHERE + E.DepartmentId = D.Id + AND E.DepartmentId = M.DepartmentId + AND E.Salary = M.Salary; +``` + +### SQL Schema + +```sql +DROP TABLE IF EXISTS Employee; +CREATE TABLE Employee ( Id INT, NAME VARCHAR ( 255 ), Salary INT, DepartmentId INT ); +DROP TABLE IF EXISTS Department; +CREATE TABLE Department ( Id INT, NAME VARCHAR ( 255 ) ); +INSERT INTO Employee ( Id, NAME, Salary, DepartmentId ) +VALUES + ( 1, 'Joe', 70000, 1 ), + ( 2, 'Henry', 80000, 2 ), + ( 3, 'Sam', 60000, 2 ), + ( 4, 'Max', 90000, 1 ); +INSERT INTO Department ( Id, NAME ) +VALUES + ( 1, 'IT' ), + ( 2, 'Sales' ); +``` + + +## 176. Second Highest Salary + +https://leetcode.com/problems/second-highest-salary/description/ + +### Description + +```html ++----+--------+ +| Id | Salary | ++----+--------+ +| 1 | 100 | +| 2 | 200 | +| 3 | 300 | ++----+--------+ +``` + +查找工资第二高的员工。 + +```html ++---------------------+ +| SecondHighestSalary | ++---------------------+ +| 200 | ++---------------------+ +``` + +没有找到返回 null 而不是不返回数据。 + +### Solution + +为了在没有查找到数据时返回 null,需要在查询结果外面再套一层 SELECT。 + +```sql +SELECT + ( SELECT DISTINCT Salary + FROM Employee + ORDER BY Salary DESC + LIMIT 1, 1 ) SecondHighestSalary; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Employee; +CREATE TABLE Employee ( Id INT, Salary INT ); +INSERT INTO Employee ( Id, Salary ) +VALUES + ( 1, 100 ), + ( 2, 200 ), + ( 3, 300 ); +``` + +## 177. Nth Highest Salary + +### Description + +查找工资第 N 高的员工。 + +### Solution + +```sql +CREATE FUNCTION getNthHighestSalary ( N INT ) RETURNS INT BEGIN + +SET N = N - 1; +RETURN ( + SELECT ( + SELECT DISTINCT Salary + FROM Employee + ORDER BY Salary DESC + LIMIT N, 1 + ) +); + +END +``` + +### SQL Schema + +同 176。 + + +## 178. Rank Scores + +https://leetcode.com/problems/rank-scores/description/ + +### Description + +得分表: + +```html ++----+-------+ +| Id | Score | ++----+-------+ +| 1 | 3.50 | +| 2 | 3.65 | +| 3 | 4.00 | +| 4 | 3.85 | +| 5 | 4.00 | +| 6 | 3.65 | ++----+-------+ +``` + +将得分排序,并统计排名。 + +```html ++-------+------+ +| Score | Rank | ++-------+------+ +| 4.00 | 1 | +| 4.00 | 1 | +| 3.85 | 2 | +| 3.65 | 3 | +| 3.65 | 3 | +| 3.50 | 4 | ++-------+------+ +``` + +### Solution + +要统计某个 score 的排名,只要统计大于等于该 score 的 score 数量。 + +| Id | score | 大于等于该 score 的 score 数量 | 排名 | +| :---: | :---: | :---: | :---: | +| 1 | 4.1 | 3 | 3 | +| 2 | 4.2 | 2 | 2 | +| 3 | 4.3 | 1 | 1 | + +使用连接操作找到某个 score 对应的大于等于其值的记录: + +```sql +SELECT + * +FROM + Scores S1 + INNER JOIN Scores S2 + ON S1.score <= S2.score +ORDER BY + S1.score DESC, S1.Id; +``` + +| S1.Id | S1.score | S2.Id | S2.score | +| :---: | :---: | :---: | :---: | +|3| 4.3| 3 |4.3| +|2| 4.2| 2| 4.2| +|2| 4.2 |3 |4.3| +|1| 4.1 |1| 4.1| +|1| 4.1 |2| 4.2| +|1| 4.1 |3| 4.3| + +可以看到每个 S1.score 都有对应好几条记录,我们再进行分组,并统计每个分组的数量作为 'Rank' + +```sql +SELECT + S1.score 'Score', + COUNT(*) 'Rank' +FROM + Scores S1 + INNER JOIN Scores S2 + ON S1.score <= S2.score +GROUP BY + S1.id, S1.score +ORDER BY + S1.score DESC, S1.Id; +``` + +| score | Rank | +| :---: | :---: | +| 4.3 | 1 | +| 4.2 | 2 | +| 4.1 | 3 | + +上面的解法看似没问题,但是对于以下数据,它却得到了错误的结果: + +| Id | score | +| :---: | :---: | +| 1 | 4.1 | +| 2 | 4.2 | +| 3 | 4.2 | + +| score | Rank | +| :---: | :--: | +| 4.2 | 2 | +| 4.2 | 2 | +| 4.1 | 3 | + +而我们希望的结果为: + +| score | Rank | +| :---: | :--: | +| 4.2 | 1 | +| 4.2 | 1 | +| 4.1 | 2 | + +连接情况如下: + +| S1.Id | S1.score | S2.Id | S2.score | +| :---: | :------: | :---: | :------: | +| 2 | 4.2 | 3 | 4.2 | +| 2 | 4.2 | 2 | 4.2 | +| 3 | 4.2 | 3 | 4.2 | +| 3 | 4.2 | 2 | 4.1 | +| 1 | 4.1 | 3 | 4.2 | +| 1 | 4.1 | 2 | 4.2 | +| 1 | 4.1 | 1 | 4.1 | + +我们想要的结果是,把分数相同的放在同一个排名,并且相同分数只占一个位置,例如上面的分数,Id=2 和 Id=3 的记录都有相同的分数,并且最高,他们并列第一。而 Id=1 的记录应该排第二名,而不是第三名。所以在进行 COUNT 计数统计时,我们需要使用 COUNT( DISTINCT S2.score ) 从而只统计一次相同的分数。 + +```sql +SELECT + S1.score 'Score', + COUNT( DISTINCT S2.score ) 'Rank' +FROM + Scores S1 + INNER JOIN Scores S2 + ON S1.score <= S2.score +GROUP BY + S1.id, S1.score +ORDER BY + S1.score DESC; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS Scores; +CREATE TABLE Scores ( Id INT, Score DECIMAL ( 3, 2 ) ); +INSERT INTO Scores ( Id, Score ) +VALUES + ( 1, 4.1 ), + ( 2, 4.1 ), + ( 3, 4.2 ), + ( 4, 4.2 ), + ( 5, 4.3 ), + ( 6, 4.3 ); +``` + +## 180. Consecutive Numbers + +https://leetcode.com/problems/consecutive-numbers/description/ + +### Description + +数字表: + +```html ++----+-----+ +| Id | Num | ++----+-----+ +| 1 | 1 | +| 2 | 1 | +| 3 | 1 | +| 4 | 2 | +| 5 | 1 | +| 6 | 2 | +| 7 | 2 | ++----+-----+ +``` + +查找连续出现三次的数字。 + +```html ++-----------------+ +| ConsecutiveNums | ++-----------------+ +| 1 | ++-----------------+ +``` + +### Solution + +```sql +SELECT + DISTINCT L1.num ConsecutiveNums +FROM + Logs L1, + Logs L2, + Logs L3 +WHERE L1.id = l2.id - 1 + AND L2.id = L3.id - 1 + AND L1.num = L2.num + AND l2.num = l3.num; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS LOGS; +CREATE TABLE LOGS ( Id INT, Num INT ); +INSERT INTO LOGS ( Id, Num ) +VALUES + ( 1, 1 ), + ( 2, 1 ), + ( 3, 1 ), + ( 4, 2 ), + ( 5, 1 ), + ( 6, 2 ), + ( 7, 2 ); +``` + +## 626. Exchange Seats + +https://leetcode.com/problems/exchange-seats/description/ + +### Description + +seat 表存储着座位对应的学生。 + +```html ++---------+---------+ +| id | student | ++---------+---------+ +| 1 | Abbot | +| 2 | Doris | +| 3 | Emerson | +| 4 | Green | +| 5 | Jeames | ++---------+---------+ +``` + +要求交换相邻座位的两个学生,如果最后一个座位是奇数,那么不交换这个座位上的学生。 + +```html ++---------+---------+ +| id | student | ++---------+---------+ +| 1 | Doris | +| 2 | Abbot | +| 3 | Green | +| 4 | Emerson | +| 5 | Jeames | ++---------+---------+ +``` + +### Solution + +使用多个 union。 + +```sql +## 处理偶数 id,让 id 减 1 +## 例如 2,4,6,... 变成 1,3,5,... +SELECT + s1.id - 1 AS id, + s1.student +FROM + seat s1 +WHERE + s1.id MOD 2 = 0 UNION +## 处理奇数 id,让 id 加 1。但是如果最大的 id 为奇数,则不做处理 +## 例如 1,3,5,... 变成 2,4,6,... +SELECT + s2.id + 1 AS id, + s2.student +FROM + seat s2 +WHERE + s2.id MOD 2 = 1 + AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION +## 如果最大的 id 为奇数,单独取出这个数 +SELECT + s4.id AS id, + s4.student +FROM + seat s4 +WHERE + s4.id MOD 2 = 1 + AND s4.id = ( SELECT max( s5.id ) FROM seat s5 ) +ORDER BY + id; +``` + +### SQL Schema + +```sql +DROP TABLE +IF + EXISTS seat; +CREATE TABLE seat ( id INT, student VARCHAR ( 255 ) ); +INSERT INTO seat ( id, student ) +VALUES + ( '1', 'Abbot' ), + ( '2', 'Doris' ), + ( '3', 'Emerson' ), + ( '4', 'Green' ), + ( '5', 'Jeames' ); +``` diff --git "a/notes/SQL \350\257\255\346\263\225.md" "b/notes/SQL \350\257\255\346\263\225.md" new file mode 100644 index 0000000000..b0230dc4a1 --- /dev/null +++ "b/notes/SQL \350\257\255\346\263\225.md" @@ -0,0 +1,783 @@ +# SQL 语法 + +* [SQL 语法](#sql-语法) + * [一、基础](#一基础) + * [二、创建表](#二创建表) + * [三、修改表](#三修改表) + * [四、插入](#四插入) + * [五、更新](#五更新) + * [六、删除](#六删除) + * [七、查询](#七查询) + * [DISTINCT](#distinct) + * [LIMIT](#limit) + * [八、排序](#八排序) + * [九、过滤](#九过滤) + * [十、通配符](#十通配符) + * [十一、计算字段](#十一计算字段) + * [十二、函数](#十二函数) + * [汇总](#汇总) + * [文本处理](#文本处理) + * [日期和时间处理](#日期和时间处理) + * [数值处理](#数值处理) + * [十三、分组](#十三分组) + * [十四、子查询](#十四子查询) + * [十五、连接](#十五连接) + * [内连接](#内连接) + * [自连接](#自连接) + * [自然连接](#自然连接) + * [外连接](#外连接) + * [十六、组合查询](#十六组合查询) + * [十七、视图](#十七视图) + * [十八、存储过程](#十八存储过程) + * [十九、游标](#十九游标) + * [二十、触发器](#二十触发器) + * [二十一、事务管理](#二十一事务管理) + * [二十二、字符集](#二十二字符集) + * [二十三、权限管理](#二十三权限管理) + * [参考资料](#参考资料) + + + +## 一、基础 + +模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。 + +主键的值不允许修改,也不允许复用(不能将已经删除的主键值赋给新数据行的主键)。 + +SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。 + +SQL 语句不区分大小写,但是数据库表名、列名和值是否区分依赖于具体的 DBMS 以及配置。 + +SQL 支持以下三种注释: + +```sql +## 注释 +SELECT * +FROM mytable; -- 注释 +/* 注释1 + 注释2 */ +``` + +数据库创建与使用: + +```sql +CREATE DATABASE test; +USE test; +``` + +## 二、创建表 + +```sql +CREATE TABLE mytable ( + # int 类型,不为空,自增 + id INT NOT NULL AUTO_INCREMENT, + # int 类型,不可为空,默认值为 1,不为空 + col1 INT NOT NULL DEFAULT 1, + # 变长字符串类型,最长为 45 个字符,可以为空 + col2 VARCHAR(45) NULL, + # 日期类型,可为空 + col3 DATE NULL, + # 设置主键为 id + PRIMARY KEY (`id`)); +``` + +## 三、修改表 + +添加列 + +```sql +ALTER TABLE mytable +ADD col CHAR(20); +``` + +删除列 + +```sql +ALTER TABLE mytable +DROP COLUMN col; +``` + +删除表 + +```sql +DROP TABLE mytable; +``` + +## 四、插入 + +普通插入 + +```sql +INSERT INTO mytable(col1, col2) +VALUES(val1, val2); +``` + +插入检索出来的数据 + +```sql +INSERT INTO mytable1(col1, col2) +SELECT col1, col2 +FROM mytable2; +``` + +将一个表的内容插入到一个新表 + +```sql +CREATE TABLE newtable AS +SELECT * FROM mytable; +``` + +## 五、更新 + +```sql +UPDATE mytable +SET col = val +WHERE id = 1; +``` + +## 六、删除 + +```sql +DELETE FROM mytable +WHERE id = 1; +``` + +**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 + +```sql +TRUNCATE TABLE mytable; +``` + +使用更新和删除操作时一定要用 WHERE 子句,不然会把整张表的数据都破坏。可以先用 SELECT 语句进行测试,防止错误删除。 + +## 七、查询 + +### DISTINCT + +相同值只会出现一次。它作用于所有列,也就是说所有列的值都相同才算相同。 + +```sql +SELECT DISTINCT col1, col2 +FROM mytable; +``` + +### LIMIT + +限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。 + +返回前 5 行: + +```sql +SELECT * +FROM mytable +LIMIT 5; +``` + +```sql +SELECT * +FROM mytable +LIMIT 0, 5; +``` + +返回第 3 \~ 5 行: + +```sql +SELECT * +FROM mytable +LIMIT 2, 3; +``` + +## 八、排序 + +- **ASC** :升序(默认) +- **DESC** :降序 + +可以按多个列进行排序,并且为每个列指定不同的排序方式: + +```sql +SELECT * +FROM mytable +ORDER BY col1 DESC, col2 ASC; +``` + +## 九、过滤 + +不进行过滤的数据非常大,导致通过网络传输了多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。 + +```sql +SELECT * +FROM mytable +WHERE col IS NULL; +``` + +下表显示了 WHERE 子句可用的操作符 + +| 操作符 | 说明 | +| :---: | :---: | +| = | 等于 | +| < | 小于 | +| > | 大于 | +| <> != | 不等于 | +| <= !> | 小于等于 | +| >= !< | 大于等于 | +| BETWEEN | 在两个值之间 | +| IS NULL | 为 NULL 值 | + +应该注意到,NULL 与 0、空字符串都不同。 + +**AND 和 OR** 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。 + +**IN** 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。 + +**NOT** 操作符用于否定一个条件。 + +## 十、通配符 + +通配符也是用在过滤语句中,但它只能用于文本字段。 + +- **%** 匹配 \>=0 个任意字符; + +- **\_** 匹配 ==1 个任意字符; + +- **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 + +使用 Like 来进行通配符匹配。 + +```sql +SELECT * +FROM mytable +WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本 +``` + +不要滥用通配符,通配符位于开头处匹配会非常慢。 + +## 十一、计算字段 + +在数据库服务器上完成数据的转换和格式化的工作往往比客户端上快得多,并且转换和格式化后的数据量更少的话可以减少网络通信量。 + +计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 + +```sql +SELECT col1 * col2 AS alias +FROM mytable; +``` + +**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 + +```sql +SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col +FROM mytable; +``` + +## 十二、函数 + +各个 DBMS 的函数都是不相同的,因此不可移植,以下主要是 MySQL 的函数。 + +### 汇总 + +|函 数 |说 明| +| :---: | :---: | +| AVG() | 返回某列的平均值 | +| COUNT() | 返回某列的行数 | +| MAX() | 返回某列的最大值 | +| MIN() | 返回某列的最小值 | +| SUM() |返回某列值之和 | + +AVG() 会忽略 NULL 行。 + +使用 DISTINCT 可以汇总不同的值。 + +```sql +SELECT AVG(DISTINCT col1) AS avg_col +FROM mytable; +``` + +### 文本处理 + +| 函数 | 说明 | +| :---: | :---: | +| LEFT() | 左边的字符 | +| RIGHT() | 右边的字符 | +| LOWER() | 转换为小写字符 | +| UPPER() | 转换为大写字符 | +| LTRIM() | 去除左边的空格 | +| RTRIM() | 去除右边的空格 | +| LENGTH() | 长度 | +| SOUNDEX() | 转换为语音值 | + +其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 + +```sql +SELECT * +FROM mytable +WHERE SOUNDEX(col1) = SOUNDEX('apple') +``` + +### 日期和时间处理 + + +- 日期格式:YYYY-MM-DD +- 时间格式:HH:\MM:SS + +|函 数 | 说 明| +| :---: | :---: | +| ADDDATE() | 增加一个日期(天、周等)| +| ADDTIME() | 增加一个时间(时、分等)| +| CURDATE() | 返回当前日期 | +| CURTIME() | 返回当前时间 | +| DATE() |返回日期时间的日期部分| +| DATEDIFF() |计算两个日期之差| +| DATE_ADD() |高度灵活的日期运算函数| +| DATE_FORMAT() |返回一个格式化的日期或时间串| +| DAY()| 返回一个日期的天数部分| +| DAYOFWEEK() |对于一个日期,返回对应的星期几| +| HOUR() |返回一个时间的小时部分| +| MINUTE() |返回一个时间的分钟部分| +| MONTH() |返回一个日期的月份部分| +| NOW() |返回当前日期和时间| +| SECOND() |返回一个时间的秒部分| +| TIME() |返回一个日期时间的时间部分| +| YEAR() |返回一个日期的年份部分| + +```sql +mysql> SELECT NOW(); +``` + +``` +2018-4-14 20:25:11 +``` + +### 数值处理 + +| 函数 | 说明 | +| :---: | :---: | +| SIN() | 正弦 | +| COS() | 余弦 | +| TAN() | 正切 | +| ABS() | 绝对值 | +| SQRT() | 平方根 | +| MOD() | 余数 | +| EXP() | 指数 | +| PI() | 圆周率 | +| RAND() | 随机数 | + +## 十三、分组 + +把具有相同的数据值的行放在同一组中。 + +可以对同一分组数据使用汇总函数进行处理,例如求分组数据的平均值等。 + +指定的分组字段除了能按该字段进行分组,也会自动按该字段进行排序。 + +```sql +SELECT col, COUNT(*) AS num +FROM mytable +GROUP BY col; +``` + +GROUP BY 自动按分组字段进行排序,ORDER BY 也可以按汇总字段来进行排序。 + +```sql +SELECT col, COUNT(*) AS num +FROM mytable +GROUP BY col +ORDER BY num; +``` + +WHERE 过滤行,HAVING 过滤分组,行过滤应当先于分组过滤。 + +```sql +SELECT col, COUNT(*) AS num +FROM mytable +WHERE col > 2 +GROUP BY col +HAVING num >= 2; +``` + +分组规定: + +- GROUP BY 子句出现在 WHERE 子句之后,ORDER BY 子句之前; +- 除了汇总字段外,SELECT 语句中的每一字段都必须在 GROUP BY 子句中给出; +- NULL 的行会单独分为一组; +- 大多数 SQL 实现不支持 GROUP BY 列具有可变长度的数据类型。 + +## 十四、子查询 + +子查询中只能返回一个字段的数据。 + +可以将子查询的结果作为 WHRER 语句的过滤条件: + +```sql +SELECT * +FROM mytable1 +WHERE col1 IN (SELECT col2 + FROM mytable2); +``` + +下面的语句可以检索出客户的订单数量,子查询语句会对第一个查询检索出的每个客户执行一次: + +```sql +SELECT cust_name, (SELECT COUNT(*) + FROM Orders + WHERE Orders.cust_id = Customers.cust_id) + AS orders_num +FROM Customers +ORDER BY cust_name; +``` + +## 十五、连接 + +连接用于连接多个表,使用 JOIN 关键字,并且条件语句使用 ON 而不是 WHERE。 + +连接可以替换子查询,并且比子查询的效率一般会更快。 + +可以用 AS 给列名、计算字段和表名取别名,给表名取别名是为了简化 SQL 语句以及连接相同表。 + +### 内连接 + +内连接又称等值连接,使用 INNER JOIN 关键字。 + +```sql +SELECT A.value, B.value +FROM tablea AS A INNER JOIN tableb AS B +ON A.key = B.key; +``` + +可以不明确使用 INNER JOIN,而使用普通查询并在 WHERE 中将两个表中要连接的列用等值方法连接起来。 + +```sql +SELECT A.value, B.value +FROM tablea AS A, tableb AS B +WHERE A.key = B.key; +``` + +### 自连接 + +自连接可以看成内连接的一种,只是连接的表是自身而已。 + +一张员工表,包含员工姓名和员工所属部门,要找出与 Jim 处在同一部门的所有员工姓名。 + +子查询版本 + +```sql +SELECT name +FROM employee +WHERE department = ( + SELECT department + FROM employee + WHERE name = "Jim"); +``` + +自连接版本 + +```sql +SELECT e1.name +FROM employee AS e1 INNER JOIN employee AS e2 +ON e1.department = e2.department + AND e2.name = "Jim"; +``` + +### 自然连接 + +自然连接是把同名列通过等值测试连接起来的,同名列可以有多个。 + +内连接和自然连接的区别:内连接提供连接的列,而自然连接自动连接所有同名列。 + +```sql +SELECT A.value, B.value +FROM tablea AS A NATURAL JOIN tableb AS B; +``` + +### 外连接 + +外连接保留了没有关联的那些行。分为左外连接,右外连接以及全外连接,左外连接就是保留左表没有关联的行。 + +检索所有顾客的订单信息,包括还没有订单信息的顾客。 + +```sql +SELECT Customers.cust_id, Customer.cust_name, Orders.order_id +FROM Customers LEFT OUTER JOIN Orders +ON Customers.cust_id = Orders.cust_id; +``` + +customers 表: + +| cust_id | cust_name | +| :---: | :---: | +| 1 | a | +| 2 | b | +| 3 | c | + +orders 表: + +| order_id | cust_id | +| :---: | :---: | +|1 | 1 | +|2 | 1 | +|3 | 3 | +|4 | 3 | + +结果: + +| cust_id | cust_name | order_id | +| :---: | :---: | :---: | +| 1 | a | 1 | +| 1 | a | 2 | +| 3 | c | 3 | +| 3 | c | 4 | +| 2 | b | Null | + +## 十六、组合查询 + +使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 + +每个查询必须包含相同的列、表达式和聚集函数。 + +默认会去除相同行,如果需要保留相同行,使用 UNION ALL。 + +只能包含一个 ORDER BY 子句,并且必须位于语句的最后。 + +```sql +SELECT col +FROM mytable +WHERE col = 1 +UNION +SELECT col +FROM mytable +WHERE col =2; +``` + +## 十七、视图 + +视图是虚拟的表,本身不包含数据,也就不能对其进行索引操作。 + +对视图的操作和对普通表的操作一样。 + +视图具有如下好处: + +- 简化复杂的 SQL 操作,比如复杂的连接; +- 只使用实际表的一部分数据; +- 通过只给用户访问视图的权限,保证数据的安全性; +- 更改数据格式和表示。 + +```sql +CREATE VIEW myview AS +SELECT Concat(col1, col2) AS concat_col, col3*col4 AS compute_col +FROM mytable +WHERE col5 = val; +``` + +## 十八、存储过程 + +存储过程可以看成是对一系列 SQL 操作的批处理。 + +使用存储过程的好处: + +- 代码封装,保证了一定的安全性; +- 代码复用; +- 由于是预先编译,因此具有很高的性能。 + +命令行中创建存储过程需要自定义分隔符,因为命令行是以 ; 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。 + +包含 in、out 和 inout 三种参数。 + +给变量赋值都需要用 select into 语句。 + +每次只能给一个变量赋值,不支持集合的操作。 + +```sql +delimiter // + +create procedure myprocedure( out ret int ) + begin + declare y int; + select sum(col1) + from mytable + into y; + select y*y into ret; + end // + +delimiter ; +``` + +```sql +call myprocedure(@ret); +select @ret; +``` + +## 十九、游标 + +在存储过程中使用游标可以对一个结果集进行移动遍历。 + +游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。 + +使用游标的四个步骤: + +1. 声明游标,这个过程没有实际检索出数据; +2. 打开游标; +3. 取出数据; +4. 关闭游标; + +```sql +delimiter // +create procedure myprocedure(out ret int) + begin + declare done boolean default 0; + + declare mycursor cursor for + select col1 from mytable; + # 定义了一个 continue handler,当 sqlstate '02000' 这个条件出现时,会执行 set done = 1 + declare continue handler for sqlstate '02000' set done = 1; + + open mycursor; + + repeat + fetch mycursor into ret; + select ret; + until done end repeat; + + close mycursor; + end // + delimiter ; +``` + +## 二十、触发器 + +触发器会在某个表执行以下语句时而自动执行:DELETE、INSERT、UPDATE。 + +触发器必须指定在语句执行之前还是之后自动执行,之前执行使用 BEFORE 关键字,之后执行使用 AFTER 关键字。BEFORE 用于数据验证和净化,AFTER 用于审计跟踪,将修改记录到另外一张表中。 + +INSERT 触发器包含一个名为 NEW 的虚拟表。 + +```sql +CREATE TRIGGER mytrigger AFTER INSERT ON mytable +FOR EACH ROW SELECT NEW.col into @result; + +SELECT @result; -- 获取结果 +``` + +DELETE 触发器包含一个名为 OLD 的虚拟表,并且是只读的。 + +UPDATE 触发器包含一个名为 NEW 和一个名为 OLD 的虚拟表,其中 NEW 是可以被修改的,而 OLD 是只读的。 + +MySQL 不允许在触发器中使用 CALL 语句,也就是不能调用存储过程。 + +## 二十一、事务管理 + +基本术语: + +- 事务(transaction)指一组 SQL 语句; +- 回退(rollback)指撤销指定 SQL 语句的过程; +- 提交(commit)指将未存储的 SQL 语句结果写入数据库表; +- 保留点(savepoint)指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。 + +不能回退 SELECT 语句,回退 SELECT 语句也没意义;也不能回退 CREATE 和 DROP 语句。 + +MySQL 的事务提交默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。 + +设置 autocommit 为 0 可以取消自动提交;autocommit 标记是针对每个连接而不是针对服务器的。 + +如果没有设置保留点,ROLLBACK 会回退到 START TRANSACTION 语句处;如果设置了保留点,并且在 ROLLBACK 中指定该保留点,则会回退到该保留点。 + +```sql +START TRANSACTION +// ... +SAVEPOINT delete1 +// ... +ROLLBACK TO delete1 +// ... +COMMIT +``` + +## 二十二、字符集 + +基本术语: + +- 字符集为字母和符号的集合; +- 编码为某个字符集成员的内部表示; +- 校对字符指定如何比较,主要用于排序和分组。 + +除了给表指定字符集和校对外,也可以给列指定: + +```sql +CREATE TABLE mytable +(col VARCHAR(10) CHARACTER SET latin COLLATE latin1_general_ci ) +DEFAULT CHARACTER SET hebrew COLLATE hebrew_general_ci; +``` + +可以在排序、分组时指定校对: + +```sql +SELECT * +FROM mytable +ORDER BY col COLLATE latin1_general_ci; +``` + +## 二十三、权限管理 + +MySQL 的账户信息保存在 mysql 这个数据库中。 + +```sql +USE mysql; +SELECT user FROM user; +``` + +**创建账户** + +新创建的账户没有任何权限。 + +```sql +CREATE USER myuser IDENTIFIED BY 'mypassword'; +``` + +**修改账户名** + +```sql +RENAME USER myuser TO newuser; +``` + +**删除账户** + +```sql +DROP USER myuser; +``` + +**查看权限** + +```sql +SHOW GRANTS FOR myuser; +``` + +**授予权限** + +账户用 username@host 的形式定义,username@% 使用的是默认主机名。 + +```sql +GRANT SELECT, INSERT ON mydatabase.* TO myuser; +``` + +**删除权限** + +GRANT 和 REVOKE 可在几个层次上控制访问权限: + +- 整个服务器,使用 GRANT ALL 和 REVOKE ALL; +- 整个数据库,使用 ON database.\*; +- 特定的表,使用 ON database.table; +- 特定的列; +- 特定的存储过程。 + +```sql +REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; +``` + +**更改密码** + +必须使用 Password() 函数进行加密。 + +```sql +SET PASSWROD FOR myuser = Password('new_password'); +``` + +## 参考资料 + +- BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013. diff --git a/docs/notes/SQL.md b/notes/SQL.md similarity index 82% rename from docs/notes/SQL.md rename to notes/SQL.md index 8471bb8e07..7e5f1636a4 100644 --- a/docs/notes/SQL.md +++ b/notes/SQL.md @@ -6,14 +6,24 @@ * [五、更新](#五更新) * [六、删除](#六删除) * [七、查询](#七查询) + * [DISTINCT](#distinct) + * [LIMIT](#limit) * [八、排序](#八排序) * [九、过滤](#九过滤) * [十、通配符](#十通配符) * [十一、计算字段](#十一计算字段) * [十二、函数](#十二函数) + * [汇总](#汇总) + * [文本处理](#文本处理) + * [日期和时间处理](#日期和时间处理) + * [数值处理](#数值处理) * [十三、分组](#十三分组) * [十四、子查询](#十四子查询) * [十五、连接](#十五连接) + * [内连接](#内连接) + * [自连接](#自连接) + * [自然连接](#自然连接) + * [外连接](#外连接) * [十六、组合查询](#十六组合查询) * [十七、视图](#十七视图) * [十八、存储过程](#十八存储过程) @@ -30,7 +40,7 @@ 模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。 -主键的值不允许修改,也不允许复用(不能使用已经删除的主键值赋给新数据行的主键)。 +主键的值不允许修改,也不允许复用(不能将已经删除的主键值赋给新数据行的主键)。 SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。 @@ -57,10 +67,15 @@ USE test; ```sql CREATE TABLE mytable ( + # int 类型,不为空,自增 id INT NOT NULL AUTO_INCREMENT, + # int 类型,不可为空,默认值为 1,不为空 col1 INT NOT NULL DEFAULT 1, + # 变长字符串类型,最长为 45 个字符,可以为空 col2 VARCHAR(45) NULL, + # 日期类型,可为空 col3 DATE NULL, + # 设置主键为 id PRIMARY KEY (`id`)); ``` @@ -125,7 +140,7 @@ DELETE FROM mytable WHERE id = 1; ``` -**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 +**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 ```sql TRUNCATE TABLE mytable; @@ -172,8 +187,8 @@ LIMIT 2, 3; # 八、排序 -- **ASC** :升序(默认) -- **DESC** :降序 +- **ASC** :升序(默认) +- **DESC** :降序 可以按多个列进行排序,并且为每个列指定不同的排序方式: @@ -208,21 +223,21 @@ WHERE col IS NULL; 应该注意到,NULL 与 0、空字符串都不同。 -**AND 和 OR** 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。 +**AND 和 OR** 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。 -**IN** 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。 +**IN** 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。 -**NOT** 操作符用于否定一个条件。 +**NOT** 操作符用于否定一个条件。 # 十、通配符 通配符也是用在过滤语句中,但它只能用于文本字段。 -- **%** 匹配 >=0 个任意字符; +- **%** 匹配 \>=0 个任意字符; -- **\_** 匹配 ==1 个任意字符; +- **\_** 匹配 ==1 个任意字符; -- **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 +- **[ ]** 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。 使用 Like 来进行通配符匹配。 @@ -238,14 +253,14 @@ WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本 在数据库服务器上完成数据的转换和格式化的工作往往比客户端上快得多,并且转换和格式化后的数据量更少的话可以减少网络通信量。 -计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 +计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 ```sql SELECT col1 * col2 AS alias FROM mytable; ``` -**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 +**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 ```sql SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col @@ -268,7 +283,7 @@ FROM mytable; AVG() 会忽略 NULL 行。 -使用 DISTINCT 可以让汇总函数值汇总不同的值。 +使用 DISTINCT 可以汇总不同的值。 ```sql SELECT AVG(DISTINCT col1) AS avg_col @@ -288,7 +303,7 @@ FROM mytable; | LENGTH() | 长度 | | SOUNDEX() | 转换为语音值 | -其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 +其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 ```sql SELECT * @@ -298,28 +313,29 @@ WHERE SOUNDEX(col1) = SOUNDEX('apple') ## 日期和时间处理 + - 日期格式:YYYY-MM-DD -- 时间格式:HH:MM:SS +- 时间格式:HH:\MM:SS |函 数 | 说 明| | :---: | :---: | -| AddDate() | 增加一个日期(天、周等)| -| AddTime() | 增加一个时间(时、分等)| -| CurDate() | 返回当前日期 | -| CurTime() | 返回当前时间 | -| Date() |返回日期时间的日期部分| -| DateDiff() |计算两个日期之差| -| Date_Add() |高度灵活的日期运算函数| -| Date_Format() |返回一个格式化的日期或时间串| -| Day()| 返回一个日期的天数部分| -| DayOfWeek() |对于一个日期,返回对应的星期几| -| Hour() |返回一个时间的小时部分| -| Minute() |返回一个时间的分钟部分| -| Month() |返回一个日期的月份部分| -| Now() |返回当前日期和时间| -| Second() |返回一个时间的秒部分| -| Time() |返回一个日期时间的时间部分| -| Year() |返回一个日期的年份部分| +| ADDDATE() | 增加一个日期(天、周等)| +| ADDTIME() | 增加一个时间(时、分等)| +| CURDATE() | 返回当前日期 | +| CURTIME() | 返回当前时间 | +| DATE() |返回日期时间的日期部分| +| DATEDIFF() |计算两个日期之差| +| DATE_ADD() |高度灵活的日期运算函数| +| DATE_FORMAT() |返回一个格式化的日期或时间串| +| DAY()| 返回一个日期的天数部分| +| DAYOFWEEK() |对于一个日期,返回对应的星期几| +| HOUR() |返回一个时间的小时部分| +| MINUTE() |返回一个时间的分钟部分| +| MONTH() |返回一个日期的月份部分| +| NOW() |返回当前日期和时间| +| SECOND() |返回一个时间的秒部分| +| TIME() |返回一个日期时间的时间部分| +| YEAR() |返回一个日期的年份部分| ```sql mysql> SELECT NOW(); @@ -345,7 +361,7 @@ mysql> SELECT NOW(); # 十三、分组 -分组就是把具有相同的数据值的行放在同一组中。 +把具有相同的数据值的行放在同一组中。 可以对同一分组数据使用汇总函数进行处理,例如求分组数据的平均值等。 @@ -433,8 +449,6 @@ FROM tablea AS A, tableb AS B WHERE A.key = B.key; ``` -在没有条件语句的情况下返回笛卡尔积。 - ## 自连接 自连接可以看成内连接的一种,只是连接的表是自身而已。 @@ -479,7 +493,7 @@ FROM tablea AS A NATURAL JOIN tableb AS B; 检索所有顾客的订单信息,包括还没有订单信息的顾客。 ```sql -SELECT Customers.cust_id, Orders.order_num +SELECT Customers.cust_id, Customer.cust_name, Orders.order_id FROM Customers LEFT OUTER JOIN Orders ON Customers.cust_id = Orders.cust_id; ``` @@ -513,7 +527,7 @@ orders 表: # 十六、组合查询 -使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 +使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 每个查询必须包含相同的列、表达式和聚集函数。 @@ -659,7 +673,7 @@ MySQL 不允许在触发器中使用 CALL 语句,也就是不能调用存储 MySQL 的事务提交默认是隐式提交,每执行一条语句就把这条语句当成一个事务然后进行提交。当出现 START TRANSACTION 语句时,会关闭隐式提交;当 COMMIT 或 ROLLBACK 语句执行后,事务会自动关闭,重新恢复隐式提交。 -通过设置 autocommit 为 0 可以取消自动提交;autocommit 标记是针对每个连接而不是针对服务器的。 +设置 autocommit 为 0 可以取消自动提交;autocommit 标记是针对每个连接而不是针对服务器的。 如果没有设置保留点,ROLLBACK 会回退到 START TRANSACTION 语句处;如果设置了保留点,并且在 ROLLBACK 中指定该保留点,则会回退到该保留点。 @@ -706,7 +720,7 @@ USE mysql; SELECT user FROM user; ``` -**创建账户** +**创建账户** 新创建的账户没有任何权限。 @@ -714,25 +728,25 @@ SELECT user FROM user; CREATE USER myuser IDENTIFIED BY 'mypassword'; ``` -**修改账户名** +**修改账户名** ```sql -RENAME myuser TO newuser; +RENAME USER myuser TO newuser; ``` -**删除账户** +**删除账户** ```sql DROP USER myuser; ``` -**查看权限** +**查看权限** ```sql SHOW GRANTS FOR myuser; ``` -**授予权限** +**授予权限** 账户用 username@host 的形式定义,username@% 使用的是默认主机名。 @@ -740,7 +754,7 @@ SHOW GRANTS FOR myuser; GRANT SELECT, INSERT ON mydatabase.* TO myuser; ``` -**删除权限** +**删除权限** GRANT 和 REVOKE 可在几个层次上控制访问权限: @@ -754,9 +768,9 @@ GRANT 和 REVOKE 可在几个层次上控制访问权限: REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; ``` -**更改密码** +**更改密码** -必须使用 Password() 函数 +必须使用 Password() 函数进行加密。 ```sql SET PASSWROD FOR myuser = Password('new_password'); @@ -765,9 +779,3 @@ SET PASSWROD FOR myuser = Password('new_password'); # 参考资料 - BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git a/notes/Socket.md b/notes/Socket.md new file mode 100644 index 0000000000..2348ebfa56 --- /dev/null +++ b/notes/Socket.md @@ -0,0 +1,337 @@ +# Socket + +* [Socket](#socket) + * [一、I/O 模型](#一io-模型) + * [阻塞式 I/O](#阻塞式-io) + * [非阻塞式 I/O](#非阻塞式-io) + * [I/O 复用](#io-复用) + * [信号驱动 I/O](#信号驱动-io) + * [异步 I/O](#异步-io) + * [五大 I/O 模型比较](#五大-io-模型比较) + * [二、I/O 复用](#二io-复用) + * [select](#select) + * [poll](#poll) + * [比较](#比较) + * [epoll](#epoll) + * [工作模式](#工作模式) + * [应用场景](#应用场景) + * [参考资料](#参考资料) + + + +## 一、I/O 模型 + +一个输入操作通常包括两个阶段: + +- 等待数据准备好 +- 从内核向进程复制数据 + +对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所等待数据到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用进程缓冲区。 + +Unix 有五种 I/O 模型: + +- 阻塞式 I/O +- 非阻塞式 I/O +- I/O 复用(select 和 poll) +- 信号驱动式 I/O(SIGIO) +- 异步 I/O(AIO) + +### 阻塞式 I/O + +应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。 + +应该注意到,在阻塞的过程中,其它应用进程还可以执行,因此阻塞不意味着整个操作系统都被阻塞。因为其它应用进程还可以执行,所以不消耗 CPU 时间,这种模型的 CPU 利用率会比较高。 + +下图中,recvfrom() 用于接收 Socket 传来的数据,并复制到应用进程的缓冲区 buf 中。这里把 recvfrom() 当成系统调用。 + +```c +ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); +``` + +

+ +### 非阻塞式 I/O + +应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的执行系统调用来获知 I/O 是否完成,这种方式称为轮询(polling)。 + +由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低。 + +

+ +### I/O 复用 + +使用 select 或者 poll 等待数据,并且可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时返回,之后再使用 recvfrom 把数据从内核复制到进程中。 + +它可以让单个进程具有处理多个 I/O 事件的能力。又被称为 Event Driven I/O,即事件驱动 I/O。 + +如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。 + +

+ +### 信号驱动 I/O + +应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。 + +相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。 + +

+ +### 异步 I/O + +应用进程执行 aio_read 系统调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。 + +异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。 + +

+ +### 五大 I/O 模型比较 + +- 同步 I/O:将数据从内核缓冲区复制到应用进程缓冲区的阶段(第二阶段),应用进程会阻塞。 +- 异步 I/O:第二阶段应用进程不会阻塞。 + +同步 I/O 包括阻塞式 I/O、非阻塞式 I/O、I/O 复用和信号驱动 I/O ,它们的主要区别在第一个阶段。 + +非阻塞式 I/O 、信号驱动 I/O 和异步 I/O 在第一阶段不会阻塞。 + +

+ +## 二、I/O 复用 + +select/poll/epoll 都是 I/O 多路复用的具体实现,select 出现的最早,之后是 poll,再是 epoll。 + +### select + +```c +int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +``` + +select 允许应用程序监视一组文件描述符,等待一个或者多个描述符成为就绪状态,从而完成 I/O 操作。 + +- fd_set 使用数组实现,数组大小使用 FD_SETSIZE 定义,所以只能监听少于 FD_SETSIZE 数量的描述符。有三种类型的描述符类型:readset、writeset、exceptset,分别对应读、写、异常条件的描述符集合。 + +- timeout 为超时参数,调用 select 会一直阻塞直到有描述符的事件到达或者等待的时间超过 timeout。 + +- 成功调用返回结果大于 0,出错返回结果为 -1,超时返回结果为 0。 + +```c +fd_set fd_in, fd_out; +struct timeval tv; + +// Reset the sets +FD_ZERO( &fd_in ); +FD_ZERO( &fd_out ); + +// Monitor sock1 for input events +FD_SET( sock1, &fd_in ); + +// Monitor sock2 for output events +FD_SET( sock2, &fd_out ); + +// Find out which socket has the largest numeric value as select requires it +int largest_sock = sock1 > sock2 ? sock1 : sock2; + +// Wait up to 10 seconds +tv.tv_sec = 10; +tv.tv_usec = 0; + +// Call the select +int ret = select( largest_sock + 1, &fd_in, &fd_out, NULL, &tv ); + +// Check if select actually succeed +if ( ret == -1 ) + // report error and abort +else if ( ret == 0 ) + // timeout; no event detected +else +{ + if ( FD_ISSET( sock1, &fd_in ) ) + // input event on sock1 + + if ( FD_ISSET( sock2, &fd_out ) ) + // output event on sock2 +} +``` + +### poll + +```c +int poll(struct pollfd *fds, unsigned int nfds, int timeout); +``` + +poll 的功能与 select 类似,也是等待一组描述符中的一个成为就绪状态。 + +poll 中的描述符是 pollfd 类型的数组,pollfd 的定义如下: + +```c +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ + }; +``` + + +```c +// The structure for two events +struct pollfd fds[2]; + +// Monitor sock1 for input +fds[0].fd = sock1; +fds[0].events = POLLIN; + +// Monitor sock2 for output +fds[1].fd = sock2; +fds[1].events = POLLOUT; + +// Wait 10 seconds +int ret = poll( &fds, 2, 10000 ); +// Check if poll actually succeed +if ( ret == -1 ) + // report error and abort +else if ( ret == 0 ) + // timeout; no event detected +else +{ + // If we detect the event, zero it out so we can reuse the structure + if ( fds[0].revents & POLLIN ) + fds[0].revents = 0; + // input event on sock1 + + if ( fds[1].revents & POLLOUT ) + fds[1].revents = 0; + // output event on sock2 +} +``` + +### 比较 + +#### 1. 功能 + +select 和 poll 的功能基本相同,不过在一些实现细节上有所不同。 + +- select 会修改描述符,而 poll 不会; +- select 的描述符类型使用数组实现,FD_SETSIZE 大小默认为 1024,因此默认只能监听少于 1024 个描述符。如果要监听更多描述符的话,需要修改 FD_SETSIZE 之后重新编译;而 poll 没有描述符数量的限制; +- poll 提供了更多的事件类型,并且对描述符的重复利用上比 select 高。 +- 如果一个线程对某个描述符调用了 select 或者 poll,另一个线程关闭了该描述符,会导致调用结果不确定。 + +#### 2. 速度 + +select 和 poll 速度都比较慢,每次调用都需要将全部描述符从应用进程缓冲区复制到内核缓冲区。 + +#### 3. 可移植性 + +几乎所有的系统都支持 select,但是只有比较新的系统支持 poll。 + +### epoll + +```c +int epoll_create(int size); +int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); +int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); +``` + +epoll_ctl() 用于向内核注册新的描述符或者是改变某个文件描述符的状态。已注册的描述符在内核中会被维护在一棵红黑树上,通过回调函数内核会将 I/O 准备好的描述符加入到一个链表中管理,进程调用 epoll_wait() 便可以得到事件完成的描述符。 + +从上面的描述可以看出,epoll 只需要将描述符从进程缓冲区向内核缓冲区拷贝一次,并且进程不需要通过轮询来获得事件完成的描述符。 + +epoll 仅适用于 Linux OS。 + +epoll 比 select 和 poll 更加灵活而且没有描述符数量限制。 + +epoll 对多线程编程更有友好,一个线程调用了 epoll_wait() 另一个线程关闭了同一个描述符也不会产生像 select 和 poll 的不确定情况。 + +```c +// Create the epoll descriptor. Only one is needed per app, and is used to monitor all sockets. +// The function argument is ignored (it was not before, but now it is), so put your favorite number here +int pollingfd = epoll_create( 0xCAFE ); + +if ( pollingfd < 0 ) + // report error + +// Initialize the epoll structure in case more members are added in future +struct epoll_event ev = { 0 }; + +// Associate the connection class instance with the event. You can associate anything +// you want, epoll does not use this information. We store a connection class pointer, pConnection1 +ev.data.ptr = pConnection1; + +// Monitor for input, and do not automatically rearm the descriptor after the event +ev.events = EPOLLIN | EPOLLONESHOT; +// Add the descriptor into the monitoring list. We can do it even if another thread is +// waiting in epoll_wait - the descriptor will be properly added +if ( epoll_ctl( epollfd, EPOLL_CTL_ADD, pConnection1->getSocket(), &ev ) != 0 ) + // report error + +// Wait for up to 20 events (assuming we have added maybe 200 sockets before that it may happen) +struct epoll_event pevents[ 20 ]; + +// Wait for 10 seconds, and retrieve less than 20 epoll_event and store them into epoll_event array +int ready = epoll_wait( pollingfd, pevents, 20, 10000 ); +// Check if epoll actually succeed +if ( ret == -1 ) + // report error and abort +else if ( ret == 0 ) + // timeout; no event detected +else +{ + // Check if any events detected + for ( int i = 0; i < ready; i++ ) + { + if ( pevents[i].events & EPOLLIN ) + { + // Get back our connection pointer + Connection * c = (Connection*) pevents[i].data.ptr; + c->handleReadEvent(); + } + } +} +``` + + +### 工作模式 + +epoll 的描述符事件有两种触发模式:LT(level trigger)和 ET(edge trigger)。 + +#### 1. LT 模式 + +当 epoll_wait() 检测到描述符事件到达时,将此事件通知进程,进程可以不立即处理该事件,下次调用 epoll_wait() 会再次通知进程。是默认的一种模式,并且同时支持 Blocking 和 No-Blocking。 + +#### 2. ET 模式 + +和 LT 模式不同的是,通知之后进程必须立即处理事件,下次再调用 epoll_wait() 时不会再得到事件到达的通知。 + +很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。只支持 No-Blocking,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。 + +### 应用场景 + +很容易产生一种错觉认为只要用 epoll 就可以了,select 和 poll 都已经过时了,其实它们都有各自的使用场景。 + +#### 1. select 应用场景 + +select 的 timeout 参数精度为微秒,而 poll 和 epoll 为毫秒,因此 select 更加适用于实时性要求比较高的场景,比如核反应堆的控制。 + +select 可移植性更好,几乎被所有主流平台所支持。 + +#### 2. poll 应用场景 + +poll 没有最大描述符数量的限制,如果平台支持并且对实时性要求不高,应该使用 poll 而不是 select。 + +#### 3. epoll 应用场景 + +只需要运行在 Linux 平台上,有大量的描述符需要同时轮询,并且这些连接最好是长连接。 + +需要同时监控小于 1000 个描述符,就没有必要使用 epoll,因为这个应用场景下并不能体现 epoll 的优势。 + +需要监控的描述符状态变化多,而且都是非常短暂的,也没有必要使用 epoll。因为 epoll 中的所有描述符都存储在内核中,造成每次需要对描述符的状态改变都需要通过 epoll_ctl() 进行系统调用,频繁系统调用降低效率。并且 epoll 的描述符存储在内核,不容易调试。 + +## 参考资料 + +- Stevens W R, Fenner B, Rudoff A M. UNIX network programming[M]. Addison-Wesley Professional, 2004. +- http://man7.org/linux/man-pages/man2/select.2.html +- http://man7.org/linux/man-pages/man2/poll.2.html +- [Boost application performance using asynchronous I/O](https://www.ibm.com/developerworks/linux/library/l-async/) +- [Synchronous and Asynchronous I/O](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx) +- [Linux IO 模式及 select、poll、epoll 详解](https://segmentfault.com/a/1190000003063859) +- [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html) +- [select / poll / epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/) +- [Browse the source code of userspace/glibc/sysdeps/unix/sysv/linux/ online](https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/) diff --git a/notes/pics/0038204c-4b8a-42a5-921d-080f6674f989.png b/notes/pics/0038204c-4b8a-42a5-921d-080f6674f989.png new file mode 100644 index 0000000000..31a2d8d2e5 Binary files /dev/null and b/notes/pics/0038204c-4b8a-42a5-921d-080f6674f989.png differ diff --git a/notes/pics/005b481b-502b-4e3f-985d-d043c2b330aa.png b/notes/pics/005b481b-502b-4e3f-985d-d043c2b330aa.png new file mode 100644 index 0000000000..0f8543c2fe Binary files /dev/null and b/notes/pics/005b481b-502b-4e3f-985d-d043c2b330aa.png differ diff --git a/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png b/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png new file mode 100644 index 0000000000..c5fc251192 Binary files /dev/null and b/notes/pics/011f3ef6-d824-4d43-8b2c-36dab8eaaa72-1.png differ diff --git a/docs/notes/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg b/notes/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg similarity index 100% rename from docs/notes/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg rename to notes/pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg diff --git a/notes/pics/02943a90-7dd4-4e9a-9325-f8217d3cc54d.jpg b/notes/pics/02943a90-7dd4-4e9a-9325-f8217d3cc54d.jpg new file mode 100644 index 0000000000..41d0a90425 Binary files /dev/null and b/notes/pics/02943a90-7dd4-4e9a-9325-f8217d3cc54d.jpg differ diff --git a/notes/pics/02a1fbfd-7a9d-4114-95df-ca2445587a1f.jpg b/notes/pics/02a1fbfd-7a9d-4114-95df-ca2445587a1f.jpg new file mode 100644 index 0000000000..d345a1780c Binary files /dev/null and b/notes/pics/02a1fbfd-7a9d-4114-95df-ca2445587a1f.jpg differ diff --git a/docs/notes/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg b/notes/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg similarity index 100% rename from docs/notes/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg rename to notes/pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg diff --git a/docs/notes/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png b/notes/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png similarity index 100% rename from docs/notes/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png rename to notes/pics/042cf928-3c8e-4815-ae9c-f2780202c68f.png diff --git a/notes/pics/047faac4-a368-4565-8331-2b66253080d3.jpg b/notes/pics/047faac4-a368-4565-8331-2b66253080d3.jpg new file mode 100644 index 0000000000..f6506214ce Binary files /dev/null and b/notes/pics/047faac4-a368-4565-8331-2b66253080d3.jpg differ diff --git a/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png b/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png new file mode 100644 index 0000000000..44e720085f Binary files /dev/null and b/notes/pics/051e436c-0e46-4c59-8f67-52d89d656182.png differ diff --git a/notes/pics/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg b/notes/pics/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg new file mode 100644 index 0000000000..0e9bbc868c Binary files /dev/null and b/notes/pics/05a08f2e-9914-4a77-92ef-aebeaecf4f66.jpg differ diff --git a/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg b/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg new file mode 100644 index 0000000000..09e496ef8d Binary files /dev/null and b/notes/pics/067b310c-6877-40fe-9dcf-10654e737485.jpg differ diff --git a/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg b/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg new file mode 100644 index 0000000000..80b1791d59 Binary files /dev/null and b/notes/pics/075e1977-7846-4928-96c8-bb5b0268693c.jpg differ diff --git a/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png b/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png new file mode 100644 index 0000000000..c4dbe76450 Binary files /dev/null and b/notes/pics/0889c0b4-07b4-45fc-873c-e0e16b97f67d.png differ diff --git a/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg b/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg new file mode 100644 index 0000000000..c87a6f8674 Binary files /dev/null and b/notes/pics/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg differ diff --git a/notes/pics/093f9e57-429c-413a-83ee-c689ba596cef.png b/notes/pics/093f9e57-429c-413a-83ee-c689ba596cef.png new file mode 100644 index 0000000000..7958531c41 Binary files /dev/null and b/notes/pics/093f9e57-429c-413a-83ee-c689ba596cef.png differ diff --git a/notes/pics/094b279a-b2db-4be7-87a3-b2a039c7448e.jpg b/notes/pics/094b279a-b2db-4be7-87a3-b2a039c7448e.jpg new file mode 100644 index 0000000000..eb7437e647 Binary files /dev/null and b/notes/pics/094b279a-b2db-4be7-87a3-b2a039c7448e.jpg differ diff --git a/notes/pics/094e3ac8-e080-4e94-9f0a-64c25abc695e.gif b/notes/pics/094e3ac8-e080-4e94-9f0a-64c25abc695e.gif new file mode 100644 index 0000000000..6d964560a8 Binary files /dev/null and b/notes/pics/094e3ac8-e080-4e94-9f0a-64c25abc695e.gif differ diff --git a/notes/pics/0972501d-f854-4d26-8fce-babb27c267f6.jpg b/notes/pics/0972501d-f854-4d26-8fce-babb27c267f6.jpg new file mode 100644 index 0000000000..d43cafc09b Binary files /dev/null and b/notes/pics/0972501d-f854-4d26-8fce-babb27c267f6.jpg differ diff --git a/notes/pics/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif b/notes/pics/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif new file mode 100644 index 0000000000..81ebc9d26e Binary files /dev/null and b/notes/pics/0ad9f7ba-f408-4999-a77a-9b73562c9088.gif differ diff --git a/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg b/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg new file mode 100644 index 0000000000..430719d0b4 Binary files /dev/null and b/notes/pics/0b587744-c0a8-46f2-8d72-e8f070d67b4b.jpg differ diff --git a/notes/pics/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg b/notes/pics/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg new file mode 100644 index 0000000000..068f6dee4e Binary files /dev/null and b/notes/pics/0c12221f-729e-4c22-b0ba-0dfc909f8adf.jpg differ diff --git a/notes/pics/0c6f9930-8704-4a54-af23-19f9ca3e48b0.jpg b/notes/pics/0c6f9930-8704-4a54-af23-19f9ca3e48b0.jpg new file mode 100644 index 0000000000..8bb39d713b Binary files /dev/null and b/notes/pics/0c6f9930-8704-4a54-af23-19f9ca3e48b0.jpg differ diff --git a/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f-1572687622947.gif b/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f-1572687622947.gif new file mode 100644 index 0000000000..c850753245 Binary files /dev/null and b/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f-1572687622947.gif differ diff --git a/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif b/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif new file mode 100644 index 0000000000..e1e41d2dc5 Binary files /dev/null and b/notes/pics/0dae7e93-cfd1-4bd3-97e8-325b032b716f.gif differ diff --git a/notes/pics/0dbc4f7d-05c9-4aae-8065-7b7ea7e9709e.gif b/notes/pics/0dbc4f7d-05c9-4aae-8065-7b7ea7e9709e.gif new file mode 100644 index 0000000000..c5051aebc5 Binary files /dev/null and b/notes/pics/0dbc4f7d-05c9-4aae-8065-7b7ea7e9709e.gif differ diff --git a/notes/pics/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png b/notes/pics/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png new file mode 100644 index 0000000000..1bb834c8ca Binary files /dev/null and b/notes/pics/0dd2d40a-5b2b-4d45-b176-e75a4cd4bdbf.png differ diff --git a/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png b/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png new file mode 100644 index 0000000000..9e35ec60bd Binary files /dev/null and b/notes/pics/0df5d84c-e7ca-4e3a-a688-bb8e68894467.png differ diff --git a/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png b/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png new file mode 100644 index 0000000000..4a1fcfa98f Binary files /dev/null and b/notes/pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png differ diff --git a/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png b/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png new file mode 100644 index 0000000000..837088e815 Binary files /dev/null and b/notes/pics/0ea37ee2-c224-4c79-b895-e131c6805c40.png differ diff --git a/notes/pics/0f399a9f-1351-4b2d-b8a4-2ebe82b1a703.jpg b/notes/pics/0f399a9f-1351-4b2d-b8a4-2ebe82b1a703.jpg new file mode 100644 index 0000000000..e1bd6fe898 Binary files /dev/null and b/notes/pics/0f399a9f-1351-4b2d-b8a4-2ebe82b1a703.jpg differ diff --git a/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png b/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png new file mode 100644 index 0000000000..778d7c90b2 Binary files /dev/null and b/notes/pics/0f754c1d-b5cb-48cd-90e0-4a86034290a1.png differ diff --git a/notes/pics/0f8d178b-52d8-491b-9dfd-41e05a952578.gif b/notes/pics/0f8d178b-52d8-491b-9dfd-41e05a952578.gif new file mode 100644 index 0000000000..b28b214e00 Binary files /dev/null and b/notes/pics/0f8d178b-52d8-491b-9dfd-41e05a952578.gif differ diff --git a/notes/pics/0fa6c237-a909-4e2a-a771-2c5485cd8ce0.png b/notes/pics/0fa6c237-a909-4e2a-a771-2c5485cd8ce0.png new file mode 100644 index 0000000000..a4eead8096 Binary files /dev/null and b/notes/pics/0fa6c237-a909-4e2a-a771-2c5485cd8ce0.png differ diff --git a/notes/pics/101550414151983.gif b/notes/pics/101550414151983.gif new file mode 100644 index 0000000000..f1c63cf7a0 Binary files /dev/null and b/notes/pics/101550414151983.gif differ diff --git a/notes/pics/1097658b-c0e6-4821-be9b-25304726a11c.jpg b/notes/pics/1097658b-c0e6-4821-be9b-25304726a11c.jpg new file mode 100644 index 0000000000..fb8aa6720b Binary files /dev/null and b/notes/pics/1097658b-c0e6-4821-be9b-25304726a11c.jpg differ diff --git a/notes/pics/111521118015898.gif b/notes/pics/111521118015898.gif new file mode 100644 index 0000000000..ca57676921 Binary files /dev/null and b/notes/pics/111521118015898.gif differ diff --git a/notes/pics/111521118445538.gif b/notes/pics/111521118445538.gif new file mode 100644 index 0000000000..a4f2fe4ca1 Binary files /dev/null and b/notes/pics/111521118445538.gif differ diff --git a/notes/pics/111521118483039.gif b/notes/pics/111521118483039.gif new file mode 100644 index 0000000000..0f63c44310 Binary files /dev/null and b/notes/pics/111521118483039.gif differ diff --git a/notes/pics/111521118640738.gif b/notes/pics/111521118640738.gif new file mode 100644 index 0000000000..cb6a9b2957 Binary files /dev/null and b/notes/pics/111521118640738.gif differ diff --git a/notes/pics/111521119203347.gif b/notes/pics/111521119203347.gif new file mode 100644 index 0000000000..deb7c350ab Binary files /dev/null and b/notes/pics/111521119203347.gif differ diff --git a/notes/pics/111521119368714.gif b/notes/pics/111521119368714.gif new file mode 100644 index 0000000000..0793cfbda7 Binary files /dev/null and b/notes/pics/111521119368714.gif differ diff --git a/notes/pics/111550414182638.gif b/notes/pics/111550414182638.gif new file mode 100644 index 0000000000..96f5a51892 Binary files /dev/null and b/notes/pics/111550414182638.gif differ diff --git a/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png b/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png new file mode 100644 index 0000000000..cc6726ff09 Binary files /dev/null and b/notes/pics/11236498-1417-46ce-a1b0-e10054256955.png differ diff --git a/notes/pics/1176f9e1-3442-4808-a47a-76fbaea1b806.png b/notes/pics/1176f9e1-3442-4808-a47a-76fbaea1b806.png new file mode 100644 index 0000000000..581673ff36 Binary files /dev/null and b/notes/pics/1176f9e1-3442-4808-a47a-76fbaea1b806.png differ diff --git a/notes/pics/11b27de5-5a9d-45e4-95cc-417fa3ad1d38.jpg b/notes/pics/11b27de5-5a9d-45e4-95cc-417fa3ad1d38.jpg new file mode 100644 index 0000000000..4bc7e502fa Binary files /dev/null and b/notes/pics/11b27de5-5a9d-45e4-95cc-417fa3ad1d38.jpg differ diff --git a/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png b/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png new file mode 100644 index 0000000000..0db77ec4f3 Binary files /dev/null and b/notes/pics/1202b2d6-9469-4251-bd47-ca6034fb6116.png differ diff --git a/notes/pics/12a65cc6-20e0-4706-9fe6-3ba49413d7f6.png b/notes/pics/12a65cc6-20e0-4706-9fe6-3ba49413d7f6.png new file mode 100644 index 0000000000..6928361750 Binary files /dev/null and b/notes/pics/12a65cc6-20e0-4706-9fe6-3ba49413d7f6.png differ diff --git a/notes/pics/13454fa1-23a8-4578-9663-2b13a6af564a.jpg b/notes/pics/13454fa1-23a8-4578-9663-2b13a6af564a.jpg new file mode 100644 index 0000000000..5284457bb2 Binary files /dev/null and b/notes/pics/13454fa1-23a8-4578-9663-2b13a6af564a.jpg differ diff --git a/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png b/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png new file mode 100644 index 0000000000..790e5c923e Binary files /dev/null and b/notes/pics/137c593d-0a9e-47b8-a9e6-b71f540b82dd.png differ diff --git a/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png b/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png new file mode 100644 index 0000000000..5de1625e92 Binary files /dev/null and b/notes/pics/13b0940e-d1d7-4b17-af4f-b70cb0a75e08.png differ diff --git a/notes/pics/14389ea4-8d96-4e96-9f76-564ca3324c1e.png b/notes/pics/14389ea4-8d96-4e96-9f76-564ca3324c1e.png new file mode 100644 index 0000000000..71b87153a4 Binary files /dev/null and b/notes/pics/14389ea4-8d96-4e96-9f76-564ca3324c1e.png differ diff --git a/notes/pics/1492928105791_3.png b/notes/pics/1492928105791_3.png new file mode 100644 index 0000000000..95684c6125 Binary files /dev/null and b/notes/pics/1492928105791_3.png differ diff --git a/notes/pics/1492928416812_4.png b/notes/pics/1492928416812_4.png new file mode 100644 index 0000000000..341d1da26b Binary files /dev/null and b/notes/pics/1492928416812_4.png differ diff --git a/notes/pics/1492929000361_5.png b/notes/pics/1492929000361_5.png new file mode 100644 index 0000000000..449b97dd23 Binary files /dev/null and b/notes/pics/1492929000361_5.png differ diff --git a/notes/pics/1492929444818_6.png b/notes/pics/1492929444818_6.png new file mode 100644 index 0000000000..8c401099ed Binary files /dev/null and b/notes/pics/1492929444818_6.png differ diff --git a/notes/pics/1492929553651_7.png b/notes/pics/1492929553651_7.png new file mode 100644 index 0000000000..62363b36ee Binary files /dev/null and b/notes/pics/1492929553651_7.png differ diff --git a/notes/pics/1492930243286_8.png b/notes/pics/1492930243286_8.png new file mode 100644 index 0000000000..777a68cb28 Binary files /dev/null and b/notes/pics/1492930243286_8.png differ diff --git a/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png b/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png new file mode 100644 index 0000000000..ece9366350 Binary files /dev/null and b/notes/pics/14fe1e71-8518-458f-a220-116003061a83.png differ diff --git a/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg b/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg new file mode 100644 index 0000000000..1d62bef3ab Binary files /dev/null and b/notes/pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg differ diff --git a/docs/notes/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg b/notes/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg similarity index 100% rename from docs/notes/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg rename to notes/pics/1556770b-8c01-4681-af10-46f1df69202c.jpg diff --git a/notes/pics/15851555-5abc-497d-ad34-efed10f43a6b.png b/notes/pics/15851555-5abc-497d-ad34-efed10f43a6b.png new file mode 100644 index 0000000000..fc45f2eccb Binary files /dev/null and b/notes/pics/15851555-5abc-497d-ad34-efed10f43a6b.png differ diff --git a/notes/pics/1623f524-b011-40c8-b83f-eab38d538f76.png b/notes/pics/1623f524-b011-40c8-b83f-eab38d538f76.png new file mode 100644 index 0000000000..90f74bd81a Binary files /dev/null and b/notes/pics/1623f524-b011-40c8-b83f-eab38d538f76.png differ diff --git a/notes/pics/164944d3-bbd2-4bb2-924b-e62199c51b90.png b/notes/pics/164944d3-bbd2-4bb2-924b-e62199c51b90.png new file mode 100644 index 0000000000..a81dbb83fc Binary files /dev/null and b/notes/pics/164944d3-bbd2-4bb2-924b-e62199c51b90.png differ diff --git a/notes/pics/17e301df-52e8-4886-b593-841a16d13e44.png b/notes/pics/17e301df-52e8-4886-b593-841a16d13e44.png new file mode 100644 index 0000000000..b1ae4d1c55 Binary files /dev/null and b/notes/pics/17e301df-52e8-4886-b593-841a16d13e44.png differ diff --git a/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png b/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png new file mode 100644 index 0000000000..33bb262129 Binary files /dev/null and b/notes/pics/1818e141-8700-4026-99f7-900a545875f5.png differ diff --git a/notes/pics/195f8693-5ec4-4987-8560-f25e365879dd.png b/notes/pics/195f8693-5ec4-4987-8560-f25e365879dd.png new file mode 100644 index 0000000000..b2ea3f87a4 Binary files /dev/null and b/notes/pics/195f8693-5ec4-4987-8560-f25e365879dd.png differ diff --git a/notes/pics/19d423e9-74f7-4c2b-9b97-55890e0d5193.png b/notes/pics/19d423e9-74f7-4c2b-9b97-55890e0d5193.png new file mode 100644 index 0000000000..eeee892069 Binary files /dev/null and b/notes/pics/19d423e9-74f7-4c2b-9b97-55890e0d5193.png differ diff --git a/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg b/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg new file mode 100644 index 0000000000..dc92720774 Binary files /dev/null and b/notes/pics/1a851e90-0d5c-4d4f-ac54-34c20ecfb903.jpg differ diff --git a/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png b/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png new file mode 100644 index 0000000000..3457a74b0c Binary files /dev/null and b/notes/pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png differ diff --git a/docs/notes/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg b/notes/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg similarity index 100% rename from docs/notes/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg rename to notes/pics/1ab49e39-012b-4383-8284-26570987e3c4.jpg diff --git a/notes/pics/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png b/notes/pics/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png new file mode 100644 index 0000000000..44b12ccd81 Binary files /dev/null and b/notes/pics/1b80288d-1b35-4cd3-aa17-7e27ab9a2389.png differ diff --git a/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png b/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png new file mode 100644 index 0000000000..f7b624cbbe Binary files /dev/null and b/notes/pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png differ diff --git a/notes/pics/1d28ad05-39e5-49a2-a6a1-a6f496adba6a.png b/notes/pics/1d28ad05-39e5-49a2-a6a1-a6f496adba6a.png new file mode 100644 index 0000000000..7c7cce6d27 Binary files /dev/null and b/notes/pics/1d28ad05-39e5-49a2-a6a1-a6f496adba6a.png differ diff --git a/notes/pics/1daadd30-bd54-419e-a764-0dc95ac69653_200.png b/notes/pics/1daadd30-bd54-419e-a764-0dc95ac69653_200.png new file mode 100644 index 0000000000..3e0cd6499a Binary files /dev/null and b/notes/pics/1daadd30-bd54-419e-a764-0dc95ac69653_200.png differ diff --git a/notes/pics/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png b/notes/pics/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png new file mode 100644 index 0000000000..e308217703 Binary files /dev/null and b/notes/pics/1db1c7ea-0443-478b-8df9-7e33b1336cc4.png differ diff --git a/notes/pics/1e2c588c-72b7-445e-aacb-d55dc8a88c29.png b/notes/pics/1e2c588c-72b7-445e-aacb-d55dc8a88c29.png new file mode 100644 index 0000000000..ec214a8e75 Binary files /dev/null and b/notes/pics/1e2c588c-72b7-445e-aacb-d55dc8a88c29.png differ diff --git a/notes/pics/1e46fd03-0cda-4d60-9b1c-0c256edaf6b2.png b/notes/pics/1e46fd03-0cda-4d60-9b1c-0c256edaf6b2.png new file mode 100644 index 0000000000..8c4960337c Binary files /dev/null and b/notes/pics/1e46fd03-0cda-4d60-9b1c-0c256edaf6b2.png differ diff --git a/notes/pics/1e6affc4-18e5-4596-96ef-fb84c63bf88a.png b/notes/pics/1e6affc4-18e5-4596-96ef-fb84c63bf88a.png new file mode 100644 index 0000000000..e28387b06c Binary files /dev/null and b/notes/pics/1e6affc4-18e5-4596-96ef-fb84c63bf88a.png differ diff --git a/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png b/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png new file mode 100644 index 0000000000..6b4c77813d Binary files /dev/null and b/notes/pics/1fc969e4-0e7c-441b-b53c-01950d2f2be5.png differ diff --git a/notes/pics/1fe2dc77-9a2d-4643-90b3-bbf50f649bac.png b/notes/pics/1fe2dc77-9a2d-4643-90b3-bbf50f649bac.png new file mode 100644 index 0000000000..0c1ae3cd08 Binary files /dev/null and b/notes/pics/1fe2dc77-9a2d-4643-90b3-bbf50f649bac.png differ diff --git a/notes/pics/2017-06-11-ca.png b/notes/pics/2017-06-11-ca.png new file mode 100644 index 0000000000..d3b7b2093a Binary files /dev/null and b/notes/pics/2017-06-11-ca.png differ diff --git a/notes/pics/206f965e-53b2-4732-90cf-75910b80d7ac.png b/notes/pics/206f965e-53b2-4732-90cf-75910b80d7ac.png new file mode 100644 index 0000000000..069b7e1752 Binary files /dev/null and b/notes/pics/206f965e-53b2-4732-90cf-75910b80d7ac.png differ diff --git a/notes/pics/22870bbe-898f-4c17-a31a-d7c5ee5d1c10.png b/notes/pics/22870bbe-898f-4c17-a31a-d7c5ee5d1c10.png new file mode 100644 index 0000000000..6cbae1578a Binary files /dev/null and b/notes/pics/22870bbe-898f-4c17-a31a-d7c5ee5d1c10.png differ diff --git a/docs/notes/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png b/notes/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png similarity index 100% rename from docs/notes/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png rename to notes/pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png diff --git a/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg b/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg new file mode 100644 index 0000000000..b45bb54639 Binary files /dev/null and b/notes/pics/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg differ diff --git a/notes/pics/23219e4c-9fc0-4051-b33a-2bd95bf054ab.jpg b/notes/pics/23219e4c-9fc0-4051-b33a-2bd95bf054ab.jpg new file mode 100644 index 0000000000..61c390693b Binary files /dev/null and b/notes/pics/23219e4c-9fc0-4051-b33a-2bd95bf054ab.jpg differ diff --git a/notes/pics/233f8d89-31d7-413f-9c02-042f19c46ba1.png b/notes/pics/233f8d89-31d7-413f-9c02-042f19c46ba1.png new file mode 100644 index 0000000000..05d4de2eaa Binary files /dev/null and b/notes/pics/233f8d89-31d7-413f-9c02-042f19c46ba1.png differ diff --git a/notes/pics/23b9d625-ef28-42b5-bb22-d7aedd007e16.jpg b/notes/pics/23b9d625-ef28-42b5-bb22-d7aedd007e16.jpg new file mode 100644 index 0000000000..b243195575 Binary files /dev/null and b/notes/pics/23b9d625-ef28-42b5-bb22-d7aedd007e16.jpg differ diff --git a/docs/notes/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png b/notes/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png similarity index 100% rename from docs/notes/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png rename to notes/pics/23ba890e-e11c-45e2-a20c-64d217f83430.png diff --git a/notes/pics/23e4462b-263f-4d15-8805-529e0ca7a4d1.jpg b/notes/pics/23e4462b-263f-4d15-8805-529e0ca7a4d1.jpg new file mode 100644 index 0000000000..dad602bb71 Binary files /dev/null and b/notes/pics/23e4462b-263f-4d15-8805-529e0ca7a4d1.jpg differ diff --git a/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg b/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg new file mode 100644 index 0000000000..1e85dc95ed Binary files /dev/null and b/notes/pics/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg differ diff --git a/notes/pics/265bab88-7be9-44c5-a33f-f93d9882c096.png b/notes/pics/265bab88-7be9-44c5-a33f-f93d9882c096.png new file mode 100644 index 0000000000..c68007b42f Binary files /dev/null and b/notes/pics/265bab88-7be9-44c5-a33f-f93d9882c096.png differ diff --git a/docs/notes/pics/2719067e-b299-4639-9065-bed6729dbf0b.png b/notes/pics/2719067e-b299-4639-9065-bed6729dbf0b.png similarity index 100% rename from docs/notes/pics/2719067e-b299-4639-9065-bed6729dbf0b.png rename to notes/pics/2719067e-b299-4639-9065-bed6729dbf0b.png diff --git a/docs/notes/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png b/notes/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png similarity index 100% rename from docs/notes/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png rename to notes/pics/271ce08f-c124-475f-b490-be44fedc6d2e.png diff --git a/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg b/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg new file mode 100644 index 0000000000..c2697572e7 Binary files /dev/null and b/notes/pics/278fe431-af88-4a95-a895-9c3b80117de3.jpg differ diff --git a/notes/pics/2797a609-68db-4d7b-8701-41ac9a34b14f.jpg b/notes/pics/2797a609-68db-4d7b-8701-41ac9a34b14f.jpg new file mode 100644 index 0000000000..f9e7786315 Binary files /dev/null and b/notes/pics/2797a609-68db-4d7b-8701-41ac9a34b14f.jpg differ diff --git a/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg b/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg new file mode 100644 index 0000000000..2cf77c687b Binary files /dev/null and b/notes/pics/292b4a35-4507-4256-84ff-c218f108ee31.jpg differ diff --git a/notes/pics/29a14735-e154-4f60-9a04-c9628e5d09f4.png b/notes/pics/29a14735-e154-4f60-9a04-c9628e5d09f4.png new file mode 100644 index 0000000000..b2655874d1 Binary files /dev/null and b/notes/pics/29a14735-e154-4f60-9a04-c9628e5d09f4.png differ diff --git a/docs/notes/pics/2_14_microkernelArchitecture.jpg b/notes/pics/2_14_microkernelArchitecture.jpg similarity index 100% rename from docs/notes/pics/2_14_microkernelArchitecture.jpg rename to notes/pics/2_14_microkernelArchitecture.jpg diff --git a/notes/pics/2a1f8b0f-1dd7-4409-b177-a381c58066ad.png b/notes/pics/2a1f8b0f-1dd7-4409-b177-a381c58066ad.png new file mode 100644 index 0000000000..7d76559513 Binary files /dev/null and b/notes/pics/2a1f8b0f-1dd7-4409-b177-a381c58066ad.png differ diff --git a/docs/notes/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png b/notes/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png similarity index 100% rename from docs/notes/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png rename to notes/pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png diff --git a/notes/pics/2b125bcd-1b36-43be-9b78-d90b076be549.png b/notes/pics/2b125bcd-1b36-43be-9b78-d90b076be549.png new file mode 100644 index 0000000000..e07b1812ad Binary files /dev/null and b/notes/pics/2b125bcd-1b36-43be-9b78-d90b076be549.png differ diff --git a/notes/pics/2b8bfd57-b4d1-4a75-bfb0-bcf1fba4014a.png b/notes/pics/2b8bfd57-b4d1-4a75-bfb0-bcf1fba4014a.png new file mode 100644 index 0000000000..a8b7184bee Binary files /dev/null and b/notes/pics/2b8bfd57-b4d1-4a75-bfb0-bcf1fba4014a.png differ diff --git a/notes/pics/2bab4127-3e7d-48cc-914e-436be859fb05.png b/notes/pics/2bab4127-3e7d-48cc-914e-436be859fb05.png new file mode 100644 index 0000000000..c65e1eaaac Binary files /dev/null and b/notes/pics/2bab4127-3e7d-48cc-914e-436be859fb05.png differ diff --git a/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg b/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg new file mode 100644 index 0000000000..d2dd5fc6cc Binary files /dev/null and b/notes/pics/2bcc58ad-bf7f-485c-89b5-e7cafc211ce2.jpg differ diff --git a/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png b/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png new file mode 100644 index 0000000000..f3ab93e526 Binary files /dev/null and b/notes/pics/2d09a847-b854-439c-9198-b29c65810944.png differ diff --git a/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg b/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg new file mode 100644 index 0000000000..403fbe4424 Binary files /dev/null and b/notes/pics/2de794ca-aa7b-48f3-a556-a0e2708cb976.jpg differ diff --git a/notes/pics/2f683fe8-bee8-46a9-86a7-685c8981555856191616.png b/notes/pics/2f683fe8-bee8-46a9-86a7-685c8981555856191616.png new file mode 100644 index 0000000000..ba7b5b0c87 Binary files /dev/null and b/notes/pics/2f683fe8-bee8-46a9-86a7-685c8981555856191616.png differ diff --git a/notes/pics/308a02e9-3346-4251-8c41-bd5536dab491.png b/notes/pics/308a02e9-3346-4251-8c41-bd5536dab491.png new file mode 100644 index 0000000000..5ec4647dd6 Binary files /dev/null and b/notes/pics/308a02e9-3346-4251-8c41-bd5536dab491.png differ diff --git a/notes/pics/30d6e95c-2e3c-4d32-bf4f-68128a70bc05.png b/notes/pics/30d6e95c-2e3c-4d32-bf4f-68128a70bc05.png new file mode 100644 index 0000000000..e5397988b7 Binary files /dev/null and b/notes/pics/30d6e95c-2e3c-4d32-bf4f-68128a70bc05.png differ diff --git a/notes/pics/31b7e8de-ed11-4f69-b5fd-ba454120ac31.jpg b/notes/pics/31b7e8de-ed11-4f69-b5fd-ba454120ac31.jpg new file mode 100644 index 0000000000..2f5fa186dd Binary files /dev/null and b/notes/pics/31b7e8de-ed11-4f69-b5fd-ba454120ac31.jpg differ diff --git a/notes/pics/31d9adce-2af8-4754-8386-0aabb4e500b0.png b/notes/pics/31d9adce-2af8-4754-8386-0aabb4e500b0.png new file mode 100644 index 0000000000..1248f60f6d Binary files /dev/null and b/notes/pics/31d9adce-2af8-4754-8386-0aabb4e500b0.png differ diff --git a/notes/pics/32b05e81-41b3-414a-8656-736c9604e3d6.png b/notes/pics/32b05e81-41b3-414a-8656-736c9604e3d6.png new file mode 100644 index 0000000000..38a07f6f02 Binary files /dev/null and b/notes/pics/32b05e81-41b3-414a-8656-736c9604e3d6.png differ diff --git a/notes/pics/33576849-9275-47bb-ada7-8ded5f5e7c73.png b/notes/pics/33576849-9275-47bb-ada7-8ded5f5e7c73.png new file mode 100644 index 0000000000..14f5b19d4f Binary files /dev/null and b/notes/pics/33576849-9275-47bb-ada7-8ded5f5e7c73.png differ diff --git a/notes/pics/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png b/notes/pics/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png new file mode 100644 index 0000000000..57fad7324f Binary files /dev/null and b/notes/pics/335fe19c-4a76-45ab-9320-88c90d6a0d7e.png differ diff --git a/notes/pics/34751bd9-e8e4-4c20-94bc-f7217049fada.png b/notes/pics/34751bd9-e8e4-4c20-94bc-f7217049fada.png new file mode 100644 index 0000000000..31e7cfc371 Binary files /dev/null and b/notes/pics/34751bd9-e8e4-4c20-94bc-f7217049fada.png differ diff --git a/notes/pics/35253fa4-f60a-4e3b-aaec-8fc835aabdac.gif b/notes/pics/35253fa4-f60a-4e3b-aaec-8fc835aabdac.gif new file mode 100644 index 0000000000..133e5b4058 Binary files /dev/null and b/notes/pics/35253fa4-f60a-4e3b-aaec-8fc835aabdac.gif differ diff --git a/notes/pics/35a8c711-0dc0-4613-95f3-be96c6c6e104.gif b/notes/pics/35a8c711-0dc0-4613-95f3-be96c6c6e104.gif new file mode 100644 index 0000000000..771fa1b20b Binary files /dev/null and b/notes/pics/35a8c711-0dc0-4613-95f3-be96c6c6e104.gif differ diff --git a/notes/pics/379444c9-f1d1-45cd-b7aa-b0c18427d388.jpg b/notes/pics/379444c9-f1d1-45cd-b7aa-b0c18427d388.jpg new file mode 100644 index 0000000000..18a144522f Binary files /dev/null and b/notes/pics/379444c9-f1d1-45cd-b7aa-b0c18427d388.jpg differ diff --git a/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg b/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg new file mode 100644 index 0000000000..27699929c0 Binary files /dev/null and b/notes/pics/390c913b-5f31-444f-bbdb-2b88b688e7ce.jpg differ diff --git a/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png b/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png new file mode 100644 index 0000000000..4f3d0ccd4f Binary files /dev/null and b/notes/pics/395a9e83-b1a1-4a1d-b170-d081e7bb5bab.png differ diff --git a/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg b/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg new file mode 100644 index 0000000000..79c5704ac0 Binary files /dev/null and b/notes/pics/396be981-3f2c-4fd9-8101-dbf9c841504b.jpg differ diff --git a/notes/pics/399b459d-db9e-4e77-b879-e6492c7d382b.gif b/notes/pics/399b459d-db9e-4e77-b879-e6492c7d382b.gif new file mode 100644 index 0000000000..66d52391ef Binary files /dev/null and b/notes/pics/399b459d-db9e-4e77-b879-e6492c7d382b.gif differ diff --git a/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png b/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png new file mode 100644 index 0000000000..1240d1f5e1 Binary files /dev/null and b/notes/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png differ diff --git a/notes/pics/3be42601-9d33-4d29-8358-a9d16453af93.png b/notes/pics/3be42601-9d33-4d29-8358-a9d16453af93.png new file mode 100644 index 0000000000..ca17dae9cf Binary files /dev/null and b/notes/pics/3be42601-9d33-4d29-8358-a9d16453af93.png differ diff --git a/docs/notes/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png b/notes/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png similarity index 100% rename from docs/notes/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png rename to notes/pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png diff --git a/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png b/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png new file mode 100644 index 0000000000..5d9504eebf Binary files /dev/null and b/notes/pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png differ diff --git a/notes/pics/3ea280b5-be7d-471b-ac76-ff020384357c.gif b/notes/pics/3ea280b5-be7d-471b-ac76-ff020384357c.gif new file mode 100644 index 0000000000..4c8540a968 Binary files /dev/null and b/notes/pics/3ea280b5-be7d-471b-ac76-ff020384357c.gif differ diff --git a/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png b/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png new file mode 100644 index 0000000000..fac9723209 Binary files /dev/null and b/notes/pics/3fb5b255-b791-45b6-8754-325c8741855a.png differ diff --git a/notes/pics/40121fa5-cb5c-4add-9945-e087220cd039_200.png b/notes/pics/40121fa5-cb5c-4add-9945-e087220cd039_200.png new file mode 100644 index 0000000000..91d510e703 Binary files /dev/null and b/notes/pics/40121fa5-cb5c-4add-9945-e087220cd039_200.png differ diff --git a/notes/pics/40c0c17e-bba6-4493-9857-147c0044a018.png b/notes/pics/40c0c17e-bba6-4493-9857-147c0044a018.png new file mode 100644 index 0000000000..9182292c9c Binary files /dev/null and b/notes/pics/40c0c17e-bba6-4493-9857-147c0044a018.png differ diff --git a/notes/pics/40c6570d-c1d7-4c38-843e-ba991b2328c2.png b/notes/pics/40c6570d-c1d7-4c38-843e-ba991b2328c2.png new file mode 100644 index 0000000000..7274bedd7f Binary files /dev/null and b/notes/pics/40c6570d-c1d7-4c38-843e-ba991b2328c2.png differ diff --git a/notes/pics/417bc315-4409-48c6-83e0-59e8d405429e.jpg b/notes/pics/417bc315-4409-48c6-83e0-59e8d405429e.jpg new file mode 100644 index 0000000000..38ab13b7b0 Binary files /dev/null and b/notes/pics/417bc315-4409-48c6-83e0-59e8d405429e.jpg differ diff --git a/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png b/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png new file mode 100644 index 0000000000..b366f810e7 Binary files /dev/null and b/notes/pics/423f2a40-bee1-488e-b460-8e76c48ee560.png differ diff --git a/notes/pics/4240a69f-4d51-4d16-b797-2dfe110f30bd.png b/notes/pics/4240a69f-4d51-4d16-b797-2dfe110f30bd.png new file mode 100644 index 0000000000..d889e516a2 Binary files /dev/null and b/notes/pics/4240a69f-4d51-4d16-b797-2dfe110f30bd.png differ diff --git a/notes/pics/42430e94-3137-48c0-bdb6-3cebaf9102e3.jpg b/notes/pics/42430e94-3137-48c0-bdb6-3cebaf9102e3.jpg new file mode 100644 index 0000000000..4a949dd846 Binary files /dev/null and b/notes/pics/42430e94-3137-48c0-bdb6-3cebaf9102e3.jpg differ diff --git a/notes/pics/424f34ab-a9fd-49a6-9969-d76b42251365.png b/notes/pics/424f34ab-a9fd-49a6-9969-d76b42251365.png new file mode 100644 index 0000000000..d5c1a58d87 Binary files /dev/null and b/notes/pics/424f34ab-a9fd-49a6-9969-d76b42251365.png differ diff --git a/notes/pics/42661013-750f-420b-b3c1-437e9a11fb65.png b/notes/pics/42661013-750f-420b-b3c1-437e9a11fb65.png new file mode 100644 index 0000000000..964f97f8e6 Binary files /dev/null and b/notes/pics/42661013-750f-420b-b3c1-437e9a11fb65.png differ diff --git a/notes/pics/437cb54c-5970-4ba9-b2ef-2541f7d6c81e.gif b/notes/pics/437cb54c-5970-4ba9-b2ef-2541f7d6c81e.gif new file mode 100644 index 0000000000..7a188a573c Binary files /dev/null and b/notes/pics/437cb54c-5970-4ba9-b2ef-2541f7d6c81e.gif differ diff --git a/notes/pics/43d323ac-9f07-4e4a-a315-4eaf8c38766c.jpg b/notes/pics/43d323ac-9f07-4e4a-a315-4eaf8c38766c.jpg new file mode 100644 index 0000000000..d550480266 Binary files /dev/null and b/notes/pics/43d323ac-9f07-4e4a-a315-4eaf8c38766c.jpg differ diff --git a/notes/pics/44d33643-1004-43a3-b99a-4d688a08d0a1.png b/notes/pics/44d33643-1004-43a3-b99a-4d688a08d0a1.png new file mode 100644 index 0000000000..fc1800891f Binary files /dev/null and b/notes/pics/44d33643-1004-43a3-b99a-4d688a08d0a1.png differ diff --git a/notes/pics/45016e98-6879-4709-8569-262b2d6d60b9.png b/notes/pics/45016e98-6879-4709-8569-262b2d6d60b9.png new file mode 100644 index 0000000000..57da978672 Binary files /dev/null and b/notes/pics/45016e98-6879-4709-8569-262b2d6d60b9.png differ diff --git a/docs/notes/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg b/notes/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg similarity index 100% rename from docs/notes/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg rename to notes/pics/45be9587-6069-4ab7-b9ac-840db1a53744.jpg diff --git a/notes/pics/476329d4-e2ef-4f7b-8ac9-a52a6f784600.png b/notes/pics/476329d4-e2ef-4f7b-8ac9-a52a6f784600.png new file mode 100644 index 0000000000..a53d1307cd Binary files /dev/null and b/notes/pics/476329d4-e2ef-4f7b-8ac9-a52a6f784600.png differ diff --git a/docs/notes/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg b/notes/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg similarity index 100% rename from docs/notes/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg rename to notes/pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg diff --git a/notes/pics/48517227-324c-4664-bd26-a2d2cffe2bfe.png b/notes/pics/48517227-324c-4664-bd26-a2d2cffe2bfe.png new file mode 100644 index 0000000000..ca11ce320f Binary files /dev/null and b/notes/pics/48517227-324c-4664-bd26-a2d2cffe2bfe.png differ diff --git a/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png b/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png new file mode 100644 index 0000000000..7f8424581a Binary files /dev/null and b/notes/pics/485fdf34-ccf8-4185-97c6-17374ee719a0.png differ diff --git a/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png b/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png new file mode 100644 index 0000000000..d9ef579ab4 Binary files /dev/null and b/notes/pics/48a934ff-a29b-434c-8e1d-8c8ec20cb91d.png differ diff --git a/docs/notes/pics/48b1d459-8832-4e92-938a-728aae730739.jpg b/notes/pics/48b1d459-8832-4e92-938a-728aae730739.jpg similarity index 100% rename from docs/notes/pics/48b1d459-8832-4e92-938a-728aae730739.jpg rename to notes/pics/48b1d459-8832-4e92-938a-728aae730739.jpg diff --git a/notes/pics/48d79be8-085b-4862-8a9d-18402eb93b31.png b/notes/pics/48d79be8-085b-4862-8a9d-18402eb93b31.png new file mode 100644 index 0000000000..9b585629d4 Binary files /dev/null and b/notes/pics/48d79be8-085b-4862-8a9d-18402eb93b31.png differ diff --git a/notes/pics/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif b/notes/pics/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif new file mode 100644 index 0000000000..e2af5f10d4 Binary files /dev/null and b/notes/pics/49d2adc1-b28a-44bf-babb-d44993f4a2e3.gif differ diff --git a/notes/pics/4aa5e057-bc57-4719-ab57-c6fbc861c505.png b/notes/pics/4aa5e057-bc57-4719-ab57-c6fbc861c505.png new file mode 100644 index 0000000000..cf18debb67 Binary files /dev/null and b/notes/pics/4aa5e057-bc57-4719-ab57-c6fbc861c505.png differ diff --git a/notes/pics/4ab87717-e264-4232-825d-8aaf08f14e8b.jpg b/notes/pics/4ab87717-e264-4232-825d-8aaf08f14e8b.jpg new file mode 100644 index 0000000000..b5333a2bc4 Binary files /dev/null and b/notes/pics/4ab87717-e264-4232-825d-8aaf08f14e8b.jpg differ diff --git a/notes/pics/4b2ae78c-e254-44df-9e37-578e2f2bef52.jpg b/notes/pics/4b2ae78c-e254-44df-9e37-578e2f2bef52.jpg new file mode 100644 index 0000000000..3669e15bdf Binary files /dev/null and b/notes/pics/4b2ae78c-e254-44df-9e37-578e2f2bef52.jpg differ diff --git a/notes/pics/4bf5e3fb-a285-4138-b3b6-780956eb1df1.gif b/notes/pics/4bf5e3fb-a285-4138-b3b6-780956eb1df1.gif new file mode 100644 index 0000000000..e2b8d75ea8 Binary files /dev/null and b/notes/pics/4bf5e3fb-a285-4138-b3b6-780956eb1df1.gif differ diff --git a/notes/pics/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png b/notes/pics/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png new file mode 100644 index 0000000000..4b485ecdc4 Binary files /dev/null and b/notes/pics/4bf8d0ba-36f0-459e-83a0-f15278a5a157.png differ diff --git a/notes/pics/4c457532-550b-4eca-b881-037b84b4934b.jpg b/notes/pics/4c457532-550b-4eca-b881-037b84b4934b.jpg new file mode 100644 index 0000000000..0c03ab09c7 Binary files /dev/null and b/notes/pics/4c457532-550b-4eca-b881-037b84b4934b.jpg differ diff --git a/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png b/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png new file mode 100644 index 0000000000..03d1e44750 Binary files /dev/null and b/notes/pics/4cf711a8-7ab2-4152-b85c-d5c226733807.png differ diff --git a/docs/notes/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png b/notes/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png similarity index 100% rename from docs/notes/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png rename to notes/pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png diff --git a/notes/pics/4e93f7d4-2623-4129-a939-59051256561e.png b/notes/pics/4e93f7d4-2623-4129-a939-59051256561e.png new file mode 100644 index 0000000000..b48fb693d1 Binary files /dev/null and b/notes/pics/4e93f7d4-2623-4129-a939-59051256561e.png differ diff --git a/notes/pics/50678f34-694f-45a4-91c6-34d985c83fee.png b/notes/pics/50678f34-694f-45a4-91c6-34d985c83fee.png new file mode 100644 index 0000000000..3af4acb0b4 Binary files /dev/null and b/notes/pics/50678f34-694f-45a4-91c6-34d985c83fee.png differ diff --git a/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg b/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg new file mode 100644 index 0000000000..8bd241bcc1 Binary files /dev/null and b/notes/pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg differ diff --git a/notes/pics/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg b/notes/pics/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg new file mode 100644 index 0000000000..f7f3ca5b64 Binary files /dev/null and b/notes/pics/508c6e52-9f93-44ed-b6b9-e69050e14807.jpg differ diff --git a/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg b/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg new file mode 100644 index 0000000000..4627281d2c Binary files /dev/null and b/notes/pics/51e2ed95-65b8-4ae9-8af3-65602d452a25.jpg differ diff --git a/notes/pics/52a7744f-5bce-4ff3-a6f0-8449334d9f3d.png b/notes/pics/52a7744f-5bce-4ff3-a6f0-8449334d9f3d.png new file mode 100644 index 0000000000..5770e1beae Binary files /dev/null and b/notes/pics/52a7744f-5bce-4ff3-a6f0-8449334d9f3d.png differ diff --git a/notes/pics/530764d5-f67f-47a2-8df4-57e8646e1400.png b/notes/pics/530764d5-f67f-47a2-8df4-57e8646e1400.png new file mode 100644 index 0000000000..656e6501a8 Binary files /dev/null and b/notes/pics/530764d5-f67f-47a2-8df4-57e8646e1400.png differ diff --git a/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg b/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg new file mode 100644 index 0000000000..d0f876995a Binary files /dev/null and b/notes/pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg differ diff --git a/docs/notes/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png b/notes/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png similarity index 100% rename from docs/notes/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png rename to notes/pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png diff --git a/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png b/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png new file mode 100644 index 0000000000..d5194f554c Binary files /dev/null and b/notes/pics/55dc4e84-573d-4c13-a765-52ed1dd251f9.png differ diff --git a/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png b/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png new file mode 100644 index 0000000000..4fa7c1fd54 Binary files /dev/null and b/notes/pics/562f2844-d77c-40e0-887a-28a7128abd42.png differ diff --git a/notes/pics/5778d113-8e13-4c53-b5bf-801e58080b97.png b/notes/pics/5778d113-8e13-4c53-b5bf-801e58080b97.png new file mode 100644 index 0000000000..b0f94a993a Binary files /dev/null and b/notes/pics/5778d113-8e13-4c53-b5bf-801e58080b97.png differ diff --git a/notes/pics/58b70113-3876-49af-85a9-68eb00a72d59.jpg b/notes/pics/58b70113-3876-49af-85a9-68eb00a72d59.jpg new file mode 100644 index 0000000000..39713d40c5 Binary files /dev/null and b/notes/pics/58b70113-3876-49af-85a9-68eb00a72d59.jpg differ diff --git a/notes/pics/58c8e370-3bec-4c2b-bf17-c8d34345dd17.gif b/notes/pics/58c8e370-3bec-4c2b-bf17-c8d34345dd17.gif new file mode 100644 index 0000000000..b5f9f79331 Binary files /dev/null and b/notes/pics/58c8e370-3bec-4c2b-bf17-c8d34345dd17.gif differ diff --git a/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg b/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg new file mode 100644 index 0000000000..8f4dd78a32 Binary files /dev/null and b/notes/pics/5942debd-fc00-477a-b390-7c5692cc8070.jpg differ diff --git a/notes/pics/5b718e86-7102-4bb6-8ca5-d1dd791530c5.png b/notes/pics/5b718e86-7102-4bb6-8ca5-d1dd791530c5.png new file mode 100644 index 0000000000..6a6112577a Binary files /dev/null and b/notes/pics/5b718e86-7102-4bb6-8ca5-d1dd791530c5.png differ diff --git a/notes/pics/5bb1b38a-527e-4802-a385-267dadbd30ba.png b/notes/pics/5bb1b38a-527e-4802-a385-267dadbd30ba.png new file mode 100644 index 0000000000..fb1a182c49 Binary files /dev/null and b/notes/pics/5bb1b38a-527e-4802-a385-267dadbd30ba.png differ diff --git a/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg b/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg new file mode 100644 index 0000000000..25fbc7e6a9 Binary files /dev/null and b/notes/pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg differ diff --git a/notes/pics/5ea609cb-8ad4-4c4c-aee6-45a40a81794a.jpg b/notes/pics/5ea609cb-8ad4-4c4c-aee6-45a40a81794a.jpg new file mode 100644 index 0000000000..331d33dfd1 Binary files /dev/null and b/notes/pics/5ea609cb-8ad4-4c4c-aee6-45a40a81794a.jpg differ diff --git a/notes/pics/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png b/notes/pics/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png new file mode 100644 index 0000000000..a3fdc2288f Binary files /dev/null and b/notes/pics/5f1cb999-cb9a-4f6c-a0af-d90377295ab8.png differ diff --git a/notes/pics/5f5c22d5-9c0e-49e1-b5b0-6cc7032724d4.png b/notes/pics/5f5c22d5-9c0e-49e1-b5b0-6cc7032724d4.png new file mode 100644 index 0000000000..72722db4e4 Binary files /dev/null and b/notes/pics/5f5c22d5-9c0e-49e1-b5b0-6cc7032724d4.png differ diff --git a/docs/notes/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png b/notes/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png similarity index 100% rename from docs/notes/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png rename to notes/pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png diff --git a/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png b/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png new file mode 100644 index 0000000000..3842580141 Binary files /dev/null and b/notes/pics/600e9c75-5033-4dad-ae2b-930957db638e.png differ diff --git a/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png b/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png new file mode 100644 index 0000000000..79388c091e Binary files /dev/null and b/notes/pics/6019b2db-bc3e-4408-b6d8-96025f4481d6.png differ diff --git a/notes/pics/603dbb49-dac5-4825-9694-5f1d65cefd44.png b/notes/pics/603dbb49-dac5-4825-9694-5f1d65cefd44.png new file mode 100644 index 0000000000..791efc3d63 Binary files /dev/null and b/notes/pics/603dbb49-dac5-4825-9694-5f1d65cefd44.png differ diff --git a/notes/pics/60c4a44c-7829-4242-b3a1-26c3b513aaf0.gif b/notes/pics/60c4a44c-7829-4242-b3a1-26c3b513aaf0.gif new file mode 100644 index 0000000000..f53a4f8590 Binary files /dev/null and b/notes/pics/60c4a44c-7829-4242-b3a1-26c3b513aaf0.gif differ diff --git a/notes/pics/6234eb3d-ccf2-4987-a724-235aef6957b1.png b/notes/pics/6234eb3d-ccf2-4987-a724-235aef6957b1.png new file mode 100644 index 0000000000..f259d26ea7 Binary files /dev/null and b/notes/pics/6234eb3d-ccf2-4987-a724-235aef6957b1.png differ diff --git a/notes/pics/6270c216-7ec0-4db7-94de-0003bce37cd2.png b/notes/pics/6270c216-7ec0-4db7-94de-0003bce37cd2.png new file mode 100644 index 0000000000..eaa364e89d Binary files /dev/null and b/notes/pics/6270c216-7ec0-4db7-94de-0003bce37cd2.png differ diff --git a/notes/pics/6283be2a-814a-4a10-84bf-9592533fe6bc.png b/notes/pics/6283be2a-814a-4a10-84bf-9592533fe6bc.png new file mode 100644 index 0000000000..07b89658d1 Binary files /dev/null and b/notes/pics/6283be2a-814a-4a10-84bf-9592533fe6bc.png differ diff --git a/docs/notes/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png b/notes/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png similarity index 100% rename from docs/notes/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png rename to notes/pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png diff --git a/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg b/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg new file mode 100644 index 0000000000..2c6a953ea9 Binary files /dev/null and b/notes/pics/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg differ diff --git a/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg b/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg new file mode 100644 index 0000000000..92d9f59b64 Binary files /dev/null and b/notes/pics/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg differ diff --git a/notes/pics/63c8ffea-a9f2-4ebe-97d1-d71be71246f9.jpg b/notes/pics/63c8ffea-a9f2-4ebe-97d1-d71be71246f9.jpg new file mode 100644 index 0000000000..7aa6788c0d Binary files /dev/null and b/notes/pics/63c8ffea-a9f2-4ebe-97d1-d71be71246f9.jpg differ diff --git a/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49-1572675454173.gif b/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49-1572675454173.gif new file mode 100644 index 0000000000..e9cd7d56b3 Binary files /dev/null and b/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49-1572675454173.gif differ diff --git a/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49.gif b/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49.gif new file mode 100644 index 0000000000..e9cd7d56b3 Binary files /dev/null and b/notes/pics/643b6f18-f933-4ac5-aa7a-e304dbd7fe49.gif differ diff --git a/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png b/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png new file mode 100644 index 0000000000..f1181b86fe Binary files /dev/null and b/notes/pics/658fc5e7-79c0-4247-9445-d69bf194c539.png differ diff --git a/docs/notes/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg b/notes/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg similarity index 100% rename from docs/notes/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg rename to notes/pics/66192382-558b-4b05-a35d-ac4a2b1a9811.jpg diff --git a/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg b/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg new file mode 100644 index 0000000000..be5ad35738 Binary files /dev/null and b/notes/pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg differ diff --git a/notes/pics/66a01953-5303-43b1-8646-0c77b825e980.png b/notes/pics/66a01953-5303-43b1-8646-0c77b825e980.png new file mode 100644 index 0000000000..48a120be9b Binary files /dev/null and b/notes/pics/66a01953-5303-43b1-8646-0c77b825e980.png differ diff --git a/notes/pics/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png b/notes/pics/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png new file mode 100644 index 0000000000..a8536c9428 Binary files /dev/null and b/notes/pics/66a6899d-c6b0-4a47-8569-9d08f0baf86c.png differ diff --git a/notes/pics/66ae164c-ad47-4905-895e-51fe38ce797a.png b/notes/pics/66ae164c-ad47-4905-895e-51fe38ce797a.png new file mode 100644 index 0000000000..17210ca81b Binary files /dev/null and b/notes/pics/66ae164c-ad47-4905-895e-51fe38ce797a.png differ diff --git a/notes/pics/66d00642-ce37-466c-8f7a-143d0bf84cd6.png b/notes/pics/66d00642-ce37-466c-8f7a-143d0bf84cd6.png new file mode 100644 index 0000000000..be928f7004 Binary files /dev/null and b/notes/pics/66d00642-ce37-466c-8f7a-143d0bf84cd6.png differ diff --git a/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg b/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg new file mode 100644 index 0000000000..0020b3d757 Binary files /dev/null and b/notes/pics/6729baa0-57d7-4817-b3aa-518cbccf824c.jpg differ diff --git a/notes/pics/67582ade-d44a-46a6-8757-3c1296cc1ef9.png b/notes/pics/67582ade-d44a-46a6-8757-3c1296cc1ef9.png new file mode 100644 index 0000000000..2d42dcba92 Binary files /dev/null and b/notes/pics/67582ade-d44a-46a6-8757-3c1296cc1ef9.png differ diff --git a/notes/pics/6782674c-1bfe-4879-af39-e9d722a95d39.png b/notes/pics/6782674c-1bfe-4879-af39-e9d722a95d39.png new file mode 100644 index 0000000000..ca2b35e87e Binary files /dev/null and b/notes/pics/6782674c-1bfe-4879-af39-e9d722a95d39.png differ diff --git a/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png b/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png new file mode 100644 index 0000000000..5de38f6949 Binary files /dev/null and b/notes/pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png differ diff --git a/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg b/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg new file mode 100644 index 0000000000..324b7fec25 Binary files /dev/null and b/notes/pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg differ diff --git a/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png b/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png new file mode 100644 index 0000000000..59f47a8cdc Binary files /dev/null and b/notes/pics/691f11eb-31a7-46be-9de1-61f433c4b3c7.png differ diff --git a/notes/pics/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif b/notes/pics/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif new file mode 100644 index 0000000000..05704a44dc Binary files /dev/null and b/notes/pics/6980aef0-debe-4b4b-8da5-8b1befbc1408.gif differ diff --git a/notes/pics/69d6c38d-1dec-4f72-ae60-60dbc10e9d15.png b/notes/pics/69d6c38d-1dec-4f72-ae60-60dbc10e9d15.png new file mode 100644 index 0000000000..84611618d6 Binary files /dev/null and b/notes/pics/69d6c38d-1dec-4f72-ae60-60dbc10e9d15.png differ diff --git a/notes/pics/6a88a398-c494-41f5-bb62-9f7fb811df7c.jpg b/notes/pics/6a88a398-c494-41f5-bb62-9f7fb811df7c.jpg new file mode 100644 index 0000000000..1b1c0bb3db Binary files /dev/null and b/notes/pics/6a88a398-c494-41f5-bb62-9f7fb811df7c.jpg differ diff --git a/notes/pics/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png b/notes/pics/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png new file mode 100644 index 0000000000..b613aa38a9 Binary files /dev/null and b/notes/pics/6b504f1f-bf76-4aab-a146-a9c7a58c2029.png differ diff --git a/notes/pics/6b833bc2-517a-4270-8a5e-0a5f6df8cd96.png b/notes/pics/6b833bc2-517a-4270-8a5e-0a5f6df8cd96.png new file mode 100644 index 0000000000..05faa726a8 Binary files /dev/null and b/notes/pics/6b833bc2-517a-4270-8a5e-0a5f6df8cd96.png differ diff --git a/notes/pics/6cfdf7b0-ea9d-4371-893f-76e78f004223.png b/notes/pics/6cfdf7b0-ea9d-4371-893f-76e78f004223.png new file mode 100644 index 0000000000..adac7ef4dc Binary files /dev/null and b/notes/pics/6cfdf7b0-ea9d-4371-893f-76e78f004223.png differ diff --git a/notes/pics/6fb7cf89-71b3-48c1-bc6a-5110e1f1fc15.gif b/notes/pics/6fb7cf89-71b3-48c1-bc6a-5110e1f1fc15.gif new file mode 100644 index 0000000000..b9f2cbdc73 Binary files /dev/null and b/notes/pics/6fb7cf89-71b3-48c1-bc6a-5110e1f1fc15.gif differ diff --git a/notes/pics/6fda1dc7-5c74-49c1-bb79-237a77e43a43.png b/notes/pics/6fda1dc7-5c74-49c1-bb79-237a77e43a43.png new file mode 100644 index 0000000000..dec1bde84f Binary files /dev/null and b/notes/pics/6fda1dc7-5c74-49c1-bb79-237a77e43a43.png differ diff --git a/notes/pics/7002c01b-1ed5-475a-9e5f-5fc8a4cdbcc0.jpg b/notes/pics/7002c01b-1ed5-475a-9e5f-5fc8a4cdbcc0.jpg new file mode 100644 index 0000000000..4b894134cf Binary files /dev/null and b/notes/pics/7002c01b-1ed5-475a-9e5f-5fc8a4cdbcc0.jpg differ diff --git a/notes/pics/7008dc2b-6f13-4174-a516-28b2d75b0152.gif b/notes/pics/7008dc2b-6f13-4174-a516-28b2d75b0152.gif new file mode 100644 index 0000000000..829f20d91a Binary files /dev/null and b/notes/pics/7008dc2b-6f13-4174-a516-28b2d75b0152.gif differ diff --git a/notes/pics/71550414107576.gif b/notes/pics/71550414107576.gif new file mode 100644 index 0000000000..874dc8e368 Binary files /dev/null and b/notes/pics/71550414107576.gif differ diff --git a/notes/pics/71b97a50-a49f-4f1a-81d1-48c3364d61b3.png b/notes/pics/71b97a50-a49f-4f1a-81d1-48c3364d61b3.png new file mode 100644 index 0000000000..5cd9214582 Binary files /dev/null and b/notes/pics/71b97a50-a49f-4f1a-81d1-48c3364d61b3.png differ diff --git a/notes/pics/72a01242-e6b4-46c5-a285-24e754d63093.png b/notes/pics/72a01242-e6b4-46c5-a285-24e754d63093.png new file mode 100644 index 0000000000..362910acdd Binary files /dev/null and b/notes/pics/72a01242-e6b4-46c5-a285-24e754d63093.png differ diff --git a/notes/pics/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png b/notes/pics/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png new file mode 100644 index 0000000000..125a804c75 Binary files /dev/null and b/notes/pics/72aac98a-d5df-4bfa-a71a-4bb16a87474c.png differ diff --git a/notes/pics/72be01cd-41ae-45f7-99b9-a8d284e44dd4.png b/notes/pics/72be01cd-41ae-45f7-99b9-a8d284e44dd4.png new file mode 100644 index 0000000000..ad0ae80d05 Binary files /dev/null and b/notes/pics/72be01cd-41ae-45f7-99b9-a8d284e44dd4.png differ diff --git a/notes/pics/72ee7e9a-194d-42e9-b4d7-29c23417ca18.png b/notes/pics/72ee7e9a-194d-42e9-b4d7-29c23417ca18.png new file mode 100644 index 0000000000..2ad89c0ada Binary files /dev/null and b/notes/pics/72ee7e9a-194d-42e9-b4d7-29c23417ca18.png differ diff --git a/notes/pics/73403d84-d921-49f1-93a9-d8fe050f3497.png b/notes/pics/73403d84-d921-49f1-93a9-d8fe050f3497.png new file mode 100644 index 0000000000..09e4a7516f Binary files /dev/null and b/notes/pics/73403d84-d921-49f1-93a9-d8fe050f3497.png differ diff --git a/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png b/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png new file mode 100644 index 0000000000..dae4c8f48c Binary files /dev/null and b/notes/pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png differ diff --git a/notes/pics/75184b58-c627-4edc-8dcf-605762ebb733.png b/notes/pics/75184b58-c627-4edc-8dcf-605762ebb733.png new file mode 100644 index 0000000000..ef2e74117f Binary files /dev/null and b/notes/pics/75184b58-c627-4edc-8dcf-605762ebb733.png differ diff --git a/notes/pics/759013d7-61d8-4509-897a-d75af598a236.png b/notes/pics/759013d7-61d8-4509-897a-d75af598a236.png new file mode 100644 index 0000000000..7befb90847 Binary files /dev/null and b/notes/pics/759013d7-61d8-4509-897a-d75af598a236.png differ diff --git a/notes/pics/76a49594323247f21c9b3a69945445ee.png b/notes/pics/76a49594323247f21c9b3a69945445ee.png new file mode 100644 index 0000000000..00300dc34c Binary files /dev/null and b/notes/pics/76a49594323247f21c9b3a69945445ee.png differ diff --git a/docs/notes/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg b/notes/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg similarity index 100% rename from docs/notes/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg rename to notes/pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg diff --git a/notes/pics/774d756b-902a-41a3-a3fd-81ca3ef688dc.png b/notes/pics/774d756b-902a-41a3-a3fd-81ca3ef688dc.png new file mode 100644 index 0000000000..0ef06be5de Binary files /dev/null and b/notes/pics/774d756b-902a-41a3-a3fd-81ca3ef688dc.png differ diff --git a/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png b/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png new file mode 100644 index 0000000000..8fb36a37c9 Binary files /dev/null and b/notes/pics/77931a4b-72ba-4016-827d-84b9a6845a51.png differ diff --git a/notes/pics/7818c574-97a8-48db-8e62-8bfb030b02ba.png b/notes/pics/7818c574-97a8-48db-8e62-8bfb030b02ba.png new file mode 100644 index 0000000000..46c887bf21 Binary files /dev/null and b/notes/pics/7818c574-97a8-48db-8e62-8bfb030b02ba.png differ diff --git a/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png b/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png new file mode 100644 index 0000000000..b6e54e2679 Binary files /dev/null and b/notes/pics/794239e3-4baf-4aad-92df-f02f59b2a6fe.png differ diff --git a/notes/pics/79c6f036-bde6-4393-85a3-ef36a0327bd2.png b/notes/pics/79c6f036-bde6-4393-85a3-ef36a0327bd2.png new file mode 100644 index 0000000000..cd07255596 Binary files /dev/null and b/notes/pics/79c6f036-bde6-4393-85a3-ef36a0327bd2.png differ diff --git a/notes/pics/79df886f-fdc3-4020-a07f-c991bb58e0d8.png b/notes/pics/79df886f-fdc3-4020-a07f-c991bb58e0d8.png new file mode 100644 index 0000000000..2c8b681ba0 Binary files /dev/null and b/notes/pics/79df886f-fdc3-4020-a07f-c991bb58e0d8.png differ diff --git a/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg b/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg new file mode 100644 index 0000000000..bc671f240b Binary files /dev/null and b/notes/pics/7a29acce-f243-4914-9f00-f2988c528412.jpg differ diff --git a/notes/pics/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png b/notes/pics/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png new file mode 100644 index 0000000000..6ddfe7ba47 Binary files /dev/null and b/notes/pics/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png differ diff --git a/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg b/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg new file mode 100644 index 0000000000..23f8217827 Binary files /dev/null and b/notes/pics/7a3c6a30-c735-4edb-8115-337288a4f0f2.jpg differ diff --git a/notes/pics/7a85e285-e152-4116-b6dc-3fab27ba9437.jpg b/notes/pics/7a85e285-e152-4116-b6dc-3fab27ba9437.jpg new file mode 100644 index 0000000000..6ad6d641b8 Binary files /dev/null and b/notes/pics/7a85e285-e152-4116-b6dc-3fab27ba9437.jpg differ diff --git a/docs/notes/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png b/notes/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png similarity index 100% rename from docs/notes/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png rename to notes/pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png diff --git a/docs/notes/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png b/notes/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png similarity index 100% rename from docs/notes/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png rename to notes/pics/7b3efa99-d306-4982-8cfb-e7153c33aab4.png diff --git a/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png b/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png new file mode 100644 index 0000000000..d325c907d4 Binary files /dev/null and b/notes/pics/7bd202a7-93d4-4f3a-a878-af68ae25539a.png differ diff --git a/notes/pics/7c349b91-050b-4d72-a7f8-ec86320307ea.png b/notes/pics/7c349b91-050b-4d72-a7f8-ec86320307ea.png new file mode 100644 index 0000000000..d2f9705eb1 Binary files /dev/null and b/notes/pics/7c349b91-050b-4d72-a7f8-ec86320307ea.png differ diff --git a/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png b/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png new file mode 100644 index 0000000000..5f493376f2 Binary files /dev/null and b/notes/pics/7c54de21-e2ff-402e-bc42-4037de1c1592.png differ diff --git a/notes/pics/7d97dde0-0695-4707-bb68-e6c13a2e1b45.png b/notes/pics/7d97dde0-0695-4707-bb68-e6c13a2e1b45.png new file mode 100644 index 0000000000..d1ac27c747 Binary files /dev/null and b/notes/pics/7d97dde0-0695-4707-bb68-e6c13a2e1b45.png differ diff --git a/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg b/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg new file mode 100644 index 0000000000..5232e7dd1f Binary files /dev/null and b/notes/pics/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg differ diff --git a/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png b/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png new file mode 100644 index 0000000000..d853302e4a Binary files /dev/null and b/notes/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png differ diff --git a/docs/notes/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png b/notes/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png similarity index 100% rename from docs/notes/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png rename to notes/pics/8006a450-6c2f-498c-a928-c927f758b1d0.png diff --git a/notes/pics/807f4258-dba8-4c54-9c3c-a707c7ccffa2.jpg b/notes/pics/807f4258-dba8-4c54-9c3c-a707c7ccffa2.jpg new file mode 100644 index 0000000000..047f619d52 Binary files /dev/null and b/notes/pics/807f4258-dba8-4c54-9c3c-a707c7ccffa2.jpg differ diff --git a/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png b/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png new file mode 100644 index 0000000000..7e3234c4c5 Binary files /dev/null and b/notes/pics/80804f52-8815-4096-b506-48eef3eed5c6.png differ diff --git a/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg b/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg new file mode 100644 index 0000000000..ecf6a7d001 Binary files /dev/null and b/notes/pics/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg differ diff --git a/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg b/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg new file mode 100644 index 0000000000..68829f1e8c Binary files /dev/null and b/notes/pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg differ diff --git a/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg b/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg new file mode 100644 index 0000000000..48a52864a9 Binary files /dev/null and b/notes/pics/83185315-793a-453a-a927-5e8d92b5c0ef.jpg differ diff --git a/notes/pics/83acbb02-872a-4178-b22a-c89c3cb60263.jpg b/notes/pics/83acbb02-872a-4178-b22a-c89c3cb60263.jpg new file mode 100644 index 0000000000..af346a0fe3 Binary files /dev/null and b/notes/pics/83acbb02-872a-4178-b22a-c89c3cb60263.jpg differ diff --git a/notes/pics/83d466bd-946b-4430-854a-cf7b0696d4c8.jpg b/notes/pics/83d466bd-946b-4430-854a-cf7b0696d4c8.jpg new file mode 100644 index 0000000000..ece6156c21 Binary files /dev/null and b/notes/pics/83d466bd-946b-4430-854a-cf7b0696d4c8.jpg differ diff --git a/notes/pics/83d909d2-3858-4fe1-8ff4-16471db0b180.png b/notes/pics/83d909d2-3858-4fe1-8ff4-16471db0b180.png new file mode 100644 index 0000000000..5904529bcd Binary files /dev/null and b/notes/pics/83d909d2-3858-4fe1-8ff4-16471db0b180.png differ diff --git a/notes/pics/8441b2c4-dca7-4d6b-8efb-f22efccaf331.png b/notes/pics/8441b2c4-dca7-4d6b-8efb-f22efccaf331.png new file mode 100644 index 0000000000..376c348cd8 Binary files /dev/null and b/notes/pics/8441b2c4-dca7-4d6b-8efb-f22efccaf331.png differ diff --git a/notes/pics/8442519f-0b4d-48f4-8229-56f984363c69.png b/notes/pics/8442519f-0b4d-48f4-8229-56f984363c69.png new file mode 100644 index 0000000000..b8290f0bde Binary files /dev/null and b/notes/pics/8442519f-0b4d-48f4-8229-56f984363c69.png differ diff --git a/notes/pics/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg b/notes/pics/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg new file mode 100644 index 0000000000..22bf89a321 Binary files /dev/null and b/notes/pics/84a5b15a-86c5-4d8e-9439-d9fd5a4699a1.jpg differ diff --git a/docs/notes/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg b/notes/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg similarity index 100% rename from docs/notes/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg rename to notes/pics/85c05fb1-5546-4c50-9221-21f231cdc8c5.jpg diff --git a/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png b/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png new file mode 100644 index 0000000000..02ead80d3a Binary files /dev/null and b/notes/pics/8668a3e1-c9c7-4fcb-98b2-a96a5d841579.png differ diff --git a/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png b/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png new file mode 100644 index 0000000000..4b19db6d84 Binary files /dev/null and b/notes/pics/867e93eb-3161-4f39-b2d2-c0cd3788e194.png differ diff --git a/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png b/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png new file mode 100644 index 0000000000..d2f88403a2 Binary files /dev/null and b/notes/pics/86e6a91d-a285-447a-9345-c5484b8d0c47.png differ diff --git a/notes/pics/874b3ff7-7c5c-4e7a-b8ab-a82a3e038d20.png b/notes/pics/874b3ff7-7c5c-4e7a-b8ab-a82a3e038d20.png new file mode 100644 index 0000000000..f5f8f0f3a9 Binary files /dev/null and b/notes/pics/874b3ff7-7c5c-4e7a-b8ab-a82a3e038d20.png differ diff --git a/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png b/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png new file mode 100644 index 0000000000..1f9f077cf7 Binary files /dev/null and b/notes/pics/879814ee-48b5-4bcb-86f5-dcc400cb81ad.png differ diff --git a/notes/pics/89292ae1-5f13-44dc-b508-3f035e80bf89.png b/notes/pics/89292ae1-5f13-44dc-b508-3f035e80bf89.png new file mode 100644 index 0000000000..bb186222df Binary files /dev/null and b/notes/pics/89292ae1-5f13-44dc-b508-3f035e80bf89.png differ diff --git a/notes/pics/8996a537-7c4a-4ec8-a3b7-7ef1798eae26.png b/notes/pics/8996a537-7c4a-4ec8-a3b7-7ef1798eae26.png new file mode 100644 index 0000000000..25b7a7ae8f Binary files /dev/null and b/notes/pics/8996a537-7c4a-4ec8-a3b7-7ef1798eae26.png differ diff --git a/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg b/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg new file mode 100644 index 0000000000..8d52d76646 Binary files /dev/null and b/notes/pics/8b798007-e0fb-420c-b981-ead215692417.jpg differ diff --git a/notes/pics/8b7ebbad-9604-4375-84e3-f412099d170c.png b/notes/pics/8b7ebbad-9604-4375-84e3-f412099d170c.png new file mode 100644 index 0000000000..6b3dfeb494 Binary files /dev/null and b/notes/pics/8b7ebbad-9604-4375-84e3-f412099d170c.png differ diff --git a/docs/notes/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png b/notes/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png similarity index 100% rename from docs/notes/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png rename to notes/pics/8c662999-c16c-481c-9f40-1fdba5bc9167.png diff --git a/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png b/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png new file mode 100644 index 0000000000..f701076c21 Binary files /dev/null and b/notes/pics/8cb2be66-3d47-41ba-b55b-319fc68940d4.png differ diff --git a/notes/pics/8d779ab7-ffcc-47c6-90ec-ede8260b2368.png b/notes/pics/8d779ab7-ffcc-47c6-90ec-ede8260b2368.png new file mode 100644 index 0000000000..20f171c868 Binary files /dev/null and b/notes/pics/8d779ab7-ffcc-47c6-90ec-ede8260b2368.png differ diff --git a/notes/pics/8edc5164-810b-4cc5-bda8-2a2c98556377.jpg b/notes/pics/8edc5164-810b-4cc5-bda8-2a2c98556377.jpg new file mode 100644 index 0000000000..924bacf284 Binary files /dev/null and b/notes/pics/8edc5164-810b-4cc5-bda8-2a2c98556377.jpg differ diff --git a/notes/pics/8fdc577d-552d-4b43-b5e4-a8f98bc2cb51.png b/notes/pics/8fdc577d-552d-4b43-b5e4-a8f98bc2cb51.png new file mode 100644 index 0000000000..3182c7549b Binary files /dev/null and b/notes/pics/8fdc577d-552d-4b43-b5e4-a8f98bc2cb51.png differ diff --git a/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png b/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png new file mode 100644 index 0000000000..2f5520347e Binary files /dev/null and b/notes/pics/910f613f-514f-4534-87dd-9b4699d59d31.png differ diff --git a/notes/pics/9112288f-23f5-4e53-b222-a46fdbca1603.png b/notes/pics/9112288f-23f5-4e53-b222-a46fdbca1603.png new file mode 100644 index 0000000000..457f9860d7 Binary files /dev/null and b/notes/pics/9112288f-23f5-4e53-b222-a46fdbca1603.png differ diff --git a/notes/pics/914894c2-0bc4-46b5-bef9-0316a69ef521.jpg b/notes/pics/914894c2-0bc4-46b5-bef9-0316a69ef521.jpg new file mode 100644 index 0000000000..6993cf9e90 Binary files /dev/null and b/notes/pics/914894c2-0bc4-46b5-bef9-0316a69ef521.jpg differ diff --git a/notes/pics/91550414131331.gif b/notes/pics/91550414131331.gif new file mode 100644 index 0000000000..2626fdf8d6 Binary files /dev/null and b/notes/pics/91550414131331.gif differ diff --git a/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg b/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg new file mode 100644 index 0000000000..900a0e192d Binary files /dev/null and b/notes/pics/924914c0-660c-4e4a-bbc0-1df1146e7516.jpg differ diff --git a/notes/pics/93fb1d38-83f9-464a-a733-67b2e6bfddda.png b/notes/pics/93fb1d38-83f9-464a-a733-67b2e6bfddda.png new file mode 100644 index 0000000000..c971b0e0cf Binary files /dev/null and b/notes/pics/93fb1d38-83f9-464a-a733-67b2e6bfddda.png differ diff --git a/notes/pics/9420a703-1f9d-42ce-808e-bcb82b56483d.png b/notes/pics/9420a703-1f9d-42ce-808e-bcb82b56483d.png new file mode 100644 index 0000000000..4141618f9b Binary files /dev/null and b/notes/pics/9420a703-1f9d-42ce-808e-bcb82b56483d.png differ diff --git a/notes/pics/942ca0d2-9d5c-45a4-89cb-5fd89b61913f.png b/notes/pics/942ca0d2-9d5c-45a4-89cb-5fd89b61913f.png new file mode 100644 index 0000000000..09de6b0bfd Binary files /dev/null and b/notes/pics/942ca0d2-9d5c-45a4-89cb-5fd89b61913f.png differ diff --git a/notes/pics/942f33c9-8ad9-4987-836f-007de4c21de0.png b/notes/pics/942f33c9-8ad9-4987-836f-007de4c21de0.png new file mode 100644 index 0000000000..0fd33f0f1e Binary files /dev/null and b/notes/pics/942f33c9-8ad9-4987-836f-007de4c21de0.png differ diff --git a/notes/pics/94617147-0cbd-4a28-847d-81e52efb1b1e.png b/notes/pics/94617147-0cbd-4a28-847d-81e52efb1b1e.png new file mode 100644 index 0000000000..db8349872a Binary files /dev/null and b/notes/pics/94617147-0cbd-4a28-847d-81e52efb1b1e.png differ diff --git a/notes/pics/95080fae-de40-463d-a76e-783a0c677fec.gif b/notes/pics/95080fae-de40-463d-a76e-783a0c677fec.gif new file mode 100644 index 0000000000..9de55285bb Binary files /dev/null and b/notes/pics/95080fae-de40-463d-a76e-783a0c677fec.gif differ diff --git a/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png b/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png new file mode 100644 index 0000000000..1028086830 Binary files /dev/null and b/notes/pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png differ diff --git a/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg b/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg new file mode 100644 index 0000000000..8a43699783 Binary files /dev/null and b/notes/pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg differ diff --git a/notes/pics/9709694b-db05-4cce-8d2f-1c8b09f4d921.png b/notes/pics/9709694b-db05-4cce-8d2f-1c8b09f4d921.png new file mode 100644 index 0000000000..cc3e2517b0 Binary files /dev/null and b/notes/pics/9709694b-db05-4cce-8d2f-1c8b09f4d921.png differ diff --git a/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg b/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg new file mode 100644 index 0000000000..0b89710aa1 Binary files /dev/null and b/notes/pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg differ diff --git a/notes/pics/99b6060e-099d-4201-8e86-f8ab3768a7cf.png b/notes/pics/99b6060e-099d-4201-8e86-f8ab3768a7cf.png new file mode 100644 index 0000000000..123575b8b9 Binary files /dev/null and b/notes/pics/99b6060e-099d-4201-8e86-f8ab3768a7cf.png differ diff --git a/notes/pics/99d5e84e-fc2a-49a3-8259-8de274617756.gif b/notes/pics/99d5e84e-fc2a-49a3-8259-8de274617756.gif new file mode 100644 index 0000000000..b5cf8e797b Binary files /dev/null and b/notes/pics/99d5e84e-fc2a-49a3-8259-8de274617756.gif differ diff --git a/notes/pics/9a519773-84b2-4c81-81cf-4e7dd739a97a.png b/notes/pics/9a519773-84b2-4c81-81cf-4e7dd739a97a.png new file mode 100644 index 0000000000..c90e62dc31 Binary files /dev/null and b/notes/pics/9a519773-84b2-4c81-81cf-4e7dd739a97a.png differ diff --git a/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg b/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg new file mode 100644 index 0000000000..654735d58c Binary files /dev/null and b/notes/pics/9ae89f16-7905-4a6f-88a2-874b4cac91f4.jpg differ diff --git a/notes/pics/9b679ff5-94c6-48a7-b9b7-2ea868e828ed.png b/notes/pics/9b679ff5-94c6-48a7-b9b7-2ea868e828ed.png new file mode 100644 index 0000000000..d361b7b334 Binary files /dev/null and b/notes/pics/9b679ff5-94c6-48a7-b9b7-2ea868e828ed.png differ diff --git a/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png b/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png new file mode 100644 index 0000000000..0104993035 Binary files /dev/null and b/notes/pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png differ diff --git a/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png b/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png new file mode 100644 index 0000000000..02b1f35ace Binary files /dev/null and b/notes/pics/9bbddeeb-e939-41f0-8e8e-2b1a0aa7e0a7.png differ diff --git a/notes/pics/9c422923-1447-4a3b-a4e1-97e663738187.jpg b/notes/pics/9c422923-1447-4a3b-a4e1-97e663738187.jpg new file mode 100644 index 0000000000..ce06630276 Binary files /dev/null and b/notes/pics/9c422923-1447-4a3b-a4e1-97e663738187.jpg differ diff --git a/docs/notes/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png b/notes/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png similarity index 100% rename from docs/notes/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png rename to notes/pics/9cd0ae20-4fb5-4017-a000-f7d3a0eb3529.png diff --git a/notes/pics/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif b/notes/pics/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif new file mode 100644 index 0000000000..41146c22d1 Binary files /dev/null and b/notes/pics/9d1deeba-4ae1-41dc-98f4-47d85b9831bc.gif differ diff --git a/notes/pics/9daa3616-00a4-48c4-9146-792dc8499be3.jpg b/notes/pics/9daa3616-00a4-48c4-9146-792dc8499be3.jpg new file mode 100644 index 0000000000..ab7ef4a500 Binary files /dev/null and b/notes/pics/9daa3616-00a4-48c4-9146-792dc8499be3.jpg differ diff --git a/notes/pics/9dae7475-934f-42e5-b3b3-12724337170a.png b/notes/pics/9dae7475-934f-42e5-b3b3-12724337170a.png new file mode 100644 index 0000000000..7508fa0826 Binary files /dev/null and b/notes/pics/9dae7475-934f-42e5-b3b3-12724337170a.png differ diff --git a/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png b/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png new file mode 100644 index 0000000000..61717a2664 Binary files /dev/null and b/notes/pics/9ea86eb5-000a-4281-b948-7b567bd6f1d8.png differ diff --git a/notes/pics/BSD_disk.png b/notes/pics/BSD_disk.png new file mode 100644 index 0000000000..b5ada5b3e8 Binary files /dev/null and b/notes/pics/BSD_disk.png differ diff --git a/notes/pics/GUID_Partition_Table_Scheme.svg.png b/notes/pics/GUID_Partition_Table_Scheme.svg.png new file mode 100644 index 0000000000..ddb9975fcd Binary files /dev/null and b/notes/pics/GUID_Partition_Table_Scheme.svg.png differ diff --git a/notes/pics/HTTP1_x_Connections.png b/notes/pics/HTTP1_x_Connections.png new file mode 100644 index 0000000000..68039d6b37 Binary files /dev/null and b/notes/pics/HTTP1_x_Connections.png differ diff --git a/notes/pics/HTTP_RequestMessageExample.png b/notes/pics/HTTP_RequestMessageExample.png new file mode 100644 index 0000000000..e970928e53 Binary files /dev/null and b/notes/pics/HTTP_RequestMessageExample.png differ diff --git a/notes/pics/HTTP_ResponseMessageExample.png b/notes/pics/HTTP_ResponseMessageExample.png new file mode 100644 index 0000000000..4d050b3a57 Binary files /dev/null and b/notes/pics/HTTP_ResponseMessageExample.png differ diff --git a/notes/pics/How-HTTPS-Works.png b/notes/pics/How-HTTPS-Works.png new file mode 100644 index 0000000000..7c31acf2be Binary files /dev/null and b/notes/pics/How-HTTPS-Works.png differ diff --git a/notes/pics/PPjwP.png b/notes/pics/PPjwP.png new file mode 100644 index 0000000000..f5895f647f Binary files /dev/null and b/notes/pics/PPjwP.png differ diff --git a/docs/notes/pics/ProcessState.png b/notes/pics/ProcessState.png similarity index 100% rename from docs/notes/pics/ProcessState.png rename to notes/pics/ProcessState.png diff --git a/notes/pics/_u4E0B_u8F7D.png b/notes/pics/_u4E0B_u8F7D.png new file mode 100644 index 0000000000..f6903ee7ee Binary files /dev/null and b/notes/pics/_u4E0B_u8F7D.png differ diff --git a/docs/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif b/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif similarity index 100% rename from docs/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif rename to notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_.gif diff --git a/docs/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif b/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif similarity index 100% rename from docs/notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif rename to notes/pics/_u4E8C_u53C9_u6811_u7684_u4E0B_1548504426508.gif diff --git a/docs/notes/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif b/notes/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif similarity index 100% rename from docs/notes/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif rename to notes/pics/_u4E8C_u7EF4_u6570_u7EC4_u4E2D_.gif diff --git a/docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif b/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif similarity index 100% rename from docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif rename to notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548293972480.gif diff --git a/docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif b/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif similarity index 100% rename from docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif rename to notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548295232667.gif diff --git a/docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif b/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif similarity index 100% rename from docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif rename to notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548296249372.gif diff --git a/docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif b/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif similarity index 100% rename from docs/notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif rename to notes/pics/_u4ECE_u5C3E_u5230_u5934_u6253_1548503461113.gif diff --git a/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif b/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif new file mode 100644 index 0000000000..ad4a1ff78d Binary files /dev/null and b/notes/pics/_u6590_u6CE2_u90A3_u5951_u6570_u5217.gif differ diff --git a/docs/notes/pics/_u66FF_u6362_u7A7A_u683C.gif b/notes/pics/_u66FF_u6362_u7A7A_u683C.gif similarity index 100% rename from docs/notes/pics/_u66FF_u6362_u7A7A_u683C.gif rename to notes/pics/_u66FF_u6362_u7A7A_u683C.gif diff --git a/docs/notes/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif b/notes/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif similarity index 100% rename from docs/notes/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif rename to notes/pics/_u7528_u4E24_u4E2A_u6808_u5B9E_.gif diff --git a/docs/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif b/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif similarity index 100% rename from docs/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif rename to notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-1.gif diff --git a/docs/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif b/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif similarity index 100% rename from docs/notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif rename to notes/pics/_u91CD_u5EFA_u4E8C_u53C9_u6811-21548502782193.gif diff --git a/notes/pics/a0ce43b7-afa8-4397-a96e-5c12a070f2ae.jpg b/notes/pics/a0ce43b7-afa8-4397-a96e-5c12a070f2ae.jpg new file mode 100644 index 0000000000..5e4695070f Binary files /dev/null and b/notes/pics/a0ce43b7-afa8-4397-a96e-5c12a070f2ae.jpg differ diff --git a/notes/pics/a14268b3-b937-4ffa-a34a-4cc53071686b.jpg b/notes/pics/a14268b3-b937-4ffa-a34a-4cc53071686b.jpg new file mode 100644 index 0000000000..38996fa263 Binary files /dev/null and b/notes/pics/a14268b3-b937-4ffa-a34a-4cc53071686b.jpg differ diff --git a/notes/pics/a1616dac-0e12-40b2-827d-9e3f7f0b940d.png b/notes/pics/a1616dac-0e12-40b2-827d-9e3f7f0b940d.png new file mode 100644 index 0000000000..b6b5bc445c Binary files /dev/null and b/notes/pics/a1616dac-0e12-40b2-827d-9e3f7f0b940d.png differ diff --git a/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png b/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png new file mode 100644 index 0000000000..2d6503656a Binary files /dev/null and b/notes/pics/a314bb79-5b18-4e63-a976-3448bffa6f1b.png differ diff --git a/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg b/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg new file mode 100644 index 0000000000..135749154c Binary files /dev/null and b/notes/pics/a3253deb-8d21-40a1-aae4-7d178e4aa319.jpg differ diff --git a/notes/pics/a3e4dc62-0da5-4d22-94f2-140078281812.jpg b/notes/pics/a3e4dc62-0da5-4d22-94f2-140078281812.jpg new file mode 100644 index 0000000000..5fb9e5ca92 Binary files /dev/null and b/notes/pics/a3e4dc62-0da5-4d22-94f2-140078281812.jpg differ diff --git a/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png b/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png new file mode 100644 index 0000000000..e602c1f353 Binary files /dev/null and b/notes/pics/a40661e4-1a71-46d2-a158-ff36f7fc3331.png differ diff --git a/notes/pics/a42ad3a7-3574-4c48-a783-ed3d08a0688a.jpg b/notes/pics/a42ad3a7-3574-4c48-a783-ed3d08a0688a.jpg new file mode 100644 index 0000000000..4fe7b4b50d Binary files /dev/null and b/notes/pics/a42ad3a7-3574-4c48-a783-ed3d08a0688a.jpg differ diff --git a/notes/pics/a4444545-0d68-4015-9a3d-19209dc436b3.png b/notes/pics/a4444545-0d68-4015-9a3d-19209dc436b3.png new file mode 100644 index 0000000000..b35be3f561 Binary files /dev/null and b/notes/pics/a4444545-0d68-4015-9a3d-19209dc436b3.png differ diff --git a/notes/pics/a4a0a6e6-386b-4bfa-b899-ec33d3310f3e.png b/notes/pics/a4a0a6e6-386b-4bfa-b899-ec33d3310f3e.png new file mode 100644 index 0000000000..7866d799b6 Binary files /dev/null and b/notes/pics/a4a0a6e6-386b-4bfa-b899-ec33d3310f3e.png differ diff --git a/docs/notes/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png b/notes/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png similarity index 100% rename from docs/notes/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png rename to notes/pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png diff --git a/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png b/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png new file mode 100644 index 0000000000..d6c39eb408 Binary files /dev/null and b/notes/pics/a6c20f60-5eba-427d-9413-352ada4b40fe.png differ diff --git a/notes/pics/a7cb8423-895d-4975-8ef8-662a0029c772.png b/notes/pics/a7cb8423-895d-4975-8ef8-662a0029c772.png new file mode 100644 index 0000000000..29f9d362b2 Binary files /dev/null and b/notes/pics/a7cb8423-895d-4975-8ef8-662a0029c772.png differ diff --git a/notes/pics/a8c8f894-a712-447c-9906-5caef6a016e3.png b/notes/pics/a8c8f894-a712-447c-9906-5caef6a016e3.png new file mode 100644 index 0000000000..bd757f8137 Binary files /dev/null and b/notes/pics/a8c8f894-a712-447c-9906-5caef6a016e3.png differ diff --git a/docs/notes/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg b/notes/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg similarity index 100% rename from docs/notes/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg rename to notes/pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg diff --git a/notes/pics/a9f18f8a-c1ea-422e-aa56-d91716b0f755.jpg b/notes/pics/a9f18f8a-c1ea-422e-aa56-d91716b0f755.jpg new file mode 100644 index 0000000000..9579a564bc Binary files /dev/null and b/notes/pics/a9f18f8a-c1ea-422e-aa56-d91716b0f755.jpg differ diff --git a/docs/notes/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png b/notes/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png similarity index 100% rename from docs/notes/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png rename to notes/pics/aa29cc88-7256-4399-8c7f-3cf4a6489559.png diff --git a/notes/pics/aaac1bbe-8e37-43d6-b645-cec16a092654_200.png b/notes/pics/aaac1bbe-8e37-43d6-b645-cec16a092654_200.png new file mode 100644 index 0000000000..f6d7d7908a Binary files /dev/null and b/notes/pics/aaac1bbe-8e37-43d6-b645-cec16a092654_200.png differ diff --git a/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png b/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png new file mode 100644 index 0000000000..45848b4546 Binary files /dev/null and b/notes/pics/abb3e0d1-c1bd-45d0-8190-73c74a9f6679.png differ diff --git a/notes/pics/ac6a794b-68c0-486c-902f-8d988eee5766.png b/notes/pics/ac6a794b-68c0-486c-902f-8d988eee5766.png new file mode 100644 index 0000000000..3c05e805f2 Binary files /dev/null and b/notes/pics/ac6a794b-68c0-486c-902f-8d988eee5766.png differ diff --git a/notes/pics/ac929ea3-daca-40ec-9e95-4b2fa6678243.png b/notes/pics/ac929ea3-daca-40ec-9e95-4b2fa6678243.png new file mode 100644 index 0000000000..a9361a1eec Binary files /dev/null and b/notes/pics/ac929ea3-daca-40ec-9e95-4b2fa6678243.png differ diff --git a/notes/pics/ace20410-f053-4c4a-aca4-2c603ff11bbe.png b/notes/pics/ace20410-f053-4c4a-aca4-2c603ff11bbe.png new file mode 100644 index 0000000000..25f75527ce Binary files /dev/null and b/notes/pics/ace20410-f053-4c4a-aca4-2c603ff11bbe.png differ diff --git a/notes/pics/ad5cc8fc-d59b-45ce-8899-63a18320d97e.gif b/notes/pics/ad5cc8fc-d59b-45ce-8899-63a18320d97e.gif new file mode 100644 index 0000000000..48cf5e0139 Binary files /dev/null and b/notes/pics/ad5cc8fc-d59b-45ce-8899-63a18320d97e.gif differ diff --git a/notes/pics/adfb427d-3b21-40d7-a142-757f4ed73079.png b/notes/pics/adfb427d-3b21-40d7-a142-757f4ed73079.png new file mode 100644 index 0000000000..9da3a1370d Binary files /dev/null and b/notes/pics/adfb427d-3b21-40d7-a142-757f4ed73079.png differ diff --git a/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png b/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png new file mode 100644 index 0000000000..5b5bfc4754 Binary files /dev/null and b/notes/pics/ae1b27b8-bc13-42e7-ac12-a2242e125499.png differ diff --git a/notes/pics/aefa8042-15fa-4e8b-9f50-20b282a2c624.png b/notes/pics/aefa8042-15fa-4e8b-9f50-20b282a2c624.png new file mode 100644 index 0000000000..9b7ce9dfed Binary files /dev/null and b/notes/pics/aefa8042-15fa-4e8b-9f50-20b282a2c624.png differ diff --git a/notes/pics/af0e94d9-502d-4531-938f-d46dd29de52d.png b/notes/pics/af0e94d9-502d-4531-938f-d46dd29de52d.png new file mode 100644 index 0000000000..7aab9bed4f Binary files /dev/null and b/notes/pics/af0e94d9-502d-4531-938f-d46dd29de52d.png differ diff --git a/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png b/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png new file mode 100644 index 0000000000..908d678a51 Binary files /dev/null and b/notes/pics/af198da1-2480-4043-b07f-a3b91a88b815.png differ diff --git a/notes/pics/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg b/notes/pics/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg new file mode 100644 index 0000000000..00ea4f6b23 Binary files /dev/null and b/notes/pics/af1d1166-63af-47b6-9aa3-2bf2bd37bd03.jpg differ diff --git a/notes/pics/b0611f89-1e5f-4494-a795-3544bf65042a.gif b/notes/pics/b0611f89-1e5f-4494-a795-3544bf65042a.gif new file mode 100644 index 0000000000..3e1fdb8bdf Binary files /dev/null and b/notes/pics/b0611f89-1e5f-4494-a795-3544bf65042a.gif differ diff --git a/notes/pics/b0f0107a-e35b-4ace-b25e-cacb22b1556035029196.gif b/notes/pics/b0f0107a-e35b-4ace-b25e-cacb22b1556035029196.gif new file mode 100644 index 0000000000..080f51aa26 Binary files /dev/null and b/notes/pics/b0f0107a-e35b-4ace-b25e-cacb22b1556035029196.gif differ diff --git a/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png b/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png new file mode 100644 index 0000000000..8561aad3f9 Binary files /dev/null and b/notes/pics/b0f61ac2-a4b6-4042-9cf0-ccf4238c1ff7.png differ diff --git a/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg b/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg new file mode 100644 index 0000000000..0444210b05 Binary files /dev/null and b/notes/pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg differ diff --git a/docs/notes/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png b/notes/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png similarity index 100% rename from docs/notes/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png rename to notes/pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png diff --git a/notes/pics/b25e75a5-7aa4-42f8-b2f8-d5f81c4fb2fc.png b/notes/pics/b25e75a5-7aa4-42f8-b2f8-d5f81c4fb2fc.png new file mode 100644 index 0000000000..7ed8a50b31 Binary files /dev/null and b/notes/pics/b25e75a5-7aa4-42f8-b2f8-d5f81c4fb2fc.png differ diff --git a/notes/pics/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png b/notes/pics/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png new file mode 100644 index 0000000000..c376a02755 Binary files /dev/null and b/notes/pics/b2b77b9e-958c-4016-8ae5-9c6edd83871e.png differ diff --git a/docs/notes/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg b/notes/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg similarity index 100% rename from docs/notes/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg rename to notes/pics/b396d726-b75f-4a32-89a2-03a7b6e19f6f.jpg diff --git a/docs/notes/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg b/notes/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg similarity index 100% rename from docs/notes/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg rename to notes/pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg diff --git a/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png b/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png new file mode 100644 index 0000000000..8b44b87139 Binary files /dev/null and b/notes/pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png differ diff --git a/notes/pics/b5e9fa4d-78d3-4176-8273-756d970742c7.png b/notes/pics/b5e9fa4d-78d3-4176-8273-756d970742c7.png new file mode 100644 index 0000000000..59ec8ce1f3 Binary files /dev/null and b/notes/pics/b5e9fa4d-78d3-4176-8273-756d970742c7.png differ diff --git a/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg b/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg new file mode 100644 index 0000000000..e881294b5e Binary files /dev/null and b/notes/pics/b8081c84-62c4-4019-b3ee-4bd0e443d647.jpg differ diff --git a/notes/pics/b8922f8c-95e6-4187-be85-572a509afb71.png b/notes/pics/b8922f8c-95e6-4187-be85-572a509afb71.png new file mode 100644 index 0000000000..c70d69708b Binary files /dev/null and b/notes/pics/b8922f8c-95e6-4187-be85-572a509afb71.png differ diff --git a/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png b/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png new file mode 100644 index 0000000000..ed37aeb069 Binary files /dev/null and b/notes/pics/b8dd708d-f372-4b04-b828-1dd99021c244.png differ diff --git a/notes/pics/b903fda8-07d0-46a7-91a7-e803892895cf.gif b/notes/pics/b903fda8-07d0-46a7-91a7-e803892895cf.gif new file mode 100644 index 0000000000..873aac8679 Binary files /dev/null and b/notes/pics/b903fda8-07d0-46a7-91a7-e803892895cf.gif differ diff --git a/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg b/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg new file mode 100644 index 0000000000..ba00013865 Binary files /dev/null and b/notes/pics/b988877c-0f0a-4593-916d-de2081320628.jpg differ diff --git a/docs/notes/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg b/notes/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg similarity index 100% rename from docs/notes/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg rename to notes/pics/b9d79a5a-e7af-499b-b989-f10483e71b8b.jpg diff --git a/notes/pics/ba078291-791e-4378-b6d1-ece76c2f0b14.png b/notes/pics/ba078291-791e-4378-b6d1-ece76c2f0b14.png new file mode 100644 index 0000000000..c2a32e7dce Binary files /dev/null and b/notes/pics/ba078291-791e-4378-b6d1-ece76c2f0b14.png differ diff --git a/notes/pics/ba355101-4a93-4c71-94fb-1da83639727b.jpg b/notes/pics/ba355101-4a93-4c71-94fb-1da83639727b.jpg new file mode 100644 index 0000000000..b5625ae0d6 Binary files /dev/null and b/notes/pics/ba355101-4a93-4c71-94fb-1da83639727b.jpg differ diff --git a/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png b/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png new file mode 100644 index 0000000000..e033180860 Binary files /dev/null and b/notes/pics/baaa681f-7c52-4198-a5ae-303b9386cf47.png differ diff --git a/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png b/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png new file mode 100644 index 0000000000..7845ba0004 Binary files /dev/null and b/notes/pics/bb6a49be-00f2-4f27-a0ce-4ed764bc605c.png differ diff --git a/notes/pics/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png b/notes/pics/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png new file mode 100644 index 0000000000..92dfa7f53d Binary files /dev/null and b/notes/pics/bb7fc182-98c2-4860-8ea3-630e27a5f29f.png differ diff --git a/notes/pics/bc6be2d0-ed5e-4def-89e5-3ada9afa811a.gif b/notes/pics/bc6be2d0-ed5e-4def-89e5-3ada9afa811a.gif new file mode 100644 index 0000000000..23de8ec7df Binary files /dev/null and b/notes/pics/bc6be2d0-ed5e-4def-89e5-3ada9afa811a.gif differ diff --git a/docs/notes/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg b/notes/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg similarity index 100% rename from docs/notes/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg rename to notes/pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg diff --git a/notes/pics/be608a77-7b7f-4f8e-87cc-f2237270bf69.png b/notes/pics/be608a77-7b7f-4f8e-87cc-f2237270bf69.png new file mode 100644 index 0000000000..44696cc022 Binary files /dev/null and b/notes/pics/be608a77-7b7f-4f8e-87cc-f2237270bf69.png differ diff --git a/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png b/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png new file mode 100644 index 0000000000..158015bc65 Binary files /dev/null and b/notes/pics/beba612e-dc5b-4fc2-869d-0b23408ac90a.png differ diff --git a/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg b/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg new file mode 100644 index 0000000000..71496f6af9 Binary files /dev/null and b/notes/pics/bf16c541-0717-473b-b75d-4115864f4fbf.jpg differ diff --git a/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg b/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg new file mode 100644 index 0000000000..399edf3162 Binary files /dev/null and b/notes/pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg differ diff --git a/notes/pics/bfea8772-d01b-4a51-8adc-edfd7d3dce84.jpg b/notes/pics/bfea8772-d01b-4a51-8adc-edfd7d3dce84.jpg new file mode 100644 index 0000000000..597be53c61 Binary files /dev/null and b/notes/pics/bfea8772-d01b-4a51-8adc-edfd7d3dce84.jpg differ diff --git a/docs/notes/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png b/notes/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png similarity index 100% rename from docs/notes/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png rename to notes/pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png diff --git a/notes/pics/c0874e0a-dba3-467e-9c86-dd9313e0843e.jpg b/notes/pics/c0874e0a-dba3-467e-9c86-dd9313e0843e.jpg new file mode 100644 index 0000000000..8d95cb5d1a Binary files /dev/null and b/notes/pics/c0874e0a-dba3-467e-9c86-dd9313e0843e.jpg differ diff --git a/notes/pics/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png b/notes/pics/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png new file mode 100644 index 0000000000..9e8e93c920 Binary files /dev/null and b/notes/pics/c094d2bc-ec75-444b-af77-d369dfb6b3b4.png differ diff --git a/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png b/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png new file mode 100644 index 0000000000..14c225b359 Binary files /dev/null and b/notes/pics/c0a9fa91-da2e-4892-8c9f-80206a6f7047.png differ diff --git a/notes/pics/c11528f6-fc71-4a2b-8d2f-51b8954c38f1.jpg b/notes/pics/c11528f6-fc71-4a2b-8d2f-51b8954c38f1.jpg new file mode 100644 index 0000000000..3ae21c60bd Binary files /dev/null and b/notes/pics/c11528f6-fc71-4a2b-8d2f-51b8954c38f1.jpg differ diff --git a/notes/pics/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png b/notes/pics/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png new file mode 100644 index 0000000000..69fe1ba22d Binary files /dev/null and b/notes/pics/c13e2a3d-b01c-4a08-a69b-db2c4e821e09.png differ diff --git a/notes/pics/c269e362-1128-4212-9cf3-d4c12b363b2f.gif b/notes/pics/c269e362-1128-4212-9cf3-d4c12b363b2f.gif new file mode 100644 index 0000000000..e372dea1fd Binary files /dev/null and b/notes/pics/c269e362-1128-4212-9cf3-d4c12b363b2f.gif differ diff --git a/notes/pics/c2ca8dd2-8d00-4a3e-bece-db7849ac9cfd.gif b/notes/pics/c2ca8dd2-8d00-4a3e-bece-db7849ac9cfd.gif new file mode 100644 index 0000000000..99bb07434f Binary files /dev/null and b/notes/pics/c2ca8dd2-8d00-4a3e-bece-db7849ac9cfd.gif differ diff --git a/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png b/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png new file mode 100644 index 0000000000..74acb7c2b8 Binary files /dev/null and b/notes/pics/c2cbf5d2-82af-4c78-bd43-495da5adf55f.png differ diff --git a/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png b/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png new file mode 100644 index 0000000000..d50c1da4bb Binary files /dev/null and b/notes/pics/c2d343f7-604c-4856-9a3c-c71d6f67fecc.png differ diff --git a/docs/notes/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg b/notes/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg similarity index 100% rename from docs/notes/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg rename to notes/pics/c3369072-c740-43b0-b276-202bd1d3960d.jpg diff --git a/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg b/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg new file mode 100644 index 0000000000..f4ed4dce2c Binary files /dev/null and b/notes/pics/c34f4503-f62c-4043-9dc6-3e03288657df.jpg differ diff --git a/notes/pics/c395a428-827c-405b-abd7-8a069316f583.jpg b/notes/pics/c395a428-827c-405b-abd7-8a069316f583.jpg new file mode 100644 index 0000000000..8cee2ac86a Binary files /dev/null and b/notes/pics/c395a428-827c-405b-abd7-8a069316f583.jpg differ diff --git a/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png b/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png new file mode 100644 index 0000000000..adebfb1177 Binary files /dev/null and b/notes/pics/c3c1c0e8-3a78-4426-961f-b46dd0879dd8.png differ diff --git a/notes/pics/c44a0342-f405-4f17-b750-e27cf4aadde2.png b/notes/pics/c44a0342-f405-4f17-b750-e27cf4aadde2.png new file mode 100644 index 0000000000..200fa5d45b Binary files /dev/null and b/notes/pics/c44a0342-f405-4f17-b750-e27cf4aadde2.png differ diff --git a/notes/pics/c4859290-e27d-4f12-becf-e2a5c1f3a275.gif b/notes/pics/c4859290-e27d-4f12-becf-e2a5c1f3a275.gif new file mode 100644 index 0000000000..9e8a50a25d Binary files /dev/null and b/notes/pics/c4859290-e27d-4f12-becf-e2a5c1f3a275.gif differ diff --git a/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png b/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png new file mode 100644 index 0000000000..75adb584b1 Binary files /dev/null and b/notes/pics/c5085437-54df-4304-b62d-44b961711ba7.png differ diff --git a/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg b/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg new file mode 100644 index 0000000000..4a6d519733 Binary files /dev/null and b/notes/pics/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg differ diff --git a/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png b/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png new file mode 100644 index 0000000000..6770675ecf Binary files /dev/null and b/notes/pics/c79da808-0f28-4a36-bc04-33ccc5b83c13.png differ diff --git a/notes/pics/c8563120-cb00-4dd6-9213-9d9b337a7f7c.png b/notes/pics/c8563120-cb00-4dd6-9213-9d9b337a7f7c.png new file mode 100644 index 0000000000..228e1d8a06 Binary files /dev/null and b/notes/pics/c8563120-cb00-4dd6-9213-9d9b337a7f7c.png differ diff --git a/notes/pics/c9714e03-eb43-48e8-b1b0-1fb620a036c8.gif b/notes/pics/c9714e03-eb43-48e8-b1b0-1fb620a036c8.gif new file mode 100644 index 0000000000..b5ed6300b0 Binary files /dev/null and b/notes/pics/c9714e03-eb43-48e8-b1b0-1fb620a036c8.gif differ diff --git a/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg b/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg new file mode 100644 index 0000000000..8faf0c508e Binary files /dev/null and b/notes/pics/c9cfd600-bc91-4f3a-9f99-b42f88a5bb24.jpg differ diff --git a/notes/pics/ca9f23bf-55a4-47b2-9534-a28e35397988.png b/notes/pics/ca9f23bf-55a4-47b2-9534-a28e35397988.png new file mode 100644 index 0000000000..959697418f Binary files /dev/null and b/notes/pics/ca9f23bf-55a4-47b2-9534-a28e35397988.png differ diff --git a/notes/pics/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png b/notes/pics/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png new file mode 100644 index 0000000000..8cd71a8777 Binary files /dev/null and b/notes/pics/cafbfeb8-7dfe-4c0a-a3c9-750eeb824068.png differ diff --git a/notes/pics/cbbfe06c-f0cb-47c4-bf7b-2780aebd98b2.png b/notes/pics/cbbfe06c-f0cb-47c4-bf7b-2780aebd98b2.png new file mode 100644 index 0000000000..eef74b3648 Binary files /dev/null and b/notes/pics/cbbfe06c-f0cb-47c4-bf7b-2780aebd98b2.png differ diff --git a/docs/notes/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png b/notes/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png similarity index 100% rename from docs/notes/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png rename to notes/pics/cbf50eb8-22b4-4528-a2e7-d187143d57f7.png diff --git a/notes/pics/ccd773a5-ad38-4022-895c-7ac318f31437.png b/notes/pics/ccd773a5-ad38-4022-895c-7ac318f31437.png new file mode 100644 index 0000000000..e4706e590f Binary files /dev/null and b/notes/pics/ccd773a5-ad38-4022-895c-7ac318f31437.png differ diff --git a/notes/pics/cd1be8c2-755a-4a66-ad92-2e30f8f47922.png b/notes/pics/cd1be8c2-755a-4a66-ad92-2e30f8f47922.png new file mode 100644 index 0000000000..441597fbc8 Binary files /dev/null and b/notes/pics/cd1be8c2-755a-4a66-ad92-2e30f8f47922.png differ diff --git a/notes/pics/cd411a94-3786-4c94-9e08-f28320e010d5.png b/notes/pics/cd411a94-3786-4c94-9e08-f28320e010d5.png new file mode 100644 index 0000000000..d941a7cbaf Binary files /dev/null and b/notes/pics/cd411a94-3786-4c94-9e08-f28320e010d5.png differ diff --git a/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png b/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png new file mode 100644 index 0000000000..b721cc0a93 Binary files /dev/null and b/notes/pics/cd5fbcff-3f35-43a6-8ffa-082a93ce0f0e.png differ diff --git a/docs/notes/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png b/notes/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png similarity index 100% rename from docs/notes/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png rename to notes/pics/cf4386a1-58c9-4eca-a17f-e12b1e9770eb.png diff --git a/notes/pics/d02f74dd-8e33-4f3c-bf29-53203a06695a.png b/notes/pics/d02f74dd-8e33-4f3c-bf29-53203a06695a.png new file mode 100644 index 0000000000..d76c03540b Binary files /dev/null and b/notes/pics/d02f74dd-8e33-4f3c-bf29-53203a06695a.png differ diff --git a/notes/pics/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png b/notes/pics/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png new file mode 100644 index 0000000000..5a4fdbcf50 Binary files /dev/null and b/notes/pics/d03a2efa-ef19-4c96-97e8-ff61df8061d3.png differ diff --git a/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png b/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png new file mode 100644 index 0000000000..9f1fbb0762 Binary files /dev/null and b/notes/pics/d0afdd23-c9a5-4d1c-9b3d-404bff3bd0d1.png differ diff --git a/notes/pics/d156bcda-ac8d-4324-95e0-0c8df41567c9.gif b/notes/pics/d156bcda-ac8d-4324-95e0-0c8df41567c9.gif new file mode 100644 index 0000000000..66afd965bd Binary files /dev/null and b/notes/pics/d156bcda-ac8d-4324-95e0-0c8df41567c9.gif differ diff --git a/docs/notes/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png b/notes/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png similarity index 100% rename from docs/notes/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png rename to notes/pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png diff --git a/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif b/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif new file mode 100644 index 0000000000..9681e7e7e4 Binary files /dev/null and b/notes/pics/d1ed87eb-da5a-4728-b0dc-e3705aa028ea.gif differ diff --git a/notes/pics/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg b/notes/pics/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg new file mode 100644 index 0000000000..fe765547c5 Binary files /dev/null and b/notes/pics/d27c99f0-7881-4f2d-9675-c75cbdee3acd.jpg differ diff --git a/notes/pics/d2ae9932-e2b1-4191-8ee9-e573f36d3895.png b/notes/pics/d2ae9932-e2b1-4191-8ee9-e573f36d3895.png new file mode 100644 index 0000000000..f8223bd829 Binary files /dev/null and b/notes/pics/d2ae9932-e2b1-4191-8ee9-e573f36d3895.png differ diff --git a/notes/pics/d2c072cc-8b17-480c-813e-18cdb3b4b51f.jpg b/notes/pics/d2c072cc-8b17-480c-813e-18cdb3b4b51f.jpg new file mode 100644 index 0000000000..b58c676eec Binary files /dev/null and b/notes/pics/d2c072cc-8b17-480c-813e-18cdb3b4b51f.jpg differ diff --git a/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg b/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg new file mode 100644 index 0000000000..6acf5626b1 Binary files /dev/null and b/notes/pics/d4c3a4a1-0846-46ec-9cc3-eaddfca71254.jpg differ diff --git a/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png b/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png new file mode 100644 index 0000000000..16ff310ed3 Binary files /dev/null and b/notes/pics/d52270b4-9097-4667-9f18-f405fc661c99.png differ diff --git a/notes/pics/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg b/notes/pics/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg new file mode 100644 index 0000000000..dca06e4692 Binary files /dev/null and b/notes/pics/d5e838cf-d8a2-49af-90df-1b2a714ee676.jpg differ diff --git a/notes/pics/d85870db-f28c-48c3-9d24-85a36fda5e51.png b/notes/pics/d85870db-f28c-48c3-9d24-85a36fda5e51.png new file mode 100644 index 0000000000..f0a85bef3c Binary files /dev/null and b/notes/pics/d85870db-f28c-48c3-9d24-85a36fda5e51.png differ diff --git a/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png b/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png new file mode 100644 index 0000000000..87da9b0477 Binary files /dev/null and b/notes/pics/da1f96b9-fd4d-44ca-8925-fb14c5733388.png differ diff --git a/notes/pics/db5e376d-0b3e-490e-a43a-3231914b6668.png b/notes/pics/db5e376d-0b3e-490e-a43a-3231914b6668.png new file mode 100644 index 0000000000..0cf8929bf8 Binary files /dev/null and b/notes/pics/db5e376d-0b3e-490e-a43a-3231914b6668.png differ diff --git a/notes/pics/db5f30a7-8bfa-4ecc-ab5d-747c77818964.gif b/notes/pics/db5f30a7-8bfa-4ecc-ab5d-747c77818964.gif new file mode 100644 index 0000000000..9b78e709df Binary files /dev/null and b/notes/pics/db5f30a7-8bfa-4ecc-ab5d-747c77818964.gif differ diff --git a/notes/pics/db808eff-31d7-4229-a4ad-b8ae71870a3a.png b/notes/pics/db808eff-31d7-4229-a4ad-b8ae71870a3a.png new file mode 100644 index 0000000000..1fc4f47e7f Binary files /dev/null and b/notes/pics/db808eff-31d7-4229-a4ad-b8ae71870a3a.png differ diff --git a/notes/pics/db85a909-5e11-48b2-85d2-f003e7bb35c0.png b/notes/pics/db85a909-5e11-48b2-85d2-f003e7bb35c0.png new file mode 100644 index 0000000000..2c87f48ebe Binary files /dev/null and b/notes/pics/db85a909-5e11-48b2-85d2-f003e7bb35c0.png differ diff --git a/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg b/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg new file mode 100644 index 0000000000..aa4cdad859 Binary files /dev/null and b/notes/pics/dc00f70e-c5c8-4d20-baf1-2d70014a97e3.jpg differ diff --git a/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg b/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg new file mode 100644 index 0000000000..6a5c3e4bc5 Binary files /dev/null and b/notes/pics/dc82f0f3-c1d4-4ac8-90ac-d5b32a9bd75a.jpg differ diff --git a/notes/pics/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png b/notes/pics/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png new file mode 100644 index 0000000000..a08e6be1d3 Binary files /dev/null and b/notes/pics/dc964b86-7a08-4bde-a3d9-e6ddceb29f98.png differ diff --git a/notes/pics/dcdbb96c-9077-4121-aeb8-743e54ac02a4.png b/notes/pics/dcdbb96c-9077-4121-aeb8-743e54ac02a4.png new file mode 100644 index 0000000000..f97a64bd40 Binary files /dev/null and b/notes/pics/dcdbb96c-9077-4121-aeb8-743e54ac02a4.png differ diff --git a/notes/pics/dceea2c3-78ba-4bc5-9836-9dd5df2381a2.gif b/notes/pics/dceea2c3-78ba-4bc5-9836-9dd5df2381a2.gif new file mode 100644 index 0000000000..5f031bf255 Binary files /dev/null and b/notes/pics/dceea2c3-78ba-4bc5-9836-9dd5df2381a2.gif differ diff --git a/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png b/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png new file mode 100644 index 0000000000..53dd099d63 Binary files /dev/null and b/notes/pics/dd3b289c-d90e-44a6-a44c-4880517eb1de.png differ diff --git a/notes/pics/dd563037-fcaa-4bd8-83b6-b39d93a12c77.jpg b/notes/pics/dd563037-fcaa-4bd8-83b6-b39d93a12c77.jpg new file mode 100644 index 0000000000..91072441c5 Binary files /dev/null and b/notes/pics/dd563037-fcaa-4bd8-83b6-b39d93a12c77.jpg differ diff --git a/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png b/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png new file mode 100644 index 0000000000..d2e5e8fe87 Binary files /dev/null and b/notes/pics/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png differ diff --git a/notes/pics/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png b/notes/pics/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png new file mode 100644 index 0000000000..11aaa32579 Binary files /dev/null and b/notes/pics/dfd5d3f8-673c-486b-8ecf-d2082107b67b.png differ diff --git a/notes/pics/docker-filesystems-busyboxrw.png b/notes/pics/docker-filesystems-busyboxrw.png new file mode 100644 index 0000000000..5f046cbc0a Binary files /dev/null and b/notes/pics/docker-filesystems-busyboxrw.png differ diff --git a/notes/pics/e0870f80-b79e-4542-ae39-7420d4b0d8fe.png b/notes/pics/e0870f80-b79e-4542-ae39-7420d4b0d8fe.png new file mode 100644 index 0000000000..a6447ad16d Binary files /dev/null and b/notes/pics/e0870f80-b79e-4542-ae39-7420d4b0d8fe.png differ diff --git a/docs/notes/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png b/notes/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png similarity index 100% rename from docs/notes/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png rename to notes/pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png diff --git a/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg b/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg new file mode 100644 index 0000000000..17e92f788e Binary files /dev/null and b/notes/pics/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg differ diff --git a/notes/pics/e151b5df-5390-4365-b66e-b130cd253c12.png b/notes/pics/e151b5df-5390-4365-b66e-b130cd253c12.png new file mode 100644 index 0000000000..d1ea82874c Binary files /dev/null and b/notes/pics/e151b5df-5390-4365-b66e-b130cd253c12.png differ diff --git a/notes/pics/e1ab9f28-cb15-4178-84b2-98aad87f9bc8.jpg b/notes/pics/e1ab9f28-cb15-4178-84b2-98aad87f9bc8.jpg new file mode 100644 index 0000000000..150da80411 Binary files /dev/null and b/notes/pics/e1ab9f28-cb15-4178-84b2-98aad87f9bc8.jpg differ diff --git a/docs/notes/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png b/notes/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png similarity index 100% rename from docs/notes/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png rename to notes/pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png diff --git a/notes/pics/e2190c36-8b27-4690-bde5-9911020a1294.png b/notes/pics/e2190c36-8b27-4690-bde5-9911020a1294.png new file mode 100644 index 0000000000..e0243d9cb7 Binary files /dev/null and b/notes/pics/e2190c36-8b27-4690-bde5-9911020a1294.png differ diff --git a/docs/notes/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg b/notes/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg similarity index 100% rename from docs/notes/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg rename to notes/pics/e3124763-f75e-46c3-ba82-341e6c98d862.jpg diff --git a/notes/pics/e325a903-f0b1-4fbd-82bf-88913dc2f290.png b/notes/pics/e325a903-f0b1-4fbd-82bf-88913dc2f290.png new file mode 100644 index 0000000000..68f2e23f06 Binary files /dev/null and b/notes/pics/e325a903-f0b1-4fbd-82bf-88913dc2f290.png differ diff --git a/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png b/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png new file mode 100644 index 0000000000..be72990cf9 Binary files /dev/null and b/notes/pics/e3f1657c-80fc-4dfa-9643-bf51abd201c6.png differ diff --git a/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg b/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg new file mode 100644 index 0000000000..f922db6019 Binary files /dev/null and b/notes/pics/e41405a8-7c05-4f70-8092-e961e28d3112.jpg differ diff --git a/notes/pics/e42f188f-f4a9-4e6f-88fc-45f4682072fb.png b/notes/pics/e42f188f-f4a9-4e6f-88fc-45f4682072fb.png new file mode 100644 index 0000000000..6f01f0b321 Binary files /dev/null and b/notes/pics/e42f188f-f4a9-4e6f-88fc-45f4682072fb.png differ diff --git a/notes/pics/e69537d2-a016-4676-b169-9ea17eeb9037.gif b/notes/pics/e69537d2-a016-4676-b169-9ea17eeb9037.gif new file mode 100644 index 0000000000..4e90f7b19d Binary files /dev/null and b/notes/pics/e69537d2-a016-4676-b169-9ea17eeb9037.gif differ diff --git a/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg b/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg new file mode 100644 index 0000000000..1daa964e69 Binary files /dev/null and b/notes/pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg differ diff --git a/notes/pics/e738a3d2-f42e-4755-ae13-ca23497e7a97.png b/notes/pics/e738a3d2-f42e-4755-ae13-ca23497e7a97.png new file mode 100644 index 0000000000..d61f5515d6 Binary files /dev/null and b/notes/pics/e738a3d2-f42e-4755-ae13-ca23497e7a97.png differ diff --git a/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png b/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png new file mode 100644 index 0000000000..99f6c33d78 Binary files /dev/null and b/notes/pics/e92d0ebc-7d46-413b-aec1-34a39602f787.png differ diff --git a/notes/pics/e98deb5a-d5d4-4294-aa9b-9220d4483403.jpg b/notes/pics/e98deb5a-d5d4-4294-aa9b-9220d4483403.jpg new file mode 100644 index 0000000000..d855ff8d06 Binary files /dev/null and b/notes/pics/e98deb5a-d5d4-4294-aa9b-9220d4483403.jpg differ diff --git a/notes/pics/e98e9d20-206b-4533-bacf-3448d0096f38.png b/notes/pics/e98e9d20-206b-4533-bacf-3448d0096f38.png new file mode 100644 index 0000000000..4b71bf11ac Binary files /dev/null and b/notes/pics/e98e9d20-206b-4533-bacf-3448d0096f38.png differ diff --git a/notes/pics/eaa506b6-0747-4bee-81f8-3cda795d8154.png b/notes/pics/eaa506b6-0747-4bee-81f8-3cda795d8154.png new file mode 100644 index 0000000000..2cf0d85789 Binary files /dev/null and b/notes/pics/eaa506b6-0747-4bee-81f8-3cda795d8154.png differ diff --git a/docs/notes/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png b/notes/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png similarity index 100% rename from docs/notes/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png rename to notes/pics/eb859228-c0f2-4bce-910d-d9f76929352b.png diff --git a/notes/pics/ec4d7464-7140-46d8-827e-d63634202e1e.png b/notes/pics/ec4d7464-7140-46d8-827e-d63634202e1e.png new file mode 100644 index 0000000000..aa7d4fa814 Binary files /dev/null and b/notes/pics/ec4d7464-7140-46d8-827e-d63634202e1e.png differ diff --git a/notes/pics/ec840967-d127-4da3-b6bb-186996c56746.png b/notes/pics/ec840967-d127-4da3-b6bb-186996c56746.png new file mode 100644 index 0000000000..35599ebfdc Binary files /dev/null and b/notes/pics/ec840967-d127-4da3-b6bb-186996c56746.png differ diff --git a/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png b/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png new file mode 100644 index 0000000000..627fda3772 Binary files /dev/null and b/notes/pics/ec923dc7-864c-47b0-a411-1f2c48d084de.png differ diff --git a/notes/pics/eca1f422-8381-409b-ad04-98ef39ae38ba.png b/notes/pics/eca1f422-8381-409b-ad04-98ef39ae38ba.png new file mode 100644 index 0000000000..ea25e3d864 Binary files /dev/null and b/notes/pics/eca1f422-8381-409b-ad04-98ef39ae38ba.png differ diff --git a/notes/pics/ecd89a22-c075-4716-8423-e0ba89230e9a.jpg b/notes/pics/ecd89a22-c075-4716-8423-e0ba89230e9a.jpg new file mode 100644 index 0000000000..7de3770b85 Binary files /dev/null and b/notes/pics/ecd89a22-c075-4716-8423-e0ba89230e9a.jpg differ diff --git a/docs/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png b/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png similarity index 100% rename from docs/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png rename to notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ff1552090620367.png diff --git a/docs/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png b/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png similarity index 100% rename from docs/notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png rename to notes/pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png diff --git a/docs/notes/pics/ed523051-608f-4c3f-b343-383e2d194470.png b/notes/pics/ed523051-608f-4c3f-b343-383e2d194470.png similarity index 100% rename from docs/notes/pics/ed523051-608f-4c3f-b343-383e2d194470.png rename to notes/pics/ed523051-608f-4c3f-b343-383e2d194470.png diff --git a/docs/notes/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png b/notes/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png similarity index 100% rename from docs/notes/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png rename to notes/pics/ed5522bb-3a60-481c-8654-43e7195a48fe.png diff --git a/notes/pics/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg b/notes/pics/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg new file mode 100644 index 0000000000..9aac16779b Binary files /dev/null and b/notes/pics/ed77b0e6-38d9-4a34-844f-724f3ffa2c12.jpg differ diff --git a/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg b/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg new file mode 100644 index 0000000000..c390adc774 Binary files /dev/null and b/notes/pics/ee994da4-0fc7-443d-ac56-c08caf00a204.jpg differ diff --git a/notes/pics/ef25ff7c-0f63-420d-8b30-eafbeea35d11.gif b/notes/pics/ef25ff7c-0f63-420d-8b30-eafbeea35d11.gif new file mode 100644 index 0000000000..ee7c43a165 Binary files /dev/null and b/notes/pics/ef25ff7c-0f63-420d-8b30-eafbeea35d11.gif differ diff --git a/notes/pics/ef552ae3-ae0d-4217-88e6-99cbe8163f0c.jpg b/notes/pics/ef552ae3-ae0d-4217-88e6-99cbe8163f0c.jpg new file mode 100644 index 0000000000..7205887be3 Binary files /dev/null and b/notes/pics/ef552ae3-ae0d-4217-88e6-99cbe8163f0c.jpg differ diff --git a/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg b/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg new file mode 100644 index 0000000000..9fe38a4697 Binary files /dev/null and b/notes/pics/f0574025-c514-49f5-a591-6d6a71f271f7.jpg differ diff --git a/notes/pics/f1912ba6-6402-4321-9aa8-13d32fd121d1.jpg b/notes/pics/f1912ba6-6402-4321-9aa8-13d32fd121d1.jpg new file mode 100644 index 0000000000..dd8ac0b47d Binary files /dev/null and b/notes/pics/f1912ba6-6402-4321-9aa8-13d32fd121d1.jpg differ diff --git a/notes/pics/f3131e98-8d20-4ff9-b14b-d6803691555844133783.png b/notes/pics/f3131e98-8d20-4ff9-b14b-d6803691555844133783.png new file mode 100644 index 0000000000..361f9dceab Binary files /dev/null and b/notes/pics/f3131e98-8d20-4ff9-b14b-d6803691555844133783.png differ diff --git a/notes/pics/f48883c8-9d8a-494e-99a4-317d8ddb8552.png b/notes/pics/f48883c8-9d8a-494e-99a4-317d8ddb8552.png new file mode 100644 index 0000000000..3b612ba120 Binary files /dev/null and b/notes/pics/f48883c8-9d8a-494e-99a4-317d8ddb8552.png differ diff --git a/notes/pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png b/notes/pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png new file mode 100644 index 0000000000..a40e2616e9 Binary files /dev/null and b/notes/pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png differ diff --git a/notes/pics/f4d534ab-0092-4a81-9e5b-ae889b9a72be.jpg b/notes/pics/f4d534ab-0092-4a81-9e5b-ae889b9a72be.jpg new file mode 100644 index 0000000000..1d85456acf Binary files /dev/null and b/notes/pics/f4d534ab-0092-4a81-9e5b-ae889b9a72be.jpg differ diff --git a/notes/pics/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png b/notes/pics/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png new file mode 100644 index 0000000000..95a55e94c5 Binary files /dev/null and b/notes/pics/f5792051-d9b2-4ca4-a234-a4a2de3d5a57.png differ diff --git a/notes/pics/f579cab0-3d49-4d00-8e14-e9e1669d0f9f.png b/notes/pics/f579cab0-3d49-4d00-8e14-e9e1669d0f9f.png new file mode 100644 index 0000000000..403aaf1785 Binary files /dev/null and b/notes/pics/f579cab0-3d49-4d00-8e14-e9e1669d0f9f.png differ diff --git a/notes/pics/f5cb6028-425d-4939-91eb-cca9dd6b6c6c.jpg b/notes/pics/f5cb6028-425d-4939-91eb-cca9dd6b6c6c.jpg new file mode 100644 index 0000000000..ae2be09ee9 Binary files /dev/null and b/notes/pics/f5cb6028-425d-4939-91eb-cca9dd6b6c6c.jpg differ diff --git a/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png b/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png new file mode 100644 index 0000000000..10ca087997 Binary files /dev/null and b/notes/pics/f61b5419-c94a-4df1-8d4d-aed9ae8cc6d5.png differ diff --git a/notes/pics/f6e146f1-57ad-411b-beb3-770a142164ef.png b/notes/pics/f6e146f1-57ad-411b-beb3-770a142164ef.png new file mode 100644 index 0000000000..8b25cf2045 Binary files /dev/null and b/notes/pics/f6e146f1-57ad-411b-beb3-770a142164ef.png differ diff --git a/notes/pics/f71af66b-0d54-4399-a44b-f47b58321984.png b/notes/pics/f71af66b-0d54-4399-a44b-f47b58321984.png new file mode 100644 index 0000000000..ba64d5ea78 Binary files /dev/null and b/notes/pics/f71af66b-0d54-4399-a44b-f47b58321984.png differ diff --git a/notes/pics/f74144be-857a-40cd-8ec7-87626ef4e20b.png b/notes/pics/f74144be-857a-40cd-8ec7-87626ef4e20b.png new file mode 100644 index 0000000000..33349af1aa Binary files /dev/null and b/notes/pics/f74144be-857a-40cd-8ec7-87626ef4e20b.png differ diff --git a/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c-1572687601927.gif b/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c-1572687601927.gif new file mode 100644 index 0000000000..a4a4a4ebb9 Binary files /dev/null and b/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c-1572687601927.gif differ diff --git a/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c.gif b/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c.gif new file mode 100644 index 0000000000..a4a4a4ebb9 Binary files /dev/null and b/notes/pics/f7c1fea2-c1e7-4d31-94b5-0d9df85e093c.gif differ diff --git a/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png b/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png new file mode 100644 index 0000000000..ccc917939a Binary files /dev/null and b/notes/pics/f7d170a3-e446-4a64-ac2d-cb95028f81a8.png differ diff --git a/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg b/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg new file mode 100644 index 0000000000..1a0253666f Binary files /dev/null and b/notes/pics/f87afe72-c2df-4c12-ac03-9b8d581a8af8.jpg differ diff --git a/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png b/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png new file mode 100644 index 0000000000..c4dbe76450 Binary files /dev/null and b/notes/pics/f8b3f73d-0fda-449f-b55b-fa36b7ac04cd.png differ diff --git a/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg b/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg new file mode 100644 index 0000000000..ca0c309657 Binary files /dev/null and b/notes/pics/f900f266-a323-42b2-bc43-218fdb8811a8.jpg differ diff --git a/notes/pics/f9240aa1-8d48-4959-b28a-7ca45c3e4d91.png b/notes/pics/f9240aa1-8d48-4959-b28a-7ca45c3e4d91.png new file mode 100644 index 0000000000..839e8f4f1f Binary files /dev/null and b/notes/pics/f9240aa1-8d48-4959-b28a-7ca45c3e4d91.png differ diff --git a/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png b/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png new file mode 100644 index 0000000000..a72845ef4b Binary files /dev/null and b/notes/pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png differ diff --git a/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg b/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg new file mode 100644 index 0000000000..96e22a7278 Binary files /dev/null and b/notes/pics/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg differ diff --git a/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg b/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg new file mode 100644 index 0000000000..b2265de923 Binary files /dev/null and b/notes/pics/f9f2a16b-4843-44d1-9759-c745772e9bcf.jpg differ diff --git a/docs/notes/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png b/notes/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png similarity index 100% rename from docs/notes/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png rename to notes/pics/fb327611-7e2b-4f2f-9f5b-38592d408f07.png diff --git a/notes/pics/fb3b8f7a-4293-4a38-aae1-62284db979a3.png b/notes/pics/fb3b8f7a-4293-4a38-aae1-62284db979a3.png new file mode 100644 index 0000000000..6575dc982a Binary files /dev/null and b/notes/pics/fb3b8f7a-4293-4a38-aae1-62284db979a3.png differ diff --git a/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg b/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg new file mode 100644 index 0000000000..f469097180 Binary files /dev/null and b/notes/pics/fb44307f-8e98-4ff7-a918-31dacfa564b4.jpg differ diff --git a/notes/pics/fcc941ec-134b-4dcd-bc86-1702fd305300.gif b/notes/pics/fcc941ec-134b-4dcd-bc86-1702fd305300.gif new file mode 100644 index 0000000000..8296aff52f Binary files /dev/null and b/notes/pics/fcc941ec-134b-4dcd-bc86-1702fd305300.gif differ diff --git a/notes/pics/fdc45a09-f838-4348-8959-d2c793727788.png b/notes/pics/fdc45a09-f838-4348-8959-d2c793727788.png new file mode 100644 index 0000000000..7463b34f07 Binary files /dev/null and b/notes/pics/fdc45a09-f838-4348-8959-d2c793727788.png differ diff --git a/notes/pics/fec3ba89-115a-4cf9-b165-756757644641.png b/notes/pics/fec3ba89-115a-4cf9-b165-756757644641.png new file mode 100644 index 0000000000..1a313d2dfc Binary files /dev/null and b/notes/pics/fec3ba89-115a-4cf9-b165-756757644641.png differ diff --git a/notes/pics/ff5152fc-4ff3-44c4-95d6-1061002c364a.png b/notes/pics/ff5152fc-4ff3-44c4-95d6-1061002c364a.png new file mode 100644 index 0000000000..31831acc8b Binary files /dev/null and b/notes/pics/ff5152fc-4ff3-44c4-95d6-1061002c364a.png differ diff --git a/notes/pics/ffd96b99-8009-487c-8e98-11c9d44ef14f.png b/notes/pics/ffd96b99-8009-487c-8e98-11c9d44ef14f.png new file mode 100644 index 0000000000..b01c1ca0e8 Binary files /dev/null and b/notes/pics/ffd96b99-8009-487c-8e98-11c9d44ef14f.png differ diff --git a/notes/pics/flow.png b/notes/pics/flow.png new file mode 100644 index 0000000000..e83997497c Binary files /dev/null and b/notes/pics/flow.png differ diff --git a/notes/pics/image-20191102011523992.png b/notes/pics/image-20191102011523992.png new file mode 100644 index 0000000000..7a41f19924 Binary files /dev/null and b/notes/pics/image-20191102011523992.png differ diff --git a/notes/pics/image-20191102011531465.png b/notes/pics/image-20191102011531465.png new file mode 100644 index 0000000000..5091e0d6ef Binary files /dev/null and b/notes/pics/image-20191102011531465.png differ diff --git a/notes/pics/image-20191102210342488.png b/notes/pics/image-20191102210342488.png new file mode 100644 index 0000000000..7b332fcef6 Binary files /dev/null and b/notes/pics/image-20191102210342488.png differ diff --git a/notes/pics/image-20191130164420139.png b/notes/pics/image-20191130164420139.png new file mode 100644 index 0000000000..06acaeb637 Binary files /dev/null and b/notes/pics/image-20191130164420139.png differ diff --git a/notes/pics/image-20191130164425351.png b/notes/pics/image-20191130164425351.png new file mode 100644 index 0000000000..06acaeb637 Binary files /dev/null and b/notes/pics/image-20191130164425351.png differ diff --git a/notes/pics/image-20191207165524880.png b/notes/pics/image-20191207165524880.png new file mode 100644 index 0000000000..38ad6d29f7 Binary files /dev/null and b/notes/pics/image-20191207165524880.png differ diff --git a/notes/pics/image-20191207170731112.png b/notes/pics/image-20191207170731112.png new file mode 100644 index 0000000000..5d864ed438 Binary files /dev/null and b/notes/pics/image-20191207170731112.png differ diff --git a/notes/pics/image-20191207171141210.png b/notes/pics/image-20191207171141210.png new file mode 100644 index 0000000000..a5a1b6e8b2 Binary files /dev/null and b/notes/pics/image-20191207171141210.png differ diff --git a/notes/pics/image-20191207171449820.png b/notes/pics/image-20191207171449820.png new file mode 100644 index 0000000000..e8abfe1b78 Binary files /dev/null and b/notes/pics/image-20191207171449820.png differ diff --git a/notes/pics/image-20191207171510150.png b/notes/pics/image-20191207171510150.png new file mode 100644 index 0000000000..e8abfe1b78 Binary files /dev/null and b/notes/pics/image-20191207171510150.png differ diff --git a/notes/pics/image-20191207171512045.png b/notes/pics/image-20191207171512045.png new file mode 100644 index 0000000000..e8abfe1b78 Binary files /dev/null and b/notes/pics/image-20191207171512045.png differ diff --git a/notes/pics/image-20191207171554083.png b/notes/pics/image-20191207171554083.png new file mode 100644 index 0000000000..39bd7fc0eb Binary files /dev/null and b/notes/pics/image-20191207171554083.png differ diff --git a/notes/pics/image-20191207171734324.png b/notes/pics/image-20191207171734324.png new file mode 100644 index 0000000000..b28838cbfa Binary files /dev/null and b/notes/pics/image-20191207171734324.png differ diff --git a/notes/pics/image-20191207173012642.png b/notes/pics/image-20191207173012642.png new file mode 100644 index 0000000000..15c0a34a0b Binary files /dev/null and b/notes/pics/image-20191207173012642.png differ diff --git a/notes/pics/image-20191207174600729.png b/notes/pics/image-20191207174600729.png new file mode 100644 index 0000000000..080a7d8f30 Binary files /dev/null and b/notes/pics/image-20191207174600729.png differ diff --git a/notes/pics/image-20191207175048914.png b/notes/pics/image-20191207175048914.png new file mode 100644 index 0000000000..fc69c68f8c Binary files /dev/null and b/notes/pics/image-20191207175048914.png differ diff --git a/notes/pics/image-20191207175109320.png b/notes/pics/image-20191207175109320.png new file mode 100644 index 0000000000..21c9305dc7 Binary files /dev/null and b/notes/pics/image-20191207175109320.png differ diff --git a/notes/pics/image-20191207180652772.png b/notes/pics/image-20191207180652772.png new file mode 100644 index 0000000000..2246165acb Binary files /dev/null and b/notes/pics/image-20191207180652772.png differ diff --git a/notes/pics/image-20191207180710529.png b/notes/pics/image-20191207180710529.png new file mode 100644 index 0000000000..2246165acb Binary files /dev/null and b/notes/pics/image-20191207180710529.png differ diff --git a/notes/pics/image-20191207205105044.png b/notes/pics/image-20191207205105044.png new file mode 100644 index 0000000000..2c502fe021 Binary files /dev/null and b/notes/pics/image-20191207205105044.png differ diff --git a/notes/pics/image-20191207205339747.png b/notes/pics/image-20191207205339747.png new file mode 100644 index 0000000000..5893249e0f Binary files /dev/null and b/notes/pics/image-20191207205339747.png differ diff --git a/notes/pics/image-20191207205737391.png b/notes/pics/image-20191207205737391.png new file mode 100644 index 0000000000..fb9942f343 Binary files /dev/null and b/notes/pics/image-20191207205737391.png differ diff --git a/notes/pics/image-20191207205825456.png b/notes/pics/image-20191207205825456.png new file mode 100644 index 0000000000..f5d0d82fbe Binary files /dev/null and b/notes/pics/image-20191207205825456.png differ diff --git a/notes/pics/image-20191207210118304.png b/notes/pics/image-20191207210118304.png new file mode 100644 index 0000000000..957aaf2247 Binary files /dev/null and b/notes/pics/image-20191207210118304.png differ diff --git a/notes/pics/image-20191207210437023.png b/notes/pics/image-20191207210437023.png new file mode 100644 index 0000000000..fb4ae3d7ac Binary files /dev/null and b/notes/pics/image-20191207210437023.png differ diff --git a/notes/pics/image-20191207210802815.png b/notes/pics/image-20191207210802815.png new file mode 100644 index 0000000000..873bf35b58 Binary files /dev/null and b/notes/pics/image-20191207210802815.png differ diff --git a/notes/pics/image-20191207211851299.png b/notes/pics/image-20191207211851299.png new file mode 100644 index 0000000000..d9a2346f57 Binary files /dev/null and b/notes/pics/image-20191207211851299.png differ diff --git a/notes/pics/image-20191207213523777.png b/notes/pics/image-20191207213523777.png new file mode 100644 index 0000000000..7a44bb21a6 Binary files /dev/null and b/notes/pics/image-20191207213523777.png differ diff --git a/notes/pics/image-20191207214442687.png b/notes/pics/image-20191207214442687.png new file mode 100644 index 0000000000..d4babb566d Binary files /dev/null and b/notes/pics/image-20191207214442687.png differ diff --git a/notes/pics/image-20191207220440451.png b/notes/pics/image-20191207220440451.png new file mode 100644 index 0000000000..fbb4854e14 Binary files /dev/null and b/notes/pics/image-20191207220440451.png differ diff --git a/notes/pics/image-20191207220831843.png b/notes/pics/image-20191207220831843.png new file mode 100644 index 0000000000..0d172aeb45 Binary files /dev/null and b/notes/pics/image-20191207220831843.png differ diff --git a/notes/pics/image-20191207221128997.png b/notes/pics/image-20191207221128997.png new file mode 100644 index 0000000000..c3b256ca22 Binary files /dev/null and b/notes/pics/image-20191207221128997.png differ diff --git a/notes/pics/image-20191207221313819.png b/notes/pics/image-20191207221313819.png new file mode 100644 index 0000000000..38fd7d695e Binary files /dev/null and b/notes/pics/image-20191207221313819.png differ diff --git a/notes/pics/image-20191207221744244.png b/notes/pics/image-20191207221744244.png new file mode 100644 index 0000000000..32c0ded247 Binary files /dev/null and b/notes/pics/image-20191207221744244.png differ diff --git a/notes/pics/image-20191207221920368.png b/notes/pics/image-20191207221920368.png new file mode 100644 index 0000000000..c1df6edb94 Binary files /dev/null and b/notes/pics/image-20191207221920368.png differ diff --git a/notes/pics/image-20191207222102010.png b/notes/pics/image-20191207222102010.png new file mode 100644 index 0000000000..a0d37b6940 Binary files /dev/null and b/notes/pics/image-20191207222102010.png differ diff --git a/notes/pics/image-20191207222134306.png b/notes/pics/image-20191207222134306.png new file mode 100644 index 0000000000..3a30fe5917 Binary files /dev/null and b/notes/pics/image-20191207222134306.png differ diff --git a/notes/pics/image-20191207222237925.png b/notes/pics/image-20191207222237925.png new file mode 100644 index 0000000000..4199f624ca Binary files /dev/null and b/notes/pics/image-20191207222237925.png differ diff --git a/notes/pics/image-20191207223334985.png b/notes/pics/image-20191207223334985.png new file mode 100644 index 0000000000..74377d93f6 Binary files /dev/null and b/notes/pics/image-20191207223334985.png differ diff --git a/notes/pics/image-20191207223400787.png b/notes/pics/image-20191207223400787.png new file mode 100644 index 0000000000..b716c84e33 Binary files /dev/null and b/notes/pics/image-20191207223400787.png differ diff --git a/notes/pics/image-20191208012527591.png b/notes/pics/image-20191208012527591.png new file mode 100644 index 0000000000..c0b369267c Binary files /dev/null and b/notes/pics/image-20191208012527591.png differ diff --git a/notes/pics/image-20191208164808217.png b/notes/pics/image-20191208164808217.png new file mode 100644 index 0000000000..36d8601283 Binary files /dev/null and b/notes/pics/image-20191208164808217.png differ diff --git a/notes/pics/image-20191208171445674.png b/notes/pics/image-20191208171445674.png new file mode 100644 index 0000000000..e1ad39a85d Binary files /dev/null and b/notes/pics/image-20191208171445674.png differ diff --git a/notes/pics/image-20191208194906798.png b/notes/pics/image-20191208194906798.png new file mode 100644 index 0000000000..cf99053055 Binary files /dev/null and b/notes/pics/image-20191208194906798.png differ diff --git a/notes/pics/image-20191208195354093.png b/notes/pics/image-20191208195354093.png new file mode 100644 index 0000000000..61abe5eef6 Binary files /dev/null and b/notes/pics/image-20191208195354093.png differ diff --git a/notes/pics/image-20191208195715031.png b/notes/pics/image-20191208195715031.png new file mode 100644 index 0000000000..68f43ca271 Binary files /dev/null and b/notes/pics/image-20191208195715031.png differ diff --git a/notes/pics/image-20191208195718396.png b/notes/pics/image-20191208195718396.png new file mode 100644 index 0000000000..68f43ca271 Binary files /dev/null and b/notes/pics/image-20191208195718396.png differ diff --git a/notes/pics/image-20191208195941661.png b/notes/pics/image-20191208195941661.png new file mode 100644 index 0000000000..e949e9ac21 Binary files /dev/null and b/notes/pics/image-20191208195941661.png differ diff --git a/notes/pics/image-20191208200014395.png b/notes/pics/image-20191208200014395.png new file mode 100644 index 0000000000..7c34ae425b Binary files /dev/null and b/notes/pics/image-20191208200014395.png differ diff --git a/notes/pics/image-20191208200543923.png b/notes/pics/image-20191208200543923.png new file mode 100644 index 0000000000..d3ec329ec8 Binary files /dev/null and b/notes/pics/image-20191208200543923.png differ diff --git a/notes/pics/image-20191208200656794.png b/notes/pics/image-20191208200656794.png new file mode 100644 index 0000000000..7b83d3ccbf Binary files /dev/null and b/notes/pics/image-20191208200656794.png differ diff --git a/notes/pics/image-20191208201230411.png b/notes/pics/image-20191208201230411.png new file mode 100644 index 0000000000..f1d9a936f7 Binary files /dev/null and b/notes/pics/image-20191208201230411.png differ diff --git a/notes/pics/image-20191208201554687.png b/notes/pics/image-20191208201554687.png new file mode 100644 index 0000000000..fb1c03e9de Binary files /dev/null and b/notes/pics/image-20191208201554687.png differ diff --git a/notes/pics/image-20191208201611499.png b/notes/pics/image-20191208201611499.png new file mode 100644 index 0000000000..2443caa8cd Binary files /dev/null and b/notes/pics/image-20191208201611499.png differ diff --git a/notes/pics/image-20191208201718073.png b/notes/pics/image-20191208201718073.png new file mode 100644 index 0000000000..8f6444f977 Binary files /dev/null and b/notes/pics/image-20191208201718073.png differ diff --git a/notes/pics/image-20191208201812357.png b/notes/pics/image-20191208201812357.png new file mode 100644 index 0000000000..f16da575ba Binary files /dev/null and b/notes/pics/image-20191208201812357.png differ diff --git a/notes/pics/image-20191208202110976.png b/notes/pics/image-20191208202110976.png new file mode 100644 index 0000000000..1af266d525 Binary files /dev/null and b/notes/pics/image-20191208202110976.png differ diff --git a/notes/pics/image-20191208202151457.png b/notes/pics/image-20191208202151457.png new file mode 100644 index 0000000000..c31e956c10 Binary files /dev/null and b/notes/pics/image-20191208202151457.png differ diff --git a/notes/pics/image-20191208202707905.png b/notes/pics/image-20191208202707905.png new file mode 100644 index 0000000000..0fa151dbb9 Binary files /dev/null and b/notes/pics/image-20191208202707905.png differ diff --git a/notes/pics/image-20191208202753553.png b/notes/pics/image-20191208202753553.png new file mode 100644 index 0000000000..332fb56204 Binary files /dev/null and b/notes/pics/image-20191208202753553.png differ diff --git a/notes/pics/image-20191208202926584.png b/notes/pics/image-20191208202926584.png new file mode 100644 index 0000000000..336eb9f19b Binary files /dev/null and b/notes/pics/image-20191208202926584.png differ diff --git a/notes/pics/image-20191208203010540.png b/notes/pics/image-20191208203010540.png new file mode 100644 index 0000000000..55fcd19129 Binary files /dev/null and b/notes/pics/image-20191208203010540.png differ diff --git a/notes/pics/image-20191208203034705.png b/notes/pics/image-20191208203034705.png new file mode 100644 index 0000000000..d679257fe9 Binary files /dev/null and b/notes/pics/image-20191208203034705.png differ diff --git a/notes/pics/image-20191208203112400.png b/notes/pics/image-20191208203112400.png new file mode 100644 index 0000000000..5e35cbd8fe Binary files /dev/null and b/notes/pics/image-20191208203112400.png differ diff --git a/notes/pics/image-20191208203142527.png b/notes/pics/image-20191208203142527.png new file mode 100644 index 0000000000..fcccd15642 Binary files /dev/null and b/notes/pics/image-20191208203142527.png differ diff --git a/notes/pics/image-20191208203219927.png b/notes/pics/image-20191208203219927.png new file mode 100644 index 0000000000..7a1cabb96d Binary files /dev/null and b/notes/pics/image-20191208203219927.png differ diff --git a/notes/pics/image-20191208203616628.png b/notes/pics/image-20191208203616628.png new file mode 100644 index 0000000000..e4f6d9c776 Binary files /dev/null and b/notes/pics/image-20191208203616628.png differ diff --git a/notes/pics/image-20191208203639712.png b/notes/pics/image-20191208203639712.png new file mode 100644 index 0000000000..e5bbd0799e Binary files /dev/null and b/notes/pics/image-20191208203639712.png differ diff --git a/notes/pics/image-20191208204118932.png b/notes/pics/image-20191208204118932.png new file mode 100644 index 0000000000..c63576512b Binary files /dev/null and b/notes/pics/image-20191208204118932.png differ diff --git a/notes/pics/image-20191208220948084.png b/notes/pics/image-20191208220948084.png new file mode 100644 index 0000000000..ca0da5fdcb Binary files /dev/null and b/notes/pics/image-20191208220948084.png differ diff --git a/notes/pics/image-20191208224757855.png b/notes/pics/image-20191208224757855.png new file mode 100644 index 0000000000..d6d554bfc2 Binary files /dev/null and b/notes/pics/image-20191208224757855.png differ diff --git a/notes/pics/image-20191208225301973.png b/notes/pics/image-20191208225301973.png new file mode 100644 index 0000000000..f0f3c28115 Binary files /dev/null and b/notes/pics/image-20191208225301973.png differ diff --git a/notes/pics/image-20191208232221265.png b/notes/pics/image-20191208232221265.png new file mode 100644 index 0000000000..c1759aba1a Binary files /dev/null and b/notes/pics/image-20191208232221265.png differ diff --git a/notes/pics/image-20191208233940066.png b/notes/pics/image-20191208233940066.png new file mode 100644 index 0000000000..3a6145c173 Binary files /dev/null and b/notes/pics/image-20191208233940066.png differ diff --git a/notes/pics/image-20191208234825587.png b/notes/pics/image-20191208234825587.png new file mode 100644 index 0000000000..6f596da001 Binary files /dev/null and b/notes/pics/image-20191208234825587.png differ diff --git a/notes/pics/image-20191208234948205.png b/notes/pics/image-20191208234948205.png new file mode 100644 index 0000000000..7466b3e0e4 Binary files /dev/null and b/notes/pics/image-20191208234948205.png differ diff --git a/notes/pics/image-20191208235258643.png b/notes/pics/image-20191208235258643.png new file mode 100644 index 0000000000..6c5471cc2e Binary files /dev/null and b/notes/pics/image-20191208235258643.png differ diff --git a/notes/pics/image-20191209001024618.png b/notes/pics/image-20191209001024618.png new file mode 100644 index 0000000000..9e4701495f Binary files /dev/null and b/notes/pics/image-20191209001024618.png differ diff --git a/notes/pics/image-20191209001038024.png b/notes/pics/image-20191209001038024.png new file mode 100644 index 0000000000..9e4701495f Binary files /dev/null and b/notes/pics/image-20191209001038024.png differ diff --git a/notes/pics/image-20191209002818626.png b/notes/pics/image-20191209002818626.png new file mode 100644 index 0000000000..7c67df166c Binary files /dev/null and b/notes/pics/image-20191209002818626.png differ diff --git a/notes/pics/image-20191209003356573.png b/notes/pics/image-20191209003356573.png new file mode 100644 index 0000000000..7bb33b3d55 Binary files /dev/null and b/notes/pics/image-20191209003356573.png differ diff --git a/notes/pics/image-20191209003358850.png b/notes/pics/image-20191209003358850.png new file mode 100644 index 0000000000..7bb33b3d55 Binary files /dev/null and b/notes/pics/image-20191209003358850.png differ diff --git a/notes/pics/image-20191209003451616.png b/notes/pics/image-20191209003451616.png new file mode 100644 index 0000000000..10fc28f688 Binary files /dev/null and b/notes/pics/image-20191209003451616.png differ diff --git a/notes/pics/image-20191209003453268.png b/notes/pics/image-20191209003453268.png new file mode 100644 index 0000000000..10fc28f688 Binary files /dev/null and b/notes/pics/image-20191209003453268.png differ diff --git a/notes/pics/image-20191210004132894.png b/notes/pics/image-20191210004132894.png new file mode 100644 index 0000000000..21d1a0bb27 Binary files /dev/null and b/notes/pics/image-20191210004132894.png differ diff --git a/notes/pics/image-20191212011250613.png b/notes/pics/image-20191212011250613.png new file mode 100644 index 0000000000..1b11dced3a Binary files /dev/null and b/notes/pics/image-20191212011250613.png differ diff --git a/notes/pics/image-20191212011410374.png b/notes/pics/image-20191212011410374.png new file mode 100644 index 0000000000..477749457d Binary files /dev/null and b/notes/pics/image-20191212011410374.png differ diff --git a/notes/pics/image-20191212011746080.png b/notes/pics/image-20191212011746080.png new file mode 100644 index 0000000000..2ae0d0e042 Binary files /dev/null and b/notes/pics/image-20191212011746080.png differ diff --git a/notes/pics/image-20191212011747967.png b/notes/pics/image-20191212011747967.png new file mode 100644 index 0000000000..2ae0d0e042 Binary files /dev/null and b/notes/pics/image-20191212011747967.png differ diff --git a/notes/pics/image-20191213001359994.png b/notes/pics/image-20191213001359994.png new file mode 100644 index 0000000000..9c5fd291cf Binary files /dev/null and b/notes/pics/image-20191213001359994.png differ diff --git a/notes/pics/image-20191213001413206.png b/notes/pics/image-20191213001413206.png new file mode 100644 index 0000000000..9c5fd291cf Binary files /dev/null and b/notes/pics/image-20191213001413206.png differ diff --git a/notes/pics/image-20201101234314679.png b/notes/pics/image-20201101234314679.png new file mode 100644 index 0000000000..8ed9e1740b Binary files /dev/null and b/notes/pics/image-20201101234314679.png differ diff --git a/notes/pics/image-20201101234335837.png b/notes/pics/image-20201101234335837.png new file mode 100644 index 0000000000..cd60b7a8ce Binary files /dev/null and b/notes/pics/image-20201101234335837.png differ diff --git a/notes/pics/image-20201102012333804.png b/notes/pics/image-20201102012333804.png new file mode 100644 index 0000000000..46f37ddb78 Binary files /dev/null and b/notes/pics/image-20201102012333804.png differ diff --git a/notes/pics/image-20201103021112251.png b/notes/pics/image-20201103021112251.png new file mode 100644 index 0000000000..6ac796e40a Binary files /dev/null and b/notes/pics/image-20201103021112251.png differ diff --git a/notes/pics/image-20201104010349296.png b/notes/pics/image-20201104010349296.png new file mode 100644 index 0000000000..1c62346640 Binary files /dev/null and b/notes/pics/image-20201104010349296.png differ diff --git a/notes/pics/image-20201104010609223.png b/notes/pics/image-20201104010609223.png new file mode 100644 index 0000000000..8f4ee2c4b6 Binary files /dev/null and b/notes/pics/image-20201104010609223.png differ diff --git a/notes/pics/image-20201104013936126.png b/notes/pics/image-20201104013936126.png new file mode 100644 index 0000000000..8792cb4c34 Binary files /dev/null and b/notes/pics/image-20201104013936126.png differ diff --git a/notes/pics/image-20201104020702453.png b/notes/pics/image-20201104020702453.png new file mode 100644 index 0000000000..81ee0ec442 Binary files /dev/null and b/notes/pics/image-20201104020702453.png differ diff --git a/notes/pics/image-20201105004127554.png b/notes/pics/image-20201105004127554.png new file mode 100644 index 0000000000..8c39b9e219 Binary files /dev/null and b/notes/pics/image-20201105004127554.png differ diff --git a/notes/pics/image-20201105012506187.png b/notes/pics/image-20201105012506187.png new file mode 100644 index 0000000000..98e193dd3d Binary files /dev/null and b/notes/pics/image-20201105012506187.png differ diff --git a/notes/pics/inode_with_signatures.jpg b/notes/pics/inode_with_signatures.jpg new file mode 100644 index 0000000000..ed6b0e3026 Binary files /dev/null and b/notes/pics/inode_with_signatures.jpg differ diff --git a/notes/pics/linux-filesystem.png b/notes/pics/linux-filesystem.png new file mode 100644 index 0000000000..b56fe50be8 Binary files /dev/null and b/notes/pics/linux-filesystem.png differ diff --git a/notes/pics/master-slave-proxy.png b/notes/pics/master-slave-proxy.png new file mode 100644 index 0000000000..6f47b010e6 Binary files /dev/null and b/notes/pics/master-slave-proxy.png differ diff --git a/notes/pics/master-slave.png b/notes/pics/master-slave.png new file mode 100644 index 0000000000..fa4a1e178c Binary files /dev/null and b/notes/pics/master-slave.png differ diff --git a/docs/notes/pics/network-of-networks.gif b/notes/pics/network-of-networks.gif similarity index 100% rename from docs/notes/pics/network-of-networks.gif rename to notes/pics/network-of-networks.gif diff --git a/docs/notes/pics/ssl-offloading.jpg b/notes/pics/ssl-offloading.jpg similarity index 100% rename from docs/notes/pics/ssl-offloading.jpg rename to notes/pics/ssl-offloading.jpg diff --git a/docs/notes/pics/tGPV0.png b/notes/pics/tGPV0.png similarity index 100% rename from docs/notes/pics/tGPV0.png rename to notes/pics/tGPV0.png diff --git "a/notes/pics/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2454.png" "b/notes/pics/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2454.png" new file mode 100644 index 0000000000..222cde8647 Binary files /dev/null and "b/notes/pics/\345\205\254\344\274\227\345\217\267\346\265\267\346\212\2454.png" differ diff --git "a/docs/notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" "b/notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" similarity index 91% rename from "docs/notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" rename to "notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" index 724770a94b..5c1c67b625 100644 --- "a/docs/notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" +++ "b/notes/\344\273\243\347\240\201\345\217\257\350\257\273\346\200\247.md" @@ -48,11 +48,9 @@ - 用 min、max 表示数量范围; - 用 first、last 表示访问空间的包含范围; -

- - begin、end 表示访问空间的排除范围,即 end 不包含尾部。 -

+

# 四、良好的代码风格 @@ -70,7 +68,7 @@ int c = 111; // 注释 # 五、为何编写注释 -阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是并不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。 +阅读代码首先会注意到注释,如果注释没太大作用,那么就会浪费代码阅读的时间。那些能直接看出含义的代码不需要写注释,特别是不需要为每个方法都加上注释,比如那些简单的 getter 和 setter 方法,为这些方法写注释反而让代码可读性更差。 不能因为有注释就随便起个名字,而是争取起个好名字而不写注释。 @@ -160,7 +158,7 @@ if (!(a || b)) { # 九、变量与可读性 -**去除控制流变量** 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 +**去除控制流变量** 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 ```java boolean done = false; @@ -182,7 +180,7 @@ while(/* condition */) { } ``` -**减小变量作用域** 。作用域越小,越容易定位到变量所有使用的地方。 +**减小变量作用域** 。作用域越小,越容易定位到变量所有使用的地方。 JavaScript 可以用闭包减小作用域。以下代码中 submit_form 是函数变量,submitted 变量控制函数不会被提交两次。第一个实现中 submitted 是全局变量,第二个实现把 submitted 放到匿名函数中,从而限制了起作用域范围。 @@ -212,7 +210,7 @@ JavaScript 中没有用 var 声明的变量都是全局变量,而全局变量 变量定义的位置应当离它使用的位置最近。 -**实例解析** +**实例解析** 在一个网页中有以下文本输入字段: @@ -331,9 +329,3 @@ public int findClostElement(int[] arr) { # 参考资料 - Dustin, Boswell, Trevor, 等. 编写可读代码的艺术 [M]. 机械工业出版社, 2012. - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" "b/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" new file mode 100644 index 0000000000..a30b537bde --- /dev/null +++ "b/notes/\344\273\243\347\240\201\351\243\216\346\240\274\350\247\204\350\214\203.md" @@ -0,0 +1,4 @@ +- [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) +- [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) +- [阿里巴巴Java开发手册](https://github.com/alibaba/p3c) + diff --git "a/notes/\345\210\206\345\270\203\345\274\217.md" "b/notes/\345\210\206\345\270\203\345\274\217.md" new file mode 100644 index 0000000000..318001b98d --- /dev/null +++ "b/notes/\345\210\206\345\270\203\345\274\217.md" @@ -0,0 +1,350 @@ +# 分布式 + +* [分布式](#分布式) + * [一、分布式锁](#一分布式锁) + * [数据库的唯一索引](#数据库的唯一索引) + * [Redis 的 SETNX 指令](#redis-的-setnx-指令) + * [Redis 的 RedLock 算法](#redis-的-redlock-算法) + * [Zookeeper 的有序节点](#zookeeper-的有序节点) + * [二、分布式事务](#二分布式事务) + * [2PC](#2pc) + * [本地消息表](#本地消息表) + * [三、CAP](#三cap) + * [一致性](#一致性) + * [可用性](#可用性) + * [分区容忍性](#分区容忍性) + * [权衡](#权衡) + * [四、BASE](#四base) + * [基本可用](#基本可用) + * [软状态](#软状态) + * [最终一致性](#最终一致性) + * [五、Paxos](#五paxos) + * [执行过程](#执行过程) + * [约束条件](#约束条件) + * [六、Raft](#六raft) + * [单个 Candidate 的竞选](#单个-candidate-的竞选) + * [多个 Candidate 竞选](#多个-candidate-竞选) + * [数据同步](#数据同步) + * [参考](#参考) + + + +## 一、分布式锁 + +在单机场景下,可以使用语言的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。 + +阻塞锁通常使用互斥量来实现: + +- 互斥量为 0 表示有其它进程在使用锁,此时处于锁定状态; +- 互斥量为 1 表示未锁定状态。 + +1 和 0 可以用一个整型值表示,也可以用某个数据是否存在表示。 + +### 数据库的唯一索引 + +获得锁时向表中插入一条记录,释放锁时删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否处于锁定状态。 + +存在以下几个问题: + +- 锁没有失效时间,解锁失败的话其它进程无法再获得该锁; +- 只能是非阻塞锁,插入失败直接就报错了,无法重试; +- 不可重入,已经获得锁的进程也必须重新获取锁。 + +### Redis 的 SETNX 指令 + +使用 SETNX(set if not exist)指令插入一个键值对,如果 Key 已经存在,那么会返回 False,否则插入成功并返回 True。 + +SETNX 指令和数据库的唯一索引类似,保证了只存在一个 Key 的键值对,那么可以用一个 Key 的键值对是否存在来判断是否存于锁定状态。 + +EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了数据库唯一索引实现方式中释放锁失败的问题。 + +### Redis 的 RedLock 算法 + +使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时仍然可用。 + +- 尝试从 N 个互相独立 Redis 实例获取锁; +- 计算获取锁消耗的时间,只有时间小于锁的过期时间,并且从大多数(N / 2 + 1)实例上获取了锁,才认为获取锁成功; +- 如果获取锁失败,就到每个实例上释放锁。 + +### Zookeeper 的有序节点 + +#### 1. Zookeeper 抽象模型 + +Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节点为 /app1。 + +

+ +#### 2. 节点类型 + +- 永久节点:不会因为会话结束或者超时而消失; +- 临时节点:如果会话结束或者超时就会消失; +- 有序节点:会在节点名的后面加一个数字后缀,并且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,以此类推。 + +#### 3. 监听器 + +为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。 + +#### 4. 分布式锁实现 + +- 创建一个锁目录 /lock; +- 当一个客户端需要获取锁时,在 /lock 下创建临时的且有序的子节点; +- 客户端获取 /lock 下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁;否则监听自己的前一个子节点,获得子节点的变更通知后重复此步骤直至获得锁; +- 执行业务代码,完成后,删除对应的子节点。 + +#### 5. 会话超时 + +如果一个已经获得锁的会话超时了,因为创建的是临时节点,所以该会话对应的临时节点会被删除,其它会话就可以获得锁了。可以看到,这种实现方式不会出现数据库的唯一索引实现方式释放锁失败的问题。 + +#### 6. 羊群效应 + +一个节点未获得锁,只需要监听自己的前一个子节点,这是因为如果监听所有的子节点,那么任意一个子节点状态改变,其它所有子节点都会收到通知(羊群效应,一只羊动起来,其它羊也会一哄而上),而我们只希望它的后一个子节点收到通知。 + +## 二、分布式事务 + +指事务的操作位于不同的节点上,需要保证事务的 ACID 特性。 + +例如在下单场景下,库存和订单如果不在同一个节点上,就涉及分布式事务。 + +分布式锁和分布式事务区别: + +- 锁问题的关键在于进程操作的互斥关系,例如多个进程同时修改账户的余额,如果没有互斥关系则会导致该账户的余额不正确。 +- 而事务问题的关键则在于事务涉及的一系列操作需要满足 ACID 特性,例如要满足原子性操作则需要这些操作要么都执行,要么都不执行。 + +### 2PC + +两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。 + +#### 1. 运行过程 + +##### 1.1 准备阶段 + +协调者询问参与者事务是否执行成功,参与者发回事务执行结果。询问可以看成一种投票,需要参与者都同意才能执行。 + +

+ +##### 1.2 提交阶段 + +如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。 + +需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。 + +

+ +#### 2. 存在的问题 + +##### 2.1 同步阻塞 + +所有事务参与者在等待其它参与者响应的时候都处于同步阻塞等待状态,无法进行其它操作。 + +##### 2.2 单点问题 + +协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在提交阶段发生故障,所有参与者会一直同步阻塞等待,无法完成其它操作。 + +##### 2.3 数据不一致 + +在提交阶段,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。 + +##### 2.4 太过保守 + +任意一个节点失败就会导致整个事务失败,没有完善的容错机制。 + +### 本地消息表 + +本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且使用了消息队列来保证最终一致性。 + +1. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。 +2. 之后将本地消息表中的消息转发到消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 +3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。 + +

+ + +## 三、CAP + +分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容忍性(P:Partition Tolerance),最多只能同时满足其中两项。 + +

+ +### 一致性 + +一致性指的是多个数据副本是否能保持一致的特性,在一致性的条件下,系统在执行数据更新操作之后能够从一致性状态转移到另一个一致性状态。 + +对系统的一个数据更新成功之后,如果所有用户都能够读取到最新的值,该系统就被认为具有强一致性。 + +### 可用性 + +可用性指分布式系统在面对各种异常时可以提供正常服务的能力,可以用系统可用时间占总时间的比值来衡量,4 个 9 的可用性表示系统 99.99% 的时间是可用的。 + +在可用性条件下,要求系统提供的服务一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。 + +### 分区容忍性 + +网络分区指分布式系统中的节点被划分为多个区域,每个区域内部可以通信,但是区域之间无法通信。 + +在分区容忍性条件下,分布式系统在遇到任何网络分区故障的时候,仍然需要能对外提供一致性和可用性的服务,除非是整个网络环境都发生了故障。 + +### 权衡 + +在分布式系统中,分区容忍性必不可少,因为需要总是假设网络是不可靠的。因此,CAP 理论实际上是要在可用性和一致性之间做权衡。 + +可用性和一致性往往是冲突的,很难使它们同时满足。在多个节点之间进行数据同步时, + +- 为了保证一致性(CP),不能访问未同步完成的节点,也就失去了部分可用性; +- 为了保证可用性(AP),允许读取所有节点的数据,但是数据可能不一致。 + +## 四、BASE + +BASE 是基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually Consistent)三个短语的缩写。 + +BASE 理论是对 CAP 中一致性和可用性权衡的结果,它的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。 + + +### 基本可用 + +指分布式系统在出现故障的时候,保证核心可用,允许损失部分可用性。 + +例如,电商在做促销时,为了保证购物系统的稳定性,部分消费者可能会被引导到一个降级的页面。 + +### 软状态 + +指允许系统中的数据存在中间状态,并认为该中间状态不会影响系统整体可用性,即允许系统不同节点的数据副本之间进行同步的过程存在时延。 + +### 最终一致性 + +最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能达到一致的状态。 + +ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 要求最终一致性,通过牺牲强一致性来达到可用性,通常运用在大型分布式系统中。 + +在实际的分布式场景中,不同业务单元和组件对一致性的要求是不同的,因此 ACID 和 BASE 往往会结合在一起使用。 + +## 五、Paxos + +用于达成共识性问题,即对多个节点产生的值,该算法能保证只选出唯一一个值。 + +主要有三类节点: + +- 提议者(Proposer):提议一个值; +- 接受者(Acceptor):对每个提议进行投票; +- 告知者(Learner):被告知投票的结果,不参与投票过程。 + +

+ +### 执行过程 + +规定一个提议包含两个字段:[n, v],其中 n 为序号(具有唯一性),v 为提议值。 + +#### 1. Prepare 阶段 + +下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送 Prepare 请求。 + +

+ +当 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n1, v1],并且之前还未接收过 Prepare 请求,那么发送一个 Prepare 响应,设置当前接收到的提议为 [n1, v1],并且保证以后不会再接受序号小于 n1 的提议。 + +如下图,Acceptor X 在收到 [n=2, v=8] 的 Prepare 请求时,由于之前没有接收过提议,因此就发送一个 [no previous] 的 Prepare 响应,设置当前接收到的提议为 [n=2, v=8],并且保证以后不会再接受序号小于 2 的提议。其它的 Acceptor 类似。 + +

+ +如果 Acceptor 接收到一个 Prepare 请求,包含的提议为 [n2, v2],并且之前已经接收过提议 [n1, v1]。如果 n1 \> n2,那么就丢弃该提议请求;否则,发送 Prepare 响应,该 Prepare 响应包含之前已经接收过的提议 [n1, v1],设置当前接收到的提议为 [n2, v2],并且保证以后不会再接受序号小于 n2 的提议。 + +如下图,Acceptor Z 收到 Proposer A 发来的 [n=2, v=8] 的 Prepare 请求,由于之前已经接收过 [n=4, v=5] 的提议,并且 n \> 2,因此就抛弃该提议请求;Acceptor X 收到 Proposer B 发来的 [n=4, v=5] 的 Prepare 请求,因为之前接收到的提议为 [n=2, v=8],并且 2 \<= 4,因此就发送 [n=2, v=8] 的 Prepare 响应,设置当前接收到的提议为 [n=4, v=5],并且保证以后不会再接受序号小于 4 的提议。Acceptor Y 类似。 + +

+ +#### 2. Accept 阶段 + +当一个 Proposer 接收到超过一半 Acceptor 的 Prepare 响应时,就可以发送 Accept 请求。 + +Proposer A 接收到两个 Prepare 响应之后,就发送 [n=2, v=8] Accept 请求。该 Accept 请求会被所有 Acceptor 丢弃,因为此时所有 Acceptor 都保证不接受序号小于 4 的提议。 + +Proposer B 过后也收到了两个 Prepare 响应,因此也开始发送 Accept 请求。需要注意的是,Accept 请求的 v 需要取它收到的最大提议编号对应的 v 值,也就是 8。因此它发送 [n=4, v=8] 的 Accept 请求。 + +

+ +#### 3. Learn 阶段 + +Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送 Learn 提议给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。 + +

+ +### 约束条件 + +#### 1\. 正确性 + +指只有一个提议值会生效。 + +因为 Paxos 协议要求每个生效的提议被多数 Acceptor 接收,并且 Acceptor 不会接受两个不同的提议,因此可以保证正确性。 + +#### 2\. 可终止性 + +指最后总会有一个提议生效。 + +Paxos 协议能够让 Proposer 发送的提议朝着能被大多数 Acceptor 接受的那个提议靠拢,因此能够保证可终止性。 + +## 六、Raft + +Raft 也是分布式一致性协议,主要是用来竞选主节点。 + +- [Raft: Understandable Distributed Consensus](http://thesecretlivesofdata.com/raft) + +### 单个 Candidate 的竞选 + +有三种节点:Follower、Candidate 和 Leader。Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时时间,一般为 150ms\~300ms,如果在这个时间内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。 + +- 下图展示一个分布式系统的最初阶段,此时只有 Follower 没有 Leader。Node A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。 + +

+ +- 此时 Node A 发送投票请求给其它所有节点。 + +

+ +- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。 + +

+ +- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。 + +

+ +### 多个 Candidate 竞选 + +- 如果有多个 Follower 成为 Candidate,并且所获得票数相同,那么就需要重新开始投票。例如下图中 Node B 和 Node D 都获得两票,需要重新开始投票。 + +

+ +- 由于每个节点设置的随机竞选超时时间不同,因此下一次再次出现多个 Candidate 并获得同样票数的概率很低。 + +

+ +### 数据同步 + +- 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。 + +

+ +- Leader 会把修改复制到所有 Follower。 + +

+ +- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。 + +

+ +- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。 + +

+ +## 参考 + +- 倪超. 从 Paxos 到 ZooKeeper : 分布式一致性原理与实践 [M]. 电子工业出版社, 2015. +- [Distributed locks with Redis](https://redis.io/topics/distlock) +- [浅谈分布式锁](http://www.linkedkeeper.com/detail/blog.action?bid=1023) +- [基于 Zookeeper 的分布式锁](http://www.dengshenyu.com/java/%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F/2017/10/23/zookeeper-distributed-lock.html) +- [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html) +- [分布式系统的事务处理](https://coolshell.cn/articles/10910.html) +- [深入理解分布式事务](https://juejin.im/entry/577c6f220a2b5800573492be) +- [What is CAP theorem in distributed database system?](http://www.colooshiki.com/index.php/2017/04/20/what-is-cap-theorem-in-distributed-database-system/) +- [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) +- [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) + diff --git "a/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" "b/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" new file mode 100644 index 0000000000..9660eb1566 --- /dev/null +++ "b/notes/\345\211\221\346\214\207 Offer \351\242\230\350\247\243 - \347\233\256\345\275\225.md" @@ -0,0 +1,126 @@ +# 剑指 Offer 题解 + +## 前言 + +题目来自《何海涛. 剑指 Offer[M]. 电子工业出版社, 2012.》,刷题网站推荐: + +- [牛客网](https://www.nowcoder.com/ta/coding-interviews?from=cyc_github) +- [Leetcode](https://leetcode-cn.com/problemset/lcof/) + +## 数组与矩阵 + +- [3. 数组中重复的数字](3.%20数组中重复的数字.md) +- [4. 二维数组中的查找](4.%20二维数组中的查找.md) +- [5. 替换空格](5.%20替换空格.md) +- [29. 顺时针打印矩阵](29.%20顺时针打印矩阵.md) +- [50. 第一个只出现一次的字符位置](50.%20第一个只出现一次的字符位置.md) + +## 栈队列堆 + +- [9. 用两个栈实现队列](9.%20用两个栈实现队列.md) +- [30. 包含 min 函数的栈](30.%20包含%20min%20函数的栈.md) +- [31. 栈的压入、弹出序列](31.%20栈的压入、弹出序列.md) +- [40. 最小的 K 个数](40.%20最小的%20K%20个数.md) +- [41.1 数据流中的中位数](41.1%20数据流中的中位数.md) +- [41.2 字符流中第一个不重复的字符](41.2%20字符流中第一个不重复的字符.md) +- [59. 滑动窗口的最大值](59.%20滑动窗口的最大值.md) + +## 双指针 + +- [57.1 和为 S 的两个数字](57.1%20和为%20S%20的两个数字.md) +- [57.2 和为 S 的连续正数序列](57.2%20和为%20S%20的连续正数序列.md) +- [58.1 翻转单词顺序列](58.1%20翻转单词顺序列.md) +- [58.2 左旋转字符串](58.2%20左旋转字符串.md) + +## 链表 + +- [6. 从尾到头打印链表](6.%20从尾到头打印链表.md) +- [18.1 在 O(1) 时间内删除链表节点](18.1%20在%20O(1)%20时间内删除链表节点.md) +- [18.2 删除链表中重复的结点](18.2%20删除链表中重复的结点.md) +- [22. 链表中倒数第 K 个结点](22.%20链表中倒数第%20K%20个结点.md) +- [23. 链表中环的入口结点](23.%20链表中环的入口结点.md) +- [24. 反转链表](24.%20反转链表.md) +- [25. 合并两个排序的链表](25.%20合并两个排序的链表.md) +- [35. 复杂链表的复制](35.%20复杂链表的复制.md) +- [52. 两个链表的第一个公共结点](52.%20两个链表的第一个公共结点.md) + +## 树 + +- [7. 重建二叉树](7.%20重建二叉树.md) +- [8. 二叉树的下一个结点](8.%20二叉树的下一个结点.md) +- [26. 树的子结构](26.%20树的子结构.md) +- [27. 二叉树的镜像](27.%20二叉树的镜像.md) +- [28. 对称的二叉树](28.%20对称的二叉树.md) +- [32.1 从上往下打印二叉树](32.1%20从上往下打印二叉树.md) +- [32.2 把二叉树打印成多行](32.2%20把二叉树打印成多行.md) +- [32.3 按之字形顺序打印二叉树](32.3%20按之字形顺序打印二叉树.md) +- [33. 二叉搜索树的后序遍历序列](33.%20二叉搜索树的后序遍历序列.md) +- [34. 二叉树中和为某一值的路径](34.%20二叉树中和为某一值的路径.md) +- [36. 二叉搜索树与双向链表](36.%20二叉搜索树与双向链表.md) +- [37. 序列化二叉树](37.%20序列化二叉树.md) +- [54. 二叉查找树的第 K 个结点](54.%20二叉查找树的第%20K%20个结点.md) +- [55.1 二叉树的深度](55.1%20二叉树的深度.md) +- [55.2 平衡二叉树](55.2%20平衡二叉树.md) +- [68. 树中两个节点的最低公共祖先](68.%20树中两个节点的最低公共祖先.md) + +## 贪心思想 + +- [14. 剪绳子](14.%20剪绳子.md) +- [63. 股票的最大利润](63.%20股票的最大利润.md) + +## 二分查找 + +- [11. 旋转数组的最小数字](11.%20旋转数组的最小数字.md) +- [53. 数字在排序数组中出现的次数](53.%20数字在排序数组中出现的次数.md) + +## 分治 + +- [16. 数值的整数次方](16.%20数值的整数次方.md) + +## 搜索 + +- [12. 矩阵中的路径](12.%20矩阵中的路径.md) +- [13. 机器人的运动范围](13.%20机器人的运动范围.md) +- [38. 字符串的排列](38.%20字符串的排列.md) + +## 排序 + +- [21. 调整数组顺序使奇数位于偶数前面](21.%20调整数组顺序使奇数位于偶数前面.md) +- [45. 把数组排成最小的数](45.%20把数组排成最小的数.md) +- [51. 数组中的逆序对](51.%20数组中的逆序对.md) + +## 动态规划 + +- [10.1 斐波那契数列](10.1%20斐波那契数列.md) +- [10.2 矩形覆盖](10.2%20矩形覆盖.md) +- [10.3 跳台阶](10.3%20跳台阶.md) +- [10.4 变态跳台阶](10.4%20变态跳台阶.md) +- [42. 连续子数组的最大和](42.%20连续子数组的最大和.md) +- [47. 礼物的最大价值](47.%20礼物的最大价值.md) +- [48. 最长不含重复字符的子字符串](48.%20最长不含重复字符的子字符串.md) +- [49. 丑数](49.%20丑数.md) +- [60. n 个骰子的点数](60.%20n%20个骰子的点数.md) +- [66. 构建乘积数组](66.%20构建乘积数组.md) + +## 数学 + +- [39. 数组中出现次数超过一半的数字](39.%20数组中出现次数超过一半的数字.md) +- [62. 圆圈中最后剩下的数](62.%20圆圈中最后剩下的数.md) +- [43. 从 1 到 n 整数中 1 出现的次数](43.%20从%201%20到%20n%20整数中%201%20出现的次数.md) + +## 位运算 + +- [15. 二进制中 1 的个数](15.%20二进制中%201%20的个数.md) +- [56. 数组中只出现一次的数字](56.%20数组中只出现一次的数字.md) + +## 其它 + +- [17. 打印从 1 到最大的 n 位数](17.%20打印从%201%20到最大的%20n%20位数.md) +- [19. 正则表达式匹配](19.%20正则表达式匹配.md) +- [20. 表示数值的字符串](20.%20表示数值的字符串.md) +- [44. 数字序列中的某一位数字](44.%20数字序列中的某一位数字.md) +- [46. 把数字翻译成字符串](46.%20把数字翻译成字符串.md) +- [61. 扑克牌顺子](61.%20扑克牌顺子.md) +- [64. 求 1+2+3+...+n](64.%20求%201+2+3+...+n.md) +- [65. 不用加减乘除做加法](65.%20不用加减乘除做加法.md) +- [67. 把字符串转换成整数](67.%20把字符串转换成整数.md) diff --git "a/notes/\345\211\221\346\214\207 offer \351\242\230\350\247\243.md" "b/notes/\345\211\221\346\214\207 offer \351\242\230\350\247\243.md" new file mode 100644 index 0000000000..a8c498b12a --- /dev/null +++ "b/notes/\345\211\221\346\214\207 offer \351\242\230\350\247\243.md" @@ -0,0 +1 @@ +[剑指 Offer 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md) diff --git "a/docs/notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" "b/notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" similarity index 87% rename from "docs/notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" rename to "notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" index 90ec94b2f3..e0107b4f25 100644 --- "a/docs/notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" +++ "b/notes/\346\224\273\345\207\273\346\212\200\346\234\257.md" @@ -1,19 +1,21 @@ +# 攻击技术 -* [一、跨站脚本攻击](#一跨站脚本攻击) -* [二、跨站请求伪造](#二跨站请求伪造) -* [三、SQL 注入攻击](#三sql-注入攻击) -* [四、拒绝服务攻击](#四拒绝服务攻击) -* [参考资料](#参考资料) +* [攻击技术](#攻击技术) + * [一、跨站脚本攻击](#一跨站脚本攻击) + * [二、跨站请求伪造](#二跨站请求伪造) + * [三、SQL 注入攻击](#三sql-注入攻击) + * [四、拒绝服务攻击](#四拒绝服务攻击) + * [参考资料](#参考资料) -# 一、跨站脚本攻击 +## 一、跨站脚本攻击 -## 概念 +### 概念 跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。 -## 攻击原理 +### 攻击原理 例如有一个论坛网站,攻击者可以在上面发布以下内容: @@ -29,19 +31,19 @@ 另一个用户浏览了含有这个内容的页面将会跳转到 domain.com 并携带了当前作用域的 Cookie。如果这个论坛网站通过 Cookie 管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录被攻击者的账号了。 -## 危害 +### 危害 - 窃取用户的 Cookie - 伪造虚假的输入表单骗取个人信息 - 显示伪造的文章或者图片 -## 防范手段 +### 防范手段 -### 1. 设置 Cookie 为 HttpOnly +#### 1. 设置 Cookie 为 HttpOnly 设置了 HttpOnly 的 Cookie 可以防止 JavaScript 脚本调用,就无法通过 document.cookie 获取用户 Cookie 信息。 -### 2. 过滤特殊字符 +#### 2. 过滤特殊字符 例如将 `<` 转义为 `<`,将 `>` 转义为 `>`,从而避免 HTML 和 Jascript 代码的运行。 @@ -85,15 +87,15 @@ alert(/xss/); > [XSS 过滤在线测试](http://jsxss.com/zh/try.html) -# 二、跨站请求伪造 +## 二、跨站请求伪造 -## 概念 +### 概念 跨站请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。 XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户浏览器的信任。 -## 攻击原理 +### 攻击原理 假如一家银行用以执行转账操作的 URL 地址如下: @@ -113,29 +115,29 @@ http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName 通过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。 -## 防范手段 +### 防范手段 -### 1. 检查 Referer 首部字段 +#### 1. 检查 Referer 首部字段 Referer 首部字段位于 HTTP 报文中,用于标识请求来源的地址。检查这个首部字段并要求请求来源的地址在同一个域名下,可以极大的防止 CSRF 攻击。 这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的 Referer 字段。虽然 HTTP 协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。 -### 2. 添加校验 Token +#### 2. 添加校验 Token 在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中,并且攻击者无法伪造的数据作为校验。例如服务器生成随机数并附加在表单中,并要求客户端传回这个随机数。 -### 3. 输入验证码 +#### 3. 输入验证码 因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。 -# 三、SQL 注入攻击 +## 三、SQL 注入攻击 -## 概念 +### 概念 服务器上的数据库运行非法的 SQL 语句,主要通过拼接来完成。 -## 攻击原理 +### 攻击原理 例如一个网站登录验证的 SQL 查询代码为: @@ -162,9 +164,9 @@ strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1' strSQL = "SELECT * FROM users;" ``` -## 防范手段 +### 防范手段 -### 1. 使用参数化查询 +#### 1. 使用参数化查询 Java 中的 PreparedStatement 是预先编译的 SQL 语句,可以传入适当参数并且多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。 @@ -175,25 +177,19 @@ stmt.setString(2, password); ResultSet rs = stmt.executeQuery(); ``` -### 2. 单引号转换 +#### 2. 单引号转换 将传入的参数中的单引号转换为连续两个单引号,PHP 中的 Magic quote 可以完成这个功能。 -# 四、拒绝服务攻击 +## 四、拒绝服务攻击 拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。 分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。 -# 参考资料 +## 参考资料 - [维基百科:跨站脚本](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC) - [维基百科:SQL 注入攻击](https://zh.wikipedia.org/wiki/SQL%E8%B3%87%E6%96%99%E9%9A%B1%E7%A2%BC%E6%94%BB%E6%93%8A) - [维基百科:跨站点请求伪造](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0) - [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" "b/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" new file mode 100644 index 0000000000..6d921ff1b5 --- /dev/null +++ "b/notes/\346\225\260\346\215\256\345\272\223\347\263\273\347\273\237\345\216\237\347\220\206.md" @@ -0,0 +1,535 @@ +# 数据库系统原理 + +* [数据库系统原理](#数据库系统原理) + * [一、事务](#一事务) + * [概念](#概念) + * [ACID](#acid) + * [AUTOCOMMIT](#autocommit) + * [二、并发一致性问题](#二并发一致性问题) + * [丢失修改](#丢失修改) + * [读脏数据](#读脏数据) + * [不可重复读](#不可重复读) + * [幻影读](#幻影读) + * [三、封锁](#三封锁) + * [封锁粒度](#封锁粒度) + * [封锁类型](#封锁类型) + * [封锁协议](#封锁协议) + * [MySQL 隐式与显式锁定](#mysql-隐式与显式锁定) + * [四、隔离级别](#四隔离级别) + * [未提交读(READ UNCOMMITTED)](#未提交读read-uncommitted) + * [提交读(READ COMMITTED)](#提交读read-committed) + * [可重复读(REPEATABLE READ)](#可重复读repeatable-read) + * [可串行化(SERIALIZABLE)](#可串行化serializable) + * [五、多版本并发控制](#五多版本并发控制) + * [基本思想](#基本思想) + * [版本号](#版本号) + * [Undo 日志](#undo-日志) + * [ReadView](#readview) + * [快照读与当前读](#快照读与当前读) + * [六、Next-Key Locks](#六next-key-locks) + * [Record Locks](#record-locks) + * [Gap Locks](#gap-locks) + * [Next-Key Locks](#next-key-locks) + * [七、关系数据库设计理论](#七关系数据库设计理论) + * [函数依赖](#函数依赖) + * [异常](#异常) + * [范式](#范式) + * [八、ER 图](#八er-图) + * [实体的三种联系](#实体的三种联系) + * [表示出现多次的关系](#表示出现多次的关系) + * [联系的多向性](#联系的多向性) + * [表示子类](#表示子类) + * [参考资料](#参考资料) + + + +## 一、事务 + +### 概念 + +事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。 + +

+ +### ACID + +#### 1. 原子性(Atomicity) + +事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。 + +回滚可以用回滚日志(Undo Log)来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。 + +#### 2. 一致性(Consistency) + +数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对同一个数据的读取结果都是相同的。 + +#### 3. 隔离性(Isolation) + +一个事务所做的修改在最终提交以前,对其它事务是不可见的。 + +#### 4. 持久性(Durability) + +一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。 + +系统发生崩溃可以用重做日志(Redo Log)进行恢复,从而实现持久性。与回滚日志记录数据的逻辑修改不同,重做日志记录的是数据页的物理修改。 + +---- + +事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系: + +- 只有满足一致性,事务的执行结果才是正确的。 +- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。 +- 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。 +- 事务满足持久化是为了能应对系统崩溃的情况。 + +

+ +### AUTOCOMMIT + +MySQL 默认采用自动提交模式。也就是说,如果不显式使用`START TRANSACTION`语句来开始一个事务,那么每个查询操作都会被当做一个事务并自动提交。 + +## 二、并发一致性问题 + +在并发环境下,事务的隔离性很难保证,因此会出现很多并发一致性问题。 + +### 丢失修改 + +丢失修改指一个事务的更新操作被另外一个事务的更新操作替换。一般在现实生活中常会遇到,例如:T1 和 T2 两个事务都对一个数据进行修改,T1 先修改并提交生效,T2 随后修改,T2 的修改覆盖了 T1 的修改。 + +

+ +### 读脏数据 + +读脏数据指在不同的事务下,当前事务可以读到另外事务未提交的数据。例如:T1 修改一个数据但未提交,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。 + +

+ +### 不可重复读 + +不可重复读指在一个事务内多次读取同一数据集合。在这一事务还未结束前,另一事务也访问了该同一数据集合并做了修改,由于第二个事务的修改,第一次事务的两次读取的数据可能不一致。例如:T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。 + +

+ +### 幻影读 + +幻读本质上也属于不可重复读的情况,T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同。 + +

+ +---- + +产生并发不一致性问题的主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更轻松的方式处理并发一致性问题。 + +## 三、封锁 + +### 封锁粒度 + +MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 + +应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的并发程度就越高。 + +但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度越小,系统开销就越大。 + +在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。 + + +### 封锁类型 + +#### 1. 读写锁 + +- 互斥锁(Exclusive),简写为 X 锁,又称写锁。 +- 共享锁(Shared),简写为 S 锁,又称读锁。 + +有以下两个规定: + +- 一个事务对数据对象 A 加了 X 锁,就可以对 A 进行读取和更新。加锁期间其它事务不能对 A 加任何锁。 +- 一个事务对数据对象 A 加了 S 锁,可以对 A 进行读取操作,但是不能进行更新操作。加锁期间其它事务能对 A 加 S 锁,但是不能加 X 锁。 + +锁的兼容关系如下: + +

+ +#### 2. 意向锁 + +使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。 + +在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。 + +意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S 锁。有以下两个规定: + +- 一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁; +- 一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。 + +通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。 + +各种锁的兼容关系如下: + +

+ +解释如下: + +- 任意 IS/IX 锁之间都是兼容的,因为它们只表示想要对表加锁,而不是真正加锁; +- 这里兼容关系针对的是表级锁,而表级的 IX 锁和行级的 X 锁兼容,两个事务可以对两个数据行加 X 锁。(事务 T1 想要对数据行 R1 加 X 锁,事务 T2 想要对同一个表的数据行 R2 加 X 锁,两个事务都需要对该表加 IX 锁,但是 IX 锁是兼容的,并且 IX 锁与行级的 X 锁也是兼容的,因此两个事务都能加锁成功,对同一个表中的两个数据行做修改。) + +### 封锁协议 + +#### 1. 三级封锁协议 + +**一级封锁协议** + +事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。 + +可以解决丢失修改问题,因为不能同时有两个事务对同一个数据进行修改,那么事务的修改就不会被覆盖。 + +

+ +**二级封锁协议** + +在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。 + +可以解决读脏数据问题,因为如果一个事务在对数据 A 进行修改,根据 1 级封锁协议,会加 X 锁,那么就不能再加 S 锁了,也就是不会读入数据。 + +

+ +**三级封锁协议** + +在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。 + +可以解决不可重复读的问题,因为读 A 时,其它事务不能对 A 加 X 锁,从而避免了在读的期间数据发生改变。 + +

+ +#### 2. 两段锁协议 + +加锁和解锁分为两个阶段进行。 + +可串行化调度是指,通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。串行执行的事务互不干扰,不会出现并发一致性问题。 + +事务遵循两段锁协议是保证可串行化调度的充分条件。例如以下操作满足两段锁协议,它是可串行化调度。 + +```html +lock-x(A)...lock-s(B)...lock-s(C)...unlock(A)...unlock(C)...unlock(B) +``` + +但不是必要条件,例如以下操作不满足两段锁协议,但它还是可串行化调度。 + +```html +lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C) +``` + +### MySQL 隐式与显式锁定 + +MySQL 的 InnoDB 存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定。 + +InnoDB 也可以使用特定的语句进行显示锁定: + +```sql +SELECT ... LOCK In SHARE MODE; +SELECT ... FOR UPDATE; +``` + +## 四、隔离级别 + +### 未提交读(READ UNCOMMITTED) + +事务中的修改,即使没有提交,对其它事务也是可见的。 + +### 提交读(READ COMMITTED) + +一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。 + +### 可重复读(REPEATABLE READ) + +保证在同一个事务中多次读取同一数据的结果是一样的。 + +### 可串行化(SERIALIZABLE) + +强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。 + +该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行。 + +---- + +

+ +## 五、多版本并发控制 + +多版本并发控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,要求很低,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现。 + +### 基本思想 + +在封锁一节中提到,加锁能解决多个事务同时执行时出现的并发一致性问题。在实际场景中读操作往往多于写操作,因此又引入了读写锁来避免不必要的加锁操作,例如读和读没有互斥关系。读写锁中读和写操作仍然是互斥的,而 MVCC 利用了多版本的思想,写操作更新最新的版本快照,而读操作去读旧版本快照,没有互斥关系,这一点和 CopyOnWrite 类似。 + +在 MVCC 中事务的修改操作(DELETE、INSERT、UPDATE)会为数据行新增一个版本快照。 + +脏读和不可重复读最根本的原因是事务读取到其它事务未提交的修改。在事务进行读取操作时,为了解决脏读和不可重复读问题,MVCC 规定只能读取已经提交的快照。当然一个事务可以读取自身未提交的快照,这不算是脏读。 + +### 版本号 + +- 系统版本号 SYS_ID:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增。 +- 事务版本号 TRX_ID :事务开始时的系统版本号。 + +### Undo 日志 + +MVCC 的多版本指的是多个版本的快照,快照存储在 Undo 日志中,该日志通过回滚指针 ROLL_PTR 把一个数据行的所有快照连接起来。 + +例如在 MySQL 创建一个表 t,包含主键 id 和一个字段 x。我们先插入一个数据行,然后对该数据行执行两次更新操作。 + +```sql +INSERT INTO t(id, x) VALUES(1, "a"); +UPDATE t SET x="b" WHERE id=1; +UPDATE t SET x="c" WHERE id=1; +``` + +因为没有使用 `START TRANSACTION` 将上面的操作当成一个事务来执行,根据 MySQL 的 AUTOCOMMIT 机制,每个操作都会被当成一个事务来执行,所以上面的操作总共涉及到三个事务。快照中除了记录事务版本号 TRX_ID 和操作之外,还记录了一个 bit 的 DEL 字段,用于标记是否被删除。 + +

+ +INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX_ID 写入。DELETE 可以看成是一个特殊的 UPDATE,还会额外将 DEL 字段设置为 1。 + +### ReadView + +MVCC 维护了一个 ReadView 结构,主要包含了当前系统未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, ...},还有该列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。 + +

+ +在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用: + +- TRX_ID \< TRX_ID_MIN,表示该数据行快照时在当前所有未提交事务之前进行更改的,因此可以使用。 + +- TRX_ID \> TRX_ID_MAX,表示该数据行快照是在事务启动之后被更改的,因此不可使用。 +- TRX_ID_MIN \<= TRX_ID \<= TRX_ID_MAX,需要根据隔离级别再进行判断: + - 提交读:如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。 + - 可重复读:都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。 + +在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。 + +### 快照读与当前读 + +#### 1. 快照读 + +MVCC 的 SELECT 操作是快照中的数据,不需要进行加锁操作。 + +```sql +SELECT * FROM table ...; +``` + +#### 2. 当前读 + +MVCC 其它会对数据库进行修改的操作(INSERT、UPDATE、DELETE)需要进行加锁操作,从而读取最新的数据。可以看到 MVCC 并不是完全不用加锁,而只是避免了 SELECT 的加锁操作。 + +```sql +INSERT; +UPDATE; +DELETE; +``` + +在进行 SELECT 操作时,可以强制指定进行加锁操作。以下第一个语句需要加 S 锁,第二个需要加 X 锁。 + +```sql +SELECT * FROM table WHERE ? lock in share mode; +SELECT * FROM table WHERE ? for update; +``` + +## 六、Next-Key Locks + +Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。 + +MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。 + +### Record Locks + +锁定一个记录上的索引,而不是记录本身。 + +如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用。 + +### Gap Locks + +锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。 + +```sql +SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; +``` + +### Next-Key Locks + +它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定索引之间的间隙。它锁定一个前开后闭区间,例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间: + +```sql +(-∞, 10] +(10, 11] +(11, 13] +(13, 20] +(20, +∞) +``` + +## 七、关系数据库设计理论 + +### 函数依赖 + +记 A-\>B 表示 A 函数决定 B,也可以说 B 函数依赖于 A。 + +如果 {A1,A2,... ,An} 是关系的一个或多个属性的集合,该集合函数决定了关系的其它所有属性并且是最小的,那么该集合就称为键码。 + +对于 A-\>B,如果能找到 A 的真子集 A',使得 A'-\> B,那么 A-\>B 就是部分函数依赖,否则就是完全函数依赖。 + +对于 A-\>B,B-\>C,则 A-\>C 是一个传递函数依赖。 + +### 异常 + +以下的学生课程关系的函数依赖为 {Sno, Cname} -\> {Sname, Sdept, Mname, Grade},键码为 {Sno, Cname}。也就是说,确定学生和课程之后,就能确定其它信息。 + +| Sno | Sname | Sdept | Mname | Cname | Grade | +| :---: | :---: | :---: | :---: | :---: |:---:| +| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 | +| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 | +| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 | +| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 | + +不符合范式的关系,会产生很多异常,主要有以下四种异常: + +- 冗余数据:例如 `学生-2` 出现了两次。 +- 修改异常:修改了一个记录中的信息,但是另一个记录中相同的信息却没有被修改。 +- 删除异常:删除一个信息,那么也会丢失其它信息。例如删除了 `课程-1` 需要删除第一行和第三行,那么 `学生-1` 的信息就会丢失。 +- 插入异常:例如想要插入一个学生的信息,如果这个学生还没选课,那么就无法插入。 + +### 范式 + +范式理论是为了解决以上提到四种异常。 + +高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。 + +#### 1. 第一范式 (1NF) + +属性不可分。 + +#### 2. 第二范式 (2NF) + +每个非主属性完全函数依赖于键码。 + +可以通过分解来满足。 + + **分解前**
+ +| Sno | Sname | Sdept | Mname | Cname | Grade | +| :---: | :---: | :---: | :---: | :---: |:---:| +| 1 | 学生-1 | 学院-1 | 院长-1 | 课程-1 | 90 | +| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-2 | 80 | +| 2 | 学生-2 | 学院-2 | 院长-2 | 课程-1 | 100 | +| 3 | 学生-3 | 学院-2 | 院长-2 | 课程-2 | 95 | + +以上学生课程关系中,{Sno, Cname} 为键码,有如下函数依赖: + +- Sno -\> Sname, Sdept +- Sdept -\> Mname +- Sno, Cname-\> Grade + +Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生的每门课都有特定的成绩。 + +Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。 + + **分解后**
+ +关系-1 + +| Sno | Sname | Sdept | Mname | +| :---: | :---: | :---: | :---: | +| 1 | 学生-1 | 学院-1 | 院长-1 | +| 2 | 学生-2 | 学院-2 | 院长-2 | +| 3 | 学生-3 | 学院-2 | 院长-2 | + +有以下函数依赖: + +- Sno -\> Sname, Sdept +- Sdept -\> Mname + +关系-2 + +| Sno | Cname | Grade | +| :---: | :---: |:---:| +| 1 | 课程-1 | 90 | +| 2 | 课程-2 | 80 | +| 2 | 课程-1 | 100 | +| 3 | 课程-2 | 95 | + +有以下函数依赖: + +- Sno, Cname -\> Grade + +#### 3. 第三范式 (3NF) + +非主属性不传递函数依赖于键码。 + +上面的 关系-1 中存在以下传递函数依赖: + +- Sno -\> Sdept -\> Mname + +可以进行以下分解: + +关系-11 + +| Sno | Sname | Sdept | +| :---: | :---: | :---: | +| 1 | 学生-1 | 学院-1 | +| 2 | 学生-2 | 学院-2 | +| 3 | 学生-3 | 学院-2 | + +关系-12 + +| Sdept | Mname | +| :---: | :---: | +| 学院-1 | 院长-1 | +| 学院-2 | 院长-2 | + +## 八、ER 图 + +Entity-Relationship,有三个组成部分:实体、属性、联系。 + +用来进行关系型数据库系统的概念设计。 + +### 实体的三种联系 + +包含一对一,一对多,多对多三种。 + +- 如果 A 到 B 是一对多关系,那么画个带箭头的线段指向 B; +- 如果是一对一,画两个带箭头的线段; +- 如果是多对多,画两个不带箭头的线段。 + +下图的 Course 和 Student 是一对多的关系。 + +

+ +### 表示出现多次的关系 + +一个实体在联系出现几次,就要用几条线连接。 + +下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。 + +

+ +### 联系的多向性 + +虽然老师可以开设多门课,并且可以教授多名学生,但是对于特定的学生和课程,只有一个老师教授,这就构成了一个三元联系。 + +

+ +### 表示子类 + +用一个三角形和两条线来连接类和子类,与子类有关的属性和联系都连到子类上,而与父类和子类都有关的连到父类上。 + +

+ +## 参考资料 + +- AbrahamSilberschatz, HenryF.Korth, S.Sudarshan, 等. 数据库系统概念 [M]. 机械工业出版社, 2006. +- 施瓦茨. 高性能 MYSQL(第3版)[M]. 电子工业出版社, 2013. +- 史嘉权. 数据库系统概论[M]. 清华大学出版社有限公司, 2006. +- [The InnoDB Storage Engine](https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html) +- [Transaction isolation levels](https://www.slideshare.net/ErnestoHernandezRodriguez/transaction-isolation-levels) +- [Concurrency Control](http://scanftree.com/dbms/2-phase-locking-protocol) +- [The Nightmare of Locking, Blocking and Isolation Levels!](https://www.slideshare.net/brshristov/the-nightmare-of-locking-blocking-and-isolation-levels-46391666) +- [Database Normalization and Normal Forms with an Example](https://aksakalli.github.io/2012/03/12/database-normalization-and-normal-forms-with-an-example.html) +- [The basics of the InnoDB undo logging and history system](https://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/) +- [MySQL locking for the busy web developer](https://www.brightbox.com/blog/2013/10/31/on-mysql-locks/) +- [浅入浅出 MySQL 和 InnoDB](https://draveness.me/mysql-innodb) +- [Innodb 中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html) diff --git "a/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" "b/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" new file mode 100644 index 0000000000..dbc70bc679 --- /dev/null +++ "b/notes/\346\236\204\345\273\272\345\267\245\345\205\267.md" @@ -0,0 +1,126 @@ +# 构建工具 + +* [构建工具](#构建工具) + * [一、构建工具的作用](#一构建工具的作用) + * [二、Java 主流构建工具](#二java-主流构建工具) + * [三、Maven](#三maven) + * [参考资料](#参考资料) + + + +## 一、构建工具的作用 + +构建一个项目通常包含了依赖管理、测试、编译、打包、发布等流程,构建工具可以自动化进行这些操作,从而为我们减少这些繁琐的工作。 + +其中构建工具提供的依赖管理能够可以自动处理依赖关系。例如一个项目需要用到依赖 A,A 又依赖于 B,那么构建工具就能帮我们导入 B,而不需要我们手动去寻找并导入。 + +在 Java 项目中,打包流程通常是将项目打包成 Jar 包。在没有构建工具的情况下,我们需要使用命令行工具或者 IDE 手动打包。而发布流程通常是将 Jar 包上传到服务器上。 + +## 二、Java 主流构建工具 + +Ant 具有编译、测试和打包功能,其后出现的 Maven 在 Ant 的功能基础上又新增了依赖管理功能,而最新的 Gradle 又在 Maven 的功能基础上新增了对 Groovy 语言的支持。 + +

+ +Gradle 和 Maven 的区别是,它使用 Groovy 这种特定领域语言(DSL)来管理构建脚本,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。 + +例如要在项目中引入 Junit,Maven 的代码如下: + +```xml + + + 4.0.0 + + jizg.study.maven.hello + hello-first + 0.0.1-SNAPSHOT + + + + junit + junit + 4.10 + test + + + +``` + +而 Gradle 只需要几行代码: + +```java +dependencies { + testCompile "junit:junit:4.10" +} +``` + +## 三、Maven + +### 概述 + +提供了项目对象模型(POM)文件来管理项目的构建。 + +### 仓库 + +仓库的搜索顺序为:本地仓库、中央仓库、远程仓库。 + +- 本地仓库用来存储项目的依赖库; +- 中央仓库是下载依赖库的默认位置; +- 远程仓库,因为并非所有的依赖库都在中央仓库,或者中央仓库访问速度很慢,远程仓库是中央仓库的补充。 + +### POM + +POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目录的 pom.xml 文件中。 + +```xml + + junit + junit + 4.12 + test + +``` + +[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classifier 不能直接定义的,需要结合插件使用。 + +- groupId:项目组 Id,必须全球唯一; +- artifactId:项目 Id,即项目名; +- version:项目版本; +- packaging:项目打包方式。 + +### 依赖原则 + +#### 1. 依赖路径最短优先原则 + +```html +A -> B -> C -> X(1.0) +A -> D -> X(2.0) +``` +由于 X(2.0) 路径最短,所以使用 X(2.0)。 + +#### 2. 声明顺序优先原则 + +```html +A -> B -> X(1.0) +A -> C -> X(2.0) +``` + +在 POM 中最先声明的优先,上面的两个依赖如果先声明 B,那么最后使用 X(1.0)。 + +#### 3. 覆写优先原则 + +子 POM 内声明的依赖优先于父 POM 中声明的依赖。 + +### 解决依赖冲突 + +找到 Maven 加载的 Jar 包版本,使用 `mvn dependency:tree` 查看依赖树,根据依赖原则来调整依赖在 POM 文件的声明顺序。 + +## 参考资料 + +- [POM Reference](http://maven.apache.org/pom.html#Dependency_Version_Requirement_Specification) +- [What is a build tool?](https://stackoverflow.com/questions/7249871/what-is-a-build-tool) +- [Java Build Tools Comparisons: Ant vs Maven vs Gradle](https://programmingmitra.blogspot.com/2016/05/java-build-tools-comparisons-ant-vs.html) +- [maven 2 gradle](http://sagioto.github.io/maven2gradle/) +- [新一代构建工具 gradle](https://www.imooc.com/learn/833) + diff --git "a/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" "b/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" new file mode 100644 index 0000000000..fba480218b --- /dev/null +++ "b/notes/\346\255\243\345\210\231\350\241\250\350\276\276\345\274\217.md" @@ -0,0 +1,392 @@ +# 正则表达式 + +* [正则表达式](#正则表达式) + * [一、概述](#一概述) + * [二、匹配单个字符](#二匹配单个字符) + * [三、匹配一组字符](#三匹配一组字符) + * [四、使用元字符](#四使用元字符) + * [五、重复匹配](#五重复匹配) + * [六、位置匹配](#六位置匹配) + * [七、使用子表达式](#七使用子表达式) + * [八、回溯引用](#八回溯引用) + * [九、前后查找](#九前后查找) + * [十、嵌入条件](#十嵌入条件) + * [参考资料](#参考资料) + + + +## 一、概述 + +正则表达式用于文本内容的查找和替换。 + +正则表达式内置于其它语言或者软件产品中,它本身不是一种语言或者软件。 + +[正则表达式在线工具](https://regexr.com/) + +## 二、匹配单个字符 + +**.** 可以用来匹配任何的单个字符,但是在绝大多数实现里面,不能匹配换行符; + +**.** 是元字符,表示它有特殊的含义,而不是字符本身的含义。如果需要匹配 . ,那么要用 \ 进行转义,即在 . 前面加上 \ 。 + +正则表达式一般是区分大小写的,但也有些实现不区分。 + +**正则表达式** + +``` +C.C2018 +``` + +**匹配结果** + +My name is **CyC2018** . + +## 三、匹配一组字符 + +**[ ]** 定义一个字符集合; + +0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定,字符区间在 [ ] 中使用。 + +**-** 只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; + +**^** 在 [ ] 中是取非操作。 + +**应用** + +匹配以 abc 为开头,并且最后一个字母不为数字的字符串: + +**正则表达式** + +``` +abc[^0-9] +``` + +**匹配结果** + +1. **abcd** +2. abc1 +3. abc2 + +## 四、使用元字符 + +### 匹配空白字符 + +| 元字符 | 说明 | +| :---: | :---: | +| [\b] | 回退(删除)一个字符 | +| \f | 换页符 | +| \n | 换行符 | +| \r | 回车符 | +| \t | 制表符 | +| \v | 垂直制表符 | + +\r\n 是 Windows 中的文本行结束标签,在 Unix/Linux 则是 \n。 + +\r\n\r\n 可以匹配 Windows 下的空白行,因为它匹配两个连续的行尾标签,而这正是两条记录之间的空白行; + +### 匹配特定的字符 + +#### 1. 数字元字符 + +| 元字符 | 说明 | +| :---: | :---: | +| \d | 数字字符,等价于 [0-9] | +| \D | 非数字字符,等价于 [^0-9] | + +#### 2. 字母数字元字符 + +| 元字符 | 说明 | +| :---: | :---: | +| \w | 大小写字母,下划线和数字,等价于 [a-zA-Z0-9\_] | +| \W | 对 \w 取非 | + +#### 3. 空白字符元字符 + +| 元字符 | 说明 | +| :---: | :---: | +| \s | 任何一个空白字符,等价于 [\f\n\r\t\v] | +| \S | 对 \s 取非 | + +\x 匹配十六进制字符,\0 匹配八进制,例如 \xA 对应值为 10 的 ASCII 字符 ,即 \n。 + +## 五、重复匹配 + +- **\+** 匹配 1 个或者多个字符 +- **\** * 匹配 0 个或者多个字符 +- **?** 匹配 0 个或者 1 个字符 + +**应用** + +匹配邮箱地址。 + +**正则表达式** + +``` +[\w.]+@\w+\.\w+ +``` + +[\w.] 匹配的是字母数字或者 . ,在其后面加上 + ,表示匹配多次。在字符集合 [ ] 里,. 不是元字符; + +**匹配结果** + +**abc.def\@\qq.com** + +- **{n}** 匹配 n 个字符 +- **{m,n}** 匹配 m\~n 个字符 +- **{m,}** 至少匹配 m 个字符 + +\* 和 + 都是贪婪型元字符,会匹配尽可能多的内容。在后面加 ? 可以转换为懒惰型元字符,例如 \*?、+? 和 {m,n}? 。 + +**正则表达式** + +``` +a.+c +``` + +**匹配结果** + +**abcabcabc** + +由于 + 是贪婪型的,因此 .+ 会匹配更可能多的内容,所以会把整个 abcabcabc 文本都匹配,而不是只匹配前面的 abc 文本。用懒惰型可以实现匹配前面的。 + +## 六、位置匹配 + +### 单词边界 + +**\b** 可以匹配一个单词的边界,边界是指位于 \w 和 \W 之间的位置;**\B** 匹配一个不是单词边界的位置。 + +\b 只匹配位置,不匹配字符,因此 \babc\b 匹配出来的结果为 3 个字符。 + +### 字符串边界 + +**^** 匹配整个字符串的开头,**$** 匹配结尾。 + +^ 元字符在字符集合中用作求非,在字符集合外用作匹配字符串的开头。 + +分行匹配模式(multiline)下,换行被当做字符串的边界。 + +**应用** + +匹配代码中以 // 开始的注释行 + +**正则表达式** + +``` +^\s*\/\/.*$ +``` + +

+ +**匹配结果** + +1. public void fun() { +2.      **// 注释 1** +3.      int a = 1; +4.      int b = 2; +5.      **// 注释 2** +6.      int c = a + b; +7. } + +## 七、使用子表达式 + +使用 **( )** 定义一个子表达式。子表达式的内容可以当成一个独立元素,即可以将它看成一个字符,并且使用 * 等元字符。 + +子表达式可以嵌套,但是嵌套层次过深会变得很难理解。 + +**正则表达式** + +``` +(ab){2,} +``` + +**匹配结果** + +**ababab** + +**|** 是或元字符,它把左边和右边所有的部分都看成单独的两个部分,两个部分只要有一个匹配就行。 + +**正则表达式** + +``` +(19|20)\d{2} +``` + +**匹配结果** + +1. **1900** +2. **2010** +3. 1020 + +**应用** + +匹配 IP 地址。 + +IP 地址中每部分都是 0-255 的数字,用正则表达式匹配时以下情况是合法的: + +- 一位数字 +- 不以 0 开头的两位数字 +- 1 开头的三位数 +- 2 开头,第 2 位是 0-4 的三位数 +- 25 开头,第 3 位是 0-5 的三位数 + +**正则表达式** + +``` +((25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d))\.){3}(25[0-5]|(2[0-4]\d)|(1\d{2})|([1-9]\d)|(\d)) +``` + +**匹配结果** + +1. **192.168.0.1** +2. 00.00.00.00 +3. 555.555.555.555 + +## 八、回溯引用 + +回溯引用使用 **\n** 来引用某个子表达式,其中 n 代表的是子表达式的序号,从 1 开始。它和子表达式匹配的内容一致,比如子表达式匹配到 abc,那么回溯引用部分也需要匹配 abc 。 + +**应用** + +匹配 HTML 中合法的标题元素。 + +**正则表达式** + +\1 将回溯引用子表达式 (h[1-6]) 匹配的内容,也就是说必须和子表达式匹配的内容一致。 + +``` +<(h[1-6])>\w*?<\/\1> +``` + +**匹配结果** + +1. **<h1\>x</h1\>** +2. **<h2\>x</h2\>** +3. <h3\>x</h1\> + +### 替换 + +需要用到两个正则表达式。 + +**应用** + +修改电话号码格式。 + +**文本** + +313-555-1234 + +**查找正则表达式** + +``` +(\d{3})(-)(\d{3})(-)(\d{4}) +``` + +**替换正则表达式** + +在第一个子表达式查找的结果加上 () ,然后加一个空格,在第三个和第五个字表达式查找的结果中间加上 - 进行分隔。 + +``` +($1) $3-$5 +``` + +**结果** + +(313) 555-1234 + +### 大小写转换 + +| 元字符 | 说明 | +| :---: | :---: | +| \l | 把下个字符转换为小写 | +| \u| 把下个字符转换为大写 | +| \L | 把\L 和\E 之间的字符全部转换为小写 | +| \U | 把\U 和\E 之间的字符全部转换为大写 | +| \E | 结束\L 或者\U | + +**应用** + +把文本的第二个和第三个字符转换为大写。 + +**文本** + +abcd + +**查找** + +``` +(\w)(\w{2})(\w) +``` + +**替换** + +``` +$1\U$2\E$3 +``` + +**结果** + +aBCd + +## 九、前后查找 + +前后查找规定了匹配的内容首尾应该匹配的内容,但是又不包含首尾匹配的内容。 + +向前查找使用 **?=** 定义,它规定了尾部匹配的内容,这个匹配的内容在 ?= 之后定义。所谓向前查找,就是规定了一个匹配的内容,然后以这个内容为尾部向前面查找需要匹配的内容。向后匹配用 ?\<= 定义(注: JavaScript 不支持向后匹配,Java 对其支持也不完善)。 + +**应用** + +查找出邮件地址 @ 字符前面的部分。 + +**正则表达式** + +``` +\w+(?=@) +``` + +**结果** + +**abc** @qq.com + +对向前和向后查找取非,只要把 = 替换成 ! 即可,比如 (?=) 替换成 (?!) 。取非操作使得匹配那些首尾不符合要求的内容。 + +## 十、嵌入条件 + +### 回溯引用条件 + +条件为某个子表达式是否匹配,如果匹配则需要继续匹配条件表达式后面的内容。 + +**正则表达式** + +子表达式 (\\() 匹配一个左括号,其后的 ? 表示匹配 0 个或者 1 个。 ?(1) 为条件,当子表达式 1 匹配时条件成立,需要执行 \) 匹配,也就是匹配右括号。 + +``` +(\()?abc(?(1)\)) +``` + +**结果** + +1. **(abc)** +2. **abc** +3. (abc + +### 前后查找条件 + +条件为定义的首尾是否匹配,如果匹配,则继续执行后面的匹配。注意,首尾不包含在匹配的内容中。 + +**正则表达式** + + ?(?=-) 为前向查找条件,只有在以 - 为前向查找的结尾能匹配 \d{5} ,才继续匹配 -\d{4} 。 + +``` +\d{5}(?(?=-)-\d{4}) +``` + +**结果** + +1. **11111** +2. 22222- +3. **33333-4444** + +## 参考资料 + +- BenForta. 正则表达式必知必会 [M]. 人民邮电出版社, 2007. diff --git "a/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" new file mode 100644 index 0000000000..60507bc9da --- /dev/null +++ "b/notes/\346\266\210\346\201\257\351\230\237\345\210\227.md" @@ -0,0 +1,81 @@ +# 消息队列 + +* [消息队列](#消息队列) + * [一、消息模型](#一消息模型) + * [点对点](#点对点) + * [发布/订阅](#发布订阅) + * [二、使用场景](#二使用场景) + * [异步处理](#异步处理) + * [流量削锋](#流量削锋) + * [应用解耦](#应用解耦) + * [三、可靠性](#三可靠性) + * [发送端的可靠性](#发送端的可靠性) + * [接收端的可靠性](#接收端的可靠性) + * [参考资料](#参考资料) + + + +## 一、消息模型 + +### 点对点 + +消息生产者向消息队列中发送了一个消息之后,只能被一个消费者消费一次。 + +

+ +### 发布/订阅 + +消息生产者向频道发送一个消息之后,多个消费者可以从该频道订阅到这条消息并消费。 + +

+ +发布与订阅模式和观察者模式有以下不同: + +- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,生产者与消费者不知道对方的存在,它们之间通过频道进行通信。 +- 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,生产者向频道发送一个消息之后,就不需要关心消费者何时去订阅这个消息,可以立即返回。 + +

+ +## 二、使用场景 + +### 异步处理 + +发送者将消息发送给消息队列之后,不需要同步等待消息接收者处理完毕,而是立即返回进行其它操作。消息接收者从消息队列中订阅消息之后异步处理。 + +例如在注册流程中通常需要发送验证邮件来确保注册用户身份的合法性,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。 + +只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成注册的话,就不能再使用消息队列。 + +### 流量削锋 + +在高并发的场景下,如果短时间有大量的请求到达会压垮服务器。 + +可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。 + +### 应用解耦 + +如果模块之间不直接进行调用,模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。 + +通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。 + +## 三、可靠性 + +### 发送端的可靠性 + +发送端完成操作后一定能将消息成功发送到消息队列中。 + +实现方法:在本地数据库建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息队列中,若转移消息成功则删除消息表中的数据,否则继续重传。 + +### 接收端的可靠性 + +接收端能够从消息队列成功消费一次消息。 + +两种实现方法: + +- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 +- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 + +## 参考资料 + +- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/) +- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105) diff --git "a/docs/notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" "b/notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" similarity index 79% rename from "docs/notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" rename to "notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" index cc761d5ab9..7325f23abe 100644 --- "a/docs/notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" +++ "b/notes/\347\256\227\346\263\225 - \345\205\266\345\256\203.md" @@ -1,28 +1,23 @@ - -* [汉诺塔](#汉诺塔) -* [哈夫曼编码](#哈夫曼编码) - +# 算法 - 其它 +## 汉诺塔 - -# 汉诺塔 - -

+

有三个柱子,分别为 from、buffer、to。需要将 from 上的圆盘全部移动到 to 上,并且要保证小圆盘始终在大圆盘上。 这是一个经典的递归问题,分为三步求解: -① 将 n-1 个圆盘从 from -> buffer +① 将 n-1 个圆盘从 from -\> buffer -

+

-② 将 1 个圆盘从 from -> to +② 将 1 个圆盘从 from -\> to -

+

-③ 将 n-1 个圆盘从 buffer -> to +③ 将 n-1 个圆盘从 buffer -\> to -

+

如果只有一个圆盘,那么只需要进行一次移动操作。 @@ -56,7 +51,7 @@ from H2 to H3 from H1 to H3 ``` -# 哈夫曼编码 +## 哈夫曼编码 根据数据出现的频率对数据进行编码,从而压缩原始数据。 @@ -73,7 +68,7 @@ from H1 to H3 生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到叶子节点,叶子节点代表的字符的编码就是这个路径编码。 -

+

```java public class Huffman { @@ -132,9 +127,3 @@ public class Huffman { } } ``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" "b/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" new file mode 100644 index 0000000000..275be029b0 --- /dev/null +++ "b/notes/\347\256\227\346\263\225 - \345\271\266\346\237\245\351\233\206.md" @@ -0,0 +1,194 @@ +# 算法 - 并查集 + +* [算法 - 并查集](#算法---并查集) + * [前言](#前言) + * [Quick Find](#quick-find) + * [Quick Union](#quick-union) + * [加权 Quick Union](#加权-quick-union) + * [路径压缩的加权 Quick Union](#路径压缩的加权-quick-union) + * [比较](#比较) + + + +## 前言 + +用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。 + +

+ +| 方法 | 描述 | +| :---: | :---: | +| UF(int N) | 构造一个大小为 N 的并查集 | +| void union(int p, int q) | 连接 p 和 q 节点 | +| int find(int p) | 查找 p 所在的连通分量编号 | +| boolean connected(int p, int q) | 判断 p 和 q 节点是否连通 | + +```java +public abstract class UF { + + protected int[] id; + + public UF(int N) { + id = new int[N]; + for (int i = 0; i < N; i++) { + id[i] = i; + } + } + + public boolean connected(int p, int q) { + return find(p) == find(q); + } + + public abstract int find(int p); + + public abstract void union(int p, int q); +} +``` + +## Quick Find + +可以快速进行 find 操作,也就是可以快速判断两个节点是否连通。 + +需要保证同一连通分量的所有节点的 id 值相等,就可以通过判断两个节点的 id 值是否相等从而判断其连通性。 + +但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 + +

+ +```java +public class QuickFindUF extends UF { + + public QuickFindUF(int N) { + super(N); + } + + + @Override + public int find(int p) { + return id[p]; + } + + + @Override + public void union(int p, int q) { + int pID = find(p); + int qID = find(q); + + if (pID == qID) { + return; + } + + for (int i = 0; i < id.length; i++) { + if (id[i] == pID) { + id[i] = qID; + } + } + } +} +``` + +## Quick Union + +可以快速进行 union 操作,只需要修改一个节点的 id 值即可。 + +但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。 + +

+ +```java +public class QuickUnionUF extends UF { + + public QuickUnionUF(int N) { + super(N); + } + + + @Override + public int find(int p) { + while (p != id[p]) { + p = id[p]; + } + return p; + } + + + @Override + public void union(int p, int q) { + int pRoot = find(p); + int qRoot = find(q); + + if (pRoot != qRoot) { + id[pRoot] = qRoot; + } + } +} +``` + +这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为节点的数目。 + +

+ +## 加权 Quick Union + +为了解决 quick-union 的树通常会很高的问题,加权 quick-union 在 union 操作时会让较小的树连接较大的树上面。 + +理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 + +

+ +```java +public class WeightedQuickUnionUF extends UF { + + // 保存节点的数量信息 + private int[] sz; + + + public WeightedQuickUnionUF(int N) { + super(N); + this.sz = new int[N]; + for (int i = 0; i < N; i++) { + this.sz[i] = 1; + } + } + + + @Override + public int find(int p) { + while (p != id[p]) { + p = id[p]; + } + return p; + } + + + @Override + public void union(int p, int q) { + + int i = find(p); + int j = find(q); + + if (i == j) return; + + if (sz[i] < sz[j]) { + id[i] = j; + sz[j] += sz[i]; + } else { + id[j] = i; + sz[i] += sz[j]; + } + } +} +``` + +## 路径压缩的加权 Quick Union + +在检查节点的同时将它们直接链接到根节点,只需要在 find 中添加一个循环即可。 + +## 比较 + +| 算法 | union | find | +| :---: | :---: | :---: | +| Quick Find | N | 1 | +| Quick Union | 树高 | 树高 | +| 加权 Quick Union | logN | logN | +| 路径压缩的加权 Quick Union | 非常接近 1 | 非常接近 1 | diff --git "a/docs/notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" "b/notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" similarity index 79% rename from "docs/notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" rename to "notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" index db81624d93..edd086c1d9 100644 --- "a/docs/notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" +++ "b/notes/\347\256\227\346\263\225 - \346\216\222\345\272\217.md" @@ -1,40 +1,12 @@ - -* [约定](#约定) -* [选择排序](#选择排序) -* [冒泡排序](#冒泡排序) -* [插入排序](#插入排序) -* [希尔排序](#希尔排序) -* [归并排序](#归并排序) - * [1. 归并方法](#1-归并方法) - * [2. 自顶向下归并排序](#2-自顶向下归并排序) - * [3. 自底向上归并排序](#3-自底向上归并排序) -* [快速排序](#快速排序) - * [1. 基本算法](#1-基本算法) - * [2. 切分](#2-切分) - * [3. 性能分析](#3-性能分析) - * [4. 算法改进](#4-算法改进) - * [5. 基于切分的快速选择算法](#5-基于切分的快速选择算法) -* [堆排序](#堆排序) - * [1. 堆](#1-堆) - * [2. 上浮和下沉](#2-上浮和下沉) - * [3. 插入元素](#3-插入元素) - * [4. 删除最大元素](#4-删除最大元素) - * [5. 堆排序](#5-堆排序) - * [6. 分析](#6-分析) -* [小结](#小结) - * [1. 排序算法的比较](#1-排序算法的比较) - * [2. Java 的排序算法实现](#2-java-的排序算法实现) - - - -# 约定 +# 算法 - 排序 +## 约定 待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法,可以用它来判断两个元素的大小关系。 -研究排序算法的成本模型时,统计的是比较和交换的次数。 - 使用辅助函数 less() 和 swap() 来进行比较和交换的操作,使得代码的可读性和可移植性更好。 +排序算法的成本模型是比较和交换的次数。 + ```java public abstract class Sort> { @@ -52,13 +24,13 @@ public abstract class Sort> { } ``` -# 选择排序 +## 选择排序 -选择出数组中的最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 +从数组中选择最小元素,将它与数组的第一个元素交换位置。再从数组剩下的元素中选择出最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 选择排序需要 \~N2/2 次比较和 \~N 次交换,它的运行时间与输入无关,这个特点使得它对一个已经排序的数组也需要这么多的比较和交换操作。 -

+

```java public class Selection> extends Sort { @@ -79,13 +51,13 @@ public class Selection> extends Sort { } ``` -# 冒泡排序 +## 冒泡排序 从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。 -在一轮循环中,如果没有发生交换,就说明数组已经是有序的,此时可以直接退出。 +在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。 -

+

```java public class Bubble> extends Sort { @@ -93,12 +65,12 @@ public class Bubble> extends Sort { @Override public void sort(T[] nums) { int N = nums.length; - boolean hasSorted = false; - for (int i = N - 1; i > 0 && !hasSorted; i--) { - hasSorted = true; + boolean isSorted = false; + for (int i = N - 1; i > 0 && !isSorted; i--) { + isSorted = true; for (int j = 0; j < i; j++) { if (less(nums[j + 1], nums[j])) { - hasSorted = false; + isSorted = false; swap(nums, j, j + 1); } } @@ -107,21 +79,19 @@ public class Bubble> extends Sort { } ``` -# 插入排序 +## 插入排序 每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。 对于数组 {3, 5, 2, 4, 1},它具有以下逆序:(3, 2), (3, 1), (5, 2), (5, 4), (5, 1), (2, 1), (4, 1),插入排序每次只能交换相邻元素,令逆序数量减少 1,因此插入排序需要交换的次数为逆序数量。 -插入排序的复杂度取决于数组的初始顺序,如果数组已经部分有序了,逆序较少,那么插入排序会很快。 +插入排序的时间复杂度取决于数组的初始顺序,如果数组已经部分有序了,那么逆序较少,需要的交换次数也就较少,时间复杂度较低。 - 平均情况下插入排序需要 \~N2/4 比较以及 \~N2/4 次交换; - 最坏的情况下需要 \~N2/2 比较以及 \~N2/2 次交换,最坏的情况是数组是倒序的; - 最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。 -以下演示了在一轮循环中,将元素 2 插入到左侧已经排序的数组中。 - -

+

```java public class Insertion> extends Sort { @@ -138,15 +108,13 @@ public class Insertion> extends Sort { } ``` -# 希尔排序 +## 希尔排序 -对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。 - -希尔排序的出现就是为了解决插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。 +对于大规模的数组,插入排序很慢,因为它只能交换相邻的元素,每次只能将逆序数量减少 1。希尔排序的出现就是为了解决插入排序的这种局限性,它通过交换不相邻的元素,每次可以将逆序数量减少大于 1。 希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。 -

+

```java public class Shell> extends Sort { @@ -176,13 +144,13 @@ public class Shell> extends Sort { 希尔排序的运行时间达不到平方级别,使用递增序列 1, 4, 13, 40, ... 的希尔排序所需要的比较次数不会超过 N 的若干倍乘于递增序列的长度。后面介绍的高级排序算法只会比希尔排序快两倍左右。 -# 归并排序 +## 归并排序 归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。 -

+

-## 1. 归并方法 +### 1. 归并方法 归并方法将数组中两个已经排序的部分归并成一个。 @@ -218,7 +186,7 @@ public abstract class MergeSort> extends Sort { } ``` -## 2. 自顶向下归并排序 +### 2. 自顶向下归并排序 将一个大数组分成两个小数组去求解。 @@ -245,7 +213,8 @@ public class Up2DownMergeSort> extends MergeSort { } ``` -## 3. 自底向上归并排序 + +### 3. 自底向上归并排序 先归并那些微型数组,然后成对归并得到的微型数组。 @@ -268,14 +237,14 @@ public class Down2UpMergeSort> extends MergeSort { ``` -# 快速排序 +## 快速排序 -## 1. 基本算法 +### 1. 基本算法 - 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序; - 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。 -

+

```java public class QuickSort> extends Sort { @@ -302,11 +271,11 @@ public class QuickSort> extends Sort { } ``` -## 2. 切分 +### 2. 切分 取 a[l] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于它的元素,交换这两个元素。不断进行这个过程,就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[l] 和 a[j] 交换位置。 -

+

```java private int partition(T[] nums, int l, int h) { @@ -324,7 +293,7 @@ private int partition(T[] nums, int l, int h) { } ``` -## 3. 性能分析 +### 3. 性能分析 快速排序是原地排序,不需要辅助数组,但是递归调用需要辅助栈。 @@ -332,17 +301,17 @@ private int partition(T[] nums, int l, int h) { 最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分,如此这般。因此最坏的情况下需要比较 N2/2。为了防止数组最开始就是有序的,在进行快速排序时需要随机打乱数组。 -## 4. 算法改进 +### 4. 算法改进 -#### 4.1 切换到插入排序 +##### 4.1 切换到插入排序 因为快速排序在小数组中也会递归调用自己,对于小数组,插入排序比快速排序的性能更好,因此在小数组中可以切换到插入排序。 -#### 4.2 三数取中 +##### 4.2 三数取中 最好的情况下是每次都能取数组的中位数作为切分元素,但是计算中位数的代价很高。一种折中方法是取 3 个元素,并将大小居中的元素作为切分元素。 -#### 4.3 三向切分 +##### 4.3 三向切分 对于有大量重复元素的数组,可以将数组切分为三部分,分别对应小于、等于和大于切分元素。 @@ -374,7 +343,7 @@ public class ThreeWayQuickSort> extends QuickSort { } ``` -## 5. 基于切分的快速选择算法 +### 5. 基于切分的快速选择算法 快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。 @@ -402,15 +371,15 @@ public T select(T[] nums, int k) { } ``` -# 堆排序 +## 堆排序 -## 1. 堆 +### 1. 堆 -堆中某个节点的值总是大于等于其子节点的值,并且堆是一颗完全二叉树。 +堆中某个节点的值总是大于等于或小于等于其子节点的值,并且堆是一颗完全二叉树。 堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。 -

+

```java public class Heap> { @@ -442,11 +411,11 @@ public class Heap> { } ``` -## 2. 上浮和下沉 +### 2. 上浮和下沉 在堆中,当一个节点比父节点大,那么需要交换这个两个节点。交换后还可能比它新的父节点大,因此需要不断地进行比较和交换操作,把这种操作称为上浮。 -

+

```java private void swim(int k) { @@ -459,7 +428,7 @@ private void swim(int k) { 类似地,当一个节点比子节点来得小,也需要不断地向下进行比较和交换操作,把这种操作称为下沉。一个节点如果有两个子节点,应当与两个子节点中最大那个节点进行交换。 -

+

```java private void sink(int k) { @@ -475,7 +444,7 @@ private void sink(int k) { } ``` -## 3. 插入元素 +### 3. 插入元素 将新元素放到数组末尾,然后上浮到合适的位置。 @@ -486,7 +455,7 @@ public void insert(Comparable v) { } ``` -## 4. 删除最大元素 +### 4. 删除最大元素 从数组顶端删除最大的元素,并将数组的最后一个元素放到顶端,并让这个元素下沉到合适的位置。 @@ -500,21 +469,21 @@ public T delMax() { } ``` -## 5. 堆排序 +### 5. 堆排序 把最大元素和当前堆中数组的最后一个元素交换位置,并且不删除它,那么就可以得到一个从尾到头的递减序列,从正向来看就是一个递增序列,这就是堆排序。 -#### 5.1 构建堆 +##### 5.1 构建堆 无序数组建立堆最直接的方法是从左到右遍历数组进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。 -

+

-#### 5.2 交换堆顶元素与最后一个元素 +##### 5.2 交换堆顶元素与最后一个元素 交换之后需要进行下沉操作维持堆的有序状态。 -

+

```java public class HeapSort> extends Sort { @@ -551,7 +520,7 @@ public class HeapSort> extends Sort { } ``` -## 6. 分析 +### 6. 分析 一个堆的高度为 logN,因此在堆中插入元素和删除最大元素的复杂度都为 logN。 @@ -561,9 +530,9 @@ public class HeapSort> extends Sort { 现代操作系统很少使用堆排序,因为它无法利用局部性原理进行缓存,也就是数组元素很少和相邻的元素进行比较和交换。 -# 小结 +## 小结 -## 1. 排序算法的比较 +### 1. 排序算法的比较 | 算法 | 稳定性 | 时间复杂度 | 空间复杂度 | 备注 | | :---: | :---: |:---: | :---: | :---: | @@ -580,12 +549,6 @@ public class HeapSort> extends Sort { 使用三向切分快速排序,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。 -## 2. Java 的排序算法实现 - -Java 主要排序方法为 java.util.Arrays.sort(),对于原始数据类型使用三向切分的快速排序,对于引用类型使用归并排序。 - - - +### 2. Java 的排序算法实现 -
欢迎关注公众号,获取最新文章!


-
+Java 主要排序方法为 java.util.Arrays.sort(),对于原始数据类型使用三向切分的快速排序,对于引用类型使用归并排序。 diff --git "a/docs/notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" "b/notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" similarity index 93% rename from "docs/notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" rename to "notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" index 58c1c455ea..db808c2af2 100644 --- "a/docs/notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" +++ "b/notes/\347\256\227\346\263\225 - \346\240\210\345\222\214\351\230\237\345\210\227.md" @@ -1,12 +1,14 @@ +# 算法 - 栈和队列 -* [栈](#栈) - * [1. 数组实现](#1-数组实现) - * [2. 链表实现](#2-链表实现) -* [队列](#队列) +* [算法 - 栈和队列](#算法---栈和队列) + * [栈](#栈) + * [1. 数组实现](#1-数组实现) + * [2. 链表实现](#2-链表实现) + * [队列](#队列) -# 栈 +## 栈 ```java public interface MyStack extends Iterable { @@ -22,7 +24,7 @@ public interface MyStack extends Iterable { } ``` -## 1. 数组实现 +### 1. 数组实现 ```java public class ArrayStack implements MyStack { @@ -121,7 +123,7 @@ public class ArrayStack implements MyStack { } ``` -## 2. 链表实现 +### 2. 链表实现 需要使用链表的头插法来实现,因为头插法中最后压入栈的元素在链表的开头,它的 next 指针指向前一个压入栈的元素,在弹出元素时就可以通过 next 指针遍历到前一个压入栈的元素从而让这个元素成为新的栈顶元素。 @@ -208,7 +210,7 @@ public class ListStack implements MyStack { } ``` -# 队列 +## 队列 下面是队列的链表实现,需要维护 first 和 last 节点指针,分别指向队首和队尾。 @@ -316,9 +318,3 @@ public class ListQueue implements MyQueue { } } ``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" "b/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" new file mode 100644 index 0000000000..0dd1d8ec1b --- /dev/null +++ "b/notes/\347\256\227\346\263\225 - \347\233\256\345\275\225.md" @@ -0,0 +1,12 @@ +# 算法目录 + +- [算法分析](算法%20-%20算法分析.md) +- [排序](算法%20-%20排序.md) +- [并查集](算法%20-%20并查集.md) +- [栈和队列](算法%20-%20栈和队列.md) +- [符号表](算法%20-%20符号表.md) +- [其它](算法%20-%20其它.md) + +## 参考资料 + +- Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. diff --git "a/docs/notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" "b/notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" similarity index 80% rename from "docs/notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" rename to "notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" index c6d55cbc6f..bae9ad51d1 100644 --- "a/docs/notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" +++ "b/notes/\347\256\227\346\263\225 - \347\254\246\345\217\267\350\241\250.md" @@ -1,40 +1,42 @@ +# 算法 - 符号表 -* [前言](#前言) -* [初级实现](#初级实现) - * [1. 链表实现无序符号表](#1-链表实现无序符号表) - * [2. 二分查找实现有序符号表](#2-二分查找实现有序符号表) -* [二叉查找树](#二叉查找树) - * [1. get()](#1-get) - * [2. put()](#2-put) - * [3. 分析](#3-分析) - * [4. floor()](#4-floor) - * [5. rank()](#5-rank) - * [6. min()](#6-min) - * [7. deleteMin()](#7-deletemin) - * [8. delete()](#8-delete) - * [9. keys()](#9-keys) - * [10. 分析](#10-分析) -* [2-3 查找树](#2-3-查找树) - * [1. 插入操作](#1-插入操作) - * [2. 性质](#2-性质) -* [红黑树](#红黑树) - * [1. 左旋转](#1-左旋转) - * [2. 右旋转](#2-右旋转) - * [3. 颜色转换](#3-颜色转换) - * [4. 插入](#4-插入) - * [5. 分析](#5-分析) -* [散列表](#散列表) - * [1. 散列函数](#1-散列函数) - * [2. 拉链法](#2-拉链法) - * [3. 线性探测法](#3-线性探测法) -* [小结](#小结) - * [1. 符号表算法比较](#1-符号表算法比较) - * [2. Java 的符号表实现](#2-java-的符号表实现) - * [3. 稀疏向量乘法](#3-稀疏向量乘法) +* [算法 - 符号表](#算法---符号表) + * [前言](#前言) + * [初级实现](#初级实现) + * [1. 链表实现无序符号表](#1-链表实现无序符号表) + * [2. 二分查找实现有序符号表](#2-二分查找实现有序符号表) + * [二叉查找树](#二叉查找树) + * [1. get()](#1-get) + * [2. put()](#2-put) + * [3. 分析](#3-分析) + * [4. floor()](#4-floor) + * [5. rank()](#5-rank) + * [6. min()](#6-min) + * [7. deleteMin()](#7-deletemin) + * [8. delete()](#8-delete) + * [9. keys()](#9-keys) + * [10. 分析](#10-分析) + * [2-3 查找树](#2-3-查找树) + * [1. 插入操作](#1-插入操作) + * [2. 性质](#2-性质) + * [红黑树](#红黑树) + * [1. 左旋转](#1-左旋转) + * [2. 右旋转](#2-右旋转) + * [3. 颜色转换](#3-颜色转换) + * [4. 插入](#4-插入) + * [5. 分析](#5-分析) + * [散列表](#散列表) + * [1. 散列函数](#1-散列函数) + * [2. 拉链法](#2-拉链法) + * [3. 线性探测法](#3-线性探测法) + * [小结](#小结) + * [1. 符号表算法比较](#1-符号表算法比较) + * [2. Java 的符号表实现](#2-java-的符号表实现) + * [3. 稀疏向量乘法](#3-稀疏向量乘法) -# 前言 +## 前言 符号表(Symbol Table)是一种存储键值对的数据结构,可以支持快速查找操作。 @@ -74,9 +76,9 @@ public interface OrderedST, Value> { } ``` -# 初级实现 +## 初级实现 -## 1. 链表实现无序符号表 +### 1. 链表实现无序符号表 ```java public class ListUnorderedST implements UnorderedST { @@ -151,7 +153,7 @@ public class ListUnorderedST implements UnorderedST { } ``` -## 2. 二分查找实现有序符号表 +### 2. 二分查找实现有序符号表 使用一对平行数组,一个存储键一个存储值。 @@ -241,19 +243,17 @@ public class BinarySearchOrderedST, Value> implement } ``` -# 二叉查找树 +## 二叉查找树 -**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 +**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 -

+

-**二叉查找树** (BST)是一颗二叉树,并且每个节点的值都大于等于其左子树中的所有节点的值而小于等于右子树的所有节点的值。 - -

+**二叉查找树** (BST)是一颗二叉树,并且每个节点的值都大于等于其左子树中的所有节点的值而小于等于右子树的所有节点的值。 BST 有一个重要性质,就是它的中序遍历结果递增排序。 -

+

基本数据结构: @@ -298,7 +298,7 @@ public class BST, Value> implements OrderedST
+

```java @Override @@ -350,19 +350,19 @@ private Node put(Node x, Key key, Value value) { } ``` -## 3. 分析 +### 3. 分析 二叉查找树的算法运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。 最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。 -

+

在最坏的情况下,树的高度为 N。 -

+

-## 4. floor() +### 4. floor() floor(key):小于等于键的最大键 @@ -390,7 +390,7 @@ private Node floor(Node x, Key key) { } ``` -## 5. rank() +### 5. rank() rank(key) 返回 key 的排名。 @@ -417,7 +417,7 @@ private int rank(Key key, Node x) { } ``` -## 6. min() +### 6. min() ```java @Override @@ -434,11 +434,11 @@ private Node min(Node x) { } ``` -## 7. deleteMin() +### 7. deleteMin() 令指向最小节点的链接指向最小节点的右子树。 -

+

```java public void deleteMin() { @@ -454,12 +454,12 @@ public Node deleteMin(Node x) { } ``` -## 8. delete() +### 8. delete() - 如果待删除的节点只有一个子树, 那么只需要让指向待删除节点的链接指向唯一的子树即可; - 否则,让右子树的最小节点替换该节点。 -

+

```java public void delete(Key key) { @@ -488,7 +488,7 @@ private Node delete(Node x, Key key) { } ``` -## 9. keys() +### 9. keys() 利用二叉查找树中序遍历的结果为递增的特点。 @@ -514,17 +514,17 @@ private List keys(Node x, Key l, Key h) { } ``` -## 10. 分析 +### 10. 分析 二叉查找树所有操作在最坏的情况下所需要的时间都和树的高度成正比。 -# 2-3 查找树 +## 2-3 查找树 2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。 -

+

-## 1. 插入操作 +### 1. 插入操作 插入操作和 BST 的插入操作有很大区别,BST 的插入操作是先进行一次未命中的查找,然后再将节点插入到对应的空链接上。但是 2-3 查找树如果也这么做的话,那么就会破坏了平衡性。它是将新节点插入到叶子节点上。 @@ -532,23 +532,23 @@ private List keys(Node x, Key l, Key h) { - 如果插入到 2- 节点上,那么直接将新节点和原来的节点组成 3- 节点即可。 -

+

- 如果是插入到 3- 节点上,就会产生一个临时 4- 节点时,需要将 4- 节点分裂成 3 个 2- 节点,并将中间的 2- 节点移到上层节点中。如果上移操作继续产生临时 4- 节点则一直进行分裂上移,直到不存在临时 4- 节点。 -

+

-## 2. 性质 +### 2. 性质 2-3 查找树插入操作的变换都是局部的,除了相关的节点和链接之外不必修改或者检查树的其它部分,而这些局部变换不会影响树的全局有序性和平衡性。 2-3 查找树的查找和插入操作复杂度和插入顺序无关,在最坏的情况下查找和插入操作访问的节点必然不超过 logN 个,含有 10 亿个节点的 2-3 查找树最多只需要访问 30 个节点就能进行任意的查找和插入操作。 -# 红黑树 +## 红黑树 红黑树是 2-3 查找树,但它不需要分别定义 2- 节点和 3- 节点,而是在普通的二叉查找树之上,为节点添加颜色。指向一个节点的链接颜色如果为红色,那么这个节点和上层节点表示的是一个 3- 节点,而黑色则是普通链接。 -

+

红黑树具有以下性质: @@ -557,7 +557,7 @@ private List keys(Node x, Key l, Key h) { 画红黑树时可以将红链接画平。 -

+

```java public class RedBlackBST, Value> extends BST { @@ -573,11 +573,11 @@ public class RedBlackBST, Value> extends BST
+

```java public Node rotateLeft(Node h) { @@ -592,16 +592,17 @@ public Node rotateLeft(Node h) { } ``` -## 2. 右旋转 +### 2. 右旋转 进行右旋转是为了转换两个连续的左红链接,这会在之后的插入过程中探讨。 -

+

```java public Node rotateRight(Node h) { Node x = h.left; h.left = x.right; + x.right = h; x.color = h.color; h.color = RED; x.N = h.N; @@ -610,11 +611,11 @@ public Node rotateRight(Node h) { } ``` -## 3. 颜色转换 +### 3. 颜色转换 一个 4- 节点在红黑树中表现为一个节点的左右子节点都是红色的。分裂 4- 节点除了需要将子节点的颜色由红变黑之外,同时需要将父节点的颜色由黑变红,从 2-3 树的角度看就是将中间节点移到上层节点。 -

+

```java void flipColors(Node h) { @@ -624,7 +625,7 @@ void flipColors(Node h) { } ``` -## 4. 插入 +### 4. 插入 先将一个节点按二叉查找树的方法插入到正确位置,然后再进行如下颜色操作: @@ -632,7 +633,7 @@ void flipColors(Node h) { - 如果左子节点是红色的,而且左子节点的左子节点也是红色的,进行右旋转; - 如果左右子节点均为红色的,进行颜色转换。 -

+

```java @Override @@ -671,19 +672,19 @@ private Node put(Node x, Key key, Value value) { 根节点一定为黑色,因为根节点没有上层节点,也就没有上层节点的左链接指向根节点。flipColors() 有可能会使得根节点的颜色变为红色,每当根节点由红色变成黑色时树的黑链接高度加 1. -## 5. 分析 +### 5. 分析 一颗大小为 N 的红黑树的高度不会超过 2logN。最坏的情况下是它所对应的 2-3 树,构成最左边的路径节点全部都是 3- 节点而其余都是 2- 节点。 红黑树大多数的操作所需要的时间都是对数级别的。 -# 散列表 +## 散列表 散列表类似于数组,可以把散列表的散列值看成数组的索引值。访问散列表和访问数组元素一样快速,它可以在常数时间内实现查找和插入操作。 由于无法通过散列值知道键的大小关系,因此散列表无法实现有序性操作。 -## 1. 散列函数 +### 1. 散列函数 对于一个大小为 M 的散列表,散列函数能够把任意键转换为 [0, M-1] 内的正整数,该正整数即为 hash 值。 @@ -749,23 +750,24 @@ public class Transaction { } ``` -## 2. 拉链法 +### 2. 拉链法 拉链法使用链表来存储 hash 值相同的键,从而解决冲突。 查找需要分两步,首先查找 Key 所在的链表,然后在链表中顺序查找。 -对于 N 个键,M 条链表 (N>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M,因此未命中的查找和插入操作所需要的比较次数为 \~N/M。 +对于 N 个键,M 条链表 (N\>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M,因此未命中的查找和插入操作所需要的比较次数为 \~N/M。 -

+

-## 3. 线性探测法 +### 3. 线性探测法 线性探测法使用空位来解决冲突,当冲突发生时,向前探测一个空位来存储冲突的键。 -使用线性探测法,数组的大小 M 应当大于键的个数 N(M>N)。 +使用线性探测法,数组的大小 M 应当大于键的个数 N(M\>N)。 + -

+

```java public class LinearProbingHashST implements UnorderedST { @@ -795,7 +797,7 @@ public class LinearProbingHashST implements UnorderedST } ``` -#### 3.1 查找 +##### 3.1 查找 ```java public Value get(Key key) { @@ -807,7 +809,7 @@ public Value get(Key key) { } ``` -#### 3.2 插入 +##### 3.2 插入 ```java public void put(Key key, Value value) { @@ -829,7 +831,7 @@ private void putInternal(Key key, Value value) { } ``` -#### 3.3 删除 +##### 3.3 删除 删除操作应当将右侧所有相邻的键值对重新插入散列表中。 @@ -862,11 +864,12 @@ public void delete(Key key) { } ``` -#### 3.5 调整数组大小 +##### 3.5 调整数组大小 -线性探测法的成本取决于连续条目的长度,连续条目也叫聚簇。当聚簇很长时,在查找和插入时也需要进行很多次探测。例如下图中 2\~5 位置就是一个聚簇。 +线性探测法的成本取决于连续条目的长度,连续条目也叫聚簇。当聚簇很长时,在查找和插入时也需要进行很多次探测。例如下图中 2\~4 位置就是一个聚簇。 -

+ +

α = N/M,把 α 称为使用率。理论证明,当 α 小于 1/2 时探测的预计次数只在 1.5 到 2.5 之间。为了保证散列表的性能,应当调整数组的大小,使得 α 在 [1/4, 1/2] 之间。 @@ -890,9 +893,9 @@ private void resize(int cap) { } ``` -# 小结 +## 小结 -## 1. 符号表算法比较 +### 1. 符号表算法比较 | 算法 | 插入 | 查找 | 是否有序 | | :---: | :---: | :---: | :---: | @@ -905,12 +908,12 @@ private void resize(int cap) { 应当优先考虑散列表,当需要有序性操作时使用红黑树。 -## 2. Java 的符号表实现 +### 2. Java 的符号表实现 - java.util.TreeMap:红黑树 - java.util.HashMap:拉链法的散列表 -## 3. 稀疏向量乘法 +### 3. 稀疏向量乘法 当向量为稀疏向量时,可以使用符号表来存储向量中的非 0 索引和值,使得乘法运算只需要对那些非 0 元素进行即可。 @@ -937,9 +940,3 @@ public class SparseVector { } } ``` - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" "b/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" new file mode 100644 index 0000000000..6d1ff453be --- /dev/null +++ "b/notes/\347\256\227\346\263\225 - \347\256\227\346\263\225\345\210\206\346\236\220.md" @@ -0,0 +1,238 @@ +# 算法 - 算法分析 + +* [算法 - 算法分析](#算法---算法分析) + * [数学模型](#数学模型) + * [1. 近似](#1-近似) + * [2. 增长数量级](#2-增长数量级) + * [3. 内循环](#3-内循环) + * [4. 成本模型](#4-成本模型) + * [注意事项](#注意事项) + * [1. 大常数](#1-大常数) + * [2. 缓存](#2-缓存) + * [3. 对最坏情况下的性能的保证](#3-对最坏情况下的性能的保证) + * [4. 随机化算法](#4-随机化算法) + * [5. 均摊分析](#5-均摊分析) + * [ThreeSum](#threesum) + * [1. ThreeSumSlow](#1-threesumslow) + * [2. ThreeSumBinarySearch](#2-threesumbinarysearch) + * [3. ThreeSumTwoPointer](#3-threesumtwopointer) + * [倍率实验](#倍率实验) + + + +## 数学模型 + +### 1. 近似 + +N3/6-N2/2+N/3 \~ N3/6。使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数。 + +### 2. 增长数量级 + +N3/6-N2/2+N/3 的增长数量级为 O(N3)。增长数量级将算法与它的具体实现隔离开来,一个算法的增长数量级为 O(N3) 与它是否用 Java 实现,是否运行于特定计算机上无关。 + +### 3. 内循环 + +执行最频繁的指令决定了程序执行的总时间,把这些指令称为程序的内循环。 + +### 4. 成本模型 + +使用成本模型来评估算法,例如数组的访问次数就是一种成本模型。 + +## 注意事项 + +### 1. 大常数 + +在求近似时,如果低级项的常数系数很大,那么近似的结果是错误的。 + +### 2. 缓存 + +计算机系统会使用缓存技术来组织内存,访问数组相邻的元素会比访问不相邻的元素快很多。 + +### 3. 对最坏情况下的性能的保证 + +在核反应堆、心脏起搏器或者刹车控制器中的软件,最坏情况下的性能是十分重要的。 + +### 4. 随机化算法 + +通过打乱输入,去除算法对输入的依赖。 + +### 5. 均摊分析 + +将所有操作的总成本除于操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的次数为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素的次数,其余都是调整数组大小时进行复制需要的访问数组次数),均摊后访问数组的平均次数为常数。 + +## ThreeSum + +ThreeSum 用于统计一个数组中和为 0 的三元组数量。 + +```java +public interface ThreeSum { + int count(int[] nums); +} +``` + +### 1. ThreeSumSlow + +该算法的内循环为 `if (nums[i] + nums[j] + nums[k] == 0)` 语句,总共执行的次数为 N(N-1)(N-2) = N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 O(N3)。 + +```java +public class ThreeSumSlow implements ThreeSum { + @Override + public int count(int[] nums) { + int N = nums.length; + int cnt = 0; + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + for (int k = j + 1; k < N; k++) { + if (nums[i] + nums[j] + nums[k] == 0) { + cnt++; + } + } + } + } + return cnt; + } +} +``` + +### 2. ThreeSumBinarySearch + +将数组进行排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在和为 0 的三元组。 + +应该注意的是,只有数组不含有相同元素才能使用这种解法,否则二分查找的结果会出错。 + +该方法可以将 ThreeSum 算法增长数量级降低为 O(N2logN)。 + +```java +public class ThreeSumBinarySearch implements ThreeSum { + + @Override + public int count(int[] nums) { + Arrays.sort(nums); + int N = nums.length; + int cnt = 0; + for (int i = 0; i < N; i++) { + for (int j = i + 1; j < N; j++) { + int target = -nums[i] - nums[j]; + int index = BinarySearch.search(nums, target); + // 应该注意这里的下标必须大于 j,否则会重复统计。 + if (index > j) { + cnt++; + } + } + } + return cnt; + } +} +``` + +```java +public class BinarySearch { + + public static int search(int[] nums, int target) { + int l = 0, h = nums.length - 1; + while (l <= h) { + int m = l + (h - l) / 2; + if (target == nums[m]) { + return m; + } else if (target > nums[m]) { + l = m + 1; + } else { + h = m - 1; + } + } + return -1; + } +} +``` + +### 3. ThreeSumTwoPointer + +更有效的方法是先将数组排序,然后使用双指针进行查找,时间复杂度为 O(N2)。 + +同样不适用与数组存在重复元素的情况。 + +```java +public class ThreeSumTwoPointer implements ThreeSum { + + @Override + public int count(int[] nums) { + int N = nums.length; + int cnt = 0; + Arrays.sort(nums); + for (int i = 0; i < N - 2; i++) { + int l = i + 1, h = N - 1, target = -nums[i]; + while (l < h) { + int sum = nums[l] + nums[h]; + if (sum == target) { + cnt++; + l++; + h--; + } else if (sum < target) { + l++; + } else { + h--; + } + } + } + return cnt; + } +} +``` + +## 倍率实验 + +如果 T(N) \~ aNblogN,那么 T(2N)/T(N) \~ 2b。 + +例如对于暴力的 ThreeSum 算法,近似时间为 \~N3/6。进行如下实验:多次运行该算法,每次取的 N 值为前一次的两倍,统计每次执行的时间,并统计本次运行时间与前一次运行时间的比值,得到如下结果: + +| N | Time(ms) | Ratio | +| :---: | :---: | :---: | +| 500 | 48 | / | +| 1000 | 320 | 6.7 | +| 2000 | 555 | 1.7 | +| 4000 | 4105 | 7.4 | +| 8000 | 33575 | 8.2 | +| 16000 | 268909 | 8.0 | + +可以看到,T(2N)/T(N) \~ 23,因此可以确定 T(N) \~ aN3logN。 + +```java +public class RatioTest { + + public static void main(String[] args) { + int N = 500; + int loopTimes = 7; + double preTime = -1; + while (loopTimes-- > 0) { + int[] nums = new int[N]; + StopWatch.start(); + ThreeSum threeSum = new ThreeSumSlow(); + int cnt = threeSum.count(nums); + System.out.println(cnt); + double elapsedTime = StopWatch.elapsedTime(); + double ratio = preTime == -1 ? 0 : elapsedTime / preTime; + System.out.println(N + " " + elapsedTime + " " + ratio); + preTime = elapsedTime; + N *= 2; + } + } +} +``` + +```java +public class StopWatch { + + private static long start; + + + public static void start() { + start = System.currentTimeMillis(); + } + + + public static double elapsedTime() { + long now = System.currentTimeMillis(); + return (now - start) / 1000.0; + } +} +``` diff --git "a/notes/\347\256\227\346\263\225.md" "b/notes/\347\256\227\346\263\225.md" new file mode 100644 index 0000000000..ff8a64d050 --- /dev/null +++ "b/notes/\347\256\227\346\263\225.md" @@ -0,0 +1 @@ +[算法](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E7%AE%97%E6%B3%95%20-%20%E7%9B%AE%E5%BD%95.md) diff --git "a/docs/notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" "b/notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" similarity index 80% rename from "docs/notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" rename to "notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" index 9fa7dff8be..02ffa567d0 100644 --- "a/docs/notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" +++ "b/notes/\347\263\273\347\273\237\350\256\276\350\256\241\345\237\272\347\241\200.md" @@ -1,28 +1,30 @@ +# 系统设计基础 -* [一、性能](#一性能) -* [二、伸缩性](#二伸缩性) -* [三、扩展性](#三扩展性) -* [四、可用性](#四可用性) -* [五、安全性](#五安全性) -* [参考资料](#参考资料) +* [系统设计基础](#系统设计基础) + * [一、性能](#一性能) + * [二、伸缩性](#二伸缩性) + * [三、扩展性](#三扩展性) + * [四、可用性](#四可用性) + * [五、安全性](#五安全性) + * [参考资料](#参考资料) -# 一、性能 +## 一、性能 -## 性能指标 +### 性能指标 -### 1. 响应时间 +#### 1. 响应时间 指某个请求从发出到接收到响应消耗的时间。 -在对响应时间进行测试时,通常采用重复请求方式,然后计算平均响应时间。 +在对响应时间进行测试时,通常采用重复请求的方式,然后计算平均响应时间。 -### 2. 吞吐量 +#### 2. 吞吐量 指系统在单位时间内可以处理的请求数量,通常使用每秒的请求数来衡量。 -### 3. 并发用户数 +#### 3. 并发用户数 指系统能同时处理的并发用户请求数量。 @@ -37,13 +39,13 @@ 并发用户数不是越高越好,因为如果并发用户数太高,系统来不及处理这么多的请求,会使得过多的请求需要等待,那么响应时间就会大大提高。 -## 性能优化 +### 性能优化 -### 1. 集群 +#### 1. 集群 将多台服务器组成集群,使用负载均衡将请求转发到集群中,避免单一服务器的负载压力过大导致性能降低。 -### 2. 缓存 +#### 2. 缓存 缓存能够提高性能的原因如下: @@ -51,21 +53,21 @@ - 缓存数据可以位于靠近用户的地理位置上; - 可以将计算结果进行缓存,从而避免重复计算。 -### 3. 异步 +#### 3. 异步 某些流程可以将操作转换为消息,将消息发送到消息队列之后立即返回,之后这个操作会被异步处理。 -# 二、伸缩性 +## 二、伸缩性 指不断向集群中添加服务器来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。 -## 伸缩性与性能 +### 伸缩性与性能 如果系统存在性能问题,那么单个用户的请求总是很慢的; 如果系统存在伸缩性问题,那么单个用户的请求可能会很快,但是在并发数很高的情况下系统会很慢。 -## 实现伸缩性 +### 实现伸缩性 应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。 @@ -73,7 +75,7 @@ 对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。 -# 三、扩展性 +## 三、扩展性 指的是添加新功能时对现有系统的其它应用无影响,这就要求不同应用具备低耦合的特点。 @@ -82,9 +84,9 @@ - 使用消息队列进行解耦,应用之间通过消息传递进行通信; - 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。 -# 四、可用性 +## 四、可用性 -## 冗余 +### 冗余 保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。 @@ -92,24 +94,18 @@ 存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。 -## 监控 +### 监控 -对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个数据达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。 +对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个信息达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。 -## 服务降级 +### 服务降级 服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 -# 五、安全性 +## 五、安全性 要求系统在应对各种攻击手段时能够有可靠的应对措施。 -# 参考资料 +## 参考资料 - 大型网站技术架构:核心原理与案例分析 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/docs/notes/\347\274\223\345\255\230.md" "b/notes/\347\274\223\345\255\230.md" similarity index 79% rename from "docs/notes/\347\274\223\345\255\230.md" rename to "notes/\347\274\223\345\255\230.md" index c59257ea51..f20d1dd5ab 100644 --- "a/docs/notes/\347\274\223\345\255\230.md" +++ "b/notes/\347\274\223\345\255\230.md" @@ -1,36 +1,174 @@ +# 缓存 -* [一、缓存特征](#一缓存特征) -* [二、LRU](#二lru) -* [三、缓存位置](#三缓存位置) -* [四、CDN](#四cdn) -* [五、缓存问题](#五缓存问题) -* [六、数据分布](#六数据分布) -* [七、一致性哈希](#七一致性哈希) -* [参考资料](#参考资料) +* [缓存](#缓存) + * [一、缓存特征](#一缓存特征) + * [二、缓存位置](#二缓存位置) + * [三、CDN](#三cdn) + * [四、缓存问题](#四缓存问题) + * [五、数据分布](#五数据分布) + * [六、一致性哈希](#六一致性哈希) + * [七、LRU](#七lru) + * [参考资料](#参考资料) -# 一、缓存特征 +## 一、缓存特征 -## 命中率 +### 命中率 当某个请求能够通过访问缓存而得到响应时,称为缓存命中。 缓存命中率越高,缓存的利用率也就越高。 -## 最大空间 +### 最大空间 缓存通常位于内存中,内存的空间通常比磁盘空间小的多,因此缓存的最大空间不可能非常大。 当缓存存放的数据量超过最大空间时,就需要淘汰部分数据来存放新到达的数据。 -## 淘汰策略 +### 淘汰策略 - FIFO(First In First Out):先进先出策略,在实时性的场景下,需要经常访问最新的数据,那么就可以使用 FIFO,使得最先进入的数据(最晚的数据)被淘汰。 - LRU(Least Recently Used):最近最久未使用策略,优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最久的数据。该策略可以保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率。 -# 二、LRU +- LFU(Least Frequently Used):最不经常使用策略,优先淘汰一段时间内使用次数最少的数据。 + +## 二、缓存位置 + +### 浏览器 + +当 HTTP 响应允许进行缓存时,浏览器会将 HTML、CSS、JavaScript、图片等静态资源进行缓存。 + +### ISP + +网络服务提供商(ISP)是网络访问的第一跳,通过将数据缓存在 ISP 中能够大大提高用户的访问速度。 + +### 反向代理 + +反向代理位于服务器之前,请求与响应都需要经过反向代理。通过将数据缓存在反向代理,在用户请求反向代理时就可以直接使用缓存进行响应。 + +### 本地缓存 + +使用 Guava Cache 将数据缓存在服务器本地内存中,服务器代码可以直接读取本地内存中的缓存,速度非常快。 + +### 分布式缓存 + +使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。 + +相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,而本地缓存需要在服务器集群之间进行同步,实现难度和性能开销上都非常大。 + +### 数据库缓存 + +MySQL 等数据库管理系统具有自己的查询缓存机制来提高查询效率。 + +### Java 内部的缓存 + +Java 为了优化空间,提高字符串、基本数据类型包装类的创建效率,设计了字符串常量池及 Byte、Short、Character、Integer、Long、Boolean 这六种包装类缓冲池。 + +### CPU 多级缓存 + +CPU 为了解决运算速度与主存 IO 速度不匹配的问题,引入了多级缓存结构,同时使用 MESI 等缓存一致性协议来解决多核 CPU 缓存数据一致性的问题。 + +## 三、CDN + +内容分发网络(Content distribution network,CDN)是一种互连的网络系统,它利用更靠近用户的服务器从而更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。 + +CDN 主要有以下优点: + +- 更快地将数据分发给用户; +- 通过部署多台服务器,从而提高系统整体的带宽性能; +- 多台服务器可以看成是一种冗余机制,从而具有高可用性。 + +

+ +## 四、缓存问题 + +### 缓存穿透 + +指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存到达数据库。 + +解决方案: + +- 对这些不存在的数据缓存一个空数据; +- 对这类请求进行过滤。 + +### 缓存雪崩 + +指的是由于数据没有被加载到缓存中,或者缓存数据在同一时间大面积失效(过期),又或者缓存服务器宕机,导致大量的请求都到达数据库。 + +在有缓存的系统中,系统非常依赖于缓存,缓存分担了很大一部分的数据请求。当发生缓存雪崩时,数据库无法处理这么大的请求,导致数据库崩溃。 + +解决方案: + +- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现; +- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。 +- 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。 + + +### 缓存一致性 + +缓存一致性要求数据更新的同时缓存数据也能够实时更新。 + +解决方案: + +- 在数据更新的同时立即去更新缓存; +- 在读缓存之前先判断缓存是否是最新的,如果不是最新的先进行更新。 + +要保证缓存一致性需要付出很大的代价,缓存数据最好是那些对一致性要求不高的数据,允许缓存数据存在一些脏数据。 + +### 缓存 “无底洞” 现象 + +指的是为了满足业务要求添加了大量缓存节点,但是性能不但没有好转反而下降了的现象。 + +产生原因:缓存系统通常采用 hash 函数将 key 映射到对应的缓存节点,随着缓存节点数目的增加,键值分布到更多的节点上,导致客户端一次批量操作会涉及多次网络操作,这意味着批量操作的耗时会随着节点数目的增加而不断增大。此外,网络连接数变多,对节点的性能也有一定影响。 + +解决方案: + +- 优化批量数据操作命令; +- 减少网络通信次数; +- 降低接入成本,使用长连接 / 连接池,NIO 等。 + +## 五、数据分布 + +### 哈希分布 + +哈希分布就是将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。 + +传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。 + +### 顺序分布 + +将数据划分为多个连续的部分,按数据的 ID 或者时间分布到不同节点上。例如 User 表的 ID 范围为 1 \~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 \~ 1000,1001 \~ 2000,...,6001 \~ 7000。 + +顺序分布相比于哈希分布的主要优点如下: + +- 能保持数据原有的顺序; +- 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。 + +## 六、一致性哈希 + +Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据迁移的问题。 + +### 基本原理 + +将哈希空间 [0, 2n-1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。 + +

+ +一致性哈希在增加或者删除节点时只会影响到哈希环中相邻的节点,例如下图中新增节点 X,只需要将它前一个节点 C 上的数据重新进行分布即可,对于节点 A、B、D 都没有影响。 + +

+ +### 虚拟节点 + +上面描述的一致性哈希存在数据分布不均匀的问题,节点存储的数据量有可能会存在很大的不同。 + +数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。 + +解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得多,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。 + +## 七、LRU 以下是基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下: @@ -163,129 +301,10 @@ public class LRU implements Iterable { } ``` -# 三、缓存位置 - -## 浏览器 - -当 HTTP 响应允许进行缓存时,浏览器会将 HTML、CSS、JavaScript、图片等静态资源进行缓存。 - -## ISP - -网络服务提供商(ISP)是网络访问的第一跳,通过将数据缓存在 ISP 中能够大大提高用户的访问速度。 - -## 反向代理 - -反向代理位于服务器之前,请求与响应都需要经过反向代理。通过将数据缓存在反向代理,在用户请求反向代理时就可以直接使用缓存进行响应。 - -## 本地缓存 - -使用 Guava Cache 将数据缓存在服务器本地内存中,服务器代码可以直接读取本地内存中的缓存,速度非常快。 - -## 分布式缓存 - -使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。 - -相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,而本地缓存需要在服务器集群之间进行同步,实现难度和性能开销上都非常大。 - -## 数据库缓存 - -MySQL 等数据库管理系统具有自己的查询缓存机制来提高查询效率。 - -# 四、CDN - -内容分发网络(Content distribution network,CDN)是一种互连的网络系统,它利用更靠近用户的服务器从而更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。 - -CDN 主要有以下优点: - -- 更快地将数据分发给用户; -- 通过部署多台服务器,从而提高系统整体的带宽性能; -- 多台服务器可以看成是一种冗余机制,从而具有高可用性。 - -

- -# 五、缓存问题 - -## 缓存穿透 - -指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存到达数据库。 - -解决方案: - -- 对这些不存在的数据缓存一个空数据; -- 对这类请求进行过滤。 - -## 缓存雪崩 - -指的是由于数据没有被加载到缓存中,或者缓存数据在同一时间大面积失效(过期),又或者缓存服务器宕机,导致大量的请求都到达数据库。 - -在有缓存的系统中,系统非常依赖于缓存,缓存分担了很大一部分的数据请求。当发生缓存雪崩时,数据库无法处理这么大的请求,导致数据库崩溃。 - -解决方案: - -- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现; -- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。 -- 也可以进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。 - -## 缓存一致性 - -缓存一致性要求数据更新的同时缓存数据也能够实时更新。 - -解决方案: - -- 在数据更新的同时立即去更新缓存; -- 在读缓存之前先判断缓存是否是最新的,如果不是最新的先进行更新。 - -要保证缓存一致性需要付出很大的代价,缓存数据最好是那些对一致性要求不高的数据,允许缓存数据存在一些脏数据。 - -# 六、数据分布 - -## 哈希分布 - -哈希分布就是将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key)%N。 - -传统的哈希分布算法存在一个问题:当节点数量变化时,也就是 N 值变化,那么几乎所有的数据都需要重新分布,将导致大量的数据迁移。 - -## 顺序分布 - -将数据划分为多个连续的部分,按数据的 ID 或者时间分布到不同节点上。例如 User 表的 ID 范围为 1 \~ 7000,使用顺序分布可以将其划分成多个子表,对应的主键范围为 1 \~ 1000,1001 \~ 2000,...,6001 \~ 7000。 - -顺序分布相比于哈希分布的主要优点如下: - -- 能保持数据原有的顺序; -- 并且能够准确控制每台服务器存储的数据量,从而使得存储空间的利用率最大。 - -# 七、一致性哈希 - -Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了克服传统哈希分布在服务器节点数量变化时大量数据迁移的问题。 - -## 基本原理 - -将哈希空间 [0, 2n-1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。 - -

- -一致性哈希在增加或者删除节点时只会影响到哈希环中相邻的节点,例如下图中新增节点 X,只需要将它前一个节点 C 上的数据重新进行分布即可,对于节点 A、B、D 都没有影响。 - -

- -## 虚拟节点 - -上面描述的一致性哈希存在数据分布不均匀的问题,节点存储的数据量有可能会存在很大的不同。 - -数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。 - -解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得多,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。 - -# 参考资料 +## 参考资料 - 大规模分布式存储系统 - [缓存那些事](https://tech.meituan.com/cache_about.html) - [一致性哈希算法](https://my.oschina.net/jayhu/blog/732849) - [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF) - [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" new file mode 100644 index 0000000000..9b0b78be2c --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \345\206\205\345\255\230\347\256\241\347\220\206.md" @@ -0,0 +1,141 @@ +# 计算机操作系统 - 内存管理 + +* [计算机操作系统 - 内存管理](#计算机操作系统---内存管理) + * [虚拟内存](#虚拟内存) + * [分页系统地址映射](#分页系统地址映射) + * [页面置换算法](#页面置换算法) + * [1. 最佳](#1-最佳) + * [2. 最近最久未使用](#2-最近最久未使用) + * [3. 最近未使用](#3-最近未使用) + * [4. 先进先出](#4-先进先出) + * [5. 第二次机会算法](#5-第二次机会算法) + * [6. 时钟](#6-时钟) + * [分段](#分段) + * [段页式](#段页式) + * [分页与分段的比较](#分页与分段的比较) + + + +## 虚拟内存 + +虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 + +为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。 + +从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。 + +

+ +## 分页系统地址映射 + +内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。 + +一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。 + +下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。 + +

+ +## 页面置换算法 + +在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。 + +页面置换算法和缓存淘汰策略类似,可以将内存看成磁盘的缓存。在缓存系统中,缓存的大小有限,当有新的缓存到达时,需要淘汰一部分已经存在的缓存,这样才有空间存放新的缓存数据。 + +页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。 + +### 1. 最佳 + +> OPT, Optimal replacement algorithm + +所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。 + +是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。 + +举例:一个系统为某进程分配了三个物理块,并有如下页面引用序列: + +```html +7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1 +``` + +开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。 + +### 2. 最近最久未使用 + +> LRU, Least Recently Used + +虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。 + +为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。 + +因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。 + +```html +4,7,0,7,1,0,1,2,1,2,6 +``` + +

+### 3. 最近未使用 + +> NRU, Not Recently Used + +每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类: + +- R=0,M=0 +- R=0,M=1 +- R=1,M=0 +- R=1,M=1 + +当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。 + +NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。 + +### 4. 先进先出 + +> FIFO, First In First Out + +选择换出的页面是最先进入的页面。 + +该算法会将那些经常被访问的页面换出,导致缺页率升高。 + +### 5. 第二次机会算法 + +FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改: + +当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 + +

+ +### 6. 时钟 + +> Clock + +第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 + +

+ +## 分段 + +虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。 + +下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。 + +

+ +分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 + +

+ +## 段页式 + +程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。 + +## 分页与分段的比较 + +- 对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。 + +- 地址空间的维度:分页是一维地址空间,分段是二维的。 + +- 大小是否可以改变:页的大小不可变,段的大小可以动态改变。 + +- 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。 diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" new file mode 100644 index 0000000000..e58db1a678 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\246\202\350\277\260.md" @@ -0,0 +1,124 @@ +# 计算机操作系统 - 概述 + +* [计算机操作系统 - 概述](#计算机操作系统---概述) + * [基本特征](#基本特征) + * [1. 并发](#1-并发) + * [2. 共享](#2-共享) + * [3. 虚拟](#3-虚拟) + * [4. 异步](#4-异步) + * [基本功能](#基本功能) + * [1. 进程管理](#1-进程管理) + * [2. 内存管理](#2-内存管理) + * [3. 文件管理](#3-文件管理) + * [4. 设备管理](#4-设备管理) + * [系统调用](#系统调用) + * [宏内核和微内核](#宏内核和微内核) + * [1. 宏内核](#1-宏内核) + * [2. 微内核](#2-微内核) + * [中断分类](#中断分类) + * [1. 外中断](#1-外中断) + * [2. 异常](#2-异常) + * [3. 陷入](#3-陷入) + + + +## 基本特征 + +### 1. 并发 + +并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。 + +并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。 + +操作系统通过引入进程和线程,使得程序能够并发运行。 + +### 2. 共享 + +共享是指系统中的资源可以被多个并发进程共同使用。 + +有两种共享方式:互斥共享和同时共享。 + +互斥共享的资源称为临界资源,例如打印机等,在同一时刻只允许一个进程访问,需要用同步机制来实现互斥访问。 + +### 3. 虚拟 + +虚拟技术把一个物理实体转换为多个逻辑实体。 + +主要有两种虚拟技术:时(时间)分复用技术和空(空间)分复用技术。 + +多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。 + +虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。 + +### 4. 异步 + +异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。 + +## 基本功能 + +### 1. 进程管理 + +进程控制、进程同步、进程通信、死锁处理、处理机调度等。 + +### 2. 内存管理 + +内存分配、地址映射、内存保护与共享、虚拟内存等。 + +### 3. 文件管理 + +文件存储空间的管理、目录管理、文件读写管理和保护等。 + +### 4. 设备管理 + +完成用户的 I/O 请求,方便用户使用各种设备,并提高设备的利用率。 + +主要包括缓冲管理、设备分配、设备处理、虛拟设备等。 + +## 系统调用 + +如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。 + +

+ +Linux 的系统调用主要有以下这些: + +| Task | Commands | +| :---: | --- | +| 进程控制 | fork(); exit(); wait(); | +| 进程通信 | pipe(); shmget(); mmap(); | +| 文件操作 | open(); read(); write(); | +| 设备操作 | ioctl(); read(); write(); | +| 信息维护 | getpid(); alarm(); sleep(); | +| 安全 | chmod(); umask(); chown(); | + +## 宏内核和微内核 + +### 1. 宏内核 + +宏内核是将操作系统功能作为一个紧密结合的整体放到内核。 + +由于各模块共享信息,因此有很高的性能。 + +### 2. 微内核 + +由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。 + +在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。 + +因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。 + +

+ +## 中断分类 + +### 1. 外中断 + +由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。 + +### 2. 异常 + +由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。 + +### 3. 陷入 + +在用户程序中使用系统调用。 diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" new file mode 100644 index 0000000000..d58e7f4344 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \346\255\273\351\224\201.md" @@ -0,0 +1,144 @@ +# 计算机操作系统 - 死锁 + +* [计算机操作系统 - 死锁](#计算机操作系统---死锁) + * [必要条件](#必要条件) + * [处理方法](#处理方法) + * [鸵鸟策略](#鸵鸟策略) + * [死锁检测与死锁恢复](#死锁检测与死锁恢复) + * [1. 每种类型一个资源的死锁检测](#1-每种类型一个资源的死锁检测) + * [2. 每种类型多个资源的死锁检测](#2-每种类型多个资源的死锁检测) + * [3. 死锁恢复](#3-死锁恢复) + * [死锁预防](#死锁预防) + * [1. 破坏互斥条件](#1-破坏互斥条件) + * [2. 破坏占有和等待条件](#2-破坏占有和等待条件) + * [3. 破坏不可抢占条件](#3-破坏不可抢占条件) + * [4. 破坏环路等待](#4-破坏环路等待) + * [死锁避免](#死锁避免) + * [1. 安全状态](#1-安全状态) + * [2. 单个资源的银行家算法](#2-单个资源的银行家算法) + * [3. 多个资源的银行家算法](#3-多个资源的银行家算法) + + + +## 必要条件 + +

+ +- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。 +- 占有和等待:已经得到了某个资源的进程可以再请求新的资源。 +- 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。 +- 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。 + +## 处理方法 + +主要有以下四种方法: + +- 鸵鸟策略 +- 死锁检测与死锁恢复 +- 死锁预防 +- 死锁避免 + +## 鸵鸟策略 + +把头埋在沙子里,假装根本没发生问题。 + +因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。 + +当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。 + +大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。 + +## 死锁检测与死锁恢复 + +不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复。 + +### 1. 每种类型一个资源的死锁检测 + +

+ +上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。 + +图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。 + +每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。 + +### 2. 每种类型多个资源的死锁检测 + +

+ +上图中,有三个进程四个资源,每个数据代表的含义如下: + +- E 向量:资源总量 +- A 向量:资源剩余量 +- C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量 +- R 矩阵:每个进程请求的资源数量 + +进程 P1 和 P2 所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。 + +算法总结如下: + +每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。 + +1. 寻找一个没有标记的进程 Pi,它所请求的资源小于等于 A。 +2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1。 +3. 如果没有这样一个进程,算法终止。 + +### 3. 死锁恢复 + +- 利用抢占恢复 +- 利用回滚恢复 +- 通过杀死进程恢复 + +## 死锁预防 + +在程序运行之前预防发生死锁。 + +### 1. 破坏互斥条件 + +例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。 + +### 2. 破坏占有和等待条件 + +一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。 + +### 3. 破坏不可抢占条件 + +### 4. 破坏环路等待 + +给资源统一编号,进程只能按编号顺序来请求资源。 + +## 死锁避免 + +在程序运行时避免发生死锁。 + +### 1. 安全状态 + +

+ +图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。 + +定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。 + +安全状态的检测与死锁的检测类似,因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似,可以结合着做参考对比。 + +### 2. 单个资源的银行家算法 + +一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 + +

+ +上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。 + +### 3. 多个资源的银行家算法 + +

+ +上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。 + +检查一个状态是否安全的算法如下: + +- 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。 +- 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。 +- 重复以上两步,直到所有进程都标记为终止,则状态时安全的。 + +如果一个状态不是安全的,需要拒绝进入这个状态。 diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" similarity index 77% rename from "docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" rename to "notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" index cbe35f74c0..f305431165 100644 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \347\233\256\345\275\225.md" @@ -1,10 +1,4 @@ - -* [目录](#目录) -* [参考资料](#参考资料) - - - -# 目录 +# 计算机操作系统 - [概述](计算机操作系统%20-%20概述.md) - [进程管理](计算机操作系统%20-%20进程管理.md) @@ -13,7 +7,7 @@ - [设备管理](计算机操作系统%20-%20设备管理.md) - [链接](计算机操作系统%20-%20链接.md) -# 参考资料 +## 参考资料 - Tanenbaum A S, Bos H. Modern operating systems[M]. Prentice Hall Press, 2014. - 汤子瀛, 哲凤屏, 汤小丹. 计算机操作系统[M]. 西安电子科技大学出版社, 2001. @@ -24,9 +18,3 @@ - [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php) - [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1) - [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" new file mode 100644 index 0000000000..95c7924d79 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\256\276\345\244\207\347\256\241\347\220\206.md" @@ -0,0 +1,61 @@ +# 计算机操作系统 - 设备管理 + +* [计算机操作系统 - 设备管理](#计算机操作系统---设备管理) + * [磁盘结构](#磁盘结构) + * [磁盘调度算法](#磁盘调度算法) + * [1. 先来先服务](#1-先来先服务) + * [2. 最短寻道时间优先](#2-最短寻道时间优先) + * [3. 电梯算法](#3-电梯算法) + + + +## 磁盘结构 + +- 盘面(Platter):一个磁盘有多个盘面; +- 磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道; +- 扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小; +- 磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写); +- 制动手臂(Actuator arm):用于在磁道之间移动磁头; +- 主轴(Spindle):使整个盘面转动。 + +

+ +## 磁盘调度算法 + +读写一个磁盘块的时间的影响因素有: + +- 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上) +- 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上) +- 实际的数据传输时间 + +其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。 + +### 1. 先来先服务 + +> FCFS, First Come First Served + +按照磁盘请求的顺序进行调度。 + +优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。 + +### 2. 最短寻道时间优先 + +> SSTF, Shortest Seek Time First + +优先调度与当前磁头所在磁道距离最近的磁道。 + +虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。 + +

+ +### 3. 电梯算法 + +> SCAN + +电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。 + +电梯算法(扫描算法)和电梯的运行过程类似,总是按一个方向来进行磁盘调度,直到该方向上没有未完成的磁盘请求,然后改变方向。 + +因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。 + +

diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" similarity index 77% rename from "docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" rename to "notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" index f62a42a8ef..72f8fde9bd 100644 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \350\277\233\347\250\213\347\256\241\347\220\206.md" @@ -1,34 +1,36 @@ +# 计算机操作系统 - 进程管理 -* [进程与线程](#进程与线程) - * [1. 进程](#1-进程) - * [2. 线程](#2-线程) - * [3. 区别](#3-区别) -* [进程状态的切换](#进程状态的切换) -* [进程调度算法](#进程调度算法) - * [1. 批处理系统](#1-批处理系统) - * [2. 交互式系统](#2-交互式系统) - * [3. 实时系统](#3-实时系统) -* [进程同步](#进程同步) - * [1. 临界区](#1-临界区) - * [2. 同步与互斥](#2-同步与互斥) - * [3. 信号量](#3-信号量) - * [4. 管程](#4-管程) -* [经典同步问题](#经典同步问题) - * [1. 读者-写者问题](#1-读者-写者问题) - * [2. 哲学家进餐问题](#2-哲学家进餐问题) -* [进程通信](#进程通信) - * [1. 管道](#1-管道) - * [2. FIFO](#2-fifo) - * [3. 消息队列](#3-消息队列) - * [4. 信号量](#4-信号量) - * [5. 共享存储](#5-共享存储) - * [6. 套接字](#6-套接字) +* [计算机操作系统 - 进程管理](#计算机操作系统---进程管理) + * [进程与线程](#进程与线程) + * [1. 进程](#1-进程) + * [2. 线程](#2-线程) + * [3. 区别](#3-区别) + * [进程状态的切换](#进程状态的切换) + * [进程调度算法](#进程调度算法) + * [1. 批处理系统](#1-批处理系统) + * [2. 交互式系统](#2-交互式系统) + * [3. 实时系统](#3-实时系统) + * [进程同步](#进程同步) + * [1. 临界区](#1-临界区) + * [2. 同步与互斥](#2-同步与互斥) + * [3. 信号量](#3-信号量) + * [4. 管程](#4-管程) + * [经典同步问题](#经典同步问题) + * [1. 哲学家进餐问题](#1-哲学家进餐问题) + * [2. 读者-写者问题](#2-读者-写者问题) + * [进程通信](#进程通信) + * [1. 管道](#1-管道) + * [2. FIFO](#2-fifo) + * [3. 消息队列](#3-消息队列) + * [4. 信号量](#4-信号量) + * [5. 共享存储](#5-共享存储) + * [6. 套接字](#6-套接字) -# 进程与线程 +## 进程与线程 -## 1. 进程 +### 1. 进程 进程是资源分配的基本单位。 @@ -36,9 +38,9 @@ 下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。 -

+

-## 2. 线程 +### 2. 线程 线程是独立调度的基本单位。 @@ -46,9 +48,9 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。 -

+

-## 3. 区别 +### 3. 区别 Ⅰ 拥有资源 @@ -66,9 +68,9 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H 线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。 -# 进程状态的切换 +## 进程状态的切换 -

+

- 就绪状态(ready):等待被调度 - 运行状态(running) @@ -79,35 +81,35 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H - 只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。 - 阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。 -# 进程调度算法 +## 进程调度算法 不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。 -## 1. 批处理系统 +### 1. 批处理系统 批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。 -**1.1 先来先服务 first-come first-serverd(FCFS)** +**1.1 先来先服务 first-come first-serverd(FCFS)** -按照请求的顺序进行调度。 +非抢占式的调度算法,按照请求的顺序进行调度。 有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。 -**1.2 短作业优先 shortest job first(SJF)** +**1.2 短作业优先 shortest job first(SJF)** -按估计运行时间最短的顺序进行调度。 +非抢占式的调度算法,按估计运行时间最短的顺序进行调度。 长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。 -**1.3 最短剩余时间优先 shortest remaining time next(SRTN)** +**1.3 最短剩余时间优先 shortest remaining time next(SRTN)** -按估计剩余时间最短的顺序进行调度。 +最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。 -## 2. 交互式系统 +### 2. 交互式系统 交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。 -**2.1 时间片轮转** +**2.1 时间片轮转** 将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。 @@ -116,15 +118,15 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H - 因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 - 而如果时间片过长,那么实时性就不能得到保证。 -

+

-**2.2 优先级调度** +**2.2 优先级调度** 为每个进程分配一个优先级,按优先级进行调度。 为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。 -**2.3 多级反馈队列** +**2.3 多级反馈队列** 一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。 @@ -134,17 +136,17 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H 可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 -

+

-## 3. 实时系统 +### 3. 实时系统 实时系统要求一个请求在一个确定时间内得到响应。 分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。 -# 进程同步 +## 进程同步 -## 1. 临界区 +### 1. 临界区 对临界资源进行访问的那段代码称为临界区。 @@ -156,21 +158,21 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H // exit section ``` -## 2. 同步与互斥 +### 2. 同步与互斥 -- 同步:多个进程按一定顺序执行; +- 同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。 - 互斥:多个进程在同一时刻只有一个进程能进入临界区。 -## 3. 信号量 +### 3. 信号量 信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。 -- **down** : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0; -- **up** :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。 +- **down** : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0; +- **up** :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。 down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。 -如果信号量的取值只能为 0 或者 1,那么就成为了 **互斥量(Mutex)** ,0 表示临界区已经加锁,1 表示临界区解锁。 +如果信号量的取值只能为 0 或者 1,那么就成为了 **互斥量(Mutex)** ,0 表示临界区已经加锁,1 表示临界区解锁。 ```c typedef int semaphore; @@ -188,7 +190,7 @@ void P2() { } ``` - **使用信号量实现生产者-消费者问题**
+\ **使用信号量实现生产者-消费者问题** \ \ 问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。 @@ -228,7 +230,7 @@ void consumer() { } ``` -## 4. 管程 +### 4. 管程 使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制,而管程把控制的代码独立出来,不仅不容易出错,也使得客户端代码调用更容易。 @@ -251,11 +253,11 @@ monitor ProducerConsumer end monitor; ``` -管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否者其它进程永远不能使用管程。 +管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。 -管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 +管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 - **使用管程实现生产者-消费者问题**
+ **使用管程实现生产者-消费者问题**
```pascal // 管程 @@ -302,11 +304,91 @@ begin end; ``` -# 经典同步问题 +## 经典同步问题 生产者和消费者问题前面已经讨论过了。 -## 1. 读者-写者问题 +### 1. 哲学家进餐问题 + +

+ +五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 + +下面是一种错误的解法,如果所有哲学家同时拿起左手边的筷子,那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子,导致死锁。 + +```c +#define N 5 + +void philosopher(int i) { + while(TRUE) { + think(); + take(i); // 拿起左边的筷子 + take((i+1)%N); // 拿起右边的筷子 + eat(); + put(i); + put((i+1)%N); + } +} +``` + +为了防止死锁的发生,可以设置两个条件: + +- 必须同时拿起左右两根筷子; +- 只有在两个邻居都没有进餐的情况下才允许进餐。 + +```c +#define N 5 +#define LEFT (i + N - 1) % N // 左邻居 +#define RIGHT (i + 1) % N // 右邻居 +#define THINKING 0 +#define HUNGRY 1 +#define EATING 2 +typedef int semaphore; +int state[N]; // 跟踪每个哲学家的状态 +semaphore mutex = 1; // 临界区的互斥,临界区是 state 数组,对其修改需要互斥 +semaphore s[N]; // 每个哲学家一个信号量 + +void philosopher(int i) { + while(TRUE) { + think(i); + take_two(i); + eat(i); + put_two(i); + } +} + +void take_two(int i) { + down(&mutex); + state[i] = HUNGRY; + check(i); + up(&mutex); + down(&s[i]); // 只有收到通知之后才可以开始吃,否则会一直等下去 +} + +void put_two(i) { + down(&mutex); + state[i] = THINKING; + check(LEFT); // 尝试通知左右邻居,自己吃完了,你们可以开始吃了 + check(RIGHT); + up(&mutex); +} + +void eat(int i) { + down(&mutex); + state[i] = EATING; + up(&mutex); +} + +// 检查两个邻居是否都没有用餐,如果是的话,就 up(&s[i]),使得 down(&s[i]) 能够得到通知并继续执行 +void check(i) { + if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) { + state[i] = EATING; + up(&s[i]); + } +} +``` + +### 2. 读者-写者问题 允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。 @@ -345,7 +427,7 @@ void writer() { The first case may result Writer to starve. This case favous Writers i.e no writer, once added to the queue, shall be kept waiting longer than absolutely necessary(only when there are readers that entered the queue before the writer). -```source-c +```c int readcount, writecount; //(initial value = 0) semaphore rmutex, wmutex, readLock, resource; //(initial value = 1) @@ -450,81 +532,7 @@ void reader() ``` - -## 2. 哲学家进餐问题 - -

- -五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 - -下面是一种错误的解法,考虑到如果所有哲学家同时拿起左手边的筷子,那么就无法拿起右手边的筷子,造成死锁。 - -```c -#define N 5 - -void philosopher(int i) { - while(TRUE) { - think(); - take(i); // 拿起左边的筷子 - take((i+1)%N); // 拿起右边的筷子 - eat(); - put(i); - put((i+1)%N); - } -} -``` - -为了防止死锁的发生,可以设置两个条件: - -- 必须同时拿起左右两根筷子; -- 只有在两个邻居都没有进餐的情况下才允许进餐。 - -```c -#define N 5 -#define LEFT (i + N - 1) % N // 左邻居 -#define RIGHT (i + 1) % N // 右邻居 -#define THINKING 0 -#define HUNGRY 1 -#define EATING 2 -typedef int semaphore; -int state[N]; // 跟踪每个哲学家的状态 -semaphore mutex = 1; // 临界区的互斥 -semaphore s[N]; // 每个哲学家一个信号量 - -void philosopher(int i) { - while(TRUE) { - think(); - take_two(i); - eat(); - put_two(i); - } -} - -void take_two(int i) { - down(&mutex); - state[i] = HUNGRY; - test(i); - up(&mutex); - down(&s[i]); -} - -void put_two(i) { - down(&mutex); - state[i] = THINKING; - test(LEFT); - test(RIGHT); - up(&mutex); -} - -void test(i) { // 尝试拿起两把筷子 - if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] !=EATING) { - state[i] = EATING; - up(&s[i]); - } -} -``` - -# 进程通信 +## 进程通信 进程同步与进程通信很容易混淆,它们的区别在于: @@ -533,7 +541,7 @@ void test(i) { // 尝试拿起两把筷子 进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。 -## 1. 管道 +### 1. 管道 管道是通过调用 pipe 函数创建的,fd[0] 用于读,fd[1] 用于写。 @@ -545,11 +553,11 @@ int pipe(int fd[2]); 它具有以下限制: - 只支持半双工通信(单向交替传输); -- 只能在父子进程中使用。 +- 只能在父子进程或者兄弟进程中使用。 -

+

-## 2. FIFO +### 2. FIFO 也称为命名管道,去除了管道只能在父子进程中使用的限制。 @@ -561,9 +569,9 @@ int mkfifoat(int fd, const char *path, mode_t mode); FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。 -

+

-## 3. 消息队列 +### 3. 消息队列 相比于 FIFO,消息队列具有以下优点: @@ -571,24 +579,18 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户 - 避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法; - 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。 -## 4. 信号量 +### 4. 信号量 它是一个计数器,用于为多个进程提供对共享数据对象的访问。 -## 5. 共享存储 +### 5. 共享存储 允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。 需要使用信号量用来同步对共享存储的访问。 -多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用使用内存的匿名段。 +多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用内存的匿名段。 -## 6. 套接字 +### 6. 套接字 与其它通信机制不同的是,它可用于不同机器间的进程通信。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" new file mode 100644 index 0000000000..a06caa1ba2 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237 - \351\223\276\346\216\245.md" @@ -0,0 +1,68 @@ +# 计算机操作系统 - 链接 + +* [计算机操作系统 - 链接](#计算机操作系统---链接) + * [编译系统](#编译系统) + * [静态链接](#静态链接) + * [目标文件](#目标文件) + * [动态链接](#动态链接) + + + +## 编译系统 + + +以下是一个 hello.c 程序: + +```c +#include + +int main() +{ + printf("hello, world\n"); + return 0; +} +``` + +在 Unix 系统上,由编译器把源文件转换为目标文件。 + +```bash +gcc -o hello hello.c +``` + +这个过程大致如下: + +

+ +- 预处理阶段:处理以 # 开头的预处理命令; +- 编译阶段:翻译成汇编文件; +- 汇编阶段:将汇编文件翻译成可重定位目标文件; +- 链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。 + +## 静态链接 + +静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。链接器主要完成以下两个任务: + +- 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 +- 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。 + +

+ +## 目标文件 + +- 可执行目标文件:可以直接在内存中执行; +- 可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件; +- 共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接; + +## 动态链接 + +静态库有以下两个问题: + +- 当静态库更新时那么整个程序都要重新进行链接; +- 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。 + +共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。它具有以下特点: + +- 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中; +- 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。 + +

diff --git "a/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" new file mode 100644 index 0000000000..c7dcdb10cb --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\346\223\215\344\275\234\347\263\273\347\273\237.md" @@ -0,0 +1 @@ +[计算机操作系统](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%20-%20%E7%9B%AE%E5%BD%95.md) diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" new file mode 100644 index 0000000000..394438bcda --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \344\274\240\350\276\223\345\261\202.md" @@ -0,0 +1,165 @@ +# 计算机网络 - 传输层 + +* [计算机网络 - 传输层](#计算机网络---传输层) + * [UDP 和 TCP 的特点](#udp-和-tcp-的特点) + * [UDP 首部格式](#udp-首部格式) + * [TCP 首部格式](#tcp-首部格式) + * [TCP 的三次握手](#tcp-的三次握手) + * [TCP 的四次挥手](#tcp-的四次挥手) + * [TCP 可靠传输](#tcp-可靠传输) + * [TCP 滑动窗口](#tcp-滑动窗口) + * [TCP 流量控制](#tcp-流量控制) + * [TCP 拥塞控制](#tcp-拥塞控制) + * [1. 慢开始与拥塞避免](#1-慢开始与拥塞避免) + * [2. 快重传与快恢复](#2-快重传与快恢复) + + + +网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。 + +## UDP 和 TCP 的特点 + +- 用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。 + +- 传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。 + +## UDP 首部格式 + +

+ +首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 + +## TCP 首部格式 + +

+ +- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 + +- **确认号** :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 + +- **数据偏移** :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 + +- **确认 ACK** :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 + +- **同步 SYN** :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 + +- **终止 FIN** :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。 + +- **窗口** :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 + +## TCP 的三次握手 + +

+ +假设 A 为客户端,B 为服务器端。 + +- 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。 + +- A 向 B 发送连接请求报文,SYN=1,ACK=0,选择一个初始的序号 x。 + +- B 收到连接请求报文,如果同意建立连接,则向 A 发送连接确认报文,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。 + +- A 收到 B 的连接确认报文后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。 + +- B 收到 A 的确认后,连接建立。 + +**三次握手的原因** + +第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 + +客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。 + +## TCP 的四次挥手 + +

+ +以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 + +- A 发送连接释放报文,FIN=1。 + +- B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。 + +- 当 B 不再需要连接时,发送连接释放报文,FIN=1。 + +- A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。 + +- B 收到 A 的确认后释放连接。 + +**四次挥手的原因** + +客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 + +**TIME_WAIT** + +客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: + +- 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。 + +- 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。 + +## TCP 可靠传输 + +TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。 + +一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算如下: + +

+其中,0 ≤ a < 1,RTTs 随着 a 的增加更容易受到 RTT 的影响。 + +超时时间 RTO 应该略大于 RTTs,TCP 使用的超时时间计算如下: + +

+其中 RTTd 为偏差的加权平均值。 + +## TCP 滑动窗口 + +窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 + +发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 + +接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 + +

+ +## TCP 流量控制 + +流量控制是为了控制发送方发送速率,保证接收方来得及接收。 + +接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 + +## TCP 拥塞控制 + +如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 + +

+ +TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。 + +发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 + +为了便于讨论,做如下假设: + +- 接收方有足够大的接收缓存,因此不会发生流量控制; +- 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 + +

+ +### 1. 慢开始与拥塞避免 + +发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ... + +注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd \>= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。 + +如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。 + +### 2. 快重传与快恢复 + +在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。 + +在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。 + +在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 + +慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。 + +

diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" similarity index 75% rename from "docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" rename to "notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" index eaebad98e8..5ce041a29a 100644 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \345\272\224\347\224\250\345\261\202.md" @@ -1,35 +1,37 @@ +# 计算机网络 - 应用层 -* [域名系统](#域名系统) -* [文件传送协议](#文件传送协议) -* [动态主机配置协议](#动态主机配置协议) -* [远程登录协议](#远程登录协议) -* [电子邮件协议](#电子邮件协议) - * [1. SMTP](#1-smtp) - * [2. POP3](#2-pop3) - * [3. IMAP](#3-imap) -* [常用端口](#常用端口) -* [Web 页面请求过程](#web-页面请求过程) - * [1. DHCP 配置主机信息](#1-dhcp-配置主机信息) - * [2. ARP 解析 MAC 地址](#2-arp-解析-mac-地址) - * [3. DNS 解析域名](#3-dns-解析域名) - * [4. HTTP 请求页面](#4-http-请求页面) +* [计算机网络 - 应用层](#计算机网络---应用层) + * [域名系统](#域名系统) + * [文件传送协议](#文件传送协议) + * [动态主机配置协议](#动态主机配置协议) + * [远程登录协议](#远程登录协议) + * [电子邮件协议](#电子邮件协议) + * [1. SMTP](#1-smtp) + * [2. POP3](#2-pop3) + * [3. IMAP](#3-imap) + * [常用端口](#常用端口) + * [Web 页面请求过程](#web-页面请求过程) + * [1. DHCP 配置主机信息](#1-dhcp-配置主机信息) + * [2. ARP 解析 MAC 地址](#2-arp-解析-mac-地址) + * [3. DNS 解析域名](#3-dns-解析域名) + * [4. HTTP 请求页面](#4-http-请求页面) -# 域名系统 +## 域名系统 DNS 是一个分布式数据库,提供了主机名和 IP 地址之间相互转换的服务。这里的分布式数据库是指,每个站点只保留它自己的那部分数据。 域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。 -

+

-DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传来保证可靠性。在两种情况下会使用 TCP 进行传输: +DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。在两种情况下会使用 TCP 进行传输: - 如果返回的响应超过的 512 字节(UDP 最大只支持 512 字节的数据)。 - 区域传送(区域传送是主域名服务器向辅助域名服务器传送变化的那部分数据)。 -# 文件传送协议 +## 文件传送协议 FTP 使用 TCP 进行连接,它需要两个连接来传送一个文件: @@ -40,17 +42,17 @@ FTP 使用 TCP 进行连接,它需要两个连接来传送一个文件: - 主动模式:服务器端主动建立数据连接,其中服务器端的端口号为 20,客户端的端口号随机,但是必须大于 1024,因为 0\~1023 是熟知端口号。 -

+

- 被动模式:客户端主动建立数据连接,其中客户端的端口号由客户端自己指定,服务器端的端口号随机。 -

+

主动模式要求客户端开放端口号给服务器端,需要去配置客户端的防火墙。被动模式只需要服务器端开放端口号即可,无需客户端配置防火墙。但是被动模式会导致服务器端的安全性减弱,因为开放了过多的端口号。 -# 动态主机配置协议 +## 动态主机配置协议 -DHCP (Dynamic Host Configuration Protocol) 提供了即插即用的连网方式,用户不再需要去手动配置 IP 地址等信息。 +DHCP (Dynamic Host Configuration Protocol) 提供了即插即用的连网方式,用户不再需要手动配置 IP 地址等信息。 DHCP 配置的内容不仅是 IP 地址,还包括子网掩码、网关 IP 地址。 @@ -61,37 +63,37 @@ DHCP 工作过程如下: 3. 如果客户端选择了某个 DHCP 服务器提供的信息,那么就发送 Request 报文给该 DHCP 服务器。 4. DHCP 服务器发送 Ack 报文,表示客户端此时可以使用提供给它的信息。 -

+

-# 远程登录协议 +## 远程登录协议 TELNET 用于登录到远程主机上,并且远程主机上的输出也会返回。 TELNET 可以适应许多计算机和操作系统的差异,例如不同操作系统系统的换行符定义。 -# 电子邮件协议 +## 电子邮件协议 一个电子邮件系统由三部分组成:用户代理、邮件服务器以及邮件协议。 邮件协议包含发送协议和读取协议,发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 -

+

-## 1. SMTP +### 1. SMTP SMTP 只能发送 ASCII 码,而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP,而是增加邮件主体的结构,定义了非 ASCII 码的编码规则。 -

+

-## 2. POP3 +### 2. POP3 -POP3 的特点是只要用户从服务器上读取了邮件,就把该邮件删除。 +POP3 的特点是只要用户从服务器上读取了邮件,就把该邮件删除。但最新版本的 POP3 可以不删除邮件。 -## 3. IMAP +### 3. IMAP IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删除邮件,那么服务器上的邮件也不会被删除。IMAP 这种做法可以让用户随时随地去访问服务器上的邮件。 -# 常用端口 +## 常用端口 |应用| 应用层协议 | 端口号 | 传输层协议 | 备注 | | :---: | :--: | :--: | :--: | :--: | @@ -105,9 +107,9 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 | 邮件读取协议 | POP3 | 110 | TCP | | | 网际报文存取协议 | IMAP | 143 | TCP | | -# Web 页面请求过程 +## Web 页面请求过程 -## 1. DHCP 配置主机信息 +### 1. DHCP 配置主机信息 - 假设主机最开始没有 IP 地址以及其它信息,那么就需要先使用 DHCP 来获取。 @@ -115,7 +117,7 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - 该报文段则被放入在一个具有广播 IP 目的地址(255.255.255.255) 和源 IP 地址(0.0.0.0)的 IP 数据报中。 -- 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。 +- 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:\FF:\FF:\FF:\FF:FF,将广播到与交换机连接的所有设备。 - 连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段有被放入 IP 数据报中,最后放入 MAC 帧中。 @@ -123,7 +125,7 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - 主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。 -## 2. ARP 解析 MAC 地址 +### 2. ARP 解析 MAC 地址 - 主机通过浏览器生成一个 TCP 套接字,套接字向 HTTP 服务器发送 HTTP 请求。为了生成该套接字,主机需要知道网站的域名对应的 IP 地址。 @@ -135,11 +137,11 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。 -- 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。 +- 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:\FF:\FF:\FF:\FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。 - 网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。 -## 3. DNS 解析域名 +### 3. DNS 解析域名 - 知道了网关路由器的 MAC 地址之后,就可以继续 DNS 的解析过程了。 @@ -151,7 +153,7 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - 找到 DNS 记录之后,发送 DNS 回答报文,将该回答报文放入 UDP 报文段中,然后放入 IP 数据报中,通过路由器反向转发回网关路由器,并经过以太网交换机到达主机。 -## 4. HTTP 请求页面 +### 4. HTTP 请求页面 - 有了 HTTP 服务器的 IP 地址之后,主机就能够生成 TCP 套接字,该套接字将用于向 Web 服务器发送 HTTP GET 报文。 @@ -164,9 +166,3 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。 - 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。 - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" new file mode 100644 index 0000000000..93e271d5fa --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \346\246\202\350\277\260.md" @@ -0,0 +1,134 @@ +# 计算机网络 - 概述 + +* [计算机网络 - 概述](#计算机网络---概述) + * [网络的网络](#网络的网络) + * [ISP](#isp) + * [主机之间的通信方式](#主机之间的通信方式) + * [电路交换与分组交换](#电路交换与分组交换) + * [1. 电路交换](#1-电路交换) + * [2. 分组交换](#2-分组交换) + * [时延](#时延) + * [1. 排队时延](#1-排队时延) + * [2. 处理时延](#2-处理时延) + * [3. 传输时延](#3-传输时延) + * [4. 传播时延](#4-传播时延) + * [计算机网络体系结构](#计算机网络体系结构) + * [1. 五层协议](#1-五层协议) + * [2. OSI](#2-osi) + * [3. TCP/IP](#3-tcpip) + * [4. 数据在各层之间的传递过程](#4-数据在各层之间的传递过程) + + + +## 网络的网络 + +网络把主机连接起来,而互连网(internet)是把多种不同的网络连接起来,因此互连网是网络的网络。而互联网(Internet)是全球范围的互连网。 + +

+ +## ISP + +互联网服务提供商 ISP 可以从互联网管理机构获得许多 IP 地址,同时拥有通信线路以及路由器等联网设备,个人或机构向 ISP 缴纳一定的费用就可以接入互联网。 + +

+ +目前的互联网是一种多层次 ISP 结构,ISP 根据覆盖面积的大小分为第一层 ISP、区域 ISP 和接入 ISP。互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 + +

+ +## 主机之间的通信方式 + +- 客户-服务器(C/S):客户是服务的请求方,服务器是服务的提供方。 + +

+ +- 对等(P2P):不区分客户和服务器。 + +

+ +## 电路交换与分组交换 + +### 1. 电路交换 + +电路交换用于电话通信系统,两个用户要通信之前需要建立一条专用的物理链路,并且在整个通信过程中始终占用该链路。由于通信的过程中不可能一直在使用传输线路,因此电路交换对线路的利用率很低,往往不到 10%。 + +### 2. 分组交换 + +每个分组都有首部和尾部,包含了源地址和目的地址等控制信息,在同一个传输线路上同时传输多个分组互相不会影响,因此在同一条传输线路上允许同时传输多个分组,也就是说分组交换不需要占用传输线路。 + +在一个邮局通信系统中,邮局收到一份邮件之后,先存储下来,然后把相同目的地的邮件一起转发到下一个目的地,这个过程就是存储转发过程,分组交换也使用了存储转发过程。 + +## 时延 + +总时延 = 排队时延 + 处理时延 + 传输时延 + 传播时延 + +

+ +### 1. 排队时延 + +分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量。 + +### 2. 处理时延 + +主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据、进行差错检验或查找适当的路由等。 + +### 3. 传输时延 + +主机或路由器传输数据帧所需要的时间。 + + + +

+ + +其中 l 表示数据帧的长度,v 表示传输速率。 + +### 4. 传播时延 + +电磁波在信道中传播所需要花费的时间,电磁波传播的速度接近光速。 + + + +

+ +其中 l 表示信道长度,v 表示电磁波在信道上的传播速度。 + +## 计算机网络体系结构 + +

+ +### 1. 五层协议 + +- **应用层** :为特定应用程序提供数据传输服务,例如 HTTP、DNS 等协议。数据单位为报文。 + +- **传输层** :为进程提供通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。 + +- **网络层** :为主机提供数据传输服务。而传输层协议是为主机中的进程提供数据传输服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。 + +- **数据链路层** :网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的主机提供数据传输服务。数据链路层把网络层传下来的分组封装成帧。 + +- **物理层** :考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。 + +### 2. OSI + +其中表示层和会话层用途如下: + +- **表示层** :数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。 + +- **会话层** :建立及管理会话。 + +五层协议没有表示层和会话层,而是将这些功能留给应用程序开发者处理。 + +### 3. TCP/IP + +它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。 + +TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 + +

+ +### 4. 数据在各层之间的传递过程 + +在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 + +路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。 diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" new file mode 100644 index 0000000000..2a10290f4b --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\211\251\347\220\206\345\261\202.md" @@ -0,0 +1,22 @@ +# 计算机网络 - 物理层 + +* [计算机网络 - 物理层](#计算机网络---物理层) + * [通信方式](#通信方式) + * [带通调制](#带通调制) + + + +## 通信方式 + +根据信息在传输线上的传送方向,分为以下三种通信方式: + +- 单工通信:单向传输 +- 半双工通信:双向交替传输 +- 全双工通信:双向同时传输 + +## 带通调制 + +模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 + +

+ diff --git "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" similarity index 83% rename from "docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" rename to "notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" index 7655fa1b1e..2b049dbdc6 100644 --- "a/docs/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\233\256\345\275\225.md" @@ -1,10 +1,4 @@ - -* [目录](#目录) -* [参考链接](#参考链接) - - - -# 目录 +# 计算机网络 - [概述](计算机网络%20-%20概述.md) - [物理层](计算机网络%20-%20物理层.md) @@ -13,7 +7,7 @@ - [传输层](计算机网络%20-%20传输层.md) - [应用层](计算机网络%20-%20应用层.md) -# 参考链接 +## 参考链接 - 计算机网络, 谢希仁 - JamesF.Kurose, KeithW.Ross, 库罗斯, 等. 计算机网络: 自顶向下方法 [M]. 机械工业出版社, 2014. @@ -30,9 +24,3 @@ - [Technology-Computer Networking[1]-Computer Networks and the Internet](http://www.linyibin.cn/2017/02/12/technology-ComputerNetworking-Internet/) - [P2P 网络概述.](http://slidesplayer.com/slide/11616167/) - [Circuit Switching (a) Circuit switching. (b) Packet switching.](http://slideplayer.com/slide/5115386/) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" new file mode 100644 index 0000000000..5634d7441b --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \347\275\221\347\273\234\345\261\202.md" @@ -0,0 +1,244 @@ +# 计算机网络 - 网络层 + +* [计算机网络 - 网络层](#计算机网络---网络层) + * [概述](#概述) + * [IP 数据报格式](#ip-数据报格式) + * [IP 地址编址方式](#ip-地址编址方式) + * [1. 分类](#1-分类) + * [2. 子网划分](#2-子网划分) + * [3. 无分类](#3-无分类) + * [地址解析协议 ARP](#地址解析协议-arp) + * [网际控制报文协议 ICMP](#网际控制报文协议-icmp) + * [1. Ping](#1-ping) + * [2. Traceroute](#2-traceroute) + * [虚拟专用网 VPN](#虚拟专用网-vpn) + * [网络地址转换 NAT](#网络地址转换-nat) + * [路由器的结构](#路由器的结构) + * [路由器分组转发流程](#路由器分组转发流程) + * [路由选择协议](#路由选择协议) + * [1. 内部网关协议 RIP](#1-内部网关协议-rip) + * [2. 内部网关协议 OSPF](#2-内部网关协议-ospf) + * [3. 外部网关协议 BGP](#3-外部网关协议-bgp) + + + +## 概述 + +因为网络层是整个互联网的核心,因此应当让网络层尽可能简单。网络层向上只提供简单灵活的、无连接的、尽最大努力交互的数据报服务。 + +使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 + +

+ +与 IP 协议配套使用的还有三个协议: + +- 地址解析协议 ARP(Address Resolution Protocol) +- 网际控制报文协议 ICMP(Internet Control Message Protocol) +- 网际组管理协议 IGMP(Internet Group Management Protocol) + +## IP 数据报格式 + +

+ +- **版本** : 有 4(IPv4)和 6(IPv6)两个值; + +- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为固定部分长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 + +- **区分服务** : 用来获得更好的服务,一般情况下不使用。 + +- **总长度** : 包括首部长度和数据部分长度。 + +- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 + +- **协议** :指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 + +- **首部检验和** :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 + +- **标识** : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 + +- **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 + +

+ +## IP 地址编址方式 + +IP 地址的编址方式经历了三个历史阶段: + +- 分类 +- 子网划分 +- 无分类 + +### 1. 分类 + +由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的。 + +IP 地址 ::= {\< 网络号 \>, \< 主机号 \>} + +

+ +### 2. 子网划分 + +通过在主机号字段中拿一部分作为子网号,把两级 IP 地址划分为三级 IP 地址。 + +IP 地址 ::= {\< 网络号 \>, \< 子网号 \>, \< 主机号 \>} + +要使用子网,必须配置子网掩码。一个 B 类地址的默认子网掩码为 255.255.0.0,如果 B 类地址的子网占两个比特,那么子网掩码为 11111111 11111111 11000000 00000000,也就是 255.255.192.0。 + +注意,外部网络看不到子网的存在。 + +### 3. 无分类 + +无分类编址 CIDR 消除了传统 A 类、B 类和 C 类地址以及划分子网的概念,使用网络前缀和主机号来对 IP 地址进行编码,网络前缀的长度可以根据需要变化。 + +IP 地址 ::= {\< 网络前缀号 \>, \< 主机号 \>} + +CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法,例如 128.14.35.7/20 表示前 20 位为网络前缀。 + +CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 + +一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 + +在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 + +## 地址解析协议 ARP + +网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 + +

+ +ARP 实现由 IP 地址得到 MAC 地址。 + +

+ +每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表。 + +如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。 + +

+ +## 网际控制报文协议 ICMP + +ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。 + +

+ +ICMP 报文分为差错报告报文和询问报文。 + +

+ +### 1. Ping + +Ping 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。 + +Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送 Echo 回答报文。Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。 + +### 2. Traceroute + +Traceroute 是 ICMP 的另一个应用,用来跟踪一个分组从源点到终点的路径。 + +Traceroute 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报,并由目的主机发送终点不可达差错报告报文。 + +- 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,当 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文; +- 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTL 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。 +- 不断执行这样的步骤,直到最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。 +- 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。 + +## 虚拟专用网 VPN + +由于 IP 地址的紧缺,一个机构能申请到的 IP 地址数往往远小于本机构所拥有的主机数。并且一个机构并不需要把所有的主机接入到外部的互联网中,机构内的计算机可以使用仅在本机构有效的 IP 地址(专用地址)。 + +有三个专用地址块: + +- 10.0.0.0 \~ 10.255.255.255 +- 172.16.0.0 \~ 172.31.255.255 +- 192.168.0.0 \~ 192.168.255.255 + +VPN 使用公用的互联网作为本机构各专用网之间的通信载体。专用指机构内的主机只与本机构内的其它主机通信;虚拟指好像是,而实际上并不是,它有经过公用的互联网。 + +下图中,场所 A 和 B 的通信经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 + +

+ +## 网络地址转换 NAT + +专用网内部的主机使用本地 IP 地址又想和互联网上的主机通信时,可以使用 NAT 来将本地 IP 转换为全球 IP。 + +在以前,NAT 将本地 IP 和全球 IP 一一对应,这种方式下拥有 n 个全球 IP 地址的专用网内最多只可以同时有 n 台主机接入互联网。为了更有效地利用全球 IP 地址,现在常用的 NAT 转换表把传输层的端口号也用上了,使得多个专用网内部的主机共用一个全球 IP 地址。使用端口号的 NAT 也叫做网络地址与端口转换 NAPT。 + +

+ +## 路由器的结构 + +路由器从功能上可以划分为:路由选择和分组转发。 + +分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 + +

+ +## 路由器分组转发流程 + +- 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。 +- 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付; +- 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器; +- 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器; +- 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; +- 报告转发分组出错。 + +

+ +## 路由选择协议 + +路由选择协议都是自适应的,能随着网络通信量和拓扑结构的变化而自适应地进行调整。 + +互联网可以划分为许多较小的自治系统 AS,一个 AS 可以使用一种和别的 AS 不同的路由选择协议。 + +可以把路由选择协议划分为两大类: + +- 自治系统内部的路由选择:RIP 和 OSPF +- 自治系统间的路由选择:BGP + +### 1. 内部网关协议 RIP + +RIP 是一种基于距离向量的路由选择协议。距离是指跳数,直接相连的路由器跳数为 1。跳数最多为 15,超过 15 表示不可达。 + +RIP 按固定的时间间隔仅和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。 + +距离向量算法: + +- 对地址为 X 的相邻路由器发来的 RIP 报文,先修改报文中的所有项目,把下一跳字段中的地址改为 X,并把所有的距离字段加 1; +- 对修改后的 RIP 报文中的每一个项目,进行以下步骤: + - 若原来的路由表中没有目的网络 N,则把该项目添加到路由表中; + - 否则:若下一跳路由器地址是 X,则把收到的项目替换原来路由表中的项目;否则:若收到的项目中的距离 d 小于路由表中的距离,则进行更新(例如原始路由表项为 Net2, 5, P,新表项为 Net2, 4, X,则更新);否则什么也不做。 +- 若 3 分钟还没有收到相邻路由器的更新路由表,则把该相邻路由器标为不可达,即把距离置为 16。 + +RIP 协议实现简单,开销小。但是 RIP 能使用的最大距离为 15,限制了网络的规模。并且当网络出现故障时,要经过比较长的时间才能将此消息传送到所有路由器。 + +### 2. 内部网关协议 OSPF + +开放最短路径优先 OSPF,是为了克服 RIP 的缺点而开发出来的。 + +开放表示 OSPF 不受某一家厂商控制,而是公开发表的;最短路径优先表示使用了 Dijkstra 提出的最短路径算法 SPF。 + +OSPF 具有以下特点: + +- 向本自治系统中的所有路由器发送信息,这种方法是洪泛法。 +- 发送的信息就是与相邻路由器的链路状态,链路状态包括与哪些路由器相连以及链路的度量,度量用费用、距离、时延、带宽等来表示。 +- 只有当链路状态发生变化时,路由器才会发送信息。 + +所有路由器都具有全网的拓扑结构图,并且是一致的。相比于 RIP,OSPF 的更新过程收敛的很快。 + +### 3. 外部网关协议 BGP + +BGP(Border Gateway Protocol,边界网关协议) + +AS 之间的路由选择很困难,主要是由于: + +- 互联网规模很大; +- 各个 AS 内部使用不同的路由选择协议,无法准确定义路径的度量; +- AS 之间的路由选择必须考虑有关的策略,比如有些 AS 不愿意让其它 AS 经过。 + +BGP 只能寻找一条比较好的路由,而不是最佳路由。 + +每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 + +

diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" new file mode 100644 index 0000000000..64904ff339 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234 - \351\223\276\350\267\257\345\261\202.md" @@ -0,0 +1,198 @@ +# 计算机网络 - 链路层 + +* [计算机网络 - 链路层](#计算机网络---链路层) + * [基本问题](#基本问题) + * [1. 封装成帧](#1-封装成帧) + * [2. 透明传输](#2-透明传输) + * [3. 差错检测](#3-差错检测) + * [信道分类](#信道分类) + * [1. 广播信道](#1-广播信道) + * [2. 点对点信道](#2-点对点信道) + * [信道复用技术](#信道复用技术) + * [1. 频分复用](#1-频分复用) + * [2. 时分复用](#2-时分复用) + * [3. 统计时分复用](#3-统计时分复用) + * [4. 波分复用](#4-波分复用) + * [5. 码分复用](#5-码分复用) + * [CSMA/CD 协议](#csmacd-协议) + * [PPP 协议](#ppp-协议) + * [MAC 地址](#mac-地址) + * [局域网](#局域网) + * [以太网](#以太网) + * [交换机](#交换机) + * [虚拟局域网](#虚拟局域网) + + + +## 基本问题 + +### 1. 封装成帧 + +将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 + +

+ +### 2. 透明传输 + +透明表示一个实际存在的事物看起来好像不存在一样。 + +帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符。如果数据部分出现转义字符,那么就在转义字符前面再加个转义字符。在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 + +

+ +### 3. 差错检测 + +目前数据链路层广泛使用了循环冗余检验(CRC)来检查比特差错。 + +## 信道分类 + +### 1. 广播信道 + +一对多通信,一个节点发送的数据能够被广播信道上所有的节点接收到。 + +所有的节点都在同一个广播信道上发送数据,因此需要有专门的控制方法进行协调,避免发生冲突(冲突也叫碰撞)。 + +主要有两种控制方法进行协调,一个是使用信道复用技术,一是使用 CSMA/CD 协议。 + +### 2. 点对点信道 + +一对一通信。 + +因为不会发生碰撞,因此也比较简单,使用 PPP 协议进行控制。 + +## 信道复用技术 + +### 1. 频分复用 + +频分复用的所有主机在相同的时间占用不同的频率带宽资源。 + +

+ +### 2. 时分复用 + +时分复用的所有主机在不同的时间占用相同的频率带宽资源。 + +

+ +使用频分复用和时分复用进行通信,在通信的过程中主机会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 + +### 3. 统计时分复用 + +是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 + +

+ +### 4. 波分复用 + +光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 + +### 5. 码分复用 + +为每个用户分配 m bit 的码片,并且所有的码片正交,对于任意两个码片 有 + + + +

+ +为了讨论方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 + +在计算时将 00011011 记作 (-1 -1 -1 +1 +1 -1 +1 +1),可以得到 + + + + + +

+ +

+ +其中 的反码。 + +利用上面的式子我们知道,当接收端使用码片 对接收到的数据进行内积运算时,结果为 0 的是其它用户发送的数据,结果为 1 的是用户发送的比特 1,结果为 -1 的是用户发送的比特 0。 + +码分复用需要发送的数据量为原先的 m 倍。 + +

+ + +## CSMA/CD 协议 + +CSMA/CD 表示载波监听多点接入 / 碰撞检测。 + +- **多点接入** :说明这是总线型网络,许多主机以多点的方式连接到总线上。 +- **载波监听** :每个主机都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。 +- **碰撞检测** :在发送中,如果监听到信道已有其它主机正在发送数据,就表示发生了碰撞。虽然每个主机在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 + +记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 + +当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定。从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 + +

+ +## PPP 协议 + +互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 + +

+ +PPP 的帧格式: + +- F 字段为帧的定界符 +- A 和 C 字段暂时没有意义 +- FCS 字段是使用 CRC 的检验序列 +- 信息部分的长度不超过 1500 + +

+ +## MAC 地址 + +MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标识网络适配器(网卡)。 + +一台主机拥有多少个网络适配器就有多少个 MAC 地址。例如笔记本电脑普遍存在无线网络适配器和有线网络适配器,因此就有两个 MAC 地址。 + +## 局域网 + +局域网是一种典型的广播信道,主要特点是网络为一个单位所拥有,且地理范围和站点数目均有限。 + +主要有以太网、令牌环网、FDDI 和 ATM 等局域网技术,目前以太网占领着有线局域网市场。 + +可以按照网络拓扑结构对局域网进行分类: + +

+ +## 以太网 + +以太网是一种星型拓扑结构局域网。 + +早期使用集线器进行连接,集线器是一种物理层设备, 作用于比特而不是帧,当一个比特到达接口时,集线器重新生成这个比特,并将其能量强度放大,从而扩大网络的传输距离,之后再将这个比特发送到其它所有接口。如果集线器同时收到两个不同接口的帧,那么就发生了碰撞。 + +目前以太网使用交换机替代了集线器,交换机是一种链路层设备,它不会发生碰撞,能根据 MAC 地址进行存储转发。 + +以太网帧格式: + +- **类型** :标记上层使用的协议; +- **数据** :长度在 46-1500 之间,如果太小则需要填充; +- **FCS** :帧检验序列,使用的是 CRC 检验方法; + +

+ +## 交换机 + +交换机具有自学习能力,学习的是交换表的内容,交换表中存储着 MAC 地址到接口的映射。 + +正是由于这种自学习能力,因此交换机是一种即插即用设备,不需要网络管理员手动配置交换表内容。 + +下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 回应该帧向主机 A 发送数据包时,交换机查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 2 的映射。 + +

+ +## 虚拟局域网 + +虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息。 + +例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 + +使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧上加进了 4 字节首部 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。 + +

+ diff --git "a/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" new file mode 100644 index 0000000000..c8befa0489 --- /dev/null +++ "b/notes/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" @@ -0,0 +1 @@ +[计算机网络.md](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%20-%20%E7%9B%AE%E5%BD%95.md) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\215\225\344\276\213.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\215\225\344\276\213.md" new file mode 100644 index 0000000000..3a3e18afc0 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\215\225\344\276\213.md" @@ -0,0 +1,201 @@ +## 单例(Singleton) + +### Intent + +确保一个类只有一个实例,并提供该实例的全局访问点。 + +### Class Diagram + +使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。 + +私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 + +

+ +### Implementation + +#### Ⅰ 懒汉式-线程不安全 + +以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 + +这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 `if (uniqueInstance == null)` ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 `uniqueInstance = new Singleton();` 语句,这将导致实例化多次 uniqueInstance。 + +```java +public class Singleton { + + private static Singleton uniqueInstance; + + private Singleton() { + } + + public static Singleton getUniqueInstance() { + if (uniqueInstance == null) { + uniqueInstance = new Singleton(); + } + return uniqueInstance; + } +} +``` + +#### Ⅱ 饿汉式-线程安全 + +线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。 + +但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。 + +```java +private static Singleton uniqueInstance = new Singleton(); +``` + +#### Ⅲ 懒汉式-线程安全 + +只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。 + +但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。 + +```java +public static synchronized Singleton getUniqueInstance() { + if (uniqueInstance == null) { + uniqueInstance = new Singleton(); + } + return uniqueInstance; +} +``` + +#### Ⅳ 双重校验锁-线程安全 + +uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。 + +双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。 + +```java +public class Singleton { + + private volatile static Singleton uniqueInstance; + + private Singleton() { + } + + public static Singleton getUniqueInstance() { + if (uniqueInstance == null) { + synchronized (Singleton.class) { + if (uniqueInstance == null) { + uniqueInstance = new Singleton(); + } + } + } + return uniqueInstance; + } +} +``` + +考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。 + +```java +if (uniqueInstance == null) { + synchronized (Singleton.class) { + uniqueInstance = new Singleton(); + } +} +``` + +uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行: + +1. 为 uniqueInstance 分配内存空间 +2. 初始化 uniqueInstance +3. 将 uniqueInstance 指向分配的内存地址 + +但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。 + +使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。 + +#### Ⅴ 静态内部类实现 + +当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。 + +这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。 + +```java +public class Singleton { + + private Singleton() { + } + + private static class SingletonHolder { + private static final Singleton INSTANCE = new Singleton(); + } + + public static Singleton getUniqueInstance() { + return SingletonHolder.INSTANCE; + } +} +``` + +#### Ⅵ 枚举实现 + +```java +public enum Singleton { + + INSTANCE; + + private String objName; + + + public String getObjName() { + return objName; + } + + + public void setObjName(String objName) { + this.objName = objName; + } + + + public static void main(String[] args) { + + // 单例测试 + Singleton firstSingleton = Singleton.INSTANCE; + firstSingleton.setObjName("firstName"); + System.out.println(firstSingleton.getObjName()); + Singleton secondSingleton = Singleton.INSTANCE; + secondSingleton.setObjName("secondName"); + System.out.println(firstSingleton.getObjName()); + System.out.println(secondSingleton.getObjName()); + + // 反射获取实例测试 + try { + Singleton[] enumConstants = Singleton.class.getEnumConstants(); + for (Singleton enumConstant : enumConstants) { + System.out.println(enumConstant.getObjName()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +```html +firstName +secondName +secondName +secondName +``` + +该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。 + +该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。 + +### Examples + +- Logger Classes +- Configuration Classes +- Accesing resources in shared mode +- Factories implemented as Singletons + +### JDK + +- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +- [java.lang.System#getSecurityManager()](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\270\255\344\273\213\350\200\205.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\270\255\344\273\213\350\200\205.md" new file mode 100644 index 0000000000..4cbd06caee --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\270\255\344\273\213\350\200\205.md" @@ -0,0 +1,170 @@ +## 5. 中介者(Mediator) + +### Intent + +集中相关对象之间复杂的沟通和控制方式。 + +### Class Diagram + + + +- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。 +- Colleague:同事,相关对象 + +

+ +### Implementation + +Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构: + +

+ +使用中介者模式可以将复杂的依赖结构变成星形结构: + +

+ +```java +public abstract class Colleague { + public abstract void onEvent(Mediator mediator); +} +``` + +```java +public class Alarm extends Colleague { + + @Override + public void onEvent(Mediator mediator) { + mediator.doEvent("alarm"); + } + + public void doAlarm() { + System.out.println("doAlarm()"); + } +} +``` + +```java +public class CoffeePot extends Colleague { + @Override + public void onEvent(Mediator mediator) { + mediator.doEvent("coffeePot"); + } + + public void doCoffeePot() { + System.out.println("doCoffeePot()"); + } +} +``` + +```java +public class Calender extends Colleague { + @Override + public void onEvent(Mediator mediator) { + mediator.doEvent("calender"); + } + + public void doCalender() { + System.out.println("doCalender()"); + } +} +``` + +```java +public class Sprinkler extends Colleague { + @Override + public void onEvent(Mediator mediator) { + mediator.doEvent("sprinkler"); + } + + public void doSprinkler() { + System.out.println("doSprinkler()"); + } +} +``` + +```java +public abstract class Mediator { + public abstract void doEvent(String eventType); +} +``` + +```java +public class ConcreteMediator extends Mediator { + private Alarm alarm; + private CoffeePot coffeePot; + private Calender calender; + private Sprinkler sprinkler; + + public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) { + this.alarm = alarm; + this.coffeePot = coffeePot; + this.calender = calender; + this.sprinkler = sprinkler; + } + + @Override + public void doEvent(String eventType) { + switch (eventType) { + case "alarm": + doAlarmEvent(); + break; + case "coffeePot": + doCoffeePotEvent(); + break; + case "calender": + doCalenderEvent(); + break; + default: + doSprinklerEvent(); + } + } + + public void doAlarmEvent() { + alarm.doAlarm(); + coffeePot.doCoffeePot(); + calender.doCalender(); + sprinkler.doSprinkler(); + } + + public void doCoffeePotEvent() { + // ... + } + + public void doCalenderEvent() { + // ... + } + + public void doSprinklerEvent() { + // ... + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Alarm alarm = new Alarm(); + CoffeePot coffeePot = new CoffeePot(); + Calender calender = new Calender(); + Sprinkler sprinkler = new Sprinkler(); + Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler); + // 闹钟事件到达,调用中介者就可以操作相关对象 + alarm.onEvent(mediator); + } +} +``` + +```java +doAlarm() +doCoffeePot() +doCalender() +doSprinkler() +``` + +### JDK + +- All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) +- [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) +- submit() and invokeXXX() methods of [java.util.concurrent.ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) +- scheduleXXX() methods of [java.util.concurrent.ScheduledExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) +- [java.lang.reflect.Method#invoke()](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\272\253\345\205\203.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\272\253\345\205\203.md" new file mode 100644 index 0000000000..be8cc52b6e --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\272\253\345\205\203.md" @@ -0,0 +1,87 @@ +## 享元(Flyweight) + +### Intent + + + +利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。 + +### Class Diagram + +- Flyweight:享元对象 +- IntrinsicState:内部状态,享元对象共享内部状态 +- ExtrinsicState:外部状态,每个享元对象的外部状态不同 + +

+ +### Implementation + +```java +public interface Flyweight { + void doOperation(String extrinsicState); +} +``` + +```java +public class ConcreteFlyweight implements Flyweight { + + private String intrinsicState; + + public ConcreteFlyweight(String intrinsicState) { + this.intrinsicState = intrinsicState; + } + + @Override + public void doOperation(String extrinsicState) { + System.out.println("Object address: " + System.identityHashCode(this)); + System.out.println("IntrinsicState: " + intrinsicState); + System.out.println("ExtrinsicState: " + extrinsicState); + } +} +``` + +```java +public class FlyweightFactory { + + private HashMap flyweights = new HashMap<>(); + + Flyweight getFlyweight(String intrinsicState) { + if (!flyweights.containsKey(intrinsicState)) { + Flyweight flyweight = new ConcreteFlyweight(intrinsicState); + flyweights.put(intrinsicState, flyweight); + } + return flyweights.get(intrinsicState); + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + FlyweightFactory factory = new FlyweightFactory(); + Flyweight flyweight1 = factory.getFlyweight("aa"); + Flyweight flyweight2 = factory.getFlyweight("aa"); + flyweight1.doOperation("x"); + flyweight2.doOperation("y"); + } +} +``` + +```html +Object address: 1163157884 +IntrinsicState: aa +ExtrinsicState: x +Object address: 1163157884 +IntrinsicState: aa +ExtrinsicState: y +``` + +### JDK + +Java 利用缓存来加速大量小对象的访问时间。 + +- java.lang.Integer#valueOf(int) +- java.lang.Boolean#valueOf(boolean) +- java.lang.Byte#valueOf(byte) +- java.lang.Character#valueOf(char) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\273\243\347\220\206.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\273\243\347\220\206.md" new file mode 100644 index 0000000000..8e1b123daf --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \344\273\243\347\220\206.md" @@ -0,0 +1,104 @@ +## 代理(Proxy) + +### Intent + +控制对其它对象的访问。 + +### Class Diagram + +代理有以下四类: + +- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。 +- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。 +- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。 +- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。 + +

+ +### Implementation + +以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。 + +```java +public interface Image { + void showImage(); +} +``` + +```java +public class HighResolutionImage implements Image { + + private URL imageURL; + private long startTime; + private int height; + private int width; + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public HighResolutionImage(URL imageURL) { + this.imageURL = imageURL; + this.startTime = System.currentTimeMillis(); + this.width = 600; + this.height = 600; + } + + public boolean isLoad() { + // 模拟图片加载,延迟 3s 加载完成 + long endTime = System.currentTimeMillis(); + return endTime - startTime > 3000; + } + + @Override + public void showImage() { + System.out.println("Real Image: " + imageURL); + } +} +``` + +```java +public class ImageProxy implements Image { + + private HighResolutionImage highResolutionImage; + + public ImageProxy(HighResolutionImage highResolutionImage) { + this.highResolutionImage = highResolutionImage; + } + + @Override + public void showImage() { + while (!highResolutionImage.isLoad()) { + try { + System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight()); + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + highResolutionImage.showImage(); + } +} +``` + +```java +public class ImageViewer { + + public static void main(String[] args) throws Exception { + String image = "http://image.jpg"; + URL url = new URL(image); + HighResolutionImage highResolutionImage = new HighResolutionImage(url); + ImageProxy imageProxy = new ImageProxy(highResolutionImage); + imageProxy.showImage(); + } +} +``` + +### JDK + +- java.lang.reflect.Proxy +- RMI diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\216\237\345\236\213\346\250\241\345\274\217.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\216\237\345\236\213\346\250\241\345\274\217.md" new file mode 100644 index 0000000000..50dc005a95 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\216\237\345\236\213\346\250\241\345\274\217.md" @@ -0,0 +1,56 @@ +## 6. 原型模式(Prototype) + +### Intent + +使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。 + +### Class Diagram + +

+ +### Implementation + +```java +public abstract class Prototype { + abstract Prototype myClone(); +} +``` + +```java +public class ConcretePrototype extends Prototype { + + private String filed; + + public ConcretePrototype(String filed) { + this.filed = filed; + } + + @Override + Prototype myClone() { + return new ConcretePrototype(filed); + } + + @Override + public String toString() { + return filed; + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Prototype prototype = new ConcretePrototype("abc"); + Prototype clone = prototype.myClone(); + System.out.println(clone.toString()); + } +} +``` + +```html +abc +``` + +### JDK + +- [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\221\275\344\273\244.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\221\275\344\273\244.md" new file mode 100644 index 0000000000..2846cdbd96 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\221\275\344\273\244.md" @@ -0,0 +1,127 @@ +## 2. 命令(Command) + +### Intent + +将命令封装成对象中,具有以下作用: + +- 使用命令来参数化其它对象 +- 将命令放入队列中进行排队 +- 将命令的操作记录到日志中 +- 支持可撤销的操作 + +### Class Diagram + +- Command:命令 +- Receiver:命令接收者,也就是命令真正的执行者 +- Invoker:通过它来调用命令 +- Client:可以设置命令与命令的接收者 + +

+ +### Implementation + +设计一个遥控器,可以控制电灯开关。 + +

+ +```java +public interface Command { + void execute(); +} +``` + +```java +public class LightOnCommand implements Command { + Light light; + + public LightOnCommand(Light light) { + this.light = light; + } + + @Override + public void execute() { + light.on(); + } +} +``` + +```java +public class LightOffCommand implements Command { + Light light; + + public LightOffCommand(Light light) { + this.light = light; + } + + @Override + public void execute() { + light.off(); + } +} +``` + +```java +public class Light { + + public void on() { + System.out.println("Light is on!"); + } + + public void off() { + System.out.println("Light is off!"); + } +} +``` + +```java +/** + * 遥控器 + */ +public class Invoker { + private Command[] onCommands; + private Command[] offCommands; + private final int slotNum = 7; + + public Invoker() { + this.onCommands = new Command[slotNum]; + this.offCommands = new Command[slotNum]; + } + + public void setOnCommand(Command command, int slot) { + onCommands[slot] = command; + } + + public void setOffCommand(Command command, int slot) { + offCommands[slot] = command; + } + + public void onButtonWasPushed(int slot) { + onCommands[slot].execute(); + } + + public void offButtonWasPushed(int slot) { + offCommands[slot].execute(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Invoker invoker = new Invoker(); + Light light = new Light(); + Command lightOnCommand = new LightOnCommand(light); + Command lightOffCommand = new LightOffCommand(light); + invoker.setOnCommand(lightOnCommand, 0); + invoker.setOffCommand(lightOffCommand, 0); + invoker.onButtonWasPushed(0); + invoker.offButtonWasPushed(0); + } +} +``` + +### JDK + +- [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) +- [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) +- [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\207\345\277\230\345\275\225.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\207\345\277\230\345\275\225.md" new file mode 100644 index 0000000000..c0f25dc7f0 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\207\345\277\230\345\275\225.md" @@ -0,0 +1,176 @@ +## 备忘录(Memento) + +### Intent + +在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。 + +### Class Diagram + +- Originator:原始对象 +- Caretaker:负责保存好备忘录 +- Memento:备忘录,存储原始对象的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。 + +

+ +### Implementation + +以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。 + +实现参考:[Memento Pattern - Calculator Example - Java Sourcecode](https://www.oodesign.com/memento-pattern-calculator-example-java-sourcecode.html) + +```java +/** + * Originator Interface + */ +public interface Calculator { + + // Create Memento + PreviousCalculationToCareTaker backupLastCalculation(); + + // setMemento + void restorePreviousCalculation(PreviousCalculationToCareTaker memento); + + int getCalculationResult(); + + void setFirstNumber(int firstNumber); + + void setSecondNumber(int secondNumber); +} +``` + +```java +/** + * Originator Implementation + */ +public class CalculatorImp implements Calculator { + + private int firstNumber; + private int secondNumber; + + @Override + public PreviousCalculationToCareTaker backupLastCalculation() { + // create a memento object used for restoring two numbers + return new PreviousCalculationImp(firstNumber, secondNumber); + } + + @Override + public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) { + this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber(); + this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber(); + } + + @Override + public int getCalculationResult() { + // result is adding two numbers + return firstNumber + secondNumber; + } + + @Override + public void setFirstNumber(int firstNumber) { + this.firstNumber = firstNumber; + } + + @Override + public void setSecondNumber(int secondNumber) { + this.secondNumber = secondNumber; + } +} +``` + +```java +/** + * Memento Interface to Originator + * + * This interface allows the originator to restore its state + */ +public interface PreviousCalculationToOriginator { + int getFirstNumber(); + int getSecondNumber(); +} +``` + +```java +/** + * Memento interface to CalculatorOperator (Caretaker) + */ +public interface PreviousCalculationToCareTaker { + // no operations permitted for the caretaker +} +``` + +```java +/** + * Memento Object Implementation + *

+ * Note that this object implements both interfaces to Originator and CareTaker + */ +public class PreviousCalculationImp implements PreviousCalculationToCareTaker, + PreviousCalculationToOriginator { + + private int firstNumber; + private int secondNumber; + + public PreviousCalculationImp(int firstNumber, int secondNumber) { + this.firstNumber = firstNumber; + this.secondNumber = secondNumber; + } + + @Override + public int getFirstNumber() { + return firstNumber; + } + + @Override + public int getSecondNumber() { + return secondNumber; + } +} +``` + +```java +/** + * CareTaker object + */ +public class Client { + + public static void main(String[] args) { + // program starts + Calculator calculator = new CalculatorImp(); + + // assume user enters two numbers + calculator.setFirstNumber(10); + calculator.setSecondNumber(100); + + // find result + System.out.println(calculator.getCalculationResult()); + + // Store result of this calculation in case of error + PreviousCalculationToCareTaker memento = calculator.backupLastCalculation(); + + // user enters a number + calculator.setFirstNumber(17); + + // user enters a wrong second number and calculates result + calculator.setSecondNumber(-290); + + // calculate result + System.out.println(calculator.getCalculationResult()); + + // user hits CTRL + Z to undo last operation and see last result + calculator.restorePreviousCalculation(memento); + + // result restored + System.out.println(calculator.getCalculationResult()); + } +} +``` + +```html +110 +-273 +110 +``` + +### JDK + +- java.io.Serializable diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\226\350\247\202.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\226\350\247\202.md" new file mode 100644 index 0000000000..a503dcdf71 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\244\226\350\247\202.md" @@ -0,0 +1,54 @@ +## 外观(Facade) + +### Intent + +提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。 + +### Class Diagram + +


+ +### Implementation + +观看电影需要操作很多电器,使用外观模式实现一键看电影功能。 + +```java +public class SubSystem { + public void turnOnTV() { + System.out.println("turnOnTV()"); + } + + public void setCD(String cd) { + System.out.println("setCD( " + cd + " )"); + } + + public void startWatching(){ + System.out.println("startWatching()"); + } +} +``` + +```java +public class Facade { + private SubSystem subSystem = new SubSystem(); + + public void watchMovie() { + subSystem.turnOnTV(); + subSystem.setCD("a movie"); + subSystem.startWatching(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Facade facade = new Facade(); + facade.watchMovie(); + } +} +``` + +### 设计原则 + +最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。 diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\267\245\345\216\202\346\226\271\346\263\225.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\267\245\345\216\202\346\226\271\346\263\225.md" new file mode 100644 index 0000000000..d5c57014ff --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \345\267\245\345\216\202\346\226\271\346\263\225.md" @@ -0,0 +1,59 @@ +## 工厂方法(Factory Method) + +### Intent + +定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。 + +### Class Diagram + +在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。 + +下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 + +

+ +### Implementation + +```java +public abstract class Factory { + abstract public Product factoryMethod(); + public void doSomething() { + Product product = factoryMethod(); + // do something with the product + } +} +``` + +```java +public class ConcreteFactory extends Factory { + public Product factoryMethod() { + return new ConcreteProduct(); + } +} +``` + +```java +public class ConcreteFactory1 extends Factory { + public Product factoryMethod() { + return new ConcreteProduct1(); + } +} +``` + +```java +public class ConcreteFactory2 extends Factory { + public Product factoryMethod() { + return new ConcreteProduct2(); + } +} +``` + +### JDK + +- [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +- [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +- [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +- [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +- [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-) +- [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-) +- [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\212\275\350\261\241\345\267\245\345\216\202.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\212\275\350\261\241\345\267\245\345\216\202.md" new file mode 100644 index 0000000000..fe0148eb4c --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\212\275\350\261\241\345\267\245\345\216\202.md" @@ -0,0 +1,97 @@ +## 4. 抽象工厂(Abstract Factory) + +### Intent + +提供一个接口,用于创建 **相关的对象家族** 。 + +### Class Diagram + +抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。 + +抽象工厂模式用到了工厂方法模式来创建单一对象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。 + +至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。 + +从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 + +

+ +### Implementation + +```java +public class AbstractProductA { +} +``` + +```java +public class AbstractProductB { +} +``` + +```java +public class ProductA1 extends AbstractProductA { +} +``` + +```java +public class ProductA2 extends AbstractProductA { +} +``` + +```java +public class ProductB1 extends AbstractProductB { +} +``` + +```java +public class ProductB2 extends AbstractProductB { +} +``` + +```java +public abstract class AbstractFactory { + abstract AbstractProductA createProductA(); + abstract AbstractProductB createProductB(); +} +``` + +```java +public class ConcreteFactory1 extends AbstractFactory { + AbstractProductA createProductA() { + return new ProductA1(); + } + + AbstractProductB createProductB() { + return new ProductB1(); + } +} +``` + +```java +public class ConcreteFactory2 extends AbstractFactory { + AbstractProductA createProductA() { + return new ProductA2(); + } + + AbstractProductB createProductB() { + return new ProductB2(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + AbstractFactory abstractFactory = new ConcreteFactory1(); + AbstractProductA productA = abstractFactory.createProductA(); + AbstractProductB productB = abstractFactory.createProductB(); + // do something with productA and productB + } +} +``` + +### JDK + +- [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) +- [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--) +- [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\241\245\346\216\245.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\241\245\346\216\245.md" new file mode 100644 index 0000000000..28965424c0 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\241\245\346\216\245.md" @@ -0,0 +1,158 @@ + + +## 桥接(Bridge) + +### Intent + +将抽象与实现分离开来,使它们可以独立变化。 + +### Class Diagram + +- Abstraction:定义抽象类的接口 +- Implementor:定义实现类接口 + +

+ +### Implementation + +RemoteControl 表示遥控器,指代 Abstraction。 + +TV 表示电视,指代 Implementor。 + +桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。 + +```java +public abstract class TV { + public abstract void on(); + + public abstract void off(); + + public abstract void tuneChannel(); +} +``` + +```java +public class Sony extends TV { + @Override + public void on() { + System.out.println("Sony.on()"); + } + + @Override + public void off() { + System.out.println("Sony.off()"); + } + + @Override + public void tuneChannel() { + System.out.println("Sony.tuneChannel()"); + } +} +``` + +```java +public class RCA extends TV { + @Override + public void on() { + System.out.println("RCA.on()"); + } + + @Override + public void off() { + System.out.println("RCA.off()"); + } + + @Override + public void tuneChannel() { + System.out.println("RCA.tuneChannel()"); + } +} +``` + +```java +public abstract class RemoteControl { + protected TV tv; + + public RemoteControl(TV tv) { + this.tv = tv; + } + + public abstract void on(); + + public abstract void off(); + + public abstract void tuneChannel(); +} +``` + +```java +public class ConcreteRemoteControl1 extends RemoteControl { + public ConcreteRemoteControl1(TV tv) { + super(tv); + } + + @Override + public void on() { + System.out.println("ConcreteRemoteControl1.on()"); + tv.on(); + } + + @Override + public void off() { + System.out.println("ConcreteRemoteControl1.off()"); + tv.off(); + } + + @Override + public void tuneChannel() { + System.out.println("ConcreteRemoteControl1.tuneChannel()"); + tv.tuneChannel(); + } +} +``` + +```java +public class ConcreteRemoteControl2 extends RemoteControl { + public ConcreteRemoteControl2(TV tv) { + super(tv); + } + + @Override + public void on() { + System.out.println("ConcreteRemoteControl2.on()"); + tv.on(); + } + + @Override + public void off() { + System.out.println("ConcreteRemoteControl2.off()"); + tv.off(); + } + + @Override + public void tuneChannel() { + System.out.println("ConcreteRemoteControl2.tuneChannel()"); + tv.tuneChannel(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA()); + remoteControl1.on(); + remoteControl1.off(); + remoteControl1.tuneChannel(); + RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony()); + remoteControl2.on(); + remoteControl2.off(); + remoteControl2.tuneChannel(); + } +} +``` + +### JDK + +- AWT (It provides an abstraction layer which maps onto the native OS the windowing support.) +- JDBC diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\250\241\346\235\277\346\226\271\346\263\225.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\250\241\346\235\277\346\226\271\346\263\225.md" new file mode 100644 index 0000000000..a6eddea46f --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \346\250\241\346\235\277\346\226\271\346\263\225.md" @@ -0,0 +1,100 @@ +## 模板方法(Template Method) + +### Intent + +定义算法框架,并将一些步骤的实现延迟到子类。 + +通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。 + +### Class Diagram + +

+ +### Implementation + +冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 + +

+ +```java +public abstract class CaffeineBeverage { + + final void prepareRecipe() { + boilWater(); + brew(); + pourInCup(); + addCondiments(); + } + + abstract void brew(); + + abstract void addCondiments(); + + void boilWater() { + System.out.println("boilWater"); + } + + void pourInCup() { + System.out.println("pourInCup"); + } +} +``` + +```java +public class Coffee extends CaffeineBeverage { + @Override + void brew() { + System.out.println("Coffee.brew"); + } + + @Override + void addCondiments() { + System.out.println("Coffee.addCondiments"); + } +} +``` + +```java +public class Tea extends CaffeineBeverage { + @Override + void brew() { + System.out.println("Tea.brew"); + } + + @Override + void addCondiments() { + System.out.println("Tea.addCondiments"); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + CaffeineBeverage caffeineBeverage = new Coffee(); + caffeineBeverage.prepareRecipe(); + System.out.println("-----------"); + caffeineBeverage = new Tea(); + caffeineBeverage.prepareRecipe(); + } +} +``` + +```html +boilWater +Coffee.brew +pourInCup +Coffee.addCondiments +----------- +boilWater +Tea.brew +pourInCup +Tea.addCondiments +``` + +### JDK + +- java.util.Collections#sort() +- java.io.InputStream#skip() +- java.io.InputStream#read() +- java.util.AbstractList#indexOf() diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\212\266\346\200\201.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\212\266\346\200\201.md" new file mode 100644 index 0000000000..42a30bd836 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\212\266\346\200\201.md" @@ -0,0 +1,301 @@ +## 8. 状态(State) + +### Intent + +允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。 + +### Class Diagram + +

+ +### Implementation + +糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 + +

+ +```java +public interface State { + /** + * 投入 25 分钱 + */ + void insertQuarter(); + + /** + * 退回 25 分钱 + */ + void ejectQuarter(); + + /** + * 转动曲柄 + */ + void turnCrank(); + + /** + * 发放糖果 + */ + void dispense(); +} +``` + +```java +public class HasQuarterState implements State { + + private GumballMachine gumballMachine; + + public HasQuarterState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + @Override + public void insertQuarter() { + System.out.println("You can't insert another quarter"); + } + + @Override + public void ejectQuarter() { + System.out.println("Quarter returned"); + gumballMachine.setState(gumballMachine.getNoQuarterState()); + } + + @Override + public void turnCrank() { + System.out.println("You turned..."); + gumballMachine.setState(gumballMachine.getSoldState()); + } + + @Override + public void dispense() { + System.out.println("No gumball dispensed"); + } +} +``` + +```java +public class NoQuarterState implements State { + + GumballMachine gumballMachine; + + public NoQuarterState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + @Override + public void insertQuarter() { + System.out.println("You insert a quarter"); + gumballMachine.setState(gumballMachine.getHasQuarterState()); + } + + @Override + public void ejectQuarter() { + System.out.println("You haven't insert a quarter"); + } + + @Override + public void turnCrank() { + System.out.println("You turned, but there's no quarter"); + } + + @Override + public void dispense() { + System.out.println("You need to pay first"); + } +} +``` + +```java +public class SoldOutState implements State { + + GumballMachine gumballMachine; + + public SoldOutState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + @Override + public void insertQuarter() { + System.out.println("You can't insert a quarter, the machine is sold out"); + } + + @Override + public void ejectQuarter() { + System.out.println("You can't eject, you haven't inserted a quarter yet"); + } + + @Override + public void turnCrank() { + System.out.println("You turned, but there are no gumballs"); + } + + @Override + public void dispense() { + System.out.println("No gumball dispensed"); + } +} +``` + +```java +public class SoldState implements State { + + GumballMachine gumballMachine; + + public SoldState(GumballMachine gumballMachine) { + this.gumballMachine = gumballMachine; + } + + @Override + public void insertQuarter() { + System.out.println("Please wait, we're already giving you a gumball"); + } + + @Override + public void ejectQuarter() { + System.out.println("Sorry, you already turned the crank"); + } + + @Override + public void turnCrank() { + System.out.println("Turning twice doesn't get you another gumball!"); + } + + @Override + public void dispense() { + gumballMachine.releaseBall(); + if (gumballMachine.getCount() > 0) { + gumballMachine.setState(gumballMachine.getNoQuarterState()); + } else { + System.out.println("Oops, out of gumballs"); + gumballMachine.setState(gumballMachine.getSoldOutState()); + } + } +} +``` + +```java +public class GumballMachine { + + private State soldOutState; + private State noQuarterState; + private State hasQuarterState; + private State soldState; + + private State state; + private int count = 0; + + public GumballMachine(int numberGumballs) { + count = numberGumballs; + soldOutState = new SoldOutState(this); + noQuarterState = new NoQuarterState(this); + hasQuarterState = new HasQuarterState(this); + soldState = new SoldState(this); + + if (numberGumballs > 0) { + state = noQuarterState; + } else { + state = soldOutState; + } + } + + public void insertQuarter() { + state.insertQuarter(); + } + + public void ejectQuarter() { + state.ejectQuarter(); + } + + public void turnCrank() { + state.turnCrank(); + state.dispense(); + } + + public void setState(State state) { + this.state = state; + } + + public void releaseBall() { + System.out.println("A gumball comes rolling out the slot..."); + if (count != 0) { + count -= 1; + } + } + + public State getSoldOutState() { + return soldOutState; + } + + public State getNoQuarterState() { + return noQuarterState; + } + + public State getHasQuarterState() { + return hasQuarterState; + } + + public State getSoldState() { + return soldState; + } + + public int getCount() { + return count; + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + GumballMachine gumballMachine = new GumballMachine(5); + + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + + gumballMachine.insertQuarter(); + gumballMachine.ejectQuarter(); + gumballMachine.turnCrank(); + + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + gumballMachine.ejectQuarter(); + + gumballMachine.insertQuarter(); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + gumballMachine.insertQuarter(); + gumballMachine.turnCrank(); + } +} +``` + +```html +You insert a quarter +You turned... +A gumball comes rolling out the slot... +You insert a quarter +Quarter returned +You turned, but there's no quarter +You need to pay first +You insert a quarter +You turned... +A gumball comes rolling out the slot... +You insert a quarter +You turned... +A gumball comes rolling out the slot... +You haven't insert a quarter +You insert a quarter +You can't insert another quarter +You turned... +A gumball comes rolling out the slot... +You insert a quarter +You turned... +A gumball comes rolling out the slot... +Oops, out of gumballs +You can't insert a quarter, the machine is sold out +You turned, but there are no gumballs +No gumball dispensed +``` diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\224\237\346\210\220\345\231\250.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\224\237\346\210\220\345\231\250.md" new file mode 100644 index 0000000000..50a3506240 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\224\237\346\210\220\345\231\250.md" @@ -0,0 +1,89 @@ +## 5. 生成器(Builder) + +### Intent + +封装一个对象的构造过程,并允许按步骤构造。 + +### Class Diagram + +

+ +### Implementation + +以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。 + +```java +public class AbstractStringBuilder { + protected char[] value; + + protected int count; + + public AbstractStringBuilder(int capacity) { + count = 0; + value = new char[capacity]; + } + + public AbstractStringBuilder append(char c) { + ensureCapacityInternal(count + 1); + value[count++] = c; + return this; + } + + private void ensureCapacityInternal(int minimumCapacity) { + // overflow-conscious code + if (minimumCapacity - value.length > 0) + expandCapacity(minimumCapacity); + } + + void expandCapacity(int minimumCapacity) { + int newCapacity = value.length * 2 + 2; + if (newCapacity - minimumCapacity < 0) + newCapacity = minimumCapacity; + if (newCapacity < 0) { + if (minimumCapacity < 0) // overflow + throw new OutOfMemoryError(); + newCapacity = Integer.MAX_VALUE; + } + value = Arrays.copyOf(value, newCapacity); + } +} +``` + +```java +public class StringBuilder extends AbstractStringBuilder { + public StringBuilder() { + super(16); + } + + @Override + public String toString() { + // Create a copy, don't share the array + return new String(value, 0, count); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + StringBuilder sb = new StringBuilder(); + final int count = 26; + for (int i = 0; i < count; i++) { + sb.append((char) ('a' + i)); + } + System.out.println(sb.toString()); + } +} +``` + +```html +abcdefghijklmnopqrstuvwxyz +``` + +### JDK + +- [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) +- [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) +- [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-) +- [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) +- [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\225.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\225.md" new file mode 100644 index 0000000000..c19d6044c6 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\225.md" @@ -0,0 +1,48 @@ +# 设计模式目录 + +## 一、前言 + +设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 + +## 二、创建型 + +- [单例.md](设计模式%20%20-%20单例.md) +- [简单工厂.md](设计模式%20-%20简单工厂.md) +- [工厂方法.md](设计模式%20-%20工厂方法.md) +- [抽象工厂.md](设计模式%20-%20抽象工厂.md) +- [生成器.md](设计模式%20-%20生成器.md) +- [原型模式.md](设计模式%20-%20原型模式.md) + +## 三、行为型 + +- [责任链.md](设计模式%20-%20责任链.md) +- [命令.md](设计模式%20-%20命令.md) +- [解释器.md](设计模式%20-%20解释器.md) +- [迭代器.md](设计模式%20-%20迭代器.md) +- [中介者.md](设计模式%20-%20中介者.md) +- [备忘录.md](设计模式%20-%20备忘录.md) +- [观察者.md](设计模式%20-%20观察者.md) +- [状态.md](设计模式%20-%20状态.md) +- [策略.md](设计模式%20-%20策略.md) +- [模板方法.md](设计模式%20-%20模板方法.md) +- [访问者.md](设计模式%20-%20访问者.md) +- [空对象.md](设计模式%20-%20空对象.md) + +## 四、结构型 + +- [适配器.md](设计模式%20-%20适配器.md) +- [桥接.md](设计模式%20-%20桥接.md) +- [组合.md](设计模式%20-%20组合.md) +- [装饰.md](设计模式%20-%20装饰.md) +- [外观.md](设计模式%20-%20外观.md) +- [享元.md](设计模式%20-%20享元.md) +- [代理.md](设计模式%20-%20代理.md) + +## 参考资料 + +- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. +- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007. +- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. +- [Design Patterns](http://www.oodesign.com/) +- [Design patterns implemented in Java](http://java-design-patterns.com/) +- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\2251.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\2251.md" new file mode 100644 index 0000000000..d61a1d986e --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\233\256\345\275\2251.md" @@ -0,0 +1,46 @@ +# 一、前言 + +设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 + +# 二、创建型 + +- [单例.md](notes/设计模式%20%20-%20单例.md) +- [简单工厂.md](notes/设计模式%20-%20简单工厂.md) +- [工厂方法.md](notes/设计模式%20-%20工厂方法.md) +- [抽象工厂.md](notes/设计模式%20-%20抽象工厂.md) +- [生成器.md](notes/设计模式%20-%20生成器.md) +- [原型模式.md](notes/设计模式%20-%20原型模式.md) + +# 三、行为型 + +- [责任链.md](notes/设计模式%20-%20责任链.md) +- [命令.md](notes/设计模式%20-%20命令.md) +- [解释器.md](notes/设计模式%20-%20解释器.md) +- [迭代器.md](notes/设计模式%20-%20迭代器.md) +- [中介者.md](notes/设计模式%20-%20中介者.md) +- [备忘录.md](notes/设计模式%20-%20备忘录.md) +- [观察者.md](notes/设计模式%20-%20观察者.md) +- [状态.md](notes/设计模式%20-%20状态.md) +- [策略.md](notes/设计模式%20-%20策略.md) +- [模板方法.md](notes/设计模式%20-%20模板方法.md) +- [访问者.md](notes/设计模式%20-%20访问者.md) +- [空对象.md](notes/设计模式%20-%20空对象.md) + +# 四、结构型 + +- [适配器.md](notes/设计模式%20-%20适配器.md) +- [桥接.md](notes/设计模式%20-%20桥接.md) +- [组合.md](notes/设计模式%20-%20组合.md) +- [装饰.md](notes/设计模式%20-%20装饰.md) +- [外观.md](notes/设计模式%20-%20外观.md) +- [享元.md](notes/设计模式%20-%20享元.md) +- [代理.md](notes/设计模式%20-%20代理.md) + +# 参考资料 + +- 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007. +- Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007. +- Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. +- [Design Patterns](http://www.oodesign.com/) +- [Design patterns implemented in Java](http://java-design-patterns.com/) +- [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\251\272\345\257\271\350\261\241.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\251\272\345\257\271\350\261\241.md" new file mode 100644 index 0000000000..0b15744845 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\251\272\345\257\271\350\261\241.md" @@ -0,0 +1,55 @@ +## 空对象(Null) + +### Intent + +使用什么都不做 + +的空对象来代替 NULL。 + +一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。 + +### Class Diagram + +

+ +### Implementation + +```java +public abstract class AbstractOperation { + abstract void request(); +} +``` + +```java +public class RealOperation extends AbstractOperation { + @Override + void request() { + System.out.println("do something"); + } +} +``` + +```java +public class NullOperation extends AbstractOperation{ + @Override + void request() { + // do nothing + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + AbstractOperation abstractOperation = func(-1); + abstractOperation.request(); + } + + public static AbstractOperation func(int para) { + if (para < 0) { + return new NullOperation(); + } + return new RealOperation(); + } +} +``` diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\255\226\347\225\245.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\255\226\347\225\245.md" new file mode 100644 index 0000000000..d36824ea56 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\255\226\347\225\245.md" @@ -0,0 +1,89 @@ +## 9. 策略(Strategy) + +### Intent + +定义一系列算法,封装每个算法,并使它们可以互换。 + +策略模式可以让算法独立于使用它的客户端。 + +### Class Diagram + +- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。 +- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。 + +

+ +### 与状态模式的比较 + +状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。 + +状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。 + +### Implementation + +设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。 + +```java +public interface QuackBehavior { + void quack(); +} +``` + +```java +public class Quack implements QuackBehavior { + @Override + public void quack() { + System.out.println("quack!"); + } +} +``` + +```java +public class Squeak implements QuackBehavior{ + @Override + public void quack() { + System.out.println("squeak!"); + } +} +``` + +```java +public class Duck { + + private QuackBehavior quackBehavior; + + public void performQuack() { + if (quackBehavior != null) { + quackBehavior.quack(); + } + } + + public void setQuackBehavior(QuackBehavior quackBehavior) { + this.quackBehavior = quackBehavior; + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + Duck duck = new Duck(); + duck.setQuackBehavior(new Squeak()); + duck.performQuack(); + duck.setQuackBehavior(new Quack()); + duck.performQuack(); + } +} +``` + +```html +squeak! +quack! +``` + +### JDK + +- java.util.Comparator#compare() +- javax.servlet.http.HttpServlet +- javax.servlet.Filter#doFilter() diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\256\200\345\215\225\345\267\245\345\216\202.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\256\200\345\215\225\345\267\245\345\216\202.md" new file mode 100644 index 0000000000..55d79c0221 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\256\200\345\215\225\345\267\245\345\216\202.md" @@ -0,0 +1,82 @@ +## 简单工厂(Simple Factory) + +### Intent + +在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。 + +### Class Diagram + +简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。 + +这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 + +

+ +### Implementation + +```java +public interface Product { +} +``` + +```java +public class ConcreteProduct implements Product { +} +``` + +```java +public class ConcreteProduct1 implements Product { +} +``` + +```java +public class ConcreteProduct2 implements Product { +} +``` + +以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。 + +```java +public class Client { + + public static void main(String[] args) { + int type = 1; + Product product; + if (type == 1) { + product = new ConcreteProduct1(); + } else if (type == 2) { + product = new ConcreteProduct2(); + } else { + product = new ConcreteProduct(); + } + // do something with the product + } +} +``` + +以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。 + +```java +public class SimpleFactory { + + public Product createProduct(int type) { + if (type == 1) { + return new ConcreteProduct1(); + } else if (type == 2) { + return new ConcreteProduct2(); + } + return new ConcreteProduct(); + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + SimpleFactory simpleFactory = new SimpleFactory(); + Product product = simpleFactory.createProduct(1); + // do something with the product + } +} +``` diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\273\204\345\220\210.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\273\204\345\220\210.md" new file mode 100644 index 0000000000..fd78f25ad9 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \347\273\204\345\220\210.md" @@ -0,0 +1,133 @@ +## 组合(Composite) + +### Intent + +将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。 + +### Class Diagram + +组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。 + +组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 + +

+ +### Implementation + +```java +public abstract class Component { + protected String name; + + public Component(String name) { + this.name = name; + } + + public void print() { + print(0); + } + + abstract void print(int level); + + abstract public void add(Component component); + + abstract public void remove(Component component); +} +``` + +```java +public class Composite extends Component { + + private List child; + + public Composite(String name) { + super(name); + child = new ArrayList<>(); + } + + @Override + void print(int level) { + for (int i = 0; i < level; i++) { + System.out.print("--"); + } + System.out.println("Composite:" + name); + for (Component component : child) { + component.print(level + 1); + } + } + + @Override + public void add(Component component) { + child.add(component); + } + + @Override + public void remove(Component component) { + child.remove(component); + } +} +``` + +```java +public class Leaf extends Component { + public Leaf(String name) { + super(name); + } + + @Override + void print(int level) { + for (int i = 0; i < level; i++) { + System.out.print("--"); + } + System.out.println("left:" + name); + } + + @Override + public void add(Component component) { + throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点 + } + + @Override + public void remove(Component component) { + throw new UnsupportedOperationException(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Composite root = new Composite("root"); + Component node1 = new Leaf("1"); + Component node2 = new Composite("2"); + Component node3 = new Leaf("3"); + root.add(node1); + root.add(node2); + root.add(node3); + Component node21 = new Leaf("21"); + Component node22 = new Composite("22"); + node2.add(node21); + node2.add(node22); + Component node221 = new Leaf("221"); + node22.add(node221); + root.print(); + } +} +``` + +```html +Composite:root +--left:1 +--Composite:2 +----left:21 +----Composite:22 +------left:221 +--left:3 +``` + +### JDK + +- javax.swing.JComponent#add(Component) +- java.awt.Container#add(Component) +- java.util.Map#putAll(Map) +- java.util.List#addAll(Collection) +- java.util.Set#addAll(Collection) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\243\205\351\245\260.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\243\205\351\245\260.md" new file mode 100644 index 0000000000..cf95292498 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\243\205\351\245\260.md" @@ -0,0 +1,107 @@ +## 装饰(Decorator) + +### Intent + +为对象动态添加功能。 + +### Class Diagram + +装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 + +

+ +### Implementation + +设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。 + +下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 + +

+ +```java +public interface Beverage { + double cost(); +} +``` + +```java +public class DarkRoast implements Beverage { + @Override + public double cost() { + return 1; + } +} +``` + +```java +public class HouseBlend implements Beverage { + @Override + public double cost() { + return 1; + } +} +``` + +```java +public abstract class CondimentDecorator implements Beverage { + protected Beverage beverage; +} +``` + +```java +public class Milk extends CondimentDecorator { + + public Milk(Beverage beverage) { + this.beverage = beverage; + } + + @Override + public double cost() { + return 1 + beverage.cost(); + } +} +``` + +```java +public class Mocha extends CondimentDecorator { + + public Mocha(Beverage beverage) { + this.beverage = beverage; + } + + @Override + public double cost() { + return 1 + beverage.cost(); + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + Beverage beverage = new HouseBlend(); + beverage = new Mocha(beverage); + beverage = new Milk(beverage); + System.out.println(beverage.cost()); + } +} +``` + +```html +3.0 +``` + +### 设计原则 + +类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。 + +不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。 + +### JDK + +- java.io.BufferedInputStream(InputStream) +- java.io.DataInputStream(InputStream) +- java.io.BufferedOutputStream(OutputStream) +- java.util.zip.ZipOutputStream(OutputStream) +- java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]() diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\202\345\257\237\350\200\205.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\202\345\257\237\350\200\205.md" new file mode 100644 index 0000000000..141c513cae --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\202\345\257\237\350\200\205.md" @@ -0,0 +1,134 @@ +## 7. 观察者(Observer) + +### Intent + +定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。 + +主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 + +

+ +### Class Diagram + +主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。 + +观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。 + +

+ +### Implementation + +天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 + +

+ +```java +public interface Subject { + void registerObserver(Observer o); + + void removeObserver(Observer o); + + void notifyObserver(); +} +``` + +```java +public class WeatherData implements Subject { + private List observers; + private float temperature; + private float humidity; + private float pressure; + + public WeatherData() { + observers = new ArrayList<>(); + } + + public void setMeasurements(float temperature, float humidity, float pressure) { + this.temperature = temperature; + this.humidity = humidity; + this.pressure = pressure; + notifyObserver(); + } + + @Override + public void registerObserver(Observer o) { + observers.add(o); + } + + @Override + public void removeObserver(Observer o) { + int i = observers.indexOf(o); + if (i >= 0) { + observers.remove(i); + } + } + + @Override + public void notifyObserver() { + for (Observer o : observers) { + o.update(temperature, humidity, pressure); + } + } +} +``` + +```java +public interface Observer { + void update(float temp, float humidity, float pressure); +} +``` + +```java +public class StatisticsDisplay implements Observer { + + public StatisticsDisplay(Subject weatherData) { + weatherData.registerObserver(this); + } + + @Override + public void update(float temp, float humidity, float pressure) { + System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure); + } +} +``` + +```java +public class CurrentConditionsDisplay implements Observer { + + public CurrentConditionsDisplay(Subject weatherData) { + weatherData.registerObserver(this); + } + + @Override + public void update(float temp, float humidity, float pressure) { + System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure); + } +} +``` + +```java +public class WeatherStation { + public static void main(String[] args) { + WeatherData weatherData = new WeatherData(); + CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); + StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); + + weatherData.setMeasurements(0, 0, 0); + weatherData.setMeasurements(1, 1, 1); + } +} +``` + +```html +CurrentConditionsDisplay.update: 0.0 0.0 0.0 +StatisticsDisplay.update: 0.0 0.0 0.0 +CurrentConditionsDisplay.update: 1.0 1.0 1.0 +StatisticsDisplay.update: 1.0 1.0 1.0 +``` + +### JDK + +- [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) +- [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) +- [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) +- [RxJava](https://github.com/ReactiveX/RxJava) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\243\351\207\212\345\231\250.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\243\351\207\212\345\231\250.md" new file mode 100644 index 0000000000..2f53406866 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\247\243\351\207\212\345\231\250.md" @@ -0,0 +1,123 @@ +## 解释器(Interpreter) + +### Intent + +为语言创建解释器,通常由语言的语法和语法分析来定义。 + +### Class Diagram + +- TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。 +- Context:上下文,包含解释器之外的一些全局信息。 + +

+ +### Implementation + +以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。 + +例如一颗解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。 + +这里的 Context 指的是 String。 + +```java +public abstract class Expression { + public abstract boolean interpret(String str); +} +``` + +```java +public class TerminalExpression extends Expression { + + private String literal = null; + + public TerminalExpression(String str) { + literal = str; + } + + public boolean interpret(String str) { + StringTokenizer st = new StringTokenizer(str); + while (st.hasMoreTokens()) { + String test = st.nextToken(); + if (test.equals(literal)) { + return true; + } + } + return false; + } +} +``` + +```java +public class AndExpression extends Expression { + + private Expression expression1 = null; + private Expression expression2 = null; + + public AndExpression(Expression expression1, Expression expression2) { + this.expression1 = expression1; + this.expression2 = expression2; + } + + public boolean interpret(String str) { + return expression1.interpret(str) && expression2.interpret(str); + } +} +``` + +```java +public class OrExpression extends Expression { + private Expression expression1 = null; + private Expression expression2 = null; + + public OrExpression(Expression expression1, Expression expression2) { + this.expression1 = expression1; + this.expression2 = expression2; + } + + public boolean interpret(String str) { + return expression1.interpret(str) || expression2.interpret(str); + } +} +``` + +```java +public class Client { + + /** + * 构建解析树 + */ + public static Expression buildInterpreterTree() { + // Literal + Expression terminal1 = new TerminalExpression("A"); + Expression terminal2 = new TerminalExpression("B"); + Expression terminal3 = new TerminalExpression("C"); + Expression terminal4 = new TerminalExpression("D"); + // B C + Expression alternation1 = new OrExpression(terminal2, terminal3); + // A Or (B C) + Expression alternation2 = new OrExpression(terminal1, alternation1); + // D And (A Or (B C)) + return new AndExpression(terminal4, alternation2); + } + + public static void main(String[] args) { + Expression define = buildInterpreterTree(); + String context1 = "D A"; + String context2 = "A B"; + System.out.println(define.interpret(context1)); + System.out.println(define.interpret(context2)); + } +} +``` + +```html +true +false +``` + +### JDK + +- [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) +- [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) +- All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) +- [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\256\277\351\227\256\350\200\205.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\256\277\351\227\256\350\200\205.md" new file mode 100644 index 0000000000..94fe917d6c --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\256\277\351\227\256\350\200\205.md" @@ -0,0 +1,206 @@ +## 访问者(Visitor) + +### Intent + +为一个对象结构(比如组合结构)增加新能力。 + +### Class Diagram + +- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作 +- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 +- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。 + +

+ +### Implementation + +```java +public interface Element { + void accept(Visitor visitor); +} +``` + +```java +class CustomerGroup { + + private List customers = new ArrayList<>(); + + void accept(Visitor visitor) { + for (Customer customer : customers) { + customer.accept(visitor); + } + } + + void addCustomer(Customer customer) { + customers.add(customer); + } +} +``` + +```java +public class Customer implements Element { + + private String name; + private List orders = new ArrayList<>(); + + Customer(String name) { + this.name = name; + } + + String getName() { + return name; + } + + void addOrder(Order order) { + orders.add(order); + } + + public void accept(Visitor visitor) { + visitor.visit(this); + for (Order order : orders) { + order.accept(visitor); + } + } +} +``` + +```java +public class Order implements Element { + + private String name; + private List items = new ArrayList(); + + Order(String name) { + this.name = name; + } + + Order(String name, String itemName) { + this.name = name; + this.addItem(new Item(itemName)); + } + + String getName() { + return name; + } + + void addItem(Item item) { + items.add(item); + } + + public void accept(Visitor visitor) { + visitor.visit(this); + + for (Item item : items) { + item.accept(visitor); + } + } +} +``` + +```java +public class Item implements Element { + + private String name; + + Item(String name) { + this.name = name; + } + + String getName() { + return name; + } + + public void accept(Visitor visitor) { + visitor.visit(this); + } +} +``` + +```java +public interface Visitor { + void visit(Customer customer); + + void visit(Order order); + + void visit(Item item); +} +``` + +```java +public class GeneralReport implements Visitor { + + private int customersNo; + private int ordersNo; + private int itemsNo; + + public void visit(Customer customer) { + System.out.println(customer.getName()); + customersNo++; + } + + public void visit(Order order) { + System.out.println(order.getName()); + ordersNo++; + } + + public void visit(Item item) { + System.out.println(item.getName()); + itemsNo++; + } + + public void displayResults() { + System.out.println("Number of customers: " + customersNo); + System.out.println("Number of orders: " + ordersNo); + System.out.println("Number of items: " + itemsNo); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Customer customer1 = new Customer("customer1"); + customer1.addOrder(new Order("order1", "item1")); + customer1.addOrder(new Order("order2", "item1")); + customer1.addOrder(new Order("order3", "item1")); + + Order order = new Order("order_a"); + order.addItem(new Item("item_a1")); + order.addItem(new Item("item_a2")); + order.addItem(new Item("item_a3")); + Customer customer2 = new Customer("customer2"); + customer2.addOrder(order); + + CustomerGroup customers = new CustomerGroup(); + customers.addCustomer(customer1); + customers.addCustomer(customer2); + + GeneralReport visitor = new GeneralReport(); + customers.accept(visitor); + visitor.displayResults(); + } +} +``` + +```html +customer1 +order1 +item1 +order2 +item1 +order3 +item1 +customer2 +order_a +item_a1 +item_a2 +item_a3 +Number of customers: 2 +Number of orders: 4 +Number of items: 6 +``` + +### JDK + +- javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor +- javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\264\243\344\273\273\351\223\276.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\264\243\344\273\273\351\223\276.md" new file mode 100644 index 0000000000..c652e334db --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\264\243\344\273\273\351\223\276.md" @@ -0,0 +1,129 @@ +## 责任链(Chain Of Responsibility) + +### Intent + +使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。 + +### Class Diagram + +- Handler:定义处理请求的接口,并且实现后继链(successor) + +

+ +### Implementation + +```java +public abstract class Handler { + + protected Handler successor; + + + public Handler(Handler successor) { + this.successor = successor; + } + + + protected abstract void handleRequest(Request request); +} +``` + +```java +public class ConcreteHandler1 extends Handler { + + public ConcreteHandler1(Handler successor) { + super(successor); + } + + + @Override + protected void handleRequest(Request request) { + if (request.getType() == RequestType.TYPE1) { + System.out.println(request.getName() + " is handle by ConcreteHandler1"); + return; + } + if (successor != null) { + successor.handleRequest(request); + } + } +} +``` + +```java +public class ConcreteHandler2 extends Handler { + + public ConcreteHandler2(Handler successor) { + super(successor); + } + + + @Override + protected void handleRequest(Request request) { + if (request.getType() == RequestType.TYPE2) { + System.out.println(request.getName() + " is handle by ConcreteHandler2"); + return; + } + if (successor != null) { + successor.handleRequest(request); + } + } +} +``` + +```java +public class Request { + + private RequestType type; + private String name; + + + public Request(RequestType type, String name) { + this.type = type; + this.name = name; + } + + + public RequestType getType() { + return type; + } + + + public String getName() { + return name; + } +} + +``` + +```java +public enum RequestType { + TYPE1, TYPE2 +} +``` + +```java +public class Client { + + public static void main(String[] args) { + + Handler handler1 = new ConcreteHandler1(null); + Handler handler2 = new ConcreteHandler2(handler1); + + Request request1 = new Request(RequestType.TYPE1, "request1"); + handler2.handleRequest(request1); + + Request request2 = new Request(RequestType.TYPE2, "request2"); + handler2.handleRequest(request2); + } +} +``` + +```html +request1 is handle by ConcreteHandler1 +request2 is handle by ConcreteHandler2 +``` + +### JDK + +- [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) +- [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) +- [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\277\255\344\273\243\345\231\250.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\277\255\344\273\243\345\231\250.md" new file mode 100644 index 0000000000..d911fb62a1 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \350\277\255\344\273\243\345\231\250.md" @@ -0,0 +1,89 @@ +## 迭代器(Iterator) + +### Intent + +提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。 + +### Class Diagram + +- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator; +- Iterator 主要定义了 hasNext() 和 next() 方法; +- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 + +
image-20191130164425351

+ +### Implementation + +```java +public interface Aggregate { + Iterator createIterator(); +} +``` + +```java +public class ConcreteAggregate implements Aggregate { + + private Integer[] items; + + public ConcreteAggregate() { + items = new Integer[10]; + for (int i = 0; i < items.length; i++) { + items[i] = i; + } + } + + @Override + public Iterator createIterator() { + return new ConcreteIterator(items); + } +} +``` + +```java +public interface Iterator { + + Item next(); + + boolean hasNext(); +} +``` + +```java +public class ConcreteIterator implements Iterator { + + private Item[] items; + private int position = 0; + + public ConcreteIterator(Item[] items) { + this.items = items; + } + + @Override + public Object next() { + return items[position++]; + } + + @Override + public boolean hasNext() { + return position < items.length; + } +} +``` + +```java +public class Client { + + public static void main(String[] args) { + Aggregate aggregate = new ConcreteAggregate(); + Iterator iterator = aggregate.createIterator(); + while (iterator.hasNext()) { + System.out.println(iterator.next()); + } + } +} +``` + +### JDK + +- [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) +- [java.util.Enumeration](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) diff --git "a/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \351\200\202\351\205\215\345\231\250.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \351\200\202\351\205\215\345\231\250.md" new file mode 100644 index 0000000000..c62df3da83 --- /dev/null +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217 - \351\200\202\351\205\215\345\231\250.md" @@ -0,0 +1,70 @@ +## 1. 适配器(Adapter) + +### Intent + +把一个类接口转换成另一个用户需要的接口。 + +

+ +### Class Diagram + +

+ +### Implementation + +鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。 + +要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子! + +```java +public interface Duck { + void quack(); +} +``` + +```java +public interface Turkey { + void gobble(); +} +``` + +```java +public class WildTurkey implements Turkey { + @Override + public void gobble() { + System.out.println("gobble!"); + } +} +``` + +```java +public class TurkeyAdapter implements Duck { + Turkey turkey; + + public TurkeyAdapter(Turkey turkey) { + this.turkey = turkey; + } + + @Override + public void quack() { + turkey.gobble(); + } +} +``` + +```java +public class Client { + public static void main(String[] args) { + Turkey turkey = new WildTurkey(); + Duck duck = new TurkeyAdapter(turkey); + duck.quack(); + } +} +``` + +### JDK + +- [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) +- [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-) +- [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) +- [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-) diff --git "a/docs/notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" similarity index 89% rename from "docs/notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" rename to "notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" index 612985e197..fe1002a2a5 100644 --- "a/docs/notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" +++ "b/notes/\350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -2,32 +2,132 @@ * [一、概述](#一概述) * [二、创建型](#二创建型) * [1. 单例(Singleton)](#1-单例singleton) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [Examples](#examples) + * [JDK](#jdk) * [2. 简单工厂(Simple Factory)](#2-简单工厂simple-factory) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) * [3. 工厂方法(Factory Method)](#3-工厂方法factory-method) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [4. 抽象工厂(Abstract Factory)](#4-抽象工厂abstract-factory) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [5. 生成器(Builder)](#5-生成器builder) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [6. 原型模式(Prototype)](#6-原型模式prototype) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [三、行为型](#三行为型) * [1. 责任链(Chain Of Responsibility)](#1-责任链chain-of-responsibility) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [2. 命令(Command)](#2-命令command) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [3. 解释器(Interpreter)](#3-解释器interpreter) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [4. 迭代器(Iterator)](#4-迭代器iterator) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [5. 中介者(Mediator)](#5-中介者mediator) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [6. 备忘录(Memento)](#6-备忘录memento) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [7. 观察者(Observer)](#7-观察者observer) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [8. 状态(State)](#8-状态state) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) * [9. 策略(Strategy)](#9-策略strategy) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [与状态模式的比较](#与状态模式的比较) + * [Implementation](#implementation) + * [JDK](#jdk) * [10. 模板方法(Template Method)](#10-模板方法template-method) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [11. 访问者(Visitor)](#11-访问者visitor) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [12. 空对象(Null)](#12-空对象null) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) * [四、结构型](#四结构型) * [1. 适配器(Adapter)](#1-适配器adapter) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [2. 桥接(Bridge)](#2-桥接bridge) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [3. 组合(Composite)](#3-组合composite) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [4. 装饰(Decorator)](#4-装饰decorator) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [设计原则](#设计原则) + * [JDK](#jdk) * [5. 外观(Facade)](#5-外观facade) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [设计原则](#设计原则) * [6. 享元(Flyweight)](#6-享元flyweight) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [7. 代理(Proxy)](#7-代理proxy) + * [Intent](#intent) + * [Class Diagram](#class-diagram) + * [Implementation](#implementation) + * [JDK](#jdk) * [参考资料](#参考资料) @@ -38,8 +138,6 @@ 拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 -[源码以及 UML 图](https://github.com/CyC2018/Design-Pattern-Java) - # 二、创建型 ## 1. 单例(Singleton) @@ -54,7 +152,7 @@ 私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 -

+

### Implementation @@ -133,7 +231,7 @@ public class Singleton { } ``` -考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句。 +考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。 ```java if (uniqueInstance == null) { @@ -155,7 +253,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueIn #### Ⅴ 静态内部类实现 -当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。 +当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。 这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。 @@ -226,10 +324,10 @@ secondName secondName ``` -该实现在多次序列化再进行反序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。 - 该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。 +该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。 + ### Examples - Logger Classes @@ -255,7 +353,7 @@ secondName 这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 -

+

### Implementation @@ -338,7 +436,7 @@ public class Client { 下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 -

+

### Implementation @@ -390,7 +488,7 @@ public class ConcreteFactory2 extends Factory { ### Intent -提供一个接口,用于创建 **相关的对象家族** 。 +提供一个接口,用于创建 **相关的对象家族** 。 ### Class Diagram @@ -402,7 +500,7 @@ public class ConcreteFactory2 extends Factory { 从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 -

+

### Implementation @@ -492,7 +590,7 @@ public class Client { ### Class Diagram -

+

### Implementation @@ -582,7 +680,7 @@ abcdefghijklmnopqrstuvwxyz ### Class Diagram -

+

### Implementation @@ -643,7 +741,7 @@ abc - Handler:定义处理请求的接口,并且实现后继链(successor) -

+

### Implementation @@ -781,13 +879,13 @@ request2 is handle by ConcreteHandler2 - Invoker:通过它来调用命令 - Client:可以设置命令与命令的接收者 -

+

### Implementation 设计一个遥控器,可以控制电灯开关。 -

+

```java public interface Command { @@ -902,7 +1000,7 @@ public class Client { - TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。 - Context:上下文,包含解释器之外的一些全局信息。 -

+

### Implementation @@ -1027,7 +1125,7 @@ false - Iterator 主要定义了 hasNext() 和 next() 方法。 - Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 -

+

### Implementation @@ -1116,17 +1214,17 @@ public class Client { - Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。 - Colleague:同事,相关对象 -

+

### Implementation Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构: -

+

使用中介者模式可以将复杂的依赖结构变成星形结构: -

+

```java public abstract class Colleague { @@ -1286,7 +1384,7 @@ doSprinkler() - Caretaker:负责保存好备忘录 - Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。 -

+

### Implementation @@ -1459,7 +1557,7 @@ public class Client { 主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 -

+

### Class Diagram @@ -1467,13 +1565,13 @@ public class Client { 观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。 -

+

### Implementation 天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 -

+

```java public interface Subject { @@ -1594,13 +1692,13 @@ StatisticsDisplay.update: 1.0 1.0 1.0 ### Class Diagram -

+

### Implementation 糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 -

+

```java public interface State { @@ -1901,7 +1999,7 @@ No gumball dispensed - Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。 - Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。 -

+

### 与状态模式的比较 @@ -1988,13 +2086,13 @@ quack! ### Class Diagram -

+

### Implementation 冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 -

+

```java public abstract class CaffeineBeverage { @@ -2091,7 +2189,7 @@ Tea.addCondiments - ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 - ObjectStructure:对象结构,可以是组合结构,或者是一个集合。 -

+

### Implementation @@ -2296,7 +2394,7 @@ Number of items: 6 ### Class Diagram -

+

### Implementation @@ -2348,11 +2446,11 @@ public class Client { 把一个类接口转换成另一个用户需要的接口。 -

+

### Class Diagram -

+

### Implementation @@ -2424,7 +2522,7 @@ public class Client { - Abstraction:定义抽象类的接口 - Implementor:定义实现类接口 -

+

### Implementation @@ -2582,7 +2680,7 @@ public class Client { 组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 -

+

### Implementation @@ -2714,7 +2812,7 @@ Composite:root 装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 -

+

### Implementation @@ -2722,7 +2820,7 @@ Composite:root 下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 -

+

```java public interface Beverage { @@ -2820,7 +2918,7 @@ public class Client { ### Class Diagram -

+

### Implementation @@ -2879,7 +2977,7 @@ public class Client { - IntrinsicState:内部状态,享元对象共享内部状态 - ExtrinsicState:外部状态,每个享元对象的外部状态不同 -

+

### Implementation @@ -2968,7 +3066,7 @@ Java 利用缓存来加速大量小对象的访问时间。 - 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。 - 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。 -

+

### Implementation @@ -3066,9 +3164,3 @@ public class ImageViewer { - [Design Patterns](http://www.oodesign.com/) - [Design patterns implemented in Java](http://java-design-patterns.com/) - [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) - - - - -
欢迎关注公众号,获取最新文章!


-
diff --git "a/notes/\351\233\206\347\276\244.md" "b/notes/\351\233\206\347\276\244.md" new file mode 100644 index 0000000000..2cc3a7c884 --- /dev/null +++ "b/notes/\351\233\206\347\276\244.md" @@ -0,0 +1,214 @@ +# 集群 + +* [集群](#集群) + * [一、负载均衡](#一负载均衡) + * [负载均衡算法](#负载均衡算法) + * [1. 轮询(Round Robin)](#1-轮询round-robin) + * [2. 加权轮询(Weighted Round Robbin)](#2-加权轮询weighted-round-robbin) + * [3. 最少连接(least Connections)](#3-最少连接least-connections) + * [4. 加权最少连接(Weighted Least Connection)](#4-加权最少连接weighted-least-connection) + * [5. 随机算法(Random)](#5-随机算法random) + * [6. 源地址哈希法 (IP Hash)](#6-源地址哈希法-ip-hash) + * [转发实现](#转发实现) + * [1. HTTP 重定向](#1-http-重定向) + * [2. DNS 域名解析](#2-dns-域名解析) + * [3. 反向代理服务器](#3-反向代理服务器) + * [4. 网络层](#4-网络层) + * [5. 链路层](#5-链路层) + * [二、集群下的 Session 管理](#二集群下的-session-管理) + * [Sticky Session](#sticky-session) + * [Session Replication](#session-replication) + * [Session Server](#session-server) + + + +## 一、负载均衡 + +集群中的应用服务器(节点)通常被设计成无状态,用户可以请求任何一个节点。 + +负载均衡器会根据集群中每个节点的负载情况,将用户请求转发到合适的节点上。 + +负载均衡器可以用来实现高可用以及伸缩性: + +- 高可用:当某个节点故障时,负载均衡器会将用户请求转发到另外的节点上,从而保证所有服务持续可用; +- 伸缩性:根据系统整体负载情况,可以很容易地添加或移除节点。 + +负载均衡器运行过程包含两个部分: + +1. 根据负载均衡算法得到转发的节点; +2. 进行转发。 + +### 负载均衡算法 + +#### 1. 轮询(Round Robin) + +轮询算法把每个请求轮流发送到每个服务器上。 + +下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。 + +

+ + +该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担过大的负载(下图的 Server 2)。 + +

+ +#### 2. 加权轮询(Weighted Round Robbin) + +加权轮询是在轮询的基础上,根据服务器的性能差异,为服务器赋予一定的权值,性能高的服务器分配更高的权值。 + +例如下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。 + +

+ +#### 3. 最少连接(least Connections) + +由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过大,而另一台服务器的连接过小,造成负载不均衡。 + +例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开,此时 (6, 4) 请求连接服务器 2。该系统继续运行时,服务器 2 会承担过大的负载。 + +

+ +最少连接算法就是将请求发送给当前最少连接数的服务器上。 + +例如下图中,服务器 1 当前连接数最小,那么新到来的请求 6 就会被发送到服务器 1 上。 + +

+ +#### 4. 加权最少连接(Weighted Least Connection) + +在最少连接的基础上,根据服务器的性能为每台服务器分配权重,再根据权重计算出每台服务器能处理的连接数。 + +#### 5. 随机算法(Random) + +把请求随机发送到服务器上。 + +和轮询算法类似,该算法比较适合服务器性能差不多的场景。 + +

+ +#### 6. 源地址哈希法 (IP Hash) + +源地址哈希通过对客户端 IP 计算哈希值之后,再对服务器数量取模得到目标服务器的序号。 + +可以保证同一 IP 的客户端的请求会转发到同一台服务器上,用来实现会话粘滞(Sticky Session) + +

+ +### 转发实现 + +#### 1. HTTP 重定向 + +HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服务器的 IP 地址之后,将该地址写入 HTTP 重定向报文中,状态码为 302。客户端收到重定向报文之后,需要重新向服务器发起请求。 + +缺点: + +- 需要两次请求,因此访问延迟比较高; +- HTTP 负载均衡器处理能力有限,会限制集群的规模。 + +该负载均衡转发的缺点比较明显,实际场景中很少使用它。 + +

+ +#### 2. DNS 域名解析 + +在 DNS 解析域名的同时使用负载均衡算法计算服务器 IP 地址。 + +优点: + +- DNS 能够根据地理位置进行域名解析,返回离用户最近的服务器 IP 地址。 + +缺点: + +- 由于 DNS 具有多级结构,每一级的域名记录都可能被缓存,当下线一台服务器需要修改 DNS 记录时,需要过很长一段时间才能生效。 + +大型网站基本使用了 DNS 做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。也就是说,域名解析的结果为内部的负载均衡服务器 IP 地址。 + +

+ +#### 3. 反向代理服务器 + +反向代理服务器位于源服务器前面,用户的请求需要先经过反向代理服务器才能到达源服务器。反向代理可以用来进行缓存、日志记录等,同时也可以用来做为负载均衡服务器。 + +在这种负载均衡转发方式下,客户端不直接请求源服务器,因此源服务器不需要外部 IP 地址,而反向代理需要配置内部和外部两套 IP 地址。 + +优点: + +- 与其它功能集成在一起,部署简单。 + +缺点: + +- 所有请求和响应都需要经过反向代理服务器,它可能会成为性能瓶颈。 + +#### 4. 网络层 + +在操作系统内核进程获取网络数据包,根据负载均衡算法计算源服务器的 IP 地址,并修改请求数据包的目的 IP 地址,最后进行转发。 + +源服务器返回的响应也需要经过负载均衡服务器,通常是让负载均衡服务器同时作为集群的网关服务器来实现。 + +优点: + +- 在内核进程中进行处理,性能比较高。 + +缺点: + +- 和反向代理一样,所有的请求和响应都经过负载均衡服务器,会成为性能瓶颈。 + +#### 5. 链路层 + +在链路层根据负载均衡算法计算源服务器的 MAC 地址,并修改请求数据包的目的 MAC 地址,并进行转发。 + +通过配置源服务器的虚拟 IP 地址和负载均衡服务器的 IP 地址一致,从而不需要修改 IP 地址就可以进行转发。也正因为 IP 地址一样,所以源服务器的响应不需要转发回负载均衡服务器,可以直接转发给客户端,避免了负载均衡服务器的成为瓶颈。 + +这是一种三角传输模式,被称为直接路由。对于提供下载和视频服务的网站来说,直接路由避免了大量的网络传输数据经过负载均衡服务器。 + +这是目前大型网站使用最广负载均衡转发方式,在 Linux 平台可以使用的负载均衡服务器为 LVS(Linux Virtual Server)。 + +参考: + +- [Comparing Load Balancing Algorithms](http://www.jscape.com/blog/load-balancing-algorithms) +- [Redirection and Load Balancing](http://slideplayer.com/slide/6599069/#) + +## 二、集群下的 Session 管理 + +一个用户的 Session 信息如果存储在一个服务器上,那么当负载均衡器把用户的下一个请求转发到另一个服务器,由于服务器没有用户的 Session 信息,那么该用户就需要重新进行登录等操作。 + +### Sticky Session + +需要配置负载均衡器,使得一个用户的所有请求都路由到同一个服务器,这样就可以把用户的 Session 存放在该服务器中。 + +缺点: + +- 当服务器宕机时,将丢失该服务器上的所有 Session。 + +

+ +### Session Replication + +在服务器之间进行 Session 同步操作,每个服务器都有所有用户的 Session 信息,因此用户可以向任何一个服务器进行请求。 + +缺点: + +- 占用过多内存; +- 同步过程占用网络带宽以及服务器处理器时间。 + +

+ +### Session Server + +使用一个单独的服务器存储 Session 数据,可以使用传统的 MySQL,也使用 Redis 或者 Memcached 这种内存型数据库。 + +优点: + +- 为了使得大型网站具有伸缩性,集群中的应用服务器通常需要保持无状态,那么应用服务器不能存储用户的会话信息。Session Server 将用户的会话信息单独进行存储,从而保证了应用服务器的无状态。 + +缺点: + +- 需要去实现存取 Session 的代码。 + +

+ +参考: + +- [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/) + diff --git "a/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" "b/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" new file mode 100644 index 0000000000..d5ba57d94b --- /dev/null +++ "b/notes/\351\235\242\345\220\221\345\257\271\350\261\241\346\200\235\346\203\263.md" @@ -0,0 +1,372 @@ +# 面向对象思想 + +* [面向对象思想](#面向对象思想) + * [一、三大特性](#一三大特性) + * [封装](#封装) + * [继承](#继承) + * [多态](#多态) + * [二、类图](#二类图) + * [泛化关系 (Generalization)](#泛化关系-generalization) + * [实现关系 (Realization)](#实现关系-realization) + * [聚合关系 (Aggregation)](#聚合关系-aggregation) + * [组合关系 (Composition)](#组合关系-composition) + * [关联关系 (Association)](#关联关系-association) + * [依赖关系 (Dependency)](#依赖关系-dependency) + * [三、设计原则](#三设计原则) + * [S.O.L.I.D](#solid) + * [其他常见原则](#其他常见原则) + * [参考资料](#参考资料) + + + +## 一、三大特性 + +### 封装 + +利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外的接口使其与外部发生联系。用户无需关心对象内部的细节,但可以通过对象对外提供的接口来访问该对象。 + +优点: + +- 减少耦合:可以独立地开发、测试、优化、使用、理解和修改 +- 减轻维护的负担:可以更容易被理解,并且在调试的时候可以不影响其他模块 +- 有效地调节性能:可以通过剖析来确定哪些模块影响了系统的性能 +- 提高软件的可重用性 +- 降低了构建大型系统的风险:即使整个系统不可用,但是这些独立的模块却有可能是可用的 + +以下 Person 类封装 name、gender、age 等属性,外界只能通过 get() 方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work() 方法使用。 + +注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 gender 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。 + +```java +public class Person { + + private String name; + private int gender; + private int age; + + public String getName() { + return name; + } + + public String getGender() { + return gender == 0 ? "man" : "woman"; + } + + public void work() { + if (18 <= age && age <= 50) { + System.out.println(name + " is working very hard!"); + } else { + System.out.println(name + " can't work any more!"); + } + } +} +``` + +### 继承 + +继承实现了 **IS-A** 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。 + +继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。 + +Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 **向上转型** 。 + +```java +Animal animal = new Cat(); +``` + +### 多态 + +多态分为编译时多态和运行时多态: + +- 编译时多态主要指方法的重载 +- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定 + +运行时多态有三个条件: + +- 继承 +- 覆盖(重写) +- 向上转型 + +下面的代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。 + +```java +public class Instrument { + + public void play() { + System.out.println("Instument is playing..."); + } +} +``` + +```java +public class Wind extends Instrument { + + public void play() { + System.out.println("Wind is playing..."); + } +} +``` + +```java +public class Percussion extends Instrument { + + public void play() { + System.out.println("Percussion is playing..."); + } +} +``` + +```java +public class Music { + + public static void main(String[] args) { + List instruments = new ArrayList<>(); + instruments.add(new Wind()); + instruments.add(new Percussion()); + for(Instrument instrument : instruments) { + instrument.play(); + } + } +} +``` + +``` +Wind is playing... +Percussion is playing... +``` + +## 二、类图 + +以下类图使用 [PlantUML](https://www.planttext.com/) 绘制,更多语法及使用请参考:http://plantuml.com/ 。 + +### 泛化关系 (Generalization) + +用来描述继承关系,在 Java 中使用 extends 关键字。 + +

+ +```text +@startuml + +title Generalization + +class Vihical +class Car +class Trunck + +Vihical <|-- Car +Vihical <|-- Trunck + +@enduml +``` + +### 实现关系 (Realization) + +用来实现一个接口,在 Java 中使用 implements 关键字。 + +

+ +```text +@startuml + +title Realization + +interface MoveBehavior +class Fly +class Run + +MoveBehavior <|.. Fly +MoveBehavior <|.. Run + +@enduml +``` + +### 聚合关系 (Aggregation) + +表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。 + +

+ +```text +@startuml + +title Aggregation + +class Computer +class Keyboard +class Mouse +class Screen + +Computer o-- Keyboard +Computer o-- Mouse +Computer o-- Screen + +@enduml +``` + +### 组合关系 (Composition) + +和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 + +

+ +```text +@startuml + +title Composition + +class Company +class DepartmentA +class DepartmentB + +Company *-- DepartmentA +Company *-- DepartmentB + +@enduml +``` + +### 关联关系 (Association) + +表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。 + +

+ +```text +@startuml + +title Association + +class School +class Student + +School "1" - "n" Student + +@enduml +``` + +### 依赖关系 (Dependency) + +和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式: + +- A 类是 B 类方法的局部变量; +- A 类是 B 类方法的参数; +- A 类向 B 类发送消息,从而影响 B 类发生变化。 + +

+ +```text +@startuml + +title Dependency + +class Vihicle { + move(MoveBehavior) +} + +interface MoveBehavior { + move() +} + +note "MoveBehavior.move()" as N + +Vihicle ..> MoveBehavior + +Vihicle .. N + +@enduml +``` + +## 三、设计原则 + +### S.O.L.I.D + +| 简写 | 全拼 | 中文翻译 | +| :---: | :---: | :---: | +| SRP | The Single Responsibility Principle | 单一责任原则 | +| OCP | The Open Closed Principle | 开放封闭原则 | +| LSP | The Liskov Substitution Principle | 里氏替换原则 | +| ISP | The Interface Segregation Principle | 接口分离原则 | +| DIP | The Dependency Inversion Principle | 依赖倒置原则 | + +#### 1. 单一责任原则 + +> 修改一个类的原因应该只有一个。 + +换句话说就是让一个类只负责一件事,当这个类需要做过多事情的时候,就需要分解这个类。 + +如果一个类承担的职责过多,就等于把这些职责耦合在了一起,一个职责的变化可能会削弱这个类完成其它职责的能力。 + +#### 2. 开放封闭原则 + +> 类应该对扩展开放,对修改关闭。 + +扩展就是添加新功能的意思,因此该原则要求在添加新功能时不需要修改代码。 + +符合开闭原则最典型的设计模式是装饰者模式,它可以动态地将责任附加到对象上,而不用去修改类的代码。 + +#### 3. 里氏替换原则 + +> 子类对象必须能够替换掉所有父类对象。 + +继承是一种 IS-A 关系,子类需要能够当成父类来使用,并且需要比父类更特殊。 + +如果不满足这个原则,那么各个子类的行为上就会有很大差异,增加继承体系的复杂度。 + +#### 4. 接口分离原则 + +> 不应该强迫客户依赖于它们不用的方法。 + +因此使用多个专门的接口比使用单一的总接口要好。 + +#### 5. 依赖倒置原则 + +> 高层模块不应该依赖于低层模块,二者都应该依赖于抽象;
抽象不应该依赖于细节,细节应该依赖于抽象。 + +高层模块包含一个应用程序中重要的策略选择和业务模块,如果高层模块依赖于低层模块,那么低层模块的改动就会直接影响到高层模块,从而迫使高层模块也需要改动。 + +依赖于抽象意味着: + +- 任何变量都不应该持有一个指向具体类的指针或者引用; +- 任何类都不应该从具体类派生; +- 任何方法都不应该覆写它的任何基类中的已经实现的方法。 + +### 其他常见原则 + +除了上述的经典原则,在实际开发中还有下面这些常见的设计原则。 + +| 简写 | 全拼 | 中文翻译 | +| :---: | :---: | :---: | +|LOD| The Law of Demeter | 迪米特法则 | +|CRP| The Composite Reuse Principle | 合成复用原则 | +|CCP| The Common Closure Principle | 共同封闭原则 | +|SAP| The Stable Abstractions Principle | 稳定抽象原则 | +|SDP| The Stable Dependencies Principle | 稳定依赖原则 | + +#### 1. 迪米特法则 + +迪米特法则又叫作最少知识原则(Least Knowledge Principle,简写 LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。 + +#### 2. 合成复用原则 + +尽量使用对象组合,而不是通过继承来达到复用的目的。 + +#### 3. 共同封闭原则 + +一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。 + +#### 4. 稳定抽象原则 + +最稳定的包应该是最抽象的包,不稳定的包应该是具体的包,即包的抽象程度跟它的稳定性成正比。 + +#### 5. 稳定依赖原则 + +包之间的依赖关系都应该是稳定方向依赖的,包要依赖的包要比自己更具有稳定性。 + +## 参考资料 + +- Java 编程思想 +- 敏捷软件开发:原则、模式与实践 +- [面向对象设计的 SOLID 原则](http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html) +- [看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization) +- [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html) +- [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015) diff --git a/other/README.md b/other/README.md deleted file mode 100644 index bd6f1e39de..0000000000 --- a/other/README.md +++ /dev/null @@ -1,4 +0,0 @@ -- 其他人添加的全新内容 -- 其他人的项目推广 -- 主页 README 引用的图片 -- 微信群描述文件 diff --git "a/other/leetcode \346\200\273\347\273\223.md" "b/other/leetcode \346\200\273\347\273\223.md" deleted file mode 100644 index 82ff61c7a2..0000000000 --- "a/other/leetcode \346\200\273\347\273\223.md" +++ /dev/null @@ -1,37 +0,0 @@ -# LeetCode 面试必备 - - 💪 就是干!如果你觉得有帮助请点个star,谢谢! - -> **欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远** - -## LeetCode 习题集合 - -* [LeetCode 解题集合](https://github.com/apachecn/LeetCode/tree/master/docs/Leetcode_Solutions) - - -## 模版要求 - -> 提交PR基本要求(满足任意一种即可) - -* 1. 不一样的思路 -* 2. 优化时间复杂度和空间复杂度,或者解决题目的Follow up -* 3. 有意义的简化代码 -* 4. 未提交过的题目 - -> **案例模版** - -[模版:007. Reverse Integer 反转整数](https://github.com/apachecn/LeetCode/tree/master/docs/Leetcode_Solutions/007._Reverse_Integer.md) - - -## 项目贡献者 - -> 项目发起人 - -* [@Lisanaaa](https://github.com/Lisanaaa) -* [@片刻](https://github.com/jiangzhonglian) - -> 贡献者(欢迎大家来追加) - -* [@Lisanaaa](https://github.com/Lisanaaa) -* [@片刻](https://github.com/jiangzhonglian) -* [@小瑶](https://github.com/chenyyx) - diff --git "a/other/sql \347\273\217\345\205\270\347\273\203\344\271\240\351\242\230.sql" "b/other/sql \347\273\217\345\205\270\347\273\203\344\271\240\351\242\230.sql" deleted file mode 100644 index a4b4f96400..0000000000 --- "a/other/sql \347\273\217\345\205\270\347\273\203\344\271\240\351\242\230.sql" +++ /dev/null @@ -1,483 +0,0 @@ -use fuxi; - -CREATE TABLE STUDENT -( - SNO VARCHAR(3) NOT NULL, - SNAME VARCHAR(4) NOT NULL, - SSEX VARCHAR(2) NOT NULL, - SBIRTHDAY DATETIME, - CLASS VARCHAR(5) -); - -CREATE TABLE COURSE -( - CNO VARCHAR(5) NOT NULL, - CNAME VARCHAR(10) NOT NULL, - TNO VARCHAR(10) NOT NULL -); - -CREATE TABLE SCORE -( - SNO VARCHAR(3) NOT NULL, - CNO VARCHAR(5) NOT NULL, - DEGREE NUMERIC(10, 1) NOT NULL -); - -CREATE TABLE TEACHER -( - TNO VARCHAR(3) NOT NULL, - TNAME VARCHAR(4) NOT NULL, - TSEX VARCHAR(2) NOT NULL, - TBIRTHDAY DATETIME NOT NULL, - PROF VARCHAR(6), - DEPART VARCHAR(10) NOT NULL -); - -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (108, '曾华' - , '男', '1977-09-01', 95033); -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (105, '匡明' - , '男', '1975-10-02', 95031); -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (107, '王丽' - , '女', '1976-01-23', 95033); -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (101, '李军' - , '男', '1976-02-20', 95033); -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (109, '王芳' - , '女', '1975-02-10', 95031); -INSERT INTO STUDENT (SNO, SNAME, SSEX, SBIRTHDAY, CLASS) VALUES (103, '陆君' - , '男', '1974-06-03', 95031); - -INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('3-105', '计算机导论', 825); -INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('3-245', '操作系统', 804); -INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('6-166', '数据电路', 856); -INSERT INTO COURSE (CNO, CNAME, TNO) VALUES ('9-888', '高等数学', 100); - -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (103, '3-245', 86); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (105, '3-245', 75); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (109, '3-245', 68); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (103, '3-105', 92); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (105, '3-105', 88); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (109, '3-105', 76); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (101, '3-105', 64); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (107, '3-105', 91); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (101, '6-166', 85); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (107, '6-106', 79); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (108, '3-105', 78); -INSERT INTO SCORE (SNO, CNO, DEGREE) VALUES (108, '6-166', 81); - -INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) -VALUES (804, '李诚', '男', '1958-12-02', '副教授', '计算机系'); -INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) -VALUES (856, '张旭', '男', '1969-03-12', '讲师', '电子工程系'); -INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) -VALUES (825, '王萍', '女', '1972-05-05', '助教', '计算机系'); -INSERT INTO TEACHER (TNO, TNAME, TSEX, TBIRTHDAY, PROF, DEPART) -VALUES (831, '刘冰', '女', '1977-08-14', '助教', '电子工程系'); - --- 1、 查询Student表中的所有记录的Sname、Ssex和Class列。 -select - SNAME, - SSEX, - CLASS -from STUDENT; - --- 2、 查询教师所有的单位即不重复的Depart列。 -select distinct DEPART -from TEACHER1; - --- 3、 查询Student表的所有记录。 -select * -from STUDENT; - --- 4、 查询Score表中成绩在60到80之间的所有记录。 -select * -from SCORE -where DEGREE > 60 and DEGREE < 80; - --- 5、 查询Score表中成绩为85,86或88的记录。 -select * -from SCORE -where DEGREE = 85 or DEGREE = 86 or DEGREE = 88; - --- 6、 查询Student表中“95031”班或性别为“女”的同学记录。 -select * -from STUDENT -where CLASS = '95031' or SSEX = '女'; - --- 7、 以Class降序查询Student表的所有记录。 -select * -from STUDENT -order by CLASS desc; - --- 8、 以Cno升序、Degree降序查询Score表的所有记录。 -select * -from SCORE -order by CNO asc, DEGREE desc; - --- 9、 查询“95031”班的学生人数。 -select count(*) -from STUDENT -where CLASS = '95031'; - --- 10、查询Score表中的最高分的学生学号和课程号。 -select - sno, - CNO -from SCORE -where DEGREE = ( - select max(DEGREE) - from SCORE -); - --- 11、查询‘3-105’号课程的平均分。 -select avg(DEGREE) -from SCORE -where CNO = '3-105'; - --- 12、查询Score表中至少有5名学生选修的并以3开头的课程的平均分数。 -select - avg(DEGREE), - CNO -from SCORE -where cno like '3%' -group by CNO -having count(*) > 5; - --- 13、查询最低分大于70,最高分小于90的Sno列。 -select SNO -from SCORE -group by SNO -having min(DEGREE) > 70 and max(DEGREE) < 90; - --- 14、查询所有学生的Sname、Cno和Degree列。 -select - SNAME, - CNO, - DEGREE -from STUDENT, SCORE -where STUDENT.SNO = SCORE.SNO; - --- 15、查询所有学生的Sno、Cname和Degree列。 -select - SCORE.SNO, - CNO, - DEGREE -from STUDENT, SCORE -where STUDENT.SNO = SCORE.SNO; - --- 16、查询所有学生的Sname、Cname和Degree列。 -SELECT - A.SNAME, - B.CNAME, - C.DEGREE -FROM STUDENT A - JOIN (COURSE B, SCORE C) - ON A.SNO = C.SNO AND B.CNO = C.CNO; - --- 17、查询“95033”班所选课程的平均分。 -select avg(DEGREE) -from SCORE -where sno in (select SNO - from STUDENT - where CLASS = '95033'); - --- 18、假设使用如下命令建立了一个grade表: -create table grade ( - low numeric(3, 0), - upp numeric(3), - rank char(1) -); -insert into grade values (90, 100, 'A'); -insert into grade values (80, 89, 'B'); -insert into grade values (70, 79, 'C'); -insert into grade values (60, 69, 'D'); -insert into grade values (0, 59, 'E'); --- 现查询所有同学的Sno、Cno和rank列。 -SELECT - A.SNO, - A.CNO, - B.RANK -FROM SCORE A, grade B -WHERE A.DEGREE BETWEEN B.LOW AND B.UPP -ORDER BY RANK; - --- 19、查询选修“3-105”课程的成绩高于“109”号同学成绩的所有同学的记录。 -select * -from SCORE -where CNO = '3-105' and DEGREE > ALL ( - select DEGREE - from SCORE - where SNO = '109' -); - -set @@global.sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; -set sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; - --- 20、查询score中选学一门以上课程的同学中分数为非最高分成绩的记录 -select * -from SCORE -where DEGREE < (select MAX(DEGREE) - from SCORE) -group by SNO -having count(*) > 1; - --- 21、查询成绩高于学号为“109”、课程号为“3-105”的成绩的所有记录。 --- 同19 - --- 22、查询和学号为108的同学同年出生的所有学生的Sno、Sname和Sbirthday列。 -select - SNO, - SNAME, - SBIRTHDAY -from STUDENT -where year(SBIRTHDAY) = ( - select year(SBIRTHDAY) - from STUDENT - where SNO = '108' -); - --- 23、查询“张旭“教师任课的学生成绩。 -select * -from SCORE -where cno = ( - select CNO - from COURSE - inner join TEACHER on COURSE.TNO = TEACHER.TNO and TNAME = '张旭' -); - --- 24、查询选修某课程的同学人数多于5人的教师姓名。 -select TNAME -from TEACHER -where TNO = ( - select TNO - from COURSE - where CNO = (select CNO - from SCORE - group by CNO - having count(SNO) > 5) -); - --- 25、查询95033班和95031班全体学生的记录。 -select * -from STUDENT -where CLASS in ('95033', '95031'); - --- 26、查询存在有85分以上成绩的课程Cno. -select cno -from SCORE -group by CNO -having MAX(DEGREE) > 85; - --- 27、查询出“计算机系“教师所教课程的成绩表。 -select * -from SCORE -where CNO in (select CNO - from TEACHER, COURSE - where DEPART = '计算机系' and COURSE.TNO = TEACHER.TNO); - --- 28、查询“计算机系”与“电子工程系“不同职称的教师的Tname和Prof -select - tname, - prof -from TEACHER -where depart = '计算机系' and prof not in ( - select prof - from TEACHER - where depart = '电子工程系' -); - --- 29、查询选修编号为“3-105“课程且成绩至少高于选修编号为“3-245”的同学的Cno、Sno和Degree,并按Degree从高到低次序排序。 -select - CNO, - SNO, - DEGREE -from SCORE -where CNO = '3-105' and DEGREE > any ( - select DEGREE - from SCORE - where CNO = '3-245' -) -order by DEGREE desc; - --- 30、查询选修编号为“3-105”且成绩高于选修编号为“3-245”课程的同学的Cno、Sno和Degree. -SELECT * -FROM SCORE -WHERE DEGREE > ALL ( - SELECT DEGREE - FROM SCORE - WHERE CNO = '3-245' -) -ORDER by DEGREE desc; - --- 31、查询所有教师和同学的name、sex和birthday. -select - TNAME name, - TSEX sex, - TBIRTHDAY birthday -from TEACHER -union -select - sname name, - SSEX sex, - SBIRTHDAY birthday -from STUDENT; - --- 32、查询所有“女”教师和“女”同学的name、sex和birthday. -select - TNAME name, - TSEX sex, - TBIRTHDAY birthday -from TEACHER -where TSEX = '女' -union -select - sname name, - SSEX sex, - SBIRTHDAY birthday -from STUDENT -where SSEX = '女'; - --- 33、查询成绩比该课程平均成绩低的同学的成绩表。 -SELECT A.* -FROM SCORE A -WHERE DEGREE < (SELECT AVG(DEGREE) - FROM SCORE B - WHERE A.CNO = B.CNO); - --- 34、查询所有任课教师的Tname和Depart. -select - TNAME, - DEPART -from TEACHER a -where exists(select * - from COURSE b - where a.TNO = b.TNO); - --- 35、查询所有未讲课的教师的Tname和Depart. -select - TNAME, - DEPART -from TEACHER a -where tno not in (select tno - from COURSE); - --- 36、查询至少有2名男生的班号。 -select CLASS -from STUDENT -where SSEX = '男' -group by CLASS -having count(SSEX) > 1; - --- 37、查询Student表中不姓“王”的同学记录。 -select * -from STUDENT -where SNAME not like "王%"; - --- 38、查询Student表中每个学生的姓名和年龄。 -select - SNAME, - year(now()) - year(SBIRTHDAY) -from STUDENT; - --- 39、查询Student表中最大和最小的Sbirthday日期值。 -select min(SBIRTHDAY) birthday -from STUDENT -union -select max(SBIRTHDAY) birthday -from STUDENT; - --- 40、以班号和年龄从大到小的顺序查询Student表中的全部记录。 -select * -from STUDENT -order by CLASS desc, year(now()) - year(SBIRTHDAY) desc; - --- 41、查询“男”教师及其所上的课程。 -select * -from TEACHER, COURSE -where TSEX = '男' and COURSE.TNO = TEACHER.TNO; - --- 42、查询最高分同学的Sno、Cno和Degree列。 -select - sno, - CNO, - DEGREE -from SCORE -where DEGREE = (select max(DEGREE) - from SCORE); - --- 43、查询和“李军”同性别的所有同学的Sname. -select sname -from STUDENT -where SSEX = (select SSEX - from STUDENT - where SNAME = '李军'); - --- 44、查询和“李军”同性别并同班的同学Sname. -select sname -from STUDENT -where (SSEX, CLASS) = (select - SSEX, - CLASS - from STUDENT - where SNAME = '李军'); - --- 45、查询所有选修“计算机导论”课程的“男”同学的成绩表 -select * -from SCORE, STUDENT -where SCORE.SNO = STUDENT.SNO and SSEX = '男' and CNO = ( - select CNO - from COURSE - where CNAME = '计算机导论'); - - - --- 46、使用游标方式来同时查询每位同学的名字,他所选课程及成绩。 - -declare - cursor student_cursor is - select S.SNO,S.SNAME,C.CNAME,SC.DEGREE as DEGREE - from STUDENT S, COURSE C, SCORE SC - where S.SNO=SC.SNO - and SC.CNO=C.CNO; - - student_row student_cursor%ROWTYPE; - -begin - open student_cursor; - loop - fetch student_cursor INTO student_row; - exit when student_cursor%NOTFOUND; - dbms_output.put_line( student_row.SNO || '' || - -student_row.SNAME|| '' || student_row.CNAME || '' || - -student_row.DEGREE); - end loop; - close student_cursor; -END; -/ - - --- 47、 声明触发器指令,每当有同学转换班级时执行触发器显示当前和之前所在班级。 - -CREATE OR REPLACE TRIGGER display_class_changes -AFTER DELETE OR INSERT OR UPDATE ON student -FOR EACH ROW -WHEN (NEW.sno > 0) - -BEGIN - - dbms_output.put_line('Old class: ' || :OLD.class); - dbms_output.put_line('New class: ' || :NEW.class); -END; -/ - - -Update student -set class=95031 -where sno=109; - - --- 48、 删除已设置的触发器指令 - -DROP TRIGGER display_class_changes; - diff --git "a/other/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.md" "b/other/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.md" deleted file mode 100644 index d1e4ddf360..0000000000 --- "a/other/\347\256\227\346\263\225\344\270\216\346\225\260\346\215\256\347\273\223\346\236\204.md" +++ /dev/null @@ -1,151 +0,0 @@ -# Algorithm -leetcode/lintcode上的算法题 - -**关于问题的答案和解体的思路,可以移步 : https://github.com/zhaozhengcoder/Algorithm** - -### About - - 这个仓库最初的想法是把lintcode/lintocde上面的算法题目整理一下,因为很多题目太多了显得太乱了,就不继续在GitHub上面写了,以前写的一部分移到我的博客上面了。 - GitHub上面打算整理一些比较典型 或者是 自己思考过的觉得很好的问题。 - - - 在博客上面开了两个专栏 - - 1. 数据结构/算法导论 : - https://www.jianshu.com/nb/12397278 - - 2. OJ练习题 : - https://www.jianshu.com/nb/9973135 - - 推荐两篇自己对 递归搜索和动态规划 的理解的blog : - - 1. https://www.jianshu.com/p/5eb4da919efe - - 2. https://www.jianshu.com/p/6b3a2304f63f - - - -### 题目的索引 - GITHUB上面打算整理一些比较典型 或者是 自己思考过的觉得很好的问题。 - - 1.从数据结构的角度索引 : - - a. 数组 - - 两数之和 - - 连续最大子数组 - - 乘积最大子数组 - - 买卖股票的最佳时机1,2,3 - - 买卖股票的最佳时机1:寻找数组里面的最大上升子序列 - 买卖股票的最佳时机2:寻找数组里面所有的上升子序列 - 买卖股票的最佳时机3:寻找数组里面两个不重合的上升子序列,并且使他们的和最大 to-do - - 区间合并(将有交集的区间合并) - - 寻找缺失的数 - - 1. 一个顺序的数组[1,2,3,5,6],缺少了一个数字,如何找到它? - - 2. 一个arr的数组,只有一个数字出现了一次,其他都出现了两次,如何找到它? - - 数组的近似划分(将一个数组分成两个,但是差最小) - - 数组里面第k大的数 - - 跳跃游戏1,2 - - 跳跃游戏1: - 给出一个非负整数数组,你最初定位在数组的第一个位置, - 数组中的每个元素代表你在那个位置可以跳跃的最大长度, - 返回 是否能到达数组的最后一个位置 - - 跳跃游戏2: - 给出一个非负整数数组,你最初定位在数组的第一个位置, - 数组中的每个元素代表你在那个位置可以跳跃的最大长度,    - 返回 使用最少的跳跃次数到达数组的最后一个位置 - - a+. 二维矩阵 - - 顺时针打印二维矩阵 - - 给出一个二维矩阵,找到一个路径(从某个左上角到某个角右下)使这条路径的值最大 - - b. 链表 - - c. 字符串 - - 最长公共子序列(并不是连续的) - - 最长回文子串 - - d. 二叉树 - - 返回一个平衡二叉树的第k大的节点 - - 二叉树的最低公共祖先 - - 非递归遍历二叉树 - - e. 图 - - 最短路径 - - 深度/广度优先遍历 - - 2. 从算法的角度建立索引 : - - a. 递归搜索问题 - - N后问题 - - 全排列 - - 组合问题1,2 - - b. 动态规划 - - 背包问题1,2 - - 数组的近似划分(将一个数组分成两个,但是差最小) - - 跳跃游戏1,2 - - 给出一个二维矩阵,找到一个路径(从某个左上角到某个角右下)使这条路径的值最大 - - - 3. 常用 - - a. 排列/组合 - - b. 深度优先遍历 - - c. 最短路径 - - 4. 智力题(算法本身很简单,就是想不到的那种) - - 最多有多少个点在同一条直线上 - - -### Others - - 1. 类似于系统设计的题目 - - 带最小值的栈/队列 - - url长链接转短链接 - - 2. 解决特定问题 - - 并查集 - - 布隆过滤器 - - - -如果你对机器学习的算法感兴趣,欢迎共同讨论: - -https://github.com/zhaozhengcoder/Machine-Learning diff --git "a/\345\246\202\344\275\225\344\275\277\347\224\250.md" "b/\345\246\202\344\275\225\344\275\277\347\224\250.md" deleted file mode 100644 index 4b6e34b441..0000000000 --- "a/\345\246\202\344\275\225\344\275\277\347\224\250.md" +++ /dev/null @@ -1,5 +0,0 @@ -| 阅读方式 | 步骤 | 优点 | 缺点 | -| :--: | :--: | :--: | :--: | -| Github | 点击 README 上的文档链接就可以进入相应的文档 | 及时更新、方便快捷 | GFM 对 Markdown 的渲染效果不是很好,不能自定义样式,不支持侧边栏目录。 | -| Github Pages
(推荐) | https://cyc2018.github.io/CS-Notes | 几乎克服了 Github 方式的所有缺点,支持可定制样式、侧边栏目录、搜索、评论 | 暂无 | -| 导出成 PDF、EPUB 等格式进行阅读 | 可以直接在 [CS-Notes-PDF](https://github.com/sjsdfg/CS-Notes-PDF) 下载,或者使用 Typora 编辑器的导出功能。 | 方便在移动设备上阅读、有专门的阅读器来提高阅读体验 | 不能及时更新,不能显示 GIF 图片 |