From 058e67e0ff4e1023bd67c3ac23a4a5dd8aaf0997 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Sat, 2 Nov 2019 14:39:13 +0800 Subject: [PATCH] auto commit --- docs/notes/10.1 斐波那契数列.md | 11 +- docs/notes/10.2 矩形覆盖.md | 15 +- docs/notes/10.3 跳台阶.md | 15 +- docs/notes/10.4 变态跳台阶.md | 9 +- docs/notes/11. 旋转数组的最小数字.md | 11 +- docs/notes/12. 矩阵中的路径.md | 11 +- docs/notes/13. 机器人的运动范围.md | 7 + docs/notes/14. 剪绳子.md | 7 + docs/notes/15. 二进制中 1 的个数.md | 7 + docs/notes/16. 数值的整数次方.md | 9 +- docs/notes/17. 打印从 1 到最大的 n 位数.md | 7 + docs/notes/18.1 在 O(1) 时间内删除链表节点.md | 11 +- docs/notes/18.2 删除链表中重复的结点.md | 9 +- docs/notes/19. 正则表达式匹配.md | 7 + docs/notes/20. 表示数值的字符串.md | 7 + .../21. 调整数组顺序使奇数位于偶数前面.md | 9 +- docs/notes/22. 链表中倒数第 K 个结点.md | 9 +- docs/notes/23. 链表中环的入口结点.md | 9 +- docs/notes/24. 反转链表.md | 7 + docs/notes/25. 合并两个排序的链表.md | 9 +- docs/notes/26. 树的子结构.md | 9 +- docs/notes/27. 二叉树的镜像.md | 9 +- docs/notes/28. 对称的二叉树.md | 9 +- docs/notes/29. 顺时针打印矩阵.md | 9 +- docs/notes/3. 数组中重复的数字.md | 9 +- docs/notes/30. 包含 min 函数的栈.md | 7 + docs/notes/31. 栈的压入、弹出序列.md | 7 + docs/notes/32.1 从上往下打印二叉树.md | 9 +- docs/notes/32.2 把二叉树打印成多行.md | 7 + docs/notes/32.3 按之字形顺序打印二叉树.md | 7 + docs/notes/33. 二叉搜索树的后序遍历序列.md | 9 +- docs/notes/34. 二叉树中和为某一值的路径.md | 9 +- docs/notes/35. 复杂链表的复制.md | 15 +- docs/notes/36. 二叉搜索树与双向链表.md | 9 +- docs/notes/37. 序列化二叉树.md | 7 + docs/notes/38. 字符串的排列.md | 7 + .../notes/39. 数组中出现次数超过一半的数字.md | 7 + docs/notes/4. 二维数组中的查找.md | 9 +- docs/notes/40. 最小的 K 个数.md | 7 + docs/notes/41.1 数据流中的中位数.md | 7 + docs/notes/41.2 字符流中第一个不重复的字符.md | 7 + docs/notes/42. 连续子数组的最大和.md | 7 + .../43. 从 1 到 n 整数中 1 出现的次数.md | 7 + docs/notes/44. 数字序列中的某一位数字.md | 7 + docs/notes/45. 把数组排成最小的数.md | 7 + docs/notes/46. 把数字翻译成字符串.md | 7 + docs/notes/47. 礼物的最大价值.md | 7 + docs/notes/48. 最长不含重复字符的子字符串.md | 7 + docs/notes/49. 丑数.md | 7 + docs/notes/5. 替换空格.md | 9 +- docs/notes/50. 第一个只出现一次的字符位置.md | 7 + docs/notes/51. 数组中的逆序对.md | 7 + docs/notes/52. 两个链表的第一个公共结点.md | 9 +- docs/notes/53. 数字在排序数组中出现的次数.md | 7 + docs/notes/54. 二叉查找树的第 K 个结点.md | 7 + docs/notes/55.1 二叉树的深度.md | 9 +- docs/notes/55.2 平衡二叉树.md | 9 +- docs/notes/56. 数组中只出现一次的数字.md | 7 + docs/notes/57.1 和为 S 的两个数字.md | 7 + docs/notes/57.2 和为 S 的连续正数序列.md | 7 + docs/notes/58.1 翻转单词顺序列.md | 7 + docs/notes/58.2 左旋转字符串.md | 7 + docs/notes/59. 滑动窗口的最大值.md | 7 + docs/notes/6. 从尾到头打印链表.md | 13 +- docs/notes/60. n 个骰子的点数.md | 9 +- docs/notes/61. 扑克牌顺子.md | 9 +- docs/notes/62. 圆圈中最后剩下的数.md | 7 + docs/notes/63. 股票的最大利润.md | 9 +- docs/notes/64. 求 1+2+3+...+n.md | 7 + docs/notes/65. 不用加减乘除做加法.md | 7 + docs/notes/66. 构建乘积数组.md | 9 +- docs/notes/67. 把字符串转换成整数.md | 7 + docs/notes/68. 树中两个节点的最低公共祖先.md | 11 +- docs/notes/7. 重建二叉树.md | 11 +- docs/notes/8. 二叉树的下一个结点.md | 11 +- docs/notes/9. 用两个栈实现队列.md | 9 +- docs/notes/Docker.md | 23 ++- docs/notes/Git.md | 47 +++-- docs/notes/HTTP.md | 145 ++++++++++----- docs/notes/Java IO.md | 56 +++++- docs/notes/Java 基础.md | 143 ++++++++++----- docs/notes/Java 容器.md | 47 +++-- docs/notes/Java 并发.md | 132 ++++++++++---- docs/notes/Java 虚拟机.md | 85 ++++++--- docs/notes/Leetcode 题解 - 二分查找.md | 25 ++- docs/notes/Leetcode 题解 - 位运算.md | 30 ++- docs/notes/Leetcode 题解 - 分治.md | 13 +- docs/notes/Leetcode 题解 - 动态规划.md | 81 +++++++-- docs/notes/Leetcode 题解 - 双指针.md | 28 ++- docs/notes/Leetcode 题解 - 哈希表.md | 19 +- docs/notes/Leetcode 题解 - 图.md | 18 +- docs/notes/Leetcode 题解 - 字符串.md | 20 +- docs/notes/Leetcode 题解 - 排序.md | 31 +++- docs/notes/Leetcode 题解 - 搜索.md | 59 ++++-- docs/notes/Leetcode 题解 - 数学.md | 39 +++- docs/notes/Leetcode 题解 - 数组与矩阵.md | 23 ++- docs/notes/Leetcode 题解 - 栈和队列.md | 17 +- docs/notes/Leetcode 题解 - 树.md | 49 ++++- docs/notes/Leetcode 题解 - 目录.md | 7 + docs/notes/Leetcode 题解 - 目录1.md | 7 + docs/notes/Leetcode 题解 - 贪心思想.md | 26 ++- docs/notes/Leetcode 题解 - 链表.md | 21 ++- docs/notes/Leetcode 题解.md | 7 + docs/notes/Leetcode-Database 题解.md | 26 ++- docs/notes/Linux.md | 134 +++++++++++--- docs/notes/MySQL.md | 59 ++++-- docs/notes/Redis.md | 80 ++++++-- docs/notes/SQL.md | 75 +++++--- docs/notes/Socket.md | 38 +++- docs/notes/代码可读性.md | 35 +++- docs/notes/代码风格规范.md | 7 + docs/notes/分布式.md | 78 +++++--- docs/notes/剑指 Offer 题解 - 10~19.md | 63 +++++-- docs/notes/剑指 Offer 题解 - 20~29.md | 37 +++- docs/notes/剑指 Offer 题解 - 30~39.md | 39 +++- docs/notes/剑指 Offer 题解 - 3~9.md | 40 ++-- docs/notes/剑指 Offer 题解 - 40~49.md | 7 + docs/notes/剑指 Offer 题解 - 50~59.md | 30 ++- docs/notes/剑指 Offer 题解 - 60~68.md | 32 +++- docs/notes/剑指 Offer 题解 - 目录.md | 7 + docs/notes/剑指 Offer 题解 - 目录1.md | 7 + docs/notes/剑指 offer 题解.md | 7 + docs/notes/攻击技术.md | 16 +- docs/notes/数据库系统原理.md | 94 +++++++--- docs/notes/构建工具.md | 17 +- docs/notes/正则表达式.md | 172 ++++++++++-------- docs/notes/消息队列.md | 28 ++- docs/notes/算法 - 其它.md | 17 +- docs/notes/算法 - 并查集.md | 27 ++- docs/notes/算法 - 排序.md | 31 ++-- docs/notes/算法 - 栈和队列.md | 15 +- docs/notes/算法 - 目录.md | 7 + docs/notes/算法 - 目录1.md | 7 + docs/notes/算法 - 符号表.md | 85 ++++++--- docs/notes/算法 - 算法分析.md | 27 ++- docs/notes/算法.md | 7 + docs/notes/系统设计基础.md | 17 +- docs/notes/缓存.md | 25 ++- docs/notes/计算机操作系统 - 内存管理.md | 37 +++- docs/notes/计算机操作系统 - 概述.md | 33 +++- docs/notes/计算机操作系统 - 死锁.md | 39 +++- docs/notes/计算机操作系统 - 目录.md | 7 + docs/notes/计算机操作系统 - 目录1.md | 7 + docs/notes/计算机操作系统 - 设备管理.md | 22 ++- docs/notes/计算机操作系统 - 进程管理.md | 75 +++++--- docs/notes/计算机操作系统 - 链接.md | 21 ++- docs/notes/计算机操作系统.md | 7 + docs/notes/计算机网络 - 传输层.md | 58 ++++-- docs/notes/计算机网络 - 应用层.md | 37 +++- docs/notes/计算机网络 - 概述.md | 61 +++++-- docs/notes/计算机网络 - 物理层.md | 15 +- docs/notes/计算机网络 - 目录.md | 7 + docs/notes/计算机网络 - 目录1.md | 7 + docs/notes/计算机网络 - 网络层.md | 77 +++++--- docs/notes/计算机网络 - 链路层.md | 79 +++++--- docs/notes/计算机网络.md | 7 + docs/notes/设计模式.md | 111 +++++++---- docs/notes/集群.md | 42 +++-- docs/notes/面向对象思想.md | 42 ++++- 159 files changed, 3185 insertions(+), 839 deletions(-) diff --git a/docs/notes/10.1 斐波那契数列.md b/docs/notes/10.1 斐波那契数列.md index c2940fb9..b5e3d27c 100644 --- a/docs/notes/10.1 斐波那契数列.md +++ b/docs/notes/10.1 斐波那契数列.md @@ -8,13 +8,13 @@ - +

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

递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。 @@ -65,3 +65,10 @@ public class Solution { } } ``` + + + + + + +
diff --git a/docs/notes/10.2 矩形覆盖.md b/docs/notes/10.2 矩形覆盖.md index 2242e70f..b7762030 100644 --- a/docs/notes/10.2 矩形覆盖.md +++ b/docs/notes/10.2 矩形覆盖.md @@ -6,23 +6,23 @@ 我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法? - +

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

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

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

```java public int RectCover(int n) { @@ -38,3 +38,10 @@ public int RectCover(int n) { return result; } ``` + + + + + + +
diff --git a/docs/notes/10.3 跳台阶.md b/docs/notes/10.3 跳台阶.md index 829dfb01..a61e33f5 100644 --- a/docs/notes/10.3 跳台阶.md +++ b/docs/notes/10.3 跳台阶.md @@ -6,21 +6,21 @@ 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - +

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

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

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

```java public int JumpFloor(int n) { @@ -36,3 +36,10 @@ public int JumpFloor(int n) { return result; } ``` + + + + + + +
diff --git a/docs/notes/10.4 变态跳台阶.md b/docs/notes/10.4 变态跳台阶.md index 03973f3e..82c115b1 100644 --- a/docs/notes/10.4 变态跳台阶.md +++ b/docs/notes/10.4 变态跳台阶.md @@ -6,7 +6,7 @@ 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - +

## 解题思路 @@ -56,3 +56,10 @@ public int JumpFloorII(int target) { return (int) Math.pow(2, target - 1); } ``` + + + + + + +
diff --git a/docs/notes/11. 旋转数组的最小数字.md b/docs/notes/11. 旋转数组的最小数字.md index 688fc161..ef7a5b94 100644 --- a/docs/notes/11. 旋转数组的最小数字.md +++ b/docs/notes/11. 旋转数组的最小数字.md @@ -6,13 +6,13 @@ 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 - +

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

此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组,哪一个是非递减数组。我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素。 @@ -63,3 +63,10 @@ private int minNumber(int[] nums, int l, int h) { return nums[l]; } ``` + + + + + + +
diff --git a/docs/notes/12. 矩阵中的路径.md b/docs/notes/12. 矩阵中的路径.md index 06a410d8..6f3e616f 100644 --- a/docs/notes/12. 矩阵中的路径.md +++ b/docs/notes/12. 矩阵中的路径.md @@ -8,13 +8,13 @@ 例如下面的矩阵包含了一条 bfce 路径。 - +

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

本题的输入是数组而不是矩阵(二维数组),因此需要先将数组转换成矩阵。 @@ -62,3 +62,10 @@ private char[][] buildMatrix(char[] array) { return matrix; } ``` + + + + + + +
diff --git a/docs/notes/13. 机器人的运动范围.md b/docs/notes/13. 机器人的运动范围.md index 766b1bd6..0da8fed7 100644 --- a/docs/notes/13. 机器人的运动范围.md +++ b/docs/notes/13. 机器人的运动范围.md @@ -56,3 +56,10 @@ private void initDigitSum() { this.digitSum[i][j] = digitSumOne[i] + digitSumOne[j]; } ``` + + + + + + +
diff --git a/docs/notes/14. 剪绳子.md b/docs/notes/14. 剪绳子.md index b3fd9478..1cac46d3 100644 --- a/docs/notes/14. 剪绳子.md +++ b/docs/notes/14. 剪绳子.md @@ -50,3 +50,10 @@ public int integerBreak(int n) { return dp[n]; } ``` + + + + + + +
diff --git a/docs/notes/15. 二进制中 1 的个数.md b/docs/notes/15. 二进制中 1 的个数.md index 703e6211..455764d3 100644 --- a/docs/notes/15. 二进制中 1 的个数.md +++ b/docs/notes/15. 二进制中 1 的个数.md @@ -38,3 +38,10 @@ public int NumberOf1(int n) { return Integer.bitCount(n); } ``` + + + + + + +
diff --git a/docs/notes/16. 数值的整数次方.md b/docs/notes/16. 数值的整数次方.md index 286f3fe6..aac026cc 100644 --- a/docs/notes/16. 数值的整数次方.md +++ b/docs/notes/16. 数值的整数次方.md @@ -12,7 +12,7 @@ - +

因为 (x\*x)n/2 可以通过递归求解,并且每次递归 n 都减小一半,因此整个算法的时间复杂度为 O(logN)。 @@ -34,3 +34,10 @@ public double Power(double base, int exponent) { return isNegative ? 1 / pow : pow; } ``` + + + + + + +
diff --git a/docs/notes/17. 打印从 1 到最大的 n 位数.md b/docs/notes/17. 打印从 1 到最大的 n 位数.md index 4a05f379..27a6cd12 100644 --- a/docs/notes/17. 打印从 1 到最大的 n 位数.md +++ b/docs/notes/17. 打印从 1 到最大的 n 位数.md @@ -38,3 +38,10 @@ private void printNumber(char[] number) { System.out.println(); } ``` + + + + + + +
diff --git a/docs/notes/18.1 在 O(1) 时间内删除链表节点.md b/docs/notes/18.1 在 O(1) 时间内删除链表节点.md index 769dd08e..d3dc3d60 100644 --- a/docs/notes/18.1 在 O(1) 时间内删除链表节点.md +++ b/docs/notes/18.1 在 O(1) 时间内删除链表节点.md @@ -4,11 +4,11 @@ ① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。 - +

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

综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。 @@ -35,3 +35,10 @@ public ListNode deleteNode(ListNode head, ListNode tobeDelete) { return head; } ``` + + + + + + +
diff --git a/docs/notes/18.2 删除链表中重复的结点.md b/docs/notes/18.2 删除链表中重复的结点.md index 09d0d419..8640bd63 100644 --- a/docs/notes/18.2 删除链表中重复的结点.md +++ b/docs/notes/18.2 删除链表中重复的结点.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题描述 @@ -23,3 +23,10 @@ public ListNode deleteDuplication(ListNode pHead) { } } ``` + + + + + + +
diff --git a/docs/notes/19. 正则表达式匹配.md b/docs/notes/19. 正则表达式匹配.md index 97de47d7..162e2873 100644 --- a/docs/notes/19. 正则表达式匹配.md +++ b/docs/notes/19. 正则表达式匹配.md @@ -38,3 +38,10 @@ public boolean match(char[] str, char[] pattern) { return dp[m][n]; } ``` + + + + + + +
diff --git a/docs/notes/20. 表示数值的字符串.md b/docs/notes/20. 表示数值的字符串.md index c03369ba..eb158242 100644 --- a/docs/notes/20. 表示数值的字符串.md +++ b/docs/notes/20. 表示数值的字符串.md @@ -47,3 +47,10 @@ public boolean isNumeric(char[] str) { return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?"); } ``` + + + + + + +
diff --git a/docs/notes/21. 调整数组顺序使奇数位于偶数前面.md b/docs/notes/21. 调整数组顺序使奇数位于偶数前面.md index 631a718d..c7a15f8e 100644 --- a/docs/notes/21. 调整数组顺序使奇数位于偶数前面.md +++ b/docs/notes/21. 调整数组顺序使奇数位于偶数前面.md @@ -6,7 +6,7 @@ 需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 - +

## 解题思路 @@ -58,3 +58,10 @@ private void swap(int[] nums, int i, int j) { nums[j] = t; } ``` + + + + + + +
diff --git a/docs/notes/22. 链表中倒数第 K 个结点.md b/docs/notes/22. 链表中倒数第 K 个结点.md index fdd8e250..7ccb3b0d 100644 --- a/docs/notes/22. 链表中倒数第 K 个结点.md +++ b/docs/notes/22. 链表中倒数第 K 个结点.md @@ -6,7 +6,7 @@ 设链表的长度为 N。设置两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。 - +

```java public ListNode FindKthToTail(ListNode head, int k) { @@ -25,3 +25,10 @@ public ListNode FindKthToTail(ListNode head, int k) { return P2; } ``` + + + + + + +
diff --git a/docs/notes/23. 链表中环的入口结点.md b/docs/notes/23. 链表中环的入口结点.md index 8623d6a9..c11a1f70 100644 --- a/docs/notes/23. 链表中环的入口结点.md +++ b/docs/notes/23. 链表中环的入口结点.md @@ -12,7 +12,7 @@ 在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。 - +

```java public ListNode EntryNodeOfLoop(ListNode pHead) { @@ -31,3 +31,10 @@ public ListNode EntryNodeOfLoop(ListNode pHead) { return slow; } ``` + + + + + + +
diff --git a/docs/notes/24. 反转链表.md b/docs/notes/24. 反转链表.md index 05bc439f..4fa7a32e 100644 --- a/docs/notes/24. 反转链表.md +++ b/docs/notes/24. 反转链表.md @@ -34,3 +34,10 @@ public ListNode ReverseList(ListNode head) { return newList.next; } ``` + + + + + + +
diff --git a/docs/notes/25. 合并两个排序的链表.md b/docs/notes/25. 合并两个排序的链表.md index 4923e835..78590cc0 100644 --- a/docs/notes/25. 合并两个排序的链表.md +++ b/docs/notes/25. 合并两个排序的链表.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题思路 @@ -49,3 +49,10 @@ public ListNode Merge(ListNode list1, ListNode list2) { return head.next; } ``` + + + + + + +
diff --git a/docs/notes/26. 树的子结构.md b/docs/notes/26. 树的子结构.md index 79dabc80..deae82a4 100644 --- a/docs/notes/26. 树的子结构.md +++ b/docs/notes/26. 树的子结构.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题思路 @@ -25,3 +25,10 @@ private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) { return isSubtreeWithRoot(root1.left, root2.left) && isSubtreeWithRoot(root1.right, root2.right); } ``` + + + + + + +
diff --git a/docs/notes/27. 二叉树的镜像.md b/docs/notes/27. 二叉树的镜像.md index 3a55d573..0407fb05 100644 --- a/docs/notes/27. 二叉树的镜像.md +++ b/docs/notes/27. 二叉树的镜像.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题思路 @@ -23,3 +23,10 @@ private void swap(TreeNode root) { root.right = t; } ``` + + + + + + +
diff --git a/docs/notes/28. 对称的二叉树.md b/docs/notes/28. 对称的二叉树.md index 0a258ec6..fca49be9 100644 --- a/docs/notes/28. 对称的二叉树.md +++ b/docs/notes/28. 对称的二叉树.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题思路 @@ -25,3 +25,10 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2) { return isSymmetrical(t1.left, t2.right) && isSymmetrical(t1.right, t2.left); } ``` + + + + + + +
diff --git a/docs/notes/29. 顺时针打印矩阵.md b/docs/notes/29. 顺时针打印矩阵.md index 7ac6adb5..1dc8f392 100644 --- a/docs/notes/29. 顺时针打印矩阵.md +++ b/docs/notes/29. 顺时针打印矩阵.md @@ -6,7 +6,7 @@ 下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 - +

## 解题思路 @@ -30,3 +30,10 @@ public ArrayList printMatrix(int[][] matrix) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/3. 数组中重复的数字.md b/docs/notes/3. 数组中重复的数字.md index 9356f6f9..3deaaa85 100644 --- a/docs/notes/3. 数组中重复的数字.md +++ b/docs/notes/3. 数组中重复的数字.md @@ -24,7 +24,7 @@ Output: 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: - +

```java @@ -49,3 +49,10 @@ private void swap(int[] nums, int i, int j) { nums[j] = t; } ``` + + + + + + +
diff --git a/docs/notes/30. 包含 min 函数的栈.md b/docs/notes/30. 包含 min 函数的栈.md index 49bc324a..041e5139 100644 --- a/docs/notes/30. 包含 min 函数的栈.md +++ b/docs/notes/30. 包含 min 函数的栈.md @@ -30,3 +30,10 @@ public int min() { return minStack.peek(); } ``` + + + + + + +
diff --git a/docs/notes/31. 栈的压入、弹出序列.md b/docs/notes/31. 栈的压入、弹出序列.md index 4ecea9f0..58ff69cc 100644 --- a/docs/notes/31. 栈的压入、弹出序列.md +++ b/docs/notes/31. 栈的压入、弹出序列.md @@ -27,3 +27,10 @@ public boolean IsPopOrder(int[] pushSequence, int[] popSequence) { return stack.isEmpty(); } ``` + + + + + + +
diff --git a/docs/notes/32.1 从上往下打印二叉树.md b/docs/notes/32.1 从上往下打印二叉树.md index 92046582..5ad352ef 100644 --- a/docs/notes/32.1 从上往下打印二叉树.md +++ b/docs/notes/32.1 从上往下打印二叉树.md @@ -8,7 +8,7 @@ 例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7 - +

## 解题思路 @@ -35,3 +35,10 @@ public ArrayList PrintFromTopToBottom(TreeNode root) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/32.2 把二叉树打印成多行.md b/docs/notes/32.2 把二叉树打印成多行.md index 2366963c..0de90b52 100644 --- a/docs/notes/32.2 把二叉树打印成多行.md +++ b/docs/notes/32.2 把二叉树打印成多行.md @@ -30,3 +30,10 @@ ArrayList> Print(TreeNode pRoot) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/32.3 按之字形顺序打印二叉树.md b/docs/notes/32.3 按之字形顺序打印二叉树.md index 640caaad..0a283ae3 100644 --- a/docs/notes/32.3 按之字形顺序打印二叉树.md +++ b/docs/notes/32.3 按之字形顺序打印二叉树.md @@ -34,3 +34,10 @@ public ArrayList> Print(TreeNode pRoot) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/33. 二叉搜索树的后序遍历序列.md b/docs/notes/33. 二叉搜索树的后序遍历序列.md index 24dd9832..ab9209ff 100644 --- a/docs/notes/33. 二叉搜索树的后序遍历序列.md +++ b/docs/notes/33. 二叉搜索树的后序遍历序列.md @@ -8,7 +8,7 @@ 例如,下图是后序遍历序列 1,3,2 所对应的二叉搜索树。 - +

## 解题思路 @@ -32,3 +32,10 @@ private boolean verify(int[] sequence, int first, int last) { return verify(sequence, first, cutIndex - 1) && verify(sequence, cutIndex, last - 1); } ``` + + + + + + +
diff --git a/docs/notes/34. 二叉树中和为某一值的路径.md b/docs/notes/34. 二叉树中和为某一值的路径.md index dd767ec0..72f08e14 100644 --- a/docs/notes/34. 二叉树中和为某一值的路径.md +++ b/docs/notes/34. 二叉树中和为某一值的路径.md @@ -8,7 +8,7 @@ 下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12 - +

