diff --git a/README.md b/README.md
index ed285aa4..7a73df4f 100644
--- a/README.md
+++ b/README.md
@@ -1,318 +1,15 @@
-项目对应 WebSIte:https://duhouan.github.io/Java/#/
+本仓库是对 Java 的一些基础知识、数据库知识、以及框架知识进行收集、整理(持续更新中)。
+仓库对应 WebSite:https://duhouan.github.io/Java/#
+如何流畅访问 Github
-# ✏️ 计算机基础
+

-## 1. 计算机网络
+
-- [第一节 概述](docs/Net/1_概述.md)
-- [第二节 物理层](docs/Net/2_物理层.md)
-- [第三节 数据链路层](docs/Net/3_数据链路层.md)
-- [第四节 网络层](docs/Net/4_网络层.md)
-- [第五节 运输层](docs/Net/5_运输层.md)
-- [第六节 应用层](docs/Net/6_应用层.md)
-
-## 2. 操作系统
-
-- [第一节 操作系统概述](docs/OS/1_操作系统概述.md)
-- [第二节 进程管理](docs/OS/2_进程管理.md)
-- [第三节 死锁](docs/OS/3_死锁.md)
-- [第四节 内存管理](docs/OS/4_内存管理.md)
-- [第五节 设备管理](docs/OS/4_设备管理.md)
-- [第六节 链接](docs/OS/6_链接.md)
-
-## 3. 数据结构和算法
-
-- [第一节 概述](docs/data_structure/1_概述.md)
-- [第二节 线性表](docs/data_structure/2_线性表.md)
-- [第三节 栈和队列](docs/data_structure/3_栈和队列.md)
-- [第四节 树](docs/data_structure/4_树.md)
-- [第五节 图](docs/data_structure/5_图.md)
-- [第六节 集合和映射](docs/data_structure/6_集合和映射.md)
-- [第七节 并查集](docs/data_structure/7_并查集.md)
-- [第八节 优先队列和堆](docs/data_structure/8_优先队列和堆.md)
-- [第九节 哈希表](docs/data_structure/9_哈希表.md)
-- [第十节 排序](docs/data_structure/10_排序.md)
-- [第十一节 线段树](docs/data_structure/11_线段树.md)
-- [第十二节 Trie树](docs/data_structure/12_Trie树.md)
-- [第十三节 AVL](docs/data_structure/13_AVL.md)
-- [第十四节 红黑树](docs/data_structure/14_红黑树.md)
-
-## 4. HTTP
-
-- [第一节 HTTP概述](http/1_HTTP概述.md)
-- [第二节 HTTP状态码](http/2_HTTP状态码.md)
-- [第三节 具体应用](http/3_具体应用.md)
-- [第四节 HTTPS](http/4_HTTPS.md)
-- [第五节 get和post比较](http/5_get和post比较.md)
-
-## 5. Linux
-
-- [第一节 Linux概论](docs/Linux/1_Linux概论.md)
-- [第二节 Linux文件系统](docs/Linux/2_Linux文件系统.md)
-- [第三节 Linux常用命令](docs/Linux/3_Linux常用命令.md)
-- [第四节 Liunx进程管理](docs/Linux/4_Liunx进程管理.md)
-- [第五节 Linux压缩与打包](docs/Linux/5_Linux压缩与打包.md)
-
-# ☕️ Java
-
-## 1. Java 基础
-
-- [第一节 数据类型](docs/JavaBasics/1_数据类型.md)
-- [第二节 String](docs/JavaBasics/2_String.md)
-- [第三节 运算](docs/JavaBasics/3_运算.md)
-- [第四节 Object通用方法](docs/JavaBasics/4_Object通用方法.md)
-- [第五节 关键字](docs/JavaBasics/5_关键字.md)
-- [第六节 反射](docs/JavaBasics/6_反射.md)
-- [第七节 异常](docs/JavaBasics/7_异常.md)
-- [第八节 泛型](docs/JavaBasics/8_泛型.md)
-- [第九节 注解](docs/JavaBasics/9_注解.md)
-- [第十节 Java常见对象](docs/JavaBasics/10_Java常见对象.md)
-- [第十一节 抽象类和接口](docs/JavaBasics/11_抽象类和接口.md)
-- [第十二节 其他](docs/JavaBasics/12_其他.md)
-
-## 2. Java 容器
-
-- [第一节 Java容器概览](docs/JavaContainer/1_Java容器概览.md)
-- [第二节 容器中的设计模式](docs/JavaContainer/2_容器中的设计模式.md)
-- [第三节 容器源码分析 - List](docs/JavaContainer/3_容器源码分析%20-%20List.md)
-- [第四节 容器源码分析 - Map](docs/JavaContainer/4_容器源码分析%20-%20Map.md)
-- [第五节 容器源码分析 - 并发容器](docs/JavaContainer/5_容器源码分析%20-%20并发容器.md)
-
-## 3. Java 虚拟机
-
-- [第一节 运行时数据区域](docs/JVM/1_JVM.md)
-- [第二节 HotSpot 虚拟机对象](docs/JVM/2_JVM.md)
-- [第三节 String 类和常量池](docs/JVM/3_JVM.md)
-- [第四节 8 种基本类型的包装类和常量池](docs/JVM/4_JVM.md)
-- [第五节 垃圾收集](docs/JVM/5_JVM.md)
-- [第六节 内存分配与回收策略](docs/JVM/6_JVM.md)
-- [第七节 类加载机制](docs/JVM/7_JVM.md)
-
-## 4. Java 并发
-
-- [第一节 基础知识](docs/Java_Concurrency/1_基础知识.md)
-- [第二节 并发理论](docs/Java_Concurrency/2_并发理论.md)
-- [第三节 并发关键字](docs/Java_Concurrency/3_并发关键字.md)
-- [第四节 Lock 体系](docs/Java_Concurrency/4_Lock%20体系.md)
-- [第五节 原子操作类](docs/Java_Concurrency/5_原子操作类.md)
-- [第六节 并发容器](docs/Java_Concurrency/6_并发容器.md)
-- [第七节 并发工具](docs/Java_Concurrency/7_并发工具.md)
-- [第八节 线程池](docs/Java_Concurrency/8_线程池.md)
-- [第九节 并发实践](docs/Java_Concurrency/9_并发实践.md)
-
-## 5. JavaIO
-
-- [第一节 概览](docs/JavaIO/1_概览.md)
-- [第二节 磁盘操作](docs/JavaIO/2_磁盘操作.md)
-- [第三节 字节操作](docs/JavaIO/3_字节操作.md)
-- [第四节 字符操作](docs/JavaIO/4_字符操作.md)
-- [第五节 对象操作](docs/JavaIO/5_对象操作.md)
-- [第六节 网络操作](docs/JavaIO/6_网络操作.md)
-- [第七节 NIO](docs/JavaIO/7_NIO.md)
-- [第八节 JavaIO方式](docs/JavaIO/8_JavaIO方式.md)
-
-## 6. 正则表达式
-
-- [第一节 概述](docs/Regex/1_概述.md)
-- [第二节 应用](docs/Regex/2_应用.md)
-
-# 👫 面向对象
-
-## 1. 设计模式
-
-- [第一节 概述](docs/OO/1_概述.md)
-- [第二节 创建型](docs/OO/2_创建型.md)
-- [第三节 行为型](docs/OO/3_行为型.md)
-- [第四节 结构型](docs/OO/4_结构型.md)
-
-## 2. 面向对象思想
-
-- [第一节 面向对象三大特性](docs/OO/5_面向对象三大特性.md)
-- [第二节 关系类图](docs/OO/6_关系类图.md)
-- [第三节 面向对象设计原则](docs/OO/7_面向对象设计原则.md)
-
-# 📝 编程题
-
-## 1. 剑指 Offer 编程题
-
-> **数据结构相关**
-
-- [第一节 数组和矩阵](docs/AimForOffer/数据结构相关/1_数组和矩阵.md)
-- [第二节 字符串](docs/AimForOffer/数据结构相关/2_字符串.md)
-- [第三节 链表](docs/AimForOffer/数据结构相关/3_链表.md)
-- [第四节 树](docs/AimForOffer/数据结构相关/4_树.md)
-- [第五节 栈](docs/AimForOffer/数据结构相关/5_栈.md)
-- [第六节 队列](docs/AimForOffer/数据结构相关/6_队列.md)
-- [第七节 堆](docs/AimForOffer/数据结构相关/7_堆.md)
-- [第八节 哈希.](docs/AimForOffer/数据结构相关/8_哈希.md)
-
-> **算法相关**
-
-- [第一节 查找](docs/AimForOffer/算法思想相关/1_查找.md)
-- [第二节 排序](docs/AimForOffer/算法思想相关/2_排序.md)
-- [第三节 动态规划](docs/AimForOffer/算法思想相关/3_动态规划.md)
-- [第四节 回溯](docs/AimForOffer/算法思想相关/4_回溯.md)
-- [第五节 深度优先](docs/AimForOffer/算法思想相关/5_深度优先.md)
-- [第六节 贪心](docs/AimForOffer/算法思想相关/6_贪心.md)
-- [第七节 数学运算](docs/AimForOffer/算法思想相关/7_数学运算.md)
-- [第八节 其他](docs/AimForOffer/算法思想相关/8_其他.md)
-
-## 2. LeetCode 编程题
-
-> **数据结构相关**
-
-- [第一节 数组问题](docs/LeetCode/数据结构相关/1_数组问题.md)
-- [第二节 链表问题](docs/LeetCode/数据结构相关/2_链表问题.md)
-- [第三节 栈和队列](docs/LeetCode/数据结构相关/3_栈和队列.md)
-- [第四节 二叉树](docs/LeetCode/数据结构相关/4_二叉树.md)
-- [第五节 字符串](docs/LeetCode/数据结构相关/5_字符串.md)
-- [第六节 哈希](docs/LeetCode/数据结构相关/6_哈希.md)
-- [第七节 图](docs/LeetCode/数据结构相关/7_图.md)
-- [第八节 数据结构设计](docs/LeetCode/数据结构相关/8_数据结构设计.md)
-
-> **算法思想相关**
-
-- [第一节 排序](docs/LeetCode/算法思想相关/1_排序.md)
-- [第二节 分治思想](docs/LeetCode/算法思想相关/2_分治思想.md)
-- [第三节 贪心思想](docs/LeetCode/算法思想相关/3_贪心思想.md)
-- [第四节 LRU](docs/LeetCode/算法思想相关/4_LRU.md)
-- [第五节 DFS](docs/LeetCode/算法思想相关/5_DFS.md)
-- [第六节 回溯法](docs/LeetCode/算法思想相关/6_回溯法.md)
-- [第七节 动态规划](docs/LeetCode/算法思想相关/7_动态规划.md)
-- [第八节 数学问题](docs/LeetCode/算法思想相关/8_数学问题.md)
-
-# 💾 数据库
-
-## 1. DataBase
-
-- [第一节 数据库系统原理](docs/DataBase/1_数据库系统原理.md)
-- [第二节 关系数据库设计理论](docs/DataBase/2_关系数据库设计理论.md)
-- [第三节 设计关系型数据库](docs/DataBase/3_设计关系型数据库.md)
-- [第四节 SQL](docs/DataBase/4_SQL.md)
-- [第五节 LeetCode_Database题解](docs/DataBase/5_LeetCode_Database题解.md)
-
-## 2. MySQL
-
-- [第一节 锁机制](docs/MySQL/1_锁机制.md)
-- [第二节 事务隔离级别实现](docs/MySQL/2_事务隔离级别实现.md)
-- [第三节 索引](docs/MySQL/3_索引.md)
-- [第四节 MySQL架构](docs/MySQL/4_MySQL架构.md)
-- [第五节 MySQL优化](docs/MySQL/5_MySQL优化.md)
-- [第六节 补充知识](docs/MySQL/6_补充知识.md)
-
-## *3. Redis
-
-- [第一节 Redis初探](docs/Redis/1_Redis初探.md)
-- [第二节 Redis持久化](docs/Redis/2_Redis持久化.md)
-- [第三节 Redis复制](docs/Redis/3_Redis复制.md)
-- [第四节 处理系统故障](docs/Redis/4_处理系统故障.md)
-- [第五节 Redis事务](docs/Redis/5_Redis事务.md)
-- [第六节 Redis性能方面注意事项](docs/Redis/6_Redis性能方面注意事项.md)
-- [第七节 降低内存占用](docs/Redis/7_降低内存占用.md)
-- [第八节 简单点赞系统](docs/Redis/8_简单点赞系统)
-
-## 4. 海量数据处理
-
-- [第一节 概述](docs/MassDataProcessing/1_概述.md)
-- [第二节 哈希分治](docs/MassDataProcessing/2_哈希分治.md)
-- [第三节 位图](docs/MassDataProcessing/3_位图.md)
-- [第四节 布隆过滤器](docs/MassDataProcessing/4_布隆过滤器.md)
-- [第五节 Trie树](docs/MassDataProcessing/5_Trie树.md)
-- [第六节 数据库](docs/MassDataProcessing/6_数据库.md)
-- [第七节 倒排索引](docs/MassDataProcessing/7_倒排索引.md)
-
-# 🔨 消息中间件
-
-## 1. Kafka
-
-- [第一节 消息队列](docs/Kafka/1_消息队列.md)
-- [第二节 Kafka的架构](docs/Kafka/2_Kafka的架构.md)
-- [第三节 Kafka的高可用原理](docs/Kafka/3_Kafka的高可用原理.md)
-- [第四节 Kafka中一些常见问题](docs/Kafka/4_Kafka中一些常见问题.md)
-- [第五节 Kafka特点](docs/Kafka/5_Kafka特点.md)
-
-## 2. RabbitMQ
-
-- [第一节 主流消息中间件](docs/RabbitMQ/1_主流消息中间件.md)
-- [第二节 RabbitMQ概述](docs/RabbitMQ/2_RabbitMQ概述.md)
-- [第三节 RabbitMQ入门](docs/RabbitMQ/3_RabbitMQ入门.md)
-- [第四节 RabbitMQ高级特性](docs/RabbitMQ/4_RabbitMQ高级特性.md)
-- [第五节 RabbitMQ整合SpringAMQP](docs/RabbitMQ/5_RabbitMQ整合SpringAMQP.md)
-- [第六节 RabbitMQ整合SpringBoot](docs/RabbitMQ/6_RabbitMQ整合SpringBoot.md)
-- [RabbitMQ 官网](https://www.rabbitmq.com/)
-
-# 📖 系统设计
-
-## 1. 常用框架
-
-- [第一节 SpringMVC](docs/SSM/1_SpringMVC.md)
-- [第二节 SpringIOC](docs/SSM/2_SpringIOC.md)
-- [第三节 SpringAOP](docs/SSM/3_SpringAOP.md)
-- [第四节 Spring事务管理](docs/SSM/4_Spring事务管理.md)
-- [第五节 Spring中Bean的作用域](docs/SSM/5_Spring中Bean的作用域.md)
-- [第六节 Spring中Bean的生命周期](docs/SSM/6_Spring中Bean的生命周期.md)
-- [第七节 Spring中常见注解](docs/SSM/7_Spring中常见注解.md)
-- [第八节 Spring中涉及到的设计模式](docs/SSM/8_Spring中涉及到的设计模式.md)
-- [第九节 MyBaits](docs/SSM/9_MyBaits.md)
-
-## 2. Web 安全
-
-- [第一节 常见安全问题](docs/Safety/1_常见安全问题.md)
-- [第二节 跨站脚本攻击](docs/Safety/2_跨站脚本攻击.md)
-- [第三节 跨站请求伪造](docs/Safety/3_跨站请求伪造.md)
-- [第四节 Cookies问题](docs/Safety/4_Cookies问题.md)
-- [第五节 点击劫持问题](docs/Safety/5_点击劫持问题.md)
-- [第六节 传输安全](docs/Safety/6_传输安全.md)
-- [第七节 密码安全](docs/Safety/7_密码安全.md)
-- [第八节 接入层注入问题](docs/Safety/8_接入层注入问题.md)
-- [第九节 接入层上传问题](docs/Safety/9_接入层上传问题.md)
-- [第十节 信息泄露](docs/Safety/10_信息泄露.md)
-- [第十一节 DoS攻击](docs/Safety/11_DoS攻击.md)
-- [第十二节 重放攻击](docs/Safety/12_重放攻击.md)
-
-## 3. 分布式
-
-- [第一节 分布式系统设计理念](docs/distribution/1_分布式系统设计理念.md)
-- [第二节 CAP理论](docs/distribution/2_CAP理论.md)
-- [第三节 BASE理论](docs/distribution/3_BASE理论.md)
-- [第四节 分布式锁](docs/distribution/4_分布式锁.md)
-- [第五节 分布式事务](docs/distribution/5_分布式事务.md)
-- [第六节 分布式缓存的一致性哈希算法](docs/distribution/6_分布式缓存的一致性哈希算法.md)
-
-## *4. 微服务
-
-## 5. 网站架构
-
-- [网站架构](docs/web_architecture/1_网站架构.md)
-- [设计秒杀系统](docs/web_architecture/2_设计秒杀系统.md)
-
-# 💻 工具
-
-## 1. Git
-
-- [git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
-- [git - 图解](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
-
-## 2. Maven
-
-- [Maven](docs/Maven/1_Maven.md)
-- [Maven 下载](http://maven.apache.org/download.cgi)
-- [Maven 官网](https://maven.apache.org/)
-
-## *2. Nginx
-
-## *3. Docker
-
-
-
-# 🔧 进阶指南
-
-- [后端面试进阶指南](https://xiaozhuanlan.com/CyC2018)
-- [Java 面试进阶指南](https://xiaozhuanlan.com/javainterview)
-- [编码规范指南](https://github.com/alibaba/p3c)
-
-# 🙊 参考资料
-
-- [参考仓库](docs/reference/参考仓库.md)
-- [参考书籍](docs/reference/参考书籍.md)
-- [慕课网](docs/reference/慕课网.md)
\ No newline at end of file
+| ☕️ Java | 👫 面向对象 | 📝 编程题 | 💾 数据库 | 🎓 系统设计 | ☎️ 常用框架 | 📖 工具 | 📚 参考资料 |
+| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
+|
|
|
|
|
|
|
|
|
diff --git "a/docs/1_\345\237\272\347\241\200.md" "b/docs/1_\345\237\272\347\241\200.md"
new file mode 100644
index 00000000..406b2d57
--- /dev/null
+++ "b/docs/1_\345\237\272\347\241\200.md"
@@ -0,0 +1,99 @@
+# 系统设计基础
+
+## 安全性
+
+要求系统在应对各种攻击手段时能够有可靠的应对措施。
+
+## 性能
+
+### 性能指标
+
+#### 响应时间
+
+指某个请求从发出到接收到响应消耗的时间。
+
+在对响应时间进行测试时,通常采用重复请求的方式,然后计算平均响应时间。
+
+#### 吞吐量
+
+指系统在单位时间内可以处理的请求数量,通常使用每秒的请求数来衡量。
+
+#### 并发量
+
+指系统能同时处理的并发用户请求数量。
+
+在没有并发存在的系统中,请求被顺序执行,此时响应时间为吞吐量的倒数。例如系统支持的吞吐量为 100 req/s,那么平均响应时间应该为 0.01s。
+
+目前的大型系统都支持多线程来处理并发请求,多线程能够提高吞吐量以及缩短响应时间,主要有两个原因:
+
+- 多 CPU
+- IO 等待时间
+
+使用 IO 多路复用等方式,系统在等待一个 IO 操作完成的这段时间内不需要被阻塞,可以去处理其它请求。通过将这个等待时间利用起来,使得 CPU 利用率大大提高。
+
+并发用户数不是越高越好,因为如果并发用户数太高,系统来不及处理这么多的请求,会使得过多的请求需要等待,那么响应时间就会大大提高。
+
+### 性能优化
+
+#### 集群
+
+将多台服务器组成集群,使用负载均衡将请求转发到集群中,避免单一服务器的负载压力过大导致性能降低。
+
+#### 缓存
+
+缓存能够提高性能的原因如下:
+
+- 缓存数据通常位于内存等介质中,这种介质对于读操作特别快;
+- 缓存数据可以位于靠近用户的地理位置上;
+- 可以将计算结果进行缓存,从而避免重复计算。
+
+#### 异步
+
+某些流程可以将操作转换为消息,将消息发送到**消息队列**之后立即返回,之后这个操作会被异步处理。
+
+
+
+## 伸缩性
+
+指不断向集群中添加服务器来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。
+
+### 伸缩性与性能
+
+如果系统存在性能问题,那么单个用户的请求总是很慢的;
+
+如果系统存在伸缩性问题,那么单个用户的请求可能会很快,但是在并发数很高的情况下系统会很慢。
+
+### 实现伸缩性
+
+应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。
+
+关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器的存储空间限制。
+
+对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。
+
+## 扩展性
+
+指的是添加新功能时对现有系统的其它应用无影响,这就要求不同应用具备低耦合的特点。
+
+实现可扩展主要有两种方式:
+
+- 使用消息队列进行解耦,应用之间通过消息传递进行通信;
+- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
+
+## 可用性
+
+### 冗余
+
+保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。
+
+应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上,不会对用户有任何影响。
+
+存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。
+
+### 监控
+
+对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个信息达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。
+
+### 服务降级
+
+服务降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。
\ No newline at end of file
diff --git "a/docs/Kafka/1_\346\266\210\346\201\257\351\230\237\345\210\227.md" "b/docs/1_\346\266\210\346\201\257\351\230\237\345\210\227.md"
similarity index 100%
rename from "docs/Kafka/1_\346\266\210\346\201\257\351\230\237\345\210\227.md"
rename to "docs/1_\346\266\210\346\201\257\351\230\237\345\210\227.md"
diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md"
index 890a88e1..6d56d3ec 100644
--- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md"
+++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/1_\346\225\260\347\273\204\345\222\214\347\237\251\351\230\265.md"
@@ -1,4 +1,4 @@
-# 数组
+# 数组和矩阵
## 1、二维数组中的查找
diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md"
index 97aef1ca..619b66af 100644
--- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md"
+++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/2_\345\255\227\347\254\246\344\270\262.md"
@@ -384,4 +384,111 @@ private void reverse(char[] chs,int start,int end){
}
```
+## *10、实现 strStr()
+
+[实现 strStr()](https://leetcode-cn.com/problems/implement-strstr/)
+
+思路一:朴素模式匹配算法
+
+最朴素的方法就是依次从待匹配串的每一个位置开始,逐一与模版串匹配,
+因为最多检查 (n - m)个位置,所以方法的复杂度为 O(m*(n-1))。
+
+
+
+```java
+//haystack 是待匹配串
+//needle 是模板串
+public int strStr(String haystack, String needle) {
+ if(needle==null || needle.length()==0){
+ return 0;
+ }
+ int i=0,j=0;
+ //k存储的是模板串在待匹配串的位置
+ int k=i;
+ while(i printListFromTailToHead(ListNode listNode) {
-## *2、链表中环的入口结点(9)
+## *2、链表中环的入口结点
[链表中环的入口结点](https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4?tpId=13&tqId=11208&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -128,7 +128,7 @@ public ListNode removeElements(ListNode head, int val) {
-## *4、删除链表中重复的结点(10)
+## *4、删除链表中重复的结点
[删除链表中重复的结点](https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef?tpId=13&tqId=11209&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -171,7 +171,7 @@ public ListNode deleteDuplication(ListNode pHead) {
-## 5、链表中倒数第 k 个结点(29)
+## 5、链表中倒数第 k 个结点
[链表中倒数第 k 个结点](https://www.nowcoder.com/practice/529d3ae5a407492994ad2a246518148a?tpId=13&tqId=11167&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -214,7 +214,7 @@ public ListNode FindKthToTail(ListNode head, int k) {
-## 7、反转链表(30)
+## 7、反转链表
[反转链表](https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca?tpId=13&tqId=11168&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -262,7 +262,7 @@ public ListNode ReverseList(ListNode head) {
-## 8、合并两个排序的链表(31)
+## 8、合并两个排序的链表
[合并两个排序的链表](https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337?tpId=13&tqId=11169&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -343,7 +343,7 @@ public ListNode Merge(ListNode list1, ListNode list2) {
-## 9、二叉搜索树与双向链表(41)
+## 9、二叉搜索树与双向链表
[二叉搜索树与双向链表](https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -386,7 +386,7 @@ private void inOrder(TreeNode node){
-## 10、两个链表的第一个公共结点(51)
+## 10、两个链表的第一个公共结点
[两个链表的第一个公共结点](https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46?tpId=13&tqId=11189&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md"
index 148b4fb6..a49dc51d 100644
--- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md"
+++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/4_\346\240\221.md"
@@ -424,7 +424,7 @@ private String buildPath(List values){
-## 13、二叉树中和为某一值的路径(39)
+## 13、二叉树中和为某一值的路径
[二叉树中和为某一值的路径](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
diff --git "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md" "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md"
index cbcadc12..1bfbef76 100644
--- "a/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md"
+++ "b/docs/AimForOffer/\346\225\260\346\215\256\347\273\223\346\236\204\347\233\270\345\205\263/5_\346\240\210.md"
@@ -1,6 +1,6 @@
# 栈
-## 1、用两个栈实现队列(19)
+## 1、用两个栈实现队列
[用两个栈实现队列](https://www.nowcoder.com/practice/54275ddae22f475981afa2244dd448c6?tpId=13&tqId=11158&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -24,7 +24,7 @@ public int pop() {
-## *2、包含 min 函数的栈(35)
+## *2、包含 min 函数的栈
[包含 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)
@@ -58,7 +58,7 @@ public int min() {
-## *3、栈的压入、弹出序列(36)
+## *3、栈的压入、弹出序列
[栈的压入、弹出序列](https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106?tpId=13&tqId=11174&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md"
index f5657b81..ed9329c7 100644
--- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md"
+++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/1_\346\237\245\346\211\276.md"
@@ -1,6 +1,8 @@
# 查找
-## 1、二维数组中的查找(1)
+
+
+## 1、二维数组中的查找
[二维数组中的查找](https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -30,7 +32,7 @@ public boolean Find(int target, int [][] array) {
-## 2、旋转数组的最小数字(21)
+## 2、旋转数组的最小数字
[旋转数组的最小数字](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -86,7 +88,7 @@ public int minNumberInRotateArray(int [] array) {
-## 3、数字在排序数组中出现的次数(52)
+## 3、数字在排序数组中出现的次数
[数字在排序数组中出现的次数](https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2?tpId=13&tqId=11190&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -192,3 +194,4 @@ private int binarySearchLast(int[] array,int k){
return res;
}
```
+
diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md"
index eb55b6d6..d60c1f20 100644
--- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md"
+++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/3_\345\212\250\346\200\201\350\247\204\345\210\222.md"
@@ -1,6 +1,6 @@
# 动态规划
-## 1、正则表达式匹配(5)
+## 1、正则表达式匹配
[正则表达式匹配](https://www.nowcoder.com/practice/45327ae22b7b413ea21df13ee7d6429c?tpId=13&tqId=11205&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -62,7 +62,7 @@ private boolean match(char[] str,int strIndex,char[] pattern,int patternIndex){
-## 2、斐波那契数列(22)
+## 2、斐波那契数列
[斐波那契数列](https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3?tpId=13&tqId=11160&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -97,7 +97,7 @@ public int Fibonacci(int n) {
-## 3、跳台阶(23)
+## 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)
@@ -119,7 +119,7 @@ public int JumpFloor(int target) {
-## 4、变态跳台阶(24)
+## 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)
@@ -164,7 +164,7 @@ public int JumpFloorII(int target) {
-## 5、矩形覆盖(25)
+## 5、矩形覆盖
[矩形覆盖](https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6?tpId=13&tqId=11163&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -191,7 +191,7 @@ public int RectCover(int target) {
-## 6、连续子数组的最大和(45)
+## 6、连续子数组的最大和
[连续子数组的最大和](https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484?tpId=13&tqId=11183&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -256,7 +256,7 @@ public int FindGreatestSumOfSubArray(int[] array) {
-## *7、丑数(46)
+## *7、丑数
[丑数](https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b?tpId=13&tqId=11186&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -448,14 +448,14 @@ public int numDecodings(String s) {
int n =s.length();
int[] dp = new int[n+1];
dp[0]=1; // 没有字符则只有一种解码方式
- dp[1]=s.charAt(0)=='0'?0:1; //字符 '0',不能进行解码
+ dp[1]=s.charAt(0)=='0'?0:1; // 字符 '0',不能进行解码
for(int i=2;i<=n;i++){
//解码一位字符
int one = Integer.parseInt(s.substring(i-1,i));
if(one!=0){
dp[i] += dp[i-1];
}
- if(s.charAt(i-2)=='0'){ ////字符 '0',不能进行解码
+ if(s.charAt(i-2)=='0'){ //字符 '0',不能进行解码
continue;
}
int two=Integer.parseInt(s.substring(i-2,i));
@@ -522,48 +522,51 @@ public int LCS(String s,String t){
```java
private char[][] x;
+private StringBuilder res;
//x[i][j]=='c' 表示斜方向
//x[i][j]=='u' 表示向上
//x[i][j]=='l' 表示向左
-private StringBuilder res;
-public int LCS(String s,String t){
+public String LCS(String s,String t){
+ if(s==null || s==null){
+ return "";
+ }
int m=s.length();
int n=t.length();
- if(m==0 || n==0){
- return 0;
- }
-
- //lcs[i][j] 表示 s[0...i] 和 t[0...j] 之间的最长公共子序列长度
- int[][] lcs=new int[m+1][n+1];
x=new char[m+1][n+1];
res=new StringBuilder();
+ // lcs[i][j] 表示 s[0,i-1]、t[0,j-1] 最长公共子序列长度
+ // s[i-1]==t[j-1] 则 lcs[i][j]=lcs[i-1][j-1]+1;
+ // s[i-1]!=t[j-1] 则 lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1])
+ int[][] lcs=new int[m+1][n+1];
for(int i=0;i<=m;i++){
for(int j=0;j<=n;j++){
lcs[i][j]=0;
}
}
-
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(s.charAt(i-1)==t.charAt(j-1)){
- lcs[i][j]=1+lcs[i-1][j-1];
+ lcs[i][j]=lcs[i-1][j-1]+1;
x[i][j]='c';
- }else if(lcs[i-1][j]>=lcs[i][j-1]){
- lcs[i][j]=lcs[i-1][j];
- x[i][j]='u';
- }else if(lcs[i-1][j]lcs[i][j-1]){
+ lcs[i][j]=lcs[i-1][j];
+ x[i][j]='u';
+ }
}
}
}
- return lcs[m][n];
+ print(s,m,n);
+ return res.toString();
}
-public void print(String s,int m,int n){
- if(m==0 || n==0){
+private void print(String s,int m,int n){
+ if(s==null || s.length()==0){
return;
}
if(x[m][n]=='c'){
@@ -728,6 +731,26 @@ private int binarySearch(int[] tails,int l,int r,int key){
[完全平方数](https://leetcode-cn.com/problems/perfect-squares/)
```java
-
+// 思路:dp[i] 表示整数 i 的完全平方数的最少数量
+// 显然这些平方数开方后数值的大小范围在 [1,sqrc(n)] 之间
+// 假设 j 在 [1,sqrc(n)] 中取值,我们知道 i 和(i-j^2)之间就差了一个j^2完全平方数
+// 则有 dp[i]=1 + min{dp[i-j^2]}
+public int numSquares(int n) {
+ if(n<=2){
+ return n;
+ }
+ int N=(int)Math.sqrt(n*1.0);
+ int[] dp=new int[n+1];
+ dp[0]=0; // 0不是正整数,显然不能使用完全平方数去表示
+ for(int i=1;i<=n;i++){
+ int min=dp[i-1];
+ for(int j=1;i-j*j>=0 && j<=N;j++){
+ if(dp[i-j*j]> FindPath(TreeNode root, int target) {
- ArrayList> res = new ArrayList<>();
- ArrayList values = new ArrayList<>();
- backtrack(root,target,values,res);
- return res;
-}
-
-// values : 记录从根节点到叶子节点的所有路径
-// paths : 存储所有可能的结果
-private void backtrack(TreeNode root, int target,
- ArrayList values,
- ArrayList> paths) {
- if (root == null) {
- return;
- }
- values.add(root.val);
- if(root.left==null && root.right==null && root.val==target){
- paths.add(new ArrayList<>(values));
- }else{
- backtrack(root.left,target-root.val,values,paths);
- backtrack(root.right,target-root.val,values,paths);
- }
- values.remove(values.size()-1);
-}
-```
-
-
-
-## 2、矩阵中的路径(65)
-
-[矩阵中的路径](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
-
-```java
-//思路:典型的回溯法思想
-private int m,n; //矩阵的长度、宽度
-private boolean[][] visted; //标记是否访问
-
-private int[][] d={
- {0,1}, //向右
- {-1,0},//向上
- {0,-1},//向左
- {1,0} //向下
-};
-
-private boolean inArea(int x,int y){
- return (x>=0 && x=0 && y=0 && x=0 && y
-
-```java
-private List> res;
-
-private boolean[] visited;
-
-public List> permute(int[] nums) {
- res = new ArrayList<>();
- if(nums==null || nums.length==0){
- return res;
- }
- visited = new boolean[nums.length];
- List p =new ArrayList<>();
- generatePermutation(nums,0,p);
- return res;
-}
-
-//p中保存一个有 index 的元素的排列
-//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列
-private void generatePermutation(int[] nums,int index,List p){
- if(index==nums.length){
- res.add(new ArrayList<>(p));
- return;
- }
- for(int i=0;i> res;
-
-private boolean[] visited;
-
-public List> permuteUnique(int[] nums) {
- res = new ArrayList<>();
- if(nums==null || nums.length==0){
- return res;
- }
- visited = new boolean[nums.length];
- Arrays.sort(nums);
- List p = new ArrayList<>();
- findUniquePermutation(nums,0,p);
- return res;
-}
-
-//p 保存的是有 index 元素的排列
-//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列
-private void findUniquePermutation(int[] nums,int index,List p){
- if(index==nums.length){
- res.add(new ArrayList<>(p));
- return;
- }
- for(int i=0;i0 && nums[i-1]==nums[i] &&
- !visited[i-1]){ //注意:相邻元素相同,并且都未被访问
- continue;
- }
- visited[i]=true;
- p.add(nums[i]);
- findUniquePermutation(nums,index+1,p);
- p.remove(p.size()-1);
- visited[i]=false;
- }
- }
- return;
-}
-```
-
-
-
-## *7、字符串的排列
-
-[字符串的排列](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
-
-```java
-private ArrayList res;
-private boolean[] visited;
-
-public ArrayList Permutation(String str) {
- res = new ArrayList<>();
- if(str==null || str.length()==0){
- return res;
- }
- char[] chs = str.toCharArray();
- Arrays.sort(chs); //方便后面的去重处理
- visited = new boolean[str.length()];
- permute(chs,0,new StringBuilder());
- return res;
-}
-
-//产生排列
-//p中保存一个存在index个元素的排列
-//向这个排列的末尾添加第(index+1)个元素,获得包含(index+1)个元素的排列
-private void permute(char[] chs,int index,StringBuilder p){
- if(index==chs.length){
- res.add(p.toString());
- return;
- }
- for(int i=0;i0 && chs[i-1]==chs[i] && !visited[i-1]){
- continue;
- }
- if(!visited[i]){
- p.append(chs[i]);
- visited[i] = true;
- permute(chs,index+1,p);
- p.deleteCharAt(p.length()-1);
- visited[i] = false;
- }
- }
-}
-```
-
-
-
-## 8、字母大小写全排列
-
-[字母大小写全排列](https://leetcode-cn.com/problems/letter-case-permutation/)
-
-```java
-private List res;
-
-//处理 index 位置的数据
-//如果 index 位置是字母的话,则替换
-private void replaceLetter(int index,StringBuilder p){
- if(index==p.length()){
- res.add(p.toString());
- return;
- }
- char ch=p.charAt(index);
- if(Character.isLetter(ch)){
- p.setCharAt(index,Character.toUpperCase(ch));
- replaceLetter(index+1,p);
- p.setCharAt(index,Character.toLowerCase(ch));
- replaceLetter(index+1,p);
- }else{
- replaceLetter(index+1,p);
- }
- return;
-}
-
-public List letterCasePermutation(String S) {
- res=new ArrayList<>();
- if(S==null || S.length()==0){
- return res;
- }
- StringBuilder p=new StringBuilder(S);
- replaceLetter(0,p);
- return res;
-}
-```
-
-
-
-## 9、电话号码的字母组合
-
-[电话号码的字母组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/)
-
-```java
-private final String[] letterMap={
- " ",//0
- "",//1
- "abc",//2
- "def",//3
- "ghi",//4
- "jkl",//5
- "mno",//6
- "pqrs",//7
- "tuv",//8
- "wxyz"//9
-};
-
-private List res;
-//s 中保留了从 digits[0...index-1] 翻译得到的字符串
-//寻找和 digits[index] 匹配的字母,获取 digits[0...index] 翻译得到的解
-private void findCombination(String digits,int index,String s){
- if(index==digits.length()){
- res.add(s);
- return;
- }
-
- char c = digits.charAt(index);
- String letters = letterMap[c-'0']; // 翻译字符串
- for(int i=0;i letterCombinations(String digits) {
- res = new ArrayList<>();
- if(digits==null || digits.length()==0){
- return res;
- }
- findCombination(digits,0,"");
- return res;
-}
-```
-
-
-
-## 10、组合
-
-[组合](https://leetcode-cn.com/problems/combinations/)
-
-```java
-//思路一:暴力解法
-
-private List> res;
-
-//c存储已经找到的组合
-//从start开始搜索新的元素
-private void findCombination(int n,int k,int start,List c){
- if(k==c.size()){
- res.add(new ArrayList<>(c));
- return;
- }
- for(int i=start;i<=n;i++){
- c.add(i);
- findCombination(n,k,i+1,c);
- c.remove(c.size()-1);
- }
- return;
-}
-
-public List> combine(int n, int k) {
- res=new ArrayList<>();
- if(n<=0 || k<=0 || n c=new ArrayList<>();
- findCombination(n,k,1,c);
- return res;
-}
-```
-
-
-
-```java
-//思路二:优化,进行剪枝
-private List> res;
-
-//c存储已经找到的组合
-//从start开始搜索新的元素
-private void findCombination(int n,int k,int start,List c){
- if(k==c.size()){
- res.add(new ArrayList<>(c));
- return;
- }
-
- //TODO:优化--剪枝
- //c存储的是已经找到的组合。
- //此时还剩下k-c.size()个空位,
- //则 [i...n]之间的元素最少要有 k-c.size() 个,即 n-i+1 >= k-c.size()
- //所以有 i <= n-(k-c.size())+1
-
- for(int i=start;i<=n-(k-c.size())+1;i++){
- c.add(i);
- findCombination(n,k,i+1,c);
- c.remove(c.size()-1);
- }
- return;
-}
-
-public List> combine(int n, int k) {
- res=new ArrayList<>();
- if(n<=0 || k<=0 || n c=new ArrayList<>();
- findCombination(n,k,1,c);
- return res;
-}
-```
-
-
-
-## 11、N皇后
-
-[N皇后](https://leetcode-cn.com/problems/n-queens/)
-
-
-
-
-
-- 思路:
-
-
-
-
-
-判断对角线不合法的情况:
-
-(1)竖向:col[i] 表示第 i 列被占用
-
-(2)对角线1:dai1[i] 表示第 i 条对角线被1占用
-
-
-
-(3)对角线2:dai2[i]表示第i对角线被2占用
-
-
-
-```java
-private List> res;
-
-//用于判断是否在同一竖线上,因为index表示行数,是变化的,所以不用判断是否在相同行
-private boolean[] cols;
-//判断是否在1类对角线上,相应坐标 i+j
-private boolean[] dial1;
-//判断是否在2类对角线上,相应坐标 i-j+n-1
-private boolean[] dial2;
-
-//在 index 行放皇后
-//row 记录在 index 行能够放皇后的位置
-//比如 row.get(0) = 1,就表示在棋盘的 [0,1] 位置放上皇后
-private void putQueen(int n,int index,List row){
- if(index==n){
- res.add(generateBoard(n,row));
- return;
- }
- // [index,j] 放皇后
- for(int j=0;j generateBoard(int n, List row) {
- List res=new ArrayList<>();
- if(n<=0){
- return res;
- }
-
- char[][] board=new char[n][n];
- for(int i=0;i> solveNQueens(int n) {
- res=new ArrayList<>();
- if(n==0){
- return res;
- }
- cols=new boolean[n];
- dial1=new boolean[2*n-1];
- dial2=new boolean[2*n-1];
- List row=new ArrayList<>();
- putQueen(n,0,row);
- return res;
-}
-```
-
-
-
-## 12、N皇后 II
-
-[N皇后 II](https://leetcode-cn.com/problems/n-queens-ii/)
-
-```java
-
-```
-
-
-
-## 13、解数独
-
-[解数独](https://leetcode-cn.com/problems/sudoku-solver/)
-
-```java
-
-```
-
diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md"
new file mode 100644
index 00000000..3bbfe622
--- /dev/null
+++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/4_\346\220\234\347\264\242.md"
@@ -0,0 +1,812 @@
+
+
+
+
+
+
+# 回溯-Backtract
+
+## 1、二叉树中和为某一值的路径
+
+[二叉树中和为某一值的路径](https://www.nowcoder.com/practice/b736e784e3e34731af99065031301bca?tpId=13&tqId=11177&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
+```java
+public ArrayList> FindPath(TreeNode root, int target) {
+ ArrayList> res = new ArrayList<>();
+ ArrayList values = new ArrayList<>();
+ backtrack(root,target,values,res);
+ return res;
+}
+
+// values : 记录从根节点到叶子节点的所有路径
+// paths : 存储所有可能的结果
+private void backtrack(TreeNode root, int target,
+ ArrayList values,
+ ArrayList> paths) {
+ if (root == null) {
+ return;
+ }
+ values.add(root.val);
+ if(root.left==null && root.right==null && root.val==target){
+ paths.add(new ArrayList<>(values));
+ }else{
+ backtrack(root.left,target-root.val,values,paths);
+ backtrack(root.right,target-root.val,values,paths);
+ }
+ values.remove(values.size()-1);
+}
+```
+
+
+
+## *2、二叉树的所有路径
+
+[二叉树的所有路径](https://leetcode-cn.com/problems/binary-tree-paths/)
+
+```java
+public List binaryTreePaths(TreeNode root) {
+ List paths=new ArrayList<>();
+ if(root==null){
+ return paths;
+ }
+ List values=new ArrayList<>();
+ backtrack(root,values,paths);
+ return paths;
+}
+
+//values 记录从根节点到叶子节点
+//paths 记录路径
+private void backtrack(TreeNode root, List values,List paths){
+ if(root==null){
+ return;
+ }
+ values.add(root.val);
+ if(root.left==null && root.right==null){ // 到达叶子节点
+ paths.add(buildPath(values));
+ }else{
+ backtrack(root.left,values,paths);
+ backtrack(root.right,values,paths);
+ }
+ values.remove(values.size()-1);
+}
+
+private String buildPath(List values){
+ StringBuilder res=new StringBuilder();
+ for(int i=0;i");
+ }
+ }
+ return res.toString();
+}
+```
+
+
+
+## *3、矩阵中的路径
+
+[矩阵中的路径](https://www.nowcoder.com/practice/c61c6999eecb4b8f88a98f66b273a3cc?tpId=13&tqId=11218&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
+```java
+//思路:典型的回溯法思想
+private int m,n; //矩阵的长度、宽度
+private boolean[][] visted; //标记是否访问
+
+private int[][] d={
+ {0,1}, //向右
+ {-1,0},//向上
+ {0,-1},//向左
+ {1,0} //向下
+};
+
+private boolean inArea(int x,int y){
+ return (x>=0 && x=0 && y=0 && x=0 && y
+
+
+
+- 思路:
+
+
+
+
+
+在第 i 行放上皇后,判断是否其他地方能够放皇后的情况:
+
+(1)竖向:col[j] 表示第 j 列被占用
+
+(2)对角线1:dai1[m] 表示第 m 条对角线1被占用,如 dia1[0]=true 表示[0,0] 位置不能放皇后,
+
+dia1[1] =true 就表示 [0,1]和[1,0] 位置不能放皇后。
+
+对角线1总共有 (2*n-1) 条;m 与 i 、 j 的关系:m=i+j。
+
+
+
+(3)对角线2:dai2[m]表示第 m 条对角线被2占用,如 dai2[0]=true 表示[0,3] 位置不能放皇后,
+
+dai2[2]=true 则表示 [0,2] 和 [1,3] 不能放皇后,
+
+对角线2总共有(2*n-1)条;m 与 i、j 关系:m=i-j+n-1。
+
+
+
+```java
+private List> res;
+
+//用于判断是否在同一竖线上,因为index表示行数,是变化的,所以不用判断是否在相同行
+private boolean[] cols;
+//判断是否在1类对角线上,相应坐标 i+j
+private boolean[] dial1;
+//判断是否在2类对角线上,相应坐标 i-j+n-1
+private boolean[] dial2;
+
+//在 index 行放皇后
+//row 记录在 index 行能够放皇后的位置
+//比如 row.get(0) = 1,就表示在棋盘的 [0,1] 位置放上皇后
+private void putQueen(int n,int index,List row){
+ if(index==n){
+ res.add(generateBoard(n,row));
+ return;
+ }
+ // [index,j] 放皇后
+ for(int j=0;j generateBoard(int n, List row) {
+ List res=new ArrayList<>();
+ if(n<=0){
+ return res;
+ }
+
+ char[][] board=new char[n][n];
+ for(int i=0;i> solveNQueens(int n) {
+ res=new ArrayList<>();
+ if(n==0){
+ return res;
+ }
+ cols=new boolean[n];
+ dial1=new boolean[2*n-1];
+ dial2=new boolean[2*n-1];
+ List row=new ArrayList<>();
+ putQueen(n,0,row);
+ return res;
+}
+```
+
+
+
+## 6、解数独
+
+[解数独](https://leetcode-cn.com/problems/sudoku-solver/)
+
+```java
+// 思路:回溯法
+// 数独矩阵是 9*9 的方阵,也就是有 81 个位置,
+// 可以看成是一个长度为 81 的数组,使用 pos(从0开始) 记录在数组中位置
+// row[i][m] 表示第 i 行填数字 m 的占用情况,即 row[i][m]=true,表示在第i行填上数字 m 了。
+// col[i][m] 表示第 i 列填数字 m 的占用情况,即 col[i][m]=true,表示在第i列填上数字 m 了。
+// block[i][m] 表示第 i个九宫格填数字 m 的占用情况
+public void solveSudoku(char[][] board) {
+ if(board==null){
+ return;
+ }
+ boolean[][] row=new boolean[9][10];
+ boolean[][] col=new boolean[9][10];
+ boolean[][] block=new boolean[9][10];
+ for(int i=0;i<9;i++){
+ for(int j=0;j<9;j++){
+ if(board[i][j]!='.'){ // [i,j]位置已填上数字
+ int num= board[i][j]-'0';
+ row[i][num]=true;
+ col[j][num]=true;
+ block[i/3*3+j/3][num]=true;
+ }
+ }
+ }
+ for(int pos=0;pos<81;pos++){
+ int x=pos/9;
+ int y=pos%9;
+ if(board[x][y]=='.'){
+ if(!putNum(board,pos,row,col,block)){
+ continue;
+ }
+ }
+ }
+}
+
+// 向 pos 位置放数字
+private boolean putNum(char[][] board, int pos,
+ boolean[][] row, boolean[][] col, boolean[][] block){
+ if(pos==81){
+ return true;
+ }
+ int nextPos = pos+1; // pos 位置的下一未填数字的位置
+ for(;nextPos<81;nextPos++){
+ if(board[nextPos/9][nextPos%9]=='.'){
+ break;
+ }
+ }
+ // [x,y] 表示 pos 在表格中位置
+ int x=pos/9;
+ int y=pos%9;
+
+ for(int num=1;num<=9;num++){ // pos 位置可以填 1-9 任意整数
+ if(!row[x][num] && !col[y][num] && !block[x/3*3+y/3][num]){
+ board[x][y]=(char)(num+'0');
+ row[x][num]=true;
+ col[y][num]=true;
+ block[x/3*3+y/3][num]=true;
+ if(putNum(board,nextPos,row,col,block)){
+ return true;
+ }
+ block[x/3*3+y/3][num]=false;
+ col[y][num]=false;
+ row[x][num]=false;
+ board[x][y]='.';
+ }
+ }
+ return false;
+}
+```
+
+
+
+# 广度优先-BFS
+
+## 1、单词接龙
+
+[单词接龙](https://leetcode-cn.com/problems/word-ladder/description/)
+
+```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
+
+## 1、岛屿数量
+
+[岛屿数量](https://leetcode-cn.com/problems/number-of-islands/)
+
+```java
+// 思路:典型的 flood-fill 算法
+private int m,n;
+
+private boolean[][] visited;
+
+private boolean inArea(int x,int y){
+ return (x>=0 && x=0 && y=0 && x=0 && y=0 && x=0 && y0){
+ sum += num %10;
+ num/=10;
+ }
+ return sum;
+}
+
+private boolean valid(int threshlod ,int x,int y){
+ return (getNum(x)+getNum(y))<=threshlod;
+}
+
+private int walk(int threshold,int startx,int starty){
+ visited[startx][starty]=true;
+ int walks = 0;
+ if(valid(threshold,startx,starty)){
+ walks=1;
+ }
+ for(int i=0;i<4;i++){
+ int newX = startx+d[i][0];
+ int newY = starty+d[i][1];
+ if(inArea(newX,newY)){
+ if(!visited[newX][newY] && valid(threshold,newX,newY)){
+ walks += walk(threshold,newX,newY);
+ }
+ }
+ }
+ return walks;
+}
+
+public int movingCount(int threshold, int rows, int cols) {
+ if(threshold<0){ //threshold<0,则机器人就不能走了
+ return 0;
+ }
+ m = rows;
+ if(m==0){
+ return 0;
+ }
+ n= cols;
+ visited = new boolean[m][n];
+ return walk(threshold,0,0);
+}
+```
+
+
+
+## 4、被围绕的区域
+
+[被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/)
+
+```java
+private int m,n;
+
+private boolean[][] visited;
+
+private int[][] d={
+ {-1,0},
+ {0,1},
+ {1,0},
+ {0,-1}
+};
+
+private boolean inArea(int x,int y){
+ return (x>=0 && x=0 && y=0 && x=0 && y=matrix[startx][starty]){
+ dfs(matrix,newX,newY,visited);
+ }
+ }
+ }
+}
+
+public List> pacificAtlantic(int[][] matrix) {
+ List> res=new ArrayList<>();
+ m=matrix.length;
+ if(m==0){
+ return res;
+ }
+ n=matrix[0].length;
+ pacific=new boolean[m][n];
+ atlantic=new boolean[m][n];
+
+ for(int j=0;j
+
+```java
+private List> res;
+
+private boolean[] visited;
+
+public List> permute(int[] nums) {
+ res = new ArrayList<>();
+ if(nums==null || nums.length==0){
+ return res;
+ }
+ visited = new boolean[nums.length];
+ List p =new ArrayList<>();
+ generatePermutation(nums,0,p);
+ return res;
+}
+
+//p中保存一个有 index 的元素的排列
+//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列
+private void generatePermutation(int[] nums,int index,List p){
+ if(index==nums.length){
+ res.add(new ArrayList<>(p));
+ return;
+ }
+ for(int i=0;i> res;
+
+private boolean[] visited;
+
+public List> permuteUnique(int[] nums) {
+ res = new ArrayList<>();
+ if(nums==null || nums.length==0){
+ return res;
+ }
+ visited = new boolean[nums.length];
+ Arrays.sort(nums);
+ List p = new ArrayList<>();
+ findUniquePermutation(nums,0,p);
+ return res;
+}
+
+//p 保存的是有 index 元素的排列
+//向这个排列的末尾添加第 (index+1) 个元素,组成有(index+1) 个元素排列
+private void findUniquePermutation(int[] nums,int index,List p){
+ if(index==nums.length){
+ res.add(new ArrayList<>(p));
+ return;
+ }
+ for(int i=0;i0 && nums[i-1]==nums[i] &&
+ !visited[i-1]){ //注意:相邻元素相同,并且都未被访问
+ continue;
+ }
+ visited[i]=true;
+ p.add(nums[i]);
+ findUniquePermutation(nums,index+1,p);
+ p.remove(p.size()-1);
+ visited[i]=false;
+ }
+ }
+ return;
+}
+```
+
+
+
+## 3、字符串的排列
+
+[字符串的排列](https://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7?tpId=13&tqId=11180&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
+```java
+private ArrayList res;
+private boolean[] visited;
+
+public ArrayList Permutation(String str) {
+ res = new ArrayList<>();
+ if(str==null || str.length()==0){
+ return res;
+ }
+ char[] chs = str.toCharArray();
+ Arrays.sort(chs); //方便后面的去重处理
+ visited = new boolean[str.length()];
+ permute(chs,0,new StringBuilder());
+ return res;
+}
+
+//产生排列
+//p中保存一个存在index个元素的排列
+//向这个排列的末尾添加第(index+1)个元素,获得包含(index+1)个元素的排列
+private void permute(char[] chs,int index,StringBuilder p){
+ if(index==chs.length){
+ res.add(p.toString());
+ return;
+ }
+ for(int i=0;i0 && chs[i-1]==chs[i] && !visited[i-1]){
+ continue;
+ }
+ if(!visited[i]){
+ p.append(chs[i]);
+ visited[i] = true;
+ permute(chs,index+1,p);
+ p.deleteCharAt(p.length()-1);
+ visited[i] = false;
+ }
+ }
+}
+```
+
+
+
+## *4、字母大小写全排列
+
+[字母大小写全排列](https://leetcode-cn.com/problems/letter-case-permutation/)
+
+```java
+private List res;
+
+//处理 index 位置的数据
+//如果 index 位置是字母的话,则替换
+private void replaceLetter(int index,StringBuilder p){
+ if(index==p.length()){
+ res.add(p.toString());
+ return;
+ }
+ char ch=p.charAt(index);
+ if(Character.isLetter(ch)){
+ p.setCharAt(index,Character.toUpperCase(ch));
+ replaceLetter(index+1,p);
+ p.setCharAt(index,Character.toLowerCase(ch));
+ replaceLetter(index+1,p);
+ }else{
+ replaceLetter(index+1,p);
+ }
+ return;
+}
+
+public List letterCasePermutation(String S) {
+ res=new ArrayList<>();
+ if(S==null || S.length()==0){
+ return res;
+ }
+ StringBuilder p=new StringBuilder(S);
+ replaceLetter(0,p);
+ return res;
+}
+```
+
+
+
+## *5、电话号码的字母组合
+
+[电话号码的字母组合](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/)
+
+```java
+private final String[] letterMap={
+ " ",//0
+ "",//1
+ "abc",//2
+ "def",//3
+ "ghi",//4
+ "jkl",//5
+ "mno",//6
+ "pqrs",//7
+ "tuv",//8
+ "wxyz"//9
+};
+
+private List res;
+
+public List letterCombinations(String digits) {
+ res=new ArrayList<>();
+ if(digits==null || digits.length()==0){
+ return res;
+ }
+ generateLetterCombinations(digits,0,new StringBuilder());
+ return res;
+}
+
+// p 保存 digits[0,index-1] 翻译得到的字符串
+private void generateLetterCombinations(String digits,int index,StringBuilder p){
+ if(index==digits.length()){
+ res.add(p.toString());
+ return;
+ }
+ char[] chs=letterMap[digits.charAt(index)-'0'].toCharArray();
+ for(int i=0;i> res;
+
+//c存储已经找到的组合
+//从start开始搜索新的元素
+private void findCombination(int n,int k,int start,List c){
+ if(k==c.size()){
+ res.add(new ArrayList<>(c));
+ return;
+ }
+ for(int i=start;i<=n;i++){
+ c.add(i);
+ findCombination(n,k,i+1,c);
+ c.remove(c.size()-1);
+ }
+ return;
+}
+
+public List> combine(int n, int k) {
+ res=new ArrayList<>();
+ if(n<=0 || k<=0 || n c=new ArrayList<>();
+ findCombination(n,k,1,c);
+ return res;
+}
+```
+
+```java
+//思路二:剪枝优化
+private List> res;
+
+//c存储已经找到的组合
+//从start开始搜索新的元素
+private void findCombination(int n,int k,int start,List c){
+ if(k==c.size()){
+ res.add(new ArrayList<>(c));
+ return;
+ }
+
+ //优化--剪枝
+ //c存储的是已经找到的组合。
+ //此时还剩下k-c.size()个空位,
+ //则 [i...n]之间的元素最少要有 k-c.size() 个,即 n-i+1 >= k-c.size()
+ //所以有 i <= n-(k-c.size())+1
+
+ for(int i=start;i<=n-(k-c.size())+1;i++){
+ c.add(i);
+ findCombination(n,k,i+1,c);
+ c.remove(c.size()-1);
+ }
+ return;
+}
+
+public List> combine(int n, int k) {
+ res=new ArrayList<>();
+ if(n<=0 || k<=0 || n c=new ArrayList<>();
+ findCombination(n,k,1,c);
+ return res;
+}
+```
+
diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md"
deleted file mode 100644
index 60cf3bd7..00000000
--- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/5_\346\267\261\345\272\246\344\274\230\345\205\210.md"
+++ /dev/null
@@ -1,329 +0,0 @@
-# 深度优先
-
-## 1、岛屿数量
-
-[岛屿数量](https://leetcode-cn.com/problems/number-of-islands/)
-
-```java
-// 思路:典型的 flood-fill 算法
-private int m,n;
-
-private boolean[][] visited;
-
-private boolean inArea(int x,int y){
- return (x>=0 && x=0 && y=0 && x=0 && y=0 && x=0 && y0){
- sum += num %10;
- num/=10;
- }
- return sum;
-}
-
-private boolean valid(int threshlod ,int x,int y){
- return (getNum(x)+getNum(y))<=threshlod;
-}
-
-private int walk(int threshold,int startx,int starty){
- visited[startx][starty]=true;
- int walks = 0;
- if(valid(threshold,startx,starty)){
- walks=1;
- }
- for(int i=0;i<4;i++){
- int newX = startx+d[i][0];
- int newY = starty+d[i][1];
- if(inArea(newX,newY)){
- if(!visited[newX][newY] && valid(threshold,newX,newY)){
- walks += walk(threshold,newX,newY);
- }
- }
- }
- return walks;
-}
-
-public int movingCount(int threshold, int rows, int cols) {
- if(threshold<0){ //threshold<0,则机器人就不能走了
- return 0;
- }
- m = rows;
- if(m==0){
- return 0;
- }
- n= cols;
- visited = new boolean[m][n];
- return walk(threshold,0,0);
-}
-```
-
-
-
-## 4、被围绕的区域
-
-[被围绕的区域](https://leetcode-cn.com/problems/surrounded-regions/)
-
-```java
-private int m,n;
-
-private boolean[][] visited;
-
-private int[][] d={
- {-1,0},
- {0,1},
- {1,0},
- {0,-1}
-};
-
-private boolean inArea(int x,int y){
- return (x>=0 && x=0 && y=0 && x=0 && y=matrix[startx][starty]){
- dfs(matrix,newX,newY,visited);
- }
- }
- }
-}
-
-public List> pacificAtlantic(int[][] matrix) {
- List> res=new ArrayList<>();
- m=matrix.length;
- if(m==0){
- return res;
- }
- n=matrix[0].length;
- pacific=new boolean[m][n];
- atlantic=new boolean[m][n];
-
- for(int j=0;j list=new ArrayList<>();
- for(int i=0;i() { //按照其实位置进行排序
- @Override
- public int compare(Interval o1, Interval o2) {
- int num=o1.start-o2.start;
- int num2=(num==0)?(o1.end-o2.end):num;
- return num2;
+ Arrays.sort(g);
+ Arrays.sort(s);
+ int i=g.length-1;
+ int j=s.length-1;
+ int num=0;
+ while (i>=0 && j>=0){
+ if(s[j]>=g[i]){
+ num++;
+ j--;
}
- });
-
- ArrayList res=new ArrayList<>();
- Interval cur=list.get(0);
- for(int i=1;i=list.get(i).start){ //存在区间交叉
- //区间进行合并
- cur.end=Math.max(cur.end,list.get(i).end);
- }else{ //不存在交叉
- res.add(cur);
- cur=list.get(i);
- }
- if(i==m-1){
- res.add(cur);
- }
- }
-
- int n=res.size();
- int[][] matrix=new int[n][2];
- for(int i=0;ilimit){ // 如果只能载一个人,则载最重的
+ res++;
+ heavy--;
+ }else{ // people[light]+people[heavy]<=limit,船可以搭载两个人
+ res++;
+ light++;
+ heavy--;
+ }
+ }
+ return res;
+}
```
-## 5、无重叠区间
+## *5、汇总区间
-[无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/)
+[汇总区间](https://leetcode-cn.com/problems/summary-ranges/)
```java
+public List summaryRanges(int[] nums) {
+ List res=new ArrayList<>();
+ if(nums==null || nums.length==0){
+ return res;
+ }
+ if(nums.length==1) {
+ res.add(nums[0] + "");
+ return res;
+ }
+ int start=nums[0];
+ int end=start;
+ for(int i=1;i"+end;
+}
```
-## 6、救生艇
+## 6、无重叠区间
-[救生艇](https://leetcode-cn.com/problems/boats-to-save-people/)
+[无重叠区间](https://leetcode-cn.com/problems/non-overlapping-intervals/)
```java
-
+// 思路:
+// 首先计算最多能组成的不重叠区间个数,然后用区间总个数减去不重叠区间的个数。
+// 其中区间结尾至关重要,选择的区间结尾越小,留给后面的空间就越大,那么后面能够选择的区间个数也就越大。
+public int eraseOverlapIntervals(int[][] intervals) {
+ if(intervals==null || intervals.length==0){
+ return 0;
+ }
+ // 按照区间结尾进行升序排序
+ // 写法一:比较器
+ // 这里使用 p1[1]() {
+ @Override
+ public int compare(int[] p1, int[] p2) {
+ return p1[1] p1[1]p[1]));*/
+ int cnt=1; // 记录最大不重叠区间数
+ int end=intervals[0][1]; // 记录前一个不重叠区间结尾位置
+ for(int i=1;i() {
+ @Override
+ public int compare(int[] o1, int[] o2) {
+ return o1[1](o1[1]o[1]));
+ int res=1;
+ int end=points[0][1];
+ for(int i=1;i() {
+ @Override
+ public int compare(int[] o1, int[] o2) {
+ return (o1[0]
+ o1[0]o[0]));*/
+ List intervalList=new ArrayList<>(); // 保存结果区间
+ int[] cur=intervals[0]; // 记录当前重叠区间
+ int m=intervals.length;
+ for(int i=1;i=intervals[i][0]){ // cur.end >= next.start
+ cur[1]=Math.max(cur[1],intervals[i][1]);
+ }else{
+ intervalList.add(cur);
+ cur=intervals[i];
+ }
+ if(i==m-1){
+ intervalList.add(cur);
+ }
+ }
+ int[][] res=new int[intervalList.size()][2];
+ for(int i=0;i=0 || j>=0){
+ c+=(i>=0)?num1.charAt(i)-'0':0;
+ c+=(j>=0)?num2.charAt(j)-'0':0;
+ res.append(c%10);
+ c/=10;
+ i--;
+ j--;
}
-
- int l=1;
- int r=x;
- while(l<=r){
- int mid=(r-l)/2+l;
- int sqrt=x/mid;
- if(sqrt==mid){
- return mid;
- }else if(sqrtmid;
- l=mid+1;
- }
+ if(c==1){ // 考虑 num1="90"、num2="90" 的情况
+ res.append("1");
}
- return r;
+ return res.reverse().toString();
}
```
-## 7、求 1+2+3+...+n
+## 2、求 1+2+3+...+n
[求 1+2+3+...+n](https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -245,11 +216,46 @@ public int Sum_Solution(int n) {
-## *8、从 1 到 n 整数中 1 出现的次数
+## *3、从 1 到 n 整数中 1 出现的次数
[从 1 到 n 整数中 1 出现的次数](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
+// 思路一:暴力法
+// 时间复杂度:O(n^2)
+public int NumberOf1Between1AndN_Solution(int n) {
+ if(n<=0){
+ return 0;
+ }
+ int res=0;
+ for(int i=1;i<=n;i++){
+ res+=getNumberOf1(i);
+ }
+ return res;
+}
+
+private int getNumberOf1(int n){
+ int cnt=0;
+ while(n>0){
+ if(n%10==1){
+ cnt++;
+ }
+ n/=10;
+ }
+ return cnt;
+}
+```
+
+```java
+// 思路二:参考 https://blog.csdn.net/yi_Afly/article/details/52012593
+// 总结如下规律:
+// base 为当前位数 base=1,10,100,
+// weight 为当前位值
+// formatter 为 weight 的后一位
+// round 就是 weight 前的所有位
+// 当 weight==0 时,count=round*base
+// 当 weight==1 时,count=round*base+formatter+1
+// 当 weight>1 时,count==round*base+base
public int NumberOf1Between1AndN_Solution(int n) {
if(n<1){
return 0;
@@ -276,13 +282,11 @@ public int NumberOf1Between1AndN_Solution(int n) {
}
```
-> [Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython)
-
> [参考:从 1 到 n 整数中 1 出现的次数](https://blog.csdn.net/yi_Afly/article/details/52012593)
-## *9、数字序列中的某一位数字
+## *4、数字序列中的某一位数字
数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。
@@ -340,7 +344,85 @@ private int getDigitAtIndex(int index, int place) {
-## 10、圆圈中最后剩下的数
+# 经典数学问题
+
+## *1、数值的整数次方
+
+[数值的整数次方](https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
+
+```java
+//思路:分治法
+// a^n 次方
+// 若 a 为奇数 a^n = a*(a*a)^((n-1)/2)
+// 若 a 为偶数 a^n = (a*a)^(n/2)
+
+//注意:
+// n<0 时,则 a^n = 1 /(a ^(-n))
+// 但是当 n == Integer.MIN_VALUE 时,-n = Integer.MAX_VALUE+1
+// 我们可以转化为 1 / (a^(Integer.MAX_VALUE)*a)
+
+public double Power(double base, int exponent) {
+ if(exponent==0){
+ return 1.0;
+ }
+ if(exponent==1){
+ return base;
+ }
+ if(exponent==Integer.MIN_VALUE){
+ return 1/(Power(base,Integer.MAX_VALUE)*base);
+ }
+
+ if(exponent<0){
+ exponent = -exponent;
+ return 1/Power(base,exponent);
+ }
+
+ if(exponent%2==1){
+ return Power(base*base,(exponent-1)/2)*base;
+ }else{
+ return Power(base*base,exponent/2);
+ }
+}
+```
+
+
+
+## 2、x 的平方根
+
+[x 的平方根](https://leetcode-cn.com/problems/sqrtx/)
+
+```java
+// 思路:要求是非负整数,则必然 x>=0
+// 当 x<4时,可以证明 sqrt(x) > x/2
+// 当 x=4时,sqrt(4)==4/2=2
+// 当 x>4时,可以证明 sqrt(x) < x/2
+public int mySqrt(int x) {
+ if(x==0){
+ return 0;
+ }
+ if(x<4){ // x=1,x=2,x=3,sqrt(x)=1
+ return 1;
+ }
+ // 考虑 x>=4的情况
+ int l=1;
+ int r=x/2;
+ while(l<=r){
+ int mid=(r-l)/2+l;
+ if(x/midx,采用 x/midmid){
+ l=mid+1;
+ }else{
+ return mid;
+ }
+ }
+ return r; // 只保留整数部分,所以取r
+}
+```
+
+
+
+## *3、圆圈中最后剩下的数
[圆圈中最后剩下的数](https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
@@ -386,100 +468,183 @@ public int LastRemaining_Solution(int n, int m) {
> [参考:约瑟夫环问题](https://blog.csdn.net/u011500062/article/details/72855826)
-## 11、计数质数
+
+
+## 4、计数质数
[计数质数](https://leetcode-cn.com/problems/count-primes/)
```java
-
+// 思路:
+// 定义一个非素数数组 notPrimes,其中 notPrimes[i] 表示元素 i 是否是素数
+// notPrimes[i]=true,说明 i 是素数
+// notPrimes[i]=false,说明 i 不是素数
+public int countPrimes(int n) {
+ int cnt=0;
+ if(n<=1){
+ return cnt;
+ }
+ boolean[] notPrimes=new boolean[n];
+ for(int x=2;x=n 循环停止
+ for(long j=(long)x*x;j
+// 11!=11*(2*5)*9*(4*2)*7*(3*2)*(1*5)*(2*2)*3*(1*2)*1=39916800,有2对<2,5>
+// 也就是说,有多少对<2,5>就有多少0
+// 对于含有2的因子的话是 1*2, 2*2, 3*2, 4*2
+// 对于含有5的因子的话是 1*5, 2*5
+// 含有2的因子每两个出现一次,含有5的因子每5个出现一次,所以2出现的个数远远多于 5,
+// 换言之找到一个5,一定能找到一个2与之配对。
+// 所以我们只需要找有多少个 5。
+// n! = 1 * 2 * 3 * 4 * (1 * 5) * ... * (2 * 5) * ... * (3 * 5) *... * n
+// 可以得出如下规律:每隔5个数会出现一个5,每隔25个数,出现2个5,每隔125个数,出现3个5
+// 所以最终 5的个数:n/5+ n/25 (有1个5在每隔5个数已经出现过一次了) + n/125 (有1个在每隔5个数出现过了,另一个则载每隔25个数出现过了)
+public int trailingZeroes(int n) {
+ if(n<5){
+ return 0;
+ }
+ int res=0;
+ while (n>0){
+ res+=n/5;
+ n/=5;
+ }
+ return res;
+}
```
-## 13、字符串相加
+## 6、最少移动次数使数组元素相等 II
-[字符串相加](https://leetcode-cn.com/problems/add-strings/)
+[ 最少移动次数使数组元素相等 II](https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements-ii/)
```java
-public String addStrings(String num1, String num2) {
- if(num1==null || num1.length()==0){
- return num2;
- }
- if(num2==null || num2.length()==0){
- return num1;
- }
-
- int i=num1.length()-1;
- int j=num2.length()-1;
- int c=0;
-
- StringBuilder res=new StringBuilder();
-
- while(i>=0 || j>=0 || c==1){
- c+=(i>=0)?num1.charAt(i)-'0':0;
- c+=(j>=0)?num2.charAt(j)-'0':0;
- res.append(c%10);
- c/=10;
- i--;
- j--;
+// 思路:
+// 当 x 为这个 N 个数的中位数时,可以使得距离最小。
+// 具体地,若 N 为奇数,则 x 必须为这 N 个数中的唯一中位数;
+// 若 N 为偶数,中间的两个数为 p 和 q,中位数为 (p + q) / 2,
+// 此时 x 只要是区间 [p, q](注意是闭区间) 中的任意一个数即可。
+
+// 写法一:排序求中位数
+public int minMoves2(int[] nums) {
+ Arrays.sort(nums);
+ // 获取数组中位数获取[p,q]中的数值p
+ int mid=nums[nums.length/2];
+ int res=0;
+ for(int num:nums){
+ res+=Math.abs(num-mid);
}
-
- return res.reverse().toString();
+ return res;
}
```
-
-
-## 14、最少移动次数使数组元素相等 II
-
-[ 最少移动次数使数组元素相等 II](https://leetcode-cn.com/problems/minimum-moves-to-equal-array-elements-ii/)
-
```java
+// 写法二:利用快速排序求中位数
+public int minMoves2(int[] nums) {
+ if(nums.length==1){
+ return 0;
+ }
+ int k=nums.length/2;
+ int mid=nums[findKth(nums,k)];
+ int res=0;
+ for(int num:nums){
+ res+=Math.abs(num-mid);
+ }
+ return res;
+}
+private int findKth(int[] nums,int k){
+ int start=0;
+ int end=nums.length-1;
+ while (start<=end){
+ int p=partition(nums,start,end);
+ if(p==k){
+ return p;
+ }else if(pk
+ end=p-1;
+ }
+ }
+ return -1;
+}
+
+private int partition(int[] nums,int start,int end){
+ int pivot=nums[start];
+ while(start=pivot){
+ end--;
+ }
+ nums[start]=nums[end];
+ // 从左向右找第一个大于 pivot 的数
+ while (start n/2,但在本题目 m 即是多数元素
public int majorityElement(int[] nums) {
- int cnt=0; //统计众数
- int majority=-1;
- int n=nums.length;
-
+ int m=nums[0];
+ int cnt=0;
for(int num:nums){
if(cnt==0){
- majority=num;
- cnt++;
+ m=num;
+ cnt=1;
}else{
- if(majority!=num){
- cnt--;
- }else{
+ if(num==m){
cnt++;
+ }else{
+ cnt--;
}
}
}
-
- cnt=0;
- for(int num:nums){
- if(num==majority){
- cnt++;
- }
- }
- return (cnt>n/2)?majority:-1;
+ return m;
}
```
diff --git "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md" "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md"
index a69d6523..cbf78eab 100644
--- "a/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md"
+++ "b/docs/AimForOffer/\347\256\227\346\263\225\346\200\235\346\203\263\347\233\270\345\205\263/8_\345\205\266\344\273\226.md"
@@ -47,7 +47,7 @@ public class Shuffle {
## 2、抢红包算法
-线段切割法:在一条线段上找 (N-1) 个随机点,就可以将该线段随机且公平地切割成 N 段。
+线段切割法:在一条线段上找 (N-1) 个随机点,就可以将该线段随机且公平地切割成 N 段。算上首端点和尾端点的话,总共有 (N+1) 个点
```java
public class RedPacket {
@@ -57,29 +57,27 @@ public class RedPacket {
* 如果是小数的话先转化为整数,相应的结果除以 100 即可
* @return List 存储 n 个红包的金额
*/
- public List generatePocketByLineCutting(int n,double money){
+ public List generatePocketByLineCutting(int n, double money){
Random random=new Random();
//如果是小数的话先转化为整数
int newMoney=(int)money*100;
- //存储线段的的 (n-1) 个随机点,线段长度为 newMoney
- Set set=new TreeSet<>();
-
- while(set.size() points=new ArrayList<>();
+ while (points.size() res=new ArrayList<>();
-
- int pre=0;
- for(Integer p:set) {
- res.add((p - pre) * 1.0 / 100);
- pre = p;
+ for(int i=1;i
+
### 脏读
T1 修改一个数据,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。
-
+
### 不可重复读
T2 读取一个数据,T1 对该数据做了修改。如果 T2 再次读取这个数据,此时读取的结果和第一次读取的结果不同。
-
+
### 幻影读
T1 读取某个**范围**的数据,T2 在这个**范围**内插入新的数据,T1 再次读取这个**范围**的数据,此时读取的结果和和第一次读取的结果不同。
-
+
## 事务隔离级别
产生并发不一致性问题主要原因是破坏了事务的**隔离性**,解决方法是通过**并发控制**来保证隔离性。并发控制可以通过封锁(加锁)来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的**隔离级别**,让用户以一种更轻松的方式处理并发一致性问题。
-事务隔离级别如下:
+### 未提交读(READ UNCOMMITTED)
-| 隔离级别 | 说明 |
-| :--------------------------: | :------------------------------------------------: |
-| 未提交读(READ UNCOMMITTED) | 事务中的修改,即使没有提交,对其它事务也是可见的。 |
-| 提交读(READ COMMITTED) | 一个事务只能读取已经提交的事务所做的修改。 |
-| 可重复读(REPEATABLE READ) | 保证在同一个事务中多次读取同样数据的结果是一样的。 |
-| 可串行化(SERIALIZABLE) | 强制事务串行执行。 |
+事务中的修改,即使没有提交,对其它事务也是可见的。
-事务并发访问引起的问题及使用哪种事务隔离级别避免:
+### 提交读(READ COMMITTED)
+
+一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
+
+### 可重复读(REPEATABLE READ)
+
+保证在同一个事务中多次读取同一数据的结果是一样的。
+
+### 可串行化(SERIALIZABLE)
+
+强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。
+
+隔离级别能解决的并发一致性问题:
| 并发访问问题 | 事务隔离级别 |
| :----------: | :------------------------------------------: |
@@ -88,4 +95,3 @@ T1 读取某个**范围**的数据,T2 在这个**范围**内插入新的数据
| 提交读 (READ COMMITTED) | 避免 | 避免 | 发生 | 发生 |
| 可重复读 (REPEATABLE READ) | 避免 | 避免 | 避免 | 发生 |
| 可串行化 (SERIALIZABLE) | 避免 | 避免 | 避免 | 避免 |
-
diff --git "a/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md" "b/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md"
index 3846303a..244398b2 100644
--- "a/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md"
+++ "b/docs/DataBase/2_\345\205\263\347\263\273\346\225\260\346\215\256\345\272\223\350\256\276\350\256\241\347\220\206\350\256\272.md"
@@ -34,7 +34,7 @@
高级别范式的依赖于低级别的范式,1NF 是最低级别的范式。
-
+
diff --git "a/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md" "b/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md"
index f92a8792..0309567f 100644
--- "a/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md"
+++ "b/docs/DataBase/3_\350\256\276\350\256\241\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223.md"
@@ -2,7 +2,7 @@
关系数据库管理系统(RDBMS)架构图如下:
-
+
## 存储
@@ -20,7 +20,7 @@
将取出的数据放入缓存中,一次性加载多个页数据,相当一部分不是本次访问所需的行。根据“一旦数据被访问,其相邻数据极有可能下次被访问到”,优化访问效率。
- 缓存管理机制:可以使用 [LRU](https://github.com/DuHouAn/Java-Notes/blob/master/docs/LeetCode/12%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84.md#146) 对缓存进行管理。
+ 缓存管理机制:可以使用 LRU 对缓存进行管理。
- **SQL 解析**
diff --git a/docs/DataBase/4_SQL.md b/docs/DataBase/4_SQL.md
deleted file mode 100644
index 4a44aee8..00000000
--- a/docs/DataBase/4_SQL.md
+++ /dev/null
@@ -1,767 +0,0 @@
-
-* [一、基础](#一基础)
-* [二、创建表](#二创建表)
-* [三、修改表](#三修改表)
-* [四、插入](#四插入)
-* [五、更新](#五更新)
-* [六、删除](#六删除)
-* [七、查询](#七查询)
-* [八、排序](#八排序)
-* [九、过滤](#九过滤)
-* [十、通配符](#十通配符)
-* [十一、计算字段](#十一计算字段)
-* [十二、函数](#十二函数)
-* [十三、分组](#十三分组)
-* [十四、子查询](#十四子查询)
-* [十五、连接](#十五连接)
-* [十六、组合查询](#十六组合查询)
-* [十七、视图](#十七视图)
-* [十八、存储过程](#十八存储过程)
-* [十九、游标](#十九游标)
-* [二十、触发器](#二十触发器)
-* [二十一、事务管理](#二十一事务管理)
-* [二十二、字符集](#二十二字符集)
-* [二十三、权限管理](#二十三权限管理)
-* [参考资料](#参考资料)
-
-
-
-# 一、基础
-
-模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。
-
-主键的值不允许修改,也不允许复用(不能使用已经删除的主键值赋给新数据行的主键)。
-
-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 (
- id INT NOT NULL AUTO_INCREMENT,
- col1 INT NOT NULL DEFAULT 1,
- col2 VARCHAR(45) NULL,
- col3 DATE NULL,
- 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, Orders.order_num
-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 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/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md" "b/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md"
index c3d52b50..0f3123f8 100644
--- "a/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md"
+++ "b/docs/DataBase/5_LeetCode_Database\351\242\230\350\247\243.md"
@@ -422,15 +422,15 @@ ON p.PersonId=a.PersonId;
* 内连接:返回两张表的交集部分。
-
+
* 左连接:
-
+
* 右连接:
-
+
# *8、超过经理收入的员工(181)
diff --git a/docs/JVM/1_JVM.md b/docs/JVM/1_JVM.md
index bb466f50..3ec65d58 100644
--- a/docs/JVM/1_JVM.md
+++ b/docs/JVM/1_JVM.md
@@ -164,7 +164,7 @@ Class 文件中的常量池(编译器生成的字面量和符号引用)会
除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。
-直接内存
+## 直接内存
在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。
diff --git "a/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md" "b/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md"
new file mode 100644
index 00000000..ae76568a
--- /dev/null
+++ "b/docs/JVM/1_Java\345\206\205\345\255\230\345\214\272\345\237\237.md"
@@ -0,0 +1,458 @@
+# Java 运行数数据区域
+
+Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
+
+JDK 1.8 和之前的版本略有不同:
+
+- JDK 1.7 之前**运行时常量池**逻辑包含**字符串常量池**存放在方法区
+- JDK 1.7 字符串常量池被从方法区中拿到了堆,运行时常量池剩下的内容还在方法区
+- JDK1.8 HotSpot 虚拟机**移除了永久代**,采用**元空间(Metaspace)** 代替方法区,这时候**字符串常量池还在堆**,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间。
+
+
+
+## 程序计数器
+
+程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的**行号指示器(逻辑上)**。主要有以下两个作用:
+
+- 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
+- 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
+
+此外,程序计数器还有如下特性:
+
+- 通过改变计数器的值来选取下一条需要执行的字节码指令
+- 和线程一对一的关系,即“线程私有”
+- 对 Java 方法计数,如果是 Native 方法则计数器值为 Undefined
+- 只是计数,不会发生内存泄漏,生命周期随着线程的创建而创建,随着线程的结束而死亡。
+
+## Java 虚拟机栈
+
+每个 Java 方法在执行的同时会创建一个栈帧用于存储**局部变量表**、**操作数栈**、动态链接、方法出口信息等。
+
+
+
+从方法调用直至执行完成的过程,就对应着**一个栈帧在 Java 虚拟机栈中入栈和出栈的过程**。Java 方法有两种返回方式:
+
+- return 语句
+- 抛出异常
+
+不管使用哪种返回方式都会导致栈帧被弹出。
+
+可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小:
+
+```java
+java -Xss512M HackTheJava
+```
+
+该区域可能抛出以下异常:
+
+- 当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
+- 栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
+
+注意:**HotSpot 虚拟机的栈容量不可以进行动态扩展的**,所以在 HotSpot 虚拟机是不会由于虚拟机栈无法扩展而导致 OOM 的,但是如果申请时就失败,仍然会出现 OOM 异常。
+
+### 局部变量表
+
+局部变量表主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)和对象引用。
+
+### 操作数栈
+
+- 局部变量表:包含方法执行过程中的所有变量
+- 操作数栈:入栈、出栈、复制、交换、产生消费变量
+
+通过 javap 命令分析 Java 汇编指令,感受操作数栈和局部变量表的关系。
+
+定义测试类:该类中定义了一个静态方法 add()
+
+```java
+public class JVMTest {
+ public static int add(int a ,int b) {
+ int c = 0;
+ c = a + b;
+ return c;
+ }
+}
+```
+
+使用 javap 命令(javap 分析的是字节码文件)
+
+```html
+javap -verbose JVMTest
+```
+
+得到如下汇编指令:
+
+```html
+ public static int add(int, int);
+ descriptor: (II)I
+ flags: ACC_PUBLIC, ACC_STATIC
+ Code:
+ stack=2, locals=3, args_size=2
+ 0: iconst_0
+ 1: istore_2
+ 2: iload_0
+ 3: iload_1
+ 4: iadd
+ 5: istore_2
+ 6: iload_2
+ 7: ireturn
+ LineNumberTable:
+ line 5: 0
+ line 6: 2
+ line 7: 6
+```
+
+解读上述指令:
+
+- stack = 2 说明栈的深度是 2 ;
+- locals = 3 说明有 3 个本地变量 ;
+- args_size = 2 说明该方法需传入 2 个参数
+- load 指令表示入操作数栈,store 表示出操作数栈
+
+执行 add(1,2),说明局部变量表和操作数栈的关系:
+
+- 首先会将栈帧按照程序计数器指令从大到小依次入栈,栈帧按照程序计数器指令依次出栈。
+- 数据 1、2 是入参,已经存在局部变量表 0、1 位置
+- 首先执行 iconst_0,将数据 0 push 进操作数栈
+- 执行 istore_2,将数据 0 pop出操作数栈并放入局部变量表中 2 位置
+- 执行 iload_0,将 0 位置元素(数值 1) push 进操作数栈
+- 执行 iload_1,将 1 位置元素(数值 2) push 进操作数栈
+- 执行 iadd,将数值1和数值2元素 pop出操作数栈,执行加法运算后,得到结果3,将 3 push 进操作数栈
+- 执行 istore_2,将数据 3 pop出操作数栈并放入局部变量表中 2 位置
+- 执行 iload_2,将 2 位置元素(数值 3)push 进操作数栈
+- 执行 ireturn,返回操作数栈栈顶元素
+
+ 
+
+
+## 本地方法栈
+
+本地方法栈与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。
+
+本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。
+
+本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
+
+方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 。
+
+## 堆
+
+几乎所有的对象实例以及数组都在这里分配内存,是垃圾收集的主要区域,所以也被称作 **GC 堆(Garbage Collected Heap)**。
+
+现代的垃圾收集器基本都是**采用分代收集算法**,其主要的思想是针对不同类型的对象采取不同的垃圾回收算法。可以将堆分成两块:
+
+- 新生代 (Young Generation),新生代可以划分为 Eden 、From Survivor、To Survivor 空间
+- 老年代 (Old Generation)
+
+堆不需要连续内存,并且可以动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。
+
+可以通过 -Xms 和 -Xmx 这两个虚拟机参数来指定一个程序的堆内存大小,第一个参数设置初始值,第二个参数设置最大值。
+
+```java
+java -Xms1M -Xmx2M HackTheJava
+```
+
+JDK 7 版本及 JDK 7 版本之前,Hotspot 虚拟机的堆结构如下:
+
+- 新生代 (Young Generation)
+- 老年代 (Old Generation)
+- 永久代 (Permanent Generation)
+
+
+
+JDK 8 版本之后 HotSpot 虚拟机的永久代被彻底移除了,取而代之是元空间,元空间使用的是直接内存。
+
+
+
+## 堆和栈的关系
+
+Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack),其中**栈就是现在说的虚拟机栈**,或者说是虚拟机栈中局部变量表部分。引用对象、数组时,栈里定义变量保存堆中目标的首地址。
+
+栈和堆的区别:
+
+- 物理地址
+
+ 堆的物理内存分配是不连续的;
+
+ 栈的物理内存分配是连续的
+
+- 分配内存
+
+ 堆是不连续的,分配的内存是在运行期确定的,大小不固定;
+
+ 栈是连续的,分配的内存在编译器就已经确定,大小固定
+
+- 存放内容
+
+ 堆中存放的是对象和数组,关注的是数据的存储;
+
+ 栈中存放局部变量,关注的是程序方法的执行
+
+- 是否线程私有
+
+ 堆内存中的对象对所有线程可见,可被所有线程访问;
+
+ 栈内存属于某个线程私有的
+
+- 异常
+
+ 栈扩展失败,会抛出 StackOverflowError;
+
+ 堆内存不足,会抛出 OutOfMemoryError
+
+## 方法区
+
+用于存放已被加载的**类信息**、**常量**、**静态变量**、**即时编译器编译后的代码**等数据。
+
+和堆一样不需要连续的内存,并且可以动态扩展,动态扩展失败一样会抛出 OutOfMemoryError 异常。
+
+对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现。
+
+HotSpot 虚拟机把它当成永久代来进行垃圾回收。但很难确定永久代的大小,因为它受到很多因素影响,并且每次 Full GC 之后永久代的大小都会改变,所以经常会抛出 OutOfMemoryError 异常。为了更容易管理方法区,从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中。
+
+### 方法区与永久代
+
+**方法区只是一个 JVM 规范**,在不同的 JVM 上方法区的实现可能是不同的。
+
+方法区和永久代的关系类似 Java 中接口和类的关系,类实现了接口,**永久代就是 HotSpot 虚拟机对 JVM 规范中方法区的一种实现方式**。
+
+方法区是 JVM 规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久代这一说法。
+
+### 元空间与永久代
+
+**方法区只是一个 JVM 规范,永久代与元空间都是其一种实现方式**。在 JDK 1.8 之后,原来永久代的数据被分到了堆和元空间中:**元空间存储类的元信息,静态变量和常量池等则放入堆中**。
+
+元空间与永久代的最大区别在于:元空间使用本地内存,而永久代使用 JVM 的内存,元空间相比永久代具有如下优势:
+
+- 永久代存在一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。当元空间溢出时会得到如下错误: `java.lang.OutOfMemoryError: MetaSpace` 可以使用 `-XX:MaxMetaspaceSize` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。
+- 元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 `MaxPermSize` 控制了, 而由系统的实际可用空间来控制,可以加载更多的类。
+
+
+
+## 运行时常量池
+
+运行时常量池是方法区的一部分。
+
+Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池表(用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中)。
+
+运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量
+一定只有编译期才能产生,运行期间也可以将新的常量放入池中,例如 String 类的 intern()。
+
+
+
+## 字符串常量池
+
+JDK 1.7 之前**运行时常量池**逻辑包含**字符串常量池**存放在方法区。
+
+JDK 1.7 **字符串常量池被单独拿到堆**,运行时常量池剩下的内容还在方法区。
+
+JDK1.8 HotSpot 虚拟机**移除了永久代**,采用**元空间(Metaspace)** 代替方法区,这时候**字符串常量池还在堆**,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间。
+
+### String 对象创建方式
+
+创建方式 1:
+
+```java
+String str1 = "abcd";
+```
+
+创建方式 2:
+
+```java
+String str2 = new String("abcd");
+```
+
+这两种不同的创建方法是有差别的:
+
+方式 1 是在**常量池**中获取对象("abcd" 属于字符串字面量,因此编译时期会在常量池中创建一个字符串对象)。
+
+方式 2 会创建两个字符串对象(前提是常量池中还没有 "abcd" 字符串对象):
+
+- "abcd" 属于字符串字面量,因此编译时期会在常量池中创建一个字符串对象,该字符串对象指向这个 "abcd" 字符串字面量;
+- 使用 new 的方式会在堆中创建一个字符串对象。
+
+(**字符串常量"abcd"在编译期**就已经确定放入常量池,而 Java **堆上的"abcd"是在运行期**初始化阶段才确定)。
+
+**str1 指向常量池中的 “abcd” 对象,而 str2 指向堆中的字符串对象。**
+
+### String 的 intern() 方法
+
+String 的 intern() 是一个 **Native 方法**,当调用 intern() 方法时:
+
+- 如果运行时常量池中已经包含一个等于该 String 对象内容的字符串,则返回常量池中该字符串的引用。
+- 如果没有等于该 String 对象的字符串,JDK1.7 之前(不包含 1.7)是在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用,**JDK1.7 以及之后的处理方式是对于存在堆中的对象,在常量池中直接保存对象的引用,而不会重新创建对象。**
+
+```java
+String s3 = new String("1") + new String("1");
+s3.intern();
+String s4 = "11";
+System.out.println(s3 == s4);
+```
+
+JDK 6 输出结果:
+
+```html
+false
+```
+
+JDK 8 输出结果:
+
+```html
+true
+```
+
+补充:[String 的 intern() 方法详解](https://www.cnblogs.com/wxgblogs/p/5635099.html)
+
+### 字符串拼接问题
+
+```java
+String str1 = "hello";
+String str2 = "world";
+
+String str3 = "hello" + "world";//常量池中的对象
+String str4 = str1 + str2; //在堆上创建的新的对象
+String str5 = "helloworld";//常量池中的对象
+
+System.out.println(str3 == str4);
+System.out.println(str3 == str5);
+System.out.println(str4 == str5);
+```
+
+输出结果如下:
+
+```html
+false
+true
+false
+```
+
+str1、str2 是从字符串常量池中获取的对象。
+
+对于 str3,字符串 "hello" 和字符串 "world" 相加有后得到 "helloworld",在字符串常量池中创建 "helloworld" 对象。
+
+对于 str4,str1+str2 则会在堆中创建新的 "helloworld" 对象。
+
+对于 str5,字符串常量池已有 "helloworld" 对象,str5 直接引用该对象。
+
+
+
+所以,尽量避免多个字符串拼接,因为这样会重新创建新的对象。如果需要改变字符串的话,可以使用 StringBuilder 或者 StringBuffer。
+
+## 直接内存
+
+在 JDK 1.4 中新引入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在堆内存和堆外内存来回拷贝数据。
+
+
+
+# Hotpot 虚拟机对象
+
+## 对象的内存布局
+
+在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:
+
+- 对象头
+- 实例数据
+- 对齐填充
+
+
+
+### 对象头
+
+Hotspot 虚拟机的对象头包括两部分信息:
+
+一部分用于存储对象自身的运行时数据(哈希码、GC分代年龄、锁状态标志等等);
+
+另一部分是类型指针,即对象指向它的**类元数据的指针**,虚拟机通过这个指针来**确定这个对象是哪个类的实例**。
+
+### 实例数据
+
+实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。
+
+### 对齐填充
+
+对齐填充部分不是必然存在的,也没有什么特别的含义,仅仅起**占位**作用。
+
+因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
+
+
+
+## 对象的创建
+
+Java 对象的创建过程分为以下5步:
+
+- 类加载检查
+- 分配内存
+- 初始化零值
+- 设置对象头
+- 执行 \ 方法
+
+### 1. 类加载检查
+
+虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的**符号引用**,
+并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。
+如果没有,那必须先执行相应的类加载过程。
+
+### 2. 分配内存
+
+在类加载检查通过后,接下来虚拟机将为新生对象分配内存。
+对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表” 两种,选择那种分配方式由 Java 堆是否规整决定。
+
+ Java 堆内存是否规整,则取决于 GC 收集器的算法是“标记-清除”,还是“标记-整理”(也称作“标记-压缩”),值得注意的是,“复制算法”内存也是规整的。
+
+#### 两种内存分配方式
+
+##### 指针碰撞
+
+- 原理:用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界值指针,只需要向着没用过的内存方向将指针移动一段与对象大小相等的距离。
+- 适用场景:堆内存规整(即没有内存碎片)的情况
+- GC(Garbage Collection)收集器:Serial、ParNew
+
+##### 空闲列表
+
+- 原理:虚拟机会维护一个列表,在该列表中记录哪些内存块是可用的,在分配的时候,找一块足够大的内存块划分给对象示例,然后更新列表记录
+- 适用场景:堆内存规整
+- GC(Garbage Collection)收集器:CMS
+
+#### 内存分配并发问题
+
+在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,
+作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:
+
+- CAS+失败重试: CAS 是乐观锁的一种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。
+- TLAB:每一个线程预先在Java堆中分配一块内存,称为**本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)**。哪个线程要分配内存,就在哪个线程的 TLAB 上分配,只有 TLAB 用完并分配新的 TLAB 时,才采用上述的 CAS 进行内存分配。
+
+### 3. 初始化零值
+
+内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作**保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用**,程序能访问到这些字段的数据类型所对应的零值。
+
+### 4. 设置对象头
+
+初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。
+另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
+
+### 5. 执行 init 方法
+
+在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,**\ 方法还没有执行,所有的字段都还为零**。所以一般来说,执行 new 指令之后会接着执行 \ 方法,把**对象按照程序员的意愿进行初始化**,这样一个真正可用的对象才算完全产生出来。
+
+
+
+## 对象的访问定位
+
+建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。
+对象的访问方式视虚拟机的实现而定,目前主流的访问方式有两种:使用句柄、直接指针。
+
+### 使用句柄
+
+如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是**对象的句柄地址**,而句柄中包含了对象实例数据与类型数据各自的具体地址信息 。
+
+
+
+### 直接指针
+
+如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是**对象的地址**。
+
+
+
+这两种对象访问方式各有优势:
+
+- 使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 **reference 本身不需要修改**。
+- 使用直接指针访问方式最大的好处就是**速度快**,它节省了一次指针定位的时间开销。
\ No newline at end of file
diff --git a/docs/JavaBasics/2_String.md b/docs/JVM/2_String.md
similarity index 100%
rename from docs/JavaBasics/2_String.md
rename to docs/JVM/2_String.md
diff --git "a/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md" "b/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md"
new file mode 100644
index 00000000..020c1779
--- /dev/null
+++ "b/docs/JVM/2_\345\236\203\345\234\276\346\224\266\351\233\206.md"
@@ -0,0 +1,425 @@
+# 垃圾收集 (Garbage Collection,GC)
+
+垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。
+
+## 判断一个对象是否可被回收
+
+堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。
+
+### 引用计数算法
+
+为对象添加一个引用计数器,当对象增加一个引用时计数器加 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 对象无法被回收。
+
+- 优点:执行效率高,程序执行受影响较小。
+- 缺点:无法检测出循环引用的情况,引起内存泄漏。
+
+### 可达性分析算法
+
+通过判断对象的引用链是否可达来决定对象是否可以被回收。
+
+以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。
+
+
+
+**Java 虚拟机使用可达性分析算法来判断对象是否可被回收**,GC Roots 一般包含以下几种:
+
+- 虚拟机栈中局部变量表中引用的对象(栈帧中的本地方法变量表)
+- 本地方法栈中 JNI(Native方法) 中引用的对象
+- 方法区中类静态属性引用的对象
+- 方法区中的常量引用的对象
+- 活跃线程的引用对象
+
+
+
+### 方法区的回收
+
+因为方法区主要存放永久代对象,而永久代对象的回收率比新生代低很多,所以在方法区上进行回收性价比不高。
+
+主要是对常量池的回收和对类的卸载。
+
+为了避免内存溢出,在大量使用反射和动态代理的场景都需要虚拟机具备类卸载功能。
+
+类的卸载条件很多,需要满足以下三个条件,并且满足了条件也不一定会被卸载:
+
+- 该类所有的实例都已经被回收,此时堆中不存在该类的任何实例。
+- 加载该类的 ClassLoader 已经被回收。
+- 该类对应的 Class 对象没有在任何地方被引用,也就无法在任何地方通过反射访问该类方法。
+
+
+
+### finalize()
+
+类似 C++ 的析构函数(注意:只是类似,C++ 析构函数调用确定,而 finalize() 方法是不确定的),用于关闭外部资源。但是 try-finally 等方式可以做得更好,并且该方法运行代价很高,不确定性大,无法保证各个对象的调用顺序,因此最好不要使用。
+
+当垃圾回收器要宣告一个对象死亡时,至少要经历两次标记过程。
+
+如果对象在进行可达性分析以后,没有与GC Root 直接相连接的引用量,就会被**第一次标记**,并且判断是否执行 finalize() 方法;
+
+如果这个对象覆盖了 finalize() 方法,并且未被引用,就会被放置于 F-Queue 队列,稍后由虚拟机创建的一个低优先级的 finalize() 线程去执行**触发 finalize() 方法**,在该方法中让对象重新被引用,从而实现自救。但是该线程的优先级比较低,执行过程随时可能会被终止。此外,自救只能进行一次,如果回收的对象之前调用了 finalize() 方法自救,后面回收时不会再调用该方法。
+
+
+
+### 引用类型
+
+无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否可达,判定对象是否可被回收都与引用有关。
+
+JDK1.2 之前,Java 中引用定义:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用。
+
+JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)。
+
+#### 强引用 (Strong Reference)
+
+被强引用关联的对象不会被回收。
+
+使用 new 一个新对象的方式来创建强引用。
+
+```java
+Object obj = new Object();
+```
+
+当内存空间不足,JVM 抛出 OOM Error 终止程序也不会回收具有强引用的对象,只有通过将对象设置为 null 来弱化引用,才能使其被回收。
+
+#### 软引用 (Soft Reference)
+
+表示对象处在**有用但非必须**的状态。
+
+被软引用关联的对象只有在内存不够的情况下才会被回收。可以用来实现内存敏感的高速缓存。
+
+软引用可以和一个引用队列 ReferenceQueue 联合使用,如果软引用所引用的对象被垃圾回收,JVM 就会把这个软引用加入到与之关联的引用队列中。如果一个弱引用对象本身在引用队列中,就说明该引用对象所指向的对象被回收了。
+
+使用 SoftReference 类来创建软引用。
+
+```java
+Object obj = new Object();
+SoftReference