auto commit
This commit is contained in:
parent
8c67483fe9
commit
dc6973b8e1
|
@ -45,7 +45,7 @@
|
||||||
* [二进制分帧层](#二进制分帧层)
|
* [二进制分帧层](#二进制分帧层)
|
||||||
* [服务端推送](#服务端推送)
|
* [服务端推送](#服务端推送)
|
||||||
* [首部压缩](#首部压缩)
|
* [首部压缩](#首部压缩)
|
||||||
* [八、GET 和 POST 的区别](#八get-和-post-的区别)
|
* [八、GET 和 POST 比较](#八get-和-post-比较)
|
||||||
* [作用](#作用)
|
* [作用](#作用)
|
||||||
* [参数](#参数)
|
* [参数](#参数)
|
||||||
* [安全](#安全)
|
* [安全](#安全)
|
||||||
|
@ -61,13 +61,13 @@
|
||||||
|
|
||||||
## URL
|
## URL
|
||||||
|
|
||||||
- URI(Uniform Resource Identifier,统一资源标识符)
|
|
||||||
- URL(Uniform Resource Locator,统一资源定位符)
|
|
||||||
- URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。
|
|
||||||
|
|
||||||
URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。
|
URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//f716427a-94f2-4875-9c86-98793cf5dcc3.jpg" width="400"/> </div><br>
|
- URI(Uniform Resource Identifier,统一资源标识符)
|
||||||
|
- URL(Uniform Resource Locator,统一资源定位符)
|
||||||
|
- URN(Uniform Resource Name,统一资源名称)
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//urlnuri.jpg" width="600"/> </div><br>
|
||||||
|
|
||||||
## 请求和响应报文
|
## 请求和响应报文
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ CONNECT www.example.com:443 HTTP/1.1
|
||||||
|
|
||||||
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
|
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
|
||||||
|
|
||||||
- **206 Partial Content** :表示客户端进行了范围请求。响应报文包含由 Content-Range 指定范围的实体内容。
|
- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
|
||||||
|
|
||||||
## 3XX 重定向
|
## 3XX 重定向
|
||||||
|
|
||||||
|
@ -219,7 +219,7 @@ CONNECT www.example.com:443 HTTP/1.1
|
||||||
|
|
||||||
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
|
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
|
||||||
|
|
||||||
- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。
|
- **403 Forbidden** :请求被拒绝。
|
||||||
|
|
||||||
- **404 Not Found**
|
- **404 Not Found**
|
||||||
|
|
||||||
|
@ -331,7 +331,7 @@ Set-Cookie: tasty_cookie=strawberry
|
||||||
[page content]
|
[page content]
|
||||||
```
|
```
|
||||||
|
|
||||||
客户端之后对同一个服务器发送请求时,会从浏览器中读出 Cookie 信息通过 Cookie 请求首部字段发送给服务器。
|
客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。
|
||||||
|
|
||||||
```html
|
```html
|
||||||
GET /sample_page.html HTTP/1.1
|
GET /sample_page.html HTTP/1.1
|
||||||
|
@ -382,9 +382,9 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径
|
||||||
|
|
||||||
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
|
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
|
||||||
|
|
||||||
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在内存型数据库中,比如 Redis,效率会更高。
|
Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
|
||||||
|
|
||||||
使用 Session 维护用户登录的过程如下:
|
使用 Session 维护用户登录状态的过程如下:
|
||||||
|
|
||||||
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
|
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
|
||||||
- 服务器验证该用户名和密码;
|
- 服务器验证该用户名和密码;
|
||||||
|
@ -499,7 +499,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
|
||||||
|
|
||||||
### 1. 短连接与长连接
|
### 1. 短连接与长连接
|
||||||
|
|
||||||
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。
|
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
|
||||||
|
|
||||||
长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
|
长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
|
||||||
|
|
||||||
|
@ -688,7 +688,7 @@ HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
|
||||||
|
|
||||||
### 3. HTTPs 采用的加密方式
|
### 3. HTTPs 采用的加密方式
|
||||||
|
|
||||||
HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。(下图中的 Session Key 就是对称密钥)
|
HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥)
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//How-HTTPS-Works.png" width="600"/> </div><br>
|
<div align="center"> <img src="../pics//How-HTTPS-Works.png" width="600"/> </div><br>
|
||||||
|
|
||||||
|
@ -717,7 +717,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
|
||||||
## HTTPs 的缺点
|
## HTTPs 的缺点
|
||||||
|
|
||||||
- 因为需要进行加密解密等过程,因此速度会更慢;
|
- 因为需要进行加密解密等过程,因此速度会更慢;
|
||||||
- 需要支付证书授权的高费用。
|
- 需要支付证书授权的高额费用。
|
||||||
|
|
||||||
## 配置 HTTPs
|
## 配置 HTTPs
|
||||||
|
|
||||||
|
@ -727,7 +727,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
|
||||||
|
|
||||||
## HTTP/1.x 缺陷
|
## HTTP/1.x 缺陷
|
||||||
|
|
||||||
HTTP/1.x 实现简单是以牺牲应用性能为代价的:
|
HTTP/1.x 实现简单是以牺牲性能为代价的:
|
||||||
|
|
||||||
- 客户端需要使用多个连接才能实现并发和缩短延迟;
|
- 客户端需要使用多个连接才能实现并发和缩短延迟;
|
||||||
- 不会压缩请求和响应首部,从而导致不必要的网络流量;
|
- 不会压缩请求和响应首部,从而导致不必要的网络流量;
|
||||||
|
@ -763,7 +763,7 @@ HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//_u4E0B_u8F7D.png" width="600"/> </div><br>
|
<div align="center"> <img src="../pics//_u4E0B_u8F7D.png" width="600"/> </div><br>
|
||||||
|
|
||||||
# 八、GET 和 POST 的区别
|
# 八、GET 和 POST 比较
|
||||||
|
|
||||||
## 作用
|
## 作用
|
||||||
|
|
||||||
|
@ -870,6 +870,7 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
|
||||||
- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
|
- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
|
||||||
- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn)
|
- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn)
|
||||||
- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php)
|
- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php)
|
||||||
|
- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java)
|
||||||
- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement)
|
- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement)
|
||||||
- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html)
|
- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html)
|
||||||
- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/)
|
- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/)
|
||||||
|
|
1969
notes/Leetcode 题解.md
1969
notes/Leetcode 题解.md
File diff suppressed because it is too large
Load Diff
|
@ -275,8 +275,6 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。
|
||||||
|
|
||||||
## 组成
|
## 组成
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//BSD_disk.png" width="800"/> </div><br>
|
|
||||||
|
|
||||||
最主要的几个组成部分如下:
|
最主要的几个组成部分如下:
|
||||||
|
|
||||||
- inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号;
|
- inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号;
|
||||||
|
@ -287,6 +285,9 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。
|
||||||
- superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等;
|
- superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等;
|
||||||
- block bitmap:记录 block 是否被使用的位域;
|
- block bitmap:记录 block 是否被使用的位域;
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//BSD_disk.png" width="800"/> </div><br>
|
||||||
|
|
||||||
|
|
||||||
## 文件读取
|
## 文件读取
|
||||||
|
|
||||||
对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。
|
对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。
|
||||||
|
@ -336,7 +337,9 @@ inode 中记录了文件内容所在的 block 编号,但是每个 block 非常
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。可以看出文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。
|
建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。
|
||||||
|
|
||||||
|
可以看出文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。
|
||||||
|
|
||||||
## 日志
|
## 日志
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具
|
||||||
|
|
||||||
### 2. 操作
|
### 2. 操作
|
||||||
|
|
||||||
操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
|
进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
|
||||||
|
|
||||||
插入删除操作记录会破坏平衡树的平衡性,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作。
|
插入删除操作记录会破坏平衡树的平衡性,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作。
|
||||||
|
|
||||||
|
|
|
@ -633,11 +633,12 @@ public int RectCover(int n) {
|
||||||
|
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;否则解在 [m + 1, h] 之间,令 l = m + 1。
|
- 当 nums[m] <= nums[h] 的情况下,说明解在 [l, m] 之间,此时令 h = m;
|
||||||
|
- 否则解在 [m + 1, h] 之间,令 l = m + 1。
|
||||||
|
|
||||||
因为 h 的赋值表达式为 h = m,因此循环体的循环条件应该为 l < h,详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md#%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE) 二分查找部分。
|
因为 h 的赋值表达式为 h = m,因此循环体的循环条件应该为 l < h,详细解释请见 [Leetcode 题解](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3.md) 二分查找部分。
|
||||||
|
|
||||||
但是如果出现 nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,因此需要切换到顺序查找。
|
但是如果出现 nums[l] == nums[m] == nums[h],那么此时无法确定解在哪个区间,需要切换到顺序查找。
|
||||||
|
|
||||||
复杂度:O(logN) + O(1)
|
复杂度:O(logN) + O(1)
|
||||||
|
|
||||||
|
@ -1217,8 +1218,7 @@ public ListNode ReverseList(ListNode head) {
|
||||||
### 递归
|
### 递归
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ListNode Merge(ListNode list1, ListNode list2)
|
public ListNode Merge(ListNode list1, ListNode list2) {
|
||||||
{
|
|
||||||
if (list1 == null)
|
if (list1 == null)
|
||||||
return list2;
|
return list2;
|
||||||
if (list2 == null)
|
if (list2 == null)
|
||||||
|
@ -1236,8 +1236,7 @@ public ListNode Merge(ListNode list1, ListNode list2)
|
||||||
### 迭代
|
### 迭代
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ListNode Merge(ListNode list1, ListNode list2)
|
public ListNode Merge(ListNode list1, ListNode list2) {
|
||||||
{
|
|
||||||
ListNode head = new ListNode(-1);
|
ListNode head = new ListNode(-1);
|
||||||
ListNode cur = head;
|
ListNode cur = head;
|
||||||
while (list1 != null && list2 != null) {
|
while (list1 != null && list2 != null) {
|
||||||
|
@ -1269,15 +1268,13 @@ public ListNode Merge(ListNode list1, ListNode list2)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public boolean HasSubtree(TreeNode root1, TreeNode root2)
|
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
|
||||||
{
|
|
||||||
if (root1 == null || root2 == null)
|
if (root1 == null || root2 == null)
|
||||||
return false;
|
return false;
|
||||||
return isSubtreeWithRoot(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
|
return isSubtreeWithRoot(root1, root2) || HasSubtree(root1.left, root2) || HasSubtree(root1.right, root2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2)
|
private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2) {
|
||||||
{
|
|
||||||
if (root2 == null)
|
if (root2 == null)
|
||||||
return true;
|
return true;
|
||||||
if (root1 == null)
|
if (root1 == null)
|
||||||
|
@ -1298,9 +1295,10 @@ private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2)
|
||||||
|
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
|
### 递归
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public void Mirror(TreeNode root)
|
public void Mirror(TreeNode root) {
|
||||||
{
|
|
||||||
if (root == null)
|
if (root == null)
|
||||||
return;
|
return;
|
||||||
swap(root);
|
swap(root);
|
||||||
|
@ -1308,14 +1306,36 @@ public void Mirror(TreeNode root)
|
||||||
Mirror(root.right);
|
Mirror(root.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swap(TreeNode root)
|
private void swap(TreeNode root) {
|
||||||
{
|
|
||||||
TreeNode t = root.left;
|
TreeNode t = root.left;
|
||||||
root.left = root.right;
|
root.left = root.right;
|
||||||
root.right = t;
|
root.right = t;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 迭代
|
||||||
|
|
||||||
|
```java
|
||||||
|
public void Mirror(TreeNode root) {
|
||||||
|
Stack<TreeNode> stack = new Stack<>();
|
||||||
|
stack.push(root);
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
TreeNode node = stack.pop();
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
swap(node);
|
||||||
|
stack.push(node.left);
|
||||||
|
stack.push(node.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void swap(TreeNode node) {
|
||||||
|
TreeNode t = node.left;
|
||||||
|
node.left = node.right;
|
||||||
|
node.right = t;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# 28 对称的二叉树
|
# 28 对称的二叉树
|
||||||
|
|
||||||
[NowCder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
|
[NowCder](https://www.nowcoder.com/practice/ff05d44dfdb04e1d83bdbdab320efbcb?tpId=13&tqId=11211&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
|
||||||
|
@ -1327,15 +1347,13 @@ private void swap(TreeNode root)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
boolean isSymmetrical(TreeNode pRoot)
|
boolean isSymmetrical(TreeNode pRoot) {
|
||||||
{
|
|
||||||
if (pRoot == null)
|
if (pRoot == null)
|
||||||
return true;
|
return true;
|
||||||
return isSymmetrical(pRoot.left, pRoot.right);
|
return isSymmetrical(pRoot.left, pRoot.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSymmetrical(TreeNode t1, TreeNode t2)
|
boolean isSymmetrical(TreeNode t1, TreeNode t2) {
|
||||||
{
|
|
||||||
if (t1 == null && t2 == null)
|
if (t1 == null && t2 == null)
|
||||||
return true;
|
return true;
|
||||||
if (t1 == null || t2 == null)
|
if (t1 == null || t2 == null)
|
||||||
|
@ -1359,8 +1377,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> printMatrix(int[][] matrix)
|
public ArrayList<Integer> printMatrix(int[][] matrix) {
|
||||||
{
|
|
||||||
ArrayList<Integer> ret = new ArrayList<>();
|
ArrayList<Integer> ret = new ArrayList<>();
|
||||||
int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
|
int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
|
||||||
while (r1 <= r2 && c1 <= c2) {
|
while (r1 <= r2 && c1 <= c2) {
|
||||||
|
@ -1394,25 +1411,21 @@ public ArrayList<Integer> printMatrix(int[][] matrix)
|
||||||
private Stack<Integer> dataStack = new Stack<>();
|
private Stack<Integer> dataStack = new Stack<>();
|
||||||
private Stack<Integer> minStack = new Stack<>();
|
private Stack<Integer> minStack = new Stack<>();
|
||||||
|
|
||||||
public void push(int node)
|
public void push(int node) {
|
||||||
{
|
|
||||||
dataStack.push(node);
|
dataStack.push(node);
|
||||||
minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
|
minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pop()
|
public void pop() {
|
||||||
{
|
|
||||||
dataStack.pop();
|
dataStack.pop();
|
||||||
minStack.pop();
|
minStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int top()
|
public int top() {
|
||||||
{
|
|
||||||
return dataStack.peek();
|
return dataStack.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int min()
|
public int min() {
|
||||||
{
|
|
||||||
return minStack.peek();
|
return minStack.peek();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -1430,8 +1443,7 @@ public int min()
|
||||||
使用一个栈来模拟压入弹出操作。
|
使用一个栈来模拟压入弹出操作。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
|
public boolean IsPopOrder(int[] pushSequence, int[] popSequence) {
|
||||||
{
|
|
||||||
int n = pushSequence.length;
|
int n = pushSequence.length;
|
||||||
Stack<Integer> stack = new Stack<>();
|
Stack<Integer> stack = new Stack<>();
|
||||||
for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
|
for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
|
||||||
|
@ -1464,8 +1476,7 @@ public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
|
||||||
不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
|
不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root)
|
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
|
||||||
{
|
|
||||||
Queue<TreeNode> queue = new LinkedList<>();
|
Queue<TreeNode> queue = new LinkedList<>();
|
||||||
ArrayList<Integer> ret = new ArrayList<>();
|
ArrayList<Integer> ret = new ArrayList<>();
|
||||||
queue.add(root);
|
queue.add(root);
|
||||||
|
@ -1495,8 +1506,7 @@ public ArrayList<Integer> PrintFromTopToBottom(TreeNode root)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
|
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
|
||||||
{
|
|
||||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||||
Queue<TreeNode> queue = new LinkedList<>();
|
Queue<TreeNode> queue = new LinkedList<>();
|
||||||
queue.add(pRoot);
|
queue.add(pRoot);
|
||||||
|
@ -1529,8 +1539,7 @@ ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
|
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
|
||||||
{
|
|
||||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||||
Queue<TreeNode> queue = new LinkedList<>();
|
Queue<TreeNode> queue = new LinkedList<>();
|
||||||
queue.add(pRoot);
|
queue.add(pRoot);
|
||||||
|
@ -1571,15 +1580,13 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public boolean VerifySquenceOfBST(int[] sequence)
|
public boolean VerifySquenceOfBST(int[] sequence) {
|
||||||
{
|
|
||||||
if (sequence == null || sequence.length == 0)
|
if (sequence == null || sequence.length == 0)
|
||||||
return false;
|
return false;
|
||||||
return verify(sequence, 0, sequence.length - 1);
|
return verify(sequence, 0, sequence.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verify(int[] sequence, int first, int last)
|
private boolean verify(int[] sequence, int first, int last) {
|
||||||
{
|
|
||||||
if (last - first <= 1)
|
if (last - first <= 1)
|
||||||
return true;
|
return true;
|
||||||
int rootVal = sequence[last];
|
int rootVal = sequence[last];
|
||||||
|
@ -1610,14 +1617,12 @@ private boolean verify(int[] sequence, int first, int last)
|
||||||
```java
|
```java
|
||||||
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||||
|
|
||||||
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target)
|
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
|
||||||
{
|
|
||||||
backtracking(root, target, new ArrayList<>());
|
backtracking(root, target, new ArrayList<>());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
|
private void backtracking(TreeNode node, int target, ArrayList<Integer> path) {
|
||||||
{
|
|
||||||
if (node == null)
|
if (node == null)
|
||||||
return;
|
return;
|
||||||
path.add(node.val);
|
path.add(node.val);
|
||||||
|
@ -1641,8 +1646,7 @@ private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
|
||||||
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
|
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class RandomListNode
|
public class RandomListNode {
|
||||||
{
|
|
||||||
int label;
|
int label;
|
||||||
RandomListNode next = null;
|
RandomListNode next = null;
|
||||||
RandomListNode random = null;
|
RandomListNode random = null;
|
||||||
|
@ -1670,8 +1674,7 @@ public class RandomListNode
|
||||||
<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>
|
<div align="center"> <img src="../pics//8f3b9519-d705-48fe-87ad-2e4052fc81d2.png" width="600"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public RandomListNode Clone(RandomListNode pHead)
|
public RandomListNode Clone(RandomListNode pHead) {
|
||||||
{
|
|
||||||
if (pHead == null)
|
if (pHead == null)
|
||||||
return null;
|
return null;
|
||||||
// 插入新节点
|
// 插入新节点
|
||||||
|
@ -1718,14 +1721,12 @@ public RandomListNode Clone(RandomListNode pHead)
|
||||||
private TreeNode pre = null;
|
private TreeNode pre = null;
|
||||||
private TreeNode head = null;
|
private TreeNode head = null;
|
||||||
|
|
||||||
public TreeNode Convert(TreeNode root)
|
public TreeNode Convert(TreeNode root) {
|
||||||
{
|
|
||||||
inOrder(root);
|
inOrder(root);
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inOrder(TreeNode node)
|
private void inOrder(TreeNode node) {
|
||||||
{
|
|
||||||
if (node == null)
|
if (node == null)
|
||||||
return;
|
return;
|
||||||
inOrder(node.left);
|
inOrder(node.left);
|
||||||
|
@ -1752,21 +1753,18 @@ private void inOrder(TreeNode node)
|
||||||
```java
|
```java
|
||||||
private String deserializeStr;
|
private String deserializeStr;
|
||||||
|
|
||||||
public String Serialize(TreeNode root)
|
public String Serialize(TreeNode root) {
|
||||||
{
|
|
||||||
if (root == null)
|
if (root == null)
|
||||||
return "#";
|
return "#";
|
||||||
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
|
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TreeNode Deserialize(String str)
|
public TreeNode Deserialize(String str) {
|
||||||
{
|
|
||||||
deserializeStr = str;
|
deserializeStr = str;
|
||||||
return Deserialize();
|
return Deserialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TreeNode Deserialize()
|
private TreeNode Deserialize() {
|
||||||
{
|
|
||||||
if (deserializeStr.length() == 0)
|
if (deserializeStr.length() == 0)
|
||||||
return null;
|
return null;
|
||||||
int index = deserializeStr.indexOf(" ");
|
int index = deserializeStr.indexOf(" ");
|
||||||
|
@ -1795,8 +1793,7 @@ private TreeNode Deserialize()
|
||||||
```java
|
```java
|
||||||
private ArrayList<String> ret = new ArrayList<>();
|
private ArrayList<String> ret = new ArrayList<>();
|
||||||
|
|
||||||
public ArrayList<String> Permutation(String str)
|
public ArrayList<String> Permutation(String str) {
|
||||||
{
|
|
||||||
if (str.length() == 0)
|
if (str.length() == 0)
|
||||||
return ret;
|
return ret;
|
||||||
char[] chars = str.toCharArray();
|
char[] chars = str.toCharArray();
|
||||||
|
@ -1805,8 +1802,7 @@ public ArrayList<String> Permutation(String str)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
|
private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
|
||||||
{
|
|
||||||
if (s.length() == chars.length) {
|
if (s.length() == chars.length) {
|
||||||
ret.add(s.toString());
|
ret.add(s.toString());
|
||||||
return;
|
return;
|
||||||
|
@ -1836,8 +1832,7 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
|
||||||
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
|
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int MoreThanHalfNum_Solution(int[] nums)
|
public int MoreThanHalfNum_Solution(int[] nums) {
|
||||||
{
|
|
||||||
int majority = nums[0];
|
int majority = nums[0];
|
||||||
for (int i = 1, cnt = 1; i < nums.length; i++) {
|
for (int i = 1, cnt = 1; i < nums.length; i++) {
|
||||||
cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
|
cnt = nums[i] == majority ? cnt + 1 : cnt - 1;
|
||||||
|
@ -1868,8 +1863,7 @@ public int MoreThanHalfNum_Solution(int[] nums)
|
||||||
快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
|
快速排序的 partition() 方法,会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j],且 a[j+1..h] 大于等于 a[j],此时 a[j] 就是数组的第 j 大元素。可以利用这个特性找出数组的第 K 个元素,这种找第 K 个元素的算法称为快速选择算法。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
|
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
|
||||||
{
|
|
||||||
ArrayList<Integer> ret = new ArrayList<>();
|
ArrayList<Integer> ret = new ArrayList<>();
|
||||||
if (k > nums.length || k <= 0)
|
if (k > nums.length || k <= 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1880,8 +1874,7 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void findKthSmallest(int[] nums, int k)
|
public void findKthSmallest(int[] nums, int k) {
|
||||||
{
|
|
||||||
int l = 0, h = nums.length - 1;
|
int l = 0, h = nums.length - 1;
|
||||||
while (l < h) {
|
while (l < h) {
|
||||||
int j = partition(nums, l, h);
|
int j = partition(nums, l, h);
|
||||||
|
@ -1894,8 +1887,7 @@ public void findKthSmallest(int[] nums, int k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int partition(int[] nums, int l, int h)
|
private int partition(int[] nums, int l, int h) {
|
||||||
{
|
|
||||||
int p = nums[l]; /* 切分元素 */
|
int p = nums[l]; /* 切分元素 */
|
||||||
int i = l, j = h + 1;
|
int i = l, j = h + 1;
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -1909,8 +1901,7 @@ private int partition(int[] nums, int l, int h)
|
||||||
return j;
|
return j;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swap(int[] nums, int i, int j)
|
private void swap(int[] nums, int i, int j) {
|
||||||
{
|
|
||||||
int t = nums[i];
|
int t = nums[i];
|
||||||
nums[i] = nums[j];
|
nums[i] = nums[j];
|
||||||
nums[j] = t;
|
nums[j] = t;
|
||||||
|
@ -1927,8 +1918,7 @@ private void swap(int[] nums, int i, int j)
|
||||||
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。
|
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
|
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
|
||||||
{
|
|
||||||
if (k > nums.length || k <= 0)
|
if (k > nums.length || k <= 0)
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
|
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
|
||||||
|
@ -1959,8 +1949,7 @@ private PriorityQueue<Integer> right = new PriorityQueue<>();
|
||||||
/* 当前数据流读入的元素个数 */
|
/* 当前数据流读入的元素个数 */
|
||||||
private int N = 0;
|
private int N = 0;
|
||||||
|
|
||||||
public void Insert(Integer val)
|
public void Insert(Integer val) {
|
||||||
{
|
|
||||||
/* 插入要保证两个堆存于平衡状态 */
|
/* 插入要保证两个堆存于平衡状态 */
|
||||||
if (N % 2 == 0) {
|
if (N % 2 == 0) {
|
||||||
/* N 为偶数的情况下插入到右半边。
|
/* N 为偶数的情况下插入到右半边。
|
||||||
|
@ -1975,8 +1964,7 @@ public void Insert(Integer val)
|
||||||
N++;
|
N++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double GetMedian()
|
public Double GetMedian() {
|
||||||
{
|
|
||||||
if (N % 2 == 0)
|
if (N % 2 == 0)
|
||||||
return (left.peek() + right.peek()) / 2.0;
|
return (left.peek() + right.peek()) / 2.0;
|
||||||
else
|
else
|
||||||
|
@ -1998,16 +1986,14 @@ public Double GetMedian()
|
||||||
private int[] cnts = new int[256];
|
private int[] cnts = new int[256];
|
||||||
private Queue<Character> queue = new LinkedList<>();
|
private Queue<Character> queue = new LinkedList<>();
|
||||||
|
|
||||||
public void Insert(char ch)
|
public void Insert(char ch) {
|
||||||
{
|
|
||||||
cnts[ch]++;
|
cnts[ch]++;
|
||||||
queue.add(ch);
|
queue.add(ch);
|
||||||
while (!queue.isEmpty() && cnts[queue.peek()] > 1)
|
while (!queue.isEmpty() && cnts[queue.peek()] > 1)
|
||||||
queue.poll();
|
queue.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public char FirstAppearingOnce()
|
public char FirstAppearingOnce() {
|
||||||
{
|
|
||||||
return queue.isEmpty() ? '#' : queue.peek();
|
return queue.isEmpty() ? '#' : queue.peek();
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -2023,8 +2009,7 @@ public char FirstAppearingOnce()
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int FindGreatestSumOfSubArray(int[] nums)
|
public int FindGreatestSumOfSubArray(int[] nums) {
|
||||||
{
|
|
||||||
if (nums == null || nums.length == 0)
|
if (nums == null || nums.length == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int greatestSum = Integer.MIN_VALUE;
|
int greatestSum = Integer.MIN_VALUE;
|
||||||
|
@ -2044,8 +2029,7 @@ public int FindGreatestSumOfSubArray(int[] nums)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int NumberOf1Between1AndN_Solution(int n)
|
public int NumberOf1Between1AndN_Solution(int n) {
|
||||||
{
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for (int m = 1; m <= n; m *= 10) {
|
for (int m = 1; m <= n; m *= 10) {
|
||||||
int a = n / m, b = n % m;
|
int a = n / m, b = n % m;
|
||||||
|
@ -2066,8 +2050,7 @@ public int NumberOf1Between1AndN_Solution(int n)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int getDigitAtIndex(int index)
|
public int getDigitAtIndex(int index) {
|
||||||
{
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
return -1;
|
return -1;
|
||||||
int place = 1; // 1 表示个位,2 表示 十位...
|
int place = 1; // 1 表示个位,2 表示 十位...
|
||||||
|
@ -2085,8 +2068,7 @@ public int getDigitAtIndex(int index)
|
||||||
* place 位数的数字组成的字符串长度
|
* place 位数的数字组成的字符串长度
|
||||||
* 10, 90, 900, ...
|
* 10, 90, 900, ...
|
||||||
*/
|
*/
|
||||||
private int getAmountOfPlace(int place)
|
private int getAmountOfPlace(int place) {
|
||||||
{
|
|
||||||
if (place == 1)
|
if (place == 1)
|
||||||
return 10;
|
return 10;
|
||||||
return (int) Math.pow(10, place - 1) * 9;
|
return (int) Math.pow(10, place - 1) * 9;
|
||||||
|
@ -2096,8 +2078,7 @@ private int getAmountOfPlace(int place)
|
||||||
* place 位数的起始数字
|
* place 位数的起始数字
|
||||||
* 0, 10, 100, ...
|
* 0, 10, 100, ...
|
||||||
*/
|
*/
|
||||||
private int getBeginNumberOfPlace(int place)
|
private int getBeginNumberOfPlace(int place) {
|
||||||
{
|
|
||||||
if (place == 1)
|
if (place == 1)
|
||||||
return 0;
|
return 0;
|
||||||
return (int) Math.pow(10, place - 1);
|
return (int) Math.pow(10, place - 1);
|
||||||
|
@ -2106,8 +2087,7 @@ private int getBeginNumberOfPlace(int place)
|
||||||
/**
|
/**
|
||||||
* 在 place 位数组成的字符串中,第 index 个数
|
* 在 place 位数组成的字符串中,第 index 个数
|
||||||
*/
|
*/
|
||||||
private int getDigitAtIndex(int index, int place)
|
private int getDigitAtIndex(int index, int place) {
|
||||||
{
|
|
||||||
int beginNumber = getBeginNumberOfPlace(place);
|
int beginNumber = getBeginNumberOfPlace(place);
|
||||||
int shiftNumber = index / place;
|
int shiftNumber = index / place;
|
||||||
String number = (beginNumber + shiftNumber) + "";
|
String number = (beginNumber + shiftNumber) + "";
|
||||||
|
@ -2129,8 +2109,7 @@ private int getDigitAtIndex(int index, int place)
|
||||||
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。
|
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public String PrintMinNumber(int[] numbers)
|
public String PrintMinNumber(int[] numbers) {
|
||||||
{
|
|
||||||
if (numbers == null || numbers.length == 0)
|
if (numbers == null || numbers.length == 0)
|
||||||
return "";
|
return "";
|
||||||
int n = numbers.length;
|
int n = numbers.length;
|
||||||
|
@ -2156,8 +2135,7 @@ public String PrintMinNumber(int[] numbers)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int numDecodings(String s)
|
public int numDecodings(String s) {
|
||||||
{
|
|
||||||
if (s == null || s.length() == 0)
|
if (s == null || s.length() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int n = s.length();
|
int n = s.length();
|
||||||
|
@ -2200,8 +2178,7 @@ public int numDecodings(String s)
|
||||||
应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。
|
应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int getMost(int[][] values)
|
public int getMost(int[][] values) {
|
||||||
{
|
|
||||||
if (values == null || values.length == 0 || values[0].length == 0)
|
if (values == null || values.length == 0 || values[0].length == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int n = values[0].length;
|
int n = values[0].length;
|
||||||
|
@ -2224,8 +2201,7 @@ public int getMost(int[][] values)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int longestSubStringWithoutDuplication(String str)
|
public int longestSubStringWithoutDuplication(String str) {
|
||||||
{
|
|
||||||
int curLen = 0;
|
int curLen = 0;
|
||||||
int maxLen = 0;
|
int maxLen = 0;
|
||||||
int[] preIndexs = new int[26];
|
int[] preIndexs = new int[26];
|
||||||
|
@ -2257,8 +2233,7 @@ public int longestSubStringWithoutDuplication(String str)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int GetUglyNumber_Solution(int N)
|
public int GetUglyNumber_Solution(int N) {
|
||||||
{
|
|
||||||
if (N <= 6)
|
if (N <= 6)
|
||||||
return N;
|
return N;
|
||||||
int i2 = 0, i3 = 0, i5 = 0;
|
int i2 = 0, i3 = 0, i5 = 0;
|
||||||
|
@ -2291,8 +2266,7 @@ public int GetUglyNumber_Solution(int N)
|
||||||
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。
|
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int FirstNotRepeatingChar(String str)
|
public int FirstNotRepeatingChar(String str) {
|
||||||
{
|
|
||||||
int[] cnts = new int[256];
|
int[] cnts = new int[256];
|
||||||
for (int i = 0; i < str.length(); i++)
|
for (int i = 0; i < str.length(); i++)
|
||||||
cnts[str.charAt(i)]++;
|
cnts[str.charAt(i)]++;
|
||||||
|
@ -2306,8 +2280,7 @@ public int FirstNotRepeatingChar(String str)
|
||||||
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
|
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int FirstNotRepeatingChar2(String str)
|
public int FirstNotRepeatingChar2(String str) {
|
||||||
{
|
|
||||||
BitSet bs1 = new BitSet(256);
|
BitSet bs1 = new BitSet(256);
|
||||||
BitSet bs2 = new BitSet(256);
|
BitSet bs2 = new BitSet(256);
|
||||||
for (char c : str.toCharArray()) {
|
for (char c : str.toCharArray()) {
|
||||||
|
@ -2339,15 +2312,13 @@ public int FirstNotRepeatingChar2(String str)
|
||||||
private long cnt = 0;
|
private long cnt = 0;
|
||||||
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
|
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
|
||||||
|
|
||||||
public int InversePairs(int[] nums)
|
public int InversePairs(int[] nums) {
|
||||||
{
|
|
||||||
tmp = new int[nums.length];
|
tmp = new int[nums.length];
|
||||||
mergeSort(nums, 0, nums.length - 1);
|
mergeSort(nums, 0, nums.length - 1);
|
||||||
return (int) (cnt % 1000000007);
|
return (int) (cnt % 1000000007);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mergeSort(int[] nums, int l, int h)
|
private void mergeSort(int[] nums, int l, int h) {
|
||||||
{
|
|
||||||
if (h - l < 1)
|
if (h - l < 1)
|
||||||
return;
|
return;
|
||||||
int m = l + (h - l) / 2;
|
int m = l + (h - l) / 2;
|
||||||
|
@ -2356,8 +2327,7 @@ private void mergeSort(int[] nums, int l, int h)
|
||||||
merge(nums, l, m, h);
|
merge(nums, l, m, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void merge(int[] nums, int l, int m, int h)
|
private void merge(int[] nums, int l, int m, int h) {
|
||||||
{
|
|
||||||
int i = l, j = m + 1, k = l;
|
int i = l, j = m + 1, k = l;
|
||||||
while (i <= m || j <= h) {
|
while (i <= m || j <= h) {
|
||||||
if (i > m)
|
if (i > m)
|
||||||
|
@ -2392,8 +2362,7 @@ private void merge(int[] nums, int l, int m, int h)
|
||||||
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
|
当访问 A 链表的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问 B 链表的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2)
|
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
|
||||||
{
|
|
||||||
ListNode l1 = pHead1, l2 = pHead2;
|
ListNode l1 = pHead1, l2 = pHead2;
|
||||||
while (l1 != l2) {
|
while (l1 != l2) {
|
||||||
l1 = (l1 == null) ? pHead2 : l1.next;
|
l1 = (l1 == null) ? pHead2 : l1.next;
|
||||||
|
@ -2420,15 +2389,13 @@ Output:
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int GetNumberOfK(int[] nums, int K)
|
public int GetNumberOfK(int[] nums, int K) {
|
||||||
{
|
|
||||||
int first = binarySearch(nums, K);
|
int first = binarySearch(nums, K);
|
||||||
int last = binarySearch(nums, K + 1);
|
int last = binarySearch(nums, K + 1);
|
||||||
return (first == nums.length || nums[first] != K) ? 0 : last - first;
|
return (first == nums.length || nums[first] != K) ? 0 : last - first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int binarySearch(int[] nums, int K)
|
private int binarySearch(int[] nums, int K) {
|
||||||
{
|
|
||||||
int l = 0, h = nums.length;
|
int l = 0, h = nums.length;
|
||||||
while (l < h) {
|
while (l < h) {
|
||||||
int m = l + (h - l) / 2;
|
int m = l + (h - l) / 2;
|
||||||
|
@ -2453,14 +2420,12 @@ private int binarySearch(int[] nums, int K)
|
||||||
private TreeNode ret;
|
private TreeNode ret;
|
||||||
private int cnt = 0;
|
private int cnt = 0;
|
||||||
|
|
||||||
public TreeNode KthNode(TreeNode pRoot, int k)
|
public TreeNode KthNode(TreeNode pRoot, int k) {
|
||||||
{
|
|
||||||
inOrder(pRoot, k);
|
inOrder(pRoot, k);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inOrder(TreeNode root, int k)
|
private void inOrder(TreeNode root, int k) {
|
||||||
{
|
|
||||||
if (root == null || cnt >= k)
|
if (root == null || cnt >= k)
|
||||||
return;
|
return;
|
||||||
inOrder(root.left, k);
|
inOrder(root.left, k);
|
||||||
|
@ -2484,8 +2449,7 @@ private void inOrder(TreeNode root, int k)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int TreeDepth(TreeNode root)
|
public int TreeDepth(TreeNode root) {
|
||||||
{
|
|
||||||
return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
|
return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -2505,14 +2469,12 @@ public int TreeDepth(TreeNode root)
|
||||||
```java
|
```java
|
||||||
private boolean isBalanced = true;
|
private boolean isBalanced = true;
|
||||||
|
|
||||||
public boolean IsBalanced_Solution(TreeNode root)
|
public boolean IsBalanced_Solution(TreeNode root) {
|
||||||
{
|
|
||||||
height(root);
|
height(root);
|
||||||
return isBalanced;
|
return isBalanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int height(TreeNode root)
|
private int height(TreeNode root) {
|
||||||
{
|
|
||||||
if (root == null || !isBalanced)
|
if (root == null || !isBalanced)
|
||||||
return 0;
|
return 0;
|
||||||
int left = height(root.left);
|
int left = height(root.left);
|
||||||
|
@ -2538,8 +2500,7 @@ private int height(TreeNode root)
|
||||||
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
|
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
|
public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
|
||||||
{
|
|
||||||
int diff = 0;
|
int diff = 0;
|
||||||
for (int num : nums)
|
for (int num : nums)
|
||||||
diff ^= num;
|
diff ^= num;
|
||||||
|
@ -2570,8 +2531,7 @@ public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
|
||||||
- 如果 sum < target,移动较小的元素,使 sum 变大一些。
|
- 如果 sum < target,移动较小的元素,使 sum 变大一些。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum)
|
public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum) {
|
||||||
{
|
|
||||||
int i = 0, j = array.length - 1;
|
int i = 0, j = array.length - 1;
|
||||||
while (i < j) {
|
while (i < j) {
|
||||||
int cur = array[i] + array[j];
|
int cur = array[i] + array[j];
|
||||||
|
@ -2604,8 +2564,7 @@ public ArrayList<Integer> FindNumbersWithSum(int[] array, int sum)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum)
|
public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum) {
|
||||||
{
|
|
||||||
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
|
||||||
int start = 1, end = 2;
|
int start = 1, end = 2;
|
||||||
int curSum = 3;
|
int curSum = 3;
|
||||||
|
@ -2648,8 +2607,7 @@ public ArrayList<ArrayList<Integer>> FindContinuousSequence(int sum)
|
||||||
正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
|
正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public String ReverseSentence(String str)
|
public String ReverseSentence(String str) {
|
||||||
{
|
|
||||||
int n = str.length();
|
int n = str.length();
|
||||||
char[] chars = str.toCharArray();
|
char[] chars = str.toCharArray();
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
|
@ -2664,14 +2622,12 @@ public String ReverseSentence(String str)
|
||||||
return new String(chars);
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reverse(char[] c, int i, int j)
|
private void reverse(char[] c, int i, int j) {
|
||||||
{
|
|
||||||
while (i < j)
|
while (i < j)
|
||||||
swap(c, i++, j--);
|
swap(c, i++, j--);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swap(char[] c, int i, int j)
|
private void swap(char[] c, int i, int j) {
|
||||||
{
|
|
||||||
char t = c[i];
|
char t = c[i];
|
||||||
c[i] = c[j];
|
c[i] = c[j];
|
||||||
c[j] = t;
|
c[j] = t;
|
||||||
|
@ -2691,8 +2647,7 @@ private void swap(char[] c, int i, int j)
|
||||||
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
|
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public String LeftRotateString(String str, int n)
|
public String LeftRotateString(String str, int n) {
|
||||||
{
|
|
||||||
if (n >= str.length())
|
if (n >= str.length())
|
||||||
return str;
|
return str;
|
||||||
char[] chars = str.toCharArray();
|
char[] chars = str.toCharArray();
|
||||||
|
@ -2702,14 +2657,12 @@ public String LeftRotateString(String str, int n)
|
||||||
return new String(chars);
|
return new String(chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reverse(char[] chars, int i, int j)
|
private void reverse(char[] chars, int i, int j) {
|
||||||
{
|
|
||||||
while (i < j)
|
while (i < j)
|
||||||
swap(chars, i++, j--);
|
swap(chars, i++, j--);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void swap(char[] chars, int i, int j)
|
private void swap(char[] chars, int i, int j) {
|
||||||
{
|
|
||||||
char t = chars[i];
|
char t = chars[i];
|
||||||
chars[i] = chars[j];
|
chars[i] = chars[j];
|
||||||
chars[j] = t;
|
chars[j] = t;
|
||||||
|
@ -2727,8 +2680,7 @@ private void swap(char[] chars, int i, int j)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public ArrayList<Integer> maxInWindows(int[] num, int size)
|
public ArrayList<Integer> maxInWindows(int[] num, int size) {
|
||||||
{
|
|
||||||
ArrayList<Integer> ret = new ArrayList<>();
|
ArrayList<Integer> ret = new ArrayList<>();
|
||||||
if (size > num.length || size < 1)
|
if (size > num.length || size < 1)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2762,8 +2714,7 @@ public ArrayList<Integer> maxInWindows(int[] num, int size)
|
||||||
空间复杂度:O(N<sup>2</sup>)
|
空间复杂度:O(N<sup>2</sup>)
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public List<Map.Entry<Integer, Double>> dicesSum(int n)
|
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
|
||||||
{
|
|
||||||
final int face = 6;
|
final int face = 6;
|
||||||
final int pointNum = face * n;
|
final int pointNum = face * n;
|
||||||
long[][] dp = new long[n + 1][pointNum + 1];
|
long[][] dp = new long[n + 1][pointNum + 1];
|
||||||
|
@ -2790,8 +2741,7 @@ public List<Map.Entry<Integer, Double>> dicesSum(int n)
|
||||||
空间复杂度:O(N)
|
空间复杂度:O(N)
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public List<Map.Entry<Integer, Double>> dicesSum(int n)
|
public List<Map.Entry<Integer, Double>> dicesSum(int n) {
|
||||||
{
|
|
||||||
final int face = 6;
|
final int face = 6;
|
||||||
final int pointNum = face * n;
|
final int pointNum = face * n;
|
||||||
long[][] dp = new long[2][pointNum + 1];
|
long[][] dp = new long[2][pointNum + 1];
|
||||||
|
@ -2829,8 +2779,7 @@ public List<Map.Entry<Integer, Double>> dicesSum(int n)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public boolean isContinuous(int[] nums)
|
public boolean isContinuous(int[] nums) {
|
||||||
{
|
|
||||||
if (nums.length < 5)
|
if (nums.length < 5)
|
||||||
return false;
|
return false;
|
||||||
Arrays.sort(nums);
|
Arrays.sort(nums);
|
||||||
|
@ -2861,8 +2810,7 @@ public boolean isContinuous(int[] nums)
|
||||||
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
|
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int LastRemaining_Solution(int n, int m)
|
public int LastRemaining_Solution(int n, int m) {
|
||||||
{
|
|
||||||
if (n == 0) /* 特殊输入的处理 */
|
if (n == 0) /* 特殊输入的处理 */
|
||||||
return -1;
|
return -1;
|
||||||
if (n == 1) /* 返回条件 */
|
if (n == 1) /* 返回条件 */
|
||||||
|
@ -2884,8 +2832,7 @@ public int LastRemaining_Solution(int n, int m)
|
||||||
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
|
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int maxProfit(int[] prices)
|
public int maxProfit(int[] prices) {
|
||||||
{
|
|
||||||
if (prices == null || prices.length == 0)
|
if (prices == null || prices.length == 0)
|
||||||
return 0;
|
return 0;
|
||||||
int soFarMin = prices[0];
|
int soFarMin = prices[0];
|
||||||
|
@ -2915,8 +2862,7 @@ public int maxProfit(int[] prices)
|
||||||
以下实现中,递归的返回条件为 n <= 0,取非后就是 n > 0,递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
|
以下实现中,递归的返回条件为 n <= 0,取非后就是 n > 0,递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int Sum_Solution(int n)
|
public int Sum_Solution(int n) {
|
||||||
{
|
|
||||||
int sum = n;
|
int sum = n;
|
||||||
boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
|
boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
|
||||||
return sum;
|
return sum;
|
||||||
|
@ -2938,8 +2884,7 @@ a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进
|
||||||
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
|
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int Add(int a, int b)
|
public int Add(int a, int b) {
|
||||||
{
|
|
||||||
return b == 0 ? a : Add(a ^ b, (a & b) << 1);
|
return b == 0 ? a : Add(a ^ b, (a & b) << 1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -2955,8 +2900,7 @@ public int Add(int a, int b)
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int[] multiply(int[] A)
|
public int[] multiply(int[] A) {
|
||||||
{
|
|
||||||
int n = A.length;
|
int n = A.length;
|
||||||
int[] B = new int[n];
|
int[] B = new int[n];
|
||||||
for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */
|
for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */
|
||||||
|
@ -2988,8 +2932,7 @@ Output:
|
||||||
## 解题思路
|
## 解题思路
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public int StrToInt(String str)
|
public int StrToInt(String str) {
|
||||||
{
|
|
||||||
if (str == null || str.length() == 0)
|
if (str == null || str.length() == 0)
|
||||||
return 0;
|
return 0;
|
||||||
boolean isNegative = str.charAt(0) == '-';
|
boolean isNegative = str.charAt(0) == '-';
|
||||||
|
|
|
@ -46,17 +46,17 @@
|
||||||
|
|
||||||
例如在注册流程中通常需要发送验证邮件来确保注册用户的身份合法,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。
|
例如在注册流程中通常需要发送验证邮件来确保注册用户的身份合法,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。
|
||||||
|
|
||||||
只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成操作的话,就不能再使用消息队列。
|
只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成注册的话,就不能再使用消息队列。
|
||||||
|
|
||||||
## 流量削锋
|
## 流量削锋
|
||||||
|
|
||||||
在高并发的场景下,如果短时间有大量的请求会压垮服务器。
|
在高并发的场景下,如果短时间有大量的请求到达会压垮服务器。
|
||||||
|
|
||||||
可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。
|
可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。
|
||||||
|
|
||||||
## 应用解耦
|
## 应用解耦
|
||||||
|
|
||||||
如果模块之间不直接进行调用,模块之间耦合度很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。
|
如果模块之间不直接进行调用,模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。
|
||||||
|
|
||||||
通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。
|
通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。
|
||||||
|
|
||||||
|
|
30
notes/算法.md
30
notes/算法.md
|
@ -1623,10 +1623,10 @@ private List<Key> keys(Node x, Key l, Key h) {
|
||||||
|
|
||||||
## 2-3 查找树
|
## 2-3 查找树
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
|
|
||||||
|
|
||||||
2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。
|
2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//ff396233-1bb1-4e74-8bc2-d7c90146f5dd.png" width="250"/> </div><br>
|
||||||
|
|
||||||
### 1. 插入操作
|
### 1. 插入操作
|
||||||
|
|
||||||
插入操作和 BST 的插入操作有很大区别,BST 的插入操作是先进行一次未命中的查找,然后再将节点插入到对应的空链接上。但是 2-3 查找树如果也这么做的话,那么就会破坏了平衡性。它是将新节点插入到叶子节点上。
|
插入操作和 BST 的插入操作有很大区别,BST 的插入操作是先进行一次未命中的查找,然后再将节点插入到对应的空链接上。但是 2-3 查找树如果也这么做的话,那么就会破坏了平衡性。它是将新节点插入到叶子节点上。
|
||||||
|
@ -2042,24 +2042,26 @@ public class SparseVector {
|
||||||
|
|
||||||
## 汉诺塔
|
## 汉诺塔
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
|
||||||
|
|
||||||
这是一个经典的递归问题,分为三步求解:
|
这是一个经典的递归问题,分为三步求解:
|
||||||
|
|
||||||
1. 将 n-1 个圆盘从 from -> buffer
|
- 将 n-1 个圆盘从 from -> buffer
|
||||||
2. 将 1 个圆盘从 from -> to
|
|
||||||
3. 将 n-1 个圆盘从 buffer -> to
|
<div align="center"> <img src="../pics//8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
|
||||||
|
|
||||||
|
- 将 1 个圆盘从 from -> to
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
|
||||||
|
|
||||||
|
- 将 n-1 个圆盘从 buffer -> to
|
||||||
|
|
||||||
|
<div align="center"> <img src="../pics//1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
|
||||||
|
|
||||||
如果只有一个圆盘,那么只需要进行一次移动操作。
|
如果只有一个圆盘,那么只需要进行一次移动操作。
|
||||||
|
|
||||||
从上面的讨论可以知道,a<sub>n</sub> = 2 * a<sub>n-1</sub> + 1,显然 a<sub>n</sub> = 2<sup>n</sup> - 1,n 个圆盘需要移动 2<sup>n</sup> - 1 次。
|
从上面的讨论可以知道,a<sub>n</sub> = 2 * a<sub>n-1</sub> + 1,显然 a<sub>n</sub> = 2<sup>n</sup> - 1,n 个圆盘需要移动 2<sup>n</sup> - 1 次。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//54f1e052-0596-4b5e-833c-e80d75bf3f9b.png" width="300"/> </div><br>
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//8587132a-021d-4f1f-a8ec-5a9daa7157a7.png" width="300"/> </div><br>
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//2861e923-4862-4526-881c-15529279d49c.png" width="300"/> </div><br>
|
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//1c4e8185-8153-46b6-bd5a-288b15feeae6.png" width="300"/> </div><br>
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Hanoi {
|
public class Hanoi {
|
||||||
public static void move(int n, String from, String buffer, String to) {
|
public static void move(int n, String from, String buffer, String to) {
|
||||||
|
@ -2105,7 +2107,7 @@ from H1 to H3
|
||||||
|
|
||||||
生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到根节点,根节点代表的字符的编码就是这个路径编码。
|
生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到根节点,根节点代表的字符的编码就是这个路径编码。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//3ff4f00a-2321-48fd-95f4-ce6001332151.png"/> </div><br>
|
<div align="center"> <img src="../pics//3ff4f00a-2321-48fd-95f4-ce6001332151.png" width="400"/> </div><br>
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Huffman {
|
public class Huffman {
|
||||||
|
|
13
notes/缓存.md
13
notes/缓存.md
|
@ -163,7 +163,11 @@ public class LRU<K, V> implements Iterable<K> {
|
||||||
|
|
||||||
## 分布式缓存
|
## 分布式缓存
|
||||||
|
|
||||||
使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。不仅如此,服务器集群都可以访问分布式缓存,本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。
|
使用 Redis、Memcache 等分布式缓存将数据缓存在分布式缓存系统中。
|
||||||
|
|
||||||
|
相对于本地缓存来说,分布式缓存单独部署,可以根据需求分配硬件资源。
|
||||||
|
|
||||||
|
不仅如此,服务器集群都可以访问分布式缓存。而本地缓存需要在服务器集群之间进行同步,实现和性能开销上都非常大。
|
||||||
|
|
||||||
## 数据库缓存
|
## 数据库缓存
|
||||||
|
|
||||||
|
@ -171,7 +175,7 @@ MySQL 等数据库管理系统具有自己的查询缓存机制来提高 SQL 查
|
||||||
|
|
||||||
# 四、CDN
|
# 四、CDN
|
||||||
|
|
||||||
内容分发网络(Content distribution network,CDN)是一种通过互连的网络系统,利用更靠近用户的服务器更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源其它数据分发给用户。
|
内容分发网络(Content distribution network,CDN)是一种通过互连的网络系统,利用更靠近用户的服务器更快更可靠地将 HTML、CSS、JavaScript、音乐、图片、视频等静态资源分发给用户。
|
||||||
|
|
||||||
CDN 主要有以下优点:
|
CDN 主要有以下优点:
|
||||||
|
|
||||||
|
@ -190,7 +194,7 @@ CDN 主要有以下优点:
|
||||||
|
|
||||||
## 缓存穿透
|
## 缓存穿透
|
||||||
|
|
||||||
指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存来到数据库。
|
指的是对某个一定不存在的数据进行请求,该请求将会穿透缓存到达数据库。
|
||||||
|
|
||||||
解决方案:
|
解决方案:
|
||||||
|
|
||||||
|
@ -207,6 +211,7 @@ CDN 主要有以下优点:
|
||||||
|
|
||||||
- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现;
|
- 为了防止缓存在同一时间大面积过期导致的缓存雪崩,可以通过观察用户行为,合理设置缓存过期时间来实现;
|
||||||
- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。
|
- 为了防止缓存服务器宕机出现的缓存雪崩,可以使用分布式缓存,分布式缓存中每一个节点只缓存部分的数据,当某个节点宕机时可以保证其它节点的缓存仍然可用。
|
||||||
|
- 也可以在进行缓存预热,避免在系统刚启动不久由于还未将大量数据进行缓存而导致缓存雪崩。
|
||||||
|
|
||||||
## 缓存一致性
|
## 缓存一致性
|
||||||
|
|
||||||
|
@ -258,7 +263,7 @@ Distributed Hash Table(DHT) 是一种哈希分布方式,其目的是为了
|
||||||
|
|
||||||
上面描述的一致性哈希存在数据分布不均匀的问题,节点存储的数据量有可能会存在很大的不同。
|
上面描述的一致性哈希存在数据分布不均匀的问题,节点存储的数据量有可能会存在很大的不同。
|
||||||
|
|
||||||
数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真是节点号,从而使得数据分布也更加均匀。
|
数据不均匀主要是因为节点在哈希环上分布的不均匀,这种情况在节点数量很少的情况下尤其明显。解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得大,那么虚拟节点在哈希环上分布的均匀性就会比原来的真是节点好,从而使得数据分布也更加均匀。
|
||||||
|
|
||||||
参考资料:
|
参考资料:
|
||||||
|
|
||||||
|
|
|
@ -1851,11 +1851,7 @@ No gumball dispensed
|
||||||
|
|
||||||
### 与状态模式的比较
|
### 与状态模式的比较
|
||||||
|
|
||||||
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。
|
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
|
||||||
|
|
||||||
但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。
|
|
||||||
|
|
||||||
所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
|
|
||||||
|
|
||||||
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
|
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
|
||||||
|
|
||||||
|
@ -1969,7 +1965,7 @@ public abstract class CaffeineBeverage {
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Coffee extends CaffeineBeverage{
|
public class Coffee extends CaffeineBeverage {
|
||||||
@Override
|
@Override
|
||||||
void brew() {
|
void brew() {
|
||||||
System.out.println("Coffee.brew");
|
System.out.println("Coffee.brew");
|
||||||
|
@ -1983,7 +1979,7 @@ public class Coffee extends CaffeineBeverage{
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Tea extends CaffeineBeverage{
|
public class Tea extends CaffeineBeverage {
|
||||||
@Override
|
@Override
|
||||||
void brew() {
|
void brew() {
|
||||||
System.out.println("Tea.brew");
|
System.out.println("Tea.brew");
|
||||||
|
@ -2238,7 +2234,7 @@ Number of items: 6
|
||||||
|
|
||||||
### 意图
|
### 意图
|
||||||
|
|
||||||
使用什么都不做的空对象来替代 NULL。
|
使用什么都不做的空对象来代替 NULL。
|
||||||
|
|
||||||
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
|
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
|
||||||
|
|
||||||
|
@ -2393,7 +2389,7 @@ public abstract class TV {
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Sony extends TV{
|
public class Sony extends TV {
|
||||||
@Override
|
@Override
|
||||||
public void on() {
|
public void on() {
|
||||||
System.out.println("Sony.on()");
|
System.out.println("Sony.on()");
|
||||||
|
@ -2412,7 +2408,7 @@ public class Sony extends TV{
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class RCA extends TV{
|
public class RCA extends TV {
|
||||||
@Override
|
@Override
|
||||||
public void on() {
|
public void on() {
|
||||||
System.out.println("RCA.on()");
|
System.out.println("RCA.on()");
|
||||||
|
@ -2551,9 +2547,6 @@ public abstract class Component {
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Composite extends Component {
|
public class Composite extends Component {
|
||||||
|
|
||||||
private List<Component> child;
|
private List<Component> child;
|
||||||
|
@ -2659,7 +2652,7 @@ Composite:root
|
||||||
|
|
||||||
### 类图
|
### 类图
|
||||||
|
|
||||||
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
|
装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//137c593d-0a9e-47b8-a9e6-b71f540b82dd.png"/> </div><br>
|
<div align="center"> <img src="../pics//137c593d-0a9e-47b8-a9e6-b71f540b82dd.png"/> </div><br>
|
||||||
|
|
||||||
|
@ -2770,7 +2763,7 @@ public class Client {
|
||||||
|
|
||||||
### 实现
|
### 实现
|
||||||
|
|
||||||
观看电影需要操作很多电器,使用外观模式可以实现一键看电影功能。
|
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class SubSystem {
|
public class SubSystem {
|
||||||
|
@ -2811,7 +2804,7 @@ public class Client {
|
||||||
|
|
||||||
### 设计原则
|
### 设计原则
|
||||||
|
|
||||||
最少知识原则:只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
|
最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
|
||||||
|
|
||||||
## 6. 享元(Flyweight)
|
## 6. 享元(Flyweight)
|
||||||
|
|
||||||
|
@ -2822,8 +2815,8 @@ public class Client {
|
||||||
### 类图
|
### 类图
|
||||||
|
|
||||||
- Flyweight:享元对象
|
- Flyweight:享元对象
|
||||||
- IntrinsicState:内部状态,相同的项元对象共享
|
- IntrinsicState:内部状态,享元对象共享内部状态
|
||||||
- ExtrinsicState:外部状态
|
- ExtrinsicState:外部状态,每个享元对象的外部状态不同
|
||||||
|
|
||||||
<div align="center"> <img src="../pics//d52270b4-9097-4667-9f18-f405fc661c99.png"/> </div><br>
|
<div align="center"> <img src="../pics//d52270b4-9097-4667-9f18-f405fc661c99.png"/> </div><br>
|
||||||
|
|
||||||
|
@ -2854,8 +2847,6 @@ public class ConcreteFlyweight implements Flyweight {
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class FlyweightFactory {
|
public class FlyweightFactory {
|
||||||
|
|
||||||
private HashMap<String, Flyweight> flyweights = new HashMap<>();
|
private HashMap<String, Flyweight> flyweights = new HashMap<>();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user