## 解题思路 @@ -34,3 +34,10 @@ private void backtracking(TreeNode node, int target, ArrayList path) { path.remove(path.size() - 1); } ``` + + + + + + +
diff --git a/docs/notes/35. 复杂链表的复制.md b/docs/notes/35. 复杂链表的复制.md index 5e5ce7f9..d3352d50 100644 --- a/docs/notes/35. 复杂链表的复制.md +++ b/docs/notes/35. 复杂链表的复制.md @@ -18,21 +18,21 @@ public class RandomListNode { } ``` - +

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

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

第三步,拆分。 - +

```java public RandomListNode Clone(RandomListNode pHead) { @@ -65,3 +65,10 @@ public RandomListNode Clone(RandomListNode pHead) { return pCloneHead; } ``` + + + + + + +
diff --git a/docs/notes/36. 二叉搜索树与双向链表.md b/docs/notes/36. 二叉搜索树与双向链表.md index e794ebf4..033975ce 100644 --- a/docs/notes/36. 二叉搜索树与双向链表.md +++ b/docs/notes/36. 二叉搜索树与双向链表.md @@ -6,7 +6,7 @@ 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 - +

## 解题思路 @@ -32,3 +32,10 @@ private void inOrder(TreeNode node) { inOrder(node.right); } ``` + + + + + + +
diff --git a/docs/notes/37. 序列化二叉树.md b/docs/notes/37. 序列化二叉树.md index 69ca3396..69086138 100644 --- a/docs/notes/37. 序列化二叉树.md +++ b/docs/notes/37. 序列化二叉树.md @@ -37,3 +37,10 @@ private TreeNode Deserialize() { return t; } ``` + + + + + + +
diff --git a/docs/notes/38. 字符串的排列.md b/docs/notes/38. 字符串的排列.md index 149b3772..8deb1fff 100644 --- a/docs/notes/38. 字符串的排列.md +++ b/docs/notes/38. 字符串的排列.md @@ -38,3 +38,10 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) { } } ``` + + + + + + +
diff --git a/docs/notes/39. 数组中出现次数超过一半的数字.md b/docs/notes/39. 数组中出现次数超过一半的数字.md index 50877830..4518f5a0 100644 --- a/docs/notes/39. 数组中出现次数超过一半的数字.md +++ b/docs/notes/39. 数组中出现次数超过一半的数字.md @@ -25,3 +25,10 @@ public int MoreThanHalfNum_Solution(int[] nums) { return cnt > nums.length / 2 ? majority : 0; } ``` + + + + + + +
diff --git a/docs/notes/4. 二维数组中的查找.md b/docs/notes/4. 二维数组中的查找.md index 17af60a0..b4f7d3e5 100644 --- a/docs/notes/4. 二维数组中的查找.md +++ b/docs/notes/4. 二维数组中的查找.md @@ -28,7 +28,7 @@ Given target = 20, return false. 该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。 - +

```java public boolean Find(int target, int[][] matrix) { @@ -47,3 +47,10 @@ public boolean Find(int target, int[][] matrix) { return false; } ``` + + + + + + +
diff --git a/docs/notes/40. 最小的 K 个数.md b/docs/notes/40. 最小的 K 个数.md index 8fcfda48..5730eaee 100644 --- a/docs/notes/40. 最小的 K 个数.md +++ b/docs/notes/40. 最小的 K 个数.md @@ -79,3 +79,10 @@ public ArrayList GetLeastNumbers_Solution(int[] nums, int k) { return new ArrayList<>(maxHeap); } ``` + + + + + + +
diff --git a/docs/notes/41.1 数据流中的中位数.md b/docs/notes/41.1 数据流中的中位数.md index eb4990bb..bc94e3fa 100644 --- a/docs/notes/41.1 数据流中的中位数.md +++ b/docs/notes/41.1 数据流中的中位数.md @@ -38,3 +38,10 @@ public Double GetMedian() { return (double) right.peek(); } ``` + + + + + + +
diff --git a/docs/notes/41.2 字符流中第一个不重复的字符.md b/docs/notes/41.2 字符流中第一个不重复的字符.md index fdf64aa0..d1910c8b 100644 --- a/docs/notes/41.2 字符流中第一个不重复的字符.md +++ b/docs/notes/41.2 字符流中第一个不重复的字符.md @@ -23,3 +23,10 @@ public char FirstAppearingOnce() { return queue.isEmpty() ? '#' : queue.peek(); } ``` + + + + + + +
diff --git a/docs/notes/42. 连续子数组的最大和.md b/docs/notes/42. 连续子数组的最大和.md index dce17c1e..22a46f3c 100644 --- a/docs/notes/42. 连续子数组的最大和.md +++ b/docs/notes/42. 连续子数组的最大和.md @@ -22,3 +22,10 @@ public int FindGreatestSumOfSubArray(int[] nums) { } ``` + + + + + + +
diff --git a/docs/notes/43. 从 1 到 n 整数中 1 出现的次数.md b/docs/notes/43. 从 1 到 n 整数中 1 出现的次数.md index 89323cc8..a996676b 100644 --- a/docs/notes/43. 从 1 到 n 整数中 1 出现的次数.md +++ b/docs/notes/43. 从 1 到 n 整数中 1 出现的次数.md @@ -16,3 +16,10 @@ public int NumberOf1Between1AndN_Solution(int n) { ``` > [Leetcode : 233. Number of Digit One](https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython) + + + + + + +
diff --git a/docs/notes/44. 数字序列中的某一位数字.md b/docs/notes/44. 数字序列中的某一位数字.md index 3d869f3a..0344ee56 100644 --- a/docs/notes/44. 数字序列中的某一位数字.md +++ b/docs/notes/44. 数字序列中的某一位数字.md @@ -52,3 +52,10 @@ private int getDigitAtIndex(int index, int place) { return number.charAt(count) - '0'; } ``` + + + + + + +
diff --git a/docs/notes/45. 把数组排成最小的数.md b/docs/notes/45. 把数组排成最小的数.md index 8d98b005..e65c4a4e 100644 --- a/docs/notes/45. 把数组排成最小的数.md +++ b/docs/notes/45. 把数组排成最小的数.md @@ -25,3 +25,10 @@ public String PrintMinNumber(int[] numbers) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/46. 把数字翻译成字符串.md b/docs/notes/46. 把数字翻译成字符串.md index fcbd92fe..bfe8ce42 100644 --- a/docs/notes/46. 把数字翻译成字符串.md +++ b/docs/notes/46. 把数字翻译成字符串.md @@ -29,3 +29,10 @@ public int numDecodings(String s) { return dp[n]; } ``` + + + + + + +
diff --git a/docs/notes/47. 礼物的最大价值.md b/docs/notes/47. 礼物的最大价值.md index e46fb0d0..fe5463e9 100644 --- a/docs/notes/47. 礼物的最大价值.md +++ b/docs/notes/47. 礼物的最大价值.md @@ -33,3 +33,10 @@ public int getMost(int[][] values) { return dp[n - 1]; } ``` + + + + + + +
diff --git a/docs/notes/48. 最长不含重复字符的子字符串.md b/docs/notes/48. 最长不含重复字符的子字符串.md index 576c3905..2d9c885e 100644 --- a/docs/notes/48. 最长不含重复字符的子字符串.md +++ b/docs/notes/48. 最长不含重复字符的子字符串.md @@ -27,3 +27,10 @@ public int longestSubStringWithoutDuplication(String str) { return maxLen; } ``` + + + + + + +
diff --git a/docs/notes/49. 丑数.md b/docs/notes/49. 丑数.md index f92d4d12..bfbc78ab 100644 --- a/docs/notes/49. 丑数.md +++ b/docs/notes/49. 丑数.md @@ -28,3 +28,10 @@ public int GetUglyNumber_Solution(int N) { return dp[N - 1]; } ``` + + + + + + +
diff --git a/docs/notes/5. 替换空格.md b/docs/notes/5. 替换空格.md index bc2c1abc..9751f710 100644 --- a/docs/notes/5. 替换空格.md +++ b/docs/notes/5. 替换空格.md @@ -23,7 +23,7 @@ Output: 从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。 - +

```java public String replaceSpace(StringBuffer str) { @@ -46,3 +46,10 @@ public String replaceSpace(StringBuffer str) { return str.toString(); } ``` + + + + + + +
diff --git a/docs/notes/50. 第一个只出现一次的字符位置.md b/docs/notes/50. 第一个只出现一次的字符位置.md index e25a7098..e433a214 100644 --- a/docs/notes/50. 第一个只出现一次的字符位置.md +++ b/docs/notes/50. 第一个只出现一次的字符位置.md @@ -47,3 +47,10 @@ public int FirstNotRepeatingChar2(String str) { return -1; } ``` + + + + + + +
diff --git a/docs/notes/51. 数组中的逆序对.md b/docs/notes/51. 数组中的逆序对.md index 959857d9..ee439664 100644 --- a/docs/notes/51. 数组中的逆序对.md +++ b/docs/notes/51. 数组中的逆序对.md @@ -46,3 +46,10 @@ private void merge(int[] nums, int l, int m, int h) { nums[k] = tmp[k]; } ``` + + + + + + +
diff --git a/docs/notes/52. 两个链表的第一个公共结点.md b/docs/notes/52. 两个链表的第一个公共结点.md index 8bf22071..7c98bda3 100644 --- a/docs/notes/52. 两个链表的第一个公共结点.md +++ b/docs/notes/52. 两个链表的第一个公共结点.md @@ -4,7 +4,7 @@ ## 题目描述 - +

## 解题思路 @@ -22,3 +22,10 @@ public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { return l1; } ``` + + + + + + +
diff --git a/docs/notes/53. 数字在排序数组中出现的次数.md b/docs/notes/53. 数字在排序数组中出现的次数.md index 6737a2f1..0adf759e 100644 --- a/docs/notes/53. 数字在排序数组中出现的次数.md +++ b/docs/notes/53. 数字在排序数组中出现的次数.md @@ -34,3 +34,10 @@ private int binarySearch(int[] nums, int K) { return l; } ``` + + + + + + +
diff --git a/docs/notes/54. 二叉查找树的第 K 个结点.md b/docs/notes/54. 二叉查找树的第 K 个结点.md index 7f932fe9..3e581e0c 100644 --- a/docs/notes/54. 二叉查找树的第 K 个结点.md +++ b/docs/notes/54. 二叉查找树的第 K 个结点.md @@ -25,3 +25,10 @@ private void inOrder(TreeNode root, int k) { inOrder(root.right, k); } ``` + + + + + + +
diff --git a/docs/notes/55.1 二叉树的深度.md b/docs/notes/55.1 二叉树的深度.md index e0e7d510..2aac036a 100644 --- a/docs/notes/55.1 二叉树的深度.md +++ b/docs/notes/55.1 二叉树的深度.md @@ -6,7 +6,7 @@ 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 - +

## 解题思路 @@ -15,3 +15,10 @@ public int TreeDepth(TreeNode root) { return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right)); } ``` + + + + + + +
diff --git a/docs/notes/55.2 平衡二叉树.md b/docs/notes/55.2 平衡二叉树.md index 31ef6ac6..1f4326b0 100644 --- a/docs/notes/55.2 平衡二叉树.md +++ b/docs/notes/55.2 平衡二叉树.md @@ -6,7 +6,7 @@ 平衡二叉树左右子树高度差不超过 1。 - +

## 解题思路 @@ -28,3 +28,10 @@ private int height(TreeNode root) { return 1 + Math.max(left, right); } ``` + + + + + + +
diff --git a/docs/notes/56. 数组中只出现一次的数字.md b/docs/notes/56. 数组中只出现一次的数字.md index a8b77c5e..5cbac703 100644 --- a/docs/notes/56. 数组中只出现一次的数字.md +++ b/docs/notes/56. 数组中只出现一次的数字.md @@ -26,3 +26,10 @@ public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) { } } ``` + + + + + + +
diff --git a/docs/notes/57.1 和为 S 的两个数字.md b/docs/notes/57.1 和为 S 的两个数字.md index caf0f3c8..a9d9f6bb 100644 --- a/docs/notes/57.1 和为 S 的两个数字.md +++ b/docs/notes/57.1 和为 S 的两个数字.md @@ -29,3 +29,10 @@ public ArrayList FindNumbersWithSum(int[] array, int sum) { return new ArrayList<>(); } ``` + + + + + + +
diff --git a/docs/notes/57.2 和为 S 的连续正数序列.md b/docs/notes/57.2 和为 S 的连续正数序列.md index 6a3683d5..012db7b6 100644 --- a/docs/notes/57.2 和为 S 的连续正数序列.md +++ b/docs/notes/57.2 和为 S 的连续正数序列.md @@ -41,3 +41,10 @@ public ArrayList> FindContinuousSequence(int sum) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/58.1 翻转单词顺序列.md b/docs/notes/58.1 翻转单词顺序列.md index 403ced39..2d3f0437 100644 --- a/docs/notes/58.1 翻转单词顺序列.md +++ b/docs/notes/58.1 翻转单词顺序列.md @@ -45,3 +45,10 @@ private void swap(char[] c, int i, int j) { c[j] = t; } ``` + + + + + + +
diff --git a/docs/notes/58.2 左旋转字符串.md b/docs/notes/58.2 左旋转字符串.md index 663b0322..d71486f9 100644 --- a/docs/notes/58.2 左旋转字符串.md +++ b/docs/notes/58.2 左旋转字符串.md @@ -39,3 +39,10 @@ private void swap(char[] chars, int i, int j) { chars[j] = t; } ``` + + + + + + +
diff --git a/docs/notes/59. 滑动窗口的最大值.md b/docs/notes/59. 滑动窗口的最大值.md index a4442ffe..dda1258f 100644 --- a/docs/notes/59. 滑动窗口的最大值.md +++ b/docs/notes/59. 滑动窗口的最大值.md @@ -27,3 +27,10 @@ public ArrayList maxInWindows(int[] num, int size) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/6. 从尾到头打印链表.md b/docs/notes/6. 从尾到头打印链表.md index f6e7871a..d359192d 100644 --- a/docs/notes/6. 从尾到头打印链表.md +++ b/docs/notes/6. 从尾到头打印链表.md @@ -6,7 +6,7 @@ 从尾到头反过来打印出每个结点的值。 - +

## 解题思路 @@ -34,7 +34,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { - 头结点是在头插法中使用的一个额外节点,这个节点不存储值; - 第一个节点就是链表的第一个真正存储值的节点。 - +

```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -61,7 +61,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { 栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。 - +

```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -76,3 +76,10 @@ public ArrayList printListFromTailToHead(ListNode listNode) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/60. n 个骰子的点数.md b/docs/notes/60. n 个骰子的点数.md index c743de07..e0d84d69 100644 --- a/docs/notes/60. n 个骰子的点数.md +++ b/docs/notes/60. n 个骰子的点数.md @@ -6,7 +6,7 @@ 把 n 个骰子扔在地上,求点数和为 s 的概率。 - +

## 解题思路 @@ -70,3 +70,10 @@ public List> dicesSum(int n) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/61. 扑克牌顺子.md b/docs/notes/61. 扑克牌顺子.md index c0e014e3..c0defdf3 100644 --- a/docs/notes/61. 扑克牌顺子.md +++ b/docs/notes/61. 扑克牌顺子.md @@ -6,7 +6,7 @@ 五张牌,其中大小鬼为癞子,牌面为 0。判断这五张牌是否能组成顺子。 - +

## 解题思路 @@ -35,3 +35,10 @@ public boolean isContinuous(int[] nums) { return cnt >= 0; } ``` + + + + + + +
diff --git a/docs/notes/62. 圆圈中最后剩下的数.md b/docs/notes/62. 圆圈中最后剩下的数.md index 5e32a090..41813cc5 100644 --- a/docs/notes/62. 圆圈中最后剩下的数.md +++ b/docs/notes/62. 圆圈中最后剩下的数.md @@ -19,3 +19,10 @@ public int LastRemaining_Solution(int n, int m) { return (LastRemaining_Solution(n - 1, m) + m) % n; } ``` + + + + + + +
diff --git a/docs/notes/63. 股票的最大利润.md b/docs/notes/63. 股票的最大利润.md index 702bf999..eaadd6b9 100644 --- a/docs/notes/63. 股票的最大利润.md +++ b/docs/notes/63. 股票的最大利润.md @@ -6,7 +6,7 @@ 可以有一次买入和一次卖出,买入必须在前。求最大收益。 - +

## 解题思路 @@ -25,3 +25,10 @@ public int maxProfit(int[] prices) { return maxProfit; } ``` + + + + + + +
diff --git a/docs/notes/64. 求 1+2+3+...+n.md b/docs/notes/64. 求 1+2+3+...+n.md index 7e9d0768..c7ffc571 100644 --- a/docs/notes/64. 求 1+2+3+...+n.md +++ b/docs/notes/64. 求 1+2+3+...+n.md @@ -21,3 +21,10 @@ public int Sum_Solution(int n) { return sum; } ``` + + + + + + +
diff --git a/docs/notes/65. 不用加减乘除做加法.md b/docs/notes/65. 不用加减乘除做加法.md index e15652a2..004442d5 100644 --- a/docs/notes/65. 不用加减乘除做加法.md +++ b/docs/notes/65. 不用加减乘除做加法.md @@ -17,3 +17,10 @@ public int Add(int a, int b) { return b == 0 ? a : Add(a ^ b, (a & b) << 1); } ``` + + + + + + +
diff --git a/docs/notes/66. 构建乘积数组.md b/docs/notes/66. 构建乘积数组.md index 5f532991..8665a846 100644 --- a/docs/notes/66. 构建乘积数组.md +++ b/docs/notes/66. 构建乘积数组.md @@ -6,7 +6,7 @@ 给定一个数组 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]。要求不能使用除法。 - +

## 解题思路 @@ -22,3 +22,10 @@ public int[] multiply(int[] A) { return B; } ``` + + + + + + +
diff --git a/docs/notes/67. 把字符串转换成整数.md b/docs/notes/67. 把字符串转换成整数.md index ec6dc914..8ecdccf1 100644 --- a/docs/notes/67. 把字符串转换成整数.md +++ b/docs/notes/67. 把字符串转换成整数.md @@ -35,3 +35,10 @@ public int StrToInt(String str) { return isNegative ? -ret : ret; } ``` + + + + + + +
diff --git a/docs/notes/68. 树中两个节点的最低公共祖先.md b/docs/notes/68. 树中两个节点的最低公共祖先.md index b6cb095b..38529978 100644 --- a/docs/notes/68. 树中两个节点的最低公共祖先.md +++ b/docs/notes/68. 树中两个节点的最低公共祖先.md @@ -8,7 +8,7 @@ 二叉查找树中,两个节点 p, q 的公共祖先 root 满足 root.val >= p.val && root.val <= q.val。 - +

```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -28,7 +28,7 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 在左右子树中查找是否存在 p 或者 q,如果 p 和 q 分别在两个子树中,那么就说明根节点就是最低公共祖先。 - +

```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -39,3 +39,10 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return left == null ? right : right == null ? left : root; } ``` + + + + + + +
diff --git a/docs/notes/7. 重建二叉树.md b/docs/notes/7. 重建二叉树.md index ca5385dd..851ed64e 100644 --- a/docs/notes/7. 重建二叉树.md +++ b/docs/notes/7. 重建二叉树.md @@ -7,13 +7,13 @@ 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 - +

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

