From 5608f55f361a934b261a6d25e02da0950bd1c28a Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 6 Mar 2018 11:17:27 +0800 Subject: [PATCH] auto commit --- notes/HTTP.md | 50 +++--- notes/JVM.md | 30 ++-- notes/Java IO.md | 2 +- notes/Java 基础.md | 38 ++-- notes/Java 容器.md | 4 +- notes/Java 并发.md | 50 +++--- notes/Leetcode 题解.md | 366 +++++++++++++++++++-------------------- notes/Linux.md | 40 ++--- notes/MySQL.md | 42 ++--- notes/SQL 语法.md | 70 ++++---- notes/代码可读性.md | 6 +- notes/剑指 offer 题解.md | 94 +++++----- notes/算法.md | 26 +-- notes/计算机操作系统.md | 14 +- notes/计算机网络.md | 58 +++---- notes/设计模式.md | 180 +++++++++---------- notes/重构.md | 2 +- notes/面向对象思想.md | 20 +-- 18 files changed, 546 insertions(+), 546 deletions(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index 7a266c09..a4a435d9 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -61,11 +61,11 @@ URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基 ## 请求和响应报文 -**请求报文** +**请求报文**

-**响应报文** +**响应报文**

@@ -142,43 +142,43 @@ TRACE 一般不会使用,并且它容易受到 XST 攻击(Cross-Site Tracing ## 2XX 成功 -- **200 OK** +- **200 OK** -- **204 No Content**:请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 +- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 -- **206 Partial Content** +- **206 Partial Content** ## 3XX 重定向 -- **301 Moved Permanently**:永久性重定向 +- **301 Moved Permanently** :永久性重定向 -- **302 Found**:临时性重定向 +- **302 Found** :临时性重定向 -- **303 See Other** +- **303 See Other** - 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会 在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 -- **304 Not Modified**:如果请求报文首部包含一些条件,例如:If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since,但是不满足条件,则服务器会返回 304 状态码。 +- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since,但是不满足条件,则服务器会返回 304 状态码。 -- **307 Temporary Redirect**:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 +- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 ## 4XX 客户端错误 -- **400 Bad Request**:请求报文中存在语法错误 +- **400 Bad Request** :请求报文中存在语法错误 -- **401 Unauthorized**:该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。如果之前已进行过一次请求,则表示用户认证失败。 +- **401 Unauthorized** :该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。如果之前已进行过一次请求,则表示用户认证失败。

-- **403 Forbidden**:请求被拒绝,服务器端没有必要给出拒绝的详细理由。 +- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。 -- **404 Not Found** +- **404 Not Found** ## 5XX 服务器错误 -- **500 Internal Server Error**:服务器正在执行请求时发生错误 +- **500 Internal Server Error** :服务器正在执行请求时发生错误 -- **503 Service Unavilable**:该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavilable** :该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # HTTP 首部 @@ -274,15 +274,15 @@ Set-Cookie 字段有以下属性: | Secure | 仅在 HTTPS 安全通信时才会发送 Cookie | | HttpOnly | 加以限制,使 Cookie 不能被 JavaScript 脚本访问 | -**Session 和 Cookie 区别** +**Session 和 Cookie 区别** Session 是服务器用来跟踪用户的一种手段,每个 Session 都有一个唯一标识:Session ID。当服务器创建了一个 Session 时,给客户端发送的响应报文就包含了 Set-Cookie 字段,其中有一个名为 sid 的键值对,这个键值对就是 Session ID。客户端收到后就把 Cookie 保存在浏览器中,并且之后发送的请求报文都包含 Session ID。HTTP 就是通过 Session 和 Cookie 这两种方式一起合作来实现跟踪用户状态的,Session 用于服务器端,Cookie 用于客户端。 -**浏览器禁用 Cookie 的情况** +**浏览器禁用 Cookie 的情况** 会使用 URL 重写技术,在 URL 后面加上 sid=xxx 。 -**使用 Cookie 实现用户名和密码的自动填写** +**使用 Cookie 实现用户名和密码的自动填写** 网站脚本会自动从 Cookie 中读取用户名和密码,从而实现自动填写。 @@ -296,7 +296,7 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。 ## 持久连接 -当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。**持久连接** 只需要进行一次 TCP 连接就能进行多次 HTTP 通信。HTTP/1.1 开始,所有的连接默认都是持久连接。 +当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。 **持久连接** 只需要进行一次 TCP 连接就能进行多次 HTTP 通信。HTTP/1.1 开始,所有的连接默认都是持久连接。

@@ -340,7 +340,7 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。 ## 通信数据转发 -**代理** +**代理** 代理服务器接受客户端的请求,并且转发给其它服务器。 @@ -350,13 +350,13 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。

-**网关** +**网关** 与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。

-**隧道** +**隧道** 使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。 @@ -378,13 +378,13 @@ HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Socket Layer)通信 对称密钥加密的缺点:无法安全传输密钥;公开密钥加密的缺点:相对来说更耗时。 -HTTPs 采用 **混合的加密机制**,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥) +HTTPs 采用 **混合的加密机制** ,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥)

## 认证 -通过使用 **证书** 来对通信方进行认证。证书中有公开密钥数据,如果可以验证公开密钥的确属于通信方的,那么就可以确定通信方是可靠的。 +通过使用 **证书** 来对通信方进行认证。证书中有公开密钥数据,如果可以验证公开密钥的确属于通信方的,那么就可以确定通信方是可靠的。 数字证书认证机构(CA,Certificate Authority)可以对其颁发的公开密钥证书对其进行验证。 diff --git a/notes/JVM.md b/notes/JVM.md index 1aa8d280..50fca048 100644 --- a/notes/JVM.md +++ b/notes/JVM.md @@ -371,13 +371,13 @@ Region 不可能是孤立的,一个对象分配在某个 Region 中,可以 | 收集器 | 串行、并行 or 并发 | 新生代 / 老年代 | 算法 | 目标 | 适用场景 | | --- | --- | --- | --- | --- | --- | -| **Serial** | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单 CPU 环境下的 Client 模式 | -| **Serial Old** | 串行 | 老年代 | 标记 - 整理 | 响应速度优先 | 单 CPU 环境下的 Client 模式、CMS 的后备预案 | -| **ParNew** | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多 CPU 环境时在 Server 模式下与 CMS 配合 | -| **Parallel Scavenge** | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 | -| **Parallel Old** | 并行 | 老年代 | 标记 - 整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 | -| **CMS** | 并发 | 老年代 | 标记 - 清除 | 响应速度优先 | 集中在互联网站或 B/S 系统服务端上的 Java 应用 | -| **G1** | 并发 | both | 标记 - 整理 + 复制算法 | 响应速度优先 | 面向服务端应用,将来替换 CMS | +| **Serial** | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单 CPU 环境下的 Client 模式 | +| **Serial Old** | 串行 | 老年代 | 标记 - 整理 | 响应速度优先 | 单 CPU 环境下的 Client 模式、CMS 的后备预案 | +| **ParNew** | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多 CPU 环境时在 Server 模式下与 CMS 配合 | +| **Parallel Scavenge** | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 | +| **Parallel Old** | 并行 | 老年代 | 标记 - 整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 | +| **CMS** | 并发 | 老年代 | 标记 - 清除 | 响应速度优先 | 集中在互联网站或 B/S 系统服务端上的 Java 应用 | +| **G1** | 并发 | both | 标记 - 整理 + 复制算法 | 响应速度优先 | 面向服务端应用,将来替换 CMS | ## 4. 内存分配与回收策略 @@ -437,11 +437,11 @@ JVM 为对象定义年龄计数器,经过 Minor GC 依然存活且被 Survivor 包括以下 7 个阶段: -- **加载(Loading)** -- **验证(Verification)** -- **准备(Preparation)** -- **解析(Resolution)** -- **初始化(Initialization)** +- **加载(Loading)** +- **验证(Verification)** +- **准备(Preparation)** +- **解析(Resolution)** +- **初始化(Initialization)** - 使用(Using) - 卸载(Unloading) @@ -612,15 +612,15 @@ public static void main(String[] args) {

