From c7dd125bca1664054cf40cd4d5ed9b08852291bc Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Thu, 8 Mar 2018 15:10:38 +0800 Subject: [PATCH] auto commit --- notes/HTTP.md | 28 +++++----- notes/JVM.md | 26 ++++----- notes/Java IO.md | 18 +++--- notes/Java 基础.md | 8 +-- notes/Java 容器.md | 2 +- notes/Java 并发.md | 2 +- notes/Leetcode 题解.md | 20 +++---- notes/Linux.md | 16 +++--- notes/MySQL.md | 10 ++-- notes/SQL 语法.md | 2 +- notes/代码可读性.md | 2 +- notes/剑指 offer 题解.md | 8 +-- notes/算法.md | 82 +++++++++++++-------------- notes/计算机操作系统.md | 22 ++++---- notes/计算机网络.md | 116 +++++++++++++++++++-------------------- notes/设计模式.md | 84 ++++++++++++++-------------- notes/重构.md | 24 ++++---- notes/面向对象思想.md | 26 ++++----- 18 files changed, 248 insertions(+), 248 deletions(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index 5d977738..b0a1d0d5 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -57,17 +57,17 @@ URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。 -

+
## 请求和响应报文 **请求报文** -

+
**响应报文** -

+
# HTTP 方法 @@ -120,13 +120,13 @@ GET 的传参方式相比于 POST 安全性较差,因为 GET 传的参数在 U TRACE 一般不会使用,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪),因此更不会去使用它。 -

+
## CONNECT:要求用隧道协议连接代理 主要使用 SSL(Secure Sokets Layer,安全套接字)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。 -

+
# HTTP 状态码 @@ -168,7 +168,7 @@ TRACE 一般不会使用,并且它容易受到 XST 攻击(Cross-Site Tracing - **401 Unauthorized** :该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。如果之前已进行过一次请求,则表示用户认证失败。 -

+
- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。 @@ -261,7 +261,7 @@ HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使 服务器发送的响应报文包含 Set-Cookie 字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。下次再发送请求时,从浏览器中读出 Cookie 值,在请求报文中包含 Cookie 字段,这样服务器就知道客户端的状态信息了。Cookie 状态信息保存在客户端浏览器中,而不是服务器上。 -

+
Set-Cookie 字段有以下属性: @@ -298,13 +298,13 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。 当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。 **持久连接** 只需要进行一次 TCP 连接就能进行多次 HTTP 通信。HTTP/1.1 开始,所有的连接默认都是持久连接。 -

+
持久连接需要使用 Connection 首部字段进行管理。HTTP/1.1 开始 HTTP 默认是持久化连接的,如果要断开 TCP 连接,需要由客户端或者服务器端提出断开,使用 Connection: close;而在 HTTP/1.1 之前默认是非持久化连接的,如果要维持持续连接,需要使用 Keep-Alive。 管线化方式可以同时发送多个请求和响应,而不需要发送一个请求然后等待响应之后再发下一个请求。 -

+
## 编码 @@ -320,7 +320,7 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。 例如,上传多个表单时可以使用如下方式: -

+
## 范围请求 @@ -348,19 +348,19 @@ Expires 字段可以用于告知缓存服务器该资源什么时候会过期。 使用代理的主要目的是:缓存、网络访问控制以及访问日志记录。 -

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

+
**隧道** 使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。 -

+
# HTTPs @@ -380,7 +380,7 @@ HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Socket Layer)通信 HTTPs 采用 **混合的加密机制** ,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥) -

+
## 认证 diff --git a/notes/JVM.md b/notes/JVM.md index 4817a28d..a7e7e468 100644 --- a/notes/JVM.md +++ b/notes/JVM.md @@ -65,7 +65,7 @@ # 内存模型 -

+
注:白色区域为线程私有的,蓝色区域为线程共享的。 @@ -225,7 +225,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 2.1 标记 - 清除算法 -

+
将需要回收的对象进行标记,然后清除。 @@ -238,7 +238,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 2.2 复制算法 -

+
将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 @@ -248,7 +248,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 2.3 标记 - 整理算法 -

+
让所有存活的对象都向一段移动,然后直接清理掉端边界以外的内存。 @@ -263,13 +263,13 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ## 3. 垃圾收集器 -

+
以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 ### 3.1 Serial 收集器 -

+
它是单线程的收集器,不仅意味着只会使用一个线程进行垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停所有其他工作线程,往往造成过长的等待时间。 @@ -279,7 +279,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 3.2 ParNew 收集器 -

+
它是 Serial 收集器的多线程版本。 @@ -301,7 +301,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。 ### 3.4 Serial Old 收集器 -

+
Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下的虚拟机使用。如果用在 Server 模式下,它有两大用途: @@ -310,7 +310,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下 ### 3.5 Parallel Old 收集器 -

+
是 Parallel Scavenge 收集器的老年代版本。 @@ -318,7 +318,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下 ### 3.6 CMS 收集器 -

+
CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于 标记 - 清除 算法实现的。 @@ -343,7 +343,7 @@ CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于 标记 ### 3.7 G1 收集器 -

+
G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,它是一款面向服务端应用的垃圾收集器,HotSpot 开发团队赋予它的使命是(在比较长期的)未来可以替换掉 JDK 1.5 中发布的 CMS 收集器。 @@ -433,7 +433,7 @@ JVM 为对象定义年龄计数器,经过 Minor GC 依然存活且被 Survivor ## 1 类的生命周期 -

+
包括以下 7 个阶段: @@ -610,7 +610,7 @@ public static void main(String[] args) { 应用程序都是由三种类加载器相互配合进行加载的,如果有必要,还可以加入自己定义的类加载器。下图展示的类加载器之间的层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器,这里类加载器之间的父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)的关系实现。 -

+
**工作过程** diff --git a/notes/Java IO.md b/notes/Java IO.md index 40828f59..9ac3024c 100644 --- a/notes/Java IO.md +++ b/notes/Java IO.md @@ -48,7 +48,7 @@ File 类可以用于表示文件和目录,但是它只用于表示文件的信 # 字节操作 -

+
Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStream 是抽象组件,FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作。FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 @@ -140,7 +140,7 @@ is.close(); Socket 通信模型 -

+
- ServerSocket:服务器端类 - Socket:客户端类 @@ -208,23 +208,23 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 1\. 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit == capacity == 9。capacity 变量不会改变,下面的讨论会忽略它。 -

+
2\. 从输入通道中读取 3 个字节数据写入缓冲区中,此时 position 移动设为 3,limit 保持不变。 -

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

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

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

+
## 4. 读写文件实例 @@ -283,7 +283,7 @@ buffer.clear(); 阻塞式 I/O 在调用 InputStream.read() 方法时会一直等到数据到来时(或超时)才会返回,在调用 ServerSocket.accept() 方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。 -

+
### 5.2 非阻塞式 I/O @@ -293,7 +293,7 @@ buffer.clear(); 线程通信:线程之间通过 wait()、notify() 等方式通信,保证每次上下文切换都是有意义的,减少无谓的线程切换。 -

+
## 6. 套接字实例 diff --git a/notes/Java 基础.md b/notes/Java 基础.md index 97be460d..6879f444 100644 --- a/notes/Java 基础.md +++ b/notes/Java 基础.md @@ -131,13 +131,13 @@ public InitialOrderTest() { 引用类型引用的是同一个对象,clone() 方法默认就是浅拷贝实现。 -

+
**深拷贝** 可以使用序列化实现。 -

+
> [How do I copy an object in Java?](https://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java) @@ -235,7 +235,7 @@ StringBuilder 不是线程安全的;StringBuffer 是线程安全的,使用 s 如果 String 已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 -

+
**安全性** @@ -381,7 +381,7 @@ Throwable 可以用来表示任何可以作为异常抛出的类,分为两种 Exception 分为两种: **受检异常** 和 **非受检异常**。受检异常需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。 -

+
更详细的内容: - [Java 入门之异常处理 ](https://www.tianmaying.com/tutorial/Java-Exception) diff --git a/notes/Java 容器.md b/notes/Java 容器.md index 709491ac..7a1579d8 100644 --- a/notes/Java 容器.md +++ b/notes/Java 容器.md @@ -23,7 +23,7 @@ # 概览 -

+
容器主要包括 Collection 和 Map 两种,Collection 又包含了 List、Set 以及 Queue。 diff --git a/notes/Java 并发.md b/notes/Java 并发.md index 949e46fb..5dd6d4ba 100644 --- a/notes/Java 并发.md +++ b/notes/Java 并发.md @@ -347,7 +347,7 @@ JDK 从 1.5 开始在 Thread 类中增添了 State 枚举,包含以下六种 5. **TIMED_WAITING** (调用 sleep()、带超时的 wait() 或者 join()) 6. **TERMINATED** (死亡) -

+
# 结束线程 diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index e1e233f8..324e617c 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -693,7 +693,7 @@ public List topKFrequent(int[] nums, int k) { ### BFS -

+
广度优先搜索的搜索过程有点像一层一层地进行遍历:从节点 0 出发,遍历到 6、2、1 和 5 这四个新节点。 @@ -750,7 +750,7 @@ private class Position { ### DFS -

+
广度优先搜索一层一层遍历,每一层遍历到的所有新节点,要用队列先存储起来以备下一层遍历的时候再遍历;而深度优先搜索在遍历到一个新节点时立马对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 @@ -1036,7 +1036,7 @@ private void dfs(int r, int c, boolean[][] canReach) { [Leetcode : 51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/) -

+
题目描述:在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,要求解所有的 n 皇后解。 @@ -1044,11 +1044,11 @@ private void dfs(int r, int c, boolean[][] canReach) { 45 度对角线标记数组的维度为 2\*n - 1,通过下图可以明确 (r,c) 的位置所在的数组下标为 r + c。 -

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

+
```java private List> ret; @@ -1105,7 +1105,7 @@ private void backstracking(int row) { [Leetcode : 17. Letter Combinations of a Phone Number (Medium)](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/) -

+
```html Input:Digit string "23" @@ -1547,7 +1547,7 @@ private boolean isPalindrome(String s, int begin, int end) { [Leetcode : 37. Sudoku Solver (Hard)](https://leetcode.com/problems/sudoku-solver/description/) -

+
```java private boolean[][] rowsUsed = new boolean[9][10]; @@ -2471,7 +2471,7 @@ public int minDistance(String word1, String word2) { 题目描述:交易之后需要有一天的冷却时间。 -

+
```html s0[i] = max(s0[i - 1], s2[i - 1]); // Stay at s0, or rest from s2 @@ -3768,7 +3768,7 @@ private boolean isEqual(ListNode l1, ListNode l2){ [编程之美:3.4]() -

+
```java B.val = C.val; @@ -4484,7 +4484,7 @@ private void inorder(TreeNode node, int k) { ### Trie -

+
Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 diff --git a/notes/Linux.md b/notes/Linux.md index d3d2dbf8..b8cd50f0 100644 --- a/notes/Linux.md +++ b/notes/Linux.md @@ -221,7 +221,7 @@ GPT 第 1 个区块记录了 MBR,紧接着是 33 个区块记录分区信息 GPT 没有扩展分区概念,都是主分区,最多可以分 128 个分区。 -

+
## 开机检测程序 @@ -231,7 +231,7 @@ BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可 MBR 中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动 MBR 中的开机管理程序时,就可以选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。 -

+
安装多重引导,最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉 MBR,而 Linux 可以选择将开机管理程序安装在 MBR 或者其它分区的启动扇区,并且可以设置开机管理程序的选单。 @@ -243,7 +243,7 @@ UEFI 相比于 BIOS 来说功能更为全面,也更为安全。 挂载利用目录作为分区的进入点,也就是说,进入目录之后就可以读取分区的数据。 -

+
# 文件权限与目录配置 @@ -342,7 +342,7 @@ UEFI 相比于 BIOS 来说功能更为全面,也更为安全。 完整的目录树如下: -

+
# 文件与目录 @@ -503,7 +503,7 @@ find 可以使用文件的属性和权限进行搜索。 +4、4 和 -4 的指示的时间范围如下: -

+
#### 4.2 与文件拥有者和所属群组有关的选项 @@ -545,7 +545,7 @@ find 可以使用文件的属性和权限进行搜索。 Ext2 文件系统使用了上述的文件结构,并在此之上加入了 block 群组的概念,也就是将一个文件系统划分为多个 block 群组,方便管理。 -

+
## inode @@ -553,7 +553,7 @@ Ext2 文件系统支持的 block 大小有 1k、2k 和 4k 三种,不同的 blo inode 中记录了文件内容所在的 block,但是每个 block 非常小,一个大文件随便都需要几十万的 block,而一个 inode 大小有限,无法直接引用这么多 block。因此引入了间接、双间接、三间接引用。间接引用是指,让 inode 记录的引用 block 块当成 inode 用来记录引用信息。 -

+
inode 具体包含以下信息: @@ -1031,7 +1031,7 @@ daemon 2 # vim 三个模式 -

+
在指令列模式下,有以下命令用于离开或者存储文件。 diff --git a/notes/MySQL.md b/notes/MySQL.md index f1cef838..82556343 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -238,7 +238,7 @@ customer_id_selectivity: 0.0373 ### 3.5 聚簇索引 -

+
聚簇索引并不是一种索引类型,而是一种数据存储方式。 @@ -267,7 +267,7 @@ customer_id_selectivity: 0.0373 ### 4. 1 B-Tree -

+
为了描述 B-Tree,首先定义一条数据记录为一个二元组 [key, data],key 为记录的键,data 为数据记录除 key 外的数据。 @@ -283,7 +283,7 @@ B-Tree 是满足下列条件的数据结构: ### 4.2 B+Tree -

+
与 B-Tree 相比,B+Tree 有以下不同点: @@ -292,7 +292,7 @@ B-Tree 是满足下列条件的数据结构: ### 4.3 带有顺序访问指针的 B+Tree -

+
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。 @@ -405,7 +405,7 @@ do { 通过代理,可以路由流量到可以使用的服务器上。 -

+
**1.4 在应用中处理故障转移** diff --git a/notes/SQL 语法.md b/notes/SQL 语法.md index 66a13a76..bb4e4d78 100644 --- a/notes/SQL 语法.md +++ b/notes/SQL 语法.md @@ -710,7 +710,7 @@ SHOW GRANTS FOR myuser; GRANT SELECT, INSERT ON mydatabase.* TO myuser; ``` -

+
账户用 username@host 的形式定义,username@% 使用的是默认主机名。 diff --git a/notes/代码可读性.md b/notes/代码可读性.md index f0107178..9706106a 100644 --- a/notes/代码可读性.md +++ b/notes/代码可读性.md @@ -44,7 +44,7 @@ 用 min、max 表示数量范围;用 first、last 表示访问空间的包含范围,begin、end 表示访问空间的排除范围,即 end 不包含尾部。 -

+
布尔相关的命名加上 is、can、should、has 等前缀。 diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md index 6525b93f..96ce13b3 100644 --- a/notes/剑指 offer 题解.md +++ b/notes/剑指 offer 题解.md @@ -287,7 +287,7 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, - 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点; - 否则,向上找第一个左链接指向的树包含该节点的祖先节点。 -

+
```java public TreeLinkNode GetNext(TreeLinkNode pNode) { @@ -1077,15 +1077,15 @@ private void dfs(TreeNode node, int target, int curSum, ArrayList path) 第一步,在每个节点的后面插入复制的节点。 -

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

+
第三步,拆分。 -

+
```java diff --git a/notes/算法.md b/notes/算法.md index 6c638f29..a3001168 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -86,7 +86,7 @@ T(N)=aN3 转换为 lg(T(N))=3lgN+lga -

+
## 2. 数学模型 @@ -94,13 +94,13 @@ T(N)=aN3 转换为 lg(T(N))=3lgN+lga 使用 \~f(N) 来表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数 , 例如 N3/6-N2/2+N/3 \~ N3/6。 -

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

+
**内循环** @@ -166,7 +166,7 @@ public class ThreeSumFast { 如果 T(N) \~ aNblgN,那么 T(2N)/T(N) \~ 2b,例如对于暴力方法的 ThreeSum 算法,近似时间为 \~N3/6,对它进行倍率实验得到如下结果: -

+
可见 T(2N)/T(N)\~23,也就是 b 为 3。 @@ -221,7 +221,7 @@ private void exch(Comparable[] a, int i, int j){ 找到数组中的最小元素,然后将它与数组的第一个元素交换位置。然后再从剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。不断进行这样的操作,直到将整个数组排序。 -

+
```java public class Selection { @@ -244,7 +244,7 @@ public class Selection { 将一个元素插入到已排序的数组中,使得插入之后的数组也是有序的。插入排序从左到右插入每个元素,每次插入之后左部的子数组是有序的。 -

+
```java public class Insertion { @@ -275,7 +275,7 @@ public class Insertion { 希尔排序使用插入排序对间隔 h 的序列进行排序,如果 h 很大,那么元素就能很快的移到很远的地方。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。 -

+
```java public class Shell { @@ -303,7 +303,7 @@ public class Shell { 归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。 -

+
### 2.1 归并方法 @@ -345,9 +345,9 @@ private static void sort(Comparable[] a, int lo, int hi) { } ``` -

+
-

+
很容易看出该排序算法的时间复杂度为 O(NlgN)。 @@ -357,7 +357,7 @@ private static void sort(Comparable[] a, int lo, int hi) { 先归并那些微型数组,然后成对归并得到的子数组。 -

+
```java public static void busort(Comparable[] a) { @@ -377,7 +377,7 @@ public static void busort(Comparable[] a) { 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序;快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。 -

+
```java public class QuickSort { @@ -399,7 +399,7 @@ public class QuickSort { 取 a[lo] 作为切分元素,然后从数组的左端向右扫描直到找到第一个大于等于它的元素,再从数组的右端向左扫描找到第一个小于等于它的元素,交换这两个元素,并不断继续这个过程,就可以保证左指针的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,将切分元素 a[lo] 和左子数组最右侧的元素 a[j] 交换然后返回 j 即可。 -

+
```java private static int partition(Comparable[] a, int lo, int hi) { @@ -440,7 +440,7 @@ private static int partition(Comparable[] a, int lo, int hi) { 三向切分快速排序对于只有若干不同主键的随机数组可以在线性时间内完成排序。 -

+
```java public class Quick3Way { @@ -470,7 +470,7 @@ public class Quick3Way { 堆可以用数组来表示,因为堆是一种完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里我们不使用数组索引为 0 的位置,是为了更清晰地理解节点的关系。 -

+
```java public class MaxPQ { @@ -561,7 +561,7 @@ public Key delMax() { 无序数组建立堆最直接的方法是从左到右遍历数组,然后进行上浮操作。一个更高效的方法是从右至左进行下沉操作,如果一个节点的两个节点都已经是堆有序,那么进行下沉操作可以使得这个节点为根节点的堆有序。叶子节点不需要进行下沉操作,因此可以忽略叶子节点的元素,因此只需要遍历一半的元素即可。 -

+
```java public static void sort(Comparable[] a){ @@ -590,7 +590,7 @@ public static void sort(Comparable[] a){ ### 5.1 排序算法的比较 -

+
快速排序时最快的通用排序算法,它的内循环的指令很少,而且它还能利用缓存,因为它总是顺序地访问数据。它的运行时间增长数量级为 \~cNlgN,这里的 c 比其他线性对数级别的排序算法都要小。使用三向切分之后,实际应用中可能出现的某些分布的输入能够达到线性级别,而其它排序算法仍然需要线性对数时间。 @@ -625,11 +625,11 @@ public static Comparable select(Comparable[] a, int k) { ### 1.1 无序符号表 -

+
### 1.2 有序符号表 -

+
有序符号表的键需要实现 Comparable 接口。 @@ -708,7 +708,7 @@ public class BinarySearchST, Value> { **二叉查找树** (BST)是一颗二叉树,并且每个节点的键都大于其左子树中的任意节点的键而小于右子树的任意节点的键。 -

+
二叉查找树的查找操作每次迭代都会让区间减少一半,和二分查找类似。 @@ -781,7 +781,7 @@ private Node put(Node x, Key key, Value val) { 二叉查找树的算法运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 lgN。在最坏的情况下,树的高度为 N。 -

+
复杂度:查找和插入操作都为对数级别。 @@ -837,7 +837,7 @@ private Node min(Node x) { 令指向最小节点的链接指向最小节点的右子树。 -

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

+
```java public void delete(Key key) { @@ -907,7 +907,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { ### 3.1 2-3 查找树 -

+
一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。 @@ -915,7 +915,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 当插入之后产生一个临时 4- 节点时,需要将 4- 节点分裂成 3 个 2- 节点,并将中间的 2- 节点移到上层节点中。如果上移操作继续产生临时 4- 节点则一直进行分裂上移,直到不存在临时 4- 节点。 -

+
#### 3.1.2 性质 @@ -927,7 +927,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 2-3 查找树需要用到 2- 节点和 3- 节点,红黑树使用红链接来实现 3- 节点。指向一个节点的链接颜色如果为红色,那么这个节点和上层节点表示的是一个 3- 节点,而黑色则是普通链接。 -

+
红黑树具有以下性质: @@ -936,7 +936,7 @@ private void keys(Node x, Queue queue, Key lo, Key hi) { 画红黑树时可以将红链接画平。 -

+
```java public class RedBlackBST, Value> { @@ -970,9 +970,9 @@ public class RedBlackBST, Value> { 因为合法的红链接都为左链接,如果出现右链接为红链接,那么就需要进行左旋转操作。 -

+
-

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

+
-

+
```java public Node rotateRight(Node h) { @@ -1011,9 +1011,9 @@ public Node rotateRight(Node h) { 一个 4- 节点在红黑树中表现为一个节点的左右子节点都是红色的。分裂 4- 节点除了需要将子节点的颜色由红变黑之外,同时需要将父节点的颜色由黑变红,从 2-3 树的角度看就是将中间节点移到上层节点。 -

+
-

+
```java void flipColors(Node h){ @@ -1031,7 +1031,7 @@ void flipColors(Node h){ - 如果左子节点是红色的且它的左子节点也是红色的,进行右旋转; - 如果左右子节点均为红色的,进行颜色转换。 -

+
```java public void put(Key key, Value val) { @@ -1067,11 +1067,11 @@ private Node put(Node x, Key key, Value val) { 2. 如果当前节点的左子节点是 2- 节点而它的兄弟节点不是 2- 节点,向兄弟节点拿一个 key 过来; 3. 如果当前节点的左子节点和它的兄弟节点都是 2- 节点,将左子节点、父节点中的最小键和最近的兄弟节点合并为一个 4- 节点。 -

+
最后得到一个含有最小键的 3- 节点或者 4- 节点,直接从中删除。然后再从头分解所有临时的 4- 节点。 -

+
#### 3.2.6 分析 @@ -1147,7 +1147,7 @@ public class Transaction{ 拉链法使用链表来存储 hash 值相同的键,从而解决冲突。此时查找需要分两步,首先查找 Key 所在的链表,然后在链表中顺序查找。 -

+
对于 N 个键,M 条链表 (N>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M,因此未命中的查找和插入操作所需要的比较次数为 \~N/M。 @@ -1155,7 +1155,7 @@ public class Transaction{ 线性探测法使用空位来解决冲突,当冲突发生时,向前探测一个空位来存储冲突的键。使用线程探测法,数组的大小 M 应当大于键的个数 N(M>N)。 -

+
```java public class LinearProbingHashST { @@ -1249,7 +1249,7 @@ public void delete(Key key) { α = N/M,把 α 称为利用率。理论证明,当 α 小于 1/2 时探测的预计次数只在 1.5 到 2.5 之间。 -

+
为了保证散列表的性能,应当调整数组的大小,使得 α 在 [1/4, 1/2] 之间。 @@ -1274,13 +1274,13 @@ private void resize(int cap) { 虽然每次重新调整数组都需要重新把每个键值对插入到散列表,但是从摊还分析的角度来看,所需要的代价却是很小的。从下图可以看出,每次数组长度加倍后,累计平均值都会增加 1,因为表中每个键都需要重新计算散列值,但是随后平均值会下降。 -

+
## 5. 应用 ### 5.1 各种符号表实现的比较 -

+
应当优先考虑散列表,当需要有序性操作时使用红黑树。 diff --git a/notes/计算机操作系统.md b/notes/计算机操作系统.md index a8676b3e..4a4a2067 100644 --- a/notes/计算机操作系统.md +++ b/notes/计算机操作系统.md @@ -169,7 +169,7 @@ ## 进程状态的切换 -

+
阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU,缺少 CPU 会让进程从运行态转换为就绪态。 @@ -219,7 +219,7 @@ shortest remaining time next(SRTN)。 #### 2.3 多级反馈队列 -

+
1. 设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权越高的队列中,为每个进程所规定的执行时间片就越小。 @@ -470,7 +470,7 @@ void writer() { ### 2. 哲学家进餐问题 -

+
五个哲学家围着一张圆周,每个哲学家面前放着饭。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先一根一根拿起左右两边的筷子。 @@ -520,7 +520,7 @@ void philosopher(int i) { ## 死锁的条件 -

+
1. 互斥 2. 请求与保持 @@ -561,7 +561,7 @@ void philosopher(int i) { #### 3.1 安全状态 -

+
图 a 的第二列 has 表示已拥有的资源数,第三列 max 表示总共需要的资源数,free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源,运行结束后释放 B,此时 free 变为 4;接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。 @@ -571,13 +571,13 @@ void philosopher(int i) { 一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 -

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

+
上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。 @@ -595,7 +595,7 @@ void philosopher(int i) { 死锁检测的基本思想是,如果一个进程所请求的资源能够被满足,那么就让它执行,释放它拥有的所有资源,然后让其它能满足条件的进程执行。 -

+
上图中,有三个进程四个资源,每个数据代表的含义如下: @@ -635,11 +635,11 @@ void philosopher(int i) { ### 2. 分段 -

+
上图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态递增的特点会导致覆盖问题的出现。 -

+
分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 @@ -693,7 +693,7 @@ void philosopher(int i) { 4,7,0,7,1,0,1,2,1,2,6 -

+
### 4. 时钟(Clock) diff --git a/notes/计算机网络.md b/notes/计算机网络.md index 8898e444..ebb488aa 100644 --- a/notes/计算机网络.md +++ b/notes/计算机网络.md @@ -95,7 +95,7 @@ 网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 -

+
## ISP @@ -105,14 +105,14 @@ 互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 -

+
## 互联网的组成 1. 边缘部分:所有连接在互联网上的主机,用户可以直接使用; 2. 核心部分:由大量的网络和连接这些网络的路由器组成,为边缘部分的主机提供服务。 -

+
## 主机之间的通信方式 @@ -126,7 +126,7 @@ ## 电路交换与分组交换 -

+
### 1. 电路交换 @@ -140,7 +140,7 @@ 分组交换也使用了存储转发,但是转发的是分组而不是报文。把整块数据称为一个报文,由于一个报文可能很长,需要先进行切分,来满足分组能处理的大小。在每个切分的数据前面加上首部之后就成为了分组,首部包含了目的地址和源地址等控制信息。 -

+
存储转发允许在一条传输线路上传送多个主机的分组,因此两个用户之间的通信不需要占用端到端的线路资源。 @@ -150,7 +150,7 @@ 总时延 = 发送时延 + 传播时延 + 处理时延 + 排队时延 -

+
### 1. 发送时延 @@ -178,7 +178,7 @@ ## 计算机网络体系结构* -

+
### 1. 七层协议 @@ -205,7 +205,7 @@ 路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。 -

+
### 4. TCP/IP 体系结构 @@ -213,11 +213,11 @@ 现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 -

+
TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占用举足轻重的地位。 -

+
# 第二章 物理层 @@ -231,7 +231,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 -

+
## 信道复用技术 @@ -241,19 +241,19 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 使用这两种方式进行通信,在通信的过程中用户会一直占用一部分信道资源。但是由于计算机数据的突发性质,没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 -

+
### 2. 统计时分复用 是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 -

+
### 3. 波分复用 光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 -

+
### 4. 码分复用 @@ -275,7 +275,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 码分复用需要发送的数据量为原先的 m 倍。 -

+
# 第三章 数据链路层 @@ -285,7 +285,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 -

+
### 2. 透明传输 @@ -293,7 +293,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 帧中有首部和尾部,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据中出现首部尾部相同的内容前面插入转义字符,如果需要传输的内容正好就是转义字符,那么就在转义字符前面再加个转义字符,在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 -

+
### 3. 差错检测 @@ -303,7 +303,7 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议就是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 -

+
在 PPP 的帧中 @@ -312,11 +312,11 @@ TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中 - FCS 字段是使用 CRC 的检验序列 - 信息部分的长度不超过 1500 -

+
## 局域网的拓扑 -

+
## 广播信道 - CSMA/CD 协议* @@ -328,7 +328,7 @@ CSMA/CD 表示载波监听多点接入 / 碰撞检测。 - **载波监听** :每个站都必须不停地检听信道。在发送前,如果检听信道正在使用,就必须等待。 - **碰撞检测** :在发送中,如果检听到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经检听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 -

+
记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 **争用期** 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 @@ -338,15 +338,15 @@ CSMA/CD 表示载波监听多点接入 / 碰撞检测。 从表面上看,使用集线器的局域网在物理上是一个星型网。但是集线器使用电子器件来模拟实际缆线的工作,逻辑上仍是一个总线网,整个系统仍像一个传统以太网那样运行。 -

+
-

+
## MAC 层* MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器(网卡),一台主机拥有多少个适配器就有多少个 MAC 地址,例如笔记本电脑普遍存在无线网络适配器和有线网络适配器。 -

+
- **类型** :标记上层使用的协议; - **数据** :长度在 46-1500 之间,如果太小则需要填充; @@ -357,7 +357,7 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器 虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息,例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 -

+
# 第四章 网络层* @@ -367,7 +367,7 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器 使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 -

+
与 IP 协议配套使用的还有三个协议: @@ -375,11 +375,11 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器 2. 网际控制报文协议 ICMP(Internet Control Message Protocol) 3. 网际组管理协议 IGMP(Internet Group Management Protocol) -

+
## IP 数据报格式 -

+
- **版本** : 有 4(IPv4)和 6(IPv6)两个值; @@ -393,7 +393,7 @@ MAC 地址是 6 字节(48 位)的地址,用于唯一表示网络适配器 - **片偏移** : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 -

+
- **生存时间** :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 @@ -415,7 +415,7 @@ IP 地址的编址方式经历了三个历史阶段: IP 地址 ::= {< 网络号 >, < 主机号 >} -

+
### 2. 子网划分 @@ -443,7 +443,7 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 -

+
## 地址解析协议 ARP @@ -451,7 +451,7 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 每个主机都有一个 ARP 高速缓存,存放映射表。如果一个 IP 地址到 MAC 地址的映射不在该表中,主机通过广播的方式发送 ARP 请求分组,匹配 IP 地址的主机会发送 ARP 响应分组告知其 MAC 地址。 -

+
## 路由器的结构 @@ -459,11 +459,11 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 分组转发部分由三部分组成:交换结构、一组输入端口和一组输出端口。 -

+
交换结构的交换网络有以下三种实现方式: -

+
## 交换机与路由器的区别 @@ -479,7 +479,7 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 5. 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; 6. 报告转发分组出错。 -

+
## 路由选择协议 @@ -492,7 +492,7 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 1. 内部网关协议 IGP(Interior Gateway Protocol):在 AS 内部使用,如 RIP 和 OSPF。 2. 外部网关协议 EGP(External Gateway Protocol):在 AS 之间使用,如 BGP。 -

+
### 1. 内部网关协议 RIP @@ -532,17 +532,17 @@ BGP 只能寻找一条比较好的路由,而不是最佳路由。它采用路 每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 -

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

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

+
## 分组网间探测 PING @@ -557,7 +557,7 @@ PING 的过程: 在一对多的通信中,多播不需要将分组复制多份,从而大大节约网络资源。 -

+
## 虚拟专用网 VPN @@ -573,7 +573,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 下图中,场所 A 和 B 的通信部经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 -

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

+
# 第五章 运输层* @@ -597,13 +597,13 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 ## UDP 首部格式 -

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

+
- **序号** :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 @@ -621,7 +621,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 ## TCP 的三次握手 -

+
假设 A 为客户端,B 为服务器端。 @@ -634,7 +634,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 ## TCP 的四次挥手 -

+
以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 @@ -652,7 +652,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 ## TCP 滑动窗口 -

+
窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 @@ -684,7 +684,7 @@ TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接受,而拥塞控制是为了降低整个网络的拥塞程度。 -

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

+
### 慢开始与拥塞避免 @@ -711,7 +711,7 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 在这种情况下,只是丢失个别报文段,而不是网络拥塞,因此执行快恢复,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 -

+
# 第六章 应用层* @@ -725,9 +725,9 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 一个域名由多个层次构成,从上层到下层分别为顶级域名、二级域名、三级域名以及四级域名。所有域名可以画成一颗域名树。 -

+
-

+
域名服务器可以分为以下四类: @@ -738,11 +738,11 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 区和域的概念不同,可以在一个域中划分多个区。图 b 在域 abc.com 中划分了两个区:abc.com 和 y.abc.com -

+
因此就需要两个权限域名服务器: -

+
### 2. 解析过程 @@ -750,13 +750,13 @@ TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、 迭代的方式下,本地域名服务器向一个域名服务器解析请求解析之后,结果返回到本地域名服务器,然后本地域名服务器继续向其它域名服务器请求解析;而递归地方式下,结果不是直接返回的,而是继续向前请求解析,最后的结果才会返回。 -

+
## 文件传输协议 FTP FTP 在运输层使用 TCP,并且需要建立两个并行的 TCP 连接:控制连接和数据连接。控制连接在整个会话期间一直保持打开,而数据连接在数据传送完毕之后就关闭。控制连接使用端口号 21,数据连接使用端口号 20。 -

+
## 远程终端协议 TELNET @@ -772,7 +772,7 @@ TELNET 可以适应许多计算机和操作系统的差异,例如不同操作 一个电子邮件系统由三部分组成:用户代理、邮件服务器以及邮件发送协议和读取协议。其中发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 -

+
### POP3 @@ -786,7 +786,7 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不去手动 SMTP 只能发送 ASCII 码,而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP,而是增加邮件主题的结构,定义了非 ASCII 码的编码规则。 -

+
## 动态主机配置协议 DHCP diff --git a/notes/设计模式.md b/notes/设计模式.md index 3d0c487b..47a7c4b0 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -32,7 +32,7 @@ 需要说明的一点是,文中的 UML 类图和规范的 UML 类图不大相同,其中组合关系使用以下箭头表示: -

+
# 第一章 设计模式入门 @@ -50,7 +50,7 @@ 使用继承的解决方案如下,这种方案代码无法复用,如果两个鸭子类拥有同样的飞行方式,就有两份重复的代码。 -

+
**4. 设计原则** @@ -60,17 +60,17 @@ 运用这一原则,将叫和飞行的行为抽象出来,实现多种不同的叫和飞行的子类,让子类去实现具体的叫和飞行方式。 -

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

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

+
**6. 模式定义** @@ -185,7 +185,7 @@ FlyBehavior.FlyNoWay 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)成为观察者。 -

+
**2. 模式类图** @@ -193,7 +193,7 @@ FlyBehavior.FlyNoWay 观察者拥有一个主题对象的引用,因为注册、移除还有数据都在主题当中,必须通过操作主题才能完成相应功能。 -

+
**3. 问题描述** @@ -201,7 +201,7 @@ FlyBehavior.FlyNoWay **4. 解决方案类图** -

+
**5. 设计原则** @@ -325,17 +325,17 @@ StatisticsDisplay.update:1.0 1.0 1.0 下图中 DarkRoast 对象被 Mocha 包裹,Mocha 对象又被 Whip 包裹,并且他们都继承自相同父类,都有 cost() 方法,但是外层对象的 cost() 方法实现调用了内层对象的 cost() 方法。因此,如果要在 DarkRoast 上添加 Mocha,那么只需要用 Mocha 包裹 DarkRoast,如果还需要 Whip ,就用 Whip 包裹 Mocha,最后调用 cost() 方法能把三种对象的价格都包含进去。 -

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

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

+
**5. 设计原则** @@ -343,7 +343,7 @@ StatisticsDisplay.update:1.0 1.0 1.0 **6. Java I/O 中的装饰者模式** -

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

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

+
**4. 代码实现** @@ -503,11 +503,11 @@ CheesePizza 可以为每个子类创建单独的简单工厂来创建每一个产品类,但是把简单工厂中创建对象的代码放到子类中来可以减少类的数目,因为子类不算是产品类,因此完全可以这么做。 -

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

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

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

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

+
**5. 代码实现** @@ -746,7 +746,7 @@ MarinaraSauce 使用一个私有构造器、一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 -

+
**3. 懒汉式-线程不安全** @@ -828,9 +828,9 @@ public class Singleton { 有非常多的家电,并且之后会增加家电。 -

+
-

+
**2. 模式定义** @@ -846,11 +846,11 @@ public class Singleton { - RemoteLoader 是客户端,注意它与 RemoteControl 的区别。因为 RemoteControl 不能主动地调用自身的方法,因此也就不能当成是客户端。客户端好比人,只有人才能去真正去使用遥控器。 -

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

+
**5. 代码实现** @@ -939,15 +939,15 @@ Light is on! 将一个类的接口,转换为客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。 -

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

+
-

+
**3. 问题描述** @@ -957,7 +957,7 @@ Light is on! **4. 解决方案类图** -

+
**5. 代码实现** @@ -1021,17 +1021,17 @@ gobble! **2. 模式类图** -

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

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

+
**5. 设计原则** @@ -1053,19 +1053,19 @@ gobble! 模板方法 templateMethod() 定义了算法的骨架,确定了 primitiveOperation1() 和 primitiveOperation2() 方法执行的顺序,而 primitiveOperation1() 和 primitiveOperation2() 让子类去实现。 -

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

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

+
**5. 设计原则** @@ -1171,7 +1171,7 @@ Tea.addCondiments - Client 需要拥有一个 Aggregate 对象,这是很明显的。为了迭代变量 Aggregate 对象,也需要拥有 Iterator 对象。 -

+
**3. 代码实现** @@ -1331,7 +1331,7 @@ public class Client { 组合类拥有一个组件对象,因此组合类的操作可以委托给组件对象去处理,而组件对象可以是另一个组合类或者叶子类。 -

+
**4. 代码实现** @@ -1444,7 +1444,7 @@ Composite:root Context 的 request() 方法委托给 State 对象去处理。当 Context 组合的 State 对象发生改变时,它的行为也就发生了改变。 -

+
**3. 与策略模式的比较** @@ -1460,7 +1460,7 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合 糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 -

+
**5. 直接解决方案** @@ -1468,7 +1468,7 @@ Context 的 request() 方法委托给 State 对象去处理。当 Context 组合 这种解决方案在需要增加状态的时候,必须对每个操作的代码都进行修改。 -

+
**6 代码实现** @@ -1761,13 +1761,13 @@ No gumball dispensed 视图使用组合模式,模型使用了观察者模式,控制器使用了策略模式。 -

+
**Web 中的 MVC** 模式不再使用观察者模式。 -

+
# 第十三章 与设计模式相处 @@ -1779,6 +1779,6 @@ No gumball dispensed 模式分类: -

+
# 第十四章 剩下的模式 diff --git a/notes/重构.md b/notes/重构.md index a04aa08c..f8396890 100644 --- a/notes/重构.md +++ b/notes/重构.md @@ -124,7 +124,7 @@ 包括三个类:Movie、Rental 和 Customer,Rental 包含租赁的 Movie 以及天数。 -

+
最开始的实现是把所有的计费代码都放在 Customer 类中。 @@ -159,19 +159,19 @@ double getTotalCharge() { 以下是继承 Movie 的多态解决方案,这种方案可以解决上述的 switch 问题,因为每种电影类别的计费方式都被放到了对应 Movie 子类中,当变化发生时,只需要去修改对应子类中的代码即可。 -

+
但是由于 Movie 可以在其生命周期内修改自己的类别,一个对象却不能在生命周期内修改自己所属的类,因此这种方案不可行。可以使用策略模式来解决这个问题(原书写的是使用状态模式,但是这里应该为策略模式,具体可以参考设计模式内容)。 下图中,Price 有多种实现,Movie 组合了一个 Price 对象,并且在运行时可以改变组合的 Price 对象,从而使得它的计费方式发生改变。 -

+
重构后整体的类图和时序图如下: -

+
-

+
# 重构原则 @@ -579,7 +579,7 @@ Hide Delegate 有很大好处,但是它的代价是:每当客户要使用受 将该数据赋值到一个领域对象中,建立一个 Oberver 模式,用以同步领域对象和 GUI 对象内的重复数据。 -

+
## 7. Change Unidirectional Association to Bidirectional(将单向关联改为双向关联) @@ -636,13 +636,13 @@ public 字段应当改为 private,并提供相应的访问函数。 类中有一个数值类型码,但它并不影响类的行为,就用一个新类替换该数值类型码。如果类型码出现在 switch 语句中,需要使用 Replace Conditional with Polymorphism 去掉 switch,首先必须运用 Replace Type Code with Subcalss 或 Replace Type Code with State/Strategy 去掉类型码。 -

+
## 14. Replace Type Code with Subcalsses(以子类取代类型码) 有一个不可变的类型码,它会影响类的行为,以子类取代这个类型码。 -

+
## 15. Replace Type Code with State/Strategy (以 State/Strategy 取代类型码) @@ -650,13 +650,13 @@ public 字段应当改为 private,并提供相应的访问函数。 和 Replace Type Code with Subcalsses 的区别是 Replace Type Code with State/Strategy 的类型码是动态可变的,前者通过继承的方式来实现,后者通过组合的方式来实现。因为类型码可变,如果通过继承的方式,一旦一个对象的类型码改变,那么就要改变用新的对象来取代旧对象,而客户端难以改变新的对象。但是通过组合的方式,改变引用的状态类是很容易的。 -

+
## 16. Replace Subclass with Fields(以字段取代子类) 各个子类的唯一差别只在“返回常量数据”的函数上。 -

+
# 简化条件表达式 @@ -776,7 +776,7 @@ double getSpeed() { } ``` -

+
## 7. Introduce Null Object(引入Null对象) @@ -916,7 +916,7 @@ double finalPrice = discountedPrice (basePrice); 以一个对象取代这些参数。 -

+
## 10. Remove Setting Method(移除设值函数) diff --git a/notes/面向对象思想.md b/notes/面向对象思想.md index d9aa0a61..db8ea79f 100644 --- a/notes/面向对象思想.md +++ b/notes/面向对象思想.md @@ -197,13 +197,13 @@ public class Music { 从具体类中继承 -

+
② 实现关系 (realize) 从抽象类或者接口中继承 -

+
**1.2 整体和部分** @@ -211,13 +211,13 @@ public class Music { 表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。以下表示 B 由 A 组成: -

+
② 组合关系 (composition) 和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 -

+
**1.3 相互联系** @@ -225,13 +225,13 @@ public class Music { 表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。 -

+
② 依赖关系 (dependency) 和关联关系不同的是 , 依赖关系是在运行过程中起作用的。一般依赖作为类的构造器或者方法的参数传入。双向依赖时一种不好的设计。 -

+
## 2. 时序图 @@ -245,7 +245,7 @@ http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html 从虚线从上往下表示时间的推进。 -

+
可见,通过时序图可以知道每个类具有以下操作: @@ -289,7 +289,7 @@ public class 孙权 { 有三种表现形式 -

+
在画图时,应该遵循以下原则: @@ -301,7 +301,7 @@ public class 孙权 { 生命线从对象的创建开始到对象销毁时终止 -

+
③ 消息 @@ -311,15 +311,15 @@ public class 孙权 { 1\. 简单消息,不区分同步异步。 -

+
2\. 同步消息,发送消息之后需要暂停活动来等待回应。 -

+
3\. 异步消息,发送消息之后不需要等待。 -

+
4\. 返回消息,可选。 @@ -327,7 +327,7 @@ public class 孙权 { 生命线上的方框表示激活状态,其它时间处于休眠状态。 -

+
# 参考资料