```java // 缓存中序遍历数组每个值对应的索引 @@ -36,3 +36,10 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL) { return root; } ``` + + + + + + +
diff --git a/docs/notes/8. 二叉树的下一个结点.md b/docs/notes/8. 二叉树的下一个结点.md index 4a697083..fc7b2802 100644 --- a/docs/notes/8. 二叉树的下一个结点.md +++ b/docs/notes/8. 二叉树的下一个结点.md @@ -24,11 +24,11 @@ public class TreeLinkNode { ① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点; - +

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

```java public TreeLinkNode GetNext(TreeLinkNode pNode) { @@ -48,3 +48,10 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) { return null; } ``` + + + + + + +
diff --git a/docs/notes/9. 用两个栈实现队列.md b/docs/notes/9. 用两个栈实现队列.md index eb9f5796..5a57470f 100644 --- a/docs/notes/9. 用两个栈实现队列.md +++ b/docs/notes/9. 用两个栈实现队列.md @@ -10,7 +10,7 @@ in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。 - +

```java Stack in = new Stack(); @@ -31,3 +31,10 @@ public int pop() throws Exception { return out.pop(); } ``` + + + + + + +
diff --git a/docs/notes/Docker.md b/docs/notes/Docker.md index 18bca531..23e33e7e 100644 --- a/docs/notes/Docker.md +++ b/docs/notes/Docker.md @@ -1,4 +1,12 @@ -[TOC] + +* [一、解决的问题](#一解决的问题) +* [二、与虚拟机的比较](#二与虚拟机的比较) +* [三、优势](#三优势) +* [四、使用场景](#四使用场景) +* [五、镜像与容器](#五镜像与容器) +* [参考资料](#参考资料) + + # 一、解决的问题 @@ -6,13 +14,13 @@ Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其它机器上。 - +

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

## 启动速度 @@ -66,7 +74,7 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服 构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。 -![](pics/docker-filesystems-busyboxrw.png) +

# 参考资料 @@ -79,3 +87,10 @@ Docker 轻量级的特点使得它很适合用于部署、维护、组合微服 - [What is Docker](https://www.docker.com/what-docker) - [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) + + + + + + +
diff --git a/docs/notes/Git.md b/docs/notes/Git.md index c13bb181..e84f1871 100644 --- a/docs/notes/Git.md +++ b/docs/notes/Git.md @@ -1,10 +1,24 @@ -[TOC] + +* [集中式与分布式](#集中式与分布式) +* [中心服务器](#中心服务器) +* [工作流](#工作流) +* [分支实现](#分支实现) +* [冲突](#冲突) +* [Fast forward](#fast-forward) +* [分支管理策略](#分支管理策略) +* [储藏(Stashing)](#储藏stashing) +* [SSH 传输设置](#ssh-传输设置) +* [.gitignore 文件](#gitignore-文件) +* [Git 命令一览](#git-命令一览) +* [参考资料](#参考资料) + + # 集中式与分布式 Git 属于分布式版本控制系统,而 SVN 属于集中式。 - +

集中式版本控制只有中心服务器拥有一份代码,而分布式版本控制每个人的电脑上就有一份完整的代码。 @@ -26,45 +40,45 @@ Github 就是一个中心服务器。 Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本库,History 存储所有分支信息,使用一个 HEAD 指针指向当前分支。 - +

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

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

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

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

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

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

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

Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。 @@ -86,7 +100,7 @@ Creating a new branch is quick AND simple. $ git merge --no-ff -m "merge with no-ff" dev ``` - +

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

# 储藏(Stashing) @@ -134,7 +148,7 @@ $ ssh-keygen -t rsa -C "youremail@example.com" # Git 命令一览 - +

比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf @@ -144,3 +158,10 @@ $ ssh-keygen -t rsa -C "youremail@example.com" - [图解 Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html) - [廖雪峰 : Git 教程](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) - [Learn Git Branching](https://learngitbranching.js.org/) + + + + + + +
diff --git a/docs/notes/HTTP.md b/docs/notes/HTTP.md index 6225018c..4f2071cf 100644 --- a/docs/notes/HTTP.md +++ b/docs/notes/HTTP.md @@ -1,4 +1,60 @@ -[TOC] + +* [一 、基础概念](#一-基础概念) + * [URI](#uri) + * [请求和响应报文](#请求和响应报文) +* [二、HTTP 方法](#二http-方法) + * [GET](#get) + * [HEAD](#head) + * [POST](#post) + * [PUT](#put) + * [PATCH](#patch) + * [DELETE](#delete) + * [OPTIONS](#options) + * [CONNECT](#connect) + * [TRACE](#trace) +* [三、HTTP 状态码](#三http-状态码) + * [1XX 信息](#1xx-信息) + * [2XX 成功](#2xx-成功) + * [3XX 重定向](#3xx-重定向) + * [4XX 客户端错误](#4xx-客户端错误) + * [5XX 服务器错误](#5xx-服务器错误) +* [四、HTTP 首部](#四http-首部) + * [通用首部字段](#通用首部字段) + * [请求首部字段](#请求首部字段) + * [响应首部字段](#响应首部字段) + * [实体首部字段](#实体首部字段) +* [五、具体应用](#五具体应用) + * [连接管理](#连接管理) + * [Cookie](#cookie) + * [缓存](#缓存) + * [内容协商](#内容协商) + * [内容编码](#内容编码) + * [范围请求](#范围请求) + * [分块传输编码](#分块传输编码) + * [多部分对象集合](#多部分对象集合) + * [虚拟主机](#虚拟主机) + * [通信数据转发](#通信数据转发) +* [六、HTTPS](#六https) + * [加密](#加密) + * [认证](#认证) + * [完整性保护](#完整性保护) + * [HTTPS 的缺点](#https-的缺点) +* [七、HTTP/2.0](#七http20) + * [HTTP/1.x 缺陷](#http1x-缺陷) + * [二进制分帧层](#二进制分帧层) + * [服务端推送](#服务端推送) + * [首部压缩](#首部压缩) +* [八、HTTP/1.1 新特性](#八http11-新特性) +* [九、GET 和 POST 比较](#九get-和-post-比较) + * [作用](#作用) + * [参数](#参数) + * [安全](#安全) + * [幂等性](#幂等性) + * [可缓存](#可缓存) + * [XMLHttpRequest](#xmlhttprequest) +* [参考资料](#参考资料) + + # 一 、基础概念 @@ -6,21 +62,21 @@ URI 包含 URL 和 URN。 - +

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

### 2. 响应报文 - +

# 二、HTTP 方法 -客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 +客户端发送的 **请求报文** 第一行为请求行,包含了方法字段。 ## GET @@ -103,7 +159,7 @@ DELETE /file.html HTTP/1.1 CONNECT www.example.com:443 HTTP/1.1 ``` - +

## TRACE @@ -117,7 +173,7 @@ CONNECT www.example.com:443 HTTP/1.1 # 三、HTTP 状态码 -服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 +服务器返回的 **响应报文** 中第一行为状态行,包含了状态码以及原因短语,用来告知客户端请求的结果。 | 状态码 | 类别 | 含义 | | :---: | :---: | :---: | @@ -129,45 +185,45 @@ CONNECT www.example.com:443 HTTP/1.1 ## 1XX 信息 -- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 +- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。 ## 2XX 成功 -- **200 OK** +- **200 OK** -- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 +- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。 -- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 +- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。 ## 3XX 重定向 -- **301 Moved Permanently** :永久性重定向 +- **301 Moved Permanently** :永久性重定向 -- **302 Found** :临时性重定向 +- **302 Found** :临时性重定向 -- **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 +- **303 See Other** :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。 - 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。 -- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 +- **304 Not Modified** :如果请求报文首部包含一些条件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不满足条件,则服务器会返回 304 状态码。 -- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 +- **307 Temporary Redirect** :临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。 ## 4XX 客户端错误 -- **400 Bad Request** :请求报文中存在语法错误。 +- **400 Bad Request** :请求报文中存在语法错误。 -- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 +- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。 -- **403 Forbidden** :请求被拒绝。 +- **403 Forbidden** :请求被拒绝。 -- **404 Not Found** +- **404 Not Found** ## 5XX 服务器错误 -- **500 Internal Server Error** :服务器正在执行请求时发生错误。 +- **500 Internal Server Error** :服务器正在执行请求时发生错误。 -- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 +- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 # 四、HTTP 首部 @@ -246,7 +302,7 @@ CONNECT www.example.com:443 HTTP/1.1 ## 连接管理 - +

### 1. 短连接与长连接 @@ -380,7 +436,7 @@ Session 可以存储在服务器上的文件、数据库或者内存中。也可 HTTP/1.1 通过 Cache-Control 首部字段来控制缓存。 -**3.1 禁止进行缓存** +**3.1 禁止进行缓存** no-store 指令规定不能对请求或响应的任何一部分进行缓存。 @@ -388,7 +444,7 @@ no-store 指令规定不能对请求或响应的任何一部分进行缓存。 Cache-Control: no-store ``` -**3.2 强制确认缓存** +**3.2 强制确认缓存** no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源的有效性,只有当缓存资源有效时才能使用该缓存对客户端的请求进行响应。 @@ -396,7 +452,7 @@ no-cache 指令规定缓存服务器需要先向源服务器验证缓存资源 Cache-Control: no-cache ``` -**3.3 私有缓存和公共缓存** +**3.3 私有缓存和公共缓存** private 指令规定了将资源作为私有缓存,只能被单独用户使用,一般存储在用户浏览器中。 @@ -410,7 +466,7 @@ public 指令规定了将资源作为公共缓存,可以被多个用户使用 Cache-Control: public ``` -**3.4 缓存过期机制** +**3.4 缓存过期机制** max-age 指令出现在请求报文,并且缓存资源的缓存时间小于该指令指定的时间,那么就能接受该缓存。 @@ -459,7 +515,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT ### 1. 类型 -**1.1 服务端驱动型** +**1.1 服务端驱动型** 客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服务器根据这些字段返回特定的资源。 @@ -469,7 +525,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT - 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术); - 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。 -**1.2 代理驱动型** +**1.2 代理驱动型** 服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。 @@ -575,11 +631,11 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名, - 用户察觉得到正向代理的存在。 - +

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

### 2. 网关 @@ -601,7 +657,7 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer) 通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。 - +

## 加密 @@ -612,7 +668,7 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer) - 优点:运算速度快; - 缺点:无法安全地将密钥传输给通信方。 - +

### 2.非对称密钥加密 @@ -625,17 +681,17 @@ HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer) - 优点:可以更安全地将公开密钥传输给通信发送方; - 缺点:运算速度慢。 - +

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

## 认证 -通过使用 **证书** 来对通信方进行认证。 +通过使用 **证书** 来对通信方进行认证。 数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。 @@ -643,7 +699,7 @@ HTTPS 采用混合的加密机制,使用非对称密钥加密用于传输对 进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。 - +

## 完整性保护 @@ -672,7 +728,7 @@ HTTP/1.x 实现简单是以牺牲性能为代价的: HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。 - +

在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。 @@ -680,13 +736,13 @@ HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式 - 消息(Message)是与逻辑请求或响应对应的完整的一系列帧。 - 帧(Frame)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。 - +

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

## 首部压缩 @@ -696,7 +752,7 @@ HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见 不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。 - +

# 八、HTTP/1.1 新特性 @@ -821,3 +877,10 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404 - [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences) - [Web 性能优化与 HTTP/2](https://www.kancloud.cn/digest/web-performance-http2) - [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn) + + + + + + +
diff --git a/docs/notes/Java IO.md b/docs/notes/Java IO.md index 910789b8..912d5bb3 100644 --- a/docs/notes/Java IO.md +++ b/docs/notes/Java IO.md @@ -1,4 +1,35 @@ -[TOC] + +* [一、概览](#一概览) +* [二、磁盘操作](#二磁盘操作) +* [三、字节操作](#三字节操作) + * [实现文件复制](#实现文件复制) + * [装饰者模式](#装饰者模式) +* [四、字符操作](#四字符操作) + * [编码与解码](#编码与解码) + * [String 的编码方式](#string-的编码方式) + * [Reader 与 Writer](#reader-与-writer) + * [实现逐行输出文本文件的内容](#实现逐行输出文本文件的内容) +* [五、对象操作](#五对象操作) + * [序列化](#序列化) + * [Serializable](#serializable) + * [transient](#transient) +* [六、网络操作](#六网络操作) + * [InetAddress](#inetaddress) + * [URL](#url) + * [Sockets](#sockets) + * [Datagram](#datagram) +* [七、NIO](#七nio) + * [流与块](#流与块) + * [通道与缓冲区](#通道与缓冲区) + * [缓冲区状态变量](#缓冲区状态变量) + * [文件 NIO 实例](#文件-nio-实例) + * [选择器](#选择器) + * [套接字 NIO 实例](#套接字-nio-实例) + * [内存映射文件](#内存映射文件) + * [对比](#对比) +* [八、参考资料](#八参考资料) + + # 一、概览 @@ -66,7 +97,7 @@ Java I/O 使用了装饰者模式来实现。以 InputStream 为例, - FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作; - FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。 - +

实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。 @@ -246,7 +277,7 @@ public static void main(String[] args) throws IOException { - Socket:客户端类 - 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。 - +

## Datagram @@ -308,23 +339,23 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重 ① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。 -![](pics/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png) +

② 从输入通道中读取 5 个字节数据写入缓冲区中,此时 position 为 5,limit 保持不变。 -![](pics/80804f52-8815-4096-b506-48eef3eed5c6.png) +

③ 在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。 -![](pics/952e06bd-5a65-4cab-82e4-dd1536462f38.png) +

④ 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。 -![](pics/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png) +

⑤ 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。 -![](pics/67bf5487-c45d-49b6-b9c0-a058d8c68902.png) +

## 文件 NIO 实例 @@ -382,7 +413,7 @@ NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用 应该注意的是,只有套接字 Channel 才能配置为非阻塞,而 FileChannel 不能,为 FileChannel 配置非阻塞也没有意义。 - +

### 1. 创建选择器 @@ -587,3 +618,10 @@ NIO 与普通 I/O 的区别主要有以下两点: - [NIO 与传统 IO 的区别](http://blog.csdn.net/shimiso/article/details/24990499) - [Decorator Design Pattern](http://stg-tud.github.io/sedc/Lecture/ws13-14/5.3-Decorator.html#mode=document) - [Socket Multicast](http://labojava.blogspot.com/2012/12/socket-multicast.html) + + + + + + +
diff --git a/docs/notes/Java 基础.md b/docs/notes/Java 基础.md index 64080c8c..2d074840 100644 --- a/docs/notes/Java 基础.md +++ b/docs/notes/Java 基础.md @@ -1,4 +1,44 @@ -[TOC] + +* [一、数据类型](#一数据类型) + * [基本类型](#基本类型) + * [包装类型](#包装类型) + * [缓存池](#缓存池) +* [二、String](#二string) + * [概览](#概览) + * [不可变的好处](#不可变的好处) + * [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder) + * [String Pool](#string-pool) + * [new String("abc")](#new-string"abc") +* [三、运算](#三运算) + * [参数传递](#参数传递) + * [float 与 double](#float-与-double) + * [隐式类型转换](#隐式类型转换) + * [switch](#switch) +* [四、继承](#四继承) + * [访问权限](#访问权限) + * [抽象类与接口](#抽象类与接口) + * [super](#super) + * [重写与重载](#重写与重载) +* [五、Object 通用方法](#五object-通用方法) + * [概览](#概览) + * [equals()](#equals) + * [hashCode()](#hashcode) + * [toString()](#tostring) + * [clone()](#clone) +* [六、关键字](#六关键字) + * [final](#final) + * [static](#static) +* [七、反射](#七反射) +* [八、异常](#八异常) +* [九、泛型](#九泛型) +* [十、注解](#十注解) +* [十一、特性](#十一特性) + * [Java 各版本的新特性](#java-各版本的新特性) + * [Java 与 C++ 的区别](#java-与-c-的区别) + * [JRE or JDK](#jre-or-jdk) +* [参考资料](#参考资料) + + # 一、数据类型 @@ -145,21 +185,21 @@ value 数组被声明为 final,这意味着 value 数组初始化之后就不 ## 不可变的好处 -**1. 可以缓存 hash 值** +**1. 可以缓存 hash 值** 因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。 -**2. String Pool 的需要** +**2. String Pool 的需要** 如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。 - +

-**3. 安全性** +**3. 安全性** String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。 -**4. 线程安全** +**4. 线程安全** String 不可变性天生具备线程安全,可以在多个线程中安全地使用。 @@ -167,12 +207,12 @@ String 不可变性天生具备线程安全,可以在多个线程中安全地 ## String, StringBuffer and StringBuilder -**1. 可变性** +**1. 可变性** - String 不可变 - StringBuffer 和 StringBuilder 可变 -**2. 线程安全** +**2. 线程安全** - String 不可变,因此是线程安全的 - StringBuilder 不是线程安全的 @@ -469,7 +509,7 @@ public class AccessWithInnerClassExample { ## 抽象类与接口 -**1. 抽象类** +**1. 抽象类** 抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。 @@ -504,7 +544,7 @@ AbstractClassExample ac2 = new AbstractExtendClassExample(); ac2.func1(); ``` -**2. 接口** +**2. 接口** 接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。 @@ -548,14 +588,14 @@ ie2.func1(); System.out.println(InterfaceExample.x); ``` -**3. 比较** +**3. 比较** - 从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 - 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。 - 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。 - 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。 -**4. 使用选择** +**4. 使用选择** 使用接口: @@ -629,7 +669,7 @@ SuperExtendExample.func() ## 重写与重载 -**1. 重写(Override)** +**1. 重写(Override)** 存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。 @@ -733,7 +773,7 @@ public static void main(String[] args) { } ``` -**2. 重载(Overload)** +**2. 重载(Overload)** 存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。 @@ -770,7 +810,7 @@ public final void wait() throws InterruptedException ## equals() -**1. 等价关系** +**1. 等价关系** Ⅰ 自反性 @@ -807,7 +847,7 @@ x.equals(y) == x.equals(y); // true x.equals(null); // false; ``` -**2. 等价与相等** +**2. 等价与相等** - 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。 - 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。 @@ -819,7 +859,7 @@ System.out.println(x.equals(y)); // true System.out.println(x == y); // false ``` -**3. 实现** +**3. 实现** - 检查是否为同一个对象的引用,如果是直接返回 true; - 检查是否是同一个类型,如果不是,直接返回 false; @@ -912,7 +952,7 @@ ToStringExample@4554617c ## clone() -**1. cloneable** +**1. cloneable** clone() 是 Object 的 protected 方法,它不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 @@ -971,7 +1011,7 @@ public class CloneExample implements Cloneable { } ``` -**2. 浅拷贝** +**2. 浅拷贝** 拷贝对象和原始对象的引用类型引用同一个对象。 @@ -1014,7 +1054,7 @@ e1.set(2, 222); System.out.println(e2.get(2)); // 222 ``` -**3. 深拷贝** +**3. 深拷贝** 拷贝对象和原始对象的引用类型引用不同对象。 @@ -1062,7 +1102,7 @@ e1.set(2, 222); System.out.println(e2.get(2)); // 2 ``` -**4. clone() 的替代方案** +**4. clone() 的替代方案** 使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。 @@ -1106,7 +1146,7 @@ System.out.println(e2.get(2)); // 2 ## final -**1. 数据** +**1. 数据** 声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。 @@ -1120,19 +1160,19 @@ final A y = new A(); y.a = 1; ``` -**2. 方法** +**2. 方法** 声明方法不能被子类重写。 private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。 -**3. 类** +**3. 类** 声明类不允许被继承。 ## static -**1. 静态变量** +**1. 静态变量** - 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。 - 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。 @@ -1152,7 +1192,7 @@ public class A { } ``` -**2. 静态方法** +**2. 静态方法** 静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。 @@ -1180,7 +1220,7 @@ public class A { } ``` -**3. 静态语句块** +**3. 静态语句块** 静态语句块在类初始化时运行一次。 @@ -1201,7 +1241,7 @@ public class A { 123 ``` -**4. 静态内部类** +**4. 静态内部类** 非静态内部类依赖于外部类的实例,而静态内部类不需要。 @@ -1225,7 +1265,7 @@ public class OuterClass { 静态内部类不能访问外部类的非静态的变量和方法。 -**5. 静态导包** +**5. 静态导包** 在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。 @@ -1233,7 +1273,7 @@ public class OuterClass { import static com.xxx.ClassName.* ``` -**6. 初始化顺序** +**6. 初始化顺序** 静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。 @@ -1277,7 +1317,7 @@ public InitialOrderTest() { # 七、反射 -每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 +每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。 类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 `Class.forName("com.mysql.jdbc.Driver")` 这种方式来控制类的加载,该方法会返回一个 Class 对象。 @@ -1285,25 +1325,25 @@ public InitialOrderTest() { Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类: -- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; -- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; -- **Constructor** :可以用 Constructor 的 newInstance() 创建新的对象。 +- **Field** :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段; +- **Method** :可以使用 invoke() 方法调用与 Method 对象关联的方法; +- **Constructor** :可以用 Constructor 的 newInstance() 创建新的对象。 -**反射的优点:** +**反射的优点:** -* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 -* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 -* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 +* **可扩展性** :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。 +* **类浏览器和可视化开发环境** :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。 +* **调试器和测试工具** : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。 -**反射的缺点:** +**反射的缺点:** 尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。 -* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 +* **性能开销** :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。 -* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 +* **安全限制** :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。 -* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 +* **内部暴露** :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。 - [Trail: The Reflection API](https://docs.oracle.com/javase/tutorial/reflect/index.html) @@ -1311,12 +1351,12 @@ Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect # 八、异常 -Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: +Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种: -- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; -- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 +- **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复; +- **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。 - +

- [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception) - [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html) @@ -1345,7 +1385,7 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译 ## Java 各版本的新特性 -**New highlights in Java SE 8** +**New highlights in Java SE 8** 1. Lambda Expressions 2. Pipelines and Streams @@ -1357,7 +1397,7 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译 8. Parallel operations 9. PermGen Error Removed -**New highlights in Java SE 7** +**New highlights in Java SE 7** 1. Strings in Switch Statement 2. Type Inference for Generic Instance Creation @@ -1392,3 +1432,10 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译 - Eckel B. Java 编程思想[M]. 机械工业出版社, 2002. - Bloch J. Effective java[M]. Addison-Wesley Professional, 2017. + + + + + + +
diff --git a/docs/notes/Java 容器.md b/docs/notes/Java 容器.md index f566e600..ed810d35 100644 --- a/docs/notes/Java 容器.md +++ b/docs/notes/Java 容器.md @@ -1,4 +1,22 @@ -[TOC] + +* [一、概览](#一概览) + * [Collection](#collection) + * [Map](#map) +* [二、容器中的设计模式](#二容器中的设计模式) + * [迭代器模式](#迭代器模式) + * [适配器模式](#适配器模式) +* [三、源码分析](#三源码分析) + * [ArrayList](#arraylist) + * [Vector](#vector) + * [CopyOnWriteArrayList](#copyonwritearraylist) + * [LinkedList](#linkedlist) + * [HashMap](#hashmap) + * [ConcurrentHashMap](#concurrenthashmap) + * [LinkedHashMap](#linkedhashmap) + * [WeakHashMap](#weakhashmap) +* [参考资料](#参考资料) + + # 一、概览 @@ -6,7 +24,7 @@ ## Collection - +

### 1. Set @@ -32,7 +50,7 @@ ## Map - +

- TreeMap:基于红黑树实现。 @@ -47,7 +65,7 @@ ## 迭代器模式 - +

Collection 继承了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。 @@ -108,7 +126,7 @@ public class ArrayList extends AbstractList private static final int DEFAULT_CAPACITY = 10; ``` - +

### 2. 扩容 @@ -412,7 +430,7 @@ transient Node first; transient Node last; ``` - +

### 2. 与 ArrayList 的比较 @@ -434,7 +452,7 @@ transient Entry[] table; Entry 存储着键值对。它包含了四个字段,从 next 字段我们可以看出 Entry 是一个链表。即数组中的每个位置被当成一个桶,一个桶存放一个链表。HashMap 使用拉链法来解决冲突,同一个链表中存放哈希值和散列桶取模运算结果相同的 Entry。 - +

```java static class Entry implements Map.Entry { @@ -510,7 +528,7 @@ map.put("K3", "V3"); - 计算键值对所在的桶; - 在链表上顺序查找,时间复杂度显然和链表的长度成正比。 - +

### 3. put 操作 @@ -600,7 +618,7 @@ int hash = hash(key); int i = indexFor(hash, table.length); ``` -**4.1 计算 hash 值** +**4.1 计算 hash 值** ```java final int hash(Object k) { @@ -625,7 +643,7 @@ public final int hashCode() { } ``` -**4.2 取模** +**4.2 取模** 令 x = 1<<4,即 x 为 2 的 4 次方,它具有以下性质: @@ -846,7 +864,7 @@ final Segment[] segments; static final int DEFAULT_CONCURRENCY_LEVEL = 16; ``` - +

### 2. size 操作 @@ -1132,3 +1150,10 @@ public final class ConcurrentCache { - [Java 集合细节(二):asList 的缺陷](http://wiki.jikexueyuan.com/project/java-enhancement/java-thirtysix.html) - [Java Collection Framework – The LinkedList Class](http://javaconceptoftheday.com/java-collection-framework-linkedlist-class/) + + + + + + +
diff --git a/docs/notes/Java 并发.md b/docs/notes/Java 并发.md index 4f1e412f..e634e2ab 100644 --- a/docs/notes/Java 并发.md +++ b/docs/notes/Java 并发.md @@ -1,8 +1,67 @@ -[TOC] + +* [一、线程状态转换](#一线程状态转换) + * [新建(New)](#新建new) + * [可运行(Runnable)](#可运行runnable) + * [阻塞(Blocked)](#阻塞blocked) + * [无限期等待(Waiting)](#无限期等待waiting) + * [限期等待(Timed Waiting)](#限期等待timed-waiting) + * [死亡(Terminated)](#死亡terminated) +* [二、使用线程](#二使用线程) + * [实现 Runnable 接口](#实现-runnable-接口) + * [实现 Callable 接口](#实现-callable-接口) + * [继承 Thread 类](#继承-thread-类) + * [实现接口 VS 继承 Thread](#实现接口-vs-继承-thread) +* [三、基础线程机制](#三基础线程机制) + * [Executor](#executor) + * [Daemon](#daemon) + * [sleep()](#sleep) + * [yield()](#yield) +* [四、中断](#四中断) + * [InterruptedException](#interruptedexception) + * [interrupted()](#interrupted) + * [Executor 的中断操作](#executor-的中断操作) +* [五、互斥同步](#五互斥同步) + * [synchronized](#synchronized) + * [ReentrantLock](#reentrantlock) + * [比较](#比较) + * [使用选择](#使用选择) +* [六、线程之间的协作](#六线程之间的协作) + * [join()](#join) + * [wait() notify() notifyAll()](#wait-notify-notifyall) + * [await() signal() signalAll()](#await-signal-signalall) +* [七、J.U.C - AQS](#七juc---aqs) + * [CountDownLatch](#countdownlatch) + * [CyclicBarrier](#cyclicbarrier) + * [Semaphore](#semaphore) +* [八、J.U.C - 其它组件](#八juc---其它组件) + * [FutureTask](#futuretask) + * [BlockingQueue](#blockingqueue) + * [ForkJoin](#forkjoin) +* [九、线程不安全示例](#九线程不安全示例) +* [十、Java 内存模型](#十java-内存模型) + * [主内存与工作内存](#主内存与工作内存) + * [内存间交互操作](#内存间交互操作) + * [内存模型三大特性](#内存模型三大特性) + * [先行发生原则](#先行发生原则) +* [十一、线程安全](#十一线程安全) + * [不可变](#不可变) + * [互斥同步](#互斥同步) + * [非阻塞同步](#非阻塞同步) + * [无同步方案](#无同步方案) +* [十二、锁优化](#十二锁优化) + * [自旋锁](#自旋锁) + * [锁消除](#锁消除) + * [锁粗化](#锁粗化) + * [轻量级锁](#轻量级锁) + * [偏向锁](#偏向锁) +* [十三、多线程开发良好的实践](#十三多线程开发良好的实践) +* [参考资料](#参考资料) + + # 一、线程状态转换 - +

## 新建(New) @@ -325,7 +384,7 @@ Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问 ## synchronized -**1. 同步一个代码块** +**1. 同步一个代码块** ```java public void func() { @@ -382,7 +441,7 @@ public static void main(String[] args) { ``` -**2. 同步一个方法** +**2. 同步一个方法** ```java public synchronized void func () { @@ -392,7 +451,7 @@ public synchronized void func () { 它和同步代码块一样,作用于同一个对象。 -**3. 同步一个类** +**3. 同步一个类** ```java public void func() { @@ -431,7 +490,7 @@ public static void main(String[] args) { 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 ``` -**4. 同步一个静态方法** +**4. 同步一个静态方法** ```java public synchronized static void fun() { @@ -479,27 +538,27 @@ public static void main(String[] args) { ## 比较 -**1. 锁的实现** +**1. 锁的实现** synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。 -**2. 性能** +**2. 性能** 新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。 -**3. 等待可中断** +**3. 等待可中断** 当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。 ReentrantLock 可中断,而 synchronized 不行。 -**4. 公平锁** +**4. 公平锁** 公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。 synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。 -**5. 锁绑定多个条件** +**5. 锁绑定多个条件** 一个 ReentrantLock 可以同时绑定多个 Condition 对象。 @@ -610,7 +669,7 @@ before after ``` -**wait() 和 sleep() 的区别** +**wait() 和 sleep() 的区别** - wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法; - wait() 会释放锁,sleep() 不会。 @@ -677,7 +736,7 @@ java.util.concurrent(J.U.C)大大提高了并发性能,AQS 被认为是 J. 维护了一个计数器 cnt,每次调用 countDown() 方法会让计数器的值减 1,减到 0 的时候,那些因为调用 await() 方法而在等待的线程就会被唤醒。 - +

```java public class CountdownLatchExample { @@ -726,7 +785,7 @@ public CyclicBarrier(int parties) { } ``` - +

```java public class CyclicBarrierExample { @@ -848,12 +907,12 @@ other task is running... java.util.concurrent.BlockingQueue 接口有以下阻塞队列的实现: -- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度) -- **优先级队列** :PriorityBlockingQueue +- **FIFO 队列** :LinkedBlockingQueue、ArrayBlockingQueue(固定长度) +- **优先级队列** :PriorityBlockingQueue 提供了阻塞的 take() 和 put() 方法:如果队列为空 take() 将阻塞,直到队列中有内容;如果队列为满 put() 将阻塞,直到队列有空闲位置。 -**使用 BlockingQueue 实现生产者消费者问题** +**使用 BlockingQueue 实现生产者消费者问题** ```java public class ProducerConsumer { @@ -963,7 +1022,7 @@ public class ForkJoinPool extends AbstractExecutorService ForkJoinPool 实现了工作窃取算法来提高 CPU 的利用率。每个线程都维护了一个双端队列,用来存储需要执行的任务。工作窃取算法允许空闲的线程从其它线程的双端队列中窃取一个任务来执行。窃取的任务必须是最晚的任务,避免和队列所属线程发生竞争。例如下图中,Thread2 从 Thread1 的队列中拿出最晚的 Task1 任务,Thread1 会拿出 Task2 来执行,这样就避免发生竞争。但是如果队列中只有一个任务时还是会发生竞争。 - +

# 九、线程不安全示例 @@ -1018,19 +1077,19 @@ Java 内存模型试图屏蔽各种硬件和操作系统的内存访问差异, 加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。 - +

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

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

- read:把一个变量的值从主内存传输到工作内存中 - load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中 @@ -1053,11 +1112,11 @@ Java 内存模型保证了 read、load、use、assign、store、write、lock 和 下图演示了两个线程同时对 cnt 进行操作,load、assign、store 这一系列操作整体上看不具备原子性,那么在 T1 修改 cnt 并且还没有将修改后的值写入主内存,T2 依然可以读入旧值。可以看出,这两个线程虽然执行了两次自增运算,但是主内存中 cnt 的值最后为 1 而不是 2。因此对 int 类型读写操作满足原子性只是说明 load、assign、store 这些单个操作具备原子性。 - +

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

使用 AtomicInteger 重写之前线程不安全的代码之后得到以下线程安全实现: @@ -1165,7 +1224,7 @@ volatile 关键字通过添加内存屏障的方式来禁止指令重排,即 在一个线程内,在程序前面的操作先行发生于后面的操作。 - +

### 2. 管程锁定规则 @@ -1173,7 +1232,7 @@ volatile 关键字通过添加内存屏障的方式来禁止指令重排,即 一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。 - +

### 3. volatile 变量规则 @@ -1181,7 +1240,7 @@ volatile 关键字通过添加内存屏障的方式来禁止指令重排,即 对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。 - +

### 4. 线程启动规则 @@ -1189,7 +1248,7 @@ volatile 关键字通过添加内存屏障的方式来禁止指令重排,即 Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。 - +

### 5. 线程加入规则 @@ -1197,7 +1256,7 @@ Thread 对象的 start() 方法调用先行发生于此线程的每一个动作 Thread 对象的结束先行发生于 join() 方法返回。 - +

### 6. 线程中断规则 @@ -1415,7 +1474,7 @@ public class ThreadLocalExample1 { 它所对应的底层结构图为: - +

每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。 @@ -1518,17 +1577,17 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态: 以下是 HotSpot 虚拟机对象头的内存布局,这些数据被称为 Mark Word。其中 tag bits 对应了五个状态,这些状态在右侧的 state 表格中给出。除了 marked for gc 状态,其它四个状态已经在前面介绍过了。 - +

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

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

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

# 十三、多线程开发良好的实践 @@ -1575,3 +1634,10 @@ JDK 1.6 引入了偏向锁和轻量级锁,从而让锁拥有了四个状态: - [JAVA FORK JOIN EXAMPLE](http://www.javacreed.com/java-fork-join-example/ "Java Fork Join Example") - [聊聊并发(八)——Fork/Join 框架介绍](http://ifeve.com/talk-concurrency-forkjoin/) - [Eliminating SynchronizationRelated Atomic Operations with Biased Locking and Bulk Rebiasing](http://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf) + + + + + + +
diff --git a/docs/notes/Java 虚拟机.md b/docs/notes/Java 虚拟机.md index 9e676a3f..f95fc405 100644 --- a/docs/notes/Java 虚拟机.md +++ b/docs/notes/Java 虚拟机.md @@ -1,10 +1,38 @@ -[TOC] + +* [一、运行时数据区域](#一运行时数据区域) + * [程序计数器](#程序计数器) + * [Java 虚拟机栈](#java-虚拟机栈) + * [本地方法栈](#本地方法栈) + * [堆](#堆) + * [方法区](#方法区) + * [运行时常量池](#运行时常量池) + * [直接内存](#直接内存) +* [二、垃圾收集](#二垃圾收集) + * [判断一个对象是否可被回收](#判断一个对象是否可被回收) + * [引用类型](#引用类型) + * [垃圾收集算法](#垃圾收集算法) + * [垃圾收集器](#垃圾收集器) +* [三、内存分配与回收策略](#三内存分配与回收策略) + * [Minor GC 和 Full GC](#minor-gc-和-full-gc) + * [内存分配策略](#内存分配策略) + * [Full GC 的触发条件](#full-gc-的触发条件) +* [四、类加载机制](#四类加载机制) + * [类的生命周期](#类的生命周期) + * [类加载过程](#类加载过程) + * [类初始化时机](#类初始化时机) + * [类与类加载器](#类与类加载器) + * [类加载器分类](#类加载器分类) + * [双亲委派模型](#双亲委派模型) + * [自定义类加载器实现](#自定义类加载器实现) +* [参考资料](#参考资料) + -本文大部分内容参考 **周志明《深入理解 Java 虚拟机》** ,想要深入学习的话请看原书。 + +本文大部分内容参考 **周志明《深入理解 Java 虚拟机》** ,想要深入学习的话请看原书。 # 一、运行时数据区域 - +

## 程序计数器 @@ -14,7 +42,7 @@ 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 - +

可以通过 -Xss 这个虚拟机参数来指定每个线程的 Java 虚拟机栈内存大小,在 JDK 1.4 中默认为 256K,而在 JDK 1.5+ 默认为 1M: @@ -33,7 +61,7 @@ java -Xss2M HackTheJava 本地方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。 - +

## 堆 @@ -118,7 +146,7 @@ Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般 - 方法区中类静态属性引用的对象 - 方法区中的常量引用的对象 - +

### 3. 方法区的回收 @@ -199,7 +227,7 @@ obj = null; ### 1. 标记 - 清除 - +

在标记阶段,程序会检查每个对象是否为活动对象,如果是活动对象,则程序会在对象头部打上标记。 @@ -214,7 +242,7 @@ obj = null; ### 2. 标记 - 整理 - +

让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 @@ -228,7 +256,7 @@ obj = null; ### 3. 复制 - +

将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。 @@ -249,7 +277,7 @@ HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内 ## 垃圾收集器 - +

以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。 @@ -258,7 +286,7 @@ HotSpot 虚拟机的 Eden 和 Survivor 大小比例默认为 8:1,保证了内 ### 1. Serial 收集器 - +

Serial 翻译为串行,也就是说它以串行的方式执行。 @@ -270,7 +298,7 @@ Serial 翻译为串行,也就是说它以串行的方式执行。 ### 2. ParNew 收集器 - +

它是 Serial 收集器的多线程版本。 @@ -290,7 +318,7 @@ Serial 翻译为串行,也就是说它以串行的方式执行。 ### 4. Serial Old 收集器 - +

是 Serial 收集器的老年代版本,也是给 Client 场景下的虚拟机使用。如果用在 Server 场景下,它有两大用途: @@ -299,7 +327,7 @@ Serial 翻译为串行,也就是说它以串行的方式执行。 ### 5. Parallel Old 收集器 - +

是 Parallel Scavenge 收集器的老年代版本。 @@ -307,7 +335,7 @@ Serial 翻译为串行,也就是说它以串行的方式执行。 ### 6. CMS 收集器 - +

CMS(Concurrent Mark Sweep),Mark Sweep 指的是标记 - 清除算法。 @@ -332,17 +360,17 @@ G1(Garbage-First),它是一款面向服务端应用的垃圾收集器, 堆被分为新生代和老年代,其它收集器进行收集的范围都是整个新生代或者老年代,而 G1 可以直接对新生代和老年代一起回收。 - +

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

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

如果不计算维护 Remembered Set 的操作,G1 收集器的运作大致可划分为以下几个步骤: @@ -430,15 +458,15 @@ G1 把堆划分成多个大小相等的独立区域(Region),新生代和 ## 类的生命周期 - +

包括以下 7 个阶段: -- **加载(Loading)** -- **验证(Verification)** -- **准备(Preparation)** -- **解析(Resolution)** -- **初始化(Initialization)** +- **加载(Loading)** +- **验证(Verification)** +- **准备(Preparation)** +- **解析(Resolution)** +- **初始化(Initialization)** - 使用(Using) - 卸载(Unloading) @@ -600,7 +628,7 @@ System.out.println(ConstClass.HELLOWORLD); 下图展示了类加载器之间的层次关系,称为双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其它的类加载器都要有自己的父类加载器。这里的父子关系一般通过组合关系(Composition)来实现,而不是继承关系(Inheritance)。 - +

### 1. 工作过程 @@ -725,3 +753,10 @@ public class FileSystemClassLoader extends ClassLoader { - [深入探讨 Java 类加载器](https://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html#code6) - [Guide to WeakHashMap in Java](http://www.baeldung.com/java-weakhashmap) - [Tomcat example source code file (ConcurrentCache.java)](https://alvinalexander.com/java/jwarehouse/apache-tomcat-6.0.16/java/org/apache/el/util/ConcurrentCache.java.shtml) + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 二分查找.md b/docs/notes/Leetcode 题解 - 二分查找.md index 4e6ef976..ff9624e7 100644 --- a/docs/notes/Leetcode 题解 - 二分查找.md +++ b/docs/notes/Leetcode 题解 - 二分查找.md @@ -1,4 +1,12 @@ -[TOC] + +* [1. 求开方](#1-求开方) +* [2. 大于给定元素的最小元素](#2-大于给定元素的最小元素) +* [3. 有序数组的 Single Element](#3-有序数组的-single-element) +* [4. 第一个错误的版本](#4-第一个错误的版本) +* [5. 旋转数组的最小数字](#5-旋转数组的最小数字) +* [6. 查找区间](#6-查找区间) + + 正常实现** @@ -25,11 +33,11 @@ public int binarySearch(int[] nums, int key) { } ``` -**时间复杂度** +**时间复杂度** 二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。 -**m 计算** +**m 计算** 有两种计算中值 m 的方式: @@ -38,14 +46,14 @@ public int binarySearch(int[] nums, int key) { l + h 可能出现加法溢出,也就是说加法的结果大于整型能够表示的范围。但是 l 和 h 都为正数,因此 h - l 不会出现加法溢出问题。所以,最好使用第二种计算法方法。 -**未成功查找的返回值** +**未成功查找的返回值** 循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值: - -1:以一个错误码表示没有查找到 key - l:将 key 插入到 nums 中的正确位置 -**变种** +**变种** 二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下: @@ -293,3 +301,10 @@ private int binarySearch(int[] nums, int target) { } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 位运算.md b/docs/notes/Leetcode 题解 - 位运算.md index d6ca2040..d89047bc 100644 --- a/docs/notes/Leetcode 题解 - 位运算.md +++ b/docs/notes/Leetcode 题解 - 位运算.md @@ -1,6 +1,21 @@ -[TOC] + +* [1. 统计两个数的二进制表示有多少位不同](#1-统计两个数的二进制表示有多少位不同) +* [2. 数组中唯一一个不重复的元素](#2-数组中唯一一个不重复的元素) +* [3. 找出数组中缺失的那个数](#3-找出数组中缺失的那个数) +* [4. 数组中不重复的两个元素](#4-数组中不重复的两个元素) +* [5. 翻转一个数的比特位](#5-翻转一个数的比特位) +* [6. 不用额外变量交换两个整数](#6-不用额外变量交换两个整数) +* [7. 判断一个数是不是 2 的 n 次方](#7-判断一个数是不是-2-的-n-次方) +* [8. 判断一个数是不是 4 的 n 次方](#8--判断一个数是不是-4-的-n-次方) +* [9. 判断一个数的位级表示是否不会出现连续的 0 和 1](#9-判断一个数的位级表示是否不会出现连续的-0-和-1) +* [10. 求一个数的补码](#10-求一个数的补码) +* [11. 实现整数的加法](#11-实现整数的加法) +* [12. 字符串数组最大乘积](#12-字符串数组最大乘积) +* [13. 统计从 0 \~ n 每个数的二进制表示中 1 的个数](#13-统计从-0-\~-n-每个数的二进制表示中-1-的个数) + -**基本原理** + +**基本原理** 0s 表示一串 0,1s 表示一串 1。 @@ -26,7 +41,7 @@ x ^ x = 0 x & x = x x | x = x - \>\>\> n 为无符号右移,左边会补上 0。 - << n 为算术左移,相当于乘以 2n。 -** mask 计算** +** mask 计算** 要获取 111111111,将 0 取反即可,\~0。 @@ -36,7 +51,7 @@ x ^ x = 0 x & x = x x | x = x 要得到 1 到 i 位为 0 的 mask,只需将 1 到 i 位为 1 的 mask 取反,即 \~((1<<i)-1)。 -**Java 中的位操作** +**Java 中的位操作** ```html static int Integer.bitCount(); // 统计 1 的数量 @@ -425,3 +440,10 @@ public int[] countBits(int num) { } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 分治.md b/docs/notes/Leetcode 题解 - 分治.md index 8bb7abee..aa715127 100644 --- a/docs/notes/Leetcode 题解 - 分治.md +++ b/docs/notes/Leetcode 题解 - 分治.md @@ -1,4 +1,8 @@ -[TOC] + +* [1. 给表达式加括号](#1-给表达式加括号) +* [2. 不同的二叉搜索树](#2-不同的二叉搜索树) + + # 1. 给表达式加括号 @@ -104,3 +108,10 @@ private List generateSubtrees(int s, int e) { return res; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 动态规划.md b/docs/notes/Leetcode 题解 - 动态规划.md index e828f05f..46a38c4d 100644 --- a/docs/notes/Leetcode 题解 - 动态规划.md +++ b/docs/notes/Leetcode 题解 - 动态规划.md @@ -1,4 +1,44 @@ -[TOC] + +* [斐波那契数列](#斐波那契数列) + * [1. 爬楼梯](#1-爬楼梯) + * [2. 强盗抢劫](#2-强盗抢劫) + * [3. 强盗在环形街区抢劫](#3-强盗在环形街区抢劫) + * [4. 信件错排](#4-信件错排) + * [5. 母牛生产](#5-母牛生产) +* [矩阵路径](#矩阵路径) + * [1. 矩阵的最小路径和](#1-矩阵的最小路径和) + * [2. 矩阵的总路径数](#2-矩阵的总路径数) +* [数组区间](#数组区间) + * [1. 数组区间和](#1-数组区间和) + * [2. 数组中等差递增子区间的个数](#2-数组中等差递增子区间的个数) +* [分割整数](#分割整数) + * [1. 分割整数的最大乘积](#1-分割整数的最大乘积) + * [2. 按平方数来分割整数](#2-按平方数来分割整数) + * [3. 分割整数构成字母字符串](#3-分割整数构成字母字符串) +* [最长递增子序列](#最长递增子序列) + * [1. 最长递增子序列](#1-最长递增子序列) + * [2. 一组整数对能够构成的最长链](#2-一组整数对能够构成的最长链) + * [3. 最长摆动子序列](#3-最长摆动子序列) +* [最长公共子序列](#最长公共子序列) +* [0-1 背包](#0-1-背包) + * [1. 划分数组为和相等的两部分](#1-划分数组为和相等的两部分) + * [2. 改变一组数的正负号使得它们的和为一给定数](#2-改变一组数的正负号使得它们的和为一给定数) + * [3. 01 字符构成最多的字符串](#3-01-字符构成最多的字符串) + * [4. 找零钱的最少硬币数](#4-找零钱的最少硬币数) + * [5. 找零钱的硬币数组合](#5-找零钱的硬币数组合) + * [6. 字符串按单词列表分割](#6-字符串按单词列表分割) + * [7. 组合总和](#7-组合总和) +* [股票交易](#股票交易) + * [1. 需要冷却期的股票交易](#1-需要冷却期的股票交易) + * [2. 需要交易费用的股票交易](#2-需要交易费用的股票交易) + * [3. 只能进行两次的股票交易](#3-只能进行两次的股票交易) + * [4. 只能进行 k 次的股票交易](#4-只能进行-k-次的股票交易) +* [字符串编辑](#字符串编辑) + * [1. 删除两个字符串的字符使它们相等](#1-删除两个字符串的字符使它们相等) + * [2. 编辑距离](#2-编辑距离) + * [3. 复制粘贴字符](#3-复制粘贴字符) + + @@ -20,7 +60,7 @@ - +

考虑到 dp[i] 只与 dp[i - 1] 和 dp[i - 2] 有关,因此可以只用两个变量来存储 dp[i - 1] 和 dp[i - 2],使得原来的 O(N) 空间复杂度优化为 O(1) 复杂度。 @@ -53,7 +93,7 @@ public int climbStairs(int n) { - +

```java public int rob(int[] nums) { @@ -109,7 +149,7 @@ private int rob(int[] nums, int first, int last) { - +

## 5. 母牛生产 @@ -121,7 +161,7 @@ private int rob(int[] nums, int first, int last) { - +

# 矩阵路径 @@ -171,7 +211,7 @@ public int minPathSum(int[][] grid) { 题目描述:统计从矩阵左上角到右下角的路径总数,每次只能向右或者向下移动。 - +

```java public int uniquePaths(int m, int n) { @@ -392,9 +432,9 @@ public int numDecodings(String s) { # 最长递增子序列 -已知一个序列 {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} 。 @@ -402,7 +442,7 @@ public int numDecodings(String s) { - +

对于一个长度为 N 的序列,最长递增子序列并不一定会以 SN 为结尾,因此 dp[N] 不是序列的最长递增子序列的长度,需要遍历 dp 数组找出最大值才是所要的结果,max{ dp[i] | 1 <= i <= N} 即为所求。 @@ -575,7 +615,7 @@ public int wiggleMaxLength(int[] nums) { - +

对于长度为 N 的序列 S1 和长度为 M 的序列 S2,dp[N][M] 就是序列 S1 和序列 S2 的最长公共子序列长度。 @@ -615,7 +655,7 @@ public int lengthOfLCS(int[] nums1, int[] nums2) { - +

```java // W 为背包总体积 @@ -638,13 +678,13 @@ 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]。此时, - +

因为 dp[j-w] 表示 dp[i-1][j-w],因此不能先求 dp[i][j-w],防止将 dp[i-1][j-w] 覆盖。也就是说要先计算 dp[i][j] 再计算 dp[i][j-w],在程序实现时需要按倒序来循环求解。 @@ -663,7 +703,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { } ``` -**无法使用贪心算法的解释** +**无法使用贪心算法的解释** 0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优。考虑下面的物品和一个容量为 5 的背包,如果先添加物品 0 再添加物品 1,那么只能存放的价值为 16,浪费了大小为 2 的空间。最优的方式是存放物品 1 和物品 2,价值为 22. @@ -673,7 +713,7 @@ public int knapsack(int W, int N, int[] weights, int[] values) { | 1 | 2 | 10 | 5 | | 2 | 3 | 12 | 4 | -**变种** +**变种** - 完全背包:物品数量为无限个 @@ -1008,7 +1048,7 @@ public int combinationSum4(int[] nums, int target) { 题目描述:交易之后需要有一天的冷却时间。 - +

```java public int maxProfit(int[] prices) { @@ -1051,7 +1091,7 @@ The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. 题目描述:每交易一次,都要支付一定的费用。 - +

```java public int maxProfit(int[] prices, int fee) { @@ -1263,3 +1303,10 @@ public int minSteps(int n) { return dp[n]; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 双指针.md b/docs/notes/Leetcode 题解 - 双指针.md index f720e21d..c97b1aa2 100644 --- a/docs/notes/Leetcode 题解 - 双指针.md +++ b/docs/notes/Leetcode 题解 - 双指针.md @@ -1,4 +1,13 @@ -[TOC] + +* [1. 有序数组的 Two Sum](#1-有序数组的-two-sum) +* [2. 两数平方和](#2-两数平方和) +* [3. 反转字符串中的元音字符](#3-反转字符串中的元音字符) +* [4. 回文字符串](#4-回文字符串) +* [5. 归并两个有序数组](#5-归并两个有序数组) +* [6. 判断链表是否存在环](#6-判断链表是否存在环) +* [7. 最长子序列](#7-最长子序列) + + 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。 @@ -23,7 +32,7 @@ Output: index1=1, index2=2 数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。 - +

```java public int[] twoSum(int[] numbers, int target) { @@ -93,7 +102,7 @@ Explanation: 1 * 1 + 2 * 2 = 5 Given s = "leetcode", return "leotcede". ``` - +

使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。 @@ -102,7 +111,7 @@ Given s = "leetcode", return "leotcede". - 时间复杂度为 O(N):只需要遍历所有元素一次 - 空间复杂度 O(1):只需要使用两个额外变量 - +

```java private final static HashSet vowels = new HashSet<>( @@ -146,7 +155,7 @@ Explanation: You could delete the character 'c'. 使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。 - +

本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。 @@ -154,7 +163,7 @@ Explanation: You could delete the character 'c'. 在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。 - +

```java public boolean validPalindrome(String s) { @@ -281,3 +290,10 @@ private boolean isSubstr(String s, String target) { return j == target.length(); } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 哈希表.md b/docs/notes/Leetcode 题解 - 哈希表.md index 465d9dc1..c9512df6 100644 --- a/docs/notes/Leetcode 题解 - 哈希表.md +++ b/docs/notes/Leetcode 题解 - 哈希表.md @@ -1,10 +1,16 @@ -[TOC] + +* [1. 数组中两个数的和为给定值](#1-数组中两个数的和为给定值) +* [2. 判断数组是否含有重复元素](#2-判断数组是否含有重复元素) +* [3. 最长和谐序列](#3-最长和谐序列) +* [4. 最长连续序列](#4-最长连续序列) + + 哈希表使用 O(N) 空间复杂度存储数据,并且以 O(1) 时间复杂度求解问题。 -- Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。 +- Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。 - Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中 [Leetcdoe : 535. Encode and Decode TinyURL (Medium) + Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中 [Leetcdoe : 535. Encode and Decode TinyURL (Medium) [Leetcode](https://leetcode.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源�) / [力扣](https://leetcode-cn.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源�) @@ -125,3 +131,10 @@ private int maxCount(Map countForNum) { return max; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 图.md b/docs/notes/Leetcode 题解 - 图.md index 3efe5c10..c284e328 100644 --- a/docs/notes/Leetcode 题解 - 图.md +++ b/docs/notes/Leetcode 题解 - 图.md @@ -1,4 +1,13 @@ -[TOC] + +* [二分图](#二分图) + * [1. 判断是否为二分图](#1-判断是否为二分图) +* [拓扑排序](#拓扑排序) + * [1. 课程安排的合法性](#1-课程安排的合法性) + * [2. 课程安排的顺序](#2-课程安排的顺序) +* [并查集](#并查集) + * [1. 冗余连接](#1-冗余连接) + + # 二分图 @@ -254,3 +263,10 @@ private class UF { } } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 字符串.md b/docs/notes/Leetcode 题解 - 字符串.md index 96e0d2bb..2fcf7fb3 100644 --- a/docs/notes/Leetcode 题解 - 字符串.md +++ b/docs/notes/Leetcode 题解 - 字符串.md @@ -1,4 +1,15 @@ -[TOC] + +* [1. 字符串循环移位包含](#1-字符串循环移位包含) +* [2. 字符串循环移位](#2-字符串循环移位) +* [3. 字符串中单词的翻转](#3-字符串中单词的翻转) +* [4. 两个字符串包含的字符是否完全相同](#4-两个字符串包含的字符是否完全相同) +* [5. 计算一组字符集合可以组成的回文字符串的最大长度](#5-计算一组字符集合可以组成的回文字符串的最大长度) +* [6. 字符串同构](#6-字符串同构) +* [7. 回文子字符串个数](#7-回文子字符串个数) +* [8. 判断一个整数是否是回文数](#8-判断一个整数是否是回文数) +* [9. 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数](#9-统计二进制字符串中连续-1-和连续-0-数量相同的子字符串个数) + + # 1. 字符串循环移位包含 @@ -224,3 +235,10 @@ public int countBinarySubstrings(String s) { return count; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 排序.md b/docs/notes/Leetcode 题解 - 排序.md index 39c2c769..3a972000 100644 --- a/docs/notes/Leetcode 题解 - 排序.md +++ b/docs/notes/Leetcode 题解 - 排序.md @@ -1,14 +1,24 @@ -[TOC] + +* [快速选择](#快速选择) +* [堆](#堆) + * [1. Kth Element](#1-kth-element) +* [桶排序](#桶排序) + * [1. 出现频率最多的 k 个元素](#1-出现频率最多的-k-个元素) + * [2. 按照字符出现次数对字符串排序](#2-按照字符出现次数对字符串排序) +* [荷兰国旗问题](#荷兰国旗问题) + * [1. 按颜色进行排序](#1-按颜色进行排序) + + # 快速选择 -用于求解 **Kth Element** 问题,也就是第 K 个元素的问题。 +用于求解 **Kth Element** 问题,也就是第 K 个元素的问题。 可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 # 堆 -用于求解 **TopK Elements** 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。 +用于求解 **TopK Elements** 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。 堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。 @@ -29,7 +39,7 @@ Output: 5 题目描述:找到倒数第 k 个的元素。 -**排序** :时间复杂度 O(NlogN),空间复杂度 O(1) +**排序** :时间复杂度 O(NlogN),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) { @@ -38,7 +48,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。 +**堆** :时间复杂度 O(NlogK),空间复杂度 O(K)。 ```java public int findKthLargest(int[] nums, int k) { @@ -52,7 +62,7 @@ public int findKthLargest(int[] nums, int k) { } ``` -**快速选择** :时间复杂度 O(N),空间复杂度 O(1) +**快速选择** :时间复杂度 O(N),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) { @@ -190,7 +200,7 @@ public String frequencySort(String s) { 有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 -![](pics/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png) +

## 1. 按颜色进行排序 @@ -226,3 +236,10 @@ private void swap(int[] nums, int i, int j) { nums[j] = t; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 搜索.md b/docs/notes/Leetcode 题解 - 搜索.md index 3b6874ff..3c220c38 100644 --- a/docs/notes/Leetcode 题解 - 搜索.md +++ b/docs/notes/Leetcode 题解 - 搜索.md @@ -1,10 +1,38 @@ -[TOC] + +* [BFS](#bfs) + * [1. 计算在网格中从原点到特定点的最短路径长度](#1-计算在网格中从原点到特定点的最短路径长度) + * [2. 组成整数的最小平方数数量](#2-组成整数的最小平方数数量) + * [3. 最短单词路径](#3-最短单词路径) +* [DFS](#dfs) + * [1. 查找最大的连通面积](#1-查找最大的连通面积) + * [2. 矩阵中的连通分量数目](#2-矩阵中的连通分量数目) + * [3. 好友关系的连通分量数目](#3-好友关系的连通分量数目) + * [4. 填充封闭区域](#4-填充封闭区域) + * [5. 能到达的太平洋和大西洋的区域](#5-能到达的太平洋和大西洋的区域) +* [Backtracking](#backtracking) + * [1. 数字键盘组合](#1-数字键盘组合) + * [2. IP 地址划分](#2-ip-地址划分) + * [3. 在矩阵中寻找字符串](#3-在矩阵中寻找字符串) + * [4. 输出二叉树中所有从根到叶子的路径](#4-输出二叉树中所有从根到叶子的路径) + * [5. 排列](#5-排列) + * [6. 含有相同元素求排列](#6-含有相同元素求排列) + * [7. 组合](#7-组合) + * [8. 组合求和](#8-组合求和) + * [9. 含有相同元素的组合求和](#9-含有相同元素的组合求和) + * [10. 1-9 数字的组合求和](#10-1-9-数字的组合求和) + * [11. 子集](#11-子集) + * [12. 含有相同元素求子集](#12-含有相同元素求子集) + * [13. 分割字符串使得每个部分都是回文数](#13-分割字符串使得每个部分都是回文数) + * [14. 数独](#14-数独) + * [15. N 皇后](#15-n-皇后) + + 深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。 # BFS -![](pics/95903878-725b-4ed9-bded-bc4aae0792a9.jpg) +

广度优先搜索一层一层地进行遍历,每层遍历都以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点。需要注意的是,遍历过的节点不能再次被遍历。 @@ -24,7 +52,7 @@ - 4 -> {} - 3 -> {} -每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。 +每一层遍历的节点都与根节点距离相同。设 di 表示第 i 个节点与根节点的距离,推导出一个结论:对于先遍历的节点 i 与后遍历的节点 j,有 di <= dj。利用这个结论,可以求解最短路径等 **最优解** 问题:第一次遍历到目的节点,其所经过的路径为最短路径。应该注意的是,使用 BFS 只能求解无权图的最短路径,无权图是指从一个节点到另一个节点的代价都记为 1。 在程序实现 BFS 时需要考虑以下问题: @@ -241,13 +269,13 @@ private int getShortestPath(List[] graphic, int start, int end) { # DFS -![](pics/74dc31eb-6baa-47ea-ab1c-d27a0ca35093.png) +

广度优先搜索一层一层遍历,每一层得到的所有新节点,要用队列存储起来以备下一层遍历的时候再遍历。 而深度优先搜索在得到一个新节点时立即对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。 -从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 +从一个节点出发,使用 DFS 对一个图进行遍历时,能够遍历到的节点都是从初始节点可达的,DFS 常用来求解这种 **可达性** 问题。 在程序实现 DFS 时需要考虑以下问题: @@ -549,8 +577,8 @@ private void dfs(int r, int c, boolean[][] canReach) { Backtracking(回溯)属于 DFS。 -- 普通 DFS 主要用在 **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。 -- 而 Backtracking 主要用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。 +- 普通 DFS 主要用在 **可达性问题** ,这种问题只需要执行到特点的位置然后返回即可。 +- 而 Backtracking 主要用于求解 **排列组合** 问题,例如有 { 'a','b','c' } 三个字符,求解所有由这三个字符排列得到的字符串,这种问题在执行到特定的位置返回之后还会继续执行求解过程。 因为 Backtracking 不是立即返回,而要继续求解,因此在程序实现时,需要注意对元素的标记问题: @@ -563,7 +591,7 @@ Backtracking(回溯)属于 DFS。 [Leetcode](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/) / [力扣](https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/description/) -![](pics/9823768c-212b-4b1a-b69a-b3f59e07b977.jpg) +

```html Input:Digit string "23" @@ -1166,7 +1194,7 @@ private boolean isPalindrome(String s, int begin, int end) { [Leetcode](https://leetcode.com/problems/sudoku-solver/description/) / [力扣](https://leetcode-cn.com/problems/sudoku-solver/description/) -![](pics/0e8fdc96-83c1-4798-9abe-45fc91d70b9d.png) +

```java private boolean[][] rowsUsed = new boolean[9][10]; @@ -1225,7 +1253,7 @@ private int cubeNum(int i, int j) { [Leetcode](https://leetcode.com/problems/n-queens/description/) / [力扣](https://leetcode-cn.com/problems/n-queens/description/) -![](pics/067b310c-6877-40fe-9dcf-10654e737485.jpg) +

在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,求所有的 n 皇后的解。 @@ -1233,12 +1261,12 @@ private int cubeNum(int i, int j) { 45 度对角线标记数组的长度为 2 \* n - 1,通过下图可以明确 (r, c) 的位置所在的数组下标为 r + c。 - +

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

```java private List> solutions; @@ -1286,3 +1314,10 @@ private void backtracking(int row) { } } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 数学.md b/docs/notes/Leetcode 题解 - 数学.md index 38acbbb6..7845a636 100644 --- a/docs/notes/Leetcode 题解 - 数学.md +++ b/docs/notes/Leetcode 题解 - 数学.md @@ -1,4 +1,30 @@ -[TOC] + +* [素数分解](#素数分解) +* [整除](#整除) +* [最大公约数最小公倍数](#最大公约数最小公倍数) + * [1. 生成素数序列](#1-生成素数序列) + * [2. 最大公约数](#2-最大公约数) + * [3. 使用位操作和减法求解最大公约数](#3-使用位操作和减法求解最大公约数) +* [进制转换](#进制转换) + * [1. 7 进制](#1-7-进制) + * [2. 16 进制](#2-16-进制) + * [3. 26 进制](#3-26-进制) +* [阶乘](#阶乘) + * [1. 统计阶乘尾部有多少个 0](#1-统计阶乘尾部有多少个-0) +* [字符串加法减法](#字符串加法减法) + * [1. 二进制加法](#1-二进制加法) + * [2. 字符串加法](#2-字符串加法) +* [相遇问题](#相遇问题) + * [1. 改变数组元素使所有的数组元素都相等](#1-改变数组元素使所有的数组元素都相等) +* [多数投票问题](#多数投票问题) + * [1. 数组中出现次数多于 n / 2 的元素](#1-数组中出现次数多于-n--2-的元素) +* [其它](#其它) + * [1. 平方数](#1-平方数) + * [2. 3 的 n 次方](#2-3-的-n-次方) + * [3. 乘积数组](#3-乘积数组) + * [4. 找出数组中的乘积最大的三个数](#4-找出数组中的乘积最大的三个数) + + # 素数分解 @@ -295,7 +321,7 @@ Only two moves are needed (remember each move increments or decrements one eleme 设数组长度为 N,则可以找到 N/2 对 a 和 b 的组合,使它们都移动到 m 的位置。 -**解法 1** +**解法 1** 先排序,时间复杂度:O(NlogN) @@ -313,7 +339,7 @@ public int minMoves2(int[] nums) { } ``` -**解法 2** +**解法 2** 使用快速选择找到中位数,时间复杂度 O(N) @@ -505,3 +531,10 @@ public int maximumProduct(int[] nums) { return Math.max(max1*max2*max3, max1*min1*min2); } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 数组与矩阵.md b/docs/notes/Leetcode 题解 - 数组与矩阵.md index fb0f8a42..2e6270ea 100644 --- a/docs/notes/Leetcode 题解 - 数组与矩阵.md +++ b/docs/notes/Leetcode 题解 - 数组与矩阵.md @@ -1,4 +1,18 @@ -[TOC] + +* [1. 把数组中的 0 移到末尾](#1-把数组中的-0-移到末尾) +* [2. 改变矩阵维度](#2-改变矩阵维度) +* [3. 找出数组中最长的连续 1](#3-找出数组中最长的连续-1) +* [4. 有序矩阵查找](#4-有序矩阵查找) +* [5. 有序矩阵的 Kth Element](#5-有序矩阵的-kth-element) +* [6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数](#6-一个数组元素在-[1,-n]-之间,其中一个数被替换为另一个数,找出重复的数和丢失的数) +* [7. 找出数组中重复的数,数组值在 [1, n] 之间](#7-找出数组中重复的数,数组值在-[1,-n]-之间) +* [8. 数组相邻差值的个数](#8-数组相邻差值的个数) +* [9. 数组的度](#9-数组的度) +* [10. 对角元素相等的矩阵](#10-对角元素相等的矩阵) +* [11. 嵌套数组](#11-嵌套数组) +* [12. 分隔数组](#12-分隔数组) + + # 1. 把数组中的 0 移到末尾 @@ -447,3 +461,10 @@ public int maxChunksToSorted(int[] arr) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 栈和队列.md b/docs/notes/Leetcode 题解 - 栈和队列.md index 02eae2da..5494bbbb 100644 --- a/docs/notes/Leetcode 题解 - 栈和队列.md +++ b/docs/notes/Leetcode 题解 - 栈和队列.md @@ -1,4 +1,12 @@ -[TOC] + +* [1. 用栈实现队列](#1-用栈实现队列) +* [2. 用队列实现栈](#2-用队列实现栈) +* [3. 最小值栈](#3-最小值栈) +* [4. 用栈实现括号匹配](#4-用栈实现括号匹配) +* [5. 数组中元素与下一个比它大的元素之间的距离](#5-数组中元素与下一个比它大的元素之间的距离) +* [6. 循环数组中比当前元素大的下一个元素](#6-循环数组中比当前元素大的下一个元素) + + # 1. 用栈实现队列 @@ -222,3 +230,10 @@ public int[] nextGreaterElements(int[] nums) { return next; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 树.md b/docs/notes/Leetcode 题解 - 树.md index d7be1fcf..da7eab1c 100644 --- a/docs/notes/Leetcode 题解 - 树.md +++ b/docs/notes/Leetcode 题解 - 树.md @@ -1,4 +1,42 @@ -[TOC] + +* [递归](#递归) + * [1. 树的高度](#1-树的高度) + * [2. 平衡树](#2-平衡树) + * [3. 两节点的最长路径](#3-两节点的最长路径) + * [4. 翻转树](#4-翻转树) + * [5. 归并两棵树](#5-归并两棵树) + * [6. 判断路径和是否等于一个数](#6-判断路径和是否等于一个数) + * [7. 统计路径和等于一个数的路径数量](#7-统计路径和等于一个数的路径数量) + * [8. 子树](#8-子树) + * [9. 树的对称](#9-树的对称) + * [10. 最小路径](#10-最小路径) + * [11. 统计左叶子节点的和](#11-统计左叶子节点的和) + * [12. 相同节点值的最大路径长度](#12-相同节点值的最大路径长度) + * [13. 间隔遍历](#13-间隔遍历) + * [14. 找出二叉树中第二小的节点](#14-找出二叉树中第二小的节点) +* [层次遍历](#层次遍历) + * [1. 一棵树每层节点的平均数](#1-一棵树每层节点的平均数) + * [2. 得到左下角的节点](#2-得到左下角的节点) +* [前中后序遍历](#前中后序遍历) + * [1. 非递归实现二叉树的前序遍历](#1-非递归实现二叉树的前序遍历) + * [2. 非递归实现二叉树的后序遍历](#2-非递归实现二叉树的后序遍历) + * [3. 非递归实现二叉树的中序遍历](#3-非递归实现二叉树的中序遍历) +* [BST](#bst) + * [1. 修剪二叉查找树](#1-修剪二叉查找树) + * [2. 寻找二叉查找树的第 k 个元素](#2-寻找二叉查找树的第-k-个元素) + * [3. 把二叉查找树每个节点的值都加上比它大的节点的值](#3-把二叉查找树每个节点的值都加上比它大的节点的值) + * [4. 二叉查找树的最近公共祖先](#4-二叉查找树的最近公共祖先) + * [5. 二叉树的最近公共祖先](#5-二叉树的最近公共祖先) + * [6. 从有序数组中构造二叉查找树](#6-从有序数组中构造二叉查找树) + * [7. 根据有序链表构造平衡的二叉查找树](#7-根据有序链表构造平衡的二叉查找树) + * [8. 在二叉查找树中寻找两个节点,使它们的和为一个给定值](#8-在二叉查找树中寻找两个节点,使它们的和为一个给定值) + * [9. 在二叉查找树中查找两个节点之差的最小绝对值](#9-在二叉查找树中查找两个节点之差的最小绝对值) + * [10. 寻找二叉查找树中出现次数最多的值](#10-寻找二叉查找树中出现次数最多的值) +* [Trie](#trie) + * [1. 实现一个 Trie](#1-实现一个-trie) + * [2. 实现一个 Trie,用来求前缀和](#2-实现一个-trie,用来求前缀和) + + # 递归 @@ -1007,7 +1045,7 @@ private void inOrder(TreeNode node, List nums) { # Trie -![](pics/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg) +

Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。 @@ -1142,3 +1180,10 @@ class MapSum { } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 目录.md b/docs/notes/Leetcode 题解 - 目录.md index e40505e8..1e38c714 100644 --- a/docs/notes/Leetcode 题解 - 目录.md +++ b/docs/notes/Leetcode 题解 - 目录.md @@ -31,3 +31,10 @@ - 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014. - 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008. - 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015. + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 目录1.md b/docs/notes/Leetcode 题解 - 目录1.md index d93b96a2..9df8bab3 100644 --- a/docs/notes/Leetcode 题解 - 目录1.md +++ b/docs/notes/Leetcode 题解 - 目录1.md @@ -31,3 +31,10 @@ - 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014. - 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008. - 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015. + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 贪心思想.md b/docs/notes/Leetcode 题解 - 贪心思想.md index 4d0e1215..3414329e 100644 --- a/docs/notes/Leetcode 题解 - 贪心思想.md +++ b/docs/notes/Leetcode 题解 - 贪心思想.md @@ -1,4 +1,17 @@ -[TOC] + +* [1. 分配饼干](#1-分配饼干) +* [2. 不重叠的区间个数](#2-不重叠的区间个数) +* [3. 投飞镖刺破气球](#3-投飞镖刺破气球) +* [4. 根据身高和序号重组队列](#4-根据身高和序号重组队列) +* [5. 买卖股票最大的收益](#5-买卖股票最大的收益) +* [6. 买卖股票的最大收益 II](#6-买卖股票的最大收益-ii) +* [7. 种植花朵](#7-种植花朵) +* [8. 判断是否为子序列](#8-判断是否为子序列) +* [9. 修改一个数成为非递减数组](#9-修改一个数成为非递减数组) +* [10. 子数组最大的和](#10-子数组最大的和) +* [11. 分隔字符串使同种字符出现在一起](#11-分隔字符串使同种字符出现在一起) + + 保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。 @@ -22,7 +35,7 @@ Output: 2 证明:假设在某次选择中,贪心策略选择给当前满足度最小的孩子分配第 m 个饼干,第 m 个饼干为可以满足该孩子的最小饼干。假设存在一种最优策略,可以给该孩子分配第 n 个饼干,并且 m < n。我们可以发现,经过这一轮分配,贪心策略分配后剩下的饼干一定有一个比最优策略来得大。因此在后续的分配中,贪心策略一定能满足更多的孩子。也就是说不存在比贪心策略更优的策略,即贪心策略就是最优策略。 - +

```java public int findContentChildren(int[] grid, int[] size) { @@ -288,7 +301,7 @@ Explanation: You could modify the first 4 to 1 to get a non-decreasing array. 题目描述:判断一个数组是否能只修改一个数就成为非递减数组。 -在出现 nums[i] < nums[i - 1] 时,需要考虑的是应该修改数组的哪个数,使得本次修改能使 i 之前的数组成为非递减数组,并且 **不影响后续的操作** 。优先考虑令 nums[i - 1] = nums[i],因为如果修改 nums[i] = nums[i - 1] 的话,那么 nums[i] 这个数会变大,就有可能比 nums[i + 1] 大,从而影响了后续操作。还有一个比较特别的情况就是 nums[i] < nums[i - 2],修改 nums[i - 1] = nums[i] 不能使数组成为非递减数组,只能修改 nums[i] = nums[i - 1]。 +在出现 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) { @@ -377,3 +390,10 @@ private int char2Index(char c) { return c - 'a'; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解 - 链表.md b/docs/notes/Leetcode 题解 - 链表.md index e47344cf..d5892146 100644 --- a/docs/notes/Leetcode 题解 - 链表.md +++ b/docs/notes/Leetcode 题解 - 链表.md @@ -1,4 +1,16 @@ -[TOC] + +* [1. 找出两个链表的交点](#1-找出两个链表的交点) +* [2. 链表反转](#2-链表反转) +* [3. 归并两个有序的链表](#3-归并两个有序的链表) +* [4. 从有序链表中删除重复节点](#4-从有序链表中删除重复节点) +* [5. 删除链表的倒数第 n 个节点](#5-删除链表的倒数第-n-个节点) +* [6. 交换链表中的相邻结点](#6-交换链表中的相邻结点) +* [7. 链表求和](#7-链表求和) +* [8. 回文链表](#8-回文链表) +* [9. 分隔链表](#9-分隔链表) +* [10. 链表元素按奇偶聚集](#10-链表元素按奇偶聚集) + + 链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。 @@ -349,3 +361,10 @@ public ListNode oddEvenList(ListNode head) { return head; } ``` + + + + + + +
diff --git a/docs/notes/Leetcode 题解.md b/docs/notes/Leetcode 题解.md index 5df5d8b7..099ae848 100644 --- a/docs/notes/Leetcode 题解.md +++ b/docs/notes/Leetcode 题解.md @@ -1 +1,8 @@ [Leetcode 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md) + + + + + + +
diff --git a/docs/notes/Leetcode-Database 题解.md b/docs/notes/Leetcode-Database 题解.md index c109eb68..898c66d1 100644 --- a/docs/notes/Leetcode-Database 题解.md +++ b/docs/notes/Leetcode-Database 题解.md @@ -1,4 +1,21 @@ -[TOC] + +* [595. Big Countries](#595-big-countries) +* [627. Swap Salary](#627-swap-salary) +* [620. Not Boring Movies](#620-not-boring-movies) +* [596. Classes More Than 5 Students](#596-classes-more-than-5-students) +* [182. Duplicate Emails](#182-duplicate-emails) +* [196. Delete Duplicate Emails](#196-delete-duplicate-emails) +* [175. Combine Two Tables](#175-combine-two-tables) +* [181. Employees Earning More Than Their Managers](#181-employees-earning-more-than-their-managers) +* [183. Customers Who Never Order](#183-customers-who-never-order) +* [184. Department Highest Salary](#184-department-highest-salary) +* [176. Second Highest Salary](#176-second-highest-salary) +* [177. Nth Highest Salary](#177-nth-highest-salary) +* [178. Rank Scores](#178-rank-scores) +* [180. Consecutive Numbers](#180-consecutive-numbers) +* [626. Exchange Seats](#626-exchange-seats) + + # 595. Big Countries @@ -986,3 +1003,10 @@ WHERE ORDER BY id; ``` + + + + + + +
diff --git a/docs/notes/Linux.md b/docs/notes/Linux.md index d36592aa..d2effd83 100644 --- a/docs/notes/Linux.md +++ b/docs/notes/Linux.md @@ -1,4 +1,71 @@ -[TOC] + +* [一、常用操作以及概念](#一常用操作以及概念) + * [快捷键](#快捷键) + * [求助](#求助) + * [关机](#关机) + * [PATH](#path) + * [sudo](#sudo) + * [包管理工具](#包管理工具) + * [发行版](#发行版) + * [VIM 三个模式](#vim-三个模式) + * [GNU](#gnu) + * [开源协议](#开源协议) +* [二、磁盘](#二磁盘) + * [磁盘接口](#磁盘接口) + * [磁盘的文件名](#磁盘的文件名) +* [三、分区](#三分区) + * [分区表](#分区表) + * [开机检测程序](#开机检测程序) +* [四、文件系统](#四文件系统) + * [分区与文件系统](#分区与文件系统) + * [组成](#组成) + * [文件读取](#文件读取) + * [磁盘碎片](#磁盘碎片) + * [block](#block) + * [inode](#inode) + * [目录](#目录) + * [日志](#日志) + * [挂载](#挂载) + * [目录配置](#目录配置) +* [五、文件](#五文件) + * [文件属性](#文件属性) + * [文件与目录的基本操作](#文件与目录的基本操作) + * [修改权限](#修改权限) + * [默认权限](#默认权限) + * [目录的权限](#目录的权限) + * [链接](#链接) + * [获取文件内容](#获取文件内容) + * [指令与文件搜索](#指令与文件搜索) +* [六、压缩与打包](#六压缩与打包) + * [压缩文件名](#压缩文件名) + * [压缩指令](#压缩指令) + * [打包](#打包) +* [七、Bash](#七bash) + * [特性](#特性) + * [变量操作](#变量操作) + * [指令搜索顺序](#指令搜索顺序) + * [数据流重定向](#数据流重定向) +* [八、管道指令](#八管道指令) + * [提取指令](#提取指令) + * [排序指令](#排序指令) + * [双向输出重定向](#双向输出重定向) + * [字符转换指令](#字符转换指令) + * [分区指令](#分区指令) +* [九、正则表达式](#九正则表达式) + * [grep](#grep) + * [printf](#printf) + * [awk](#awk) +* [十、进程管理](#十进程管理) + * [查看进程](#查看进程) + * [进程状态](#进程状态) + * [SIGCHLD](#sigchld) + * [wait()](#wait) + * [waitpid()](#waitpid) + * [孤儿进程](#孤儿进程) + * [僵尸进程](#僵尸进程) +* [参考资料](#参考资料) + + # 一、常用操作以及概念 @@ -88,7 +155,7 @@ Linux 发行版是 Linux 内核及各种应用软件的集成版本。 - 编辑模式(Insert mode):按下 "i" 等按键之后进入,可以对文本进行编辑; - 指令列模式(Bottom-line mode):按下 ":" 按键之后进入,用于保存退出等操作。 - +

在指令列模式下,有以下命令用于离开或者保存文件。 @@ -122,25 +189,25 @@ GNU 计划,译为革奴计划,它的目标是创建一套完全自由的操 IDE(ATA)全称 Advanced Technology Attachment,接口速度最大为 133MB/s,因为并口线的抗干扰性太差,且排线占用空间较大,不利电脑内部散热,已逐渐被 SATA 所取代。 - +

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

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

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

## 磁盘的文件名 @@ -175,7 +242,7 @@ GPT 没有扩展分区概念,都是主分区,每个 LBA 可以分 4 个分 MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB = 8 ZB。 - +

## 开机检测程序 @@ -183,7 +250,7 @@ MBR 不支持 2.2 TB 以上的硬盘,GPT 则最多支持到 233 TB BIOS(Basic Input/Output System,基本输入输出系统),它是一个固件(嵌入在硬件中的软件),BIOS 程序存放在断电后内容不会丢失的只读内存中。 -![](pics/50831a6f-2777-46ea-a571-29f23c85cc21.jpg) +

BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可以开机的磁盘,并读取磁盘第一个扇区的主要开机记录(MBR),由主要开机记录(MBR)执行其中的开机管理程序,这个开机管理程序会加载操作系统的核心文件。 @@ -191,7 +258,7 @@ BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可 下图中,第一扇区的主要开机记录(MBR)中的开机管理程序提供了两个选单:M1、M2,M1 指向了 Windows 操作系统,而 M2 指向其它分区的启动扇区,里面包含了另外一个开机管理程序,提供了一个指向 Linux 的选单。 - +

安装多重引导,最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉主要开机记录(MBR),而 Linux 可以选择将开机管理程序安装在主要开机记录(MBR)或者其它分区的启动扇区,并且可以设置开机管理程序的选单。 @@ -217,17 +284,17 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 - superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等; - block bitmap:记录 block 是否被使用的位图。 - +

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

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

## 磁盘碎片 @@ -264,7 +331,7 @@ inode 具有以下特点: inode 中记录了文件内容所在的 block 编号,但是每个 block 非常小,一个大文件随便都需要几十万的 block。而一个 inode 大小有限,无法直接引用这么多 block 编号。因此引入了间接、双间接、三间接引用。间接引用让 inode 记录的引用 block 块记录引用信息。 - +

## 目录 @@ -290,7 +357,7 @@ ext3/ext4 文件系统引入了日志功能,可以利用日志来修复文件 - /usr (unix software resource):所有系统默认软件都会安装到这个目录; - /var (variable):存放系统或程序运行过程中的数据文件。 - +

# 五、文件 @@ -457,7 +524,7 @@ cp [-adfilprsu] source destination ## 链接 - +

```html @@ -574,7 +641,7 @@ locate 使用 /var/lib/mlocate/ 这个数据库来进行搜索,它存储在内 example: find . -name "shadow*" ``` -**① 与时间有关的选项** +**① 与时间有关的选项** ```html -mtime n :列出在 n 天前的那一天修改过内容的文件 @@ -585,9 +652,9 @@ example: find . -name "shadow*" +4、4 和 -4 的指示的时间范围如下: - +

-**② 与文件拥有者和所属群组有关的选项** +**② 与文件拥有者和所属群组有关的选项** ```html -uid n @@ -598,7 +665,7 @@ example: find . -name "shadow*" -nogroup:搜索所属群组不存在于 /etc/group 的文件 ``` -**③ 与文件权限和名称有关的选项** +**③ 与文件权限和名称有关的选项** ```html -name filename @@ -825,7 +892,7 @@ $ export | cut -c 12- ## 排序指令 -**sort** 用于排序。 +**sort** 用于排序。 ```html $ sort [-fbMnrtuk] [file or stdin] @@ -849,7 +916,7 @@ alex:x:1001:1002::/home/alex:/bin/bash arod:x:1002:1003::/home/arod:/bin/bash ``` -**uniq** 可以将重复的数据只取一个。 +**uniq** 可以将重复的数据只取一个。 ```html $ uniq [-ic] @@ -871,7 +938,7 @@ $ last | cut -d ' ' -f 1 | sort | uniq -c ## 双向输出重定向 -输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 +输出重定向会将输出内容重定向到文件中,而 **tee** 不仅能够完成这个功能,还能保留屏幕上的输出。也就是说,使用 tee 指令,一个输出会同时传送到文件和屏幕上。 ```html $ tee [-a] file @@ -879,7 +946,7 @@ $ tee [-a] file ## 字符转换指令 -**tr** 用来删除一行中的字符,或者对字符进行替换。 +**tr** 用来删除一行中的字符,或者对字符进行替换。 ```html $ tr [-ds] SET1 ... @@ -892,21 +959,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 @@ -916,7 +983,7 @@ $ join [-ti12] file1 file2 -2 :第二个文件所用的比较字段 ``` -**paste** 直接将两行粘贴在一起。 +**paste** 直接将两行粘贴在一起。 ```html $ paste [-d] file1 file2 @@ -925,7 +992,7 @@ $ paste [-d] file1 file2 ## 分区指令 -**split** 将一个文件划分成多个文件。 +**split** 将一个文件划分成多个文件。 ```html $ split [-bl] file PREFIX @@ -1099,7 +1166,7 @@ dmtsai lines: 5 columns: 9 | T | stopped (either by a job control signal or because it is being traced)
结束,进程既可以被作业控制信号结束,也可能是正在被追踪。|
- +

## SIGCHLD @@ -1112,7 +1179,7 @@ dmtsai lines: 5 columns: 9 在子进程退出时,它的进程描述符不会立即释放,这是为了让父进程得到子进程信息,父进程通过 wait() 和 waitpid() 来获得一个已经退出的子进程的信息。 - +

## wait() @@ -1176,3 +1243,10 @@ options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 w - [File system design case studies](https://www.cs.rutgers.edu/\~pxk/416/notes/13-fs-studies.html) - [Programming Project #4](https://classes.soe.ucsc.edu/cmps111/Fall08/proj4.shtml) - [FILE SYSTEM DESIGN](http://web.cs.ucla.edu/classes/fall14/cs111/scribe/11a/index.html) + + + + + + +
diff --git a/docs/notes/MySQL.md b/docs/notes/MySQL.md index ef1c5f27..9c3fb2c7 100644 --- a/docs/notes/MySQL.md +++ b/docs/notes/MySQL.md @@ -1,4 +1,34 @@ -[TOC] + +* [一、索引](#一索引) + * [B+ Tree 原理](#b-tree-原理) + * [MySQL 索引](#mysql-索引) + * [索引优化](#索引优化) + * [索引的优点](#索引的优点) + * [索引的使用条件](#索引的使用条件) +* [二、查询性能优化](#二查询性能优化) + * [使用 Explain 进行分析](#使用-explain-进行分析) + * [优化数据访问](#优化数据访问) + * [重构查询方式](#重构查询方式) +* [三、存储引擎](#三存储引擎) + * [InnoDB](#innodb) + * [MyISAM](#myisam) + * [比较](#比较) +* [四、数据类型](#四数据类型) + * [整型](#整型) + * [浮点数](#浮点数) + * [字符串](#字符串) + * [时间和日期](#时间和日期) +* [五、切分](#五切分) + * [水平切分](#水平切分) + * [垂直切分](#垂直切分) + * [Sharding 策略](#sharding-策略) + * [Sharding 存在的问题](#sharding-存在的问题) +* [六、复制](#六复制) + * [主从复制](#主从复制) + * [读写分离](#读写分离) +* [参考资料](#参考资料) + + # 一、索引 @@ -12,7 +42,7 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 keyi 和 keyi+1,且不为 null,则该指针指向节点的所有 key 大于等于 keyi 且小于等于 keyi+1。 - +

### 2. 操作 @@ -54,11 +84,11 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。 - +

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

### 2. 哈希索引 @@ -320,7 +350,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。 - +

## 垂直切分 @@ -328,7 +358,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。 - +

## Sharding 策略 @@ -358,11 +388,11 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。 -- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志(Binary log)中。 -- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。 -- **SQL 线程** :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。 +- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志(Binary log)中。 +- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。 +- **SQL 线程** :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。 - +

## 读写分离 @@ -376,7 +406,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。 - +

# 参考资料 @@ -391,3 +421,10 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提 - [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6) - [大众点评订单系统分库分表实践](https://tech.meituan.com/dianping_order_db_sharding.html) - [B + 树](https://zh.wikipedia.org/wiki/B%2B%E6%A0%91) + + + + + + +
diff --git a/docs/notes/Redis.md b/docs/notes/Redis.md index f1dc5462..067522b1 100644 --- a/docs/notes/Redis.md +++ b/docs/notes/Redis.md @@ -1,4 +1,49 @@ -[TOC] + +* [一、概述](#一概述) +* [二、数据类型](#二数据类型) + * [STRING](#string) + * [LIST](#list) + * [SET](#set) + * [HASH](#hash) + * [ZSET](#zset) +* [三、数据结构](#三数据结构) + * [字典](#字典) + * [跳跃表](#跳跃表) +* [四、使用场景](#四使用场景) + * [计数器](#计数器) + * [缓存](#缓存) + * [查找表](#查找表) + * [消息队列](#消息队列) + * [会话缓存](#会话缓存) + * [分布式锁实现](#分布式锁实现) + * [其它](#其它) +* [五、Redis 与 Memcached](#五redis-与-memcached) + * [数据类型](#数据类型) + * [数据持久化](#数据持久化) + * [分布式](#分布式) + * [内存管理机制](#内存管理机制) +* [六、键的过期时间](#六键的过期时间) +* [七、数据淘汰策略](#七数据淘汰策略) +* [八、持久化](#八持久化) + * [RDB 持久化](#rdb-持久化) + * [AOF 持久化](#aof-持久化) +* [九、事务](#九事务) +* [十、事件](#十事件) + * [文件事件](#文件事件) + * [时间事件](#时间事件) + * [事件的调度与执行](#事件的调度与执行) +* [十一、复制](#十一复制) + * [连接过程](#连接过程) + * [主从链](#主从链) +* [十二、Sentinel](#十二sentinel) +* [十三、分片](#十三分片) +* [十四、一个简单的论坛系统分析](#十四一个简单的论坛系统分析) + * [文章信息](#文章信息) + * [点赞功能](#点赞功能) + * [对文章进行排序](#对文章进行排序) +* [参考资料](#参考资料) + + # 一、概述 @@ -22,7 +67,7 @@ Redis 支持很多特性,例如将内存中的数据持久化到硬盘中, ## STRING - +

```html > set hello world @@ -37,7 +82,7 @@ OK ## LIST - +

```html > rpush list-key item @@ -65,7 +110,7 @@ OK ## SET - +

```html > sadd set-key item @@ -99,7 +144,7 @@ OK ## HASH - +

```html > hset hash-key sub-key1 value1 @@ -130,7 +175,7 @@ OK ## ZSET - +

```html > zadd zset-key 728 member1 @@ -272,11 +317,11 @@ int dictRehash(dict *d, int n) { 跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。 - +

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

与红黑树等平衡树相比,跳跃表具有以下优点: @@ -427,7 +472,7 @@ Redis 服务器是一个事件驱动程序。 Redis 基于 Reactor 模式开发了自己的网络事件处理器,使用 I/O 多路复用程序来同时监听多个套接字,并将到达的事件传送给文件事件分派器,分派器会根据套接字产生的事件类型调用相应的事件处理器。 - +

## 时间事件 @@ -480,7 +525,7 @@ def main(): 从事件处理的角度来看,服务器运行流程如下: - +

# 十一、复制 @@ -500,7 +545,7 @@ def main(): 随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。 - +

# 十二、Sentinel @@ -535,7 +580,7 @@ Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入 Redis 没有关系型数据库中的表这一概念来将同种类型的数据存放在一起,而是使用命名空间的方式来实现这一功能。键名的前面部分存储命名空间,后面部分的内容存储 ID,通常使用 : 来进行分隔。例如下面的 HASH 的键名为 article:92617,其中 article 为命名空间,ID 为 92617。 - +

## 点赞功能 @@ -543,13 +588,13 @@ Redis 没有关系型数据库中的表这一概念来将同种类型的数据 为了节约内存,规定一篇文章发布满一周之后,就不能再对它进行投票,而文章的已投票集合也会被删除,可以为文章的已投票集合设置一个一周的过期时间就能实现这个规定。 - +

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

# 参考资料 @@ -561,3 +606,10 @@ Redis 没有关系型数据库中的表这一概念来将同种类型的数据 - [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide) - [Redis 应用场景](http://www.scienjus.com/redis-use-case/) - [Using Redis as an LRU cache](https://redis.io/topics/lru-cache) + + + + + + +
diff --git a/docs/notes/SQL.md b/docs/notes/SQL.md index 05d0f47f..42b25c50 100644 --- a/docs/notes/SQL.md +++ b/docs/notes/SQL.md @@ -1,4 +1,30 @@ -[TOC] + +* [一、基础](#一基础) +* [二、创建表](#二创建表) +* [三、修改表](#三修改表) +* [四、插入](#四插入) +* [五、更新](#五更新) +* [六、删除](#六删除) +* [七、查询](#七查询) +* [八、排序](#八排序) +* [九、过滤](#九过滤) +* [十、通配符](#十通配符) +* [十一、计算字段](#十一计算字段) +* [十二、函数](#十二函数) +* [十三、分组](#十三分组) +* [十四、子查询](#十四子查询) +* [十五、连接](#十五连接) +* [十六、组合查询](#十六组合查询) +* [十七、视图](#十七视图) +* [十八、存储过程](#十八存储过程) +* [十九、游标](#十九游标) +* [二十、触发器](#二十触发器) +* [二十一、事务管理](#二十一事务管理) +* [二十二、字符集](#二十二字符集) +* [二十三、权限管理](#二十三权限管理) +* [参考资料](#参考资料) + + # 一、基础 @@ -104,7 +130,7 @@ DELETE FROM mytable WHERE id = 1; ``` -**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 +**TRUNCATE TABLE** 可以清空表,也就是删除所有行。 ```sql TRUNCATE TABLE mytable; @@ -151,8 +177,8 @@ LIMIT 2, 3; # 八、排序 -- **ASC** :升序(默认) -- **DESC** :降序 +- **ASC** :升序(默认) +- **DESC** :降序 可以按多个列进行排序,并且为每个列指定不同的排序方式: @@ -187,21 +213,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 来进行通配符匹配。 @@ -217,14 +243,14 @@ WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本 在数据库服务器上完成数据的转换和格式化的工作往往比客户端上快得多,并且转换和格式化后的数据量更少的话可以减少网络通信量。 -计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 +计算字段通常需要使用 **AS** 来取别名,否则输出的时候字段名为计算表达式。 ```sql SELECT col1 * col2 AS alias FROM mytable; ``` -**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 +**CONCAT()** 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 **TRIM()** 可以去除首尾空格。 ```sql SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col @@ -267,7 +293,7 @@ FROM mytable; | LENGTH() | 长度 | | SOUNDEX() | 转换为语音值 | -其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 +其中, **SOUNDEX()** 可以将一个字符串转换为描述其语音表示的字母数字模式。 ```sql SELECT * @@ -491,7 +517,7 @@ orders 表: # 十六、组合查询 -使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 +使用 **UNION** 来组合两个查询,如果第一个查询返回 M 行,第二个查询返回 N 行,那么组合查询的结果一般为 M+N 行。 每个查询必须包含相同的列、表达式和聚集函数。 @@ -684,7 +710,7 @@ USE mysql; SELECT user FROM user; ``` -**创建账户** +**创建账户** 新创建的账户没有任何权限。 @@ -692,25 +718,25 @@ SELECT user FROM user; CREATE USER myuser IDENTIFIED BY 'mypassword'; ``` -**修改账户名** +**修改账户名** ```sql RENAME USER myuser TO newuser; ``` -**删除账户** +**删除账户** ```sql DROP USER myuser; ``` -**查看权限** +**查看权限** ```sql SHOW GRANTS FOR myuser; ``` -**授予权限** +**授予权限** 账户用 username@host 的形式定义,username@% 使用的是默认主机名。 @@ -718,7 +744,7 @@ SHOW GRANTS FOR myuser; GRANT SELECT, INSERT ON mydatabase.* TO myuser; ``` -**删除权限** +**删除权限** GRANT 和 REVOKE 可在几个层次上控制访问权限: @@ -732,7 +758,7 @@ GRANT 和 REVOKE 可在几个层次上控制访问权限: REVOKE SELECT, INSERT ON mydatabase.* FROM myuser; ``` -**更改密码** +**更改密码** 必须使用 Password() 函数进行加密。 @@ -743,3 +769,10 @@ SET PASSWROD FOR myuser = Password('new_password'); # 参考资料 - BenForta. SQL 必知必会 [M]. 人民邮电出版社, 2013. + + + + + + +
diff --git a/docs/notes/Socket.md b/docs/notes/Socket.md index b1393e41..d7395aff 100644 --- a/docs/notes/Socket.md +++ b/docs/notes/Socket.md @@ -1,4 +1,21 @@ -[TOC] + +* [一、I/O 模型](#一io-模型) + * [阻塞式 I/O](#阻塞式-io) + * [非阻塞式 I/O](#非阻塞式-io) + * [I/O 复用](#io-复用) + * [信号驱动 I/O](#信号驱动-io) + * [异步 I/O](#异步-io) + * [五大 I/O 模型比较](#五大-io-模型比较) +* [二、I/O 复用](#二io-复用) + * [select](#select) + * [poll](#poll) + * [比较](#比较) + * [epoll](#epoll) + * [工作模式](#工作模式) + * [应用场景](#应用场景) +* [参考资料](#参考资料) + + # 一、I/O 模型 @@ -29,7 +46,7 @@ Unix 有五种 I/O 模型: ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ``` -![](pics/1492928416812_4.png) +

## 非阻塞式 I/O @@ -37,7 +54,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * 由于 CPU 要处理更多的系统调用,因此这种模型的 CPU 利用率比较低。 -![](pics/1492929000361_5.png) +

## I/O 复用 @@ -47,7 +64,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * 如果一个 Web 服务器没有 I/O 复用,那么每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万个连接,那么就需要创建相同数量的线程。相比于多进程和多线程技术,I/O 复用不需要进程线程创建和切换的开销,系统开销更小。 -![](pics/1492929444818_6.png) +

## 信号驱动 I/O @@ -55,7 +72,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * 相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。 -![](pics/1492929553651_7.png) +

## 异步 I/O @@ -63,7 +80,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * 异步 I/O 与信号驱动 I/O 的区别在于,异步 I/O 的信号是通知应用进程 I/O 完成,而信号驱动 I/O 的信号是通知应用进程可以开始 I/O。 -![](pics/1492930243286_8.png) +

## 五大 I/O 模型比较 @@ -74,7 +91,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * 非阻塞式 I/O 、信号驱动 I/O 和异步 I/O 在第一阶段不会阻塞。 -![](pics/1492928105791_3.png) +

# 二、I/O 复用 @@ -316,3 +333,10 @@ poll 没有最大描述符数量的限制,如果平台支持并且对实时性 - [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html) - [select / poll / epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/) - [Browse the source code of userspace/glibc/sysdeps/unix/sysv/linux/ online](https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/) + + + + + + +
diff --git a/docs/notes/代码可读性.md b/docs/notes/代码可读性.md index 562f2f96..f973f6bc 100644 --- a/docs/notes/代码可读性.md +++ b/docs/notes/代码可读性.md @@ -1,4 +1,20 @@ -[TOC] + +* [一、可读性的重要性](#一可读性的重要性) +* [二、用名字表达代码含义](#二用名字表达代码含义) +* [三、名字不能带来歧义](#三名字不能带来歧义) +* [四、良好的代码风格](#四良好的代码风格) +* [五、为何编写注释](#五为何编写注释) +* [六、如何编写注释](#六如何编写注释) +* [七、提高控制流的可读性](#七提高控制流的可读性) +* [八、拆分长表达式](#八拆分长表达式) +* [九、变量与可读性](#九变量与可读性) +* [十、抽取函数](#十抽取函数) +* [十一、一次只做一件事](#十一一次只做一件事) +* [十二、用自然语言表述代码](#十二用自然语言表述代码) +* [十三、减少代码量](#十三减少代码量) +* [参考资料](#参考资料) + + # 一、可读性的重要性 @@ -32,11 +48,11 @@ - 用 min、max 表示数量范围; - 用 first、last 表示访问空间的包含范围; - +

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

# 四、良好的代码风格 @@ -144,7 +160,7 @@ if (!(a || b)) { # 九、变量与可读性 -**去除控制流变量** 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 +**去除控制流变量** 。在循环中通过使用 break 或者 return 可以减少控制流变量的使用。 ```java boolean done = false; @@ -166,7 +182,7 @@ while(/* condition */) { } ``` -**减小变量作用域** 。作用域越小,越容易定位到变量所有使用的地方。 +**减小变量作用域** 。作用域越小,越容易定位到变量所有使用的地方。 JavaScript 可以用闭包减小作用域。以下代码中 submit_form 是函数变量,submitted 变量控制函数不会被提交两次。第一个实现中 submitted 是全局变量,第二个实现把 submitted 放到匿名函数中,从而限制了起作用域范围。 @@ -196,7 +212,7 @@ JavaScript 中没有用 var 声明的变量都是全局变量,而全局变量 变量定义的位置应当离它使用的位置最近。 -**实例解析** +**实例解析** 在一个网页中有以下文本输入字段: @@ -315,3 +331,10 @@ public int findClostElement(int[] arr) { # 参考资料 - Dustin, Boswell, Trevor, 等. 编写可读代码的艺术 [M]. 机械工业出版社, 2012. + + + + + + +
diff --git a/docs/notes/代码风格规范.md b/docs/notes/代码风格规范.md index cc266519..eb9f7d44 100644 --- a/docs/notes/代码风格规范.md +++ b/docs/notes/代码风格规范.md @@ -1,3 +1,10 @@ - [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) - [Google Java Style Guide](http://google.github.io/styleguide/javaguide.html) - [阿里巴巴Java开发手册](https://github.com/alibaba/p3c) + + + + + + +
diff --git a/docs/notes/分布式.md b/docs/notes/分布式.md index df00f6ab..70e2d115 100644 --- a/docs/notes/分布式.md +++ b/docs/notes/分布式.md @@ -1,4 +1,31 @@ -[TOC] + +* [一、分布式锁](#一分布式锁) + * [数据库的唯一索引](#数据库的唯一索引) + * [Redis 的 SETNX 指令](#redis-的-setnx-指令) + * [Redis 的 RedLock 算法](#redis-的-redlock-算法) + * [Zookeeper 的有序节点](#zookeeper-的有序节点) +* [二、分布式事务](#二分布式事务) + * [2PC](#2pc) + * [本地消息表](#本地消息表) +* [三、CAP](#三cap) + * [一致性](#一致性) + * [可用性](#可用性) + * [分区容忍性](#分区容忍性) + * [权衡](#权衡) +* [四、BASE](#四base) + * [基本可用](#基本可用) + * [软状态](#软状态) + * [最终一致性](#最终一致性) +* [五、Paxos](#五paxos) + * [执行过程](#执行过程) + * [约束条件](#约束条件) +* [六、Raft](#六raft) + * [单个 Candidate 的竞选](#单个-candidate-的竞选) + * [多个 Candidate 竞选](#多个-candidate-竞选) + * [数据同步](#数据同步) +* [参考](#参考) + + # 一、分布式锁 @@ -43,7 +70,7 @@ EXPIRE 指令可以为一个键值对设置一个过期时间,从而避免了 Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节点为 /app1。 - +

### 2. 节点类型 @@ -86,7 +113,7 @@ Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节 协调者询问参与者事务是否执行成功,参与者发回事务执行结果。 - +

#### 1.2 提交阶段 @@ -94,7 +121,7 @@ Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节 需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。 - +

### 2. 存在的问题 @@ -122,14 +149,14 @@ Zookeeper 提供了一种树形结构的命名空间,/app1/p_1 节点的父节 2. 之后将本地消息表中的消息转发到消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。 3. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作。 - +

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

## 一致性 @@ -193,7 +220,7 @@ ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE - 接受者(Acceptor):对每个提议进行投票; - 告知者(Learner):被告知投票的结果,不参与投票过程。 -![](pics/b988877c-0f0a-4593-916d-de2081320628.jpg) +

## 执行过程 @@ -203,19 +230,19 @@ ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE 下图演示了两个 Proposer 和三个 Acceptor 的系统中运行该算法的初始过程,每个 Proposer 都会向所有 Acceptor 发送 Prepare 请求。 -![](pics/1a9977e4-2f5c-49a6-aec9-f3027c9f46a7.png) +

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

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

### 2. Accept 阶段 @@ -225,13 +252,13 @@ Proposer A 接收到两个 Prepare 响应之后,就发送 [n=2, v=8] Accept Proposer B 过后也收到了两个 Prepare 响应,因此也开始发送 Accept 请求。需要注意的是,Accept 请求的 v 需要取它收到的最大提议编号对应的 v 值,也就是 8。因此它发送 [n=4, v=8] 的 Accept 请求。 -![](pics/9b838aee-0996-44a5-9b0f-3d1e3e2f5100.png) +

### 3. Learn 阶段 Acceptor 接收到 Accept 请求时,如果序号大于等于该 Acceptor 承诺的最小序号,那么就发送 Learn 提议给所有的 Learner。当 Learner 发现有大多数的 Acceptor 接收了某个提议,那么该提议的提议值就被 Paxos 选择出来。 -![](pics/bf667594-bb4b-4634-bf9b-0596a45415ba.jpg) +

## 约束条件 @@ -257,47 +284,47 @@ Raft 也是分布式一致性协议,主要是用来竞选主节点。 - 下图展示一个分布式系统的最初阶段,此时只有 Follower 没有 Leader。Node A 等待一个随机的竞选超时时间之后,没收到 Leader 发来的心跳包,因此进入竞选阶段。 -![](pics/111521118015898.gif) +

- 此时 Node A 发送投票请求给其它所有节点。 -![](pics/111521118445538.gif) +

- 其它节点会对请求进行回复,如果超过一半的节点回复了,那么该 Candidate 就会变成 Leader。 -![](pics/111521118483039.gif) +

- 之后 Leader 会周期性地发送心跳包给 Follower,Follower 接收到心跳包,会重新开始计时。 -![](pics/111521118640738.gif) +

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

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

## 数据同步 - 来自客户端的修改都会被传入 Leader。注意该修改还未被提交,只是写入日志中。 -![](pics/71550414107576.gif) +

- Leader 会把修改复制到所有 Follower。 -![](pics/91550414131331.gif) +

- Leader 会等待大多数的 Follower 也进行了修改,然后才将修改提交。 -![](pics/101550414151983.gif) +

- 此时 Leader 会通知的所有 Follower 让它们也提交修改,此时所有节点的值达成一致。 -![](pics/111550414182638.gif) +

# 参考 @@ -313,3 +340,10 @@ Raft 也是分布式一致性协议,主要是用来竞选主节点。 - [NEAT ALGORITHMS - PAXOS](http://harry.me/blog/2014/12/27/neat-algorithms-paxos/) - [Paxos By Example](https://angus.nyc/2012/paxos-by-example/) + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 10~19.md b/docs/notes/剑指 Offer 题解 - 10~19.md index 668b6638..28dab1f6 100644 --- a/docs/notes/剑指 Offer 题解 - 10~19.md +++ b/docs/notes/剑指 Offer 题解 - 10~19.md @@ -1,4 +1,20 @@ -[TOC] + +* [10.1 斐波那契数列](#101-斐波那契数列) +* [10.2 矩形覆盖](#102-矩形覆盖) +* [10.3 跳台阶](#103-跳台阶) +* [10.4 变态跳台阶](#104-变态跳台阶) +* [11. 旋转数组的最小数字](#11-旋转数组的最小数字) +* [12. 矩阵中的路径](#12-矩阵中的路径) +* [13. 机器人的运动范围](#13-机器人的运动范围) +* [14. 剪绳子](#14-剪绳子) +* [15. 二进制中 1 的个数](#15-二进制中-1-的个数) +* [16. 数值的整数次方](#16-数值的整数次方) +* [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数) +* [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点) +* [18.2 删除链表中重复的结点](#182-删除链表中重复的结点) +* [19. 正则表达式匹配](#19-正则表达式匹配) + + # 10.1 斐波那契数列 @@ -10,13 +26,13 @@ - +

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

递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。 @@ -76,23 +92,23 @@ public class Solution { 我们可以用 2\*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2\*1 的小矩形无重叠地覆盖一个 2\*n 的大矩形,总共有多少种方法? - +

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

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

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

```java public int RectCover(int n) { @@ -117,21 +133,21 @@ public int RectCover(int n) { 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - +

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

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

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

```java public int JumpFloor(int n) { @@ -156,7 +172,7 @@ public int JumpFloor(int n) { 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 - +

## 解题思路 @@ -216,13 +232,13 @@ public int JumpFloorII(int target) { 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 - +

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

此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组,哪一个是非递减数组。我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素。 @@ -284,13 +300,13 @@ private int minNumber(int[] nums, int l, int h) { 例如下面的矩阵包含了一条 bfce 路径。 - +

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

本题的输入是数组而不是矩阵(二维数组),因此需要先将数组转换成矩阵。 @@ -506,7 +522,7 @@ public int NumberOf1(int n) { - +

因为 (x\*x)n/2 可以通过递归求解,并且每次递归 n 都减小一半,因此整个算法的时间复杂度为 O(logN)。 @@ -576,11 +592,11 @@ private void printNumber(char[] number) { ① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。 - +

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

综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N \~ 2,因此该算法的平均时间复杂度为 O(1)。 @@ -614,7 +630,7 @@ public ListNode deleteNode(ListNode head, ListNode tobeDelete) { ## 题目描述 - +

## 解题描述 @@ -674,3 +690,10 @@ public boolean match(char[] str, char[] pattern) { return dp[m][n]; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 20~29.md b/docs/notes/剑指 Offer 题解 - 20~29.md index 66a5953e..7f211b68 100644 --- a/docs/notes/剑指 Offer 题解 - 20~29.md +++ b/docs/notes/剑指 Offer 题解 - 20~29.md @@ -1,4 +1,16 @@ -[TOC] + +* [20. 表示数值的字符串](#20-表示数值的字符串) +* [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面) +* [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点) +* [23. 链表中环的入口结点](#23-链表中环的入口结点) +* [24. 反转链表](#24-反转链表) +* [25. 合并两个排序的链表](#25-合并两个排序的链表) +* [26. 树的子结构](#26-树的子结构) +* [27. 二叉树的镜像](#27-二叉树的镜像) +* [28 对称的二叉树](#28-对称的二叉树) +* [29. 顺时针打印矩阵](#29-顺时针打印矩阵) + + # 20. 表示数值的字符串 @@ -58,7 +70,7 @@ public boolean isNumeric(char[] str) { 需要保证奇数和奇数,偶数和偶数之间的相对位置不变,这和书本不太一样。 - +

## 解题思路 @@ -119,7 +131,7 @@ private void swap(int[] nums, int i, int j) { 设链表的长度为 N。设置两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。 - +

```java public ListNode FindKthToTail(ListNode head, int k) { @@ -153,7 +165,7 @@ public ListNode FindKthToTail(ListNode head, int k) { 在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。 - +

```java public ListNode EntryNodeOfLoop(ListNode pHead) { @@ -216,7 +228,7 @@ public ListNode ReverseList(ListNode head) { ## 题目描述 - +

## 解题思路 @@ -268,7 +280,7 @@ public ListNode Merge(ListNode list1, ListNode list2) { ## 题目描述 - +

## 解题思路 @@ -296,7 +308,7 @@ private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) { ## 题目描述 - +

## 解题思路 @@ -322,7 +334,7 @@ private void swap(TreeNode root) { ## 题目描述 - +

## 解题思路 @@ -352,7 +364,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2) { 下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 - +

## 解题思路 @@ -376,3 +388,10 @@ public ArrayList printMatrix(int[][] matrix) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 30~39.md b/docs/notes/剑指 Offer 题解 - 30~39.md index 180bd975..75c45bf1 100644 --- a/docs/notes/剑指 Offer 题解 - 30~39.md +++ b/docs/notes/剑指 Offer 题解 - 30~39.md @@ -1,4 +1,18 @@ -[TOC] + +* [30. 包含 min 函数的栈](#30-包含-min-函数的栈) +* [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) +* [32.1 从上往下打印二叉树](#321-从上往下打印二叉树) +* [32.2 把二叉树打印成多行](#322-把二叉树打印成多行) +* [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树) +* [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列) +* [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径) +* [35. 复杂链表的复制](#35-复杂链表的复制) +* [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表) +* [37. 序列化二叉树](#37-序列化二叉树) +* [38. 字符串的排列](#38-字符串的排列) +* [39. 数组中出现次数超过一半的数字](#39-数组中出现次数超过一半的数字) + + # 30. 包含 min 函数的栈 @@ -73,7 +87,7 @@ public boolean IsPopOrder(int[] pushSequence, int[] popSequence) { 例如,以下二叉树层次遍历的结果为:1,2,3,4,5,6,7 - +

## 解题思路 @@ -181,7 +195,7 @@ public ArrayList> Print(TreeNode pRoot) { 例如,下图是后序遍历序列 1,3,2 所对应的二叉搜索树。 - +

## 解题思路 @@ -216,7 +230,7 @@ private boolean verify(int[] sequence, int first, int last) { 下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12 - +

## 解题思路 @@ -263,21 +277,21 @@ public class RandomListNode { } ``` - +

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

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

第三步,拆分。 - +

```java public RandomListNode Clone(RandomListNode pHead) { @@ -319,7 +333,7 @@ public RandomListNode Clone(RandomListNode pHead) { 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 - +

## 解题思路 @@ -454,3 +468,10 @@ public int MoreThanHalfNum_Solution(int[] nums) { return cnt > nums.length / 2 ? majority : 0; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 3~9.md b/docs/notes/剑指 Offer 题解 - 3~9.md index eb9a3eca..b236ac36 100644 --- a/docs/notes/剑指 Offer 题解 - 3~9.md +++ b/docs/notes/剑指 Offer 题解 - 3~9.md @@ -1,4 +1,13 @@ -[TOC] + +* [3. 数组中重复的数字](#3-数组中重复的数字) +* [4. 二维数组中的查找](#4-二维数组中的查找) +* [5. 替换空格](#5-替换空格) +* [6. 从尾到头打印链表](#6-从尾到头打印链表) +* [7. 重建二叉树](#7-重建二叉树) +* [8. 二叉树的下一个结点](#8-二叉树的下一个结点) +* [9. 用两个栈实现队列](#9-用两个栈实现队列) + + # 3. 数组中重复的数字 @@ -24,7 +33,7 @@ Output: 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: - +

```java @@ -78,7 +87,7 @@ Given target = 20, return false. 该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。 - +

```java public boolean Find(int target, int[][] matrix) { @@ -123,7 +132,7 @@ Output: 从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。 - +

```java public String replaceSpace(StringBuffer str) { @@ -155,7 +164,7 @@ public String replaceSpace(StringBuffer str) { 从尾到头反过来打印出每个结点的值。 - +

## 解题思路 @@ -183,7 +192,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { - 头结点是在头插法中使用的一个额外节点,这个节点不存储值; - 第一个节点就是链表的第一个真正存储值的节点。 - +

```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -210,7 +219,7 @@ public ArrayList printListFromTailToHead(ListNode listNode) { 栈具有后进先出的特点,在遍历链表时将值按顺序放入栈中,最后出栈的顺序即为逆序。 - +

```java public ArrayList printListFromTailToHead(ListNode listNode) { @@ -235,13 +244,13 @@ public ArrayList printListFromTailToHead(ListNode listNode) { 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 - +

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

```java // 缓存中序遍历数组每个值对应的索引 @@ -291,11 +300,11 @@ public class TreeLinkNode { ① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点; - +

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

```java public TreeLinkNode GetNext(TreeLinkNode pNode) { @@ -328,7 +337,7 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) { in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。一个元素进入 in 栈之后,出栈的顺序被反转。当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的,先进入的元素先退出,这就是队列的顺序。 - +

```java Stack in = new Stack(); @@ -350,3 +359,10 @@ public int pop() throws Exception { } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 40~49.md b/docs/notes/剑指 Offer 题解 - 40~49.md index 5ba1f2ab..d2f5df09 100644 --- a/docs/notes/剑指 Offer 题解 - 40~49.md +++ b/docs/notes/剑指 Offer 题解 - 40~49.md @@ -401,3 +401,10 @@ public int GetUglyNumber_Solution(int N) { return dp[N - 1]; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 50~59.md b/docs/notes/剑指 Offer 题解 - 50~59.md index 82397bc1..b2b53493 100644 --- a/docs/notes/剑指 Offer 题解 - 50~59.md +++ b/docs/notes/剑指 Offer 题解 - 50~59.md @@ -1,4 +1,19 @@ -[TOC] + +* [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置) +* [51. 数组中的逆序对](#51-数组中的逆序对) +* [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) +* [53. 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) +* [54. 二叉查找树的第 K 个结点](#54-二叉查找树的第-k-个结点) +* [55.1 二叉树的深度](#551-二叉树的深度) +* [55.2 平衡二叉树](#552-平衡二叉树) +* [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字) +* [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) +* [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) +* [58.1 翻转单词顺序列](#581-翻转单词顺序列) +* [58.2 左旋转字符串](#582-左旋转字符串) +* [59. 滑动窗口的最大值](#59-滑动窗口的最大值) + + # 50. 第一个只出现一次的字符位置 @@ -105,7 +120,7 @@ private void merge(int[] nums, int l, int m, int h) { ## 题目描述 - +

## 解题思路 @@ -197,7 +212,7 @@ private void inOrder(TreeNode root, int k) { 从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 - +

## 解题思路 @@ -215,7 +230,7 @@ public int TreeDepth(TreeNode root) { 平衡二叉树左右子树高度差不超过 1。 - +

## 解题思路 @@ -462,3 +477,10 @@ public ArrayList maxInWindows(int[] num, int size) { return ret; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 60~68.md b/docs/notes/剑指 Offer 题解 - 60~68.md index 856cae67..8f701f01 100644 --- a/docs/notes/剑指 Offer 题解 - 60~68.md +++ b/docs/notes/剑指 Offer 题解 - 60~68.md @@ -1,4 +1,15 @@ -[TOC] + +* [60. n 个骰子的点数](#60-n-个骰子的点数) +* [61. 扑克牌顺子](#61-扑克牌顺子) +* [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数) +* [63. 股票的最大利润](#63-股票的最大利润) +* [64. 求 1+2+3+...+n](#64-求-123n) +* [65. 不用加减乘除做加法](#65-不用加减乘除做加法) +* [66. 构建乘积数组](#66-构建乘积数组) +* [67. 把字符串转换成整数](#67-把字符串转换成整数) +* [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) + + # 60. n 个骰子的点数 @@ -8,7 +19,7 @@ 把 n 个骰子扔在地上,求点数和为 s 的概率。 - +

## 解题思路 @@ -81,7 +92,7 @@ public List> dicesSum(int n) { 五张牌,其中大小鬼为癞子,牌面为 0。判断这五张牌是否能组成顺子。 - +

## 解题思路 @@ -141,7 +152,7 @@ public int LastRemaining_Solution(int n, int m) { 可以有一次买入和一次卖出,买入必须在前。求最大收益。 - +

## 解题思路 @@ -213,7 +224,7 @@ public int Add(int a, int b) { 给定一个数组 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]。要求不能使用除法。 - +

## 解题思路 @@ -278,7 +289,7 @@ public int StrToInt(String str) { 二叉查找树中,两个节点 p, q 的公共祖先 root 满足 root.val >= p.val && root.val <= q.val。 - +

```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -298,7 +309,7 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 在左右子树中查找是否存在 p 或者 q,如果 p 和 q 分别在两个子树中,那么就说明根节点就是最低公共祖先。 - +

```java public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { @@ -309,3 +320,10 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return left == null ? right : right == null ? left : root; } ``` + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 目录.md b/docs/notes/剑指 Offer 题解 - 目录.md index 301242e2..40c1aacc 100644 --- a/docs/notes/剑指 Offer 题解 - 目录.md +++ b/docs/notes/剑指 Offer 题解 - 目录.md @@ -81,3 +81,10 @@ # 参考文献 何海涛. 剑指 Offer[M]. 电子工业出版社, 2012. + + + + + + +
diff --git a/docs/notes/剑指 Offer 题解 - 目录1.md b/docs/notes/剑指 Offer 题解 - 目录1.md index a4c6701a..cdd614a7 100644 --- a/docs/notes/剑指 Offer 题解 - 目录1.md +++ b/docs/notes/剑指 Offer 题解 - 目录1.md @@ -81,3 +81,10 @@ # 参考文献 何海涛. 剑指 Offer[M]. 电子工业出版社, 2012. + + + + + + +
diff --git a/docs/notes/剑指 offer 题解.md b/docs/notes/剑指 offer 题解.md index a8c498b1..7ee9a8c6 100644 --- a/docs/notes/剑指 offer 题解.md +++ b/docs/notes/剑指 offer 题解.md @@ -1 +1,8 @@ [剑指 Offer 题解](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%20%E7%9B%AE%E5%BD%95.md) + + + + + + +
diff --git a/docs/notes/攻击技术.md b/docs/notes/攻击技术.md index 6ba68f32..b8407b56 100644 --- a/docs/notes/攻击技术.md +++ b/docs/notes/攻击技术.md @@ -1,4 +1,11 @@ -[TOC] + +* [一、跨站脚本攻击](#一跨站脚本攻击) +* [二、跨站请求伪造](#二跨站请求伪造) +* [三、SQL 注入攻击](#三sql-注入攻击) +* [四、拒绝服务攻击](#四拒绝服务攻击) +* [参考资料](#参考资料) + + # 一、跨站脚本攻击 @@ -184,3 +191,10 @@ ResultSet rs = stmt.executeQuery(); - [维基百科:SQL 注入攻击](https://zh.wikipedia.org/wiki/SQL%E8%B3%87%E6%96%99%E9%9A%B1%E7%A2%BC%E6%94%BB%E6%93%8A) - [维基百科:跨站点请求伪造](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0) - [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A) + + + + + + +
diff --git a/docs/notes/数据库系统原理.md b/docs/notes/数据库系统原理.md index b1a572a3..69c538c9 100644 --- a/docs/notes/数据库系统原理.md +++ b/docs/notes/数据库系统原理.md @@ -1,4 +1,45 @@ -[TOC] + +* [一、事务](#一事务) + * [概念](#概念) + * [ACID](#acid) + * [AUTOCOMMIT](#autocommit) +* [二、并发一致性问题](#二并发一致性问题) + * [丢失修改](#丢失修改) + * [读脏数据](#读脏数据) + * [不可重复读](#不可重复读) + * [幻影读](#幻影读) +* [三、封锁](#三封锁) + * [封锁粒度](#封锁粒度) + * [封锁类型](#封锁类型) + * [封锁协议](#封锁协议) + * [MySQL 隐式与显示锁定](#mysql-隐式与显示锁定) +* [四、隔离级别](#四隔离级别) + * [未提交读(READ UNCOMMITTED)](#未提交读read-uncommitted) + * [提交读(READ COMMITTED)](#提交读read-committed) + * [可重复读(REPEATABLE READ)](#可重复读repeatable-read) + * [可串行化(SERIALIZABLE)](#可串行化serializable) +* [五、多版本并发控制](#五多版本并发控制) + * [版本号](#版本号) + * [隐藏的列](#隐藏的列) + * [Undo 日志](#undo-日志) + * [实现过程](#实现过程) + * [快照读与当前读](#快照读与当前读) +* [六、Next-Key Locks](#六next-key-locks) + * [Record Locks](#record-locks) + * [Gap Locks](#gap-locks) + * [Next-Key Locks](#next-key-locks) +* [七、关系数据库设计理论](#七关系数据库设计理论) + * [函数依赖](#函数依赖) + * [异常](#异常) + * [范式](#范式) +* [八、ER 图](#八er-图) + * [实体的三种联系](#实体的三种联系) + * [表示出现多次的关系](#表示出现多次的关系) + * [联系的多向性](#联系的多向性) + * [表示子类](#表示子类) +* [参考资料](#参考资料) + + # 一、事务 @@ -6,7 +47,7 @@ 事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。 - +

## ACID @@ -39,7 +80,7 @@ - 在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。 - 事务满足持久化是为了能应对数据库崩溃的情况。 - +

## AUTOCOMMIT @@ -53,25 +94,25 @@ MySQL 默认采用自动提交模式。也就是说,如果不显式使用`STAR T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。 - +

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

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

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

---- @@ -106,8 +147,8 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | - | X | S | | :--: | :--: | :--: | -| **X** |×|×| -| **S** |×|√| +| **X** |×|×| +| **S** |×|√| ### 2. 意向锁 @@ -126,10 +167,10 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | - | X | IX | S | IS | | :--: | :--: | :--: | :--: | :--: | -| **X** |× |× |× | ×| -| **IX** |× |√ |× | √| -| **S** |× |× |√ | √| -| **IS** |× |√ |√ | √| +| **X** |× |× |× | ×| +| **IX** |× |√ |× | √| +| **S** |× |× |√ | √| +| **IS** |× |√ |√ | √| 解释如下: @@ -140,7 +181,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 ### 1. 三级封锁协议 -**一级封锁协议** +**一级封锁协议** 事务 T 要修改数据 A 时必须加 X 锁,直到 T 结束才释放锁。 @@ -161,7 +202,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | | commit | | | unlock-x(A)| -**二级封锁协议** +**二级封锁协议** 在一级的基础上,要求读取数据 A 时必须加 S 锁,读取完马上释放 S 锁。 @@ -182,7 +223,7 @@ MySQL 中提供了两种封锁粒度:行级锁以及表级锁。 | | unlock-s(A)| | | commit | -**三级封锁协议** +**三级封锁协议** 在二级的基础上,要求读取数据 A 时必须加 S 锁,直到事务结束了才能释放 S 锁。 @@ -281,7 +322,7 @@ MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版 MVCC 使用到的快照存储在 Undo 日志中,该日志通过回滚指针把一个数据行(Record)的所有快照连接起来。 - +

## 实现过程 @@ -407,7 +448,7 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE; 可以通过分解来满足。 - **分解前**
+ **分解前**
| Sno | Sname | Sdept | Mname | Cname | Grade | | :---: | :---: | :---: | :---: | :---: |:---:| @@ -426,7 +467,7 @@ Grade 完全函数依赖于键码,它没有任何冗余数据,每个学生 Sname, Sdept 和 Mname 都部分依赖于键码,当一个学生选修了多门课时,这些数据就会出现多次,造成大量冗余数据。 - **分解后**
+ **分解后**
关系-1 @@ -495,7 +536,7 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。 下图的 Course 和 Student 是一对多的关系。 - +

## 表示出现多次的关系 @@ -503,19 +544,19 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。 下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。 - +

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

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

# 参考资料 @@ -531,3 +572,10 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。 - [MySQL locking for the busy web developer](https://www.brightbox.com/blog/2013/10/31/on-mysql-locks/) - [浅入浅出 MySQL 和 InnoDB](https://draveness.me/mysql-innodb) - [Innodb 中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html) + + + + + + +
diff --git a/docs/notes/构建工具.md b/docs/notes/构建工具.md index a22592d9..a5a6c5ce 100644 --- a/docs/notes/构建工具.md +++ b/docs/notes/构建工具.md @@ -1,4 +1,10 @@ -[TOC] + +* [一、构建工具的作用](#一构建工具的作用) +* [二、Java 主流构建工具](#二java-主流构建工具) +* [三、Maven](#三maven) +* [参考资料](#参考资料) + + # 一、构建工具的作用 @@ -29,7 +35,7 @@ Ant 具有编译、测试和打包功能,其后出现的 Maven 在 Ant 的功能基础上又新增了依赖管理功能,而最新的 Gradle 又在 Maven 的功能基础上新增了对 Groovy 语言的支持。 - +

Gradle 和 Maven 的区别是,它使用 Groovy 这种特定领域语言(DSL)来管理构建脚本,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。 @@ -133,3 +139,10 @@ A -> C -> X(2.0) - [maven 2 gradle](http://sagioto.github.io/maven2gradle/) - [新一代构建工具 gradle](https://www.imooc.com/learn/833) + + + + + + +
diff --git a/docs/notes/正则表达式.md b/docs/notes/正则表达式.md index b44f02e4..1887bd66 100644 --- a/docs/notes/正则表达式.md +++ b/docs/notes/正则表达式.md @@ -1,4 +1,17 @@ -[TOC] + +* [一、概述](#一概述) +* [二、匹配单个字符](#二匹配单个字符) +* [三、匹配一组字符](#三匹配一组字符) +* [四、使用元字符](#四使用元字符) +* [五、重复匹配](#五重复匹配) +* [六、位置匹配](#六位置匹配) +* [七、使用子表达式](#七使用子表达式) +* [八、回溯引用](#八回溯引用) +* [九、前后查找](#九前后查找) +* [十、嵌入条件](#十嵌入条件) +* [参考资料](#参考资料) + + # 一、概述 @@ -10,45 +23,45 @@ # 二、匹配单个字符 -**.** 可以用来匹配任何的单个字符,但是在绝大多数实现里面,不能匹配换行符; +**.** 可以用来匹配任何的单个字符,但是在绝大多数实现里面,不能匹配换行符; -**.** 是元字符,表示它有特殊的含义,而不是字符本身的含义。如果需要匹配 . ,那么要用 \ 进行转义,即在 . 前面加上 \ 。 +**.** 是元字符,表示它有特殊的含义,而不是字符本身的含义。如果需要匹配 . ,那么要用 \ 进行转义,即在 . 前面加上 \ 。 正则表达式一般是区分大小写的,但也有些实现不区分。 -**正则表达式** +**正则表达式** ``` C.C2018 ``` -**匹配结果** +**匹配结果** -My name is **CyC2018** . +My name is **CyC2018** . # 三、匹配一组字符 -**[ ]** 定义一个字符集合; +**[ ]** 定义一个字符集合; 0-9、a-z 定义了一个字符区间,区间使用 ASCII 码来确定,字符区间在 [ ] 中使用。 -**-** 只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; +**-** 只有在 [ ] 之间才是元字符,在 [ ] 之外就是一个普通字符; -**^** 在 [ ] 中是取非操作。 +**^** 在 [ ] 中是取非操作。 -**应用** +**应用** 匹配以 abc 为开头,并且最后一个字母不为数字的字符串: -**正则表达式** +**正则表达式** ``` abc[^0-9] ``` -**匹配结果** +**匹配结果** -1. **abcd** +1. **abcd** 2. abc1 3. abc2 @@ -96,15 +109,15 @@ abc[^0-9] # 五、重复匹配 -- **\+** 匹配 1 个或者多个字符 -- **\** * 匹配 0 个或者多个字符 -- **?** 匹配 0 个或者 1 个字符 +- **\+** 匹配 1 个或者多个字符 +- **\** * 匹配 0 个或者多个字符 +- **?** 匹配 0 个或者 1 个字符 -**应用** +**应用** 匹配邮箱地址。 -**正则表达式** +**正则表达式** ``` [\w.]+@\w+\.\w+ @@ -112,25 +125,25 @@ abc[^0-9] [\w.] 匹配的是字母数字或者 . ,在其后面加上 + ,表示匹配多次。在字符集合 [ ] 里,. 不是元字符; -**匹配结果** +**匹配结果** -**abc.def@qq.com** +**abc.def@qq.com** -- **{n}** 匹配 n 个字符 -- **{m,n}** 匹配 m\~n 个字符 -- **{m,}** 至少匹配 m 个字符 +- **{n}** 匹配 n 个字符 +- **{m,n}** 匹配 m\~n 个字符 +- **{m,}** 至少匹配 m 个字符 \* 和 + 都是贪婪型元字符,会匹配尽可能多的内容。在后面加 ? 可以转换为懒惰型元字符,例如 \*?、+? 和 {m,n}? 。 -**正则表达式** +**正则表达式** ``` a.+c ``` -**匹配结果** +**匹配结果** -**abcabcabc** +**abcabcabc** 由于 + 是贪婪型的,因此 .+ 会匹配更可能多的内容,所以会把整个 abcabcabc 文本都匹配,而不是只匹配前面的 abc 文本。用懒惰型可以实现匹配前面的。 @@ -138,71 +151,71 @@ a.+c ## 单词边界 -**\b** 可以匹配一个单词的边界,边界是指位于 \w 和 \W 之间的位置;**\B** 匹配一个不是单词边界的位置。 +**\b** 可以匹配一个单词的边界,边界是指位于 \w 和 \W 之间的位置;**\B** 匹配一个不是单词边界的位置。 \b 只匹配位置,不匹配字符,因此 \babc\b 匹配出来的结果为 3 个字符。 ## 字符串边界 -**^** 匹配整个字符串的开头,**$** 匹配结尾。 +**^** 匹配整个字符串的开头,**$** 匹配结尾。 ^ 元字符在字符集合中用作求非,在字符集合外用作匹配字符串的开头。 分行匹配模式(multiline)下,换行被当做字符串的边界。 -**应用** +**应用** 匹配代码中以 // 开始的注释行 -**正则表达式** +**正则表达式** ``` ^\s*\/\/.*$ ``` -![](pics/600e9c75-5033-4dad-ae2b-930957db638e.png) +

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

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

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

# 二、使用场景 @@ -64,3 +77,10 @@ - [Observer vs Pub-Sub](http://developers-club.com/posts/270339/) - [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105) + + + + + + +
diff --git a/docs/notes/算法 - 其它.md b/docs/notes/算法 - 其它.md index 5d057683..c82c3241 100644 --- a/docs/notes/算法 - 其它.md +++ b/docs/notes/算法 - 其它.md @@ -1,6 +1,6 @@ # 汉诺塔 - +

有三个柱子,分别为 from、buffer、to。需要将 from 上的圆盘全部移动到 to 上,并且要保证小圆盘始终在大圆盘上。 @@ -8,15 +8,15 @@ ① 将 n-1 个圆盘从 from -> buffer - +

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

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

如果只有一个圆盘,那么只需要进行一次移动操作。 @@ -67,7 +67,7 @@ from H1 to H3 生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到叶子节点,叶子节点代表的字符的编码就是这个路径编码。 - +

```java public class Huffman { @@ -126,3 +126,10 @@ public class Huffman { } } ``` + + + + + + +
diff --git a/docs/notes/算法 - 并查集.md b/docs/notes/算法 - 并查集.md index d0918e0e..22e40b21 100644 --- a/docs/notes/算法 - 并查集.md +++ b/docs/notes/算法 - 并查集.md @@ -1,10 +1,18 @@ -[TOC] + +* [前言](#前言) +* [Quick Find](#quick-find) +* [Quick Union](#quick-union) +* [加权 Quick Union](#加权-quick-union) +* [路径压缩的加权 Quick Union](#路径压缩的加权-quick-union) +* [比较](#比较) + + # 前言 用于解决动态连通性问题,能动态连接两个点,并且判断两个点是否连通。 - +

| 方法 | 描述 | | :---: | :---: | @@ -43,7 +51,7 @@ public abstract class UF { 但是 union 操作代价却很高,需要将其中一个连通分量中的所有节点 id 值都修改为另一个节点的 id 值。 - +

```java public class QuickFindUF extends UF { @@ -83,7 +91,7 @@ public class QuickFindUF extends UF { 但是 find 操作开销很大,因为同一个连通分量的节点 id 值不同,id 值只是用来指向另一个节点。因此需要一直向上查找操作,直到找到最上层的节点。 - +

```java public class QuickUnionUF extends UF { @@ -116,7 +124,7 @@ public class QuickUnionUF extends UF { 这种方法可以快速进行 union 操作,但是 find 操作和树高成正比,最坏的情况下树的高度为节点的数目。 - +

# 加权 Quick Union @@ -124,7 +132,7 @@ public class QuickUnionUF extends UF { 理论研究证明,加权 quick-union 算法构造的树深度最多不超过 logN。 - +

```java public class WeightedQuickUnionUF extends UF { @@ -182,3 +190,10 @@ public class WeightedQuickUnionUF extends UF { | Quick Union | 树高 | 树高 | | 加权 Quick Union | logN | logN | | 路径压缩的加权 Quick Union | 非常接近 1 | 非常接近 1 | + + + + + + +
diff --git a/docs/notes/算法 - 排序.md b/docs/notes/算法 - 排序.md index 51697fea..0876082e 100644 --- a/docs/notes/算法 - 排序.md +++ b/docs/notes/算法 - 排序.md @@ -29,7 +29,7 @@ public abstract class Sort> { 选择排序需要 \~N2/2 次比较和 \~N 次交换,它的运行时间与输入无关,这个特点使得它对一个已经排序的数组也需要这么多的比较和交换操作。 - +

```java public class Selection> extends Sort { @@ -56,7 +56,7 @@ public class Selection> extends Sort { 在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。 - +

```java public class Bubble> extends Sort { @@ -90,7 +90,7 @@ public class Bubble> extends Sort { - 最坏的情况下需要 \~N2/2 比较以及 \~N2/2 次交换,最坏的情况是数组是倒序的; - 最好的情况下需要 N-1 次比较和 0 次交换,最好的情况就是数组已经有序了。 - +

```java public class Insertion> extends Sort { @@ -113,7 +113,7 @@ public class Insertion> extends Sort { 希尔排序使用插入排序对间隔 h 的序列进行排序。通过不断减小 h,最后令 h=1,就可以使得整个数组是有序的。 - +

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

## 1. 归并方法 @@ -243,7 +243,7 @@ public class Down2UpMergeSort> extends MergeSort { - 归并排序将数组分为两个子数组分别排序,并将有序的子数组归并使得整个数组排序; - 快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。 - +

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

```java private int partition(T[] nums, int l, int h) { @@ -378,7 +378,7 @@ public T select(T[] nums, int k) { 堆可以用数组来表示,这是因为堆是完全二叉树,而完全二叉树很容易就存储在数组中。位置 k 的节点的父节点位置为 k/2,而它的两个子节点的位置分别为 2k 和 2k+1。这里不使用数组索引为 0 的位置,是为了更清晰地描述节点的位置关系。 - +

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

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

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

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

```java public class HeapSort> extends Sort { @@ -551,3 +551,10 @@ public class HeapSort> extends Sort { ## 2. Java 的排序算法实现 Java 主要排序方法为 java.util.Arrays.sort(),对于原始数据类型使用三向切分的快速排序,对于引用类型使用归并排序。 + + + + + + +
diff --git a/docs/notes/算法 - 栈和队列.md b/docs/notes/算法 - 栈和队列.md index 8d58c346..36b21586 100644 --- a/docs/notes/算法 - 栈和队列.md +++ b/docs/notes/算法 - 栈和队列.md @@ -1,4 +1,10 @@ -[TOC] + +* [栈](#栈) + * [1. 数组实现](#1-数组实现) + * [2. 链表实现](#2-链表实现) +* [队列](#队列) + + # 栈 @@ -310,3 +316,10 @@ public class ListQueue implements MyQueue { } } ``` + + + + + + +
diff --git a/docs/notes/算法 - 目录.md b/docs/notes/算法 - 目录.md index 2f66d5b3..d0768ba5 100644 --- a/docs/notes/算法 - 目录.md +++ b/docs/notes/算法 - 目录.md @@ -10,3 +10,10 @@ # 参考资料 - Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. + + + + + + +
diff --git a/docs/notes/算法 - 目录1.md b/docs/notes/算法 - 目录1.md index aa982d16..12613393 100644 --- a/docs/notes/算法 - 目录1.md +++ b/docs/notes/算法 - 目录1.md @@ -10,3 +10,10 @@ # 参考资料 - Sedgewick, Robert, and Kevin Wayne. _Algorithms_. Addison-Wesley Professional, 2011. + + + + + + +
diff --git a/docs/notes/算法 - 符号表.md b/docs/notes/算法 - 符号表.md index cf77b103..53df173d 100644 --- a/docs/notes/算法 - 符号表.md +++ b/docs/notes/算法 - 符号表.md @@ -1,4 +1,38 @@ -[TOC] + +* [前言](#前言) +* [初级实现](#初级实现) + * [1. 链表实现无序符号表](#1-链表实现无序符号表) + * [2. 二分查找实现有序符号表](#2-二分查找实现有序符号表) +* [二叉查找树](#二叉查找树) + * [1. get()](#1-get) + * [2. put()](#2-put) + * [3. 分析](#3-分析) + * [4. floor()](#4-floor) + * [5. rank()](#5-rank) + * [6. min()](#6-min) + * [7. deleteMin()](#7-deletemin) + * [8. delete()](#8-delete) + * [9. keys()](#9-keys) + * [10. 分析](#10-分析) +* [2-3 查找树](#2-3-查找树) + * [1. 插入操作](#1-插入操作) + * [2. 性质](#2-性质) +* [红黑树](#红黑树) + * [1. 左旋转](#1-左旋转) + * [2. 右旋转](#2-右旋转) + * [3. 颜色转换](#3-颜色转换) + * [4. 插入](#4-插入) + * [5. 分析](#5-分析) +* [散列表](#散列表) + * [1. 散列函数](#1-散列函数) + * [2. 拉链法](#2-拉链法) + * [3. 线性探测法](#3-线性探测法) +* [小结](#小结) + * [1. 符号表算法比较](#1-符号表算法比较) + * [2. Java 的符号表实现](#2-java-的符号表实现) + * [3. 稀疏向量乘法](#3-稀疏向量乘法) + + # 前言 @@ -209,15 +243,15 @@ public class BinarySearchOrderedST, Value> implement # 二叉查找树 -**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 +**二叉树** 是一个空链接,或者是一个有左右两个链接的节点,每个链接都指向一颗子二叉树。 - +

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

基本数据结构: @@ -291,7 +325,7 @@ private Value get(Node x, Key key) { 当插入的键不存在于树中,需要创建一个新节点,并且更新上层节点的链接指向该节点,使得该节点正确地链接到树中。 - +

```java @Override @@ -320,11 +354,11 @@ private Node put(Node x, Key key, Value value) { 最好的情况下树是完全平衡的,每条空链接和根节点的距离都为 logN。 - +

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

## 4. floor() @@ -402,7 +436,7 @@ private Node min(Node x) { 令指向最小节点的链接指向最小节点的右子树。 - +

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

```java public void delete(Key key) { @@ -486,7 +520,7 @@ private List keys(Node x, Key l, Key h) { 2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。 - +

## 1. 插入操作 @@ -496,11 +530,11 @@ private List keys(Node x, Key l, Key h) { - 如果插入到 2- 节点上,那么直接将新节点和原来的节点组成 3- 节点即可。 - +

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

## 2. 性质 @@ -512,7 +546,7 @@ private List keys(Node x, Key l, Key h) { 红黑树是 2-3 查找树,但它不需要分别定义 2- 节点和 3- 节点,而是在普通的二叉查找树之上,为节点添加颜色。指向一个节点的链接颜色如果为红色,那么这个节点和上层节点表示的是一个 3- 节点,而黑色则是普通链接。 - +

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

```java public class RedBlackBST, Value> extends BST { @@ -541,7 +575,7 @@ public class RedBlackBST, Value> extends BST +

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

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

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

```java @Override @@ -722,7 +756,7 @@ public class Transaction { 对于 N 个键,M 条链表 (N>M),如果哈希函数能够满足均匀性的条件,每条链表的大小趋向于 N/M,因此未命中的查找和插入操作所需要的比较次数为 \~N/M。 - +

## 3. 线性探测法 @@ -731,7 +765,7 @@ public class Transaction { 使用线性探测法,数组的大小 M 应当大于键的个数 N(M>N)。 - +

```java public class LinearProbingHashST implements UnorderedST { @@ -833,7 +867,7 @@ public void delete(Key key) { 线性探测法的成本取决于连续条目的长度,连续条目也叫聚簇。当聚簇很长时,在查找和插入时也需要进行很多次探测。例如下图中 2\~4 位置就是一个聚簇。 - +

α = N/M,把 α 称为使用率。理论证明,当 α 小于 1/2 时探测的预计次数只在 1.5 到 2.5 之间。为了保证散列表的性能,应当调整数组的大小,使得 α 在 [1/4, 1/2] 之间。 @@ -904,3 +938,10 @@ public class SparseVector { } } ``` + + + + + + +
diff --git a/docs/notes/算法 - 算法分析.md b/docs/notes/算法 - 算法分析.md index 74813bb4..336568ae 100644 --- a/docs/notes/算法 - 算法分析.md +++ b/docs/notes/算法 - 算法分析.md @@ -1,4 +1,22 @@ -[TOC] + +* [数学模型](#数学模型) + * [1. 近似](#1-近似) + * [2. 增长数量级](#2-增长数量级) + * [3. 内循环](#3-内循环) + * [4. 成本模型](#4-成本模型) +* [注意事项](#注意事项) + * [1. 大常数](#1-大常数) + * [2. 缓存](#2-缓存) + * [3. 对最坏情况下的性能的保证](#3-对最坏情况下的性能的保证) + * [4. 随机化算法](#4-随机化算法) + * [5. 均摊分析](#5-均摊分析) +* [ThreeSum](#threesum) + * [1. ThreeSumSlow](#1-threesumslow) + * [2. ThreeSumBinarySearch](#2-threesumbinarysearch) + * [3. ThreeSumTwoPointer](#3-threesumtwopointer) +* [倍率实验](#倍率实验) + + # 数学模型 @@ -216,3 +234,10 @@ public class StopWatch { } } ``` + + + + + + +
diff --git a/docs/notes/算法.md b/docs/notes/算法.md index ff8a64d0..2597009f 100644 --- a/docs/notes/算法.md +++ b/docs/notes/算法.md @@ -1 +1,8 @@ [算法](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E7%AE%97%E6%B3%95%20-%20%E7%9B%AE%E5%BD%95.md) + + + + + + +
diff --git a/docs/notes/系统设计基础.md b/docs/notes/系统设计基础.md index 5caedb10..815135bf 100644 --- a/docs/notes/系统设计基础.md +++ b/docs/notes/系统设计基础.md @@ -1,4 +1,12 @@ -[TOC] + +* [一、性能](#一性能) +* [二、伸缩性](#二伸缩性) +* [三、扩展性](#三扩展性) +* [四、可用性](#四可用性) +* [五、安全性](#五安全性) +* [参考资料](#参考资料) + + # 一、性能 @@ -99,3 +107,10 @@ # 参考资料 - 大型网站技术架构:核心原理与案例分析 + + + + + + +
diff --git a/docs/notes/缓存.md b/docs/notes/缓存.md index e98dbbfd..5a171e72 100644 --- a/docs/notes/缓存.md +++ b/docs/notes/缓存.md @@ -1,4 +1,14 @@ -[TOC] + +* [一、缓存特征](#一缓存特征) +* [二、LRU](#二lru) +* [三、缓存位置](#三缓存位置) +* [四、CDN](#四cdn) +* [五、缓存问题](#五缓存问题) +* [六、数据分布](#六数据分布) +* [七、一致性哈希](#七一致性哈希) +* [参考资料](#参考资料) + + # 一、缓存特征 @@ -201,7 +211,7 @@ CDN 主要有以下优点: - 通过部署多台服务器,从而提高系统整体的带宽性能; - 多台服务器可以看成是一种冗余机制,从而具有高可用性。 -![](pics/15313ed8-a520-4799-a300-2b6b36be314f.jpg) +

# 五、缓存问题 @@ -275,11 +285,11 @@ Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了 将哈希空间 [0, 2n-1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。 -![](pics/68b110b9-76c6-4ee2-b541-4145e65adb3e.jpg) +

一致性哈希在增加或者删除节点时只会影响到哈希环中相邻的节点,例如下图中新增节点 X,只需要将它前一个节点 C 上的数据重新进行分布即可,对于节点 A、B、D 都没有影响。 -![](pics/66402828-fb2b-418f-83f6-82153491bcfe.jpg) +

## 虚拟节点 @@ -296,3 +306,10 @@ Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了 - [一致性哈希算法](https://my.oschina.net/jayhu/blog/732849) - [内容分发网络](https://zh.wikipedia.org/wiki/%E5%85%A7%E5%AE%B9%E5%82%B3%E9%81%9E%E7%B6%B2%E8%B7%AF) - [How Aspiration CDN helps to improve your website loading speed?](https://www.aspirationhosting.com/aspiration-cdn/) + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 内存管理.md b/docs/notes/计算机操作系统 - 内存管理.md index 9d988334..5375d91b 100644 --- a/docs/notes/计算机操作系统 - 内存管理.md +++ b/docs/notes/计算机操作系统 - 内存管理.md @@ -1,4 +1,18 @@ -[TOC] + +* [虚拟内存](#虚拟内存) +* [分页系统地址映射](#分页系统地址映射) +* [页面置换算法](#页面置换算法) + * [1. 最佳](#1-最佳) + * [2. 最近最久未使用](#2-最近最久未使用) + * [3. 最近未使用](#3-最近未使用) + * [4. 先进先出](#4-先进先出) + * [5. 第二次机会算法](#5-第二次机会算法) + * [6. 时钟](#6-时钟) +* [分段](#分段) +* [段页式](#段页式) +* [分页与分段的比较](#分页与分段的比较) + + # 虚拟内存 @@ -8,7 +22,7 @@ 从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0\~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。 -![](pics/7b281b1e-0595-402b-ae35-8c91084c33c1.png) +

# 分页系统地址映射 @@ -18,7 +32,7 @@ 下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。 - +

# 页面置换算法 @@ -58,7 +72,7 @@ 4,7,0,7,1,0,1,2,1,2,6 ``` -
![](pics/eb859228-c0f2-4bce-910d-d9f76929352b.png)
+

## 3. 最近未使用 > NRU, Not Recently Used @@ -88,7 +102,7 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。 -![](pics/ecf8ad5d-5403-48b9-b6e7-f2e20ffe8fca.png) +

## 6. 时钟 @@ -96,7 +110,7 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。 -![](pics/5f5ef0b6-98ea-497c-a007-f6c55288eab1.png) +

# 分段 @@ -104,11 +118,11 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。 -![](pics/22de0538-7c6e-4365-bd3b-8ce3c5900216.png) +

分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。 -![](pics/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png) +

# 段页式 @@ -123,3 +137,10 @@ FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问 - 大小是否可以改变:页的大小不可变,段的大小可以动态改变。 - 出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。 + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 概述.md b/docs/notes/计算机操作系统 - 概述.md index 5b58083c..035e15bf 100644 --- a/docs/notes/计算机操作系统 - 概述.md +++ b/docs/notes/计算机操作系统 - 概述.md @@ -1,4 +1,24 @@ -[TOC] + +* [基本特征](#基本特征) + * [1. 并发](#1-并发) + * [2. 共享](#2-共享) + * [3. 虚拟](#3-虚拟) + * [4. 异步](#4-异步) +* [基本功能](#基本功能) + * [1. 进程管理](#1-进程管理) + * [2. 内存管理](#2-内存管理) + * [3. 文件管理](#3-文件管理) + * [4. 设备管理](#4-设备管理) +* [系统调用](#系统调用) +* [大内核和微内核](#大内核和微内核) + * [1. 大内核](#1-大内核) + * [2. 微内核](#2-微内核) +* [中断分类](#中断分类) + * [1. 外中断](#1-外中断) + * [2. 异常](#2-异常) + * [3. 陷入](#3-陷入) + + # 基本特征 @@ -56,7 +76,7 @@ 如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。 - +

Linux 的系统调用主要有以下这些: @@ -85,7 +105,7 @@ Linux 的系统调用主要有以下这些: 因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。 -![](pics/2_14_microkernelArchitecture.jpg) +

# 中断分类 @@ -100,3 +120,10 @@ Linux 的系统调用主要有以下这些: ## 3. 陷入 在用户程序中使用系统调用。 + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 死锁.md b/docs/notes/计算机操作系统 - 死锁.md index 370fc4a0..be20dd80 100644 --- a/docs/notes/计算机操作系统 - 死锁.md +++ b/docs/notes/计算机操作系统 - 死锁.md @@ -1,8 +1,26 @@ -[TOC] + +* [必要条件](#必要条件) +* [处理方法](#处理方法) +* [鸵鸟策略](#鸵鸟策略) +* [死锁检测与死锁恢复](#死锁检测与死锁恢复) + * [1. 每种类型一个资源的死锁检测](#1-每种类型一个资源的死锁检测) + * [2. 每种类型多个资源的死锁检测](#2-每种类型多个资源的死锁检测) + * [3. 死锁恢复](#3-死锁恢复) +* [死锁预防](#死锁预防) + * [1. 破坏互斥条件](#1-破坏互斥条件) + * [2. 破坏占有和等待条件](#2-破坏占有和等待条件) + * [3. 破坏不可抢占条件](#3-破坏不可抢占条件) + * [4. 破坏环路等待](#4-破坏环路等待) +* [死锁避免](#死锁避免) + * [1. 安全状态](#1-安全状态) + * [2. 单个资源的银行家算法](#2-单个资源的银行家算法) + * [3. 多个资源的银行家算法](#3-多个资源的银行家算法) + + # 必要条件 -![](pics/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png) +

- 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。 - 占有和等待:已经得到了某个资源的进程可以再请求新的资源。 @@ -34,7 +52,7 @@ ## 1. 每种类型一个资源的死锁检测 -![](pics/b1fa0453-a4b0-4eae-a352-48acca8fff74.png) +

上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。 @@ -44,7 +62,7 @@ ## 2. 每种类型多个资源的死锁检测 -![](pics/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png) +

上图中,有三个进程四个资源,每个数据代表的含义如下: @@ -93,7 +111,7 @@ ## 1. 安全状态 -![](pics/ed523051-608f-4c3f-b343-383e2d194470.png) +

图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。 @@ -105,13 +123,13 @@ 一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。 -![](pics/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png) +

上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。 ## 3. 多个资源的银行家算法 -![](pics/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png) +

上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。 @@ -122,3 +140,10 @@ - 重复以上两步,直到所有进程都标记为终止,则状态时安全的。 如果一个状态不是安全的,需要拒绝进入这个状态。 + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 目录.md b/docs/notes/计算机操作系统 - 目录.md index c7d161fc..db5f29d7 100644 --- a/docs/notes/计算机操作系统 - 目录.md +++ b/docs/notes/计算机操作系统 - 目录.md @@ -18,3 +18,10 @@ - [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php) - [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1) - [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 目录1.md b/docs/notes/计算机操作系统 - 目录1.md index e4895e72..616f34e0 100644 --- a/docs/notes/计算机操作系统 - 目录1.md +++ b/docs/notes/计算机操作系统 - 目录1.md @@ -18,3 +18,10 @@ - [Processes](http://cse.csusb.edu/tongyu/courses/cs460/notes/process.php) - [Inter Process Communication Presentation[1]](https://www.slideshare.net/rkolahalam/inter-process-communication-presentation1) - [Decoding UCS Invicta – Part 1](https://blogs.cisco.com/datacenter/decoding-ucs-invicta-part-1) + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 设备管理.md b/docs/notes/计算机操作系统 - 设备管理.md index e1abf3ed..535c5e01 100644 --- a/docs/notes/计算机操作系统 - 设备管理.md +++ b/docs/notes/计算机操作系统 - 设备管理.md @@ -1,4 +1,11 @@ -[TOC] + +* [磁盘结构](#磁盘结构) +* [磁盘调度算法](#磁盘调度算法) + * [1. 先来先服务](#1-先来先服务) + * [2. 最短寻道时间优先](#2-最短寻道时间优先) + * [3. 电梯算法](#3-电梯算法) + + # 磁盘结构 @@ -9,7 +16,7 @@ - 制动手臂(Actuator arm):用于在磁道之间移动磁头; - 主轴(Spindle):使整个盘面转动。 -![](pics/014fbc4d-d873-4a12-b160-867ddaed9807.jpg) +

# 磁盘调度算法 @@ -37,7 +44,7 @@ 虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。 -![](pics/4e2485e4-34bd-4967-9f02-0c093b797aaa.png) +

## 3. 电梯算法 @@ -49,4 +56,11 @@ 因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。 -![](pics/271ce08f-c124-475f-b490-be44fedc6d2e.png) +

+ + + + + + +
diff --git a/docs/notes/计算机操作系统 - 进程管理.md b/docs/notes/计算机操作系统 - 进程管理.md index 6991951c..91663de6 100644 --- a/docs/notes/计算机操作系统 - 进程管理.md +++ b/docs/notes/计算机操作系统 - 进程管理.md @@ -1,4 +1,30 @@ -[TOC] + +* [进程与线程](#进程与线程) + * [1. 进程](#1-进程) + * [2. 线程](#2-线程) + * [3. 区别](#3-区别) +* [进程状态的切换](#进程状态的切换) +* [进程调度算法](#进程调度算法) + * [1. 批处理系统](#1-批处理系统) + * [2. 交互式系统](#2-交互式系统) + * [3. 实时系统](#3-实时系统) +* [进程同步](#进程同步) + * [1. 临界区](#1-临界区) + * [2. 同步与互斥](#2-同步与互斥) + * [3. 信号量](#3-信号量) + * [4. 管程](#4-管程) +* [经典同步问题](#经典同步问题) + * [1. 读者-写者问题](#1-读者-写者问题) + * [2. 哲学家进餐问题](#2-哲学家进餐问题) +* [进程通信](#进程通信) + * [1. 管道](#1-管道) + * [2. FIFO](#2-fifo) + * [3. 消息队列](#3-消息队列) + * [4. 信号量](#4-信号量) + * [5. 共享存储](#5-共享存储) + * [6. 套接字](#6-套接字) + + # 进程与线程 @@ -10,7 +36,7 @@ 下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。 -![](pics/a6ac2b08-3861-4e85-baa8-382287bfee9f.png) +

## 2. 线程 @@ -20,7 +46,7 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。 -![](pics/3cd630ea-017c-488d-ad1d-732b4efeddf5.png) +

## 3. 区别 @@ -42,7 +68,7 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H # 进程状态的切换 - +

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

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

## 3. 实时系统 @@ -139,12 +165,12 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H 信号量(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; @@ -162,7 +188,7 @@ void P2() { } ``` - **使用信号量实现生产者-消费者问题**
+ **使用信号量实现生产者-消费者问题**
问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。 @@ -227,9 +253,9 @@ end monitor; 管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。 -管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 +管程引入了 **条件变量** 以及相关的操作:**wait()** 和 **signal()** 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。 - **使用管程实现生产者-消费者问题**
+ **使用管程实现生产者-消费者问题**
```pascal // 管程 @@ -427,7 +453,7 @@ void reader() ## 2. 哲学家进餐问题 -![](pics/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg) +

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 @@ -521,7 +547,7 @@ int pipe(int fd[2]); - 只支持半双工通信(单向交替传输); - 只能在父子进程或者兄弟进程中使用。 -![](pics/53cd9ade-b0a6-4399-b4de-7f1fbd06cdfb.png) +

## 2. FIFO @@ -535,7 +561,7 @@ int mkfifoat(int fd, const char *path, mode_t mode); FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。 -![](pics/2ac50b81-d92a-4401-b9ec-f2113ecc3076.png) +

## 3. 消息队列 @@ -560,3 +586,10 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户 ## 6. 套接字 与其它通信机制不同的是,它可用于不同机器间的进程通信。 + + + + + + +
diff --git a/docs/notes/计算机操作系统 - 链接.md b/docs/notes/计算机操作系统 - 链接.md index bdfc8ee6..2f8f9fef 100644 --- a/docs/notes/计算机操作系统 - 链接.md +++ b/docs/notes/计算机操作系统 - 链接.md @@ -1,4 +1,10 @@ -[TOC] + +* [编译系统](#编译系统) +* [静态链接](#静态链接) +* [目标文件](#目标文件) +* [动态链接](#动态链接) + + # 编译系统 @@ -23,7 +29,7 @@ gcc -o hello hello.c 这个过程大致如下: - +

- 预处理阶段:处理以 # 开头的预处理命令; - 编译阶段:翻译成汇编文件; @@ -37,7 +43,7 @@ gcc -o hello hello.c - 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 - 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。 -![](pics/47d98583-8bb0-45cc-812d-47eefa0a4a40.jpg) +

# 目标文件 @@ -57,4 +63,11 @@ gcc -o hello hello.c - 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中; - 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。 -![](pics/76dc7769-1aac-4888-9bea-064f1caa8e77.jpg) +

+ + + + + + +
diff --git a/docs/notes/计算机操作系统.md b/docs/notes/计算机操作系统.md index c7dcdb10..84d12598 100644 --- a/docs/notes/计算机操作系统.md +++ b/docs/notes/计算机操作系统.md @@ -1 +1,8 @@ [计算机操作系统](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%20-%20%E7%9B%AE%E5%BD%95.md) + + + + + + +
diff --git a/docs/notes/计算机网络 - 传输层.md b/docs/notes/计算机网络 - 传输层.md index 1febd298..adf84371 100644 --- a/docs/notes/计算机网络 - 传输层.md +++ b/docs/notes/计算机网络 - 传输层.md @@ -1,4 +1,17 @@ -[TOC] + +* [UDP 和 TCP 的特点](#udp-和-tcp-的特点) +* [UDP 首部格式](#udp-首部格式) +* [TCP 首部格式](#tcp-首部格式) +* [TCP 的三次握手](#tcp-的三次握手) +* [TCP 的四次挥手](#tcp-的四次挥手) +* [TCP 可靠传输](#tcp-可靠传输) +* [TCP 滑动窗口](#tcp-滑动窗口) +* [TCP 流量控制](#tcp-流量控制) +* [TCP 拥塞控制](#tcp-拥塞控制) + * [1. 慢开始与拥塞避免](#1-慢开始与拥塞避免) + * [2. 快重传与快恢复](#2-快重传与快恢复) + + 网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。传输层提供了进程间的逻辑通信,传输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个传输层实体之间有一条端到端的逻辑通信信道。 @@ -10,31 +23,31 @@ # UDP 首部格式 - +

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

-- **序号** :用于对字节流进行编号,例如序号为 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 的三次握手 - +

假设 A 为客户端,B 为服务器端。 @@ -48,7 +61,7 @@ - B 收到 A 的确认后,连接建立。 -**三次握手的原因** +**三次握手的原因** 第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。 @@ -56,7 +69,7 @@ # TCP 的四次挥手 - +

以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 @@ -70,11 +83,11 @@ - B 收到 A 的确认后释放连接。 -**四次挥手的原因** +**四次挥手的原因** 客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 -**TIME_WAIT** +**TIME_WAIT** 客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: @@ -104,7 +117,7 @@ TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文 接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 34, 35},其中 {31} 按序到达,而 {34, 35} 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 - +

# TCP 流量控制 @@ -116,7 +129,7 @@ TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 - +

TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。 @@ -127,7 +140,7 @@ TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、 - 接收方有足够大的接收缓存,因此不会发生流量控制; - 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 - +

## 1. 慢开始与拥塞避免 @@ -147,4 +160,11 @@ TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、 慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。 - +

+ + + + + + +
diff --git a/docs/notes/计算机网络 - 应用层.md b/docs/notes/计算机网络 - 应用层.md index 8e5a638e..1e28cbc2 100644 --- a/docs/notes/计算机网络 - 应用层.md +++ b/docs/notes/计算机网络 - 应用层.md @@ -1,4 +1,20 @@ -[TOC] + +* [域名系统](#域名系统) +* [文件传送协议](#文件传送协议) +* [动态主机配置协议](#动态主机配置协议) +* [远程登录协议](#远程登录协议) +* [电子邮件协议](#电子邮件协议) + * [1. SMTP](#1-smtp) + * [2. POP3](#2-pop3) + * [3. IMAP](#3-imap) +* [常用端口](#常用端口) +* [Web 页面请求过程](#web-页面请求过程) + * [1. DHCP 配置主机信息](#1-dhcp-配置主机信息) + * [2. ARP 解析 MAC 地址](#2-arp-解析-mac-地址) + * [3. DNS 解析域名](#3-dns-解析域名) + * [4. HTTP 请求页面](#4-http-请求页面) + + # 域名系统 @@ -6,7 +22,7 @@ DNS 是一个分布式数据库,提供了主机名和 IP 地址之间相互转 域名具有层次结构,从上到下依次为:根域名、顶级域名、二级域名。 -![](pics/b54eeb16-0b0e-484c-be62-306f57c40d77.jpg) +

DNS 可以使用 UDP 或者 TCP 进行传输,使用的端口号都为 53。大多数情况下 DNS 使用 UDP 进行传输,这就要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。在两种情况下会使用 TCP 进行传输: @@ -24,11 +40,11 @@ FTP 使用 TCP 进行连接,它需要两个连接来传送一个文件: - 主动模式:服务器端主动建立数据连接,其中服务器端的端口号为 20,客户端的端口号随机,但是必须大于 1024,因为 0\~1023 是熟知端口号。 -![](pics/03f47940-3843-4b51-9e42-5dcaff44858b.jpg) +

- 被动模式:客户端主动建立数据连接,其中客户端的端口号由客户端自己指定,服务器端的端口号随机。 -![](pics/be5c2c61-86d2-4dba-a289-b48ea23219de.jpg) +

主动模式要求客户端开放端口号给服务器端,需要去配置客户端的防火墙。被动模式只需要服务器端开放端口号即可,无需客户端配置防火墙。但是被动模式会导致服务器端的安全性减弱,因为开放了过多的端口号。 @@ -45,7 +61,7 @@ DHCP 工作过程如下: 3. 如果客户端选择了某个 DHCP 服务器提供的信息,那么就发送 Request 报文给该 DHCP 服务器。 4. DHCP 服务器发送 Ack 报文,表示客户端此时可以使用提供给它的信息。 -![](pics/23219e4c-9fc0-4051-b33a-2bd95bf054ab.jpg) +

# 远程登录协议 @@ -59,13 +75,13 @@ TELNET 可以适应许多计算机和操作系统的差异,例如不同操作 邮件协议包含发送协议和读取协议,发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 - +

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

## 2. POP3 @@ -148,3 +164,10 @@ IMAP 协议中客户端和服务器上的邮件保持同步,如果不手动删 - HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。 - 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。 + + + + + + +
diff --git a/docs/notes/计算机网络 - 概述.md b/docs/notes/计算机网络 - 概述.md index 1b6338a3..7aee018f 100644 --- a/docs/notes/计算机网络 - 概述.md +++ b/docs/notes/计算机网络 - 概述.md @@ -1,30 +1,48 @@ -[TOC] + +* [网络的网络](#网络的网络) +* [ISP](#isp) +* [主机之间的通信方式](#主机之间的通信方式) +* [电路交换与分组交换](#电路交换与分组交换) + * [1. 电路交换](#1-电路交换) + * [2. 分组交换](#2-分组交换) +* [时延](#时延) + * [1. 排队时延](#1-排队时延) + * [2. 处理时延](#2-处理时延) + * [3. 传输时延](#3-传输时延) + * [4. 传播时延](#4-传播时延) +* [计算机网络体系结构](#计算机网络体系结构) + * [1. 五层协议](#1-五层协议) + * [2. OSI](#2-osi) + * [3. TCP/IP](#3-tcpip) + * [4. 数据在各层之间的传递过程](#4-数据在各层之间的传递过程) + + # 网络的网络 网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 - +

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

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

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

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

# 电路交换与分组交换 @@ -42,7 +60,7 @@ 总时延 = 排队时延 + 处理时延 + 传输时延 + 传播时延 - +

## 1. 排队时延 @@ -58,7 +76,7 @@ - +

其中 l 表示数据帧的长度,v 表示传输速率。 @@ -69,33 +87,33 @@ - +

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

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

## 4. 数据在各层之间的传递过程 在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。 + + + + + + +
diff --git a/docs/notes/计算机网络 - 物理层.md b/docs/notes/计算机网络 - 物理层.md index c2bd4b90..d312e310 100644 --- a/docs/notes/计算机网络 - 物理层.md +++ b/docs/notes/计算机网络 - 物理层.md @@ -1,4 +1,8 @@ -[TOC] + +* [通信方式](#通信方式) +* [带通调制](#带通调制) + + # 通信方式 @@ -12,5 +16,12 @@ 模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 - +

+ + + + + + +
diff --git a/docs/notes/计算机网络 - 目录.md b/docs/notes/计算机网络 - 目录.md index ffb1f218..7d7b0def 100644 --- a/docs/notes/计算机网络 - 目录.md +++ b/docs/notes/计算机网络 - 目录.md @@ -24,3 +24,10 @@ - [Technology-Computer Networking[1]-Computer Networks and the Internet](http://www.linyibin.cn/2017/02/12/technology-ComputerNetworking-Internet/) - [P2P 网络概述.](http://slidesplayer.com/slide/11616167/) - [Circuit Switching (a) Circuit switching. (b) Packet switching.](http://slideplayer.com/slide/5115386/) + + + + + + +
diff --git a/docs/notes/计算机网络 - 目录1.md b/docs/notes/计算机网络 - 目录1.md index 6bf3f786..28a9aa1e 100644 --- a/docs/notes/计算机网络 - 目录1.md +++ b/docs/notes/计算机网络 - 目录1.md @@ -25,3 +25,10 @@ - [P2P 网络概述.](http://slidesplayer.com/slide/11616167/) - [Circuit Switching (a) Circuit switching. (b) Packet switching.](http://slideplayer.com/slide/5115386/) + + + + + + +
diff --git a/docs/notes/计算机网络 - 网络层.md b/docs/notes/计算机网络 - 网络层.md index a4d275fa..8e5fb672 100644 --- a/docs/notes/计算机网络 - 网络层.md +++ b/docs/notes/计算机网络 - 网络层.md @@ -1,4 +1,24 @@ -[TOC] + +* [概述](#概述) +* [IP 数据报格式](#ip-数据报格式) +* [IP 地址编址方式](#ip-地址编址方式) + * [1. 分类](#1-分类) + * [2. 子网划分](#2-子网划分) + * [3. 无分类](#3-无分类) +* [地址解析协议 ARP](#地址解析协议-arp) +* [网际控制报文协议 ICMP](#网际控制报文协议-icmp) + * [1. Ping](#1-ping) + * [2. Traceroute](#2-traceroute) +* [虚拟专用网 VPN](#虚拟专用网-vpn) +* [网络地址转换 NAT](#网络地址转换-nat) +* [路由器的结构](#路由器的结构) +* [路由器分组转发流程](#路由器分组转发流程) +* [路由选择协议](#路由选择协议) + * [1. 内部网关协议 RIP](#1-内部网关协议-rip) + * [2. 内部网关协议 OSPF](#2-内部网关协议-ospf) + * [3. 外部网关协议 BGP](#3-外部网关协议-bgp) + + # 概述 @@ -6,7 +26,7 @@ 使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 - +

与 IP 协议配套使用的还有三个协议: @@ -16,27 +36,27 @@ # IP 数据报格式 - +

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

# IP 地址编址方式 @@ -52,7 +72,7 @@ IP 地址的编址方式经历了三个历史阶段: IP 地址 ::= {< 网络号 >, < 主机号 >} - +

## 2. 子网划分 @@ -74,7 +94,7 @@ CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法, CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 -一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 +一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 **构成超网** 。 在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 @@ -82,27 +102,27 @@ CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为 网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 - +

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

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

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

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

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

# 路由器的结构 @@ -151,7 +171,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 - +

# 路由器分组转发流程 @@ -162,7 +182,7 @@ VPN 使用公用的互联网作为本机构各专用网之间的通信载体。 - 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; - 报告转发分组出错。 - +

# 路由选择协议 @@ -219,4 +239,11 @@ BGP 只能寻找一条比较好的路由,而不是最佳路由。 每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 - +

+ + + + + + +
diff --git a/docs/notes/计算机网络 - 链路层.md b/docs/notes/计算机网络 - 链路层.md index 2e76cdd9..79e6e6a9 100644 --- a/docs/notes/计算机网络 - 链路层.md +++ b/docs/notes/计算机网络 - 链路层.md @@ -1,4 +1,26 @@ -[TOC] + +* [基本问题](#基本问题) + * [1. 封装成帧](#1-封装成帧) + * [2. 透明传输](#2-透明传输) + * [3. 差错检测](#3-差错检测) +* [信道分类](#信道分类) + * [1. 广播信道](#1-广播信道) + * [2. 点对点信道](#2-点对点信道) +* [信道复用技术](#信道复用技术) + * [1. 频分复用](#1-频分复用) + * [2. 时分复用](#2-时分复用) + * [3. 统计时分复用](#3-统计时分复用) + * [4. 波分复用](#4-波分复用) + * [5. 码分复用](#5-码分复用) +* [CSMA/CD 协议](#csmacd-协议) +* [PPP 协议](#ppp-协议) +* [MAC 地址](#mac-地址) +* [局域网](#局域网) +* [以太网](#以太网) +* [交换机](#交换机) +* [虚拟局域网](#虚拟局域网) + + # 基本问题 @@ -6,7 +28,7 @@ 将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 - +

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

## 3. 差错检测 @@ -42,13 +64,13 @@ 频分复用的所有主机在相同的时间占用不同的频率带宽资源。 - +

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

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

## 4. 波分复用 @@ -68,7 +90,7 @@ - +

为了讨论方便,取 m=8,设码片 为 00011011。在拥有该码片的用户发送比特 1 时就发送该码片,发送比特 0 时就发送该码片的反码 11100100。 @@ -78,9 +100,9 @@ - +

- +

其中 的反码。 @@ -88,28 +110,28 @@ 码分复用需要发送的数据量为原先的 m 倍。 - +

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

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

PPP 的帧格式: @@ -118,7 +140,7 @@ PPP 的帧格式: - FCS 字段是使用 CRC 的检验序列 - 信息部分的长度不超过 1500 - +

# MAC 地址 @@ -134,7 +156,7 @@ MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标 可以按照网络拓扑结构对局域网进行分类: - +

# 以太网 @@ -146,11 +168,11 @@ MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标 以太网帧格式: -- **类型** :标记上层使用的协议; -- **数据** :长度在 46-1500 之间,如果太小则需要填充; -- **FCS** :帧检验序列,使用的是 CRC 检验方法; +- **类型** :标记上层使用的协议; +- **数据** :长度在 46-1500 之间,如果太小则需要填充; +- **FCS** :帧检验序列,使用的是 CRC 检验方法; - +

# 交换机 @@ -160,7 +182,7 @@ MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标 下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧,主机 B 回应该帧向主机 A 发送数据包时,交换机查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 2 的映射。 - +

# 虚拟局域网 @@ -170,5 +192,12 @@ MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标 使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧上加进了 4 字节首部 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。 - +

+ + + + + + +
diff --git a/docs/notes/计算机网络.md b/docs/notes/计算机网络.md index c8befa04..d3d1b1d7 100644 --- a/docs/notes/计算机网络.md +++ b/docs/notes/计算机网络.md @@ -1 +1,8 @@ [计算机网络.md](https://github.com/CyC2018/CS-Notes/blob/master/notes/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%20-%20%E7%9B%AE%E5%BD%95.md) + + + + + + +
diff --git a/docs/notes/设计模式.md b/docs/notes/设计模式.md index c90f78dd..11d3d74d 100644 --- a/docs/notes/设计模式.md +++ b/docs/notes/设计模式.md @@ -1,4 +1,36 @@ -[TOC] + +* [一、概述](#一概述) +* [二、创建型](#二创建型) + * [1. 单例(Singleton)](#1-单例singleton) + * [2. 简单工厂(Simple Factory)](#2-简单工厂simple-factory) + * [3. 工厂方法(Factory Method)](#3-工厂方法factory-method) + * [4. 抽象工厂(Abstract Factory)](#4-抽象工厂abstract-factory) + * [5. 生成器(Builder)](#5-生成器builder) + * [6. 原型模式(Prototype)](#6-原型模式prototype) +* [三、行为型](#三行为型) + * [1. 责任链(Chain Of Responsibility)](#1-责任链chain-of-responsibility) + * [2. 命令(Command)](#2-命令command) + * [3. 解释器(Interpreter)](#3-解释器interpreter) + * [4. 迭代器(Iterator)](#4-迭代器iterator) + * [5. 中介者(Mediator)](#5-中介者mediator) + * [6. 备忘录(Memento)](#6-备忘录memento) + * [7. 观察者(Observer)](#7-观察者observer) + * [8. 状态(State)](#8-状态state) + * [9. 策略(Strategy)](#9-策略strategy) + * [10. 模板方法(Template Method)](#10-模板方法template-method) + * [11. 访问者(Visitor)](#11-访问者visitor) + * [12. 空对象(Null)](#12-空对象null) +* [四、结构型](#四结构型) + * [1. 适配器(Adapter)](#1-适配器adapter) + * [2. 桥接(Bridge)](#2-桥接bridge) + * [3. 组合(Composite)](#3-组合composite) + * [4. 装饰(Decorator)](#4-装饰decorator) + * [5. 外观(Facade)](#5-外观facade) + * [6. 享元(Flyweight)](#6-享元flyweight) + * [7. 代理(Proxy)](#7-代理proxy) +* [参考资料](#参考资料) + + # 一、概述 @@ -20,7 +52,7 @@ 私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。 -![](pics/eca1f422-8381-409b-ad04-98ef39ae38ba.png) +

### Implementation @@ -221,7 +253,7 @@ secondName 这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。 -![](pics/40c0c17e-bba6-4493-9857-147c0044a018.png) +

### Implementation @@ -304,7 +336,7 @@ public class Client { 下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。 -![](pics/f4d0afd0-8e78-4914-9e60-4366eaf065b5.png) +

### Implementation @@ -356,7 +388,7 @@ public class ConcreteFactory2 extends Factory { ### Intent -提供一个接口,用于创建 **相关的对象家族** 。 +提供一个接口,用于创建 **相关的对象家族** 。 ### Class Diagram @@ -368,7 +400,7 @@ public class ConcreteFactory2 extends Factory { 从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。 -![](pics/e2190c36-8b27-4690-bde5-9911020a1294.png) +

### Implementation @@ -458,7 +490,7 @@ public class Client { ### Class Diagram -![](pics/db5e376d-0b3e-490e-a43a-3231914b6668.png) +

### Implementation @@ -548,7 +580,7 @@ abcdefghijklmnopqrstuvwxyz ### Class Diagram -![](pics/b8922f8c-95e6-4187-be85-572a509afb71.png) +

### Implementation @@ -609,7 +641,7 @@ abc - Handler:定义处理请求的接口,并且实现后继链(successor) -![](pics/ca9f23bf-55a4-47b2-9534-a28e35397988.png) +

### Implementation @@ -747,13 +779,13 @@ request2 is handle by ConcreteHandler2 - Invoker:通过它来调用命令 - Client:可以设置命令与命令的接收者 -![](pics/c44a0342-f405-4f17-b750-e27cf4aadde2.png) +

### Implementation 设计一个遥控器,可以控制电灯开关。 -![](pics/e6bded8e-41a0-489a-88a6-638e88ab7666.jpg) +

```java public interface Command { @@ -868,7 +900,7 @@ public class Client { - TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。 - Context:上下文,包含解释器之外的一些全局信息。 -![](pics/2b125bcd-1b36-43be-9b78-d90b076be549.png) +

### Implementation @@ -993,7 +1025,7 @@ false - Iterator 主要定义了 hasNext() 和 next() 方法。 - Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。 -![](pics/89292ae1-5f13-44dc-b508-3f035e80bf89.png) +

### Implementation @@ -1082,17 +1114,17 @@ public class Client { - Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。 - Colleague:同事,相关对象 -![](pics/30d6e95c-2e3c-4d32-bf4f-68128a70bc05.png) +

### Implementation Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构: -![](pics/82cfda3b-b53b-4c89-9fdb-26dd2db0cd02.jpg) +

使用中介者模式可以将复杂的依赖结构变成星形结构: -![](pics/5359cbf5-5a79-4874-9b17-f23c53c2cb80.jpg) +

```java public abstract class Colleague { @@ -1252,7 +1284,7 @@ doSprinkler() - Caretaker:负责保存好备忘录 - Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。 -![](pics/50678f34-694f-45a4-91c6-34d985c83fee.png) +

### Implementation @@ -1425,7 +1457,7 @@ public class Client { 主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 - +

### Class Diagram @@ -1433,13 +1465,13 @@ public class Client { 观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。 -![](pics/a8c8f894-a712-447c-9906-5caef6a016e3.png) +

### Implementation 天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。 -![](pics/b1df9732-86ce-4d69-9f06-fba1db7b3b5a.jpg) +

```java public interface Subject { @@ -1560,13 +1592,13 @@ StatisticsDisplay.update: 1.0 1.0 1.0 ### Class Diagram -![](pics/79df886f-fdc3-4020-a07f-c991bb58e0d8.png) +

### Implementation 糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。 - +

```java public interface State { @@ -1867,7 +1899,7 @@ No gumball dispensed - Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。 - Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。 -![](pics/cd1be8c2-755a-4a66-ad92-2e30f8f47922.png) +

### 与状态模式的比较 @@ -1954,13 +1986,13 @@ quack! ### Class Diagram -![](pics/ac6a794b-68c0-486c-902f-8d988eee5766.png) +

### Implementation 冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。 -![](pics/11236498-1417-46ce-a1b0-e10054256955.png) +

```java public abstract class CaffeineBeverage { @@ -2057,7 +2089,7 @@ Tea.addCondiments - ConcreteVisitor:具体访问者,存储遍历过程中的累计结果 - ObjectStructure:对象结构,可以是组合结构,或者是一个集合。 -![](pics/79c6f036-bde6-4393-85a3-ef36a0327bd2.png) +

### Implementation @@ -2262,7 +2294,7 @@ Number of items: 6 ### Class Diagram -![](pics/22870bbe-898f-4c17-a31a-d7c5ee5d1c10.png) +

### Implementation @@ -2314,11 +2346,11 @@ public class Client { 把一个类接口转换成另一个用户需要的接口。 -![](pics/3d5b828e-5c4d-48d8-a440-281e4a8e1c92.png) +

### Class Diagram -![](pics/ff5152fc-4ff3-44c4-95d6-1061002c364a.png) +

### Implementation @@ -2390,7 +2422,7 @@ public class Client { - Abstraction:定义抽象类的接口 - Implementor:定义实现类接口 -![](pics/2a1f8b0f-1dd7-4409-b177-a381c58066ad.png) +

### Implementation @@ -2548,7 +2580,7 @@ public class Client { 组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。 -![](pics/2b8bfd57-b4d1-4a75-bfb0-bcf1fba4014a.png) +

### Implementation @@ -2680,7 +2712,7 @@ Composite:root 装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。 -![](pics/6b833bc2-517a-4270-8a5e-0a5f6df8cd96.png) +

### Implementation @@ -2688,7 +2720,7 @@ Composite:root 下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。 - +

```java public interface Beverage { @@ -2786,7 +2818,7 @@ public class Client { ### Class Diagram -![](pics/f9978fa6-9f49-4a0f-8540-02d269ac448f.png) +

### Implementation @@ -2845,7 +2877,7 @@ public class Client { - IntrinsicState:内部状态,享元对象共享内部状态 - ExtrinsicState:外部状态,每个享元对象的外部状态不同 -![](pics/5f5c22d5-9c0e-49e1-b5b0-6cc7032724d4.png) +

### Implementation @@ -2934,7 +2966,7 @@ Java 利用缓存来加速大量小对象的访问时间。 - 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。 - 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。 -![](pics/9b679ff5-94c6-48a7-b9b7-2ea868e828ed.png) +

### Implementation @@ -3032,3 +3064,10 @@ public class ImageViewer { - [Design Patterns](http://www.oodesign.com/) - [Design patterns implemented in Java](http://java-design-patterns.com/) - [The breakdown of design patterns in JDK](http://www.programering.com/a/MTNxAzMwATY.html) + + + + + + +
diff --git a/docs/notes/集群.md b/docs/notes/集群.md index 783d1666..640d825e 100644 --- a/docs/notes/集群.md +++ b/docs/notes/集群.md @@ -1,4 +1,13 @@ -[TOC] + +* [一、负载均衡](#一负载均衡) + * [负载均衡算法](#负载均衡算法) + * [转发实现](#转发实现) +* [二、集群下的 Session 管理](#二集群下的-session-管理) + * [Sticky Session](#sticky-session) + * [Session Replication](#session-replication) + * [Session Server](#session-server) + + # 一、负载均衡 @@ -24,12 +33,12 @@ 下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。 - +

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

### 2. 加权轮询(Weighted Round Robbin) @@ -37,7 +46,7 @@ 例如下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。 - +

### 3. 最少连接(least Connections) @@ -45,13 +54,13 @@ 例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开,此时 (6, 4) 请求连接服务器 2。该系统继续运行时,服务器 2 会承担过大的负载。 - +

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

### 4. 加权最少连接(Weighted Least Connection) @@ -63,7 +72,7 @@ 和轮询算法类似,该算法比较适合服务器性能差不多的场景。 - +

### 6. 源地址哈希法 (IP Hash) @@ -71,7 +80,7 @@ 可以保证同一 IP 的客户端的请求会转发到同一台服务器上,用来实现会话粘滞(Sticky Session) - +

## 转发实现 @@ -86,7 +95,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服 该负载均衡转发的缺点比较明显,实际场景中很少使用它。 - +

### 2. DNS 域名解析 @@ -102,7 +111,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服 大型网站基本使用了 DNS 做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。也就是说,域名解析的结果为内部的负载均衡服务器 IP 地址。 - +

### 3. 反向代理服务器 @@ -159,7 +168,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服 - 当服务器宕机时,将丢失该服务器上的所有 Session。 - +

## Session Replication @@ -170,7 +179,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服 - 占用过多内存; - 同步过程占用网络带宽以及服务器处理器时间。 - +

## Session Server @@ -184,9 +193,16 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服 - 需要去实现存取 Session 的代码。 - +

参考: - [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/) + + + + + + +
diff --git a/docs/notes/面向对象思想.md b/docs/notes/面向对象思想.md index 20287302..a0d3d68a 100644 --- a/docs/notes/面向对象思想.md +++ b/docs/notes/面向对象思想.md @@ -1,4 +1,21 @@ -[TOC] + +* [一、三大特性](#一三大特性) + * [封装](#封装) + * [继承](#继承) + * [多态](#多态) +* [二、类图](#二类图) + * [泛化关系 (Generalization)](#泛化关系-generalization) + * [实现关系 (Realization)](#实现关系-realization) + * [聚合关系 (Aggregation)](#聚合关系-aggregation) + * [组合关系 (Composition)](#组合关系-composition) + * [关联关系 (Association)](#关联关系-association) + * [依赖关系 (Dependency)](#依赖关系-dependency) +* [三、设计原则](#三设计原则) + * [S.O.L.I.D](#solid) + * [其他常见原则](#其他常见原则) +* [参考资料](#参考资料) + + # 一、三大特性 @@ -45,11 +62,11 @@ public class Person { ## 继承 -继承实现了 **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 对象。父类引用指向子类对象称为 **向上转型** 。 ```java Animal animal = new Cat(); @@ -124,7 +141,7 @@ Percussion is playing... 用来描述继承关系,在 Java 中使用 extends 关键字。 - +

```text @startuml @@ -145,7 +162,7 @@ Vihical <|-- Trunck 用来实现一个接口,在 Java 中使用 implements 关键字。 - +

```text @startuml @@ -166,7 +183,7 @@ MoveBehavior <|.. Run 表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。 - +

```text @startuml @@ -189,7 +206,7 @@ Computer o-- Screen 和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。 - +

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

```text @startuml @@ -233,7 +250,7 @@ School "1" - "n" Student - A 类是 B 类方法当中的一个参数; - A 类向 B 类发送消息,从而影响 B 类发生变化。 - +

```text @startuml @@ -351,3 +368,10 @@ Vihicle .. N - [看懂 UML 类图和时序图](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization) - [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html) - [面向对象编程三大特性 ------ 封装、继承、多态](http://blog.csdn.net/jianyuerensheng/article/details/51602015) + + + + + + +