-**工作过程** +**工作过程** 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载,而是把这个请求委派给父类加载器,每一个层次的加载器都是如此,依次递归,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成此加载请求(它搜索范围中没有找到所需类)时,子加载器才会尝试自己加载。 -**好处** +**好处** 使用双亲委派模型来组织类加载器之间的关系,使得 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object,它存放再 rt.jar 中,无论哪个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此 Object 类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型,由各个类加载器自行加载的话,如果用户编写了一个称为`java.lang.Object 的类,并放在程序的 ClassPath 中,那系统中将会出现多个不同的 Object 类,程序将变得一片混乱。如果开发者尝试编写一个与 rt.jar 类库中已有类重名的 Java 类,将会发现可以正常编译,但是永远无法被加载运行。 -**实现** +**实现** ```java protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException{ diff --git a/notes/Java IO.md b/notes/Java IO.md index e55e45c2..f3bf417f 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -98,7 +98,7 @@ GBK 编码中,中文占 2 个字节,英文占 1 个字节;UTF-8 编码中 transient 关键字可以使一些属性不会被序列化。 -**ArrayList 序列化和反序列化的实现**:ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 +**ArrayList 序列化和反序列化的实现** :ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。 ``` private transient Object[] elementData; diff --git a/notes/Java 基础.md b/notes/Java 基础.md index 942b57ea..dbd32c9f 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -33,39 +33,39 @@ ## 1. final -**数据** +**数据** 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 对于基本类型,final 使数值不变;对于引用对象,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。 -**方法** +**方法** 声明方法不能被子类覆盖。 private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是重载了。 -**类** +**类** 声明类不允许被继承。 ## 2. static -**变量** +**变量** 静态变量在内存中只存在一份,只在类第一次实例化时初始化一次,同时类所有的实例都共享静态变量,可以直接通过类名来访问它。 但是实例变量则不同,它是伴随着实例的,每创建一个实例就会产生一个实例变量,它与该实例同生共死。 -**方法** +**方法** 静态方法在类加载的时候就存在了,它不依赖于任何实例,所以 static 方法必须实现,也就是说他不能是抽象方法 abstract。 -**静态语句块** +**静态语句块** 静态语句块和静态变量一样在类第一次实例化时运行一次。 -**初始化顺序** +**初始化顺序** 静态数据优先于其它数据的初始化,静态变量和静态语句块哪个先运行取决于它们在代码中的顺序。 @@ -127,13 +127,13 @@ public InitialOrderTest() { ## 2. clone() -**浅拷贝** +**浅拷贝** 引用类型引用的是同一个对象,clone() 方法默认就是浅拷贝实现。

-**深拷贝** +**深拷贝** 可以使用序列化实现。 @@ -213,11 +213,11 @@ public class Subclass extends Superclass { ## 1. String, StringBuffer and StringBuilder -**是否可变** +**是否可变** String 不可变,StringBuffer 和 StringBuilder 可变。 -**是否线程安全** +**是否线程安全** String 不可变,因此是线程安全的。 @@ -227,21 +227,21 @@ StringBuilder 不是线程安全的;StringBuffer 是线程安全的,使用 s ## 2. String 不可变的原因 -**可以缓存 hash 值** +**可以缓存 hash 值** 因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 等。不可变的特性可以使得 hash 值也不可变,因此就只需要进行一次计算。 -**String Pool 的需要** +**String Pool 的需要** 如果 String 已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

-**安全性** +**安全性** String 经常作为参数,例如网络连接参数等,在作为网络连接参数的情况下,如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。String 不可变性可以保证参数不可变。 -**线程安全** +**线程安全** String 不可变性天生具备线程安全,可以在多个线程中使用。 @@ -363,13 +363,13 @@ public static void main(java.lang.String[]); # 反射 -每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 +每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName('com.mysql.jdbc.Driver.class') 这种方式来控制类的加载,该方法会返回一个 Class 对象。 反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。 -Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库包含了 **Field**、**Method** 以及 **Constructor** 类。可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段,可以使用 invoke() 方法调用与 Method 对象关联的方法,可以用 Constructor 创建新的对象。 +Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库包含了 **Field** 、**Method** 以及 **Constructor** 类。可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段,可以使用 invoke() 方法调用与 Method 对象关联的方法,可以用 Constructor 创建新的对象。 IDE 使用反射机制获取类的信息,在使用一个类的对象时,能够把类的字段、方法和构造函数等信息列出来供用户选择。 @@ -377,9 +377,9 @@ IDE 使用反射机制获取类的信息,在使用一个类的对象时,能 # 异常 -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种:**Error** 和 **Exception**,其中 Error 用来表示编译时系统错误。 +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**,其中 Error 用来表示编译时系统错误。 -Exception 分为两种:**受检异常** 和 **非受检异常**。受检异常需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。 +Exception 分为两种: **受检异常** 和 **非受检异常**。受检异常需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。

diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 5c368ac5..2d29ca52 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -214,14 +214,14 @@ private void writeObject(java.io.ObjectOutputStream s) } ``` -**和 Vector 的区别** +**和 Vector 的区别** 1. Vector 和 ArrayList 几乎是完全相同的,唯一的区别在于 Vector 是同步的,因此开销就比 ArrayList 要大,访问要慢。最好使用 ArrayList 而不是 Vector,因为同步完全可以由程序员自己来控制; 2. Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。 为了使用线程安全的 ArrayList,可以使用 Collections.synchronizedList(new ArrayList<>()); 返回一个线程安全的 ArrayList,也可以使用 concurrent 并发包下的 CopyOnWriteArrayList 类; -**和 LinkedList 的区别** +**和 LinkedList 的区别** 1. ArrayList 基于动态数组实现,LinkedList 基于双向循环链表实现; 2. ArrayList 支持随机访问,LinkedList 不支持; diff --git a/notes/Java 并发.md b/notes/Java 并发.md index dfe524b4..3249d909 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -122,7 +122,7 @@ for(int i = 0; i < 5; i++) { ## 1. sleep() -**Thread.sleep(millisec)** 方法会休眠当前正在执行的线程,millisec 单位为毫秒。也可以使用 TimeUnit.TILLISECONDS.sleep(millisec)。 +**Thread.sleep(millisec)** 方法会休眠当前正在执行的线程,millisec 单位为毫秒。也可以使用 TimeUnit.TILLISECONDS.sleep(millisec)。 sleep() 可能会抛出 InterruptedException。因为异常不能跨线程传播回 main() 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。 @@ -140,7 +140,7 @@ public void run() { ## 2. yield() -对静态方法 **Thread.yield()** 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。 +对静态方法 **Thread.yield()** 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。 ```java public void run() { @@ -151,13 +151,13 @@ public void run() { ## 3. join() -在线程中调用另一个线程的 **join()** 方法,会将当前线程挂起,直到目标线程结束。 +在线程中调用另一个线程的 **join()** 方法,会将当前线程挂起,直到目标线程结束。 可以加一个超时参数。 ## 4. deamon -后台线程(**deamon**)是程序运行时在后台提供服务的线程,并不属于程序中不可或缺的部分。 +后台线程( **deamon** )是程序运行时在后台提供服务的线程,并不属于程序中不可或缺的部分。 当所有非后台线程结束时,程序也就终止,同时会杀死所有后台线程。 @@ -167,14 +167,14 @@ main() 属于非后台线程。 # 线程之间的协作 -- **线程通信**:保证线程以一定的顺序执行; -- **线程同步**:保证线程对临界资源的互斥访问。 +- **线程通信** :保证线程以一定的顺序执行; +- **线程同步** :保证线程对临界资源的互斥访问。 线程通信往往是基于线程同步的基础上完成的,因此很多线程通信问题也是线程同步问题。 ## 1. 线程通信 -**wait()、notify() 和 notifyAll()** 三者实现了线程之间的通信。 +**wait()、notify() 和 notifyAll()** 三者实现了线程之间的通信。 wait() 会在等待时将线程挂起,而不是忙等待,并且只有在 notify() 或者 notifyAll() 到达时才唤醒。 @@ -198,7 +198,7 @@ public synchronized void before() { } ``` -**wait() 和 sleep() 的区别** +**wait() 和 sleep() 的区别** 1. wait() 是 Object 类的方法,而 sleep() 是 Thread 的静态方法; 2. wait() 会放弃锁,而 sleep() 不会。 @@ -209,7 +209,7 @@ public synchronized void before() { ### 2.1 synchronized -**同步一个方法** +**同步一个方法** 使多个线程不能同时访问该方法。 @@ -219,7 +219,7 @@ public synchronized void func(String name) { } ``` -**同步一个代码块** +**同步一个代码块** ```java public void func(String name) { @@ -246,12 +246,12 @@ public int func(int value) { java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现: -- **FIFO 队列**:LinkedBlockingQueue、ArrayListBlockingQueue(固定长度) -- **优先级队列**:PriorityBlockingQueue +- **FIFO 队列** :LinkedBlockingQueue、ArrayListBlockingQueue(固定长度) +- **优先级队列** :PriorityBlockingQueue 提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将一直阻塞到队列中有内容,如果队列为满 put() 将阻塞到队列有空闲位置。它们响应中断,当收到中断请求的时候会抛出 InterruptedException,从而提前结束阻塞状态。 -**使用 BlockingQueue 实现生产者消费者问题** +**使用 BlockingQueue 实现生产者消费者问题** ```java // 生产者 @@ -340,12 +340,12 @@ Producer4 is consuming product made by Consumer4... JDK 从 1.5 开始在 Thread 类中增添了 State 枚举,包含以下六种状态: -1. **NEW**(新建) -2. **RUNNABLE**(当线程正在运行或者已经就绪正等待 CPU 时间片) -3. **BLOCKED**(阻塞,线程在等待获取对象同步锁) -4. **Waiting**(调用不带超时的 wait() 或 join()) -5. **TIMED_WAITING**(调用 sleep()、带超时的 wait() 或者 join()) -6. **TERMINATED**(死亡) +1. **NEW** (新建) +2. **RUNNABLE** (当线程正在运行或者已经就绪正等待 CPU 时间片) +3. **BLOCKED** (阻塞,线程在等待获取对象同步锁) +4. **Waiting** (调用不带超时的 wait() 或 join()) +5. **TIMED_WAITING** (调用 sleep()、带超时的 wait() 或者 join()) +6. **TERMINATED** (死亡)

@@ -364,21 +364,21 @@ JDK 从 1.5 开始在 Thread 类中增添了 State 枚举,包含以下六种 使用中断机制即可终止阻塞的线程。 -使用 **interrupt()** 方法来中断某个线程,它会设置线程的中断状态。Object.wait(), Thread.join() 和 Thread.sleep() 三种方法在收到中断请求的时候会清除中断状态,并抛出 InterruptedException。 +使用 **interrupt()** 方法来中断某个线程,它会设置线程的中断状态。Object.wait(), Thread.join() 和 Thread.sleep() 三种方法在收到中断请求的时候会清除中断状态,并抛出 InterruptedException。 应当捕获这个 InterruptedException 异常,从而做一些清理资源的操作。 -**不可中断的阻塞** +**不可中断的阻塞** 不能中断 I/O 阻塞和 synchronized 锁阻塞。 -**Executor 的中断操作** +**Executor 的中断操作** Executor 避免对 Thread 对象的直接操作,但是使用 interrupt() 方法必须持有 Thread 对象。Executor 使用 shutdownNow() 方法来中断所有它里面的所有线程,shutdownNow() 方法会发送 interrupt() 调用给所有线程。 如果只想中断一个线程,那么使用 Executor 的 submit() 而不是 executor() 来启动线程,就可以持有线程的上下文。submit() 将返回一个泛型 Futrue,可以在它之上调用 cancel(),如果将 true 传递给 cancel(),那么它将会发送 interrupt() 调用给特定的线程。 -**检查中断** +**检查中断** 通过中断的方法来终止线程,需要线程进入阻塞状态才能终止。如果编写的 run() 方法循环条件为 true,但是该线程不发生阻塞,那么线程就永远无法终止。 @@ -390,9 +390,9 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样 对于除 long 和 double 之外的基本类型变量的读写,可以看成是具有原子性的,以不可分割的步骤操作内存。 -JVM 将 64 位变量(long 和 double)的读写当做两个分离的 32 位操作来执行,在两个操作之间可能会发生上下文切换,因此不具有原子性。可以使用 **volatile** 关键字来定义 long 和 double 变量,从而获得原子性。 +JVM 将 64 位变量(long 和 double)的读写当做两个分离的 32 位操作来执行,在两个操作之间可能会发生上下文切换,因此不具有原子性。可以使用 **volatile** 关键字来定义 long 和 double 变量,从而获得原子性。 -**AtomicInteger、AtomicLong、AtomicReference** 等特殊的原子性变量类提供了下面形式的原子性条件更新语句,使得比较和更新这两个操作能够不可分割地执行。 +**AtomicInteger、AtomicLong、AtomicReference** 等特殊的原子性变量类提供了下面形式的原子性条件更新语句,使得比较和更新这两个操作能够不可分割地执行。 ```java boolean compareAndSet(expectedValue, updateValue); diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index bb03fc94..b4e1305b 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -76,7 +76,7 @@ public int search(int key, int[] arr) { } ``` -**求开方** +**求开方** [Leetcode : 69. Sqrt(x) (Easy)](https://leetcode.com/problems/sqrtx/description/) @@ -97,7 +97,7 @@ public int mySqrt(int x) { } ``` -**摆硬币** +**摆硬币** [Leetcode : 441. Arranging Coins (Easy)](https://leetcode.com/problems/arranging-coins/description/) @@ -144,7 +144,7 @@ public int arrangeCoins(int n) { } ``` -**有序数组的 Single Element** +**有序数组的 Single Element** [Leetcode : 540. Single Element in a Sorted Array (Medium)](https://leetcode.com/problems/single-element-in-a-sorted-array/description/) @@ -167,7 +167,7 @@ public int singleNonDuplicate(int[] nums) { 贪心思想保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。 -**分配饼干** +**分配饼干** [Leetcode : 455. Assign Cookies (Easy)](https://leetcode.com/problems/assign-cookies/description/) @@ -190,7 +190,7 @@ public int findContentChildren(int[] g, int[] s) { } ``` -**投飞镖刺破气球** +**投飞镖刺破气球** [Leetcode : 452. Minimum Number of Arrows to Burst Balloons (Medium)](https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/description/) @@ -226,7 +226,7 @@ public int findMinArrowShots(int[][] points) { } ``` -**股票的最大收益** +**股票的最大收益** [Leetcode : 122. Best Time to Buy and Sell Stock II (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/description/) @@ -244,7 +244,7 @@ public int maxProfit(int[] prices) { } ``` -**种植花朵** +**种植花朵** [Leetcode : 605. Can Place Flowers (Easy)](https://leetcode.com/problems/can-place-flowers/description/) @@ -271,13 +271,13 @@ public boolean canPlaceFlowers(int[] flowerbed, int n) { } ``` -**修改一个数成为非递减数组** +**修改一个数成为非递减数组** [Leetcode : 665. Non-decreasing Array (Easy)](https://leetcode.com/problems/non-decreasing-array/description/) 题目描述:判断一个数组能不能只修改一个数就成为非递减数组。 -在出现 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] 才行。 +在出现 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) { @@ -293,7 +293,7 @@ public boolean checkPossibility(int[] nums) { } ``` -**判断是否为子串** +**判断是否为子串** [Leetcode : 392. Is Subsequence (Medium)](https://leetcode.com/problems/is-subsequence/description/) @@ -312,7 +312,7 @@ public boolean isSubsequence(String s, String t) { } ``` -**分隔字符串使同种字符出现在一起** +**分隔字符串使同种字符出现在一起** [Leetcode : 763. Partition Labels (Medium)](https://leetcode.com/problems/partition-labels/description/) @@ -345,7 +345,7 @@ public List partitionLabels(String S) { } ``` -**根据身高和序号重组队列** +**根据身高和序号重组队列** [Leetcode : 406. Queue Reconstruction by Height(Medium)](https://leetcode.com/problems/queue-reconstruction-by-height/description/) @@ -393,7 +393,7 @@ public int[][] reconstructQueue(int[][] people) { 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 -**从一个已经排序的数组中查找出两个数,使它们的和为 0** +**从一个已经排序的数组中查找出两个数,使它们的和为 0** [Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/) @@ -414,7 +414,7 @@ public int[] twoSum(int[] numbers, int target) { } ``` -**反转字符串中的元音字符** +**反转字符串中的元音字符** [Leetcode : 345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/) @@ -447,7 +447,7 @@ public String reverseVowels(String s) { } ``` -**两数平方和** +**两数平方和** [Leetcode : 633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/) @@ -466,7 +466,7 @@ public boolean judgeSquareSum(int c) { } ``` -**回文字符串** +**回文字符串** [Leetcode : 680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/) @@ -496,7 +496,7 @@ private boolean isPalindrome(String s, int l, int r){ } ``` -**归并两个有序数组** +**归并两个有序数组** [Leetcode : 88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/) @@ -516,7 +516,7 @@ public void merge(int[] nums1, int m, int[] nums2, int n) { } ``` -**判断链表是否存在环** +**判断链表是否存在环** [Leetcode : 141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/) @@ -536,7 +536,7 @@ public boolean hasCycle(ListNode head) { } ``` -**最长子序列** +**最长子序列** [Leetcode : 524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/) @@ -572,19 +572,19 @@ public String findLongestWord(String s, List d) { ### 快速选择 -一般用于求解 **Kth Element** 问题,可以在 O(n) 时间复杂度,O(1) 空间复杂度完成求解工作。 +一般用于求解 **Kth Element** 问题,可以在 O(n) 时间复杂度,O(1) 空间复杂度完成求解工作。 与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为 O(n2)。 ### 堆排序 -堆排序用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。当然它也可以用于求解 Kth Element 问题,因为最后出堆的那个元素就是 Kth Element。快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 +堆排序用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。当然它也可以用于求解 Kth Element 问题,因为最后出堆的那个元素就是 Kth Element。快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 -**Kth Element** +**Kth Element** [Leetocde : 215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) -**排序**:时间复杂度 O(nlgn),空间复杂度 O(1) 解法 +**排序** :时间复杂度 O(nlgn),空间复杂度 O(1) 解法 ```java public int findKthLargest(int[] nums, int k) { @@ -594,7 +594,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**堆排序**:时间复杂度 O(nlgk),空间复杂度 O(k) +**堆排序** :时间复杂度 O(nlgk),空间复杂度 O(k) ```java public int findKthLargest(int[] nums, int k) { @@ -609,7 +609,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**快速选择**:时间复杂度 O(n),空间复杂度 O(1) +**快速选择** :时间复杂度 O(n),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) { @@ -658,7 +658,7 @@ public int findKthLargest(int[] nums, int k) { ### 桶排序 -**找出出现频率最多的 k 个数** +**找出出现频率最多的 k 个数** [Leetcode : 347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) @@ -701,14 +701,14 @@ public List topKFrequent(int[] nums, int k) { 反复从新节点出发进行上述的遍历操作。 -可以看到,每一轮遍历的节点都与根节点路径长度相同。设 di 表示第 i 个节点与根节点的路径长度,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di<=dj。利用这个结论,可以求解最短路径 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径,如果继续遍历,之后再遍历到目的节点,所经过的路径就不是最短路径。 +可以看到,每一轮遍历的节点都与根节点路径长度相同。设 di 表示第 i 个节点与根节点的路径长度,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di<=dj。利用这个结论,可以求解最短路径 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径,如果继续遍历,之后再遍历到目的节点,所经过的路径就不是最短路径。 在程序实现 BFS 时需要考虑以下问题: - 队列:用来存储每一轮遍历的节点 - 标记:对于遍历过得节点,应该将它标记,防止重复遍历; -**计算在网格中从原点到特定点的最短路径长度** +**计算在网格中从原点到特定点的最短路径长度** ```html [[1,1,0,1], @@ -754,14 +754,14 @@ private class Position { 广度优先搜索一层一层遍历,每一层遍历到的所有新节点,要用队列先存储起来以备下一层遍历的时候再遍历;而深度优先搜索在遍历到一个新节点时立马对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 -从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 +从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 在程序实现 DFS 时需要考虑以下问题: - 栈:用栈来保存当前节点信息,当遍历新节点返回时能够继续遍历当前节点。也可以使用递归栈。 - 标记:和 BFS 一样同样需要对已经遍历过得节点进行标记。 -**查找最大的连通面积** +**查找最大的连通面积** [Leetcode : 695. Max Area of Island (Easy)](https://leetcode.com/problems/max-area-of-island/description/) @@ -797,7 +797,7 @@ private int dfs(int[][] grid, int i, int j){ } ``` -**图的连通分量** +**图的连通分量** [Leetcode : 547. Friend Circles (Medium)](https://leetcode.com/problems/friend-circles/description/) @@ -836,7 +836,7 @@ private void dfs(int[][] M, int i, boolean[] hasFind) { } ``` -**矩阵中的连通区域数量** +**矩阵中的连通区域数量** [Leetcode : 200. Number of Islands (Medium)](https://leetcode.com/problems/number-of-islands/description/) @@ -877,7 +877,7 @@ private void dfs(char[][] grid, int i, int j) { } ``` -**输出二叉树中所有从根到叶子的路径** +**输出二叉树中所有从根到叶子的路径** [Leetcode : 257. Binary Tree Paths (Easy)](https://leetcode.com/problems/binary-tree-paths/description/) @@ -912,7 +912,7 @@ private void dfs(TreeNode root, String prefix, List ret){ } ``` -**填充封闭区域** +**填充封闭区域** [Leetcode : 130. Surrounded Regions (Medium)](https://leetcode.com/problems/surrounded-regions/description/) @@ -967,7 +967,7 @@ private void dfs(char[][] board, int r, int c) { } ``` -**从两个方向都能到达的区域** +**从两个方向都能到达的区域** [Leetcode : 417. Pacific Atlantic Water Flow (Medium)](https://leetcode.com/problems/pacific-atlantic-water-flow/description/) @@ -1032,7 +1032,7 @@ private void dfs(int r, int c, boolean[][] canReach) { } ``` -**N 皇后** +**N 皇后** [Leetcode : 51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/) @@ -1097,11 +1097,11 @@ private void backstracking(int row) { ### Backtracking -回溯是 DFS 的一种,它不是用在遍历图的节点上,而是用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串。 +回溯是 DFS 的一种,它不是用在遍历图的节点上,而是用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串。 在程序实现时,回溯需要注意对元素进行标记的问题。使用递归实现的回溯,在访问一个新元素进入新的递归调用,此时需要将新元素标记为已经访问,这样才能在继续递归调用时不用重复访问该元素;但是在递归返回时,需要将该元素标记为未访问,因为只需要保证在一个递归链中不同时访问一个元素,而在不同的递归链是可以访问已经访问过但是不在当前递归链中的元素。 -**数字键盘组合** +**数字键盘组合** [Leetcode : 17. Letter Combinations of a Phone Number (Medium)](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/) @@ -1135,7 +1135,7 @@ private void combination(String prefix, String digits, int offset, List } ``` -**在矩阵中寻找字符串** +**在矩阵中寻找字符串** [Leetcode : 79. Word Search (Medium)](https://leetcode.com/problems/word-search/description/) @@ -1190,7 +1190,7 @@ private boolean dfs(char[][] board, String word, int start, int r, int c) { } ``` -**IP 地址划分** +**IP 地址划分** [Leetcode : 93. Restore IP Addresses(Medium)](https://leetcode.com/problems/restore-ip-addresses/description/) @@ -1225,7 +1225,7 @@ private void doRestore(int k, String path, String s) { } ``` -**排列** +**排列** [Leetcode : 46. Permutations (Medium)](https://leetcode.com/problems/permutations/description/) @@ -1267,7 +1267,7 @@ private void backtracking(List permuteList, boolean[] visited, int[] nu } ``` -**含有相同元素求排列** +**含有相同元素求排列** [Leetcode : 47. Permutations II (Medium)](https://leetcode.com/problems/permutations-ii/description/) @@ -1308,7 +1308,7 @@ private void backtracking(List permuteList, boolean[] visited, int[] nu } ``` -**组合** +**组合** [Leetcode : 77. Combinations (Medium)](https://leetcode.com/problems/combinations/description/) @@ -1347,7 +1347,7 @@ private void backtracking(int start, int n, int k, List combineList, Li } ``` -**组合求和** +**组合求和** [Leetcode : 39. Combination Sum (Medium)](https://leetcode.com/problems/combination-sum/description/) @@ -1381,7 +1381,7 @@ A solution set is: } ``` -**含有相同元素的求组合求和** +**含有相同元素的求组合求和** [Leetcode : 40. Combination Sum II (Medium)](https://leetcode.com/problems/combination-sum-ii/description/) @@ -1424,7 +1424,7 @@ private void doCombination(int[] candidates, int target, int start, List diffWaysToCompute(String input) { ### 分割整数 -**分割整数的最大乘积** +**分割整数的最大乘积** [Leetcode : 343. Integer Break (Medim)](https://leetcode.com/problems/integer-break/description/) @@ -1664,7 +1664,7 @@ public int integerBreak(int n) { } ``` -**按平方数来分割整数** +**按平方数来分割整数** [Leetcode : 279. Perfect Squares(Medium)](https://leetcode.com/problems/perfect-squares/description/) @@ -1692,7 +1692,7 @@ public int numSquares(int n) { } ``` -**分割整数构成字母字符串** +**分割整数构成字母字符串** [Leetcode : 91. Decode Ways (Medium)](https://leetcode.com/problems/decode-ways/description/) @@ -1718,7 +1718,7 @@ public int numDecodings(String s) { ### 矩阵路径 -**矩阵的总路径数** +**矩阵的总路径数** [Leetcode : 62. Unique Paths (Medium)](https://leetcode.com/problems/unique-paths/description/) @@ -1737,7 +1737,7 @@ public int uniquePaths(int m, int n) { } ``` -**矩阵的最小路径和** +**矩阵的最小路径和** [Leetcode : 64. Minimum Path Sum (Medium)](https://leetcode.com/problems/minimum-path-sum/description/) @@ -1761,7 +1761,7 @@ public int minPathSum(int[][] grid) { ### 斐波那契数列 -**爬楼梯** +**爬楼梯** [Leetcode : 70. Climbing Stairs (Easy)](https://leetcode.com/problems/climbing-stairs/description/) @@ -1790,7 +1790,7 @@ public int climbStairs(int n) { } ``` -**母牛生产** +**母牛生产** [程序员代码面试指南-P181](#) @@ -1800,7 +1800,7 @@ public int climbStairs(int n) {

-**强盗抢劫** +**强盗抢劫** [Leetcode : 198. House Robber (Easy)](https://leetcode.com/problems/house-robber/description/) @@ -1848,7 +1848,7 @@ public int rob(int[] nums) { } ``` -**强盗在环形街区抢劫** +**强盗在环形街区抢劫** [Leetcode : 213. House Robber II (Medium)](https://leetcode.com/problems/house-robber-ii/description/) @@ -1876,7 +1876,7 @@ private int rob(int[] nums, int s, int e) { ``` -**信件错排** +**信件错排** 题目描述:有 N 个 信 和 信封,它们被打乱,求错误装信的方式数量。 @@ -1896,9 +1896,9 @@ dp[N] 即为所求。 ### 最长递增子序列 -已知一个序列 {S1, S2,...,Sn} ,取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个**子序列**。 +已知一个序列 {S1, S2,...,Sn} ,取出若干数组成新的序列 {Si1, Si2,..., Sim},其中 i1、i2 ... im 保持递增,即新序列中各个数仍然保持原数列中的先后顺序,称新序列为原序列的一个 **子序列** 。 -如果在子序列中,当下标 ix > iy 时,Six > Siy,称子序列为原序列的一个**递增子序列**。 +如果在子序列中,当下标 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} 。 @@ -1908,7 +1908,7 @@ dp[N] 即为所求。 对于一个长度为 N 的序列,最长子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,即 max{ dp[i] | 1 <= i <= N} 即为所求。 -**最长递增子序列** +**最长递增子序列** [Leetcode : 300. Longest Increasing Subsequence (Medium)](https://leetcode.com/problems/longest-increasing-subsequence/description/) @@ -1967,7 +1967,7 @@ private int binarySearch(int[] nums, int sIdx, int eIdx, int key){ } ``` -**最长摆动子序列** +**最长摆动子序列** [Leetcode : 376. Wiggle Subsequence (Medium)](https://leetcode.com/problems/wiggle-subsequence/description/) @@ -2057,7 +2057,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { } ``` -**空间优化** +**空间优化** 在程序实现时可以对 0-1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅由前 i-1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时, @@ -2065,7 +2065,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { 因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w] 防止将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 -**无法使用贪心算法的解释** +**无法使用贪心算法的解释** 0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优。考虑下面的物品和一个容量为 5 的背包,如果先添加物品 0 再添加物品 1,那么只能存放的价值为 16,浪费了大小为 2 的空间。最优的方式是存放物品 1 和物品 2,价值为 22. @@ -2075,7 +2075,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { | 1 | 2 | 10 | 5 | | 2 | 3 | 12 | 4 | -**变种** +**变种** 完全背包:物品可以无限个,可以转换为 0-1 背包,令每种物品的体积和价值变为 1/2/4... 倍数,把它们都当成一个新物品,然后一种物品只能添加一次。 @@ -2085,7 +2085,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { 其它:物品之间相互约束或者依赖。 -**划分数组为和相等的两部分** +**划分数组为和相等的两部分** [Leetcode : 416. Partition Equal Subset Sum (Medium)](https://leetcode.com/problems/partition-equal-subset-sum/description/) @@ -2118,7 +2118,7 @@ public boolean canPartition(int[] nums) { } ``` -**字符串按单词列表分割** +**字符串按单词列表分割** [Leetcode : 139. Word Break (Medium)](https://leetcode.com/problems/word-break/description/) @@ -2145,7 +2145,7 @@ public boolean wordBreak(String s, List wordDict) { } ``` -**改变一组数的正负号使得它们的和为一给定数** +**改变一组数的正负号使得它们的和为一给定数** [Leetcode : 494. Target Sum (Medium)](https://leetcode.com/problems/target-sum/description/) @@ -2199,7 +2199,7 @@ private int subsetSum(int[] nums, int targetSum) { } ``` -**01字符构成最多的字符串** +**01字符构成最多的字符串** [Leetcode : 474. Ones and Zeroes (Medium)](https://leetcode.com/problems/ones-and-zeroes/description/) @@ -2236,7 +2236,7 @@ public int findMaxForm(String[] strs, int m, int n) { } ``` -**找零钱** +**找零钱** [Leetcode : 322. Coin Change (Medium)](https://leetcode.com/problems/coin-change/description/) @@ -2260,7 +2260,7 @@ public int coinChange(int[] coins, int amount) { } ``` -**组合总和** +**组合总和** [Leetcode : 377. Combination Sum IV (Medium)](https://leetcode.com/problems/combination-sum-iv/description/) @@ -2297,7 +2297,7 @@ public int combinationSum4(int[] nums, int target) { } ``` -**只能进行两次的股票交易** +**只能进行两次的股票交易** [Leetcode : 123. Best Time to Buy and Sell Stock III (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/) @@ -2315,7 +2315,7 @@ public int maxProfit(int[] prices) { } ``` -**只能进行 k 次的股票交易** +**只能进行 k 次的股票交易** [Leetcode : 188. Best Time to Buy and Sell Stock IV (Hard)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/description/) @@ -2348,7 +2348,7 @@ public int maxProfit(int k, int[] prices) { ### 数组区间 -**数组区间和** +**数组区间和** [Leetcode : 303. Range Sum Query - Immutable (Easy)](https://leetcode.com/problems/range-sum-query-immutable/description/) @@ -2371,7 +2371,7 @@ class NumArray { } ``` -**子数组最大的和** +**子数组最大的和** [Leetcode : 53. Maximum Subarray (Easy)](https://leetcode.com/problems/maximum-subarray/description/) @@ -2405,7 +2405,7 @@ public int maxSubArray(int[] nums) { } ``` -**数组中等差递增子区间的个数** +**数组中等差递增子区间的个数** [Leetcode : 413. Arithmetic Slices (Medium)](https://leetcode.com/problems/arithmetic-slices/description/) @@ -2436,7 +2436,7 @@ public int numberOfArithmeticSlices(int[] A) { ### 字符串编辑 -**删除两个字符串的字符使它们相等** +**删除两个字符串的字符使它们相等** [Leetcode : 583. Delete Operation for Two Strings (Medium)](https://leetcode.com/problems/delete-operation-for-two-strings/description/) @@ -2458,14 +2458,14 @@ public int minDistance(String word1, String word2) { ``` -**修改一个字符串称为另一个字符串** // TODO +**修改一个字符串称为另一个字符串** // TODO [Leetcode : 72. Edit Distance (Hard)](https://leetcode.com/problems/edit-distance/description/) ### 其它问题 -**需要冷却期的股票交易** +**需要冷却期的股票交易** [Leetcode : 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/) @@ -2500,7 +2500,7 @@ public int maxProfit(int[] prices) { ``` -**统计从 0 \~ n 每个数的二进制表示中 1 的个数** +**统计从 0 \~ n 每个数的二进制表示中 1 的个数** [Leetcode : 338. Counting Bits (Medium)](https://leetcode.com/problems/counting-bits/description/) @@ -2516,7 +2516,7 @@ public int maxProfit(int[] prices) { } ``` -**一组整数对能够构成的最长链** +**一组整数对能够构成的最长链** [Leetcode : 646. Maximum Length of Pair Chain (Medium)](https://leetcode.com/problems/maximum-length-of-pair-chain/description/) @@ -2547,7 +2547,7 @@ public int findLongestChain(int[][] pairs) { } ``` -**买入和售出股票最大的收益** +**买入和售出股票最大的收益** [Leetcode : 121. Best Time to Buy and Sell Stock (Easy)](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/description/) @@ -2569,7 +2569,7 @@ public int maxProfit(int[] prices) { } ``` -**复制粘贴字符** +**复制粘贴字符** [Leetcode : 650. 2 Keys Keyboard (Medium)](https://leetcode.com/problems/2-keys-keyboard/description/) @@ -2603,22 +2603,22 @@ public int minSteps(int 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 的 **最大公约数** 为: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) \* ... +x 和 y 的 **最小公倍数** 为:lcm(x,y) = 2max(m0,n0) \* 3max(m1,n1) \* 5max(m2,n2) \* ... -**生成素数序列** +**生成素数序列** [Leetcode : 204. Count Primes (Easy)](https://leetcode.com/problems/count-primes/description/) @@ -2672,7 +2672,7 @@ int lcm(int a, int b){ Java 中 static String toString(int num, int radix) 可以将一个整数装换为 redix 进制表示的字符串。 -**7 进制** +**7 进制** [Leetcode : 504. Base 7 (Easy)](https://leetcode.com/problems/base-7/description/) @@ -2688,7 +2688,7 @@ public String convertToBase7(int num) { } ``` -**16 进制** +**16 进制** [Leetcode : 405. Convert a Number to Hexadecimal (Easy)](https://leetcode.com/problems/convert-a-number-to-hexadecimal/description/) @@ -2707,7 +2707,7 @@ public String toHex(int num) { ### 阶乘 -**统计阶乘尾部有多少个 0** +**统计阶乘尾部有多少个 0** [Leetcode : 172. Factorial Trailing Zeroes (Easy)](https://leetcode.com/problems/factorial-trailing-zeroes/description/) @@ -2725,7 +2725,7 @@ public int trailingZeroes(int n) { ### 字符串加法减法 -**二进制加法** +**二进制加法** [Leetcode : 67. Add Binary (Easy)](https://leetcode.com/problems/add-binary/description/) @@ -2744,7 +2744,7 @@ public String addBinary(String a, String b) { } ``` -**字符串加法** +**字符串加法** [Leetcode : 415. Add Strings (Easy)](https://leetcode.com/problems/add-strings/description/) @@ -2766,7 +2766,7 @@ public String addStrings(String num1, String num2) { ### 相遇问题 -**改变数组元素使所有的数组元素都相等** +**改变数组元素使所有的数组元素都相等** [Leetcode : 462. Minimum Moves to Equal Array Elements II (Medium)](https://leetcode.com/problems/minimum-moves-to-equal-array-elements-ii/description/) @@ -2778,7 +2778,7 @@ public String addStrings(String num1, String num2) { 设数组长度为 N,则可以找到 N/2 对 a 和 b 的组合,使它们都移动到 m 的位置。 -**解法 1** +**解法 1** 先排序,时间复杂度:O(NlgN) @@ -2796,7 +2796,7 @@ public int minMoves2(int[] nums) { } ``` -**解法 2** +**解法 2** 使用快速排序找到中位数,时间复杂度 O(N) @@ -2832,7 +2832,7 @@ private void swap(int[] nums, int i, int j) { ### 多数投票问题 -**数组中出现次数多于 n / 2 的元素** +**数组中出现次数多于 n / 2 的元素** [Leetcode : 169. Majority Element (Easy)](https://leetcode.com/problems/majority-element/description/) @@ -2864,7 +2864,7 @@ public int majorityElement(int[] nums) { ### 其它 -**平方数** +**平方数** [Leetcode : 367. Valid Perfect Square (Easy)](https://leetcode.com/problems/valid-perfect-square/description/) @@ -2884,7 +2884,7 @@ public boolean isPerfectSquare(int num) { } ``` -**3 的 n 次方** +**3 的 n 次方** [Leetcode : 326. Power of Three (Easy)](https://leetcode.com/problems/power-of-three/description/) @@ -2894,7 +2894,7 @@ public boolean isPowerOfThree(int n) { } ``` -**找出数组中的乘积最大的三个数** +**找出数组中的乘积最大的三个数** [Leetcode : 628. Maximum Product of Three Numbers (Easy)](https://leetcode.com/problems/maximum-product-of-three-numbers/description/) @@ -2924,7 +2924,7 @@ public int maximumProduct(int[] nums) { } ``` -**乘积数组** +**乘积数组** [Leetcode : 238. Product of Array Except Self (Medium)](https://leetcode.com/problems/product-of-array-except-self/description/) @@ -2953,7 +2953,7 @@ public int[] productExceptSelf(int[] nums) { ## 栈和队列 -**用栈实现队列** +**用栈实现队列** 一个栈实现: @@ -3021,7 +3021,7 @@ class MyQueue { } ``` -**用队列实现栈** +**用队列实现栈** [Leetcode : 225. Implement Stack using Queues (Easy)](https://leetcode.com/problems/implement-stack-using-queues/description/) @@ -3055,7 +3055,7 @@ class MyStack { } ``` -**最小值栈** +**最小值栈** [Leetcode : 155. Min Stack (Easy)](https://leetcode.com/problems/min-stack/description/) @@ -3104,7 +3104,7 @@ class MinStack { 对于实现最小值队列问题,可以先将队列使用栈来实现,然后就将问题转换为最小值栈,这个问题出现在 编程之美:3.7。 -**用栈实现括号匹配** +**用栈实现括号匹配** [Leetcode : 20. Valid Parentheses (Easy)](https://leetcode.com/problems/valid-parentheses/description/) @@ -3134,7 +3134,7 @@ public boolean isValid(String s) { } ``` -**数组中比当前元素大的下一个数组元素的距离** +**数组中比当前元素大的下一个数组元素的距离** ```html Input: [73, 74, 75, 71, 69, 72, 76, 73] @@ -3161,7 +3161,7 @@ public int[] dailyTemperatures(int[] temperatures) { } ``` -**数组中下一个比当前数大的数** +**数组中下一个比当前数大的数** [Leetcode : 496. Next Greater Element I (Easy)](https://leetcode.com/problems/next-greater-element-i/description/) @@ -3191,7 +3191,7 @@ public int[] nextGreaterElement(int[] nums1, int[] nums2) { } ``` -**循环数组中下一个比当前元素大的数** +**循环数组中下一个比当前元素大的数** [Leetcode : 503. Next Greater Element II (Medium)](https://leetcode.com/problems/next-greater-element-ii/description/) @@ -3215,18 +3215,18 @@ public int[] nextGreaterElements(int[] nums) { 利用 Hash Table 可以快速查找一个元素是否存在等问题,但是需要一定的空间来存储。在优先考虑时间复杂度的情况下,可以利用 Hash Table 这种空间换取时间的做法。 -Java 中的 **HashSet** 用于存储一个集合,并以 O(1) 的时间复杂度查找元素是否在集合中。 +Java 中的 **HashSet** 用于存储一个集合,并以 O(1) 的时间复杂度查找元素是否在集合中。 如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在,例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。 -Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。 +Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。 在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中([Leetcdoe : 535. Encode and Decode TinyURL (Medium)](https://leetcode.com/problems/encode-and-decode-tinyurl/description/)),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源。 HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。 -**数组中的两个数和为给定值** +**数组中的两个数和为给定值** [Leetcode : 1. Two Sum (Easy)](https://leetcode.com/problems/two-sum/description/) @@ -3245,7 +3245,7 @@ public int[] twoSum(int[] nums, int target) { } ``` -**最长和谐序列** +**最长和谐序列** 和谐序列中最大数和最小数只差正好为 1 @@ -3269,7 +3269,7 @@ public int findLHS(int[] nums) { ## 字符串 -**两个字符串的包含的字符是否完全相同** +**两个字符串的包含的字符是否完全相同** [Leetcode : 242. Valid Anagram (Easy)](https://leetcode.com/problems/valid-anagram/description/) @@ -3287,7 +3287,7 @@ public boolean isAnagram(String s, String t) { } ``` -**字符串同构** +**字符串同构** [Leetcode : 205. Isomorphic Strings (Easy)](https://leetcode.com/problems/isomorphic-strings/description/) @@ -3310,7 +3310,7 @@ public boolean isIsomorphic(String s, String t) { } ``` -**计算一组字符集合可以组成的回文字符串的最大长度** +**计算一组字符集合可以组成的回文字符串的最大长度** [Leetcode : 409. Longest Palindrome](https://leetcode.com/problems/longest-palindrome/description/) @@ -3327,7 +3327,7 @@ public int longestPalindrome(String s) { } ``` -**判断一个整数是否是回文数** +**判断一个整数是否是回文数** [Leetcode : 9. Palindrome Number (Easy)](https://leetcode.com/problems/palindrome-number/description/) @@ -3349,7 +3349,7 @@ public boolean isPalindrome(int x) { } ``` -**回文子字符串** +**回文子字符串** [Leetcode : 647. Palindromic Substrings (Medium)](https://leetcode.com/problems/palindromic-substrings/description/) @@ -3374,7 +3374,7 @@ private void extendSubstrings(String s, int start, int end) { } ``` -**统计二进制字符串中连续 1 和 连续 0 数量相同的子字符串个数** +**统计二进制字符串中连续 1 和 连续 0 数量相同的子字符串个数** ```html Input: "00110011" @@ -3400,7 +3400,7 @@ public int countBinarySubstrings(String s) { } ``` -**字符串循环移位包含** +**字符串循环移位包含** [ 编程之美:3.1](#) @@ -3413,7 +3413,7 @@ Return : true s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。 -**字符串循环移位** +**字符串循环移位** [ 编程之美:2.17](#) @@ -3423,7 +3423,7 @@ s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 将 abcd123 中的 abcd 和 123 单独逆序,得到 dcba321,然后对整个字符串进行逆序,得到123abcd。 -**字符串中单词的翻转** +**字符串中单词的翻转** [程序员代码面试指南](#) @@ -3433,7 +3433,7 @@ s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 ## 数组与矩阵 -**把数组中的 0 移到末尾** +**把数组中的 0 移到末尾** [Leetcode : 283. Move Zeroes (Easy)](https://leetcode.com/problems/move-zeroes/description/) @@ -3450,7 +3450,7 @@ s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 } ``` -**一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出丢失的数和重复的数** +**一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出丢失的数和重复的数** [Leetcode : 645. Set Mismatch (Easy)](https://leetcode.com/problems/set-mismatch/description/) @@ -3485,7 +3485,7 @@ private void swap(int[] nums, int i, int j){ } ``` -**找出数组中重复的数,数组值在 [0, n-1] 之间** +**找出数组中重复的数,数组值在 [0, n-1] 之间** [Leetcode : 287. Find the Duplicate Number (Medium)](https://leetcode.com/problems/find-the-duplicate-number/description/) @@ -3540,7 +3540,7 @@ public int findDuplicate(int[] nums) { ] ``` -**有序矩阵查找** +**有序矩阵查找** [Leetocde : 240. Search a 2D Matrix II (Medium)](https://leetcode.com/problems/search-a-2d-matrix-ii/description/) @@ -3558,7 +3558,7 @@ public boolean searchMatrix(int[][] matrix, int target) { } ``` -**有序矩阵的 Kth Element** +**有序矩阵的 Kth Element** [Leetcode : 378. Kth Smallest Element in a Sorted Matrix ((Medium))](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/) @@ -3626,7 +3626,7 @@ class Tuple implements Comparable { ## 链表 -**判断两个链表的交点** +**判断两个链表的交点** [Leetcode : 160. Intersection of Two Linked Lists](https://leetcode.com/problems/intersection-of-two-linked-lists/description/) @@ -3660,7 +3660,7 @@ public ListNode getIntersectionNode(ListNode headA, ListNode headB) { -**链表反转** +**链表反转** [Leetcode : 206. Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/description/) @@ -3679,7 +3679,7 @@ public ListNode reverseList(ListNode head) { } ``` -**归并两个有序的链表** +**归并两个有序的链表** [Leetcode : 21. Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/description/) @@ -3701,7 +3701,7 @@ public ListNode mergeTwoLists(ListNode l1, ListNode l2) { } ``` -**从有序链表中删除重复节点** +**从有序链表中删除重复节点** [Leetcode : 83. Remove Duplicates from Sorted List (Easy)](https://leetcode.com/problems/remove-duplicates-from-sorted-list/description/) @@ -3713,7 +3713,7 @@ public ListNode deleteDuplicates(ListNode head) { } ``` -**回文链表** +**回文链表** [Leetcode : 234. Palindrome Linked List (Easy)](https://leetcode.com/problems/palindrome-linked-list/description/) @@ -3764,7 +3764,7 @@ private boolean isEqual(ListNode l1, ListNode l2){ } ``` -**从链表中删除节点** +**从链表中删除节点** [编程之美:3.4]() @@ -3775,7 +3775,7 @@ B.val = C.val; B.next = C.next; ``` -**链表元素按奇偶聚集** +**链表元素按奇偶聚集** [Leetcode : 328. Odd Even Linked List (Medium)](https://leetcode.com/problems/odd-even-linked-list/description/) @@ -3802,7 +3802,7 @@ public ListNode oddEvenList(ListNode head) { 一棵树要么是空树,要么有两个指针,每个指针指向一棵树。树是一种递归结构,很多树的问题可以使用递归来处理。 -**树的高度** +**树的高度** [Leetcode : 104. Maximum Depth of Binary Tree (Easy)](https://leetcode.com/problems/maximum-depth-of-binary-tree/description/) @@ -3813,7 +3813,7 @@ public int maxDepth(TreeNode root) { } ``` -**翻转树** +**翻转树** [Leetcode : 226. Invert Binary Tree (Easy)](https://leetcode.com/problems/invert-binary-tree/description/) @@ -3827,7 +3827,7 @@ public TreeNode invertTree(TreeNode root) { } ``` -**归并两棵树** +**归并两棵树** [Leetcode : 617. Merge Two Binary Trees (Easy)](https://leetcode.com/problems/merge-two-binary-trees/description/) @@ -3843,7 +3843,7 @@ public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { } ``` -**判断路径和是否等于一个数** +**判断路径和是否等于一个数** [Leetcdoe : 112. Path Sum (Easy)](https://leetcode.com/problems/path-sum/description/) @@ -3857,7 +3857,7 @@ public boolean hasPathSum(TreeNode root, int sum) { } ``` -**统计路径和等于一个数的路径数量** +**统计路径和等于一个数的路径数量** [Leetcode : 437. Path Sum III (Easy)](https://leetcode.com/problems/path-sum-iii/description/) @@ -3881,7 +3881,7 @@ private int pathSumStartWithRoot(TreeNode root, int sum){ } ``` -**树的对称** +**树的对称** [Leetcode : 101. Symmetric Tree (Easy)](https://leetcode.com/problems/symmetric-tree/description/) @@ -3899,7 +3899,7 @@ private boolean isSymmetric(TreeNode t1, TreeNode t2){ } ``` -**平衡树** +**平衡树** [Leetcode : 110. Balanced Binary Tree (Easy)](https://leetcode.com/problems/balanced-binary-tree/description/) @@ -3922,7 +3922,7 @@ public int maxDepth(TreeNode root) { } ``` -**最小路径** +**最小路径** [Leetcode : 111. Minimum Depth of Binary Tree (Easy)](https://leetcode.com/problems/minimum-depth-of-binary-tree/description/) @@ -3938,7 +3938,7 @@ public int minDepth(TreeNode root) { } ``` -**统计左叶子节点的和** +**统计左叶子节点的和** [Leetcode : 404. Sum of Left Leaves (Easy)](https://leetcode.com/problems/sum-of-left-leaves/description/) @@ -3955,7 +3955,7 @@ private boolean isLeaf(TreeNode node){ } ``` -**修剪一棵树** +**修剪一棵树** [Leetcode : 669. Trim a Binary Search Tree (Easy)](https://leetcode.com/problems/trim-a-binary-search-tree/description/) @@ -3972,7 +3972,7 @@ public TreeNode trimBST(TreeNode root, int L, int R) { } ``` -**子树** +**子树** [Leetcode : 572. Subtree of Another Tree (Easy)](https://leetcode.com/problems/subtree-of-another-tree/description/) @@ -3992,7 +3992,7 @@ private boolean isSame(TreeNode s, TreeNode t){ } ``` -**从有序数组中构造二叉查找树** +**从有序数组中构造二叉查找树** [Leetcode : 108. Convert Sorted Array to Binary Search Tree (Easy)](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/description/) @@ -4013,7 +4013,7 @@ private TreeNode toBST(int[] nums, int sIdx, int eIdx){ } ``` -**两节点的最长路径** +**两节点的最长路径** ```html 1 @@ -4044,7 +4044,7 @@ private int depth(TreeNode root) { } ``` -**找出二叉树中第二小的节点** +**找出二叉树中第二小的节点** [Leetcode : 671. Second Minimum Node In a Binary Tree (Easy)](https://leetcode.com/problems/second-minimum-node-in-a-binary-tree/description/) @@ -4075,7 +4075,7 @@ public int findSecondMinimumValue(TreeNode root) { } ``` -**寻找两个节点的最近公共祖先** +**寻找两个节点的最近公共祖先** [Leetcode : 235. Lowest Common Ancestor of a Binary Search Tree (Easy)](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/description/) @@ -4087,7 +4087,7 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { } ``` -**最近公共祖先** +**最近公共祖先** [Leetcode : 236. Lowest Common Ancestor of a Binary Tree (Medium) ](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/) @@ -4100,7 +4100,7 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { } ``` -**最大相同节点值的路径长度** +**最大相同节点值的路径长度** [Leetcode : 687. Longest Univalue Path (Easy)](https://pomotodo.com/app/) @@ -4132,7 +4132,7 @@ private int dfs(TreeNode root){ } ``` -**间隔遍历** +**间隔遍历** [Leetcode : 337. House Robber III (Medium)](https://leetcode.com/problems/house-robber-iii/description/) @@ -4155,7 +4155,7 @@ public int rob(TreeNode root) { 使用 BFS,不需要使用两个队列来分别存储当前层的节点和下一层的节点, 因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 -**计算一棵树每层节点的平均数** +**计算一棵树每层节点的平均数** [637. Average of Levels in Binary Tree (Easy)](https://leetcode.com/problems/average-of-levels-in-binary-tree/description/) @@ -4180,7 +4180,7 @@ public List averageOfLevels(TreeNode root) { } ``` -**得到左下角的节点** +**得到左下角的节点** [Leetcode : 513. Find Bottom Left Tree Value (Easy)](https://leetcode.com/problems/find-bottom-left-tree-value/description/) @@ -4246,7 +4246,7 @@ void dfs(TreeNode root){ } ``` -**非递归实现二叉树的前序遍历** +**非递归实现二叉树的前序遍历** [Leetcode : 144. Binary Tree Preorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-preorder-traversal/description/) @@ -4266,7 +4266,7 @@ public List preorderTraversal(TreeNode root) { } ``` -**非递归实现二叉树的后续遍历** +**非递归实现二叉树的后续遍历** [Leetcode : ### 145. Binary Tree Postorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-postorder-traversal/description/) @@ -4289,7 +4289,7 @@ public List postorderTraversal(TreeNode root) { } ``` -**非递归实现二叉树的中序遍历** +**非递归实现二叉树的中序遍历** [Leetcode : 94. Binary Tree Inorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-inorder-traversal/description/) @@ -4311,13 +4311,13 @@ public List inorderTraversal(TreeNode root) { } ``` -**使用中序遍历和前序遍历序列重建二叉树** //TODO +**使用中序遍历和前序遍历序列重建二叉树** //TODO ### BST 主要利用 BST 中序遍历有序的特点。 -**在 BST 中寻找两个节点,使它们的和为一个给定值。** +**在 BST 中寻找两个节点,使它们的和为一个给定值。** [653. Two Sum IV - Input is a BST](https://leetcode.com/problems/two-sum-iv-input-is-a-bst/description/) @@ -4347,7 +4347,7 @@ private void inOrder(TreeNode root, List nums){ } ``` -**在 BST 中查找最小的两个节点之差的绝对值** +**在 BST 中查找最小的两个节点之差的绝对值** [Leetcode : 530. Minimum Absolute Difference in BST (Easy)](https://leetcode.com/problems/minimum-absolute-difference-in-bst/description/) @@ -4371,7 +4371,7 @@ private void inorder(TreeNode node){ } ``` -**把 BST 每个节点的值都加上比它大的节点的值** +**把 BST 每个节点的值都加上比它大的节点的值** [Leetcode : Convert BST to Greater Tree (Easy)](https://leetcode.com/problems/convert-bst-to-greater-tree/description/) @@ -4400,7 +4400,7 @@ private void traver(TreeNode root) { } ``` -**寻找 BST 中出现次数最多的节点** +**寻找 BST 中出现次数最多的节点** ```java private int cnt = 1; @@ -4438,7 +4438,7 @@ private void inorder(TreeNode node){ } ``` -**寻找 BST 的第 k 个元素** +**寻找 BST 的第 k 个元素** [Leetcode : 230. Kth Smallest Element in a BST (Medium)](https://leetcode.com/problems/kth-smallest-element-in-a-bst/description/) @@ -4488,7 +4488,7 @@ private void inorder(TreeNode node, int k) { Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 -**实现一个 Trie** +**实现一个 Trie** [Leetcode : 208. Implement Trie (Prefix Tree) (Medium)](https://leetcode.com/problems/implement-trie-prefix-tree/description/) @@ -4548,7 +4548,7 @@ class Trie { } ``` -**实现一个 Trie,用来求前缀和** +**实现一个 Trie,用来求前缀和** [Leetcode : 677. Map Sum Pairs (Medium)](https://leetcode.com/problems/map-sum-pairs/description/) @@ -4609,7 +4609,7 @@ class MapSum { ## 位运算 -**1. 基本原理** +**1. 基本原理** 0s 表示 一串 0 ,1s 表示一串 1。 @@ -4627,13 +4627,13 @@ x ^ x = 0 x & x = x x | x = x \>\>\> n 为无符号右移,左边会补上 0。 << n 为算术左移,相当于乘以 2n。 -n&(n-1) 该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110**100**,减去 1 得到 10110**011**,这两个数相与得到 10110**000**。 +n&(n-1) 该位运算是去除 n 的位级表示中最低的那一位。例如对于二进制表示 10110 **100** ,减去 1 得到 10110**011**,这两个数相与得到 10110**000**。 n-n&(\~n+1) 概运算是去除 n 的位级表示中最高的那一位。 -n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110**100**,-n 得到 01001**100**,相与得到 00000**100** +n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的反码加 1,对于二进制表示 10110 **100** ,-n 得到 01001**100**,相与得到 00000**100** -**2. mask 计算** +**2. mask 计算** 要获取 111111111,将 0 取反即可,\~0。 @@ -4643,7 +4643,7 @@ n&(-n) 该运算得到 n 的位级表示中最低的那一位。-n 得到 n 的 要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~(1<<(i+1)-1)。 -**3. 位操作举例** +**3. 位操作举例** ① 获取第 i 位 @@ -4693,7 +4693,7 @@ num & (~((1 << (i+1)) - 1)); (num & (1 << i)) | (v << i); ``` -**4. Java 中的位操作** +**4. Java 中的位操作** ```html static int Integer.bitCount() // 统计 1 的数量 @@ -4701,7 +4701,7 @@ static int Integer.highestOneBit() // 获得最高位 static String toBinaryString(int i) // 转换位二进制表示的字符串 ``` -**统计两个数的二进制表示有多少位不同** +**统计两个数的二进制表示有多少位不同** [Leetcode : 461. Hamming Distance (Easy)](https://leetcode.com/problems/hamming-distance/) @@ -4727,7 +4727,7 @@ public int hammingDistance(int x, int y) { } ``` -**翻转一个数的比特位** +**翻转一个数的比特位** [Leetcode : 190. Reverse Bits (Easy)](https://leetcode.com/problems/reverse-bits/description/) @@ -4743,7 +4743,7 @@ public int reverseBits(int n) { } ``` -**不用额外变量交换两个整数** +**不用额外变量交换两个整数** [程序员代码面试指南 :P317](#) @@ -4755,7 +4755,7 @@ a = a ^ b; 将 c = a ^ b,那么 b ^ c = b ^ b ^ a = a,a ^ c = a ^ a ^ b = b。 -**判断一个数是不是 4 的 n 次方** +**判断一个数是不是 4 的 n 次方** [Leetcode : 342. Power of Four (Easy)](https://leetcode.com/problems/power-of-four/) @@ -4780,7 +4780,7 @@ public boolean isPowerOfFour(int num) { } ``` -**判断一个数是不是 2 的 n 次方** +**判断一个数是不是 2 的 n 次方** [Leetcode : 231. Power of Two (Easy)](https://leetcode.com/problems/power-of-two/description/) @@ -4800,7 +4800,7 @@ public boolean isPowerOfTwo(int n) { } ``` -**数组中唯一一个不重复的元素** +**数组中唯一一个不重复的元素** [Leetcode : 136. Single Number (Easy)](https://leetcode.com/problems/single-number/description/) @@ -4816,7 +4816,7 @@ public int singleNumber(int[] nums) { } ``` -**数组中不重复的两个元素** +**数组中不重复的两个元素** [Leetcode : 260. Single Number III (Medium)](https://leetcode.com/problems/single-number-iii/description/) @@ -4842,7 +4842,7 @@ public int[] singleNumber(int[] nums) { } ``` -**判断一个数的位级表示是否不会出现连续的 0 和 1** +**判断一个数的位级表示是否不会出现连续的 0 和 1** [Leetcode : 693. Binary Number with Alternating Bits (Easy)](https://leetcode.com/problems/binary-number-with-alternating-bits/description/) @@ -4855,7 +4855,7 @@ public boolean hasAlternatingBits(int n) { } ``` -**求一个数的补码** +**求一个数的补码** [Leetcode : 476. Number Complement (Easy)](https://leetcode.com/problems/number-complement/description/) @@ -4904,7 +4904,7 @@ public int findComplement(int num) { } ``` -**实现整数的加法** +**实现整数的加法** [Leetcode : 371. Sum of Two Integers (Easy)](https://leetcode.com/problems/sum-of-two-integers/description/) @@ -4916,7 +4916,7 @@ public int getSum(int a, int b) { } ``` -**字符串数组最大乘积** +**字符串数组最大乘积** [Leetcode : 318. Maximum Product of Word Lengths (Medium)](https://leetcode.com/problems/maximum-product-of-word-lengths/description/) diff --git a/notes/Linux.md b/notes/Linux.md index 4674254a..56ec58ec 100644 --- a/notes/Linux.md +++ b/notes/Linux.md @@ -89,11 +89,11 @@ ## 求助 -**1. --help** +**1. --help** 指令的基本用法与选项介绍。 -**2. man** +**2. man** man 是 manual 的缩写,将指令的具体信息显示出来。 @@ -105,17 +105,17 @@ man 是 manual 的缩写,将指令的具体信息显示出来。 | 5 | 配置文件 | | 8 | 系统管理员可以使用的管理指令 | -**3. info** +**3. info** info 与 man 类似,但是 info 将文档分成一个个页面,每个页面可以进行跳转。 ## 关机 -**1. sync** +**1. sync** 为了加快对磁盘上文件的读写速度,位于内存中的文件数据不会立即同步到磁盘上,因此关机之前需要先进行 sync 同步操作。 -**2. shutdown** +**2. shutdown** ```html # /sbin/shutdown [-krhc] [时间] [警告讯息] @@ -125,7 +125,7 @@ info 与 man 类似,但是 info 将文档分成一个个页面,每个页面 -c : 取消已经在进行的 shutdown 指令内容 ``` -**3. 其它关机指令** +**3. 其它关机指令** reboot、halt、poweroff。 @@ -695,21 +695,21 @@ $ tar [-z|-j|-J] [xv] [-f 已有的tar文件] [-C 目录] ==解压缩 ## Bash 特性 -**1. 命令历史** +**1. 命令历史** 记录使用过的命令。本次登录所执行的命令都会暂时存放到内存中, \~/.bash_history 文件中记录的是前一次登录所执行过的命令。 -**2. 命令与文件补全** +**2. 命令与文件补全** 快捷键:tab -**3. 命名别名** +**3. 命名别名** 例如 lm 是 ls -al 的别名。 -**4. shell scripts** +**4. shell scripts** -**5. 通配符** +**5. 通配符** 例如 ls -l /usr/bin/X\* 列出 /usr/bin 下面所有以 X 开头的文件。 @@ -823,7 +823,7 @@ $ export | cut -c 12 ### 2. 排序命令:sort、uniq -**sort** 进行排序。 +**sort** 进行排序。 ```html $ sort [-fbMnrtuk] [file or stdin] @@ -847,7 +847,7 @@ alex:x:1001:1002::/home/alex:/bin/bash arod:x:1002:1003::/home/arod:/bin/bash ``` -**uniq** 可以将重复的数据只取一个。 +**uniq** 可以将重复的数据只取一个。 ```html $ uniq [-ic] @@ -869,7 +869,7 @@ $ last | cut -d ' ' -f 1 | sort | uniq -c ### 3. 双向输出重定向:tee -输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 +输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 ```html $ tee [-a] file @@ -877,7 +877,7 @@ $ tee [-a] file ### 4. 字符转换指令:tr、col、expand、join、paste - **tr** 用来删除一行中的字符,或者对字符进行替换。 + **tr** 用来删除一行中的字符,或者对字符进行替换。 ```html $ tr [-ds] SET1 ... @@ -890,21 +890,21 @@ $ tr [-ds] SET1 ... $ last | tr '[a-z]' '[A-Z]' ``` - **col** 将 tab 字符转为空格字符。 + **col** 将 tab 字符转为空格字符。 ```html $ col [-xb] -x : 将 tab 键转换成对等的空格键 ``` -**expand** 将 tab 转换一定数量的空格,默认是 8 个。 +**expand** 将 tab 转换一定数量的空格,默认是 8 个。 ```html $ expand [-t] file -t :tab 转为空格的数量 ``` -**join** 将有相同数据的那一行合并在一起。 +**join** 将有相同数据的那一行合并在一起。 ```html $ join [-ti12] file1 file2 @@ -914,7 +914,7 @@ $ join [-ti12] file1 file2 -2 :第二个文件所用的比较字段 ``` -**paste** 直接将两行粘贴在一起。 +**paste** 直接将两行粘贴在一起。 ```html $ paste [-d] file1 file2 @@ -923,7 +923,7 @@ $ paste [-d] file1 file2 ### 5. 分区指令:split -**split** 将一个文件划分成多个文件。 +**split** 将一个文件划分成多个文件。 ```html $ split [-bl] file PREFIX diff --git a/notes/MySQL.md b/notes/MySQL.md index 6cccf3f3..2d9ebd21 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -74,23 +74,23 @@ MyISAM 设计简单,数据以紧密格式存储,所以在某些场景下性 ## 3. InnoDB 与 MyISAM 的比较 -**事务** +**事务** InnoDB 是事务型的。 -**备份** +**备份** InnoDB 支持在线热备份。 -**崩溃恢复** +**崩溃恢复** MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 -**并发** +**并发** MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 -**其它特性** +**其它特性** MyISAM 支持全文索引,地理空间索引。 @@ -120,7 +120,7 @@ VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。 MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。 -**DATATIME** +**DATATIME** 能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。 @@ -128,7 +128,7 @@ MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。 默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATATIME 值,例如“2008-01-16 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。 -**TIMESTAMP** +**TIMESTAMP** 和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。 @@ -208,7 +208,7 @@ SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5; 对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。 -对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1 ,此时每个记录都有唯一的索引与其对应。 +对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1 ,此时每个记录都有唯一的索引与其对应。 ### 3.3 多列索引 @@ -246,12 +246,12 @@ customer_id_selectivity: 0.0373 因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。 -**优点** +**优点** 1. 可以把相关数据保存在一起,减少 I/O 操作; 2. 因为数据保存在 B-Tree 中,因此数据访问更快。 -**缺点** +**缺点** 1. 聚簇索引最大限度提高了 I/O 密集型应用的性能,但是如果数据全部放在内存,就没必要用聚簇索引。 2. 插入速度严重依赖于插入顺序,按主键的顺序插入是最快的。 @@ -351,33 +351,33 @@ do { # 分库与分表 -**1. 分表与分区的不同** +**1. 分表与分区的不同** 分表,就是讲一张表分成多个小表,这些小表拥有不同的表名;而分区是将一张表的数据分为多个区块,这些区块可以存储在同一个磁盘上,也可以存储在不同的磁盘上,这种方式下表仍然只有一个。 -**2. 使用分库与分表的原因** +**2. 使用分库与分表的原因** 随着时间和业务的发展,数据库中的表会越来越多,并且表中的数据量也会越来越大,那么读写操作的开销也会随着增大。 -**3. 垂直切分** +**3. 垂直切分** 将表按功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立商品数据库 payDB、用户数据库 userDB 等,分别用来存储项目与商品有关的表和与用户有关的表。 -**4. 水平切分** +**4. 水平切分** 把表中的数据按照某种规则存储到多个结构相同的表中,例如按 id 的散列值、性别等进行划分, -**5. 垂直切分与水平切分的选择** +**5. 垂直切分与水平切分的选择** 如果数据库中的表太多,并且项目各项业务逻辑清晰,那么垂直切分是首选。 如果数据库的表不多,但是单表的数据量很大,应该选择水平切分。 -**6. 水平切分的实现方式** +**6. 水平切分的实现方式** 最简单的是使用 merge 存储引擎。 -**7. 分库与分表存在的问题** +**7. 分库与分表存在的问题** (1) 事务问题 @@ -393,21 +393,21 @@ do { ## 1. 故障转移 -**1.1 提升备库或切换角色** +**1.1 提升备库或切换角色** 提升一台备库为主库,或者在一个主-主复制结构中调整主动和被动角色。 -**1.2 虚拟 IP 地址和 IP 托管** +**1.2 虚拟 IP 地址和 IP 托管** 为 MySQL 实例指定一个逻辑 IP 地址,当 MySQL 实例失效时,可以将 IP 地址转移到另一台 MySQL 服务器上。 -**1.3 中间件解决方案** +**1.3 中间件解决方案** 通过代理,可以路由流量到可以使用的服务器上。

-**1.4 在应用中处理故障转移** +**1.4 在应用中处理故障转移** 将故障转移整合到应用中可能导致应用变得太过笨拙。 diff --git a/notes/SQL 语法.md b/notes/SQL 语法.md index 11ed3238..12fb415a 100644 --- a/notes/SQL 语法.md +++ b/notes/SQL 语法.md @@ -66,14 +66,14 @@ CREATE TABLE mytable ( # 插入 -**普通插入** +**普通插入** ```sql INSERT INTO mytable(col1, col2) VALUES(val1, val2); ``` -**插入检索出来的数据** +**插入检索出来的数据** ```sql INSERT INTO mytable1(col1, col2) @@ -81,7 +81,7 @@ SELECT col1, col2 FROM mytable2; ``` -**将一个表的内容复制到一个新表** +**将一个表的内容复制到一个新表** ```sql CREATE TABLE newtable AS @@ -103,27 +103,27 @@ DELETE FROM mytable WHERE id = 1; ``` -**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 +**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 使用更新和删除操作时一定要用 WHERE 子句,不然会把整张表的数据都破坏。可以先用 SELECT 语句进行测试,防止错误删除。 # 修改表 -**添加列** +**添加列** ```sql ALTER TABLE mytable ADD col CHAR(20); ``` -**删除列** +**删除列** ```sql ALTER TABLE mytable DROP COLUMN col; ``` -**删除表** +**删除表** ```sql DROP TABLE mytable; @@ -131,7 +131,7 @@ DROP TABLE mytable; # 查询 -**DISTINCT** +**DISTINCT** 相同值只会出现一次。它作用于所有列,也就是说所有列的值都相同才算相同。 @@ -140,7 +140,7 @@ SELECT DISTINCT col1, col2 FROM mytable; ``` -**LIMIT** +**LIMIT** 限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。 @@ -169,8 +169,8 @@ LIMIT 2, 3; # 排序 -- **ASC**:升序(默认) -- **DESC**:降序 +- **ASC** :升序(默认) +- **DESC** :降序 可以按多个列进行排序,并且为每个列指定不同的排序方式: @@ -203,21 +203,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 来进行通配符匹配。 @@ -232,14 +232,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), ')') @@ -260,7 +260,7 @@ FROM mytable | LENGTH() | 长度 | | SUNDEX() | 转换为语音值 | -其中,**SOUNDEX()** 是将一个字符串转换为描述其语音表示的字母数字模式的算法,它是根据发音而不是字母比较。 +其中, **SOUNDEX()** 是将一个字符串转换为描述其语音表示的字母数字模式的算法,它是根据发音而不是字母比较。 ```sql SELECT * @@ -429,7 +429,7 @@ where A.key = B.key 一张员工表,包含员工姓名和员工所属部门,要找出与 Jim 处在同一部门的所有员工姓名。 -**子查询版本** +**子查询版本** ``` select name @@ -440,7 +440,7 @@ where department = ( where name = "Jim"); ``` -**自连接版本** +**自连接版本** ``` select name @@ -486,7 +486,7 @@ group by Customers.cust_id; # 组合查询 -使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果为 M+N 行。 +使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果为 M+N 行。 每个查询必须包含相同的列、表达式或者聚集函数。 @@ -526,13 +526,13 @@ WHERE col5 = val; 存储过程可以看成是对一系列 SQL 操作的批处理; -**使用存储过程的好处** +**使用存储过程的好处** 1. 代码封装,保证了一定的安全性; 2. 代码复用; 3. 由于是预先编译,因此具有很高的性能。 -**创建存储过程** +**创建存储过程** 命令行中创建存储过程需要自定义分隔符,因为命令行是以 ; 为结束符,而存储过程中也包含了分号,因此会错误把这部分分号当成是结束符,造成语法错误。 @@ -567,7 +567,7 @@ select @ret; 游标主要用于交互式应用,其中用户需要对数据集中的任意行进行浏览和修改。 -**使用游标的四个步骤:** +**使用游标的四个步骤:** 1. 声明游标,这个过程没有实际检索出数据; 2. 打开游标; @@ -620,7 +620,7 @@ MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储 # 事务处理 -**基本术语** +**基本术语** 1. 事务(transaction)指一组 SQL 语句; 2. 回退(rollback)指撤销指定 SQL 语句的过程; @@ -647,7 +647,7 @@ COMMIT # 字符集 -**基本术语** +**基本术语** 1. 字符集为字母和符号的集合; 2. 编码为某个字符集成员的内部表示; @@ -678,7 +678,7 @@ USE mysql; SELECT user FROM user; ``` -**创建账户** +**创建账户** ```sql CREATE USER myuser IDENTIFIED BY 'mypassword'; @@ -686,25 +686,25 @@ CREATE USER myuser IDENTIFIED BY 'mypassword'; 新创建的账户没有任何权限。 -**修改账户名** +**修改账户名** ```sql RENAME myuser TO newuser; ``` -**删除账户** +**删除账户** ```sql DROP USER myuser; ``` -**查看权限** +**查看权限** ```sql SHOW GRANTS FOR myuser; ``` -**授予权限** +**授予权限** ```sql GRANT SELECT, INSERT ON mydatabase.* TO myuser; @@ -714,7 +714,7 @@ GRANT SELECT, INSERT ON mydatabase.* TO myuser; 账户用 username@host 的形式定义,username@% 使用的是默认主机名。 -**删除权限** +**删除权限** ```sql REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; @@ -728,7 +728,7 @@ GRANT 和 REVOKE 可在几个层次上控制访问权限: - 特定的列; - 特定的存储过程。 -**更改密码** +**更改密码** 必须使用 Password() 函数 diff --git a/notes/代码可读性.md b/notes/代码可读性.md index eaf80f4e..77ca74cc 100644 --- a/notes/代码可读性.md +++ b/notes/代码可读性.md @@ -173,7 +173,7 @@ if(a || b) { # 变量与可读性 -**去除控制流变量**。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 +**去除控制流变量** 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 ``` boolean done = false; @@ -194,7 +194,7 @@ while(/* condition */) { } ``` -**减小变量作用域**。作用域越小,越容易定位到变量所有使用的地方。 +**减小变量作用域** 。作用域越小,越容易定位到变量所有使用的地方。 JavaScript 可以用闭包减小作用域。以下代码中 submit_form 是函数变量,submitted 变量控制函数不会被提交两次。第一个实现中 submitted 是全局变量,第二个实现把 submitted 放到匿名函数中,从而限制了起作用域范围。 @@ -224,7 +224,7 @@ JavaScript 中没有用 var 声明的变量都是全局变量,而全局变量 变量定义的位置应当离它使用的位置最近。 -**实例解析** +**实例解析** 在一个网页中有以下文本输入字段: diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index 2698b8f2..32480ec9 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -93,11 +93,11 @@ ## 3. 数组中重复的数字 -**题目描述** +**题目描述** 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为 7 的数组 {2, 3, 1, 0, 2, 5, 3},那么对应的输出是第一个重复的数字 2。 -**解题思路** +**解题思路** 这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。 @@ -125,11 +125,11 @@ private void swap(int[] numbers, int i, int j) { ## 4. 二维数组中的查找 -**题目描述** +**题目描述** 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 -**解题思路** +**解题思路** 从右上角开始查找。因为矩阵中的一个数,它左边的数都比它来的小,下边的数都比它来的大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来改变行和列的下标,从而缩小查找区间。 @@ -149,15 +149,15 @@ public boolean Find(int target, int [][] array) { ## 5. 替换空格 -**题目描述** +**题目描述** 请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为 We Are Happy. 则经过替换之后的字符串为 We%20Are%20Happy。 -**题目要求** +**题目要求** 以 O(1) 的空间复杂度和 O(n) 的空间复杂度来求解。 -**解题思路** +**解题思路** 从后向前改变字符串。 @@ -254,7 +254,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { ## 7. 重建二叉树 -**题目描述** +**题目描述** 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。 @@ -278,11 +278,11 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, ## 8. 二叉树的下一个结点 -**题目描述** +**题目描述** 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。 -**解题思路** +**解题思路** - 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点; - 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 @@ -310,7 +310,7 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) { ## 9. 用两个栈实现队列 -**解题思路** +**解题思路** 添加到栈中的序列顺序会被反转,如果进行两次反转,那么得到的序列依然是正向的。因此,添加的数据需要同时压入两个栈之后才能出栈,这样就能保证出栈的顺序为先进先出。 @@ -382,7 +382,7 @@ public int JumpFloorII(int target) { ## 10.4 矩形覆盖 -**题目描述** +**题目描述** 我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法? @@ -395,7 +395,7 @@ public int RectCover(int target) { ## 11. 旋转数组的最小数字 -**题目描述** +**题目描述** 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转,该数组的最小值为 1。NOTE:给出的所有元素都大于 0,若数组大小为 0,请返回 0。 @@ -431,7 +431,7 @@ public int minNumberInRotateArray(int[] array) { ## 12. 矩阵中的路径 -**题目描述** +**题目描述** 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。例如 a b c e s f c s a d e e 矩阵中包含一条字符串 "bcced" 的路径,但是矩阵中不包含 "abcb" 路径,因为字符串的第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。 @@ -477,7 +477,7 @@ private boolean backtracking(char[][] matrix, char[] str, boolean[][] used, int ## 13. 机器人的运动范围 -**题目描述** +**题目描述** 地上有一个 m 行和 n 列的方格。一个机器人从坐标 (0, 0) 的格子开始移动,每一次只能向左右上下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于 k 的格子。例如,当 k 为 18 时,机器人能够进入方格(35, 37),因为 3+5+3+7=18。但是,它不能进入方格(35, 38),因为 3+5+3+8=19。请问该机器人能够达到多少个格子? @@ -523,15 +523,15 @@ private void initDigitSum(int rows, int cols) { ## 14. 剪绳子 -**题目描述** +**题目描述** 把一根绳子剪成多段,并且使得每段的长度乘积最大。 -**动态规划解法** +**动态规划解法** [ 分割整数 ](https://github.com/CyC2018/InterviewNotes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E5%88%86%E5%89%B2%E6%95%B4%E6%95%B0) -**贪心解法** +**贪心解法** 尽可能多得剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现,如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。 @@ -661,7 +661,7 @@ public ListNode deleteDuplication(ListNode pHead) { ## 19. 正则表达式匹配 -**题目描述** +**题目描述** 请实现一个函数用来匹配包括 '.' 和 '\*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '\*' 表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串 "aaa" 与模式 "a.a" 和 "ab\*ac\*a" 匹配,但是与 "aa.a" 和 "ab\*a" 均不匹配 @@ -688,7 +688,7 @@ public boolean match(char[] str, char[] pattern) { ## 20. 表示数值的字符串 -**题目描述** +**题目描述** 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 "+100","5e2","-123","3.1416" 和 "-1E-16" 都表示数值。 但是 "12e","1a3.14","1.2.3","+-5" 和 "12e+4.3" 都不是。 @@ -701,7 +701,7 @@ public boolean isNumeric(char[] str) { ## 21. 调整数组顺序使奇数位于偶数前面 -**题目要求** +**题目要求** 保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 @@ -1071,7 +1071,7 @@ private void dfs(TreeNode node, int target, int curSum, ArrayList path) ## 35. 复杂链表的复制 -**题目描述** +**题目描述** 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空) @@ -1122,7 +1122,7 @@ public RandomListNode Clone(RandomListNode pHead) { ## 36. 二叉搜索树与双向链表 -**题目描述** +**题目描述** 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 @@ -1180,7 +1180,7 @@ private TreeNode Deserialize() { ## 38. 字符串的排列 -**题目描述** +**题目描述** 输入一个字符串 , 按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc, 则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。 @@ -1317,7 +1317,7 @@ private boolean less(int v, int w) { ## 41.1 数据流中的中位数 -**题目描述** +**题目描述** 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 @@ -1350,7 +1350,7 @@ public Double GetMedian() { ## 41.2 字符流中第一个不重复的字符 -**题目描述** +**题目描述** 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。 @@ -1408,7 +1408,7 @@ public int NumberOf1Between1AndN_Solution(int n) { ## 44. 数字序列中的某一位数字 -**题目描述** +**题目描述** 数字以 0123456789101112131415... 的格式序列化到一个字符串中,求这个字符串的第 index 位。 @@ -1449,7 +1449,7 @@ public static void main(String[] args) { ## 45. 把数组排成最小的数 -**题目描述** +**题目描述** 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。 @@ -1467,7 +1467,7 @@ public String PrintMinNumber(int[] numbers) { ## 46. 把数字翻译成字符串 -**题目描述** +**题目描述** 给定一个数字,按照如下规则翻译成字符串:0 翻译成“a”,1 翻译成“b”...25 翻译成“z”。一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 bccfi,bwfi,bczi,mcfi,mzi。实现一个函数,用来计算一个数字有多少种不同的翻译方法。 @@ -1489,7 +1489,7 @@ public int getTranslationCount(String number) { ## 47. 礼物的最大价值 -**题目描述** +**题目描述** 在一个 m * n 的棋盘的每一个格都放有一个礼物,每个礼物都有一定价值(大于 0)。从左上角开始拿礼物,每次向右或向下移动一格,直到右下角结束。给定一个棋盘,求拿到礼物的最大价值。例如,对于如下棋盘 @@ -1502,7 +1502,7 @@ public int getTranslationCount(String number) { 礼物的最大价值为 1+12+5+7+7+16+5=53。 -**解题思路** +**解题思路** 应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。 @@ -1524,7 +1524,7 @@ public int getMaxValue(int[][] values) { ## 48. 最长不含重复字符的子字符串 -**题目描述** +**题目描述** 输入一个字符串(只包含 a\~z 的字符),求其最长不含重复字符的子字符串的长度。例如对于 arabcacfr,最长不含重复字符的子字符串为 acfr,长度为 4。 @@ -1550,7 +1550,7 @@ public int longestSubStringWithoutDuplication(String str) { ## 49. 丑数 -**题目描述** +**题目描述** 把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。 @@ -1687,11 +1687,11 @@ public int TreeDepth(TreeNode root) { ## 56. 数组中只出现一次的数字 -**题目描述** +**题目描述** 一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。 -**解题思路** +**解题思路** 两个不相等的元素在位级表示上必定会有一位存在不同。 @@ -1714,7 +1714,7 @@ public void FindNumsAppearOnce(int[] array, int num1[], int num2[]) { ## 57.1 和为 S 的两个数字 -**题目描述** +**题目描述** 输入一个递增排序的数组和一个数字 S,在数组中查找两个数,是的他们的和正好是 S,如果有多对数字的和等于 S,输出两个数的乘积最小的。 @@ -1733,7 +1733,7 @@ public ArrayList FindNumbersWithSum(int[] array, int sum) { ## 57.2 和为 S 的连续正数序列 -**题目描述** +**题目描述** 和为 100 的连续序列有 18, 19, 20, 21, 22 @@ -1768,7 +1768,7 @@ public ArrayList> FindContinuousSequence(int sum) { ## 58.1 翻转单词顺序列 -**题目描述** +**题目描述** 输入:"I am a student." @@ -1804,7 +1804,7 @@ private void reverse(char[] c, int start, int end) { ## 58.2 左旋转字符串 -**题目描述** +**题目描述** 对于一个给定的字符序列 S,请你把其循环左移 K 位后的序列输出。例如,字符序列 S=”abcXYZdef”, 要求输出循环左移 3 位后的结果,即“XYZdefabc”。 @@ -1831,7 +1831,7 @@ private void reverse(char[] c, int i, int j) { ## 59. 滑动窗口的最大值 -**题目描述** +**题目描述** 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为 {4, 4, 6, 6, 6, 5}; @@ -1853,7 +1853,7 @@ public ArrayList maxInWindows(int[] num, int size) { ## 60. n 个骰子的点数 -**题目描述** +**题目描述** 把 n 个骰子仍在地上,求点数和为 s 的概率。 @@ -1912,7 +1912,7 @@ public double countProbability(int n, int s) { ## 61. 扑克牌顺子 -**题目描述** +**题目描述** 五张牌,其中大小鬼为癞子,牌面大小为 0。判断是否能组成顺子。 @@ -1934,11 +1934,11 @@ public boolean isContinuous(int[] numbers) { ## 62. 圆圈中最后剩下的数 -**题目描述** +**题目描述** 让小朋友们围成一个大圈。然后 , 他随机指定一个数 m, 让编号为 0 的小朋友开始报数。每次喊到 m-1 的那个小朋友要出列唱首歌 , 然后可以在礼品箱中任意的挑选礼物 , 并且不再回到圈中 , 从他的下一个小朋友开始 , 继续 0...m-1 报数 .... 这样下去 .... 直到剩下最后一个小朋友 , 可以不用表演。 -**解题思路** +**解题思路** 约瑟夫环 @@ -1952,7 +1952,7 @@ public int LastRemaining_Solution(int n, int m) { ## 63. 股票的最大利润 -**题目描述** +**题目描述** 可以有一次买入和一次卖出,买入必须在前。求最大收益。 @@ -1972,7 +1972,7 @@ public int maxProfit(int[] prices) { ## 64. 求 1+2+3+...+n -**题目描述** +**题目描述** 求 1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C) @@ -1997,7 +1997,7 @@ public int Add(int num1, int num2) { ## 66. 构建乘积数组 -**题目描述** +**题目描述** 给定一个数组 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]。不能使用除法。 diff --git a/notes/算法.md b/notes/算法.md index c9a59b62..ddc682cf 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -90,23 +90,23 @@ T(N)=aN3 转换为 lg(T(N))=3lgN+lga ## 2. 数学模型 -**近似** +**近似** 使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数 , 例如 N3/6-N2/2+N/3 \~ N3/6。

-**增长数量级** +**增长数量级** 增长数量级将算法与它的实现隔离开来,一个算法的增长数量级为 N3 与它是否用 Java 实现,是否运行于特定计算机上无关。

-**内循环** +**内循环** 执行最频繁的指令决定了程序执行的总时间,把这些指令称为程序的内循环。 -**成本模型** +**成本模型** 使用成本模型来评估算法,例如数组的访问次数就是一种成本模型。 @@ -135,7 +135,7 @@ public class ThreeSum { 该程序的内循环为 if (a[i] + a[j] + a[k] == 0) 语句,总共执行的次数为 N3/6-N2/2+N/3,因此它的近似执行次数为 \~N3/6,增长数量级为 N3。 -**改进** +**改进** 通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。 @@ -172,23 +172,23 @@ public class ThreeSumFast { ## 5. 注意事项 -**大常数** +**大常数** 在求近似时,如果低级项的常数系数很大,那么近似的结果就是错误的。 -**缓存** +**缓存** 计算机系统会使用缓存技术来组织内存,访问数组相邻的元素会比访问不相邻的元素快很多。 -**对最坏情况下的性能的保证** +**对最坏情况下的性能的保证** 在核反应堆、心脏起搏器或者刹车控制器中的软件,最坏情况下的性能是十分重要的。 -**随机化算法** +**随机化算法** 通过打乱输入,去除算法对输入的依赖。 -**均摊分析** +**均摊分析** 将所有操作的总成本所以操作总数来将成本均摊。例如对一个空栈进行 N 次连续的 push() 调用需要访问数组的元素为 N+4+8+16+...+2N=5N-4(N 是向数组写入元素,其余的都是调整数组大小时进行复制需要的访问数组操作),均摊后每次操作访问数组的平均次数为常数。 @@ -704,9 +704,9 @@ public class BinarySearchST, Value> { ## 2. 二叉查找树 -**二叉树** 定义为一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 +**二叉树** 定义为一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 -**二叉查找树**(BST)是一颗二叉树,并且每个节点的键都大于其左子树中的任意节点的键而小于右子树的任意节点的键。 +**二叉查找树** (BST)是一颗二叉树,并且每个节点的键都大于其左子树中的任意节点的键而小于右子树的任意节点的键。

@@ -921,7 +921,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 2-3 查找树插入操作的变换都是局部的,除了相关的节点和链接之外不必修改或者检查树的其它部分,而这些局部变换不会影响树的全局有序性和平衡性。 -2-3 查找树的查找和插入操作复杂度和插入顺序 **无关**,在最坏的情况下查找和插入操作访问的节点必然不超过 logN 个,含有 10 亿个节点的 2-3 查找树最多只需要访问 30 个节点就能进行任意的查找和插入操作。 +2-3 查找树的查找和插入操作复杂度和插入顺序 **无关** ,在最坏的情况下查找和插入操作访问的节点必然不超过 logN 个,含有 10 亿个节点的 2-3 查找树最多只需要访问 30 个节点就能进行任意的查找和插入操作。 ### 3.2 红黑二叉查找树 diff --git a/notes/计算机操作系统.md b/notes/计算机操作系统.md index 519f4c6a..e75dbad2 100644 --- a/notes/计算机操作系统.md +++ b/notes/计算机操作系统.md @@ -259,14 +259,14 @@ shortest remaining time next(SRTN)。 ### 3. 信号量 -**信号量(Semaphore)** 是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。 +**信号量(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 ; @@ -284,7 +284,7 @@ void P2() { } ``` -**使用信号量实现生产者-消费者问题** +**使用信号量实现生产者-消费者问题** 使用一个互斥量 mutex 来对临界资源进行访问;empty 记录空缓冲区的数量,full 记录满缓冲区的数量。 @@ -345,9 +345,9 @@ end monitor; 管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否者其它进程永远不能使用管程。 -管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来让另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 +管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来让另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 -**使用管程实现生成者-消费者问题** +**使用管程实现生成者-消费者问题** ```pascal monitor ProducerConsumer diff --git a/notes/计算机网络.md b/notes/计算机网络.md index 5c759fb9..6bc3fb46 100644 --- a/notes/计算机网络.md +++ b/notes/计算机网络.md @@ -116,11 +116,11 @@ ## 主机之间的通信方式 -**1. 客户-服务器(C/S)** +**1. 客户-服务器(C/S)** 客户是服务的请求方,服务器是服务的提供方。 -**2. 对等(P2P)** +**2. 对等(P2P)** 不区分客户和服务器。 @@ -324,15 +324,15 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 CSMA/CD 表示载波监听多点接入 / 碰撞检测。 -- **多点接入**:说明这是总线型网络,许多计算机以多点的方式连接到总线上。 -- **载波监听**:每个站都必须不停地检听信道。在发送前,如果检听信道正在使用,就必须等待。 -- **碰撞检测**:在发送中,如果检听到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经检听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 +- **多点接入** :说明这是总线型网络,许多计算机以多点的方式连接到总线上。 +- **载波监听** :每个站都必须不停地检听信道。在发送前,如果检听信道正在使用,就必须等待。 +- **碰撞检测** :在发送中,如果检听到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经检听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。

-记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期**。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 +记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 -当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定,从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 +当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 **截断二进制指数退避算法** 来确定,从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 ## 集线器 @@ -348,10 +348,10 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器

-- **类型**:标记上层使用的协议; -- **数据**:长度在 46-1500 之间,如果太小则需要填充; -- **FCS**:帧检验序列,使用的是 CRC 检验方法; -- **前同步码**:只是为了计算 FCS 临时加入的,计算结束之后会丢弃。 +- **类型** :标记上层使用的协议; +- **数据** :长度在 46-1500 之间,如果太小则需要填充; +- **FCS** :帧检验序列,使用的是 CRC 检验方法; +- **前同步码** :只是为了计算 FCS 临时加入的,计算结束之后会丢弃。 ## 虚拟局域网 @@ -381,25 +381,25 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器

-- **版本** : 有 4(IPv4)和 6(IPv6)两个值; +- **版本** : 有 4(IPv4)和 6(IPv6)两个值; -- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选部分的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 +- **首部长度** : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选部分的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 -- **区分服务** : 用来获得更好的服务,一般情况下不使用。 +- **区分服务** : 用来获得更好的服务,一般情况下不使用。 -- **总长度** : 包括首部长度和数据部分长度。 +- **总长度** : 包括首部长度和数据部分长度。 -- **标识** : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 +- **标识** : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 -- **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 +- **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。

-- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 +- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 -- **协议**:指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 +- **协议** :指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 -- **首部检验和**:因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 +- **首部检验和** :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 ## IP 地址编址 @@ -435,7 +435,7 @@ CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法, CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 -一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网**。 +一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 @@ -605,19 +605,19 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。

-- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 +- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 -- **确认号** :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 +- **确认号** :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 -- **数据偏移** :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 +- **数据偏移** :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 -- **确认 ACK** :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 +- **确认 ACK** :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 -- **同步 SYN** :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 +- **同步 SYN** :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 -- **终止 FIN** :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。 +- **终止 FIN** :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。 -- **窗口** :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 +- **窗口** :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 ## TCP 的三次握手 @@ -643,7 +643,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 3. 当 B 要不再需要连接时,发送连接释放请求报文段,FIN=1; 4. A 收到后发出确认,此时连接释放。 -**TIME_WAIT** +**TIME_WAIT** 客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间。这么做有两个理由: diff --git a/notes/设计模式.md b/notes/设计模式.md index 5b7edc19..33c10b90 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -36,47 +36,47 @@ # 第一章 设计模式入门 -**1. 设计模式概念** +**1. 设计模式概念** 设计模式不是代码,而是解决问题的方案,学习现有的设计模式可以做到经验复用。 拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。 -**2. 问题描述** +**2. 问题描述** 设计不同种类的鸭子拥有不同的叫声和飞行方式。 -**3. 简单实现方案** +**3. 简单实现方案** 使用继承的解决方案如下,这种方案代码无法复用,如果两个鸭子类拥有同样的飞行方式,就有两份重复的代码。

-**4. 设计原则** +**4. 设计原则** -**封装变化**在这里变化的是鸭子叫和飞行的行为方式。 +**封装变化** 在这里变化的是鸭子叫和飞行的行为方式。 -**针对接口编程,而不是针对实现编程** 变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。 +**针对接口编程,而不是针对实现编程** 变量声明的类型为父类,而不是具体的某个子类。父类中的方法实现不在父类,而是在各个子类。程序在运行时可以动态改变变量所指向的子类类型。 运用这一原则,将叫和飞行的行为抽象出来,实现多种不同的叫和飞行的子类,让子类去实现具体的叫和飞行方式。

-**多用组合,少用继承** 组合也就是 has-a 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。 +**多用组合,少用继承** 组合也就是 has-a 关系,通过组合,可以在运行时动态改变实现,只要通过改变父类对象具体指向哪个子类即可。而继承就不能做到这些,继承体系在创建类时就已经确定。 运用这一原则,在 Duck 类中组合 FlyBehavior 和 QuackBehavior 类,performQuack() 和 performFly() 方法委托给这两个类去处理。通过这种方式,一个 Duck 子类可以根据需要去实例化 FlyBehavior 和 QuackBehavior 的子类对象,并且也可以动态地进行改变。

-**5. 整体设计图** +**5. 整体设计图**

-**6. 模式定义** +**6. 模式定义** -**策略模式** :定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 +**策略模式** :定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。 -**7. 实现代码** +**7. 实现代码** ```java public abstract class Duck { @@ -181,13 +181,13 @@ FlyBehavior.FlyNoWay # 第二章 观察者模式 -**1. 模式定义** +**1. 模式定义** 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)成为观察者。

-**2. 模式类图** +**2. 模式类图** 主题中具有注册和移除观察者,并通知所有注册者的功能,主题是通过维护一张观察者列表来实现这些操作的。 @@ -195,19 +195,19 @@ FlyBehavior.FlyNoWay

-**3. 问题描述** +**3. 问题描述** 天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 -**4. 解决方案类图** +**4. 解决方案类图**

-**5. 设计原则** +**5. 设计原则** -**为交互对象之间的松耦合设计而努力** 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。由于松耦合的两个对象之间互相依赖程度很低,因此系统具有弹性,能够应对变化。 +**为交互对象之间的松耦合设计而努力** 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。由于松耦合的两个对象之间互相依赖程度很低,因此系统具有弹性,能够应对变化。 -**6. 实现代码** +**6. 实现代码** ```java public interface Subject { @@ -315,11 +315,11 @@ StatisticsDisplay.update:1.0 1.0 1.0 # 第三章 装饰模式 -**1. 问题描述** +**1. 问题描述** 设计不同种类的饮料,并且每种饮料可以动态添加新的材料,比如可以添加牛奶。计算一种饮料的价格。 -**2. 模式定义** +**2. 模式定义** 动态地将责任附加到对象上。在扩展功能上,装饰者提供了比继承更有弹性的替代方案。 @@ -327,25 +327,25 @@ StatisticsDisplay.update:1.0 1.0 1.0

-**3. 模式类图** +**3. 模式类图** 装饰者和具体组件都继承自组件类型,其中具体组件的方法实现不需要依赖于其它对象,而装饰者拥有一个组件类型对象,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰的对象之外,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件有直接实现而不需要委托给其它对象去处理。

-**4. 问题解决方案的类图** +**4. 问题解决方案的类图**

-**5. 设计原则** +**5. 设计原则** -**类应该对扩展开放,对修改关闭。** 也就是添加新功能时不需要修改代码。在本章问题中该原则体现在,在饮料中添加新的材料,而不需要去修改饮料的代码。观察则模式也符合这个原则。不可能所有类都能实现这个原则,应当把该原则应用于设计中最有可能改变的地方。 +**类应该对扩展开放,对修改关闭。** 也就是添加新功能时不需要修改代码。在本章问题中该原则体现在,在饮料中添加新的材料,而不需要去修改饮料的代码。观察则模式也符合这个原则。不可能所有类都能实现这个原则,应当把该原则应用于设计中最有可能改变的地方。 -**6. Java I/O 中的装饰者模式** +**6. Java I/O 中的装饰者模式**

-**7. 代码实现** +**7. 代码实现** ```java public interface Beverage { @@ -420,22 +420,22 @@ public class StartbuzzCoffee { ## 1. 简单工厂 -**1. 问题描述** +**1. 问题描述** 有不同的 Pizza,根据不同的情况用不同的子类实例化一个 Pizza 对象。 -**2. 定义** +**2. 定义** 简单工厂不是设计模式,更像是一种编程习惯。在实例化一个超类的对象时,可以用它的所有子类来进行实例化,要根据具体需求来决定使用哪个子类。在这种情况下,把实例化的操作放到工厂来中,让工厂类来决定应该用哪个子类来实例化。这样做把客户对象和具体子类的实现解耦,客户对象不再需要知道有哪些子类以及实例化哪个子类。因为客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节,一旦子类发生改变,例如增加子类,那么所有的客户类都要发生改变。

-**3. 解决方案类图** +**3. 解决方案类图**

-**4. 代码实现** +**4. 代码实现** ```java public interface Pizza { @@ -489,15 +489,15 @@ CheesePizza ## 2. 工厂方法模式 -**1. 问题描述** +**1. 问题描述** 每个地区的 Pizza 店虽然种类相同,但是都有自己的风味,需要单独区分。例如,一个客户点了纽约的 cheese 种类的 Pizza 和在芝加哥点的相同种类的 Pizza 是不同的。 -**2. 模式定义** +**2. 模式定义** 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 -**3. 模式类图** +**3. 模式类图** 在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。下图中,Creator 有一个 anOperation() 方法,这个方法需要用到一组产品类,这组产品类由每个子类来创建。 @@ -505,11 +505,11 @@ CheesePizza

-**4. 解决方案类图** +**4. 解决方案类图**

-**5. 代码实现** +**5. 代码实现** ```java public interface Pizza { @@ -607,27 +607,27 @@ ChicagoStyleCheesePizza is making.. ## 3. 抽象工厂模式 -**1. 设计原则** +**1. 设计原则** -**依赖倒置原则**:要依赖抽象,不要依赖具体类。听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 PizzaStore 属于高层组件,它依赖的是 Pizza 的抽象类,这样就可以不用关心 Pizza 的具体实现细节。 +**依赖倒置原则** :要依赖抽象,不要依赖具体类。听起来像是针对接口编程,不针对实现编程,但是这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,两者都应该依赖于抽象。例如,下图中 PizzaStore 属于高层组件,它依赖的是 Pizza 的抽象类,这样就可以不用关心 Pizza 的具体实现细节。

-**2. 模式定义** +**2. 模式定义** 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。 -**3. 模式类图** +**3. 模式类图** 抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂模式只是用于创建一个对象,这和抽象工厂模式有很大不同。并且,抽象工厂模式也用到了工厂模式来创建单一对象,在类图左部,AbstractFactory 中的 CreateProductA 和 CreateProductB 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂模式的定义。至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要这两个对象的协作才能完成任务。从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory ,而工厂模式使用了继承。

-**4. 解决方案类图** +**4. 解决方案类图**

-**5. 代码实现** +**5. 代码实现** ```java public interface Dough { @@ -738,17 +738,17 @@ MarinaraSauce # 第五章 单件模式 -**1. 模式定义** +**1. 模式定义** 确保一个类只有一个实例,并提供了一个全局访问点。 -**2. 模式类图** +**2. 模式类图** 使用一个私有构造器、一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。

-**3. 懒汉式-线程不安全** +**3. 懒汉式-线程不安全** 以下实现中,私有静态变量被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会创建私有静态变量,从而节约资源。 @@ -771,7 +771,7 @@ public class Singleton { } ``` -**4. 懒汉式-线程安全** +**4. 懒汉式-线程安全** 只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了对 uniqueInstance 进行多次实例化的问题。但是这样有一个问题,就是当一个线程进入该方法之后,其它线程视图进入该方法都必须等待,因此性能上有一定的损耗。 @@ -784,7 +784,7 @@ public class Singleton { } ``` -**5. 饿汉式-线程安全** +**5. 饿汉式-线程安全** 线程不安全问题主要是由于静态实例变量被初始化了多次,那么静态实例变量采用直接实例化就可以解决问题。但是直接初始化的方法也丢失了延迟初始化节约资源的优势。 @@ -792,7 +792,7 @@ public class Singleton { private static Singleton uniqueInstance = new Singleton(); ``` -**6. 双重校验锁-线程安全** +**6. 双重校验锁-线程安全** 因为 uniqueInstance 只需要被初始化一次,之后就可以直接使用了。加锁操作只需要对初始化那部分的代码进行,也就是说,只有当 uniqueInstance 没有被初始化时,才需要进行加锁。 @@ -822,7 +822,7 @@ public class Singleton { # 第六章 命令模式 -**1. 问题描述** +**1. 问题描述** 设计一个遥控器,它有很多按钮,每个按钮可以发起一个命令,让一个家电完成相应操作。 @@ -832,11 +832,11 @@ public class Singleton {

-**2. 模式定义** +**2. 模式定义** 将命令封装成对象,以便使用不同的命令来参数化其它对象。 -**3. 解决方案类图** +**3. 解决方案类图** - RemoteControl 是遥控器,它可以为每个按钮设置命令对象,并且调用命令对象的 execute() 方法。 @@ -848,11 +848,11 @@ public class Singleton {

-**4. 模式类图** +**4. 模式类图**

-**5. 代码实现** +**5. 代码实现** ```java public interface Command { @@ -935,13 +935,13 @@ Light is on! ## 1. 适配器模式 -**1. 模式定义** +**1. 模式定义** 将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。

-**2. 模式类图** +**2. 模式类图** 有两种适配器模式的实现,一种是对象方式,一种是类方式。对象方式是通过组合的方法,让适配器类(Adapter)拥有一个待适配的对象(Adaptee),从而把相应的处理委托给待适配的对象。类方式用到多重继承,Adapter 继承 Target 和 Adaptee,先把 Adapter 当成 Adaptee 类型然后实例化一个对象,再把它当成 Target 类型的,这样 Client 就可以把这个对象当成 Target 的对象来处理,同时拥有 Adaptee 的方法。 @@ -949,17 +949,17 @@ Light is on!

-**3. 问题描述** +**3. 问题描述** 鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 调用的是 quack() 方法,而 Turkey 调用 gobble() 方法。 要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法。 -**4. 解决方案类图** +**4. 解决方案类图**

-**5. 代码实现** +**5. 代码实现** ```java public interface Duck { @@ -1015,67 +1015,67 @@ gobble! ## 2. 外观模式 -**1. 模式定义** +**1. 模式定义** 提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。 -**2. 模式类图** +**2. 模式类图**

-**3. 问题描述** +**3. 问题描述** 家庭影院中有众多电器,当要进行观看电影时需要对很多电器进行操作。要求简化这些操作,使得家庭影院类只提供一个简化的接口,例如提供一个看电影相关的接口。

-**4. 解决方案类图** +**4. 解决方案类图**

-**5. 设计原则** +**5. 设计原则** -**最少知识原则**:只和你的密友谈话。也就是应当使得客户对象所需要交互的对象尽可能少。 +**最少知识原则** :只和你的密友谈话。也就是应当使得客户对象所需要交互的对象尽可能少。 -**6. 代码实现** +**6. 代码实现** 过于简单,无实现。 # 第八章 模板方法模式 -**1. 模式定义** +**1. 模式定义** 在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。 -**2. 模式类图** +**2. 模式类图** 模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去实现。

-**3. 问题描述** +**3. 问题描述** 冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。

-**4. 解决方案类图** +**4. 解决方案类图** 其中 prepareRecipe() 方法就是模板方法,它确定了其它四个方法的具体执行步骤。其中 brew() 和 addCondiments() 方法在子类中实现。

-**5. 设计原则** +**5. 设计原则** -**好莱坞原则**:别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖低层组件,低层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。 +**好莱坞原则** :别调用(打电话给)我们,我们会调用(打电话给)你。这一原则可以防止依赖腐败,即防止高层组件依赖低层组件,低层组件又依赖高层组件。该原则在模板方法的体现为,只有父类会调用子类,子类不会调用父类。 -**6. 钩子** +**6. 钩子** 钩子(hock):某些步骤在不同实现中可有可无,可以先定义一个什么都不做的方法,把它加到模板方法中,如果子类需要它就覆盖默认实现并加上自己的实现。 -**7. 代码实现** +**7. 代码实现** ```java public abstract class CaffeineBeverage { @@ -1159,11 +1159,11 @@ Tea.addCondiments ## 1. 迭代器模式 -**1. 模式定义** +**1. 模式定义** 提供顺序访问一个聚合对象中的各个元素的方法,而又不暴露聚合对象内部的表示。 -**2. 模式类图** +**2. 模式类图** - Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator 对象; @@ -1173,7 +1173,7 @@ Tea.addCondiments

-**3. 代码实现** +**3. 代码实现** ```java public class Aggregate { @@ -1249,7 +1249,7 @@ public class Client { ## 2. Java 内置的迭代器 -**1. 实现接口** +**1. 实现接口** 在使用 Java 的迭代器实现时,需要让聚合对象去实现 Iterable 接口,该接口有一个 iterator() 方法会返回一个 Iterator 对象。 @@ -1257,7 +1257,7 @@ public class Client { Java 中的集合类基本都实现了 Iterable 接口。 -**2. 代码实现** +**2. 代码实现** ```java import java.util.Iterator; @@ -1315,17 +1315,17 @@ public class Client { ## 3. 组合模式 -**1. 设计原则** +**1. 设计原则** 一个类应该只有一个引起改变的原因。 -**2. 模式定义** +**2. 模式定义** 允许将对象组合成树形结构来表现“整体/部分”层次结构。 组合能让客户以一致的方式处理个别对象以及组合对象。 -**3. 模式类图** +**3. 模式类图** 组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。 @@ -1333,7 +1333,7 @@ public class Client {

-**4. 代码实现** +**4. 代码实现** ```java public abstract class Component { @@ -1436,17 +1436,17 @@ Composite:root # 第十章 状态模式 -**1. 模式定义** +**1. 模式定义** 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。 -**2. 模式类图** +**2. 模式类图** Context 的 request() 方法委托给 State 对象去处理。当 Context 组合的 State 对象发生改变时,它的行为也就发生了改变。

-**3. 与策略模式的比较** +**3. 与策略模式的比较** 状态模式的类图和策略模式一样,并且都是能够动态改变对象的行为。 @@ -1456,13 +1456,13 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合 状态模式主要是用来解决状态转移的问题,当状态发生庄毅了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 需要使用哪个算法。 -**4. 问题描述** +**4. 问题描述** 糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。

-**5. 直接解决方案** +**5. 直接解决方案** 在糖果机的每个操作函数里面,判断当前的状态,根据不同的状态进行不同的处理,并且发生不同的状态转移。 @@ -1470,7 +1470,7 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合

-**6 代码实现** +**6 代码实现** 糖果销售机即 Context。 @@ -1757,13 +1757,13 @@ No gumball dispensed ## MVC -**传统 MVC** +**传统 MVC** 视图使用组合模式,模型使用了观察者模式,控制器使用了策略模式。

-**Web 中的 MVC** +**Web 中的 MVC** 模式不再使用观察者模式。 @@ -1771,7 +1771,7 @@ No gumball dispensed # 第十三章 与设计模式相处 -定义:在某 **情境** 下,针对某 **问题** 的某种 **解决方案**。 +定义:在某 **情境** 下,针对某 **问题** 的某种 **解决方案**。 过度使用设计模式可能导致代码被过度工程化,应该总是用最简单的解决方案完成工作,并在真正需要模式的地方才使用它。 diff --git a/notes/重构.md b/notes/重构.md index 4ccbca26..8e2d6da7 100644 --- a/notes/重构.md +++ b/notes/重构.md @@ -118,7 +118,7 @@ 重构需要以微小的步伐修改程序,如果重构过程发生错误,很容易就能发现错误。 -**案例分析** +**案例分析** 影片出租店应用程序,需要计算每位顾客的消费金额。 diff --git a/notes/面向对象思想.md b/notes/面向对象思想.md index acfe39a0..204d380c 100644 --- a/notes/面向对象思想.md +++ b/notes/面向对象思想.md @@ -95,9 +95,9 @@ public class Person { ## 2. 继承 -继承实现了 **is-a** 关系,例如 Cat 和 Animal 就是一种 is-a 关系,因此可以将 Cat 继承自 Animal,从而获得 Animal 非 private 的属性和方法。 +继承实现了 **is-a** 关系,例如 Cat 和 Animal 就是一种 is-a 关系,因此可以将 Cat 继承自 Animal,从而获得 Animal 非 private 的属性和方法。 -Cat 可以当做 Animal 来使用,也就是可以使用 Animal 引用 Cat 对象,这种子类转换为父类称为 **向上转型**。 +Cat 可以当做 Animal 来使用,也就是可以使用 Animal 引用 Cat 对象,这种子类转换为父类称为 **向上转型** 。 继承应该遵循里氏替换原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有 is-a 关系。 @@ -151,7 +151,7 @@ public class Music { ## 1. 类图 -**1.1 继承相关** +**1.1 继承相关** 继承有两种形式 : 泛化(generalize)和实现(realize),表现为 is-a 关系。 @@ -167,7 +167,7 @@ public class Music {

-**1.2 整体和部分** +**1.2 整体和部分** ① 聚合关系 (aggregation) @@ -181,7 +181,7 @@ public class Music {

-**1.3 相互联系** +**1.3 相互联系** ① 关联关系 (association) @@ -199,11 +199,11 @@ public class Music { http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html -**2.1 定义** +**2.1 定义** 时序图描述了对象之间传递消息的时间顺序,它用来表示用例的行为顺序。它的主要作用是通过对象间的交互来描述用例(注意是对象),从而寻找类的操作。 -**2.2 赤壁之战时序图** +**2.2 赤壁之战时序图** 从虚线从上往下表示时间的推进。 @@ -235,17 +235,17 @@ public class 孙权 { } ``` -**2.3 活动图、时序图之间的关系** +**2.3 活动图、时序图之间的关系** 活动图示从用户的角度来描述用例; 时序图是从计算机的角度(对象间的交互)描述用例。 -**2.4 类图与时序图的关系** +**2.4 类图与时序图的关系** 类图描述系统的静态结构,时序图描述系统的动态行为。 -**2.5 时序图的组成** +**2.5 时序图的组成** ① 对象