diff --git a/notes/HTTP.md b/notes/HTTP.md
index 243dc283..537b24fe 100644
--- a/notes/HTTP.md
+++ b/notes/HTTP.md
@@ -45,7 +45,7 @@
* [二进制分帧层](#二进制分帧层)
* [服务端推送](#服务端推送)
* [首部压缩](#首部压缩)
-* [八、GET 和 POST 的区别](#八get-和-post-的区别)
+* [八、GET 和 POST 比较](#八get-和-post-比较)
* [作用](#作用)
* [参数](#参数)
* [安全](#安全)
@@ -61,13 +61,13 @@
## 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(Uniform Resource Identifier,统一资源标识符)
+- URL(Uniform Resource Locator,统一资源定位符)
+- URN(Uniform Resource Name,统一资源名称)
+
+
## 请求和响应报文
@@ -197,7 +197,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
-- **206 Partial Content** :表示客户端进行了范围请求。响应报文包含由 Content-Range 指定范围的实体内容。
+- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
## 3XX 重定向
@@ -219,7 +219,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
-- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。
+- **403 Forbidden** :请求被拒绝。
- **404 Not Found**
@@ -331,7 +331,7 @@ Set-Cookie: tasty_cookie=strawberry
[page content]
```
-客户端之后对同一个服务器发送请求时,会从浏览器中读出 Cookie 信息通过 Cookie 请求首部字段发送给服务器。
+客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。
```html
GET /sample_page.html HTTP/1.1
@@ -382,9 +382,9 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
-Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在内存型数据库中,比如 Redis,效率会更高。
+Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
-使用 Session 维护用户登录的过程如下:
+使用 Session 维护用户登录状态的过程如下:
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
- 服务器验证该用户名和密码;
@@ -499,7 +499,7 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
### 1. 短连接与长连接
-当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。
+当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
@@ -688,7 +688,7 @@ HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
### 3. HTTPs 采用的加密方式
-HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。(下图中的 Session Key 就是对称密钥)
+HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥)
@@ -717,7 +717,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
## HTTPs 的缺点
- 因为需要进行加密解密等过程,因此速度会更慢;
-- 需要支付证书授权的高费用。
+- 需要支付证书授权的高额费用。
## 配置 HTTPs
@@ -727,7 +727,7 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
## HTTP/1.x 缺陷
- HTTP/1.x 实现简单是以牺牲应用性能为代价的:
+ HTTP/1.x 实现简单是以牺牲性能为代价的:
- 客户端需要使用多个连接才能实现并发和缩短延迟;
- 不会压缩请求和响应首部,从而导致不必要的网络流量;
@@ -763,7 +763,7 @@ HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见
-# 八、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)
- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn)
- [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)
- [浅谈 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/)
diff --git a/notes/Java 容器.md b/notes/Java 容器.md
index bf1921fc..af118e4c 100644
--- a/notes/Java 容器.md
+++ b/notes/Java 容器.md
@@ -25,7 +25,7 @@
## Collection
-
+
### 1. Set
@@ -129,12 +129,67 @@ private static final int DEFAULT_CAPACITY = 10;
ArrayList 基于数组实现,并且具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。
-保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
+保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。
```java
transient Object[] elementData; // non-private to simplify nested class access
```
+ArrayList 实现了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
+
+```java
+private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ elementData = EMPTY_ELEMENTDATA;
+
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in capacity
+ s.readInt(); // ignored
+
+ if (size > 0) {
+ // be like clone(), allocate array based upon size not capacity
+ ensureCapacityInternal(size);
+
+ Object[] a = elementData;
+ // Read in all elements in the proper order.
+ for (int i=0; i> 1)`,也就是旧容量的 1.5 倍。
@@ -1080,7 +1135,7 @@ Set <|.. LinkedHashSet
SortSet <|.. TreeSet
List <|.. ArrayList
List <|.. Vector
-List <|.. LinkeList
+List <|.. LinkedList
Queue <|.. LinkedList
Queue <|.. PriorityQueue
diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md
index 2813ce5d..44f3d9dc 100644
--- a/notes/Leetcode 题解.md
+++ b/notes/Leetcode 题解.md
@@ -1,18 +1,18 @@
* [算法思想](#算法思想)
- * [贪心思想](#贪心思想)
* [双指针](#双指针)
* [排序](#排序)
* [快速选择](#快速选择)
* [堆排序](#堆排序)
* [桶排序](#桶排序)
* [荷兰国旗问题](#荷兰国旗问题)
+ * [贪心思想](#贪心思想)
* [二分查找](#二分查找)
+ * [分治](#分治)
* [搜索](#搜索)
* [BFS](#bfs)
* [DFS](#dfs)
* [Backtracking](#backtracking)
- * [分治](#分治)
* [动态规划](#动态规划)
* [斐波那契数列](#斐波那契数列)
* [矩阵路径](#矩阵路径)
@@ -33,10 +33,6 @@
* [多数投票问题](#多数投票问题)
* [其它](#其它)
* [数据结构相关](#数据结构相关)
- * [栈和队列](#栈和队列)
- * [哈希表](#哈希表)
- * [字符串](#字符串)
- * [数组与矩阵](#数组与矩阵)
* [链表](#链表)
* [树](#树)
* [递归](#递归)
@@ -44,6 +40,10 @@
* [前中后序遍历](#前中后序遍历)
* [BST](#bst)
* [Trie](#trie)
+ * [栈和队列](#栈和队列)
+ * [哈希表](#哈希表)
+ * [字符串](#字符串)
+ * [数组与矩阵](#数组与矩阵)
* [图](#图)
* [二分图](#二分图)
* [拓扑排序](#拓扑排序)
@@ -55,9 +55,449 @@
# 算法思想
+## 双指针
+
+双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。
+
+**有序数组的 Two Sum**
+
+[Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/)
+
+```html
+Input: numbers={2, 7, 11, 15}, target=9
+Output: index1=1, index2=2
+```
+
+题目描述:在有序数组中找出两个数,使它们的和为 target。
+
+使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
+
+- 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
+- 如果 sum > target,移动较大的元素,使 sum 变小一些;
+- 如果 sum < target,移动较小的元素,使 sum 变大一些。
+
+```java
+public int[] twoSum(int[] numbers, int target) {
+ int i = 0, j = numbers.length - 1;
+ while (i < j) {
+ int sum = numbers[i] + numbers[j];
+ if (sum == target) {
+ return new int[]{i + 1, j + 1};
+ } else if (sum < target) {
+ i++;
+ } else {
+ j--;
+ }
+ }
+ return null;
+}
+```
+
+**两数平方和**
+
+[633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/)
+
+```html
+Input: 5
+Output: True
+Explanation: 1 * 1 + 2 * 2 = 5
+```
+
+题目描述:判断一个数是否为两个数的平方和,例如 5 = 12 + 22。
+
+```java
+public boolean judgeSquareSum(int c) {
+ int i = 0, j = (int) Math.sqrt(c);
+ while (i <= j) {
+ int powSum = i * i + j * j;
+ if (powSum == c) {
+ return true;
+ } else if (powSum > c) {
+ j--;
+ } else {
+ i++;
+ }
+ }
+ return false;
+}
+```
+
+**反转字符串中的元音字符**
+
+[345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/)
+
+```html
+Given s = "leetcode", return "leotcede".
+```
+
+使用双指针,指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。
+
+```java
+private final static HashSet vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
+
+public String reverseVowels(String s) {
+ int i = 0, j = s.length() - 1;
+ char[] result = new char[s.length()];
+ while (i <= j) {
+ char ci = s.charAt(i);
+ char cj = s.charAt(j);
+ if (!vowels.contains(ci)) {
+ result[i++] = ci;
+ } else if (!vowels.contains(cj)) {
+ result[j--] = cj;
+ } else {
+ result[i++] = cj;
+ result[j--] = ci;
+ }
+ }
+ return new String(result);
+}
+```
+
+**回文字符串**
+
+[680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/)
+
+```html
+Input: "abca"
+Output: True
+Explanation: You could delete the character 'c'.
+```
+
+题目描述:可以删除一个字符,判断是否能构成回文字符串。
+
+```java
+public boolean validPalindrome(String s) {
+ int i = -1, j = s.length();
+ while (++i < --j) {
+ if (s.charAt(i) != s.charAt(j)) {
+ return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
+ }
+ }
+ return true;
+}
+
+private boolean isPalindrome(String s, int i, int j) {
+ while (i < j) {
+ if (s.charAt(i++) != s.charAt(j--)) {
+ return false;
+ }
+ }
+ return true;
+}
+```
+
+**归并两个有序数组**
+
+[88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/)
+
+```html
+Input:
+nums1 = [1,2,3,0,0,0], m = 3
+nums2 = [2,5,6], n = 3
+
+Output: [1,2,2,3,5,6]
+```
+
+题目描述:把归并结果存到第一个数组上。
+
+需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。
+
+```java
+public void merge(int[] nums1, int m, int[] nums2, int n) {
+ int index1 = m - 1, index2 = n - 1;
+ int indexMerge = m + n - 1;
+ while (index1 >= 0 || index2 >= 0) {
+ if (index1 < 0) {
+ nums1[indexMerge--] = nums2[index2--];
+ } else if (index2 < 0) {
+ nums1[indexMerge--] = nums1[index1--];
+ } else if (nums1[index1] > nums2[index2]) {
+ nums1[indexMerge--] = nums1[index1--];
+ } else {
+ nums1[indexMerge--] = nums2[index2--];
+ }
+ }
+}
+```
+
+**判断链表是否存在环**
+
+[141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/)
+
+使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。
+
+```java
+public boolean hasCycle(ListNode head) {
+ if (head == null) {
+ return false;
+ }
+ ListNode l1 = head, l2 = head.next;
+ while (l1 != null && l2 != null && l2.next != null) {
+ if (l1 == l2) {
+ return true;
+ }
+ l1 = l1.next;
+ l2 = l2.next.next;
+ }
+ return false;
+}
+```
+
+**最长子序列**
+
+[524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/)
+
+```
+Input:
+s = "abpcplea", d = ["ale","apple","monkey","plea"]
+
+Output:
+"apple"
+```
+
+题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最大字符串。
+
+```java
+public String findLongestWord(String s, List d) {
+ String longestWord = "";
+ for (String target : d) {
+ int l1 = longestWord.length(), l2 = target.length();
+ if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
+ continue;
+ }
+ if (isValid(s, target)) {
+ longestWord = target;
+ }
+ }
+ return longestWord;
+}
+
+private boolean isValid(String s, String target) {
+ int i = 0, j = 0;
+ while (i < s.length() && j < target.length()) {
+ if (s.charAt(i) == target.charAt(j)) {
+ j++;
+ }
+ i++;
+ }
+ return j == target.length();
+}
+```
+
+## 排序
+
+### 快速选择
+
+用于求解 **Kth Element** 问题,使用快速排序的 partition() 进行实现。
+
+需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。
+
+### 堆排序
+
+用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。
+
+堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。
+
+快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。
+
+可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。
+
+**Kth Element**
+
+[215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/)
+
+**排序** :时间复杂度 O(NlogN),空间复杂度 O(1)
+
+```java
+public int findKthLargest(int[] nums, int k) {
+ Arrays.sort(nums);
+ return nums[nums.length - k];
+}
+```
+
+**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。
+
+```java
+public int findKthLargest(int[] nums, int k) {
+ PriorityQueue pq = new PriorityQueue<>(); // 小顶堆
+ for (int val : nums) {
+ pq.add(val);
+ if (pq.size() > k) // 维护堆的大小为 K
+ pq.poll();
+ }
+ return pq.peek();
+}
+```
+
+**快速选择** :时间复杂度 O(N),空间复杂度 O(1)
+
+```java
+public int findKthLargest(int[] nums, int k) {
+ k = nums.length - k;
+ int l = 0, h = nums.length - 1;
+ while (l < h) {
+ int j = partition(nums, l, h);
+ if (j == k) {
+ break;
+ } else if (j < k) {
+ l = j + 1;
+ } else {
+ h = j - 1;
+ }
+ }
+ return nums[k];
+}
+
+private int partition(int[] a, int l, int h) {
+ int i = l, j = h + 1;
+ while (true) {
+ while (a[++i] < a[l] && i < h) ;
+ while (a[--j] > a[l] && j > l) ;
+ if (i >= j) {
+ break;
+ }
+ swap(a, i, j);
+ }
+ swap(a, l, j);
+ return j;
+}
+
+private void swap(int[] a, int i, int j) {
+ int t = a[i];
+ a[i] = a[j];
+ a[j] = t;
+}
+```
+
+### 桶排序
+
+**出现频率最多的 k 个数**
+
+[347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/)
+
+```html
+Given [1,1,1,2,2,3] and k = 2, return [1,2].
+```
+
+设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。
+
+把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。
+
+```java
+public List topKFrequent(int[] nums, int k) {
+ Map frequencyForNum = new HashMap<>();
+ for (int num : nums) {
+ frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1);
+ }
+ List[] buckets = new ArrayList[nums.length + 1];
+ for (int key : frequencyForNum.keySet()) {
+ int frequency = frequencyForNum.get(key);
+ if (buckets[frequency] == null) {
+ buckets[frequency] = new ArrayList<>();
+ }
+ buckets[frequency].add(key);
+ }
+ List topK = new ArrayList<>();
+ for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) {
+ if (buckets[i] != null) {
+ topK.addAll(buckets[i]);
+ }
+ }
+ return topK;
+}
+```
+
+**按照字符出现次数对字符串排序**
+
+[451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/)
+
+```html
+Input:
+"tree"
+
+Output:
+"eert"
+
+Explanation:
+'e' appears twice while 'r' and 't' both appear once.
+So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
+```
+
+```java
+public String frequencySort(String s) {
+ Map frequencyForNum = new HashMap<>();
+ for (char c : s.toCharArray())
+ frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1);
+
+ List[] frequencyBucket = new ArrayList[s.length() + 1];
+ for (char c : frequencyForNum.keySet()) {
+ int f = frequencyForNum.get(c);
+ if (frequencyBucket[f] == null) {
+ frequencyBucket[f] = new ArrayList<>();
+ }
+ frequencyBucket[f].add(c);
+ }
+ StringBuilder str = new StringBuilder();
+ for (int i = frequencyBucket.length - 1; i >= 0; i--) {
+ if (frequencyBucket[i] == null) {
+ continue;
+ }
+ for (char c : frequencyBucket[i]) {
+ for (int j = 0; j < i; j++) {
+ str.append(c);
+ }
+ }
+ }
+ return str.toString();
+}
+```
+
+### 荷兰国旗问题
+
+荷兰国旗包含三种颜色:红、白、蓝。
+
+有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。
+
+它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。
+
+
+
+**按颜色进行排序**
+
+[75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/)
+
+```html
+Input: [2,0,2,1,1,0]
+Output: [0,0,1,1,2,2]
+```
+
+题目描述:只有 0/1/2 三种颜色。
+
+```java
+public void sortColors(int[] nums) {
+ int zero = -1, one = 0, two = nums.length;
+ while (one < two) {
+ if (nums[one] == 0) {
+ swap(nums, ++zero, one++);
+ } else if (nums[one] == 2) {
+ swap(nums, --two, one);
+ } else {
+ ++one;
+ }
+ }
+}
+
+private void swap(int[] nums, int i, int j) {
+ int t = nums[i];
+ nums[i] = nums[j];
+ nums[j] = t;
+}
+```
+
## 贪心思想
-贪心思想保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
+保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
**分配饼干**
@@ -74,7 +514,7 @@ You need to output 2.
题目描述:每个孩子都有一个满足度,每个饼干都有一个大小,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。
-因为最小的孩子最容易得到满足,因此先满足最小孩子。给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。因此贪心策略
+给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。因为最小的孩子最容易得到满足,所以先满足最小的孩子。
证明:假设在某次选择中,贪心策略选择给当前满足度最小的孩子分配第 m 个饼干,第 m 个饼干为可以满足该孩子的最小饼干。假设存在一种最优策略,给该孩子分配第 n 个饼干,并且 m < n。我们可以发现,经过这一轮分配,贪心策略分配后剩下的饼干一定有一个比最优策略来得大。因此在后续的分配中,贪心策略一定能满足更多的孩子。也就是说不存在比贪心策略更优的策略,即贪心策略就是最优策略。
@@ -362,434 +802,6 @@ public int maxProfit(int[] prices) {
}
```
-## 双指针
-
-双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。
-
-**有序数组的 Two Sum**
-
-[Leetcode :167. Two Sum II - Input array is sorted (Easy)](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/)
-
-```html
-Input: numbers={2, 7, 11, 15}, target=9
-Output: index1=1, index2=2
-```
-
-题目描述:在有序数组中找出两个数,使它们的和为 target。
-
-使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。
-
-如果两个指针指向元素的和 sum == target,那么得到要求的结果;如果 sum > target,移动较大的元素,使 sum 变小一些;如果 sum < target,移动较小的元素,使 sum 变大一些。
-
-```java
-public int[] twoSum(int[] numbers, int target) {
- int i = 0, j = numbers.length - 1;
- while (i < j) {
- int sum = numbers[i] + numbers[j];
- if (sum == target) {
- return new int[]{i + 1, j + 1};
- } else if (sum < target) {
- i++;
- } else {
- j--;
- }
- }
- return null;
-}
-```
-
-**两数平方和**
-
-[633. Sum of Square Numbers (Easy)](https://leetcode.com/problems/sum-of-square-numbers/description/)
-
-```html
-Input: 5
-Output: True
-Explanation: 1 * 1 + 2 * 2 = 5
-```
-
-题目描述:判断一个数是否为两个数的平方和,例如 5 = 12 + 22。
-
-```java
-public boolean judgeSquareSum(int c) {
- int i = 0, j = (int) Math.sqrt(c);
- while (i <= j) {
- int powSum = i * i + j * j;
- if (powSum == c) {
- return true;
- } else if (powSum > c) {
- j--;
- } else {
- i++;
- }
- }
- return false;
-}
-```
-
-**反转字符串中的元音字符**
-
-[345. Reverse Vowels of a String (Easy)](https://leetcode.com/problems/reverse-vowels-of-a-string/description/)
-
-```html
-Given s = "leetcode", return "leotcede".
-```
-
-使用双指针,指向待反转的两个元音字符,一个指针从头向尾遍历,一个指针从尾到头遍历。
-
-```java
-private final static HashSet vowels = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
-
-public String reverseVowels(String s) {
- int i = 0, j = s.length() - 1;
- char[] result = new char[s.length()];
- while (i <= j) {
- char ci = s.charAt(i);
- char cj = s.charAt(j);
- if (!vowels.contains(ci)) {
- result[i++] = ci;
- } else if (!vowels.contains(cj)) {
- result[j--] = cj;
- } else {
- result[i++] = cj;
- result[j--] = ci;
- }
- }
- return new String(result);
-}
-```
-
-**回文字符串**
-
-[680. Valid Palindrome II (Easy)](https://leetcode.com/problems/valid-palindrome-ii/description/)
-
-```html
-Input: "abca"
-Output: True
-Explanation: You could delete the character 'c'.
-```
-
-题目描述:可以删除一个字符,判断是否能构成回文字符串。
-
-```java
-public boolean validPalindrome(String s) {
- int i = -1, j = s.length();
- while (++i < --j) {
- if (s.charAt(i) != s.charAt(j)) {
- return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
- }
- }
- return true;
-}
-
-private boolean isPalindrome(String s, int i, int j) {
- while (i < j) {
- if (s.charAt(i++) != s.charAt(j--)) {
- return false;
- }
- }
- return true;
-}
-```
-
-**归并两个有序数组**
-
-[88. Merge Sorted Array (Easy)](https://leetcode.com/problems/merge-sorted-array/description/)
-
-```html
-Input:
-nums1 = [1,2,3,0,0,0], m = 3
-nums2 = [2,5,6], n = 3
-
-Output: [1,2,2,3,5,6]
-```
-
-题目描述:把归并结果存到第一个数组上。
-
-需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值
-
-```java
-public void merge(int[] nums1, int m, int[] nums2, int n) {
- int index1 = m - 1, index2 = n - 1;
- int indexMerge = m + n - 1;
- while (index1 >= 0 || index2 >= 0) {
- if (index1 < 0) {
- nums1[indexMerge--] = nums2[index2--];
- } else if (index2 < 0) {
- nums1[indexMerge--] = nums1[index1--];
- } else if (nums1[index1] > nums2[index2]) {
- nums1[indexMerge--] = nums1[index1--];
- } else {
- nums1[indexMerge--] = nums2[index2--];
- }
- }
-}
-```
-
-**判断链表是否存在环**
-
-[141. Linked List Cycle (Easy)](https://leetcode.com/problems/linked-list-cycle/description/)
-
-使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。
-
-```java
-public boolean hasCycle(ListNode head) {
- if (head == null) {
- return false;
- }
- ListNode l1 = head, l2 = head.next;
- while (l1 != null && l2 != null && l2.next != null) {
- if (l1 == l2) {
- return true;
- }
- l1 = l1.next;
- l2 = l2.next.next;
- }
- return false;
-}
-```
-
-**最长子序列**
-
-[524. Longest Word in Dictionary through Deleting (Medium)](https://leetcode.com/problems/longest-word-in-dictionary-through-deleting/description/)
-
-```
-Input:
-s = "abpcplea", d = ["ale","apple","monkey","plea"]
-
-Output:
-"apple"
-```
-
-题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回按字典序排序的最大字符串。
-
-```java
-public String findLongestWord(String s, List d) {
- String longestWord = "";
- for (String target : d) {
- int l1 = longestWord.length(), l2 = target.length();
- if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
- continue;
- }
- if (isValid(s, target)) {
- longestWord = target;
- }
- }
- return longestWord;
-}
-
-private boolean isValid(String s, String target) {
- int i = 0, j = 0;
- while (i < s.length() && j < target.length()) {
- if (s.charAt(i) == target.charAt(j)) {
- j++;
- }
- i++;
- }
- return j == target.length();
-}
-```
-
-## 排序
-
-### 快速选择
-
-一般用于求解 **Kth Element** 问题,可以在 O(N) 时间复杂度,O(1) 空间复杂度完成求解工作。
-
-与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。
-
-### 堆排序
-
-堆排序用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。当然它也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。
-
-**Kth Element**
-
-[215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/)
-
-**排序** :时间复杂度 O(NlogN),空间复杂度 O(1)
-
-```java
-public int findKthLargest(int[] nums, int k) {
- Arrays.sort(nums);
- return nums[nums.length - k];
-}
-```
-
-**堆排序** :时间复杂度 O(NlogK),空间复杂度 O(K)。
-
-```java
-public int findKthLargest(int[] nums, int k) {
- PriorityQueue pq = new PriorityQueue<>(); // 小顶堆
- for (int val : nums) {
- pq.add(val);
- if (pq.size() > k) // 维护堆的大小为 K
- pq.poll();
- }
- return pq.peek();
-}
-```
-
-**快速选择** :时间复杂度 O(N),空间复杂度 O(1)
-
-```java
-public int findKthLargest(int[] nums, int k) {
- k = nums.length - k;
- int l = 0, h = nums.length - 1;
- while (l < h) {
- int j = partition(nums, l, h);
- if (j == k) {
- break;
- } else if (j < k) {
- l = j + 1;
- } else {
- h = j - 1;
- }
- }
- return nums[k];
-}
-
-private int partition(int[] a, int l, int h) {
- int i = l, j = h + 1;
- while (true) {
- while (a[++i] < a[l] && i < h) ;
- while (a[--j] > a[l] && j > l) ;
- if (i >= j) {
- break;
- }
- swap(a, i, j);
- }
- swap(a, l, j);
- return j;
-}
-
-private void swap(int[] a, int i, int j) {
- int t = a[i];
- a[i] = a[j];
- a[j] = t;
-}
-```
-
-### 桶排序
-
-**出现频率最多的 k 个数**
-
-[347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/)
-
-```html
-Given [1,1,1,2,2,3] and k = 2, return [1,2].
-```
-
-设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。
-
-```java
-public List topKFrequent(int[] nums, int k) {
- Map frequencyForNum = new HashMap<>();
- for (int num : nums) {
- frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1);
- }
- List[] buckets = new ArrayList[nums.length + 1];
- for (int key : frequencyForNum.keySet()) {
- int frequency = frequencyForNum.get(key);
- if (buckets[frequency] == null) {
- buckets[frequency] = new ArrayList<>();
- }
- buckets[frequency].add(key);
- }
- List topK = new ArrayList<>();
- for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) {
- if (buckets[i] != null) {
- topK.addAll(buckets[i]);
- }
- }
- return topK;
-}
-```
-
-**按照字符出现次数对字符串排序**
-
-[451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/)
-
-```html
-Input:
-"tree"
-
-Output:
-"eert"
-
-Explanation:
-'e' appears twice while 'r' and 't' both appear once.
-So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer.
-```
-
-```java
-public String frequencySort(String s) {
- Map frequencyForNum = new HashMap<>();
- for (char c : s.toCharArray())
- frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1);
-
- List[] frequencyBucket = new ArrayList[s.length() + 1];
- for (char c : frequencyForNum.keySet()) {
- int f = frequencyForNum.get(c);
- if (frequencyBucket[f] == null) {
- frequencyBucket[f] = new ArrayList<>();
- }
- frequencyBucket[f].add(c);
- }
- StringBuilder str = new StringBuilder();
- for (int i = frequencyBucket.length - 1; i >= 0; i--) {
- if (frequencyBucket[i] == null) {
- continue;
- }
- for (char c : frequencyBucket[i]) {
- for (int j = 0; j < i; j++) {
- str.append(c);
- }
- }
- }
- return str.toString();
-}
-```
-
-### 荷兰国旗问题
-
-荷兰国旗包含三种颜色:红、白、蓝。有这三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。
-
-它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。
-
-
-
-**按颜色进行排序**
-
-[75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/)
-
-```html
-Input: [2,0,2,1,1,0]
-Output: [0,0,1,1,2,2]
-```
-
-题目描述:只有 0/1/2 三种颜色。
-
-```java
-public void sortColors(int[] nums) {
- int zero = -1, one = 0, two = nums.length;
- while (one < two) {
- if (nums[one] == 0) {
- swap(nums, ++zero, one++);
- } else if (nums[one] == 2) {
- swap(nums, --two, one);
- } else {
- ++one;
- }
- }
-}
-
-private void swap(int[] nums, int i, int j) {
- int t = nums[i];
- nums[i] = nums[j];
- nums[j] = t;
-}
-```
-
## 二分查找
**正常实现**
@@ -1065,6 +1077,53 @@ private int binarySearch(int[] nums, int target) {
}
```
+## 分治
+
+**给表达式加括号**
+
+[241. Different Ways to Add Parentheses (Medium)](https://leetcode.com/problems/different-ways-to-add-parentheses/description/)
+
+```html
+Input: "2-1-1".
+
+((2-1)-1) = 0
+(2-(1-1)) = 2
+
+Output : [0, 2]
+```
+
+```java
+public List diffWaysToCompute(String input) {
+ List ways = new ArrayList<>();
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (c == '+' || c == '-' || c == '*') {
+ List left = diffWaysToCompute(input.substring(0, i));
+ List right = diffWaysToCompute(input.substring(i + 1));
+ for (int l : left) {
+ for (int r : right) {
+ switch (c) {
+ case '+':
+ ways.add(l + r);
+ break;
+ case '-':
+ ways.add(l - r);
+ break;
+ case '*':
+ ways.add(l * r);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (ways.size() == 0) {
+ ways.add(Integer.valueOf(input));
+ }
+ return ways;
+}
+```
+
## 搜索
深度优先搜索和广度优先搜索广泛运用于树和图中,但是它们的应用远远不止如此。
@@ -2312,53 +2371,6 @@ private void backtracking(int row) {
}
```
-## 分治
-
-**给表达式加括号**
-
-[241. Different Ways to Add Parentheses (Medium)](https://leetcode.com/problems/different-ways-to-add-parentheses/description/)
-
-```html
-Input: "2-1-1".
-
-((2-1)-1) = 0
-(2-(1-1)) = 2
-
-Output : [0, 2]
-```
-
-```java
-public List diffWaysToCompute(String input) {
- List ways = new ArrayList<>();
- for (int i = 0; i < input.length(); i++) {
- char c = input.charAt(i);
- if (c == '+' || c == '-' || c == '*') {
- List left = diffWaysToCompute(input.substring(0, i));
- List right = diffWaysToCompute(input.substring(i + 1));
- for (int l : left) {
- for (int r : right) {
- switch (c) {
- case '+':
- ways.add(l + r);
- break;
- case '-':
- ways.add(l - r);
- break;
- case '*':
- ways.add(l * r);
- break;
- }
- }
- }
- }
- }
- if (ways.size() == 0) {
- ways.add(Integer.valueOf(input));
- }
- return ways;
-}
-```
-
## 动态规划
递归和动态规划都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存了子问题的解,避免重复计算。
@@ -4036,973 +4048,6 @@ public int maximumProduct(int[] nums) {
# 数据结构相关
-## 栈和队列
-
-**用栈实现队列**
-
-[232. Implement Queue using Stacks (Easy)](https://leetcode.com/problems/implement-queue-using-stacks/description/)
-
-```java
-class MyQueue {
-
- private Stack in = new Stack<>();
- private Stack out = new Stack<>();
-
- public void push(int x) {
- in.push(x);
- }
-
- public int pop() {
- in2out();
- return out.pop();
- }
-
- public int peek() {
- in2out();
- return out.peek();
- }
-
- private void in2out() {
- if (out.isEmpty()) {
- while (!in.isEmpty()) {
- out.push(in.pop());
- }
- }
- }
-
- public boolean empty() {
- return in.isEmpty() && out.isEmpty();
- }
-}
-```
-
-**用队列实现栈**
-
-[225. Implement Stack using Queues (Easy)](https://leetcode.com/problems/implement-stack-using-queues/description/)
-
-在将一个元素 x 插入队列时,需要让除了 x 之外的所有元素出队列,再入队列。此时 x 在队首,第一个出队列,实现了后进先出顺序。
-
-```java
-class MyStack {
-
- private Queue queue;
-
- public MyStack() {
- queue = new LinkedList<>();
- }
-
- public void push(int x) {
- queue.add(x);
- int cnt = queue.size();
- while (cnt-- > 1) {
- queue.add(queue.poll());
- }
- }
-
- public int pop() {
- return queue.remove();
- }
-
- public int top() {
- return queue.peek();
- }
-
- public boolean empty() {
- return queue.isEmpty();
- }
-}
-```
-
-**最小值栈**
-
-[155. Min Stack (Easy)](https://leetcode.com/problems/min-stack/description/)
-
-```java
-class MinStack {
-
- private Stack dataStack;
- private Stack minStack;
- private int min;
-
- public MinStack() {
- dataStack = new Stack<>();
- minStack = new Stack<>();
- min = Integer.MAX_VALUE;
- }
-
- public void push(int x) {
- dataStack.add(x);
- min = Math.min(min, x);
- minStack.add(min);
- }
-
- public void pop() {
- dataStack.pop();
- minStack.pop();
- min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek();
- }
-
- public int top() {
- return dataStack.peek();
- }
-
- public int getMin() {
- return minStack.peek();
- }
-}
-```
-
-对于实现最小值队列问题,可以先将队列使用栈来实现,然后就将问题转换为最小值栈,这个问题出现在 编程之美:3.7。
-
-**用栈实现括号匹配**
-
-[20. Valid Parentheses (Easy)](https://leetcode.com/problems/valid-parentheses/description/)
-
-```html
-"()[]{}"
-
-Output : true
-```
-
-```java
-public boolean isValid(String s) {
- Stack stack = new Stack<>();
- for (char c : s.toCharArray()) {
- if (c == '(' || c == '{' || c == '[') {
- stack.push(c);
- } else {
- if (stack.isEmpty()) {
- return false;
- }
- char cStack = stack.pop();
- boolean b1 = c == ')' && cStack != '(';
- boolean b2 = c == ']' && cStack != '[';
- boolean b3 = c == '}' && cStack != '{';
- if (b1 || b2 || b3) {
- return false;
- }
- }
- }
- return stack.isEmpty();
-}
-```
-
-**数组中元素与下一个比它大的元素之间的距离**
-
-[739. Daily Temperatures (Medium)](https://leetcode.com/problems/daily-temperatures/description/)
-
-```html
-Input: [73, 74, 75, 71, 69, 72, 76, 73]
-Output: [1, 1, 4, 2, 1, 1, 0, 0]
-```
-
-在遍历数组时用 Stack 把数组中的数存起来,如果当前遍历的数比栈顶元素来的大,说明栈顶元素的下一个比它大的数就是当前元素。
-
-```java
-public int[] dailyTemperatures(int[] temperatures) {
- int n = temperatures.length;
- int[] dist = new int[n];
- Stack indexs = new Stack<>();
- for (int curIndex = 0; curIndex < n; curIndex++) {
- while (!indexs.isEmpty() && temperatures[curIndex] > temperatures[indexs.peek()]) {
- int preIndex = indexs.pop();
- dist[preIndex] = curIndex - preIndex;
- }
- indexs.add(curIndex);
- }
- return dist;
-}
-```
-
-**循环数组中比当前元素大的下一个元素**
-
-[503. Next Greater Element II (Medium)](https://leetcode.com/problems/next-greater-element-ii/description/)
-
-```text
-Input: [1,2,1]
-Output: [2,-1,2]
-Explanation: The first 1's next greater number is 2;
-The number 2 can't find next greater number;
-The second 1's next greater number needs to search circularly, which is also 2.
-```
-
-与 739. Daily Temperatures (Medium) 不同的是,数组是循环数组,并且最后要求的不是距离而是下一个元素。
-
-```java
-public int[] nextGreaterElements(int[] nums) {
- int n = nums.length;
- int[] next = new int[n];
- Arrays.fill(next, -1);
- Stack pre = new Stack<>();
- for (int i = 0; i < n * 2; i++) {
- int num = nums[i % n];
- while (!pre.isEmpty() && nums[pre.peek()] < num) {
- next[pre.pop()] = num;
- }
- if (i < n){
- pre.push(i);
- }
- }
- return next;
-}
-```
-
-## 哈希表
-
-哈希表使用 O(N) 空间复杂度存储数据,从而能够以 O(1) 时间复杂度求解问题。
-
-Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。
-
-如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。
-
-Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。
-
-在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中[Leetcdoe : 535. Encode and Decode TinyURL (Medium)](https://leetcode.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源。
-
-HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。
-
-**数组中的两个数和为给定值**
-
-[1. Two Sum (Easy)](https://leetcode.com/problems/two-sum/description/)
-
-可以先对数组进行排序,然后使用双指针方法或者二分查找方法。这样做的时间复杂度为 O(NlogN),空间复杂度为 O(1)。
-
-用 HashMap 存储数组元素和索引的映射,在访问到 nums[i] 时,判断 HashMap 中是否存在 target - nums[i],如果存在说明 target - nums[i] 所在的索引和 i 就是要找的两个数。该方法的时间复杂度为 O(N),空间复杂度为 O(N),使用空间来换取时间。
-
-```java
-public int[] twoSum(int[] nums, int target) {
- HashMap indexForNum = new HashMap<>();
- for (int i = 0; i < nums.length; i++) {
- if (indexForNum.containsKey(target - nums[i])) {
- return new int[]{indexForNum.get(target - nums[i]), i};
- } else {
- indexForNum.put(nums[i], i);
- }
- }
- return null;
-}
-```
-
-**判断数组是否含有重复元素**
-
-[217. Contains Duplicate (Easy)](https://leetcode.com/problems/contains-duplicate/description/)
-
-```java
-public boolean containsDuplicate(int[] nums) {
- Set set = new HashSet<>();
- for (int num : nums) {
- set.add(num);
- }
- return set.size() < nums.length;
-}
-```
-
-**最长和谐序列**
-
-[594. Longest Harmonious Subsequence (Easy)](https://leetcode.com/problems/longest-harmonious-subsequence/description/)
-
-```html
-Input: [1,3,2,2,5,2,3,7]
-Output: 5
-Explanation: The longest harmonious subsequence is [3,2,2,2,3].
-```
-
-和谐序列中最大数和最小数只差正好为 1,应该注意的是序列的元素不一定是数组的连续元素。
-
-```java
-public int findLHS(int[] nums) {
- Map countForNum = new HashMap<>();
- for (int num : nums) {
- countForNum.put(num, countForNum.getOrDefault(num, 0) + 1);
- }
- int longest = 0;
- for (int num : countForNum.keySet()) {
- if (countForNum.containsKey(num + 1)) {
- longest = Math.max(longest, countForNum.get(num + 1) + countForNum.get(num));
- }
- }
- return longest;
-}
-```
-
-**最长连续序列**
-
-[128. Longest Consecutive Sequence (Hard)](https://leetcode.com/problems/longest-consecutive-sequence/description/)
-
-```html
-Given [100, 4, 200, 1, 3, 2],
-The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
-```
-
-要求以 O(N) 的时间复杂度求解。
-
-```java
-public int longestConsecutive(int[] nums) {
- Map countForNum = new HashMap<>();
- for (int num : nums) {
- countForNum.put(num, 1);
- }
- for (int num : nums) {
- forward(countForNum, num);
- }
- return maxCount(countForNum);
-}
-
-private int forward(Map countForNum, int num) {
- if (!countForNum.containsKey(num)) {
- return 0;
- }
- int cnt = countForNum.get(num);
- if (cnt > 1) {
- return cnt;
- }
- cnt = forward(countForNum, num + 1) + 1;
- countForNum.put(num, cnt);
- return cnt;
-}
-
-private int maxCount(Map countForNum) {
- int max = 0;
- for (int num : countForNum.keySet()) {
- max = Math.max(max, countForNum.get(num));
- }
- return max;
-}
-```
-
-## 字符串
-
-**两个字符串包含的字符是否完全相同**
-
-[242. Valid Anagram (Easy)](https://leetcode.com/problems/valid-anagram/description/)
-
-```html
-s = "anagram", t = "nagaram", return true.
-s = "rat", t = "car", return false.
-```
-
-字符串只包含小写字符,总共有 26 个小写字符。可以用 HashMap 来映射字符与出现次数。因为键的范围很小,因此可以使用长度为 26 的整型数组对字符串出现的字符进行统计,然后比较两个字符串出现的字符数量是否相同。
-
-```java
-public boolean isAnagram(String s, String t) {
- int[] cnts = new int[26];
- for (char c : s.toCharArray()) {
- cnts[c - 'a']++;
- }
- for (char c : t.toCharArray()) {
- cnts[c - 'a']--;
- }
- for (int cnt : cnts) {
- if (cnt != 0) {
- return false;
- }
- }
- return true;
-}
-```
-
-**计算一组字符集合可以组成的回文字符串的最大长度**
-
-[409. Longest Palindrome (Easy)](https://leetcode.com/problems/longest-palindrome/description/)
-
-```html
-Input : "abccccdd"
-Output : 7
-Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7.
-```
-
-使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。
-
-因为回文字符串最中间的那个字符可以单独出现,所以如果有单独的字符就把它放到最中间。
-
-```java
-public int longestPalindrome(String s) {
- int[] cnts = new int[256];
- for (char c : s.toCharArray()) {
- cnts[c]++;
- }
- int palindrome = 0;
- for (int cnt : cnts) {
- palindrome += (cnt / 2) * 2;
- }
- if (palindrome < s.length()) {
- palindrome++; // 这个条件下 s 中一定有单个未使用的字符存在,可以把这个字符放到回文的最中间
- }
- return palindrome;
-}
-```
-
-**字符串同构**
-
-[205. Isomorphic Strings (Easy)](https://leetcode.com/problems/isomorphic-strings/description/)
-
-```html
-Given "egg", "add", return true.
-Given "foo", "bar", return false.
-Given "paper", "title", return true.
-```
-
-记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。
-
-```java
-public boolean isIsomorphic(String s, String t) {
- int[] preIndexOfS = new int[256];
- int[] preIndexOfT = new int[256];
- for (int i = 0; i < s.length(); i++) {
- char sc = s.charAt(i), tc = t.charAt(i);
- if (preIndexOfS[sc] != preIndexOfT[tc]) {
- return false;
- }
- preIndexOfS[sc] = i + 1;
- preIndexOfT[tc] = i + 1;
- }
- return true;
-}
-```
-
-**回文子字符串**
-
-[647. Palindromic Substrings (Medium)](https://leetcode.com/problems/palindromic-substrings/description/)
-
-```html
-Input: "aaa"
-Output: 6
-Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
-```
-
-从字符串的某一位开始,尝试着去扩展子字符串。
-
-```java
-private int cnt = 0;
-
-public int countSubstrings(String s) {
- for (int i = 0; i < s.length(); i++) {
- extendSubstrings(s, i, i); // 奇数长度
- extendSubstrings(s, i, i + 1); // 偶数长度
- }
- return cnt;
-}
-
-private void extendSubstrings(String s, int start, int end) {
- while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
- start--;
- end++;
- cnt++;
- }
-}
-```
-
-**判断一个整数是否是回文数**
-
-[9. Palindrome Number (Easy)](https://leetcode.com/problems/palindrome-number/description/)
-
-要求不能使用额外空间,也就不能将整数转换为字符串进行判断。
-
-将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。
-
-```java
-public boolean isPalindrome(int x) {
- if (x == 0) {
- return true;
- }
- if (x < 0 || x % 10 == 0) {
- return false;
- }
- int right = 0;
- while (x > right) {
- right = right * 10 + x % 10;
- x /= 10;
- }
- return x == right || x == right / 10;
-}
-```
-
-**统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数**
-
-[696. Count Binary Substrings (Easy)](https://leetcode.com/problems/count-binary-substrings/description/)
-
-```html
-Input: "00110011"
-Output: 6
-Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01".
-```
-
-```java
-public int countBinarySubstrings(String s) {
- int preLen = 0, curLen = 1, count = 0;
- for (int i = 1; i < s.length(); i++) {
- if (s.charAt(i) == s.charAt(i - 1)) {
- curLen++;
- } else {
- preLen = curLen;
- curLen = 1;
- }
-
- if (preLen >= curLen) {
- count++;
- }
- }
- return count;
-}
-```
-
-**字符串循环移位包含**
-
-[编程之美:3.1](#)
-
-```html
-s1 = AABCD, s2 = CDAA
-Return : true
-```
-
-给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。
-
-s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。
-
-**字符串循环移位**
-
-[编程之美:2.17](#)
-
-```html
-s = "abcd123" k = 3
-Return "123abcd"
-```
-
-将字符串向右循环移动 k 位。
-
-将 abcd123 中的 abcd 和 123 单独逆序,得到 dcba321,然后对整个字符串进行逆序,得到 123abcd。
-
-**字符串中单词的翻转**
-
-[程序员代码面试指南](#)
-
-```html
-s = "I am a student"
-return "student a am I"
-```
-
-将每个单词逆序,然后将整个字符串逆序。
-
-## 数组与矩阵
-
-**把数组中的 0 移到末尾**
-
-[283. Move Zeroes (Easy)](https://leetcode.com/problems/move-zeroes/description/)
-
-```html
-For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].
-```
-
-```java
-public void moveZeroes(int[] nums) {
- int idx = 0;
- for (int num : nums) {
- if (num != 0) {
- nums[idx++] = num;
- }
- }
- while (idx < nums.length) {
- nums[idx++] = 0;
- }
-}
-```
-
-**改变矩阵维度**
-
-[566. Reshape the Matrix (Easy)](https://leetcode.com/problems/reshape-the-matrix/description/)
-
-```html
-Input:
-nums =
-[[1,2],
- [3,4]]
-r = 1, c = 4
-
-Output:
-[[1,2,3,4]]
-
-Explanation:
-The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list.
-```
-
-```java
-public int[][] matrixReshape(int[][] nums, int r, int c) {
- int m = nums.length, n = nums[0].length;
- if (m * n != r * c) {
- return nums;
- }
- int[][] reshapedNums = new int[r][c];
- int index = 0;
- for (int i = 0; i < r; i++) {
- for (int j = 0; j < c; j++) {
- reshapedNums[i][j] = nums[index / n][index % n];
- index++;
- }
- }
- return reshapedNums;
-}
-```
-
-**找出数组中最长的连续 1**
-
-[485. Max Consecutive Ones (Easy)](https://leetcode.com/problems/max-consecutive-ones/description/)
-
-```java
-public int findMaxConsecutiveOnes(int[] nums) {
- int max = 0, cur = 0;
- for (int x : nums) {
- cur = x == 0 ? 0 : cur + 1;
- max = Math.max(max, cur);
- }
- return max;
-}
-```
-
-**一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数**
-
-[645. Set Mismatch (Easy)](https://leetcode.com/problems/set-mismatch/description/)
-
-```html
-Input: nums = [1,2,2,4]
-Output: [2,3]
-```
-
-```html
-Input: nums = [1,2,2,4]
-Output: [2,3]
-```
-
-最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(NlogN)。本题可以以 O(N) 的时间复杂度、O(1) 空间复杂度来求解。
-
-主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。遍历数组,如果第 i 位上的元素不是 i + 1,那么一直交换第 i 位和 nums[i] - 1 位置上的元素。
-
-```java
-public int[] findErrorNums(int[] nums) {
- for (int i = 0; i < nums.length; i++) {
- while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) {
- swap(nums, i, nums[i] - 1);
- }
- }
- for (int i = 0; i < nums.length; i++) {
- if (nums[i] != i + 1) {
- return new int[]{nums[i], i + 1};
- }
- }
- return null;
-}
-
-private void swap(int[] nums, int i, int j) {
- int tmp = nums[i];
- nums[i] = nums[j];
- nums[j] = tmp;
-}
-```
-
-类似题目:
-
-- [448. Find All Numbers Disappeared in an Array (Easy)](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/description/),寻找所有丢失的元素
-- [442. Find All Duplicates in an Array (Medium)](https://leetcode.com/problems/find-all-duplicates-in-an-array/description/),寻找所有重复的元素。
-
-**找出数组中重复的数,数组值在 [1, n] 之间**
-
-[287. Find the Duplicate Number (Medium)](https://leetcode.com/problems/find-the-duplicate-number/description/)
-
-要求不能修改数组,也不能使用额外的空间。
-
-二分查找解法:
-
-```java
-public int findDuplicate(int[] nums) {
- int l = 1, h = nums.length - 1;
- while (l <= h) {
- int mid = l + (h - l) / 2;
- int cnt = 0;
- for (int i = 0; i < nums.length; i++) {
- if (nums[i] <= mid) cnt++;
- }
- if (cnt > mid) h = mid - 1;
- else l = mid + 1;
- }
- return l;
-}
-```
-
-双指针解法,类似于有环链表中找出环的入口:
-
-```java
-public int findDuplicate(int[] nums) {
- int slow = nums[0], fast = nums[nums[0]];
- while (slow != fast) {
- slow = nums[slow];
- fast = nums[nums[fast]];
- }
- fast = 0;
- while (slow != fast) {
- slow = nums[slow];
- fast = nums[fast];
- }
- return slow;
-}
-```
-
-**有序矩阵查找**
-
-[240. Search a 2D Matrix II (Medium)](https://leetcode.com/problems/search-a-2d-matrix-ii/description/)
-
-```html
-[
- [ 1, 5, 9],
- [10, 11, 13],
- [12, 13, 15]
-]
-```
-
-```java
-public boolean searchMatrix(int[][] matrix, int target) {
- if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false;
- int m = matrix.length, n = matrix[0].length;
- int row = 0, col = n - 1;
- while (row < m && col >= 0) {
- if (target == matrix[row][col]) return true;
- else if (target < matrix[row][col]) col--;
- else row++;
- }
- return false;
-}
-```
-
-**有序矩阵的 Kth Element**
-
-[378. Kth Smallest Element in a Sorted Matrix ((Medium))](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/)
-
-```html
-matrix = [
- [ 1, 5, 9],
- [10, 11, 13],
- [12, 13, 15]
-],
-k = 8,
-
-return 13.
-```
-
-解题参考:[Share my thoughts and Clean Java Code](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/discuss/85173)
-
-二分查找解法:
-
-```java
-public int kthSmallest(int[][] matrix, int k) {
- int m = matrix.length, n = matrix[0].length;
- int lo = matrix[0][0], hi = matrix[m - 1][n - 1];
- while(lo <= hi) {
- int mid = lo + (hi - lo) / 2;
- int cnt = 0;
- for(int i = 0; i < m; i++) {
- for(int j = 0; j < n && matrix[i][j] <= mid; j++) {
- cnt++;
- }
- }
- if(cnt < k) lo = mid + 1;
- else hi = mid - 1;
- }
- return lo;
-}
-```
-
-堆解法:
-
-```java
-public int kthSmallest(int[][] matrix, int k) {
- int m = matrix.length, n = matrix[0].length;
- PriorityQueue pq = new PriorityQueue();
- for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j]));
- for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数
- Tuple t = pq.poll();
- if(t.x == m - 1) continue;
- pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y]));
- }
- return pq.poll().val;
-}
-
-class Tuple implements Comparable {
- int x, y, val;
- public Tuple(int x, int y, int val) {
- this.x = x; this.y = y; this.val = val;
- }
-
- @Override
- public int compareTo(Tuple that) {
- return this.val - that.val;
- }
-}
-```
-
-**数组相邻差值的个数**
-
-[667. Beautiful Arrangement II (Medium)](https://leetcode.com/problems/beautiful-arrangement-ii/description/)
-
-```html
-Input: n = 3, k = 2
-Output: [1, 3, 2]
-Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2.
-```
-
-题目描述:数组元素为 1\~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。
-
-让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 ... k/2 k/2+1.
-
-```java
-public int[] constructArray(int n, int k) {
- int[] ret = new int[n];
- ret[0] = 1;
- for (int i = 1, interval = k; i <= k; i++, interval--) {
- ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval;
- }
- for (int i = k + 1; i < n; i++) {
- ret[i] = i + 1;
- }
- return ret;
-}
-```
-
-**数组的度**
-
-[697. Degree of an Array (Easy)](https://leetcode.com/problems/degree-of-an-array/description/)
-
-```html
-Input: [1,2,2,3,1,4,2]
-Output: 6
-```
-
-题目描述:数组的度定义为元素出现的最高频率,例如上面的数组度为 3。要求找到一个最小的子数组,这个子数组的度和原数组一样。
-
-```java
-public int findShortestSubArray(int[] nums) {
- Map numsCnt = new HashMap<>();
- Map numsLastIndex = new HashMap<>();
- Map numsFirstIndex = new HashMap<>();
- for (int i = 0; i < nums.length; i++) {
- int num = nums[i];
- numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1);
- numsLastIndex.put(num, i);
- if (!numsFirstIndex.containsKey(num)) {
- numsFirstIndex.put(num, i);
- }
- }
- int maxCnt = 0;
- for (int num : nums) {
- maxCnt = Math.max(maxCnt, numsCnt.get(num));
- }
- int ret = nums.length;
- for (int i = 0; i < nums.length; i++) {
- int num = nums[i];
- int cnt = numsCnt.get(num);
- if (cnt != maxCnt) continue;
- ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1);
- }
- return ret;
-}
-```
-
-**对角元素相等的矩阵**
-
-[766. Toeplitz Matrix (Easy)](https://leetcode.com/problems/toeplitz-matrix/description/)
-
-```html
-1234
-5123
-9512
-
-In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", and in each diagonal all elements are the same, so the answer is True.
-```
-
-```java
-public boolean isToeplitzMatrix(int[][] matrix) {
- for (int i = 0; i < matrix[0].length; i++) {
- if (!check(matrix, matrix[0][i], 0, i)) {
- return false;
- }
- }
- for (int i = 0; i < matrix.length; i++) {
- if (!check(matrix, matrix[i][0], i, 0)) {
- return false;
- }
- }
- return true;
-}
-
-private boolean check(int[][] matrix, int expectValue, int row, int col) {
- if (row >= matrix.length || col >= matrix[0].length) {
- return true;
- }
- if (matrix[row][col] != expectValue) {
- return false;
- }
- return check(matrix, expectValue, row + 1, col + 1);
-}
-```
-
-**嵌套数组**
-
-[565. Array Nesting (Medium)](https://leetcode.com/problems/array-nesting/description/)
-
-```html
-Input: A = [5,4,0,3,1,6,2]
-Output: 4
-Explanation:
-A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.
-
-One of the longest S[K]:
-S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}
-```
-
-题目描述:S[i] 表示一个集合,集合的第一个元素是 A[i],第二个元素是 A[A[i]],如此嵌套下去。求最大的 S[i]。
-
-```java
-public int arrayNesting(int[] nums) {
- int max = 0;
- for (int i = 0; i < nums.length; i++) {
- int cnt = 0;
- for (int j = i; nums[j] != -1; ) {
- cnt++;
- int t = nums[j];
- nums[j] = -1; // 标记该位置已经被访问
- j = t;
-
- }
- max = Math.max(max, cnt);
- }
- return max;
-}
-```
-
-**分隔数组**
-
-[769. Max Chunks To Make Sorted (Medium)](https://leetcode.com/problems/max-chunks-to-make-sorted/description/)
-
-```html
-Input: arr = [1,0,2,3,4]
-Output: 4
-Explanation:
-We can split into two chunks, such as [1, 0], [2, 3, 4].
-However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible.
-```
-
-题目描述:分隔数组,使得对每部分排序后数组就为有序。
-
-```java
-public int maxChunksToSorted(int[] arr) {
- if (arr == null) return 0;
- int ret = 0;
- int right = arr[0];
- for (int i = 0; i < arr.length; i++) {
- right = Math.max(right, arr[i]);
- if (right == i) ret++;
- }
- return ret;
-}
-```
-
## 链表
链表是空节点,或者有一个值和一个指向下一个链表的指针,因此很多链表问题可以用递归来处理。
@@ -5036,7 +4081,10 @@ public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
}
```
-如果只是判断是否存在交点,那么就是另一个问题,即 [编程之美:3.6]() 的问题。有两种解法:把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;或者直接比较两个链表的最后一个节点是否相同。
+如果只是判断是否存在交点,那么就是另一个问题,即 [编程之美 3.6]() 的问题。有两种解法:
+
+- 把第一个链表的结尾连接到第二个链表的开头,看第二个链表是否存在环;
+- 或者直接比较两个链表的最后一个节点是否相同。
**链表反转**
@@ -5101,7 +4149,7 @@ Given 1->1->2->3->3, return 1->2->3.
```java
public ListNode deleteDuplicates(ListNode head) {
- if(head == null || head.next == null) return head;
+ if (head == null || head.next == null) return head;
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}
@@ -5423,8 +4471,8 @@ Input:
3 2 1 3
/ \ \
5 4 7
+
Output:
-Merged tree:
3
/ \
4 5
@@ -5450,6 +4498,7 @@ public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
```html
Given the below binary tree and sum = 22,
+
5
/ \
4 8
@@ -5457,10 +4506,11 @@ Given the below binary tree and sum = 22,
11 13 4
/ \ \
7 2 1
+
return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
```
-路径和定义为从 root 到 leaf 的所有节点的和
+路径和定义为从 root 到 leaf 的所有节点的和。
```java
public boolean hasPathSum(TreeNode root, int sum) {
@@ -5852,7 +4902,7 @@ public List preorderTraversal(TreeNode root) {
[145. Binary Tree Postorder Traversal (Medium)](https://leetcode.com/problems/binary-tree-postorder-traversal/description/)
-前序遍历为 root -> left -> right,后序遍历为 left -> right -> root,可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
+前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
```java
public List postorderTraversal(TreeNode root) {
@@ -6110,7 +5160,7 @@ public TreeNode sortedListToBST(ListNode head) {
if (head == null) return null;
if (head.next == null) return new TreeNode(head.val);
ListNode preMid = preMid(head);
- ListNode mid = preMid.next;
+ ListNode mid = preMid.next;
preMid.next = null; // 断开链表
TreeNode t = new TreeNode(mid.val);
t.left = sortedListToBST(head);
@@ -6226,7 +5276,7 @@ private void inOrder(TreeNode node) {
return [2].
```
-答案可能不止一个,也就是有多个值出现的次数一样多,并且是最大的。
+答案可能不止一个,也就是有多个值出现的次数一样多。
```java
private int curCnt = 1;
@@ -6396,6 +5446,974 @@ class MapSum {
}
```
+
+## 栈和队列
+
+**用栈实现队列**
+
+[232. Implement Queue using Stacks (Easy)](https://leetcode.com/problems/implement-queue-using-stacks/description/)
+
+栈的顺序为后进先出,而队列的顺序为先进先出。使用两个栈实现队列,一个元素需要经过两个栈才能出队列,在经过第一个栈时元素顺序被反转,经过第二个栈时再次被反转,此时就是先进先出顺序。
+
+```java
+class MyQueue {
+
+ private Stack in = new Stack<>();
+ private Stack out = new Stack<>();
+
+ public void push(int x) {
+ in.push(x);
+ }
+
+ public int pop() {
+ in2out();
+ return out.pop();
+ }
+
+ public int peek() {
+ in2out();
+ return out.peek();
+ }
+
+ private void in2out() {
+ if (out.isEmpty()) {
+ while (!in.isEmpty()) {
+ out.push(in.pop());
+ }
+ }
+ }
+
+ public boolean empty() {
+ return in.isEmpty() && out.isEmpty();
+ }
+}
+```
+
+**用队列实现栈**
+
+[225. Implement Stack using Queues (Easy)](https://leetcode.com/problems/implement-stack-using-queues/description/)
+
+在将一个元素 x 插入队列时,为了维护原来的后进先出顺序,需要让 x 插入队列首部。而队列的默认插入顺序是队列尾部,因此在将 x 插入队列尾部之后,需要让除了 x 之外的所有元素出队列,再入队列。
+
+```java
+class MyStack {
+
+ private Queue queue;
+
+ public MyStack() {
+ queue = new LinkedList<>();
+ }
+
+ public void push(int x) {
+ queue.add(x);
+ int cnt = queue.size();
+ while (cnt-- > 1) {
+ queue.add(queue.poll());
+ }
+ }
+
+ public int pop() {
+ return queue.remove();
+ }
+
+ public int top() {
+ return queue.peek();
+ }
+
+ public boolean empty() {
+ return queue.isEmpty();
+ }
+}
+```
+
+**最小值栈**
+
+[155. Min Stack (Easy)](https://leetcode.com/problems/min-stack/description/)
+
+```java
+class MinStack {
+
+ private Stack dataStack;
+ private Stack minStack;
+ private int min;
+
+ public MinStack() {
+ dataStack = new Stack<>();
+ minStack = new Stack<>();
+ min = Integer.MAX_VALUE;
+ }
+
+ public void push(int x) {
+ dataStack.add(x);
+ min = Math.min(min, x);
+ minStack.add(min);
+ }
+
+ public void pop() {
+ dataStack.pop();
+ minStack.pop();
+ min = minStack.isEmpty() ? Integer.MAX_VALUE : minStack.peek();
+ }
+
+ public int top() {
+ return dataStack.peek();
+ }
+
+ public int getMin() {
+ return minStack.peek();
+ }
+}
+```
+
+对于实现最小值队列问题,可以先将队列使用栈来实现,然后就将问题转换为最小值栈,这个问题出现在 编程之美:3.7。
+
+**用栈实现括号匹配**
+
+[20. Valid Parentheses (Easy)](https://leetcode.com/problems/valid-parentheses/description/)
+
+```html
+"()[]{}"
+
+Output : true
+```
+
+```java
+public boolean isValid(String s) {
+ Stack stack = new Stack<>();
+ for (char c : s.toCharArray()) {
+ if (c == '(' || c == '{' || c == '[') {
+ stack.push(c);
+ } else {
+ if (stack.isEmpty()) {
+ return false;
+ }
+ char cStack = stack.pop();
+ boolean b1 = c == ')' && cStack != '(';
+ boolean b2 = c == ']' && cStack != '[';
+ boolean b3 = c == '}' && cStack != '{';
+ if (b1 || b2 || b3) {
+ return false;
+ }
+ }
+ }
+ return stack.isEmpty();
+}
+```
+
+**数组中元素与下一个比它大的元素之间的距离**
+
+[739. Daily Temperatures (Medium)](https://leetcode.com/problems/daily-temperatures/description/)
+
+```html
+Input: [73, 74, 75, 71, 69, 72, 76, 73]
+Output: [1, 1, 4, 2, 1, 1, 0, 0]
+```
+
+在遍历数组时用栈把数组中的数存起来,如果当前遍历的数比栈顶元素来的大,说明栈顶元素的下一个比它大的数就是当前元素。
+
+```java
+public int[] dailyTemperatures(int[] temperatures) {
+ int n = temperatures.length;
+ int[] dist = new int[n];
+ Stack indexs = new Stack<>();
+ for (int curIndex = 0; curIndex < n; curIndex++) {
+ while (!indexs.isEmpty() && temperatures[curIndex] > temperatures[indexs.peek()]) {
+ int preIndex = indexs.pop();
+ dist[preIndex] = curIndex - preIndex;
+ }
+ indexs.add(curIndex);
+ }
+ return dist;
+}
+```
+
+**循环数组中比当前元素大的下一个元素**
+
+[503. Next Greater Element II (Medium)](https://leetcode.com/problems/next-greater-element-ii/description/)
+
+```text
+Input: [1,2,1]
+Output: [2,-1,2]
+Explanation: The first 1's next greater number is 2;
+The number 2 can't find next greater number;
+The second 1's next greater number needs to search circularly, which is also 2.
+```
+
+与 739. Daily Temperatures (Medium) 不同的是,数组是循环数组,并且最后要求的不是距离而是下一个元素。
+
+```java
+public int[] nextGreaterElements(int[] nums) {
+ int n = nums.length;
+ int[] next = new int[n];
+ Arrays.fill(next, -1);
+ Stack pre = new Stack<>();
+ for (int i = 0; i < n * 2; i++) {
+ int num = nums[i % n];
+ while (!pre.isEmpty() && nums[pre.peek()] < num) {
+ next[pre.pop()] = num;
+ }
+ if (i < n){
+ pre.push(i);
+ }
+ }
+ return next;
+}
+```
+
+## 哈希表
+
+哈希表使用 O(N) 空间复杂度存储数据,并且以 O(1) 时间复杂度求解问题。
+
+- Java 中的 **HashSet** 用于存储一个集合,可以查找元素是否在集合中。如果元素有穷,并且范围不大,那么可以用一个布尔数组来存储一个元素是否存在。例如对于只有小写字符的元素,就可以用一个长度为 26 的布尔数组来存储一个字符集合,使得空间复杂度降低为 O(1)。
+
+- Java 中的 **HashMap** 主要用于映射关系,从而把两个元素联系起来。HashMap 也可以用来对元素进行计数统计,此时键为元素,值为计数。和 HashSet 类似,如果元素有穷并且范围不大,可以用整型数组来进行统计。在对一个内容进行压缩或者其它转换时,利用 HashMap 可以把原始内容和转换后的内容联系起来。例如在一个简化 url 的系统中 [Leetcdoe : 535. Encode and Decode TinyURL (Medium)](https://leetcode.com/problems/encode-and-decode-tinyurl/description/),利用 HashMap 就可以存储精简后的 url 到原始 url 的映射,使得不仅可以显示简化的 url,也可以根据简化的 url 得到原始 url 从而定位到正确的资源。
+
+
+**数组中两个数的和为给定值**
+
+[1. Two Sum (Easy)](https://leetcode.com/problems/two-sum/description/)
+
+可以先对数组进行排序,然后使用双指针方法或者二分查找方法。这样做的时间复杂度为 O(NlogN),空间复杂度为 O(1)。
+
+用 HashMap 存储数组元素和索引的映射,在访问到 nums[i] 时,判断 HashMap 中是否存在 target - nums[i],如果存在说明 target - nums[i] 所在的索引和 i 就是要找的两个数。该方法的时间复杂度为 O(N),空间复杂度为 O(N),使用空间来换取时间。
+
+```java
+public int[] twoSum(int[] nums, int target) {
+ HashMap indexForNum = new HashMap<>();
+ for (int i = 0; i < nums.length; i++) {
+ if (indexForNum.containsKey(target - nums[i])) {
+ return new int[]{indexForNum.get(target - nums[i]), i};
+ } else {
+ indexForNum.put(nums[i], i);
+ }
+ }
+ return null;
+}
+```
+
+**判断数组是否含有重复元素**
+
+[217. Contains Duplicate (Easy)](https://leetcode.com/problems/contains-duplicate/description/)
+
+```java
+public boolean containsDuplicate(int[] nums) {
+ Set set = new HashSet<>();
+ for (int num : nums) {
+ set.add(num);
+ }
+ return set.size() < nums.length;
+}
+```
+
+**最长和谐序列**
+
+[594. Longest Harmonious Subsequence (Easy)](https://leetcode.com/problems/longest-harmonious-subsequence/description/)
+
+```html
+Input: [1,3,2,2,5,2,3,7]
+Output: 5
+Explanation: The longest harmonious subsequence is [3,2,2,2,3].
+```
+
+和谐序列中最大数和最小数只差正好为 1,应该注意的是序列的元素不一定是数组的连续元素。
+
+```java
+public int findLHS(int[] nums) {
+ Map countForNum = new HashMap<>();
+ for (int num : nums) {
+ countForNum.put(num, countForNum.getOrDefault(num, 0) + 1);
+ }
+ int longest = 0;
+ for (int num : countForNum.keySet()) {
+ if (countForNum.containsKey(num + 1)) {
+ longest = Math.max(longest, countForNum.get(num + 1) + countForNum.get(num));
+ }
+ }
+ return longest;
+}
+```
+
+**最长连续序列**
+
+[128. Longest Consecutive Sequence (Hard)](https://leetcode.com/problems/longest-consecutive-sequence/description/)
+
+```html
+Given [100, 4, 200, 1, 3, 2],
+The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4.
+```
+
+要求以 O(N) 的时间复杂度求解。
+
+```java
+public int longestConsecutive(int[] nums) {
+ Map countForNum = new HashMap<>();
+ for (int num : nums) {
+ countForNum.put(num, 1);
+ }
+ for (int num : nums) {
+ forward(countForNum, num);
+ }
+ return maxCount(countForNum);
+}
+
+private int forward(Map countForNum, int num) {
+ if (!countForNum.containsKey(num)) {
+ return 0;
+ }
+ int cnt = countForNum.get(num);
+ if (cnt > 1) {
+ return cnt;
+ }
+ cnt = forward(countForNum, num + 1) + 1;
+ countForNum.put(num, cnt);
+ return cnt;
+}
+
+private int maxCount(Map countForNum) {
+ int max = 0;
+ for (int num : countForNum.keySet()) {
+ max = Math.max(max, countForNum.get(num));
+ }
+ return max;
+}
+```
+
+## 字符串
+
+**字符串循环移位包含**
+
+[编程之美 3.1](#)
+
+```html
+s1 = AABCD, s2 = CDAA
+Return : true
+```
+
+给定两个字符串 s1 和 s2,要求判定 s2 是否能够被 s1 做循环移位得到的字符串包含。
+
+s1 进行循环移位的结果是 s1s1 的子字符串,因此只要判断 s2 是否是 s1s1 的子字符串即可。
+
+**字符串循环移位**
+
+[编程之美 2.17](#)
+
+```html
+s = "abcd123" k = 3
+Return "123abcd"
+```
+
+将字符串向右循环移动 k 位。
+
+将 abcd123 中的 abcd 和 123 单独翻转,得到 dcba321,然后对整个字符串进行翻转,得到 123abcd。
+
+**字符串中单词的翻转**
+
+[程序员代码面试指南](#)
+
+```html
+s = "I am a student"
+Return "student a am I"
+```
+
+将每个单词翻转,然后将整个字符串翻转。
+
+**两个字符串包含的字符是否完全相同**
+
+[242. Valid Anagram (Easy)](https://leetcode.com/problems/valid-anagram/description/)
+
+```html
+s = "anagram", t = "nagaram", return true.
+s = "rat", t = "car", return false.
+```
+
+可以用 HashMap 来映射字符与出现次数,然后比较两个字符串出现的字符数量是否相同。
+
+由于本题的字符串只包含 26 个小写字符,因此可以使用长度为 26 的整型数组对字符串出现的字符进行统计,不再使用 HashMap。
+
+```java
+public boolean isAnagram(String s, String t) {
+ int[] cnts = new int[26];
+ for (char c : s.toCharArray()) {
+ cnts[c - 'a']++;
+ }
+ for (char c : t.toCharArray()) {
+ cnts[c - 'a']--;
+ }
+ for (int cnt : cnts) {
+ if (cnt != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+```
+
+**计算一组字符集合可以组成的回文字符串的最大长度**
+
+[409. Longest Palindrome (Easy)](https://leetcode.com/problems/longest-palindrome/description/)
+
+```html
+Input : "abccccdd"
+Output : 7
+Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7.
+```
+
+使用长度为 256 的整型数组来统计每个字符出现的个数,每个字符有偶数个可以用来构成回文字符串。
+
+因为回文字符串最中间的那个字符可以单独出现,所以如果有单独的字符就把它放到最中间。
+
+```java
+public int longestPalindrome(String s) {
+ int[] cnts = new int[256];
+ for (char c : s.toCharArray()) {
+ cnts[c]++;
+ }
+ int palindrome = 0;
+ for (int cnt : cnts) {
+ palindrome += (cnt / 2) * 2;
+ }
+ if (palindrome < s.length()) {
+ palindrome++; // 这个条件下 s 中一定有单个未使用的字符存在,可以把这个字符放到回文的最中间
+ }
+ return palindrome;
+}
+```
+
+**字符串同构**
+
+[205. Isomorphic Strings (Easy)](https://leetcode.com/problems/isomorphic-strings/description/)
+
+```html
+Given "egg", "add", return true.
+Given "foo", "bar", return false.
+Given "paper", "title", return true.
+```
+
+记录一个字符上次出现的位置,如果两个字符串中的字符上次出现的位置一样,那么就属于同构。
+
+```java
+public boolean isIsomorphic(String s, String t) {
+ int[] preIndexOfS = new int[256];
+ int[] preIndexOfT = new int[256];
+ for (int i = 0; i < s.length(); i++) {
+ char sc = s.charAt(i), tc = t.charAt(i);
+ if (preIndexOfS[sc] != preIndexOfT[tc]) {
+ return false;
+ }
+ preIndexOfS[sc] = i + 1;
+ preIndexOfT[tc] = i + 1;
+ }
+ return true;
+}
+```
+
+**回文子字符串个数**
+
+[647. Palindromic Substrings (Medium)](https://leetcode.com/problems/palindromic-substrings/description/)
+
+```html
+Input: "aaa"
+Output: 6
+Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".
+```
+
+从字符串的某一位开始,尝试着去扩展子字符串。
+
+```java
+private int cnt = 0;
+
+public int countSubstrings(String s) {
+ for (int i = 0; i < s.length(); i++) {
+ extendSubstrings(s, i, i); // 奇数长度
+ extendSubstrings(s, i, i + 1); // 偶数长度
+ }
+ return cnt;
+}
+
+private void extendSubstrings(String s, int start, int end) {
+ while (start >= 0 && end < s.length() && s.charAt(start) == s.charAt(end)) {
+ start--;
+ end++;
+ cnt++;
+ }
+}
+```
+
+**判断一个整数是否是回文数**
+
+[9. Palindrome Number (Easy)](https://leetcode.com/problems/palindrome-number/description/)
+
+要求不能使用额外空间,也就不能将整数转换为字符串进行判断。
+
+将整数分成左右两部分,右边那部分需要转置,然后判断这两部分是否相等。
+
+```java
+public boolean isPalindrome(int x) {
+ if (x == 0) {
+ return true;
+ }
+ if (x < 0 || x % 10 == 0) {
+ return false;
+ }
+ int right = 0;
+ while (x > right) {
+ right = right * 10 + x % 10;
+ x /= 10;
+ }
+ return x == right || x == right / 10;
+}
+```
+
+**统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数**
+
+[696. Count Binary Substrings (Easy)](https://leetcode.com/problems/count-binary-substrings/description/)
+
+```html
+Input: "00110011"
+Output: 6
+Explanation: There are 6 substrings that have equal number of consecutive 1's and 0's: "0011", "01", "1100", "10", "0011", and "01".
+```
+
+```java
+public int countBinarySubstrings(String s) {
+ int preLen = 0, curLen = 1, count = 0;
+ for (int i = 1; i < s.length(); i++) {
+ if (s.charAt(i) == s.charAt(i - 1)) {
+ curLen++;
+ } else {
+ preLen = curLen;
+ curLen = 1;
+ }
+
+ if (preLen >= curLen) {
+ count++;
+ }
+ }
+ return count;
+}
+```
+
+## 数组与矩阵
+
+**把数组中的 0 移到末尾**
+
+[283. Move Zeroes (Easy)](https://leetcode.com/problems/move-zeroes/description/)
+
+```html
+For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].
+```
+
+```java
+public void moveZeroes(int[] nums) {
+ int idx = 0;
+ for (int num : nums) {
+ if (num != 0) {
+ nums[idx++] = num;
+ }
+ }
+ while (idx < nums.length) {
+ nums[idx++] = 0;
+ }
+}
+```
+
+**改变矩阵维度**
+
+[566. Reshape the Matrix (Easy)](https://leetcode.com/problems/reshape-the-matrix/description/)
+
+```html
+Input:
+nums =
+[[1,2],
+ [3,4]]
+r = 1, c = 4
+
+Output:
+[[1,2,3,4]]
+
+Explanation:
+The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list.
+```
+
+```java
+public int[][] matrixReshape(int[][] nums, int r, int c) {
+ int m = nums.length, n = nums[0].length;
+ if (m * n != r * c) {
+ return nums;
+ }
+ int[][] reshapedNums = new int[r][c];
+ int index = 0;
+ for (int i = 0; i < r; i++) {
+ for (int j = 0; j < c; j++) {
+ reshapedNums[i][j] = nums[index / n][index % n];
+ index++;
+ }
+ }
+ return reshapedNums;
+}
+```
+
+**找出数组中最长的连续 1**
+
+[485. Max Consecutive Ones (Easy)](https://leetcode.com/problems/max-consecutive-ones/description/)
+
+```java
+public int findMaxConsecutiveOnes(int[] nums) {
+ int max = 0, cur = 0;
+ for (int x : nums) {
+ cur = x == 0 ? 0 : cur + 1;
+ max = Math.max(max, cur);
+ }
+ return max;
+}
+```
+
+**有序矩阵查找**
+
+[240. Search a 2D Matrix II (Medium)](https://leetcode.com/problems/search-a-2d-matrix-ii/description/)
+
+```html
+[
+ [ 1, 5, 9],
+ [10, 11, 13],
+ [12, 13, 15]
+]
+```
+
+```java
+public boolean searchMatrix(int[][] matrix, int target) {
+ if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false;
+ int m = matrix.length, n = matrix[0].length;
+ int row = 0, col = n - 1;
+ while (row < m && col >= 0) {
+ if (target == matrix[row][col]) return true;
+ else if (target < matrix[row][col]) col--;
+ else row++;
+ }
+ return false;
+}
+```
+
+**有序矩阵的 Kth Element**
+
+[378. Kth Smallest Element in a Sorted Matrix ((Medium))](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/description/)
+
+```html
+matrix = [
+ [ 1, 5, 9],
+ [10, 11, 13],
+ [12, 13, 15]
+],
+k = 8,
+
+return 13.
+```
+
+解题参考:[Share my thoughts and Clean Java Code](https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/discuss/85173)
+
+二分查找解法:
+
+```java
+public int kthSmallest(int[][] matrix, int k) {
+ int m = matrix.length, n = matrix[0].length;
+ int lo = matrix[0][0], hi = matrix[m - 1][n - 1];
+ while (lo <= hi) {
+ int mid = lo + (hi - lo) / 2;
+ int cnt = 0;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n && matrix[i][j] <= mid; j++) {
+ cnt++;
+ }
+ }
+ if (cnt < k) lo = mid + 1;
+ else hi = mid - 1;
+ }
+ return lo;
+}
+```
+
+堆解法:
+
+```java
+public int kthSmallest(int[][] matrix, int k) {
+ int m = matrix.length, n = matrix[0].length;
+ PriorityQueue pq = new PriorityQueue();
+ for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j]));
+ for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数
+ Tuple t = pq.poll();
+ if(t.x == m - 1) continue;
+ pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y]));
+ }
+ return pq.poll().val;
+}
+
+class Tuple implements Comparable {
+ int x, y, val;
+ public Tuple(int x, int y, int val) {
+ this.x = x; this.y = y; this.val = val;
+ }
+
+ @Override
+ public int compareTo(Tuple that) {
+ return this.val - that.val;
+ }
+}
+```
+
+**一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数**
+
+[645. Set Mismatch (Easy)](https://leetcode.com/problems/set-mismatch/description/)
+
+```html
+Input: nums = [1,2,2,4]
+Output: [2,3]
+```
+
+```html
+Input: nums = [1,2,2,4]
+Output: [2,3]
+```
+
+最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(NlogN)。本题可以以 O(N) 的时间复杂度、O(1) 空间复杂度来求解。
+
+主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。
+
+```java
+public int[] findErrorNums(int[] nums) {
+ for (int i = 0; i < nums.length; i++) {
+ while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) {
+ swap(nums, i, nums[i] - 1);
+ }
+ }
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != i + 1) {
+ return new int[]{nums[i], i + 1};
+ }
+ }
+ return null;
+}
+
+private void swap(int[] nums, int i, int j) {
+ int tmp = nums[i];
+ nums[i] = nums[j];
+ nums[j] = tmp;
+}
+```
+
+类似题目:
+
+- [448. Find All Numbers Disappeared in an Array (Easy)](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/description/),寻找所有丢失的元素
+- [442. Find All Duplicates in an Array (Medium)](https://leetcode.com/problems/find-all-duplicates-in-an-array/description/),寻找所有重复的元素。
+
+**找出数组中重复的数,数组值在 [1, n] 之间**
+
+[287. Find the Duplicate Number (Medium)](https://leetcode.com/problems/find-the-duplicate-number/description/)
+
+要求不能修改数组,也不能使用额外的空间。
+
+二分查找解法:
+
+```java
+public int findDuplicate(int[] nums) {
+ int l = 1, h = nums.length - 1;
+ while (l <= h) {
+ int mid = l + (h - l) / 2;
+ int cnt = 0;
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] <= mid) cnt++;
+ }
+ if (cnt > mid) h = mid - 1;
+ else l = mid + 1;
+ }
+ return l;
+}
+```
+
+双指针解法,类似于有环链表中找出环的入口:
+
+```java
+public int findDuplicate(int[] nums) {
+ int slow = nums[0], fast = nums[nums[0]];
+ while (slow != fast) {
+ slow = nums[slow];
+ fast = nums[nums[fast]];
+ }
+ fast = 0;
+ while (slow != fast) {
+ slow = nums[slow];
+ fast = nums[fast];
+ }
+ return slow;
+}
+```
+
+**数组相邻差值的个数**
+
+[667. Beautiful Arrangement II (Medium)](https://leetcode.com/problems/beautiful-arrangement-ii/description/)
+
+```html
+Input: n = 3, k = 2
+Output: [1, 3, 2]
+Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2.
+```
+
+题目描述:数组元素为 1\~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。
+
+让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 ... k/2 k/2+1.
+
+```java
+public int[] constructArray(int n, int k) {
+ int[] ret = new int[n];
+ ret[0] = 1;
+ for (int i = 1, interval = k; i <= k; i++, interval--) {
+ ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval;
+ }
+ for (int i = k + 1; i < n; i++) {
+ ret[i] = i + 1;
+ }
+ return ret;
+}
+```
+
+**数组的度**
+
+[697. Degree of an Array (Easy)](https://leetcode.com/problems/degree-of-an-array/description/)
+
+```html
+Input: [1,2,2,3,1,4,2]
+Output: 6
+```
+
+题目描述:数组的度定义为元素出现的最高频率,例如上面的数组度为 3。要求找到一个最小的子数组,这个子数组的度和原数组一样。
+
+```java
+public int findShortestSubArray(int[] nums) {
+ Map numsCnt = new HashMap<>();
+ Map numsLastIndex = new HashMap<>();
+ Map numsFirstIndex = new HashMap<>();
+ for (int i = 0; i < nums.length; i++) {
+ int num = nums[i];
+ numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1);
+ numsLastIndex.put(num, i);
+ if (!numsFirstIndex.containsKey(num)) {
+ numsFirstIndex.put(num, i);
+ }
+ }
+ int maxCnt = 0;
+ for (int num : nums) {
+ maxCnt = Math.max(maxCnt, numsCnt.get(num));
+ }
+ int ret = nums.length;
+ for (int i = 0; i < nums.length; i++) {
+ int num = nums[i];
+ int cnt = numsCnt.get(num);
+ if (cnt != maxCnt) continue;
+ ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1);
+ }
+ return ret;
+}
+```
+
+**对角元素相等的矩阵**
+
+[766. Toeplitz Matrix (Easy)](https://leetcode.com/problems/toeplitz-matrix/description/)
+
+```html
+1234
+5123
+9512
+
+In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", and in each diagonal all elements are the same, so the answer is True.
+```
+
+```java
+public boolean isToeplitzMatrix(int[][] matrix) {
+ for (int i = 0; i < matrix[0].length; i++) {
+ if (!check(matrix, matrix[0][i], 0, i)) {
+ return false;
+ }
+ }
+ for (int i = 0; i < matrix.length; i++) {
+ if (!check(matrix, matrix[i][0], i, 0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+private boolean check(int[][] matrix, int expectValue, int row, int col) {
+ if (row >= matrix.length || col >= matrix[0].length) {
+ return true;
+ }
+ if (matrix[row][col] != expectValue) {
+ return false;
+ }
+ return check(matrix, expectValue, row + 1, col + 1);
+}
+```
+
+**嵌套数组**
+
+[565. Array Nesting (Medium)](https://leetcode.com/problems/array-nesting/description/)
+
+```html
+Input: A = [5,4,0,3,1,6,2]
+Output: 4
+Explanation:
+A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.
+
+One of the longest S[K]:
+S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}
+```
+
+题目描述:S[i] 表示一个集合,集合的第一个元素是 A[i],第二个元素是 A[A[i]],如此嵌套下去。求最大的 S[i]。
+
+```java
+public int arrayNesting(int[] nums) {
+ int max = 0;
+ for (int i = 0; i < nums.length; i++) {
+ int cnt = 0;
+ for (int j = i; nums[j] != -1; ) {
+ cnt++;
+ int t = nums[j];
+ nums[j] = -1; // 标记该位置已经被访问
+ j = t;
+
+ }
+ max = Math.max(max, cnt);
+ }
+ return max;
+}
+```
+
+**分隔数组**
+
+[769. Max Chunks To Make Sorted (Medium)](https://leetcode.com/problems/max-chunks-to-make-sorted/description/)
+
+```html
+Input: arr = [1,0,2,3,4]
+Output: 4
+Explanation:
+We can split into two chunks, such as [1, 0], [2, 3, 4].
+However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible.
+```
+
+题目描述:分隔数组,使得对每部分排序后数组就为有序。
+
+```java
+public int maxChunksToSorted(int[] arr) {
+ if (arr == null) return 0;
+ int ret = 0;
+ int right = arr[0];
+ for (int i = 0; i < arr.length; i++) {
+ right = Math.max(right, arr[i]);
+ if (right == i) ret++;
+ }
+ return ret;
+}
+```
+
+
## 图
### 二分图
@@ -7056,3 +7074,4 @@ public int[] countBits(int num) {
- 何海涛, 软件工程师. 剑指 Offer: 名企面试官精讲典型编程题[M]. 电子工业出版社, 2014.
- 《编程之美》小组. 编程之美[M]. 电子工业出版社, 2008.
- 左程云. 程序员代码面试指南[M]. 电子工业出版社, 2015.
+
diff --git a/notes/Leetcode-Database 题解.md b/notes/Leetcode-Database 题解.md
index 5ab726c5..5b4a3d00 100644
--- a/notes/Leetcode-Database 题解.md
+++ b/notes/Leetcode-Database 题解.md
@@ -461,7 +461,7 @@ Employee 表:
+----+-------+--------+-----------+
```
-查找所有员工,他们的薪资大于其经理薪资。
+查找薪资大于其经理薪资的员工信息。
## SQL Schema
@@ -924,27 +924,27 @@ VALUES
```sql
SELECT
s1.id - 1 AS id,
- s1.student
+ s1.student
FROM
- seat s1
+ seat s1
WHERE
s1.id MOD 2 = 0 UNION
SELECT
s2.id + 1 AS id,
- s2.student
+ s2.student
FROM
- seat s2
+ seat s2
WHERE
- s2.id MOD 2 = 1
+ s2.id MOD 2 = 1
AND s2.id != ( SELECT max( s3.id ) FROM seat s3 ) UNION
SELECT
s4.id AS id,
- s4.student
+ s4.student
FROM
- seat s4
+ seat s4
WHERE
- s4.id MOD 2 = 1
- AND s4.id = ( SELECT max( s5.id ) FROM seat s5 )
+ s4.id MOD 2 = 1
+ AND s4.id = ( SELECT max( s5.id ) FROM seat s5 )
ORDER BY
id;
```
diff --git a/notes/剑指 offer 题解.md b/notes/剑指 offer 题解.md
index 47d5d94e..0c9bd1f9 100644
--- a/notes/剑指 offer 题解.md
+++ b/notes/剑指 offer 题解.md
@@ -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)
@@ -1217,8 +1218,7 @@ public ListNode ReverseList(ListNode head) {
### 递归
```java
-public ListNode Merge(ListNode list1, ListNode list2)
-{
+public ListNode Merge(ListNode list1, ListNode list2) {
if (list1 == null)
return list2;
if (list2 == null)
@@ -1236,8 +1236,7 @@ public ListNode Merge(ListNode list1, ListNode list2)
### 迭代
```java
-public ListNode Merge(ListNode list1, ListNode list2)
-{
+public ListNode Merge(ListNode list1, ListNode list2) {
ListNode head = new ListNode(-1);
ListNode cur = head;
while (list1 != null && list2 != null) {
@@ -1269,15 +1268,13 @@ public ListNode Merge(ListNode list1, ListNode list2)
## 解题思路
```java
-public boolean HasSubtree(TreeNode root1, TreeNode root2)
-{
+public boolean HasSubtree(TreeNode root1, TreeNode root2) {
if (root1 == null || root2 == null)
return false;
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)
return true;
if (root1 == null)
@@ -1298,9 +1295,10 @@ private boolean isSubtreeWithRoot(TreeNode root1, TreeNode root2)
## 解题思路
+### 递归
+
```java
-public void Mirror(TreeNode root)
-{
+public void Mirror(TreeNode root) {
if (root == null)
return;
swap(root);
@@ -1308,14 +1306,36 @@ public void Mirror(TreeNode root)
Mirror(root.right);
}
-private void swap(TreeNode root)
-{
+private void swap(TreeNode root) {
TreeNode t = root.left;
root.left = root.right;
root.right = t;
}
```
+### 迭代
+
+```java
+public void Mirror(TreeNode root) {
+ Stack 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 对称的二叉树
[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
-boolean isSymmetrical(TreeNode pRoot)
-{
+boolean isSymmetrical(TreeNode pRoot) {
if (pRoot == null)
return true;
return isSymmetrical(pRoot.left, pRoot.right);
}
-boolean isSymmetrical(TreeNode t1, TreeNode t2)
-{
+boolean isSymmetrical(TreeNode t1, TreeNode t2) {
if (t1 == null && t2 == null)
return true;
if (t1 == null || t2 == null)
@@ -1359,8 +1377,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2)
## 解题思路
```java
-public ArrayList printMatrix(int[][] matrix)
-{
+public ArrayList printMatrix(int[][] matrix) {
ArrayList ret = new ArrayList<>();
int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;
while (r1 <= r2 && c1 <= c2) {
@@ -1394,25 +1411,21 @@ public ArrayList printMatrix(int[][] matrix)
private Stack dataStack = new Stack<>();
private Stack minStack = new Stack<>();
-public void push(int node)
-{
+public void push(int node) {
dataStack.push(node);
minStack.push(minStack.isEmpty() ? node : Math.min(minStack.peek(), node));
}
-public void pop()
-{
+public void pop() {
dataStack.pop();
minStack.pop();
}
-public int top()
-{
+public int top() {
return dataStack.peek();
}
-public int min()
-{
+public int min() {
return minStack.peek();
}
```
@@ -1430,8 +1443,7 @@ public int min()
使用一个栈来模拟压入弹出操作。
```java
-public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
-{
+public boolean IsPopOrder(int[] pushSequence, int[] popSequence) {
int n = pushSequence.length;
Stack stack = new Stack<>();
for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) {
@@ -1464,8 +1476,7 @@ public boolean IsPopOrder(int[] pushSequence, int[] popSequence)
不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。
```java
-public ArrayList PrintFromTopToBottom(TreeNode root)
-{
+public ArrayList PrintFromTopToBottom(TreeNode root) {
Queue queue = new LinkedList<>();
ArrayList ret = new ArrayList<>();
queue.add(root);
@@ -1495,8 +1506,7 @@ public ArrayList PrintFromTopToBottom(TreeNode root)
## 解题思路
```java
-ArrayList> Print(TreeNode pRoot)
-{
+ArrayList> Print(TreeNode pRoot) {
ArrayList> ret = new ArrayList<>();
Queue queue = new LinkedList<>();
queue.add(pRoot);
@@ -1529,8 +1539,7 @@ ArrayList> Print(TreeNode pRoot)
## 解题思路
```java
-public ArrayList> Print(TreeNode pRoot)
-{
+public ArrayList> Print(TreeNode pRoot) {
ArrayList> ret = new ArrayList<>();
Queue queue = new LinkedList<>();
queue.add(pRoot);
@@ -1571,15 +1580,13 @@ public ArrayList> Print(TreeNode pRoot)
## 解题思路
```java
-public boolean VerifySquenceOfBST(int[] sequence)
-{
+public boolean VerifySquenceOfBST(int[] sequence) {
if (sequence == null || sequence.length == 0)
return false;
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)
return true;
int rootVal = sequence[last];
@@ -1610,14 +1617,12 @@ private boolean verify(int[] sequence, int first, int last)
```java
private ArrayList> ret = new ArrayList<>();
-public ArrayList> FindPath(TreeNode root, int target)
-{
+public ArrayList> FindPath(TreeNode root, int target) {
backtracking(root, target, new ArrayList<>());
return ret;
}
-private void backtracking(TreeNode node, int target, ArrayList path)
-{
+private void backtracking(TreeNode node, int target, ArrayList path) {
if (node == null)
return;
path.add(node.val);
@@ -1641,8 +1646,7 @@ private void backtracking(TreeNode node, int target, ArrayList path)
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
```java
-public class RandomListNode
-{
+public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
@@ -1670,8 +1674,7 @@ public class RandomListNode
```java
-public RandomListNode Clone(RandomListNode pHead)
-{
+public RandomListNode Clone(RandomListNode pHead) {
if (pHead == null)
return null;
// 插入新节点
@@ -1718,14 +1721,12 @@ public RandomListNode Clone(RandomListNode pHead)
private TreeNode pre = null;
private TreeNode head = null;
-public TreeNode Convert(TreeNode root)
-{
+public TreeNode Convert(TreeNode root) {
inOrder(root);
return head;
}
-private void inOrder(TreeNode node)
-{
+private void inOrder(TreeNode node) {
if (node == null)
return;
inOrder(node.left);
@@ -1752,21 +1753,18 @@ private void inOrder(TreeNode node)
```java
private String deserializeStr;
-public String Serialize(TreeNode root)
-{
+public String Serialize(TreeNode root) {
if (root == null)
return "#";
return root.val + " " + Serialize(root.left) + " " + Serialize(root.right);
}
-public TreeNode Deserialize(String str)
-{
+public TreeNode Deserialize(String str) {
deserializeStr = str;
return Deserialize();
}
-private TreeNode Deserialize()
-{
+private TreeNode Deserialize() {
if (deserializeStr.length() == 0)
return null;
int index = deserializeStr.indexOf(" ");
@@ -1795,8 +1793,7 @@ private TreeNode Deserialize()
```java
private ArrayList ret = new ArrayList<>();
-public ArrayList Permutation(String str)
-{
+public ArrayList Permutation(String str) {
if (str.length() == 0)
return ret;
char[] chars = str.toCharArray();
@@ -1805,8 +1802,7 @@ public ArrayList Permutation(String str)
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) {
ret.add(s.toString());
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。
```java
-public int MoreThanHalfNum_Solution(int[] nums)
-{
+public int MoreThanHalfNum_Solution(int[] nums) {
int majority = nums[0];
for (int i = 1, cnt = 1; i < nums.length; i++) {
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 个元素的算法称为快速选择算法。
```java
-public ArrayList GetLeastNumbers_Solution(int[] nums, int k)
-{
+public ArrayList GetLeastNumbers_Solution(int[] nums, int k) {
ArrayList ret = new ArrayList<>();
if (k > nums.length || k <= 0)
return ret;
@@ -1880,8 +1874,7 @@ public ArrayList GetLeastNumbers_Solution(int[] nums, int k)
return ret;
}
-public void findKthSmallest(int[] nums, int k)
-{
+public void findKthSmallest(int[] nums, int k) {
int l = 0, h = nums.length - 1;
while (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 i = l, j = h + 1;
while (true) {
@@ -1909,8 +1901,7 @@ private int partition(int[] nums, int l, int h)
return j;
}
-private void swap(int[] nums, int i, int j)
-{
+private void swap(int[] nums, int i, int j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
@@ -1927,8 +1918,7 @@ private void swap(int[] nums, int i, int j)
维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。
```java
-public ArrayList GetLeastNumbers_Solution(int[] nums, int k)
-{
+public ArrayList GetLeastNumbers_Solution(int[] nums, int k) {
if (k > nums.length || k <= 0)
return new ArrayList<>();
PriorityQueue maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
@@ -1959,8 +1949,7 @@ private PriorityQueue right = new PriorityQueue<>();
/* 当前数据流读入的元素个数 */
private int N = 0;
-public void Insert(Integer val)
-{
+public void Insert(Integer val) {
/* 插入要保证两个堆存于平衡状态 */
if (N % 2 == 0) {
/* N 为偶数的情况下插入到右半边。
@@ -1975,8 +1964,7 @@ public void Insert(Integer val)
N++;
}
-public Double GetMedian()
-{
+public Double GetMedian() {
if (N % 2 == 0)
return (left.peek() + right.peek()) / 2.0;
else
@@ -1998,16 +1986,14 @@ public Double GetMedian()
private int[] cnts = new int[256];
private Queue queue = new LinkedList<>();
-public void Insert(char ch)
-{
+public void Insert(char ch) {
cnts[ch]++;
queue.add(ch);
while (!queue.isEmpty() && cnts[queue.peek()] > 1)
queue.poll();
}
-public char FirstAppearingOnce()
-{
+public char FirstAppearingOnce() {
return queue.isEmpty() ? '#' : queue.peek();
}
```
@@ -2023,8 +2009,7 @@ public char FirstAppearingOnce()
## 解题思路
```java
-public int FindGreatestSumOfSubArray(int[] nums)
-{
+public int FindGreatestSumOfSubArray(int[] nums) {
if (nums == null || nums.length == 0)
return 0;
int greatestSum = Integer.MIN_VALUE;
@@ -2044,8 +2029,7 @@ public int FindGreatestSumOfSubArray(int[] nums)
## 解题思路
```java
-public int NumberOf1Between1AndN_Solution(int n)
-{
+public int NumberOf1Between1AndN_Solution(int n) {
int cnt = 0;
for (int m = 1; m <= n; m *= 10) {
int a = n / m, b = n % m;
@@ -2066,8 +2050,7 @@ public int NumberOf1Between1AndN_Solution(int n)
## 解题思路
```java
-public int getDigitAtIndex(int index)
-{
+public int getDigitAtIndex(int index) {
if (index < 0)
return -1;
int place = 1; // 1 表示个位,2 表示 十位...
@@ -2085,8 +2068,7 @@ public int getDigitAtIndex(int index)
* place 位数的数字组成的字符串长度
* 10, 90, 900, ...
*/
-private int getAmountOfPlace(int place)
-{
+private int getAmountOfPlace(int place) {
if (place == 1)
return 10;
return (int) Math.pow(10, place - 1) * 9;
@@ -2096,8 +2078,7 @@ private int getAmountOfPlace(int place)
* place 位数的起始数字
* 0, 10, 100, ...
*/
-private int getBeginNumberOfPlace(int place)
-{
+private int getBeginNumberOfPlace(int place) {
if (place == 1)
return 0;
return (int) Math.pow(10, place - 1);
@@ -2106,8 +2087,7 @@ private int getBeginNumberOfPlace(int place)
/**
* 在 place 位数组成的字符串中,第 index 个数
*/
-private int getDigitAtIndex(int index, int place)
-{
+private int getDigitAtIndex(int index, int place) {
int beginNumber = getBeginNumberOfPlace(place);
int shiftNumber = index / place;
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 排在前面。
```java
-public String PrintMinNumber(int[] numbers)
-{
+public String PrintMinNumber(int[] numbers) {
if (numbers == null || numbers.length == 0)
return "";
int n = numbers.length;
@@ -2156,8 +2135,7 @@ public String PrintMinNumber(int[] numbers)
## 解题思路
```java
-public int numDecodings(String s)
-{
+public int numDecodings(String s) {
if (s == null || s.length() == 0)
return 0;
int n = s.length();
@@ -2200,8 +2178,7 @@ public int numDecodings(String s)
应该用动态规划求解,而不是深度优先搜索,深度优先搜索过于复杂,不是最优解。
```java
-public int getMost(int[][] values)
-{
+public int getMost(int[][] values) {
if (values == null || values.length == 0 || values[0].length == 0)
return 0;
int n = values[0].length;
@@ -2224,8 +2201,7 @@ public int getMost(int[][] values)
## 解题思路
```java
-public int longestSubStringWithoutDuplication(String str)
-{
+public int longestSubStringWithoutDuplication(String str) {
int curLen = 0;
int maxLen = 0;
int[] preIndexs = new int[26];
@@ -2257,8 +2233,7 @@ public int longestSubStringWithoutDuplication(String str)
## 解题思路
```java
-public int GetUglyNumber_Solution(int N)
-{
+public int GetUglyNumber_Solution(int N) {
if (N <= 6)
return N;
int i2 = 0, i3 = 0, i5 = 0;
@@ -2291,8 +2266,7 @@ public int GetUglyNumber_Solution(int N)
最直观的解法是使用 HashMap 对出现次数进行统计,但是考虑到要统计的字符范围有限,因此可以使用整型数组代替 HashMap。
```java
-public int FirstNotRepeatingChar(String str)
-{
+public int FirstNotRepeatingChar(String str) {
int[] cnts = new int[256];
for (int i = 0; i < str.length(); i++)
cnts[str.charAt(i)]++;
@@ -2306,8 +2280,7 @@ public int FirstNotRepeatingChar(String str)
以上实现的空间复杂度还不是最优的。考虑到只需要找到只出现一次的字符,那么我们只需要统计的次数信息只有 0,1,更大,使用两个比特位就能存储这些信息。
```java
-public int FirstNotRepeatingChar2(String str)
-{
+public int FirstNotRepeatingChar2(String str) {
BitSet bs1 = new BitSet(256);
BitSet bs2 = new BitSet(256);
for (char c : str.toCharArray()) {
@@ -2339,15 +2312,13 @@ public int FirstNotRepeatingChar2(String str)
private long cnt = 0;
private int[] tmp; // 在这里创建辅助数组,而不是在 merge() 递归函数中创建
-public int InversePairs(int[] nums)
-{
+public int InversePairs(int[] nums) {
tmp = new int[nums.length];
mergeSort(nums, 0, nums.length - 1);
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)
return;
int m = l + (h - l) / 2;
@@ -2356,8 +2327,7 @@ private void mergeSort(int[] nums, int l, int 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;
while (i <= m || j <= h) {
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 两个链表的指针能同时访问到交点。
```java
-public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2)
-{
+public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode l1 = pHead1, l2 = pHead2;
while (l1 != l2) {
l1 = (l1 == null) ? pHead2 : l1.next;
@@ -2420,15 +2389,13 @@ Output:
## 解题思路
```java
-public int GetNumberOfK(int[] nums, int K)
-{
+public int GetNumberOfK(int[] nums, int K) {
int first = binarySearch(nums, K);
int last = binarySearch(nums, K + 1);
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;
while (l < h) {
int m = l + (h - l) / 2;
@@ -2453,14 +2420,12 @@ private int binarySearch(int[] nums, int K)
private TreeNode ret;
private int cnt = 0;
-public TreeNode KthNode(TreeNode pRoot, int k)
-{
+public TreeNode KthNode(TreeNode pRoot, int k) {
inOrder(pRoot, k);
return ret;
}
-private void inOrder(TreeNode root, int k)
-{
+private void inOrder(TreeNode root, int k) {
if (root == null || cnt >= k)
return;
inOrder(root.left, k);
@@ -2484,8 +2449,7 @@ private void inOrder(TreeNode root, int k)
## 解题思路
```java
-public int TreeDepth(TreeNode root)
-{
+public int TreeDepth(TreeNode root) {
return root == null ? 0 : 1 + Math.max(TreeDepth(root.left), TreeDepth(root.right));
}
```
@@ -2505,14 +2469,12 @@ public int TreeDepth(TreeNode root)
```java
private boolean isBalanced = true;
-public boolean IsBalanced_Solution(TreeNode root)
-{
+public boolean IsBalanced_Solution(TreeNode root) {
height(root);
return isBalanced;
}
-private int height(TreeNode root)
-{
+private int height(TreeNode root) {
if (root == null || !isBalanced)
return 0;
int left = height(root.left);
@@ -2538,8 +2500,7 @@ private int height(TreeNode root)
diff &= -diff 得到出 diff 最右侧不为 0 的位,也就是不存在重复的两个元素在位级表示上最右侧不同的那一位,利用这一位就可以将两个元素区分开来。
```java
-public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
-{
+public void FindNumsAppearOnce(int[] nums, int num1[], int num2[]) {
int diff = 0;
for (int num : nums)
diff ^= num;
@@ -2570,8 +2531,7 @@ public void FindNumsAppearOnce(int[] nums, int num1[], int num2[])
- 如果 sum < target,移动较小的元素,使 sum 变大一些。
```java
-public ArrayList FindNumbersWithSum(int[] array, int sum)
-{
+public ArrayList FindNumbersWithSum(int[] array, int sum) {
int i = 0, j = array.length - 1;
while (i < j) {
int cur = array[i] + array[j];
@@ -2604,8 +2564,7 @@ public ArrayList FindNumbersWithSum(int[] array, int sum)
## 解题思路
```java
-public ArrayList> FindContinuousSequence(int sum)
-{
+public ArrayList> FindContinuousSequence(int sum) {
ArrayList> ret = new ArrayList<>();
int start = 1, end = 2;
int curSum = 3;
@@ -2648,8 +2607,7 @@ public ArrayList> FindContinuousSequence(int sum)
正确的解法应该是和书上一样,先旋转每个单词,再旋转整个字符串。
```java
-public String ReverseSentence(String str)
-{
+public String ReverseSentence(String str) {
int n = str.length();
char[] chars = str.toCharArray();
int i = 0, j = 0;
@@ -2664,14 +2622,12 @@ public String ReverseSentence(String str)
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)
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];
c[i] = c[j];
c[j] = t;
@@ -2691,8 +2647,7 @@ private void swap(char[] c, int i, int j)
先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。
```java
-public String LeftRotateString(String str, int n)
-{
+public String LeftRotateString(String str, int n) {
if (n >= str.length())
return str;
char[] chars = str.toCharArray();
@@ -2702,14 +2657,12 @@ public String LeftRotateString(String str, int n)
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)
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];
chars[i] = chars[j];
chars[j] = t;
@@ -2727,8 +2680,7 @@ private void swap(char[] chars, int i, int j)
## 解题思路
```java
-public ArrayList maxInWindows(int[] num, int size)
-{
+public ArrayList maxInWindows(int[] num, int size) {
ArrayList ret = new ArrayList<>();
if (size > num.length || size < 1)
return ret;
@@ -2762,8 +2714,7 @@ public ArrayList maxInWindows(int[] num, int size)
空间复杂度:O(N2)
```java
-public List> dicesSum(int n)
-{
+public List> dicesSum(int n) {
final int face = 6;
final int pointNum = face * n;
long[][] dp = new long[n + 1][pointNum + 1];
@@ -2790,8 +2741,7 @@ public List> dicesSum(int n)
空间复杂度:O(N)
```java
-public List> dicesSum(int n)
-{
+public List> dicesSum(int n) {
final int face = 6;
final int pointNum = face * n;
long[][] dp = new long[2][pointNum + 1];
@@ -2829,8 +2779,7 @@ public List> dicesSum(int n)
## 解题思路
```java
-public boolean isContinuous(int[] nums)
-{
+public boolean isContinuous(int[] nums) {
if (nums.length < 5)
return false;
Arrays.sort(nums);
@@ -2861,8 +2810,7 @@ public boolean isContinuous(int[] nums)
约瑟夫环,圆圈长度为 n 的解可以看成长度为 n-1 的解再加上报数的长度 m。因为是圆圈,所以最后需要对 n 取余。
```java
-public int LastRemaining_Solution(int n, int m)
-{
+public int LastRemaining_Solution(int n, int m) {
if (n == 0) /* 特殊输入的处理 */
return -1;
if (n == 1) /* 返回条件 */
@@ -2884,8 +2832,7 @@ public int LastRemaining_Solution(int n, int m)
使用贪心策略,假设第 i 轮进行卖出操作,买入操作价格应该在 i 之前并且价格最低。
```java
-public int maxProfit(int[] prices)
-{
+public int maxProfit(int[] prices) {
if (prices == null || prices.length == 0)
return 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。
```java
-public int Sum_Solution(int n)
-{
+public int Sum_Solution(int n) {
int sum = n;
boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0);
return sum;
@@ -2938,8 +2884,7 @@ a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进
递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。
```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);
}
```
@@ -2955,8 +2900,7 @@ public int Add(int a, int b)
## 解题思路
```java
-public int[] multiply(int[] A)
-{
+public int[] multiply(int[] A) {
int n = A.length;
int[] B = new int[n];
for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */
@@ -2988,8 +2932,7 @@ Output:
## 解题思路
```java
-public int StrToInt(String str)
-{
+public int StrToInt(String str) {
if (str == null || str.length() == 0)
return 0;
boolean isNegative = str.charAt(0) == '-';
diff --git a/notes/攻击技术.md b/notes/攻击技术.md
index c99071a0..d5a735ba 100644
--- a/notes/攻击技术.md
+++ b/notes/攻击技术.md
@@ -29,7 +29,7 @@
## 危害
-- 窃取用户的 Cookie 值
+- 窃取用户的 Cookie
- 伪造虚假的输入表单骗取个人信息
- 显示伪造的文章或者图片
@@ -47,7 +47,7 @@
富文本编辑器允许用户输入 HTML 代码,就不能简单地将 `<` 等字符进行过滤了,极大地提高了 XSS 攻击的可能性。
-富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,可以定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
+富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,通过定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
以下例子中,form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。
@@ -131,7 +131,7 @@ http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName
。
```
-如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。
+如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 美元。
这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
@@ -153,8 +153,6 @@ Referer 首部字段位于 HTTP 报文中,用于标识请求来源的地址。
因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。
-也可以要求用户输入验证码来进行校验。
-
# 三、SQL 注入攻击
## 概念
diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md
index 151ffc0f..e7ef804b 100644
--- a/notes/数据库系统原理.md
+++ b/notes/数据库系统原理.md
@@ -383,13 +383,13 @@ MVCC 不能解决幻读的问题,Next-Key Locks 就是为了解决这个问题
## Record Locks
-锁定的对象是记录的索引,而不是记录本身。
+锁定一个记录上的索引,而不是记录本身。
如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚集索引,因此 Record Locks 依然可以使用。
## Gap Locks
-锁定一个范围内的索引,例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。
+锁定索引之间的间隙,但是不包含索引本身。例如当一个事务执行以下语句,其它事务就不能在 t.c 中插入 15。
```sql
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
@@ -397,28 +397,14 @@ SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
## Next-Key Locks
-它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录,也锁定范围内的索引。在 user 中有以下记录:
+它是 Record Locks 和 Gap Locks 的结合,不仅锁定一个记录上的索引,也锁定范围内的索引。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间:
```sql
-| id | last_name | first_name | age |
-|------|-------------|--------------|-------|
-| 4 | stark | tony | 21 |
-| 1 | tom | hiddleston | 30 |
-| 3 | morgan | freeman | 40 |
-| 5 | jeff | dean | 50 |
-| 2 | donald | trump | 80 |
-+------|-------------|--------------|-------+
-```
-
-那么就需要锁定以下范围:
-
-```sql
-(-∞, 21]
-(21, 30]
-(30, 40]
-(40, 50]
-(50, 80]
-(80, ∞)
+(negative infinity, 10]
+(10, 11]
+(11, 13]
+(13, 20]
+(20, positive infinity)
```
# 七、关系数据库设计理论
diff --git a/notes/算法.md b/notes/算法.md
index fb878742..cb5d4dbe 100644
--- a/notes/算法.md
+++ b/notes/算法.md
@@ -1623,10 +1623,10 @@ private List keys(Node x, Key l, Key h) {
## 2-3 查找树
-
-
2-3 查找树引入了 2- 节点和 3- 节点,目的是为了让树平衡。一颗完美平衡的 2-3 查找树的所有空链接到根节点的距离应该是相同的。
+
+
### 1. 插入操作
插入操作和 BST 的插入操作有很大区别,BST 的插入操作是先进行一次未命中的查找,然后再将节点插入到对应的空链接上。但是 2-3 查找树如果也这么做的话,那么就会破坏了平衡性。它是将新节点插入到叶子节点上。
@@ -2042,24 +2042,26 @@ public class SparseVector {
## 汉诺塔
+
+
这是一个经典的递归问题,分为三步求解:
-1. 将 n-1 个圆盘从 from -> buffer
-2. 将 1 个圆盘从 from -> to
-3. 将 n-1 个圆盘从 buffer -> to
+- 将 n-1 个圆盘从 from -> buffer
+
+
+
+- 将 1 个圆盘从 from -> to
+
+
+
+- 将 n-1 个圆盘从 buffer -> to
+
+
如果只有一个圆盘,那么只需要进行一次移动操作。
从上面的讨论可以知道,an = 2 * an-1 + 1,显然 an = 2n - 1,n 个圆盘需要移动 2n - 1 次。
-
-
-
-
-
-
-
-
```java
public class Hanoi {
public static void move(int n, String from, String buffer, String to) {
@@ -2105,7 +2107,7 @@ from H1 to H3
生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到根节点,根节点代表的字符的编码就是这个路径编码。
-
+
```java
public class Huffman {
diff --git a/notes/系统设计基础.md b/notes/系统设计基础.md
index 40f0ca9d..805764a9 100644
--- a/notes/系统设计基础.md
+++ b/notes/系统设计基础.md
@@ -79,7 +79,7 @@
实现可扩展主要有两种方式:
- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信;
-- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以用过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
+- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以通过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
# 四、可用性
diff --git a/notes/设计模式.md b/notes/设计模式.md
index ae44213e..b8f4dc9b 100644
--- a/notes/设计模式.md
+++ b/notes/设计模式.md
@@ -98,7 +98,9 @@ public static synchronized Singleton getUniqueInstance() {
(三)饿汉式-线程安全
-线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的优势。
+线程不安全问题主要是由于 uniqueInstance 被实例化了多次,如果 uniqueInstance 采用直接实例化的话,就不会被实例化多次,也就不会产生线程不安全问题。
+
+但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
```java
private static Singleton uniqueInstance = new Singleton();
@@ -106,7 +108,7 @@ private static Singleton uniqueInstance = new Singleton();
(四)双重校验锁-线程安全
-uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行。也就是说,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
+uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
@@ -131,7 +133,7 @@ public class Singleton {
}
```
-考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,也就是说会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
+考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程同时执行 if 语句,那么两个线程就会同时进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 `uniqueInstance = new Singleton();` 这条语句,只是先后的问题,那么就会进行两次实例化,从而产生了两个实例。因此必须使用双重校验锁,也就是需要使用两个 if 语句。
```java
if (uniqueInstance == null) {
@@ -157,7 +159,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的。`uniqueIns
这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。
-```source-java
+```java
public class Singleton {
private Singleton() {
@@ -299,7 +301,7 @@ public class Client {
### 意图
-定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化推迟到子类。
+定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
### 类图
@@ -1851,11 +1853,7 @@ No gumball dispensed
### 与状态模式的比较
-状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。
-
-但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。
-
-所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
+状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
@@ -1969,7 +1967,7 @@ public abstract class CaffeineBeverage {
```
```java
-public class Coffee extends CaffeineBeverage{
+public class Coffee extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Coffee.brew");
@@ -1983,7 +1981,7 @@ public class Coffee extends CaffeineBeverage{
```
```java
-public class Tea extends CaffeineBeverage{
+public class Tea extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Tea.brew");
@@ -2238,7 +2236,7 @@ Number of items: 6
### 意图
-使用什么都不做的空对象来替代 NULL。
+使用什么都不做的空对象来代替 NULL。
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
@@ -2393,7 +2391,7 @@ public abstract class TV {
```
```java
-public class Sony extends TV{
+public class Sony extends TV {
@Override
public void on() {
System.out.println("Sony.on()");
@@ -2412,7 +2410,7 @@ public class Sony extends TV{
```
```java
-public class RCA extends TV{
+public class RCA extends TV {
@Override
public void on() {
System.out.println("RCA.on()");
@@ -2551,9 +2549,6 @@ public abstract class Component {
```
```java
-import java.util.ArrayList;
-import java.util.List;
-
public class Composite extends Component {
private List child;
@@ -2659,7 +2654,7 @@ Composite:root
### 类图
-装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
+装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。
@@ -2770,7 +2765,7 @@ public class Client {
### 实现
-观看电影需要操作很多电器,使用外观模式可以实现一键看电影功能。
+观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
```java
public class SubSystem {
@@ -2811,7 +2806,7 @@ public class Client {
### 设计原则
-最少知识原则:只和你的密友谈话。也就是客户对象所需要交互的对象应当尽可能少。
+最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
## 6. 享元(Flyweight)
@@ -2822,8 +2817,8 @@ public class Client {
### 类图
- Flyweight:享元对象
-- IntrinsicState:内部状态,相同的项元对象共享
-- ExtrinsicState:外部状态
+- IntrinsicState:内部状态,享元对象共享内部状态
+- ExtrinsicState:外部状态,每个享元对象的外部状态不同
@@ -2854,8 +2849,6 @@ public class ConcreteFlyweight implements Flyweight {
```
```java
-import java.util.HashMap;
-
public class FlyweightFactory {
private HashMap flyweights = new HashMap<>();
diff --git a/pics/VP4n3i8m34Ntd28NQ4_0KCJ2q044Oez.png b/pics/VP4n3i8m34Ntd28NQ4_0KCJ2q044Oez.png
new file mode 100644
index 00000000..c77f4ff8
Binary files /dev/null and b/pics/VP4n3i8m34Ntd28NQ4_0KCJ2q044Oez.png differ