diff --git a/.gitignore b/.gitignore
index 5ff56e58..b4a52775 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,30 +1,3 @@
-/target/
-!.mvn/wrapper/maven-wrapper.jar
-/logs
-.svn
-
-### STS or Eclipse ###
-.apt_generated
-.classpath
-.factorypath
-.project
-.settings
-.springBeans
-.sts4-cache
-
-### IntelliJ IDEA ###
-.idea
-*.iws
+.idea/
+target/
*.iml
-*.ipr
-
-### NetBeans ###
-/nbproject/private/
-/nbbuild/
-/dist/
-/nbdist/
-/.nb-gradle/
-/build/
-
-### VS Code ###
-.vscode/
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..8dada3ed
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
index d1142214..e9df21cd 100644
--- a/README.md
+++ b/README.md
@@ -1,102 +1,18 @@
-# 1. Java基础
-
-
-
-# 2. 设计模式
-
-[设计模式](https://github.com/ggb2312/JavaNotes/tree/master/design-pattern)
-
-## 2.1 前导
-
-学设计模式最好有以下两点知识基础。
-
-[软件设计七大原则](https://www.cnblogs.com/gj-blog/p/10929440.html)
-
-[UML类图以及类与类之间的关系](https://www.cnblogs.com/gj-blog/p/10929447.html)
-
-原因:
-- 设计模式是围绕着软件设计七大原则来说的,每一种设计模式通常会满足一到两三种软件设计原则,同时又可能违反某一种原则。学习这些原则可以理解该设计模式适用什么业务场景以及为什么要这样做。
-- 设计模式通常都伴随着面向对象的特性(封装、继承、多态),所以会涉及到大量的类与类之间的关系。通过画类图可以更好的理解设计模式。
-
-## 2.2 设计模式分类
-
-### 2.2.1 创建型
-
-[创建型模式——简单工厂(不在GOF23种设计模式中)](https://www.cnblogs.com/gj-blog/p/10929455.html)
-
-[创建型模式——工厂方法(一)](https://www.cnblogs.com/gj-blog/p/10929460.html)
-
-[创建型模式——抽象工厂(二)](https://www.cnblogs.com/gj-blog/p/10929466.html)
-
-[创建型模式——建造者模式(三)](https://www.cnblogs.com/gj-blog/p/10929475.html)
-
-[创建型模式——单例模式(四)](https://www.cnblogs.com/gj-blog/p/10929478.html)
-
-[创建型模式——原型模式(五)](https://www.cnblogs.com/gj-blog/p/10929480.html)
-
-### 2.2.2 结构型
-
-[结构型模式——外观模式(一)](https://www.cnblogs.com/gj-blog/p/10929486.html)
-
-[结构型模式——装饰者模式(二)](https://www.cnblogs.com/gj-blog/p/10929489.html)
-
-[结构型模式——适配器模式(三)](https://www.cnblogs.com/gj-blog/p/10929498.html)
-
-[结构型模式——享元模式(四)](https://www.cnblogs.com/gj-blog/p/10929493.html)
-
-[结构型模式——组合模式(五)](https://www.cnblogs.com/gj-blog/p/10929506.html)
-
-[结构型模式——桥接模式(六)](https://www.cnblogs.com/gj-blog/p/10929510.html)
-
-[结构型模式——代理模式(七)](https://www.cnblogs.com/gj-blog/p/10929514.html)
-
-### 2.2.3 行为型
-
-[行为型模式——模板方法(一)](https://www.cnblogs.com/gj-blog/p/10929517.html)
-
-[行为型模式——迭代器模式(二)](https://www.cnblogs.com/gj-blog/p/10929524.html)
-
-[行为型模式——策略模式(三)](https://www.cnblogs.com/gj-blog/p/10929544.html)
-
-[行为型模式——解释器模式(四)](https://www.cnblogs.com/gj-blog/p/10929551.html)
-
-[行为型模式——观察者模式(五)](https://www.cnblogs.com/gj-blog/p/10929556.html)
-
-[行为型模式——备忘录模式(六)](https://www.cnblogs.com/gj-blog/p/10929563.html)
-
-[行为型模式——命令模式(七)](https://www.cnblogs.com/gj-blog/p/10929571.html)
-
-[行为型模式——中介者模式(八)](https://www.cnblogs.com/gj-blog/p/10929579.html)
-
-[行为型模式——责任链模式(九)](https://www.cnblogs.com/gj-blog/p/10929600.html)
-
-[行为型模式——访问者模式(十)](https://www.cnblogs.com/gj-blog/p/10929607.html)
-
-[行为型模式——状态模式(十一)](https://www.cnblogs.com/gj-blog/p/10929616.html)
-
-# 3. springboot-integration-examples
-
-[springboot-integration-examples](https://github.com/ggb2312/JavaNotes/tree/master/springboot-integration-examples)
-
-基于最新的 SpringBoot2.0+
-
-## 3.1 Idea新建一个SpringBoot项目
-
- [Idea新建一个SpringBoot项目](https://www.cnblogs.com/gj-blog/p/10803591.html)
-
-## 3.2 springboot-oss(SpringBoot整合阿里云OSS文件上传、下载、查看、删除)
-
- [SpringBoot整合阿里云OSS文件上传、下载、查看、删除](https://www.cnblogs.com/gj-blog/p/10789544.html)
-
-## 3.3 springboot-aoplog(SpringBoot整合aop日志管理)
-
- [SpringBoot整合aop日志管理](https://www.cnblogs.com/gj-blog/p/10803600.html)
-
-## 3.4 springboot-wx(SpringBoot整合微信小程序登录)
-
- [SpringBoot整合微信小程序登录](https://www.cnblogs.com/gj-blog/p/10803607.html)
-
-## 3.5 springboot-mybatisplus(SpringBoot整合MybatisPlus)
-
-[SpringBoot整合MybatisPlus](https://www.cnblogs.com/gj-blog/p/10803593.html)
+```
+├─algorithms 算法
+├─database 数据库
+├─distributed 分布式系统
+├─framework 一些常用类库
+├─java-advance 高级类库和jsr规范等
+├─java-basic Java基础
+├─lesson-code 一些课程代码
+├─mvc mvc框架
+├─orm orm框架
+├─spring-annotation spring注解驱动开发
+├─spring-batch
+├─springboot-aop springaop使用以及aop应用
+│
+└─
+
+```
diff --git a/algorithms/README.MD b/algorithms/README.MD
new file mode 100644
index 00000000..23e4b53e
--- /dev/null
+++ b/algorithms/README.MD
@@ -0,0 +1,55 @@
+
+
+```
+├─algorithm
+│
+├─datastructure 数据结构与常见算法
+│ │
+│ ├─graph 图
+│ │
+│ ├─hashtab 哈希表
+│ │
+│ ├─linkedlist 链表
+│ │
+│ ├─queue 队列
+│ │
+│ ├─recursion 递归
+│ │
+│ ├─search 搜索
+│ │
+│ ├─sort 排序
+│ │
+│ ├─sparsematrix 矩阵
+│ │
+│ ├─stack 栈
+│ │
+│ └─tree 树
+│
+├─leetcode leetcode对应数据结构刷题代码
+│ │
+│ ├─array 数组
+│ │
+│ ├─linkedlist 链表
+│ │
+│ ├─hashtable 哈希表
+│ │
+│ ├─stackqueue 栈与队列
+│ │
+│ ├─recurionbacktracking 递归与回溯
+│ │
+│ ├─binarytree 二叉树
+│ │
+│ ├─dynamic 动态规划
+│ │
+│ ├─greedy 贪心
+│ │
+│ └─other 其他
+│
+├─offer
+│
+├─thread
+│
+└─interview
+```
+
+
diff --git a/algorithms/algorithm-common/pom.xml b/algorithms/algorithm-common/pom.xml
new file mode 100644
index 00000000..3afe2696
--- /dev/null
+++ b/algorithms/algorithm-common/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ algorithms
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ algorithm-common
+
+
+
\ No newline at end of file
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtils.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtils.java
new file mode 100644
index 00000000..893e41f3
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtils.java
@@ -0,0 +1,97 @@
+package cn.lastwhisper.leetcode.common.array;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author lastwhisper
+ * @date 2020/2/4
+ */
+public class ArrayUtils {
+
+ /**
+ * {"ab","cd"}=>
+ * 创建二维字符数组
+ */
+ public static char[][] createCharArrays(String[] arr) {
+ int m = arr.length, n = arr[0].length();
+ char[][] chars = new char[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ chars[i][j] = arr[i].charAt(j);
+ }
+ }
+ return chars;
+ }
+
+ /**
+ * {{"a","b","c","d"}}=>
+ * 创建二维字符数组
+ */
+ public static char[][] createCharArrays(String[][] arr) {
+ int m = arr.length, n = arr[0].length;
+ char[][] chars = new char[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ chars[i][j] = arr[i][j].charAt(0);
+ }
+ }
+ return chars;
+ }
+
+ /**
+ * 创建二维数组
+ */
+ public static int[][] createIntArrays(String[] arr) {
+ int m = arr.length, n = arr[0].length();
+ int[][] ints = new int[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ ints[i][j] = arr[i].charAt(j) - '0';
+ }
+ }
+ return ints;
+ }
+
+
+ /**
+ * 创建list
+ */
+ public static List> createList(int[][] arrays) {
+ List> lists = new ArrayList<>(arrays.length);
+ for (int[] array : arrays) {
+ List list = new ArrayList<>(array.length);
+ for (int num : array) {
+ list.add(num);
+ }
+ lists.add(list);
+ }
+ return lists;
+ }
+
+
+ /**
+ * 生成无序数组
+ * @param length 生成数组的长度
+ * @param region 生成数字的范围
+ */
+ public static int[] generateArrByRandom(int length, int region) {
+ int[] arr = new int[length];
+ for (int i = 0; i < length; i++) {
+ arr[i] = (int) (Math.random() * region); // region=8000000 生成一个[0,8000000) 的一个数
+ }
+ return arr;
+ }
+
+ /**
+ * 生成n~0倒序数组
+ */
+ public static int[] generateArrByOrder(int n) {
+ int[] arr = new int[n];
+ for (int i = n - 1; i > 0; i--) {
+ arr[i] = i + 1;
+ }
+ return arr;
+ }
+}
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtils.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtils.java
new file mode 100644
index 00000000..01c2d3ce
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtils.java
@@ -0,0 +1,289 @@
+package cn.lastwhisper.leetcode.common.linkedlist;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 操作链表工具类
+ * @author lastwhisper
+ * @date 1/7/2020
+ */
+public class LinkedListUtils {
+
+ /**
+ * 创建单链表
+ */
+ public static ListNode createListNode(int... arr) {
+ if (arr == null) {
+ return null;
+ }
+ ListNode head = new ListNode(arr[0]);
+ ListNode current = head;
+
+ for (int i = 1; i < arr.length; i++) {
+ current.next = new ListNode(arr[i]);
+ current = current.next;
+ }
+ return head;
+ }
+
+ /**
+ * 创建环形链表
+ * 应用题目:https://leetcode-cn.com/problems/linked-list-cycle/
+ * @param pos 成环位置
+ * @param arr 链表元素
+ */
+ public static ListNode createLoopListNode(int pos, int... arr) {
+ if (arr == null) {
+ return null;
+ }
+ ListNode head = new ListNode(arr[0]);
+ ListNode current = head;
+ // 锚点,记录成环的位置
+ ListNode anchor = null;
+ if (pos == 0) {
+ anchor = head;
+ }
+ for (int i = 1; i < arr.length; i++) {
+ current.next = new ListNode(arr[i]);
+ // 记录成环位置
+ if (pos == i) {
+ if (pos == arr.length - 1) {
+ anchor = current;
+ } else {
+ anchor = current.next;
+ }
+ }
+ current = current.next;
+ }
+ current.next = anchor;
+ return head;
+ }
+
+ /**
+ * 创建两个相交链表
+ * 应用题目:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
+ * @param intersectVal 相交点的值
+ * @param listA 链表A元素数组
+ * @param listB 链表B元素数组
+ * @param skipA 链表A相交前节点数
+ * @param skipB 链表B相交前节点数
+ */
+ public static List createIntersectListNode(
+ int intersectVal, int[] listA, int[] listB, int skipA, int skipB) {
+ List listNodes = new ArrayList<>(2);
+
+ ListNode listNodeA = new ListNode(listA[0]);
+ ListNode currentA = listNodeA;
+ ListNode listNodeB = new ListNode(listB[0]);
+ ListNode currentB = listNodeB;
+
+ for (int i = 1; i < skipA; i++) {
+ currentA.next = new ListNode(listA[i]);
+ currentA = currentA.next;
+ }
+
+ for (int i = 1; i < skipB; i++) {
+ currentB.next = new ListNode(listB[i]);
+ currentB = currentB.next;
+ }
+
+ ListNode intersectListNode = new ListNode(listA[skipA]);
+ ListNode currentIntersect = intersectListNode;
+
+ for (int i = skipA + 1; i < listA.length; i++) {
+ currentIntersect.next = new ListNode(listA[i]);
+ currentIntersect = currentIntersect.next;
+ }
+
+ currentA.next = intersectListNode;
+ currentB.next = intersectListNode;
+
+ listNodes.add(listNodeA);
+ listNodes.add(listNodeB);
+
+ return listNodes;
+ }
+
+ private static ListNode createListNode(String arr) {
+ if (arr == null) {
+ return null;
+ }
+ ListNode head = new ListNode(arr.charAt(0) - '0');
+ ListNode current = head;
+
+ for (int i = 1; i < arr.length(); i++) {
+ current.next = new ListNode(arr.charAt(i) - '0');
+ current = current.next;
+ }
+ return head;
+ }
+
+ /**
+ * 反序创建链表
+ */
+ private static ListNode createReverseListNode(String arr) {
+ if (arr == null) {
+ return null;
+ }
+
+ ListNode head = new ListNode(arr.charAt(arr.length() - 1) - '0');
+ ListNode current = head;
+
+ for (int i = arr.length() - 2; i >= 0; i--) {
+ current.next = new ListNode(arr.charAt(i) - '0');
+ current = current.next;
+ }
+ return head;
+ }
+
+ /**
+ * 两个字符串整数相加
+ */
+ public String add(String a, String b) {
+ StringBuilder sb = new StringBuilder();
+ // 进位
+ int carry = 0;
+ int aIndex = a.length() - 1, bIndex = b.length() - 1;
+
+ while (aIndex >= 0 || bIndex >= 0) {
+ int x, y, sum;
+ x = aIndex >= 0 ? a.charAt(aIndex) - '0' : 0;
+ y = bIndex >= 0 ? b.charAt(bIndex) - '0' : 0;
+
+ sum = carry + x + y;
+ carry = sum / 10;
+ sb.append(sum % 10);
+
+ aIndex--;
+ bIndex--;
+ }
+
+ if (carry > 0) {
+ sb.append(carry);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 打印单链表
+ */
+ public static void printListNode(String msg, ListNode head) {
+ System.out.println(msg + appendVal(head));
+ }
+
+ /**
+ * 打印单链表
+ */
+ public static void printListNode(ListNode head) {
+ System.out.println(appendVal(head));
+ }
+
+ private static String appendVal(ListNode head) {
+ StringBuilder sb = new StringBuilder();
+ ListNode current = head;
+ while (current != null) {
+ sb.append(current.val);
+ sb.append("->");
+ current = current.next;
+ }
+ sb.append("NULL");
+ return sb.toString();
+ }
+
+ /**
+ * 反转链表
+ */
+ public static ListNode reverseListNode(ListNode head) {
+ ListNode prev = null;
+ ListNode current = head;
+ ListNode next = null;
+
+ while (current != null) {
+ next = current.next;
+
+ current.next = prev;
+ prev = current;
+ current = next;
+ }
+ return prev;
+ }
+
+ /**
+ * 交替合并两个链表,返回新的链表
+ */
+ public static ListNode alterNoteMerge(ListNode l1, ListNode l2) {
+
+ return null;
+ }
+
+ /**
+ * 交替合并两个链表,到第一个链表
+ * https://leetcode-cn.com/problems/reorder-list
+ */
+ public static void alterNoteMergeOne(ListNode firstHead, ListNode secondHead) {
+ ListNode firstNext;
+ ListNode secondNext;
+
+ while (secondHead != null) {
+ // 1.记录两个链表的next节点
+ firstNext = firstHead.next;
+ secondNext = secondHead.next;
+ // 2.将链表1的当前节点指向链表2的当前节点,链表2的当前节点指向链表1的下一个节点
+ // 左链表:1->2->3 右链表:5->4
+ // 合并后的链表1:1->5->2
+ firstHead.next = secondHead;
+ secondHead.next = firstNext;
+ // 3.将之前保存的next更新为当前节点,继续循环
+ firstHead = firstNext;
+ secondHead = secondNext;
+ // 防止奇数节点链表成环 https://leetcode-cn.com/problems/reorder-list/
+ if (secondHead == null) {
+ firstHead.next = null;
+ }
+ }
+ }
+
+ /**
+ * 找到链表中间的前一个节点
+ */
+ public static ListNode middleBeforeNode(ListNode head) {
+ ListNode fast = head;
+ ListNode slow = head;
+ while (fast.next != null && fast.next.next != null) {
+ fast = fast.next.next;
+ slow = slow.next;
+ }
+ return slow;
+ }
+
+ /**
+ * 找到链表中间的一个节点
+ * https://leetcode-cn.com/problems/middle-of-the-linked-list/
+ */
+ public static ListNode middleNode(ListNode head) {
+ ListNode fast = head;
+ ListNode slow = head;
+ while (fast != null && fast.next != null) {
+ fast = fast.next.next;
+ slow = slow.next;
+ }
+ return slow;
+ }
+
+ public static void main(String[] args) {
+ //printListNode(createListNode(1, 2, 3, 4, 5, 6, 7));
+ //ListNode headLoop = createLoopListNode(0, 4, 5, 6);
+ //ListNode centerLoop = createLoopListNode(1, 4, 5, 6);
+
+ int intersectVal = 8, skipA = 2, skipB = 3;
+ int[] listA = {4, 1, 8, 4, 5};
+ int[] listB = {5, 0, 1, 8, 4, 5};
+ List intersectListNode = createIntersectListNode(intersectVal, listA, listB, skipA, skipB);
+ for (ListNode listNode : intersectListNode) {
+ printListNode(listNode);
+ }
+
+ }
+}
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java
new file mode 100644
index 00000000..4d78a4b2
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java
@@ -0,0 +1,11 @@
+package cn.lastwhisper.leetcode.common.linkedlist;
+
+public class ListNode {
+ public int val;
+ public ListNode next;
+
+ public ListNode(int x) {
+ val = x;
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtils.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtils.java
new file mode 100644
index 00000000..9bc15820
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtils.java
@@ -0,0 +1,93 @@
+package cn.lastwhisper.leetcode.common.print;
+
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * 打印工具类
+ * @author lastwhisper
+ * @date 2020/2/1
+ */
+public class PrintUtils {
+
+ /**
+ * 层次打印二维数组
+ */
+ public static void printLists(List> lists) {
+ System.out.println("[");
+ int counter1 = 1;
+ for (List list : lists) {
+ System.out.print("\t[\"");
+ int counter2 = 1;
+ for (Integer i : list) {
+ if (counter2 != list.size()) {
+ System.out.print(i + "\",\"");
+ } else {
+ System.out.print(i);
+ }
+ counter2++;
+ }
+ if (counter1 != lists.size()) {
+ System.out.print("\"],\n");
+ } else {
+ System.out.print("\"]\n");
+ }
+ counter1++;
+ }
+ System.out.println("]");
+ }
+
+ public static void printStringLists(List> lists) {
+ System.out.println("[");
+ int counter1 = 1;
+ for (List list : lists) {
+ System.out.print("\t[\"");
+ int counter2 = 1;
+ for (String i : list) {
+ if (counter2 != list.size()) {
+ System.out.print(i + "\",\"");
+ } else {
+ System.out.print(i );
+ }
+ counter2++;
+ }
+ if (counter1 != lists.size()) {
+ System.out.print("\"],\n");
+ } else {
+ System.out.print("\"]\n");
+ }
+ counter1++;
+ }
+ System.out.println("]");
+ }
+
+ /**
+ * 打印数组
+ */
+ public static void printList(Collection> collection) {
+ System.out.println(collection2String(collection));
+ }
+
+ /**
+ * 将Collection扁平化迭代成String
+ */
+ public static String collection2String(Collection> collection) {
+ StringBuilder sb = new StringBuilder();
+ try {
+ sb.append("[\"");
+ int counter = 1;
+ for (Object object : collection) {
+ sb.append(object);
+ if (counter != collection.size()) {
+ sb.append("\",\"");
+ }
+ counter++;
+ }
+ sb.append("\"]");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return sb.toString();
+ }
+}
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeNode.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeNode.java
new file mode 100644
index 00000000..273e04b9
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeNode.java
@@ -0,0 +1,11 @@
+package cn.lastwhisper.leetcode.common.tree;
+
+public class TreeNode {
+ public int val;
+ public TreeNode left;
+ public TreeNode right;
+
+ public TreeNode(int x) {
+ val = x;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeUtils.java b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeUtils.java
new file mode 100644
index 00000000..89ec67f7
--- /dev/null
+++ b/algorithms/algorithm-common/src/main/java/cn/lastwhisper/leetcode/common/tree/TreeUtils.java
@@ -0,0 +1,259 @@
+package cn.lastwhisper.leetcode.common.tree;
+
+import javafx.util.Pair;
+
+import java.util.*;
+
+import static cn.lastwhisper.leetcode.common.print.PrintUtils.printLists;
+
+
+/**
+ * 树相关工具类
+ * @author lastwhisper
+ * @date 1/16/2020
+ */
+public class TreeUtils {
+
+ /**
+ * 根据数组创建二叉树
+ * 一、[5,4,8,11,null,13,4,7,2,null,null,null,1]
+ * 5
+ * / \
+ * 4 8
+ * / / \
+ * 11 13 4
+ * / \ \
+ * 7 2 1
+ * 二、[1,null,2,3]
+ * 1
+ * \
+ * 2
+ * /
+ * 3
+ * https://blog.csdn.net/lenfranky/article/details/84444816
+ * @param array 数组
+ */
+ public static TreeNode createTree(Integer... array) {
+ if (array.length == 0) return new TreeNode(0);
+ Deque nodeQueue = new LinkedList<>();
+ // 创建一个根节点
+ TreeNode root = new TreeNode(array[0]);
+ nodeQueue.offer(root);
+ TreeNode current;
+ // 记录当前行节点的数量(注意不一定是2的幂,而是上一行中非空节点的数量乘2)
+ int lineNodeNum = 2;
+ // 记录当前行中数字在数组中的开始位置
+ int startIndex = 1;
+ // 记录数组中剩余的元素的数量
+ int restLength = array.length - 1;
+
+ while (restLength > 0) {
+ // 只有最后一行可以不满,其余行必须是满的
+// // 若输入的数组的数量是错误的,直接跳出程序
+// if (restLength < lineNodeNum) {
+// System.out.println("Wrong Input!");
+// return new TreeNode(0);
+// }
+ for (int i = startIndex; i < startIndex + lineNodeNum; i = i + 2) {
+ // 说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
+ if (i == array.length) return root;
+ current = nodeQueue.poll();
+ if (array[i] != null) {
+ current.left = new TreeNode(array[i]);
+ nodeQueue.offer(current.left);
+ }
+ // 同上,说明已经将nums中的数字用完,此时应停止遍历,并可以直接返回root
+ if (i + 1 == array.length) return root;
+ if (array[i + 1] != null) {
+ current.right = new TreeNode(array[i + 1]);
+ nodeQueue.offer(current.right);
+ }
+ }
+ startIndex += lineNodeNum;
+ restLength -= lineNodeNum;
+ lineNodeNum = nodeQueue.size() * 2;
+ }
+
+ return root;
+ }
+
+ /**
+ * 二叉树层次遍历
+ */
+ public static List> levelOrder(TreeNode root) {
+ List> result = new ArrayList<>();
+ if (root == null)
+ return result;
+
+ Queue queue = new LinkedList<>();
+ queue.add(root);
+ while (!queue.isEmpty()) {
+ // 遍历每层的数据
+ int size = queue.size();
+ List list = new ArrayList<>();
+ while (size > 0) {
+ root = queue.poll();
+ list.add(root.val);
+ if (root.left != null) {
+ queue.add(root.left);
+ }
+ if (root.right != null) {
+ queue.add(root.right);
+ }
+ size--;
+ }
+ result.add(list);
+ }
+ return result;
+ }
+
+ /**
+ * 相同的树
+ *
+ *
+ * 输入: 1 1
+ * / \ / \
+ * 2 3 2 3
+ *
+ * [1,2,3], [1,2,3]
+ *
+ * 输出: true
+ */
+ public static boolean isSameTree(TreeNode p, TreeNode q) {
+ LinkedList queue = new LinkedList<>();
+ queue.add(p);
+ queue.add(q);
+ while (!queue.isEmpty()) {
+ p = queue.poll();
+ q = queue.poll();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ queue.add(p.left);
+ queue.add(q.left);
+ queue.add(p.right);
+ queue.add(q.right);
+ }
+ return true;
+ }
+
+ /**
+ * 完全二叉树的左子树或右子树的高度
+ *
+ * @param root 根节点的左子树或右子树
+ * @return int 高度
+ */
+ private int countLevel(TreeNode root) {
+ int level = 0;
+ while (root != null) {
+ level++;
+ root = root.left;
+ }
+ return level;
+ }
+
+ /**
+ * 所有节点之和
+ *
+ * @param root
+ * @return int
+ */
+ public int sumOfLeaves(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ return sumOfLeaves(root.left) + sumOfLeaves(root.right) + root.val;
+ }
+
+ /**
+ * 二叉树根节点至叶子节点的最高高度
+ *
+ * @param root 根节点
+ * @return int 高度
+ */
+ public int maxDepth(TreeNode root) {
+ LinkedList> stack = new LinkedList<>();
+ if (root != null) {
+ stack.push(new Pair<>(root, 0));
+ }
+ int maxDepth = 0;
+ // stack模拟递归系统栈
+ while (!stack.isEmpty()) {
+ Pair pair = stack.poll();
+ root = pair.getKey();
+ Integer height = pair.getValue();
+ // dfs过程中更新最大深度
+ maxDepth = Math.max(maxDepth, height);
+ if (root != null) {
+ stack.push(new Pair<>(root.right, height + 1));
+ stack.push(new Pair<>(root.left, height + 1));
+ }
+ }
+ return maxDepth;
+ }
+
+ /**
+ * 二叉树根节点至叶子节点的最小高度
+ *
+ * @param root 根节点
+ * @return int 高度
+ */
+ public int minDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ LinkedList> queue = new LinkedList<>();
+ queue.add(new Pair<>(root, 1));
+
+ // 当前深度
+ int currentDepth = 0;
+ Pair pair = null;
+ while (!queue.isEmpty()) {
+ pair = queue.poll();
+ root = pair.getKey();
+ currentDepth = pair.getValue();
+ // 找到层中第一个叶子节点
+ if (root.left == null && root.right == null) {
+ break;
+ }
+ if (root.left != null) {
+ queue.add(new Pair<>(root.left, currentDepth + 1));
+ }
+ if (root.right != null) {
+ queue.add(new Pair<>(root.right, currentDepth + 1));
+ }
+
+ }
+ return currentDepth;
+ }
+
+ /**
+ * 反转二叉树
+ *
+ * @param root 根节点
+ */
+ public TreeNode invertTree(TreeNode root) {
+ if (root == null)
+ return null;
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = root.left;
+ root.left = invertTree(root.right);
+ root.right = invertTree(tempNode);
+ return root;
+ }
+
+ /**
+ * 输出二叉树层次遍历的结果
+ */
+ public static void printLevelOrder(TreeNode root) {
+ printLists(levelOrder(root));
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(1, null, 2, 3));
+ //printLevelOrder(createTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1));
+ //printLevelOrder(createTree(1,2,3,null,4,5,null));
+ }
+}
diff --git a/algorithms/algorithm/pom.xml b/algorithms/algorithm/pom.xml
new file mode 100644
index 00000000..88f18783
--- /dev/null
+++ b/algorithms/algorithm/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ algorithms
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ algorithm
+
+
+
\ No newline at end of file
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/bloomfilter/BloomFilter.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/bloomfilter/BloomFilter.java
new file mode 100644
index 00000000..a6eca615
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/bloomfilter/BloomFilter.java
@@ -0,0 +1,74 @@
+package cn.lastwhisper.bloomfilter;
+
+import java.io.Serializable;
+import java.util.BitSet;
+import java.util.Random;
+
+/**
+ * 原文:https://www.iteye.com/blog/imtinx-1290636
+ * Long类型元素的布隆过滤器
+ */
+class BloomFilter implements Serializable {
+ private static final long serialVersionUID = 1L;
+ private static final int ELEM_NUM = 1000; // 欲容纳的元素个数
+ private static final double PERCENTAGE = 0.001; // 希望的误差率
+ private int hashNum; // hash函数的数量
+ private int size; // 位向量的长度
+ private BitSet bitVector; // 位向量
+
+ public BloomFilter() {
+ size = (int) Math.abs(ELEM_NUM * Math.log(PERCENTAGE)
+ / (Math.log(2) * Math.log(2))) + 1;
+ hashNum = (int) (Math.log(2) * ((double) size / ELEM_NUM));
+ bitVector = new BitSet(size);
+ }
+
+ /**
+ * 查找元素是否在集合中
+ */
+ public boolean search(Long elem) {
+ boolean flag = true;
+ int temp;
+ Random random = new Random(elem);
+ for (int i = 0; i < hashNum; i++) {
+ temp = random.nextInt(size);
+ if (!bitVector.get(temp)) {// 元素不在集合中
+ bitVector.set(temp);
+ flag = false;
+ }
+ }
+ return flag;
+ }
+
+ /**
+ * 获取位向量的长度
+ */
+ public int size() {
+ return bitVector.size();
+ }
+
+
+ public int getHashNum() {
+ return hashNum;
+ }
+
+ public void setHashNum(int hashNum) {
+ this.hashNum = hashNum;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public BitSet getBitVector() {
+ return bitVector;
+ }
+
+ public void setBitVector(BitSet bitVector) {
+ this.bitVector = bitVector;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/consistenthash/ConsistentHash.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/consistenthash/ConsistentHash.java
new file mode 100644
index 00000000..f718c144
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/consistenthash/ConsistentHash.java
@@ -0,0 +1,71 @@
+package cn.lastwhisper.consistenthash;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * https://www.iteye.com/blog/imtinx-1290696
+ *
+ */
+class ConsistentHash {
+ /**
+ * 计算使用的hash函数,推荐使用MD5
+ */
+ private final Objects hashFunction;
+ /**
+ * 虚拟节点的倍数
+ */
+ private final int numberOfReplicas;
+ /**
+ * cache节点环
+ */
+ private final SortedMap circle = new TreeMap();
+ public ConsistentHash(Objects hashFunction, int numberOfReplicas,
+ Collection nodes) {
+ this.hashFunction = hashFunction;
+ this.numberOfReplicas = numberOfReplicas;
+ for (T node : nodes) {
+ add(node);
+ }
+ }
+ /**
+ * @description 添加ceche服务器节点
+ * @param node 服务器节点,可以用服务器的IP表示
+ * @add date 2011-10-30
+ */
+ public void add(T node) {
+ for (int i = 0; i < numberOfReplicas; i++) {
+ circle.put(hashFunction.hash(node.toString() + i), node);
+ }
+ }
+ /**
+ * @description 删除服务器节点
+ * @param node 服务器节点,可以用服务器的IP表示
+ * @add date 2011-10-30
+ */
+ public void remove(T node) {
+ for (int i = 0; i < numberOfReplicas; i++) {
+ circle.remove(hashFunction.hash(node.toString() + i));
+ }
+ }
+ /**
+ * @description 获取对象对应的cache服务器
+ * @param key 需要存储的对象
+ * @return 目标cache服务器
+ * @add date 2011-10-30
+ */
+ public T get(Object key) {
+ if (circle.isEmpty()) {
+ return null;
+ }
+ int hash = hashFunction.hash(key);
+ if (!circle.containsKey(hash)) {
+ //顺时针查找最近的节点
+ SortedMap tailMap = circle.tailMap(hash);
+ hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
+ }
+ return circle.get(hash);
+ }
+}
\ No newline at end of file
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd1.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd1.java
new file mode 100644
index 00000000..07d2014b
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd1.java
@@ -0,0 +1,43 @@
+package cn.lastwhisper.gcdlcm;
+
+import org.junit.Assert;
+
+/**
+ * 最小公倍数
+ * @author lastwhisper
+ * @date 2020/3/12
+ */
+class Gcd1 {
+
+ /**
+ * (1)辗转相除法
+ * 有两整数a和b:
+ * ① a%b得余数c
+ * ② 若c=0,则b即为两数的最大公约数
+ * ③ 若c≠0,则a=b,b=c,再回去执行①
+ *
+ * 例如求27和15的最大公约数过程为:
+ * 27÷15 余12
+ * 15÷12 余3
+ * 12÷3 余0 因此,3即为最大公约数
+ */
+ public int gcd(int a, int b) {
+ int m=a,n=b;
+ if (b > a) {
+ int temp = a;a = b;b = temp;
+ }
+
+ int c;
+ while (b != 0) /* 余数不为0,继续相除,直到余数为0 */ {
+ c = a % b;
+ a = b;
+ b = c;
+ }
+ return a;// m*n/a最小公倍数
+ }
+
+ public static void main(String[] args) {
+ Gcd1 main = new Gcd1();
+ Assert.assertEquals(3, main.gcd(27, 15));
+ }
+}
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd2.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd2.java
new file mode 100644
index 00000000..66cc920b
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd2.java
@@ -0,0 +1,38 @@
+package cn.lastwhisper.gcdlcm;
+
+import org.junit.Assert;
+
+/**
+ * 相减法
+ * @author lastwhisper
+ * @date 2020/3/12
+ */
+class Gcd2 {
+
+ /**
+ * ⑵ 相减法
+ * 有两整数a和b:
+ * ① 若a>b,则a=a-b
+ * ② 若a12 )
+ * 15-12=3( 12>3 )
+ * 12-3=9( 9>3 )
+ * 9-3=6( 6>3 )
+ * 6-3=3( 3==3 )
+ */
+ public int gcd(int a, int b) {
+ while (a != b) {
+ if (a > b) a = a - b;
+ else b = b - a;
+ }
+ return a;
+ }
+
+ public static void main(String[] args) {
+ Gcd2 main = new Gcd2();
+ Assert.assertEquals(3, main.gcd(27, 15));
+ }
+}
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd3.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd3.java
new file mode 100644
index 00000000..bc29cc22
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/gcdlcm/Gcd3.java
@@ -0,0 +1,24 @@
+package cn.lastwhisper.gcdlcm;
+
+import org.junit.Assert;
+
+/**
+ * 穷举法
+ * @author lastwhisper
+ * @date 2020/3/12
+ */
+class Gcd3 {
+
+ // https://www.cnblogs.com/ECJTUACM-873284962/p/6679839.html
+ public int gcd(int a, int b) {
+ int t = 0;
+ for (int i = 1; i <= a; i++)
+ if (a % i == 0 && b % i == 0) t = i;
+ return t;
+ }
+
+ public static void main(String[] args) {
+ Gcd3 main = new Gcd3();
+ Assert.assertEquals(3, main.gcd(27, 15));
+ }
+}
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/sort/QuickSort.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/sort/QuickSort.java
new file mode 100644
index 00000000..778f8cad
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/sort/QuickSort.java
@@ -0,0 +1,78 @@
+package cn.lastwhisper.sort;
+
+
+import cn.lastwhisper.util.ArrayUtil;
+
+import java.util.Arrays;
+
+/**
+ * 快排
+ *
+ * 整个快速排序的核心是分区(partition),分区的目的是传入一个数组和选定的一个元素,把所有小于那个元素的其他元素放在左边,大于的放在右边。
+ * 元素的选择一般有如下几种:
+ * 永远选择第一个元素
+ * 永远选择最后一个元素
+ * 随机选择元素
+ * 取中间值
+ * @author lastwhisper
+ * @date 2020/3/15
+ */
+public class QuickSort {
+
+
+ public static void sort(int[] arr) {
+ sort(arr, 0, arr.length - 1);
+ }
+
+ public static void sort(int[] arr, int left, int right) {
+ if (left >= right) return;
+ int mid = partition(arr, left, right);
+ sort(arr, left, mid - 1);
+ sort(arr, mid + 1, right);
+ }
+
+ /**
+ * 选取最后的元素,把所有小于那个元素的其他元素放在左边,大于的放在右边
+ *
+ * 7, 3, 2, 8, 1, 9, 5, 4, 6 ==》
+ * 4, 3, 2, 5, 1, 6, 8, 7, 9
+ *
+ * @param arr 待排序数组
+ * @return int 返回数组
+ */
+ private static int partition(int[] arr, int leftBound, int rightBound) {
+ int i = leftBound;
+ int pivot = arr[rightBound];
+ int j = rightBound - 1;//-1是因为最后一个元素以及选择了
+ while (i <= j) {
+ while (i <= j && arr[i] <= pivot) {
+ i++;
+ }
+ while (i <= j && arr[j] > pivot) {
+ j--;
+ }
+ if (i < j) swap(arr, i, j);
+ }
+ swap(arr, i, rightBound);
+ System.out.println(Arrays.toString(arr));
+ return i;
+ }
+
+ private static void swap(int[] arr, int i, int j) {
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
+ }
+
+ public static void main(String[] args) {
+ int[] arr = {7, 3, 2, 8, 1, 9, 5, 4, 6};
+ //int arr[] = {7, 3, 2, 8, 1, 9, 5, 4, 6, 0};//为什么left++和right--条件里面要加 left <= right 限定
+ //int arr[] = {7, 3, 2, 8, 1, 9, 5, 4, 6, 10};
+ //int arr[] = {7, 3, 2, 6, 8, 1, 9, 5, 4, 6, 10, 6, 6}; // 为什么不取等 arr[right] > pivot
+ //int arr[] = {4, 6}; //小bug测试 // 为什么while (left <= right)里面要取等
+ //sort(arr);
+ sort(ArrayUtil.generateArrByRandom(10));
+ //sort(arr,0,5);
+ //System.err.println(Arrays.toString(arr));
+ }
+}
diff --git a/algorithms/algorithm/src/main/java/cn/lastwhisper/util/ArrayUtil.java b/algorithms/algorithm/src/main/java/cn/lastwhisper/util/ArrayUtil.java
new file mode 100644
index 00000000..b91dfa7c
--- /dev/null
+++ b/algorithms/algorithm/src/main/java/cn/lastwhisper/util/ArrayUtil.java
@@ -0,0 +1,24 @@
+package cn.lastwhisper.util;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class ArrayUtil {
+
+ public static int[] generateArrByRandom(int max) {
+ int[] arr = new int[max];
+ for (int i = 0; i < max; i++) {
+ arr[i] = (int) (Math.random() * 8000000); // 生成一个[0,8000000) 的一个数
+ }
+ return arr;
+ }
+
+ public static int[] generateArrByOrder(int max) {
+ int[] arr = new int[max];
+ for (int i = 0; i < max; i++) {
+ arr[i] = i + 1;
+ }
+
+ return arr;
+ }
+}
diff --git a/algorithms/datastructure/pom.xml b/algorithms/datastructure/pom.xml
new file mode 100644
index 00000000..20c2b585
--- /dev/null
+++ b/algorithms/datastructure/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+ algorithms
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper
+ datastructure
+
+
+
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/graph/Graph.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/graph/Graph.java
new file mode 100644
index 00000000..4f69cc9f
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/graph/Graph.java
@@ -0,0 +1,218 @@
+package cn.lastwhisper.atguigu.graph;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 图
+ * @author cn.lastwhisper
+ */
+public class Graph {
+ private List vertexList; // 存储顶点集合
+ private int[][] matrix; // 存储图对应的邻接矩阵
+ private int numOfEdges; // 记录边的数量
+ private boolean[] isVisited; // 记录某节点是否被访问
+
+ // 初始化图的参数
+ public Graph(int n) {
+ vertexList = new ArrayList<>(n);
+ matrix = new int[n][n];
+ //isVisited = new boolean[n];
+ numOfEdges = 0;
+ }
+
+ public static void main(String[] args) {
+ // 顶点
+ //String[] vertexs = {"A", "B", "C", "D", "E"};
+ String[] vertexs = {"1", "2", "3", "4", "5", "6", "7", "8"};
+ //创建图对象
+ Graph graph = new Graph(vertexs.length);
+ //循环的添加顶点
+ for (String vertex : vertexs) {
+ graph.addVertex(vertex);
+ }
+ //添加边
+ //A-B A-C B-C B-D B-E
+ //graph.addEdge(0, 1, 1); // A-B
+ //graph.addEdge(0, 2, 1); //
+ //graph.addEdge(1, 2, 1); //
+ //graph.addEdge(1, 3, 1); //
+ //graph.addEdge(1, 4, 1); //
+ graph.addEdge(0, 1, 1);
+ graph.addEdge(0, 2, 1);
+ graph.addEdge(1, 3, 1);
+ graph.addEdge(1, 4, 1);
+ graph.addEdge(3, 7, 1);
+ graph.addEdge(4, 7, 1);
+ graph.addEdge(2, 5, 1);
+ graph.addEdge(2, 6, 1);
+ graph.addEdge(5, 6, 1);
+ //graph.show();
+ System.out.println("深度优先:");
+ graph.dfs();
+ System.out.println("广度优先:");
+ graph.bfs();
+ }
+
+ //遍历所有的节点,都进行广度优先搜索
+ public void bfs() {
+ isVisited = new boolean[vertexList.size()];
+ for (int i = 0; i < getNumOfVertex(); i++) {
+ if (!isVisited[i]) {
+ bfs(isVisited, i);
+ }
+ }
+ System.out.println();
+ }
+
+ // 广度优先
+ public void bfs(boolean[] isVisited, int i) {
+ int u; // 表示队列头节点对应下标
+ int w; // 邻接节点w
+ // 记录节点访问的顺序
+ LinkedList queue = new LinkedList<>();
+ System.out.print(getValueByIndex(i) + "=>");
+ // 标记节点已经访问
+ isVisited[i] = true;
+ queue.addLast(i);
+ while (!queue.isEmpty()) {
+ // 出队,遍历当前出队节点的所有邻接节点,并放入队列中
+ u = queue.removeFirst();
+ // 获取u的第一个邻接节点下标
+ w = getFristNeighbor(u);
+ while (w != -1) {
+ if (!isVisited[w]) {
+ System.out.print(getValueByIndex(w) + "=>");
+ // 标记节点已经访问
+ isVisited[w] = true;
+ //入队,用于下一次出队
+ queue.addLast(w);
+ }
+ // 从队列头节点,找到邻接节点w之后的邻接节点
+ w = getNextNeighbor(u, w);
+ }
+ }
+ }
+
+ public void dfs() {
+ isVisited = new boolean[vertexList.size()];
+ for (int i = 0; i < getNumOfVertex(); i++) {
+ if (!isVisited[i]) {
+ // 0,1,2,3,4,5
+ dfs(isVisited, 0);
+ }
+ }
+ System.out.println();
+ }
+
+ // 深度优先遍历算法
+ public void dfs(boolean[] isVisited, int i) {
+ // 首先我们访问该节点,输出
+ System.out.print(getValueByIndex(i) + "->");
+ // 将节点设置为已经访问
+ isVisited[i] = true;
+ // 查找节点i的第一个邻接节点j
+ int w = getFristNeighbor(i);
+ while (w != -1) {
+ if (!isVisited[w]) {
+ dfs(isVisited, w);
+ }
+ // 第i个节点下标i,matrix的row;w是邻接节点下标,matrix的column
+ w = getNextNeighbor(i, w);
+ }
+ }
+
+ /**
+ * 根据前一个邻接节点的下标来获取下一个邻接节点
+ *
+ * 0 1 2 3 4
+ * A B C D E
+ * 0 A[0, 1, 1, 0, 0]
+ * 1 B[1, 0, 1, 1, 1]
+ * 2 C[1, 1, 0, 0, 0]
+ * 3 D[0, 1, 0, 0, 0]
+ * 4 E[0, 1, 0, 0, 0]
+ * v1=1,v2=1,下一个邻接节点下标为1
+ * @param v1 当前weight横下标
+ * @param v2 当前weight纵下标
+ * @return int
+ */
+ public int getNextNeighbor(int v1, int v2) {
+ for (int i = v2 + 1; i < getNumOfVertex(); i++) {
+ if (matrix[v1][i] > 0) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * 得到某一个节点的第一个邻接节点的下标
+ * 例如:
+ * 0 1 2 3 4
+ * A B C D E
+ * 0 A[0, 1, 1, 0, 0]
+ * 1 B[1, 0, 1, 1, 1]
+ * 2 C[1, 1, 0, 0, 0]
+ * 3 D[0, 1, 0, 0, 0]
+ * 4 E[0, 1, 0, 0, 0]
+ * index=0,第一个节点A的第一个邻接节点就是B,第一个邻接节点的下标就是1
+ * @param index 某一个节点
+ * @return int
+ */
+ public int getFristNeighbor(int index) {
+ for (int i = 0; i < getNumOfVertex(); i++) {
+ if (matrix[index][i] > 0) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ // 返回节点下标对应的数据
+ public String getValueByIndex(int i) {
+ return vertexList.get(i);
+ }
+
+ // 添加节点
+ public void addVertex(String vertex) {
+ vertexList.add(vertex);
+ }
+
+ /**
+ * 添加边
+ *
+ * @param v1 节点1的下标
+ * @param v2 节点2的下标
+ * @param weight 1表示有通路,0表示无通路
+ */
+ public void addEdge(int v1, int v2, int weight) {
+ matrix[v1][v2] = weight;
+ matrix[v2][v1] = weight;
+ numOfEdges++;
+ }
+
+ // 显示图对应矩阵
+ public void show() {
+ for (int i = 0; i < matrix.length; i++) {
+ System.out.println(Arrays.toString(matrix[i]));
+ }
+ }
+
+ //返回节点的个数
+ public int getNumOfVertex() {
+ return vertexList.size();
+ }
+
+ //得到边的数目
+ public int getNumOfEdges() {
+ return numOfEdges;
+ }
+
+ //返回v1和v2的权值
+ public int getWeight(int v1, int v2) {
+ return matrix[v1][v2];
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/hashtab/HashTabDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/hashtab/HashTabDemo.java
new file mode 100644
index 00000000..8eb6eee7
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/hashtab/HashTabDemo.java
@@ -0,0 +1,193 @@
+package cn.lastwhisper.atguigu.hashtab;
+
+/**
+ *
+ * @author cn.lastwhisper
+ */
+public class HashTabDemo {
+ public static void main(String[] args) {
+ // EmpLinkedList测试
+ //Emp emp1 = new Emp(1, "tom");
+ //Emp emp2 = new Emp(2, "condy");
+ //EmpLinkedList empLinkedList = new EmpLinkedList();
+ //empLinkedList.add(emp1);
+ //empLinkedList.add(emp2);
+ //empLinkedList.list();
+ //Emp empById = empLinkedList.findEmpById(2);
+ //System.out.printf("id:%d-->name:%s\t", empById.id, empById.name);
+
+ Emp emp1 = new Emp(1, "tom");
+ Emp emp2 = new Emp(2, "condy");
+ HashTable hashTable = new HashTable(7);
+ hashTable.add(emp1);
+ hashTable.add(emp2);
+ Emp emp9 = new Emp(9, "kangkang");
+ hashTable.add(emp9);
+ hashTable.list();
+
+ System.out.println("===============根据id查找==============");
+ System.out.println(hashTable.findEmpById(9));
+ System.out.println("===============根据id更新==============");
+ hashTable.update(new Emp(9, "jackma"));
+ System.out.println(hashTable.findEmpById(9));
+ System.out.println("===============根据id删除==============");
+ hashTable.delete(9);
+ hashTable.list();
+ }
+}
+
+// 哈希表
+class HashTable {
+ private EmpLinkedList[] empLinkedLists;
+ private int size;
+
+ public HashTable(int size) {
+ this.size = size;
+ empLinkedLists = new EmpLinkedList[size];
+ for (int i = 0; i < empLinkedLists.length; i++) {
+ empLinkedLists[i] = new EmpLinkedList();
+ }
+ }
+
+ // 添加
+ public void add(Emp emp) {
+ EmpLinkedList empLinkedList = empLinkedLists[hashFun(emp.id)];
+ empLinkedList.add(emp);
+ }
+
+ // 根据id查找
+ public Emp findEmpById(int id) {
+ EmpLinkedList empLinkedList = empLinkedLists[hashFun(id)];
+ Emp emp = empLinkedList.findEmpById(id);
+ return emp;
+ }
+
+ // 修改
+ public void update(Emp emp) {
+ EmpLinkedList empLinkedList = empLinkedLists[hashFun(emp.id)];
+ empLinkedList.update(emp);
+ }
+
+ // 删除
+ public void delete(int id) {
+ EmpLinkedList empLinkedList = empLinkedLists[hashFun(id)];
+ empLinkedList.delete(id);
+ }
+
+ // 展示节点
+ public void list() {
+ for (int i = 0; i < empLinkedLists.length; i++) {
+ System.out.printf("第%d个链表:", i + 1);
+ empLinkedLists[i].list();
+ }
+ }
+
+ // hash函数
+ public int hashFun(int id) {
+ return id % size;
+ }
+
+}
+
+// 链表
+class EmpLinkedList {
+ private Emp head;
+
+ // 添加
+ public void add(Emp emp) {
+ // 第一个元素
+ if (head == null) {
+ head = emp;
+ return;
+ }
+ // 将链表最后一个节点.next=待添加节点添加
+ Emp currEmp = head;
+ while (currEmp.next != null) {
+ currEmp = currEmp.next;
+ }
+ currEmp.next = emp;
+ }
+
+ // 根据id查找
+ public Emp findEmpById(int empId) {
+ Emp currEmp = head;
+ if (currEmp == null) {
+ System.out.println("链表元素为空");
+ return null;
+ }
+ while (currEmp != null) {
+ if (currEmp.id == empId) {
+ return currEmp;
+ }
+ currEmp = currEmp.next;
+ }
+ return null;
+ }
+
+ // 修改
+ public void update(Emp emp) {
+ Emp currEmp = head;
+ if (currEmp == null) {
+ System.out.println("链表元素为空");
+ return;
+ }
+ while (currEmp != null) {
+ if (currEmp.id == emp.id) {
+ currEmp.name = emp.name;
+ break;
+ }
+ currEmp = currEmp.next;
+ }
+ }
+
+ // 删除
+ public void delete(int id) {
+ Emp currEmp = head;
+ if (currEmp == null) {
+ System.out.println("链表元素为空");
+ return;
+ }
+
+ while (currEmp != null) {
+ if (currEmp.next.id == id) {
+ currEmp.next = currEmp.next.next;
+ break;
+ }
+ currEmp = currEmp.next;
+ }
+ }
+
+ // 展示节点
+ public void list() {
+ Emp currEmp = head;
+ if (currEmp == null) {
+ System.out.println("链表元素为空");
+ return;
+ }
+ while (currEmp != null) {
+ System.out.printf("id:%d-->name:%s\t", currEmp.id, currEmp.name);
+ currEmp = currEmp.next;
+ }
+ System.out.println();
+ }
+}
+
+// 员工节点
+class Emp {
+ public int id;
+ public String name;
+ public Emp next;
+
+ public Emp(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Emp{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/DoubleLinkedListDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/DoubleLinkedListDemo.java
new file mode 100644
index 00000000..8d725b43
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/DoubleLinkedListDemo.java
@@ -0,0 +1,179 @@
+package cn.lastwhisper.atguigu.linkedlist;
+
+/**
+ * 双向链表
+ * @author cn.lastwhisper
+ */
+public class DoubleLinkedListDemo {
+ public static void main(String[] args) {
+ DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
+ HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
+ HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
+ HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
+ HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
+ doubleLinkedList.add(hero1);
+ doubleLinkedList.add(hero2);
+ doubleLinkedList.add(hero3);
+ doubleLinkedList.add(hero4);
+ System.out.println("===============查看链表所有节点================");
+ doubleLinkedList.list();
+ System.out.println("===============删除链表某节点==================");
+ doubleLinkedList.delete(2);
+ System.out.println("===============查看链表所有节点================");
+ doubleLinkedList.list();
+ System.out.println("================更新链表节点,4=================");
+ doubleLinkedList.update(new HeroNode2(4, "林冲", "豹子头~~~"));
+ System.out.println("===============查看链表所有节点================");
+ doubleLinkedList.list();
+ System.out.println("===============按序插入节点,8================");
+ doubleLinkedList.addByNoOrder(new HeroNode2(8, "武松", "行者"));
+ System.out.println("===============查看链表所有节点================");
+ doubleLinkedList.list();
+ System.out.println("===============按序插入节点,6================");
+ doubleLinkedList.addByNoOrder(new HeroNode2(6, "入云龙", "公孙胜"));
+ System.out.println("===============查看链表所有节点================");
+ doubleLinkedList.list();
+ }
+}
+
+class DoubleLinkedList {
+ HeroNode2 head = new HeroNode2(0, "", "");
+
+ /**
+ * 添加
+ */
+ public void add(HeroNode2 newNode) {
+ HeroNode2 tempNode = head;
+ while (true) {
+ if (tempNode.next == null) {
+ break;
+ }
+ tempNode = tempNode.next;
+ }
+ // lastNode.next指向newNode
+ tempNode.next = newNode;
+ // newNode.pre指向lastNode
+ newNode.pre = tempNode;
+ }
+
+ /**
+ * 删除
+ */
+ public void delete(int no) {
+ HeroNode2 tempNode = head;
+ while (true) {
+ if (tempNode.next == null) {
+ System.out.printf("未找到no==%d 节点\n", no);
+ break;
+ }
+ tempNode = tempNode.next;
+ if (tempNode.no == no) {
+ tempNode.pre.next = tempNode.next;
+ System.out.printf("已删除no==%d 节点\n", no);
+ break;
+ }
+ }
+ }
+
+ // 修改
+ public void update(HeroNode2 updateNode) {
+ HeroNode2 tempNode = head;
+ while (true) {
+ if (tempNode.next == null) {
+ System.out.printf("未找到no==%d 节点\n", updateNode.no);
+ break;
+ }
+ tempNode = tempNode.next;
+ // 找到待更新节点
+ if (tempNode.no == updateNode.no) {
+ tempNode.name = updateNode.name;
+ tempNode.nickName = updateNode.nickName;
+ System.out.printf("已更新no==%d 节点\n", updateNode.no);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 展示所有节点,沿着头节点遍历
+ *
+ * @param
+ * @return void
+ */
+ public void list() {
+ HeroNode2 currNode = head.next;
+ while (currNode != null) {
+ System.out.printf("%d\t%s\t%s\t\n", currNode.no, currNode.name, currNode.nickName);
+ currNode = currNode.next;
+ }
+ }
+
+ /**
+ * 根据排名插入到指定位置
+ * 如果已存在该排名添加失败
+ *
+ * @param heroNode
+ * @return void
+ */
+ public void addByNoOrder(HeroNode2 heroNode) {
+ HeroNode2 tempNode = head;
+ //HeroNode2 preNode;
+ boolean flag = true;
+ while (true) {
+ if (tempNode.next == null) {
+ break;
+ }
+ //preNode = tempNode;
+ tempNode = tempNode.next;
+ // tempNode是待插入节点的前一个节点
+ if (heroNode.no == tempNode.no) {
+ flag = false;
+ System.out.println("已存在排名,添加失败" + heroNode);
+ break;
+ }
+
+ if (heroNode.no < tempNode.no) {
+ // 1,3,4 heroNode.no=2 tempNode.next.no=3 2<3 可以添加
+ // heroNode指向no=3节点,即2,3
+ heroNode.next = tempNode;
+ // tempNode.pre为no=1的节点 即1,2
+ // preNode.next = heroNode;
+ tempNode.pre.next = heroNode;
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ // 待插入节点no最大
+ tempNode.next = heroNode;
+ heroNode.pre = tempNode;
+ }
+ }
+}
+
+/**
+ * 双向链表节点
+ *
+ */
+class HeroNode2 {
+ public int no;
+ public String name;
+ public String nickName;
+ public HeroNode2 next; // 当前节点的后一个节点
+ public HeroNode2 pre; //当前节点的前一个节点
+
+ public HeroNode2(int no, String name, String nickName) {
+ this.no = no;
+ this.name = name;
+ this.nickName = nickName;
+ }
+
+ @Override
+ public String toString() {
+ return "HeroNode2{" +
+ "no=" + no +
+ ", name='" + name + '\'' +
+ ", nickName='" + nickName + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/Josepfu.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/Josepfu.java
new file mode 100644
index 00000000..05df13fc
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/Josepfu.java
@@ -0,0 +1,135 @@
+package cn.lastwhisper.atguigu.linkedlist;
+
+/**
+ * 约瑟夫环
+ * @author cn.lastwhisper
+ */
+public class Josepfu {
+ public static void main(String[] args) {
+ CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
+ circleSingleLinkedList.createCircle(5);
+ circleSingleLinkedList.showBoy();
+ System.out.println();
+ circleSingleLinkedList.calc(1, 2, 5);
+
+ }
+}
+
+/**
+ * 单向环形链表
+ */
+class CircleSingleLinkedList {
+ private Boy frist;
+
+ /**
+ * 创建单向环形链表
+ *
+ * @param num
+ * @return void
+ */
+ public void createCircle(int num) {
+ if (num < 1) {
+ System.out.println("错误的num");
+ return;
+ }
+ // 定位最后一个boy的位置,方便添加节点
+ Boy currBoy = null;
+ for (int i = 1; i <= num; i++) {
+ Boy boy = new Boy(i);
+ if (i == 1) {
+ // 只有一个boy
+ frist = boy;
+ frist.setNext(frist);
+ currBoy = boy;
+ } else {
+ // 在已有环链的基础上添加
+ currBoy.setNext(boy);
+ boy.setNext(frist);
+ currBoy = boy;
+ }
+ }
+ }
+
+ /**
+ * 打印当前环链的节点信息
+ */
+ public void showBoy() {
+ if (frist == null) {
+ System.out.println("没有小孩");
+ return;
+ }
+ Boy currBoy = frist;
+ while (true) {
+ System.out.printf("%d ", currBoy.getNo());
+ currBoy = currBoy.getNext();
+ if (currBoy == frist) {
+ break;
+ }
+ }
+
+ }
+
+ /**
+ * 有n个人的环,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,写出出队序列.
+ *
+ * @param k 从第几个小孩开始
+ * @param m 数几次
+ * @param n 总人数
+ * @return void
+ */
+ public void calc(int k, int m, int n) {
+ // 1、数据校验
+ // 2、pre指针,让pre指针移动到frist指针前一个位置
+ Boy preNode = frist;
+ while (preNode.getNext() != frist) {
+ preNode = preNode.getNext();
+ }
+ // 3、报数前,让frist、pre指针同时移动k-1次,进入起始位置
+ for (int i = 0; i < k - 1; i++) {
+ frist = frist.getNext();
+ preNode = preNode.getNext();
+ }
+ // 4、报数时,让frist、pre指针同时移动m-1次,frist指针进入待移除位置,输出待移除位置的编号
+ // frist再向前移一位,pre.next=frist,完成小孩出队。
+ // 5、重复第四步,直至还剩一个小孩
+ while (true) {
+ if (frist.getNo() == preNode.getNo()) {
+ System.out.printf("最后留在圈中小孩编号:%d\n", frist.getNo());
+ break;
+ }
+ for (int i = 0; i < m - 1; i++) {
+ frist = frist.getNext();
+ preNode = preNode.getNext();
+ }
+ System.out.printf("小孩出圈编号为:%d\n", frist.getNo());
+ frist = frist.getNext();
+ preNode.setNext(frist);
+ }
+ }
+
+}
+
+class Boy {
+ private int no; //编号
+ private Boy next;
+
+ public Boy(int no) {
+ this.no = no;
+ }
+
+ public int getNo() {
+ return no;
+ }
+
+ public void setNo(int no) {
+ this.no = no;
+ }
+
+ public Boy getNext() {
+ return next;
+ }
+
+ public void setNext(Boy next) {
+ this.next = next;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/SingleLinkedListDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/SingleLinkedListDemo.java
new file mode 100644
index 00000000..6f915a4d
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/SingleLinkedListDemo.java
@@ -0,0 +1,451 @@
+package cn.lastwhisper.atguigu.linkedlist;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * 单链表的功能实现:增删改查
+ * 有序插入、有序合并、删除重复节点
+ * @author cn.lastwhisper
+ */
+public class SingleLinkedListDemo {
+ static SingleLinkedList singleLinkedList = new SingleLinkedList();
+ static HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
+ static HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
+ static HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
+ static HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
+
+ static {
+ singleLinkedList.add(hero1);
+ singleLinkedList.add(hero2);
+ singleLinkedList.add(hero3);
+ singleLinkedList.add(hero4);
+ }
+
+ public static void main(String[] args) {
+
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("===============删除链表某节点==================");
+ singleLinkedList.delete(2);
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("================更新链表节点,4=================");
+ singleLinkedList.update(new HeroNode(4, "林冲", "豹子头~~~"));
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("===============按序插入节点,2,3================");
+ singleLinkedList.addByNoOrder(new HeroNode(2, "卢俊义回来了~~~", "玉麒麟"));
+ singleLinkedList.addByNoOrder(new HeroNode(3, "吴用", "智多星"));
+ //singleLinkedList.addByNoOrderIsEqual(new HeroNode(2, "卢俊义回来了~~~", "玉麒麟"));
+ //singleLinkedList.addByNoOrderIsEqual(new HeroNode(3, "吴用", "智多星"));
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("===============按序插入节点,8================");
+ singleLinkedList.addByNoOrder(new HeroNode(8, "武松", "行者"));
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("===============按序插入节点,7================");
+ singleLinkedList.addByNoOrder(new HeroNode(7, "入云龙", "公孙胜"));
+ System.out.println("===============查看链表所有节点================");
+ singleLinkedList.list();
+ System.out.println("---------------获取链表长度,不包括头节点-----------");
+ System.out.println(singleLinkedList.size());
+ System.out.println("---------------获取链表倒数第2个元素-----------");
+ System.out.println(singleLinkedList.findLastIndexNode(2));
+ System.out.println("---------------反转单链表--------------");
+ singleLinkedList.reverse();
+ singleLinkedList.list();
+ System.out.println("---------------反转单链表--------------");
+ singleLinkedList.reverse();
+ System.out.println("---------------从尾到头打印单链表---------------");
+ singleLinkedList.reverseList();
+
+ System.out.println("---------------合并有序单链表---------------");
+ SingleLinkedList singleLinkedList2 = new SingleLinkedList();
+ HeroNode hero21 = new HeroNode(1, "宋江", "及时雨");
+ HeroNode hero23 = new HeroNode(3, "卢俊义", "玉麒麟");
+ HeroNode hero25 = new HeroNode(6, "吴用", "智多星");
+ HeroNode hero27 = new HeroNode(7, "林冲", "豹子头");
+ singleLinkedList2.add(hero21);
+ singleLinkedList2.add(hero23);
+ singleLinkedList2.add(hero25);
+ singleLinkedList2.add(hero27);
+ System.out.println("---------------查看二号链表所有节点---------------");
+ singleLinkedList2.list();
+ SingleLinkedList singleLinkedList3 = new SingleLinkedList();
+ HeroNode hero32 = new HeroNode(2, "柴进", "小旋风");
+ HeroNode hero34 = new HeroNode(4, "鲁智深", "花和尚");
+ HeroNode hero36 = new HeroNode(6, "石秀", "拼命三郎");
+ HeroNode hero38 = new HeroNode(8, "秦明", "霹雳火");
+ singleLinkedList3.add(hero32);
+ singleLinkedList3.add(hero34);
+ singleLinkedList3.add(hero36);
+ singleLinkedList3.add(hero38);
+ System.out.println("---------------查看三号链表所有节点---------------");
+ singleLinkedList3.list();
+ System.out.println("-----------------合并结束-------------------");
+ singleLinkedList2.mergeOrderLinkedList(singleLinkedList3);
+ System.out.println("-----------------查看二号链表所有节点-----------------");
+ singleLinkedList2.list();
+ }
+
+ // 测试删除重复节点
+ @Test
+ public void testDeleteDuplicate() {
+ HeroNode hero5 = new HeroNode(5, "宋江", "豹子头");
+ singleLinkedList.add(hero5);
+ singleLinkedList.deleteDuplicate();
+ singleLinkedList.list();
+ }
+
+}
+
+/**
+ * 单链表
+ *
+ */
+class SingleLinkedList {
+ private HeroNode head = new HeroNode(0, "", "");
+
+ /**
+ * 添加节点
+ * 找到单链表lastNode,lastNode.next=newNode
+ */
+ public void add(HeroNode heroNode) {
+ HeroNode tempNode = head;
+ // 找到最后一个节点
+ while (true) {
+ if (tempNode.next == null) {
+ break;
+ }
+ tempNode = tempNode.next;
+ }
+ tempNode.next = heroNode;
+ }
+
+ /**
+ * 根据排名插入到指定位置
+ * 如果已存在该排名添加失败
+ *
+ * @param heroNode
+ * @return void
+ */
+ public void addByNoOrder(HeroNode heroNode) {
+ HeroNode tempNode = head;
+ HeroNode preNode;
+ boolean flag = true;
+ while (true) {
+ if (tempNode.next == null) {
+ break;
+ }
+ preNode = tempNode;
+ tempNode = tempNode.next;
+
+ if (heroNode.no == tempNode.no) {
+ flag = false;
+ System.out.println("已存在排名,添加失败" + heroNode);
+ break;
+ }
+
+ if (heroNode.no < tempNode.no) {
+ // 1,3,4 heroNode.no=2 tempNode.next.no=3 2<3 可以添加
+ // heroNode指向no=3节点
+ heroNode.next = tempNode;
+ // tempNode指向heroNode节点
+ preNode.next = heroNode;
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ // 待插入节点no最大
+ tempNode.next = heroNode;
+ }
+ }
+
+ /**
+ * 根据排名插入到指定位置
+ * 可以存在相同编号的节点
+ * @param heroNode
+ * @return void
+ */
+ public void addByNoOrderIsEqual(HeroNode heroNode) {
+ HeroNode tempNode = head;
+ HeroNode preNode;
+ boolean flag = true;
+ while (true) {
+ if (tempNode.next == null) {
+ break;
+ }
+ preNode = tempNode;
+ tempNode = tempNode.next;
+
+ if (heroNode.no <= tempNode.no) {
+ // 1,3,4 heroNode.no=2 tempNode.next.no=3 2<3 可以添加
+ // heroNode指向no=3节点
+ heroNode.next = tempNode;
+ // tempNode指向heroNode节点
+ preNode.next = heroNode;
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ // 待插入节点no最大
+ tempNode.next = heroNode;
+ }
+ }
+
+ /**
+ * 展示所有节点,沿着头节点遍历
+ *
+ * @param
+ * @return void
+ */
+ public void list() {
+ HeroNode currNode = head.next;
+ while (currNode != null) {
+ System.out.printf("%d\t%s\t%s\t\n", currNode.no, currNode.name, currNode.nickName);
+ currNode = currNode.next;
+ }
+ }
+
+
+ /**
+ * 删除链表中重复的元素
+ */
+ public void deleteDuplicate() {
+ Set set = new HashSet<>();
+ HeroNode currNode = head.next;
+ HeroNode preNode = null;
+ while (currNode != null) {
+ if (set.contains(currNode.name)) {
+ preNode.next = currNode.next;
+ } else {
+ set.add(currNode.name);
+ preNode = currNode;
+ }
+ currNode = currNode.next;
+ }
+
+ }
+
+ /**
+ * 根据no删除节点
+ *
+ * @param no
+ * @return void
+ */
+ public void delete(int no) {
+ // 1、沿着头节点tempNode遍历,找到node.no=no的前一个节点preNode
+ HeroNode preNode = head;
+ while (true) {
+ if (preNode.next == null) {
+ System.out.printf("未找到no==%d 节点\n", no);
+ break;
+ }
+ // tempNode是待删除节点的前一个节点
+ preNode = preNode.next;
+ // 2. 找到待删除节点的前一个节点
+ if (preNode.next.no == no) {
+ preNode.next = preNode.next.next;
+ System.out.printf("已删除no==%d 节点\n", no);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 根据no更新节点
+ *
+ * @param updateNode
+ * @return void
+ */
+ public void update(HeroNode updateNode) {
+ HeroNode tempNode = head;
+ while (true) {
+ if (tempNode.next == null) {
+ System.out.printf("未找到no==%d 节点\n", updateNode.no);
+ break;
+ }
+ tempNode = tempNode.next;
+ // 找到待更新节点
+ if (tempNode.no == updateNode.no) {
+ tempNode.name = updateNode.name;
+ tempNode.nickName = updateNode.nickName;
+ System.out.printf("已更新no==%d 节点\n", updateNode.no);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 获取单链表的节点个数(不统计头节点)
+ *
+ * @param
+ * @return int
+ */
+ public int size() {
+ HeroNode currNode = head.next;
+ int counter = 0;
+ while (currNode != null) {
+ counter++;
+ currNode = currNode.next;
+ }
+ return counter;
+ }
+
+ /**
+ * 获取单链表倒数第k个节点
+ * 思路:倒数第k个等价于正数size-k+1个
+ * 示例:7个元素,倒数第2个==正数第6个
+ * @param k
+ * @return cn.cn.lastwhisper.linkedlist.HeroNode
+ */
+ public HeroNode findLastIndexNode(int k) {
+ int end = size() - k;
+ if (end < 0) {
+ throw new RuntimeException("不存在的倒数第" + k + "个元素");
+ }
+ HeroNode currNode = head.next;
+ for (int i = 0; i < end; i++) {
+ currNode = currNode.next;
+ }
+ return currNode;
+ }
+
+ /**
+ * 单链表反转
+ * 将原链表节点依次插入到反转链表的第一个位置
+ */
+ public void reverse() {
+ // 如果单链表中无节点或者只有一个节点
+ if (head.next == null || head.next.next == null) {
+ return;
+ }
+ // currNode的作用:指向待插入反转链表节点的指针。遍历原单链表
+ HeroNode currNode = head.next;
+ // nextNode的作用:暂存待插入反转链表节点的下一个节点
+ HeroNode nextNode;
+ HeroNode reverseHead = new HeroNode(0, "", "");
+
+ while (currNode != null) {
+ // nextNode保存currNode.next,因为currNode.next本指向原链表,
+ // currNode.next = reverseHead.next会让currNode.next指向别的地方,导致找不到原链表
+ nextNode = currNode.next;
+ currNode.next = reverseHead.next;
+ reverseHead.next = currNode;
+ // currNode每一次循环都会后移一个节点
+ currNode = nextNode;
+ }
+ // 将原链表头节点指向反转链表头节点.next
+ head.next = reverseHead.next;
+ }
+
+ /**
+ * 从尾到头打印单链表
+ *
+ * @param
+ * @return void
+ */
+ public void reverseList() {
+ HeroNode currNode = head.next;
+ Stack stack = new Stack<>();
+ while (currNode != null) {
+ stack.add(currNode);
+ currNode = currNode.next;
+ }
+ int size = stack.size();
+ for (int i = 0; i < size; i++) {
+ HeroNode node = stack.pop();
+ System.out.printf("%d\t%s\t%s\t\n", node.no, node.name, node.nickName);
+ }
+ }
+
+ /**
+ * 合并两个有序链表(合并后还有序)
+ * 方法一:遍历2号链表,将每一个节点都放入1号链表.addByNoOrderIsEqual方法中
+ * 优点:方便简洁,代码容易理解。缺点:性能低。
+ * 方法二:
+ * 1号链表:1,3,6,7 2号链表:2,4,6,8
+ * 由于是有序链表,2号链表的2节点插入到1号链表的1,3节点之中后,
+ * 2号链表的4节点没有必要从1号节点的1,2,3开始遍历,直接从刚插入节点2节点开始遍历即可
+ * @param singleLinkedList
+ * @return void
+ */
+ public void mergeOrderLinkedList(SingleLinkedList singleLinkedList) {
+ // 方法一:
+ //HeroNode currNode = singleLinkedList.head.next;
+ //HeroNode nextNode;
+ //while (currNode != null) {
+ // nextNode = currNode.next;
+ // this.addByNoOrderIsEqual(currNode);
+ // currNode = nextNode;
+ //}
+ // 方法二:
+ // 2号链表 currNode1当前待合并节点
+ HeroNode currNode2 = singleLinkedList.head.next;
+ // nextNode是currNode.next,防止2号链表后续节点丢失
+ HeroNode nextNode2;
+ // 缓存刚插入节点的位置
+ HeroNode currNodeChche2 = null;
+
+ while (currNode2 != null) {
+
+ nextNode2 = currNode2.next;
+ // 1号链表
+ // 记录2号链表节点在1号链表节点插入的位置
+ HeroNode tempNode1 = currNodeChche2;
+ if (tempNode1 == null) {
+ tempNode1 = this.head;
+ }
+ HeroNode preNode1;
+ boolean flag = true; //标识该节点是否插到最后一位
+ while (true) {
+ if (tempNode1.next == null) {
+ break;
+ }
+ preNode1 = tempNode1;
+ tempNode1 = tempNode1.next;
+ if (currNode2.no <= tempNode1.no) {
+ // 1,3,4 nextNode.no=2 nextNode.next.no=3 2<3 可以添加
+ // nextNode指向no=3节点
+ currNode2.next = tempNode1;
+ // tempNode指向nextNode节点
+ preNode1.next = currNode2;
+ flag = false;
+ break;
+ }
+
+ }
+ if (flag) {
+ // 待插入节点no最大
+ tempNode1.next = currNode2;
+ }
+ currNodeChche2 = currNode2;
+ // 存储2号链表的当前节点后移
+ currNode2 = nextNode2;
+ }
+ }
+}
+
+/**
+ * 单链表的节点
+ */
+class HeroNode {
+ public int no;
+ public String name;
+ public String nickName;
+ public HeroNode next;
+
+ public HeroNode(int no, String name, String nickName) {
+ this.no = no;
+ this.name = name;
+ this.nickName = nickName;
+ }
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v1/SkipSet.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v1/SkipSet.java
new file mode 100644
index 00000000..014114ee
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v1/SkipSet.java
@@ -0,0 +1,165 @@
+package cn.lastwhisper.atguigu.linkedlist.skiplist.v1;
+
+/**
+ * https://www.iteye.com/blog/imtinx-1291165
+ * 跳表节点数据存储结构
+ */
+class SkipNode> {
+ public final E value; //节点存储的数据
+ public final SkipNode[] forward; //节点的指针数组
+
+ /**
+ * 根据节点的层级构造一个节点
+ * @param level 节点层级
+ * @param value 节点存储值
+ */
+ @SuppressWarnings("unchecked")
+ public SkipNode(int level, E value) {
+ forward = new SkipNode[level + 1];//level层的元素后面带着level+1的指针数组
+ this.value = value;
+ }
+
+}
+
+public class SkipSet> {
+
+ /**
+ * 概率因子,实验证明p=1/e比p=0.5要好,e是个神奇的数字!
+ */
+// public static final double P = 0.5;
+ public static final double P = 1 / Math.E;
+ /**
+ * 最大层级
+ */
+ public static final int MAX_LEVEL = 6;
+
+ /**
+ * 开始节点,不存值,贯穿所有层
+ */
+ public final SkipNode header = new SkipNode(MAX_LEVEL, null);
+ /**
+ * 当前跳表的最高层级
+ */
+ public int level = 0;
+
+ /**
+ * 插入一个元素
+ * @param value 待插入值
+ */
+ @SuppressWarnings("unchecked")
+ public void insert(E value) {
+ SkipNode x = header;
+ SkipNode[] update = new SkipNode[MAX_LEVEL + 1];
+ //查找元素的位置,这里其实做了一次contain操作,注释见contain
+ for (int i = level; i >= 0; i--) {
+ while (x.forward[i] != null
+ && x.forward[i].value.compareTo(value) < 0) {
+ x = x.forward[i];
+ }
+ //update[i]是比value小的数里面最大的,是value的前置节点
+ update[i] = x;
+ }
+ x = x.forward[0];
+
+ //此处不允许插入相同元素,为一个set
+ if (x == null || !x.value.equals(value)) {//跳表中不包含所要插的元素
+ //随机产生插入的层级
+ int lvl = randomLevel();
+ //产生的随机层级比当前跳表的最高层级大,需要添加相应的层级,并更新最高层级
+ if (lvl > level) {
+ for (int i = level + 1; i <= lvl; i++) {
+ update[i] = header;
+ }
+ level = lvl;
+ }
+
+ //生成新节点
+ x = new SkipNode(lvl, value);
+ //调整节点的指针,和指向它的指针
+ for (int i = 0; i <= lvl; i++) {
+ x.forward[i] = update[i].forward[i];
+ update[i].forward[i] = x;
+ }
+
+ }
+ }
+
+ /**
+ * 删除一个元素
+ * @param value 待删除值
+ */
+ @SuppressWarnings("unchecked")
+ public void delete(E value) {
+ SkipNode x = header;
+ SkipNode[] update = new SkipNode[MAX_LEVEL + 1];
+ //查找元素的位置,这里其实做了一次contain操作,注释见contain
+ for (int i = level; i >= 0; i--) {
+ while (x.forward[i] != null
+ && x.forward[i].value.compareTo(value) < 0) {
+ x = x.forward[i];
+ }
+ update[i] = x;
+ }
+ x = x.forward[0];
+ //删除元素,调整指针
+ if (x.value.equals(value)) {
+ for (int i = 0; i <= level; i++) {
+ if (update[i].forward[i] != x)
+ break;
+ update[i].forward[i] = x.forward[i];
+ }
+ //如果元素为本层最后一个元素,则删除同时降低当前层级
+ while (level > 0 && header.forward[level] == null) {
+ level--;
+ }
+
+ }
+ }
+
+ /**
+ * 查找是否包含此元素
+ * @param searchValue 带查找值
+ * @return true:包含;false:不包含
+ */
+ public boolean contains(E searchValue) {
+ SkipNode x = header;
+ //从开始节点的最高层级开始查找
+ for (int i = level; i >= 0; i--) {
+ //当到达本层级的NULL节点或者遇到比查找值大的节点时,转到下一层级查找
+ while (x.forward[i] != null
+ && x.forward[i].value.compareTo(searchValue) < 0) {
+ x = x.forward[i];
+ }
+ }
+ x = x.forward[0];
+ //此时x有三种可能,1.x=null,2.x.value=searchValue,3.x.value>searchValue
+ return x != null && x.value.equals(searchValue);
+ }
+
+ /**
+ * 这里是跳表的精髓所在,通过随机概率来判断节点的层级
+ * @return 节点的层级
+ */
+ public static int randomLevel() {
+ int lvl = (int) (Math.log(1. - Math.random()) / Math.log(1. - P));
+ return Math.min(lvl, MAX_LEVEL);
+ }
+
+ /**
+ * 输出跳表的所有元素
+ * 遍历最底层的元素即可
+ */
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ SkipNode x = header.forward[0];
+ while (x != null) {
+ sb.append(x.value);
+ x = x.forward[0];
+ if (x != null)
+ sb.append(",");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v2/SkipList.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v2/SkipList.java
new file mode 100644
index 00000000..999ca80a
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v2/SkipList.java
@@ -0,0 +1,150 @@
+package cn.lastwhisper.atguigu.linkedlist.skiplist.v2;
+
+/**
+ * 跳表的一种实现方法。
+ * 跳表中存储的是正整数,并且存储的是不重复的。
+ *
+ * -------------------------------------------------------------------------
+ * 以每两个结点抽取一个做上级索引为例分析:
+ * 时间复杂度:O(logn)
+ * 空间复杂度:O(n),需要抽出n/2+n/4+n/8…+8+4+2=n-2
+ */
+public class SkipList {
+
+ private static final float SKIPLIST_P = 0.5f;
+ private static final int MAX_LEVEL = 16;
+
+ private int levelCount = 1;
+ // 带头链表
+ private Node head = new Node();
+
+ public static class Node {
+ private int data = -1;
+ private Node[] forwards = new Node[MAX_LEVEL];
+ private int maxLevel = 0;
+
+ @Override
+ public String toString() {
+ return "{ data: " +
+ data +
+ "; levels: " +
+ maxLevel +
+ " }";
+ }
+ }
+
+ public Node find(int value) {
+ Node p = head;
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ p = p.forwards[i];
+ }
+ }
+
+ if (p.forwards[0] != null && p.forwards[0].data == value) {
+ return p.forwards[0];
+ } else {
+ return null;
+ }
+ }
+
+ public void insert(int value) {
+ int level = randomLevel();
+ Node newNode = new Node();
+ newNode.data = value;
+ newNode.maxLevel = level;
+ Node[] update = new Node[level];
+ for (int i = 0; i < level; ++i) {
+ update[i] = head;
+ }
+
+ // record every level largest value which smaller than insert value in update[]
+ Node p = head;
+ for (int i = level - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ p = p.forwards[i];
+ }
+ update[i] = p;// use update save node in search path
+ }
+
+ // in search path node next node become new node forwords(next)
+ for (int i = 0; i < level; ++i) {
+ newNode.forwards[i] = update[i].forwards[i];
+ update[i].forwards[i] = newNode;
+ }
+
+ // update node hight
+ if (levelCount < level) {
+ levelCount = level;
+ }
+ }
+
+ public void delete(int value) {
+ Node[] update = new Node[levelCount];
+ Node p = head;
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ p = p.forwards[i];
+ }
+ update[i] = p;
+ }
+
+ if (p.forwards[0] != null && p.forwards[0].data == value) {
+ for (int i = levelCount - 1; i >= 0; --i) {
+ if (update[i].forwards[i] != null && update[i].forwards[i].data == value) {
+ update[i].forwards[i] = update[i].forwards[i].forwards[i];
+ }
+ }
+ }
+
+ while (levelCount > 1 && head.forwards[levelCount] == null) {
+ levelCount--;
+ }
+
+ }
+
+ // 理论来讲,一级索引中元素个数应该占原始数据的 50%,二级索引中元素个数占 25%,三级索引12.5% ,一直到最顶层。
+ // 因为这里每一层的晋升概率是 50%。对于每一个新插入的节点,都需要调用 randomLevel 生成一个合理的层数。
+ // 该 randomLevel 方法会随机生成 1~MAX_LEVEL 之间的数,且 :
+ // 50%的概率返回 1
+ // 25%的概率返回 2
+ // 12.5%的概率返回 3 ...
+ private int randomLevel() {
+ int level = 1;
+
+ while (Math.random() < SKIPLIST_P && level < MAX_LEVEL) {
+ level += 1;
+ }
+ return level;
+ }
+
+ public void printAll() {
+ Node p = head;
+ while (p.forwards[0] != null) {
+ System.out.print(p.forwards[0] + " ");
+ p = p.forwards[0];
+ }
+ System.out.println();
+ }
+
+ public static void main(String[] args) {
+ SkipList list = new SkipList();
+ list.insert(1);
+ list.insert(2);
+ // 寻找2
+ Node node = list.find(2);
+ System.out.println(node == null ? "不存在" : node.data);
+ list.insert(6);
+ list.insert(7);
+ // 删除2
+ list.delete(2);
+ node = list.find(2);
+ System.out.println(node == null ? "不存在" : node.data);
+ list.insert(8);
+ list.insert(3);
+ list.insert(4);
+ list.insert(5);
+
+ list.printAll();
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v3/SkipList2.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v3/SkipList2.java
new file mode 100644
index 00000000..acc518bb
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/linkedlist/skiplist/v3/SkipList2.java
@@ -0,0 +1,307 @@
+package cn.lastwhisper.atguigu.linkedlist.skiplist.v3;
+
+import java.util.Random;
+
+/**
+ * 1,跳表的一种实现方法,用于练习。跳表中存储的是正整数,并且存储的是不重复的。
+ * 2,本类是参考作者zheng ,自己学习,优化了添加方法
+ * 3,看完这个,我觉得再看ConcurrentSkipListMap 源码,会有很大收获
+ * Author:ldb
+ */
+public class SkipList2 {
+
+ private static final int MAX_LEVEL = 16;
+ private int levelCount = 1;
+
+ /**
+ * 带头链表
+ */
+ private Node head = new Node(MAX_LEVEL);
+ private Random r = new Random();
+
+ /**
+ * 跳表的节点,每个节点记录了当前节点数据和所在层数数据
+ */
+ public class Node {
+ private int data = -1;
+ /**
+ * 表示当前节点位置的下一个节点所有层的数据,从上层切换到下层,就是数组下标-1,
+ * forwards[3]表示当前节点在第三层的下一个节点。
+ */
+ private Node forwards[];
+
+ /**
+ * 这个值其实可以不用,看优化insert()
+ */
+ private int maxLevel = 0;
+
+ public Node(int level) {
+ forwards = new Node[level];
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{ data: ");
+ builder.append(data);
+ builder.append("; levels: ");
+ builder.append(maxLevel);
+ builder.append(" }");
+ return builder.toString();
+ }
+ }
+
+ public Node find(int value) {
+ Node p = head;
+ // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ // 找到前一节点
+ p = p.forwards[i];
+ }
+ }
+
+ if (p.forwards[0] != null && p.forwards[0].data == value) {
+ return p.forwards[0];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 优化了作者zheng的插入方法
+ *
+ * @param value 值
+ */
+ public void insert(int value) {
+ int level = head.forwards[0] == null ? 1 : randomLevel();
+ // 每次只增加一层,如果条件满足
+ if (level > levelCount) {
+ level = ++levelCount;
+ }
+ Node newNode = new Node(level);
+ newNode.data = value;
+ Node update[] = new Node[level];
+ for (int i = 0; i < level; ++i) {
+ update[i] = head;
+ }
+
+ Node p = head;
+ // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ // 找到前一节点
+ p = p.forwards[i];
+ }
+ // levelCount 会 > level,所以加上判断
+ if (level > i) {
+ update[i] = p;
+ }
+
+ }
+ for (int i = 0; i < level; ++i) {
+ newNode.forwards[i] = update[i].forwards[i];
+ update[i].forwards[i] = newNode;
+ }
+
+ }
+
+ /**
+ * 优化了作者zheng的插入方法2
+ *
+ * @param value 值
+ */
+ public void insert2(int value) {
+ int level = head.forwards[0] == null ? 1 : randomLevel();
+ // 每次只增加一层,如果条件满足
+ if (level > levelCount) {
+ level = ++levelCount;
+ }
+ Node newNode = new Node(level);
+ newNode.data = value;
+ Node p = head;
+ // 从最大层开始查找,找到前一节点,通过--i,移动到下层再开始查找
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ // 找到前一节点
+ p = p.forwards[i];
+ }
+ // levelCount 会 > level,所以加上判断
+ if (level > i) {
+ if (p.forwards[i] == null) {
+ p.forwards[i] = newNode;
+ } else {
+ Node next = p.forwards[i];
+ p.forwards[i] = newNode;
+ newNode.forwards[i] = next;
+ }
+ }
+
+ }
+
+ }
+
+ /**
+ * 作者zheng的插入方法,未优化前,优化后参见上面insert()
+ *
+ * @param value
+ * @param level 0 表示随机层数,不为0,表示指定层数,指定层数
+ * 可以让每次打印结果不变动,这里是为了便于学习理解
+ */
+ public void insert(int value, int level) {
+ // 随机一个层数
+ if (level == 0) {
+ level = randomLevel();
+ }
+ // 创建新节点
+ Node newNode = new Node(level);
+ newNode.data = value;
+ // 表示从最大层到低层,都要有节点数据
+ newNode.maxLevel = level;
+ // 记录要更新的层数,表示新节点要更新到哪几层
+ Node update[] = new Node[level];
+ for (int i = 0; i < level; ++i) {
+ update[i] = head;
+ }
+
+ /**
+ *
+ * 1,说明:层是从下到上的,这里最下层编号是0,最上层编号是15
+ * 2,这里没有从已有数据最大层(编号最大)开始找,(而是随机层的最大层)导致有些问题。
+ * 如果数据量为1亿,随机level=1 ,那么插入时间复杂度为O(n)
+ */
+ Node p = head;
+ for (int i = level - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ p = p.forwards[i];
+ }
+ // 这里update[i]表示当前层节点的前一节点,因为要找到前一节点,才好插入数据
+ update[i] = p;
+ }
+
+ // 将每一层节点和后面节点关联
+ for (int i = 0; i < level; ++i) {
+ // 记录当前层节点后面节点指针
+ newNode.forwards[i] = update[i].forwards[i];
+ // 前一个节点的指针,指向当前节点
+ update[i].forwards[i] = newNode;
+ }
+
+ // 更新层高
+ if (levelCount < level) levelCount = level;
+ }
+
+ public void delete(int value) {
+ Node[] update = new Node[levelCount];
+ Node p = head;
+ for (int i = levelCount - 1; i >= 0; --i) {
+ while (p.forwards[i] != null && p.forwards[i].data < value) {
+ p = p.forwards[i];
+ }
+ update[i] = p;
+ }
+
+ if (p.forwards[0] != null && p.forwards[0].data == value) {
+ for (int i = levelCount - 1; i >= 0; --i) {
+ if (update[i].forwards[i] != null && update[i].forwards[i].data == value) {
+ update[i].forwards[i] = update[i].forwards[i].forwards[i];
+ }
+ }
+ }
+ }
+
+ /**
+ * 随机 level 次,如果是奇数层数 +1,防止伪随机
+ *
+ * @return
+ */
+ private int randomLevel() {
+ int level = 1;
+ for (int i = 1; i < MAX_LEVEL; ++i) {
+ if (r.nextInt() % 2 == 1) {
+ level++;
+ }
+ }
+ return level;
+ }
+
+ /**
+ * 打印每个节点数据和最大层数
+ */
+ public void printAll() {
+ Node p = head;
+ while (p.forwards[0] != null) {
+ System.out.print(p.forwards[0] + " ");
+ p = p.forwards[0];
+ }
+ System.out.println();
+ }
+
+ /**
+ * 打印所有数据
+ */
+ public void printAll_beautiful() {
+ Node p = head;
+ Node[] c = p.forwards;
+ Node[] d = c;
+ int maxLevel = c.length;
+ for (int i = maxLevel - 1; i >= 0; i--) {
+ do {
+ System.out.print((d[i] != null ? d[i].data : null) + ":" + i + "-------");
+ } while (d[i] != null && (d = d[i].forwards)[i] != null);
+ System.out.println();
+ d = c;
+ }
+ }
+
+ public static void main(String[] args) {
+ SkipList2 list = new SkipList2();
+ list.insert(1, 3);
+ list.insert(2, 3);
+ list.insert(3, 2);
+ list.insert(4, 4);
+ list.insert(5, 10);
+ list.insert(6, 4);
+ list.insert(8, 5);
+ list.insert(7, 4);
+ list.printAll_beautiful();
+ list.printAll();
+ /**
+ * 结果如下:
+ * null:15-------
+ * null:14-------
+ * null:13-------
+ * null:12-------
+ * null:11-------
+ * null:10-------
+ * 5:9-------
+ * 5:8-------
+ * 5:7-------
+ * 5:6-------
+ * 5:5-------
+ * 5:4------- 8:4-------
+ * 4:3-------5:3-------6:3-------7:3-------8:3-------
+ * 1:2-------2:2------- 4:2-------5:2-------6:2-------7:2-------8:2-------
+ * 1:1-------2:1-------3:1-------4:1-------5:1-------6:1-------7:1-------8:1-------
+ * 1:0-------2:0-------3:0-------4:0-------5:0-------6:0-------7:0-------8:0-------
+ * { data: 1; levels: 3 } { data: 2; levels: 3 } { data: 3; levels: 2 } { data: 4; levels: 4 }
+ * { data: 5; levels: 10 } { data: 6; levels: 4 } { data: 7; levels: 4 } { data: 8; levels: 5 }
+ */
+ // 优化后insert()
+
+ SkipList2 list2 = new SkipList2();
+ list2.insert2(1);
+ list2.insert2(2);
+ list2.insert2(6);
+ list2.insert2(7);
+ list2.insert2(8);
+ list2.insert2(3);
+ list2.insert2(4);
+ list2.insert2(5);
+ System.out.println();
+ list2.printAll_beautiful();
+
+
+ }
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/ArrayQueueDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/ArrayQueueDemo.java
new file mode 100644
index 00000000..5e924141
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/ArrayQueueDemo.java
@@ -0,0 +1,104 @@
+package cn.lastwhisper.atguigu.queue;
+
+/**
+ * 数组实现队列
+ * @author cn.lastwhisper
+ */
+public class ArrayQueueDemo {
+
+ public static void main(String[] args) {
+ ArrayQueue arrayQueue = new ArrayQueue(3);
+ arrayQueue.addQueue(100);
+ arrayQueue.addQueue(200);
+ arrayQueue.addQueue(300);
+ System.out.println("=========显示队列信息========");
+ arrayQueue.showQueue();
+ System.out.println("=========显示队列头信息========");
+ System.out.println(arrayQueue.headQueue());
+ System.out.println("=========显示队列长度========");
+ System.out.println(arrayQueue.size());
+ System.out.println("=========取出队头数据========");
+ System.out.println(arrayQueue.getQueue());
+ System.out.println(arrayQueue.getQueue());
+ System.out.println("=========显示队列长度========");
+ System.out.println(arrayQueue.size());
+ System.out.println("=========显示队列信息========");
+ arrayQueue.showQueue();
+ }
+}
+
+class ArrayQueue {
+ private int maxSize; // 队列长度
+ private int front; //指向头部
+ private int rear; //指向尾部
+ private int[] arr; //该数组存放元素
+
+ public ArrayQueue(int maxSize) {
+ this.maxSize = maxSize;
+ this.arr = new int[maxSize];
+ this.front = -1;
+ this.rear = -1;
+ }
+
+ // 入队前判断队列是否满了
+ public void addQueue(int data) {
+ // 先判断队列是否满了
+ if (isFull()) {
+ System.out.println("队列已满,不能添加数据");
+ return;
+ }
+ rear++;
+ arr[rear] = data;
+ }
+
+ // 出队前判断队列是否为空
+ public int getQueue() {
+ if (isEmpty()) {
+ throw new RuntimeException("队列为空,不能取出数据");
+ }
+ front++;
+ return arr[front];
+ }
+
+ // 查看队头元素
+ public int headQueue() {
+ if (isEmpty()) {
+ throw new RuntimeException("队列为空,不能取出数据");
+ }
+ return arr[front + 1];
+ }
+
+ // 判断队列是否为空
+ public boolean isEmpty() {
+ return front == rear;
+ }
+
+ // 判断队列是否满了
+ public boolean isFull() {
+ return rear == maxSize - 1;
+ }
+
+ // 显示队列情况
+ public void showQueue() {
+ if (isEmpty()) {
+ System.out.println("空队列");
+ }
+ //for (int i = front + 1; i < rear + 1; i++) {
+ // System.out.printf("%d\t", arr[i]);
+ //}
+ // 队列中元素个数
+ int counter = size();
+ // 队头
+ int start = front;
+ while (counter > 0) {
+ start++;
+ System.out.printf("%d\t", arr[start]);
+ counter--;
+ }
+ System.out.println();
+ }
+
+ public int size() {
+ return rear - front;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/CircleArrayQueueDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/CircleArrayQueueDemo.java
new file mode 100644
index 00000000..bd6aa057
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/queue/CircleArrayQueueDemo.java
@@ -0,0 +1,107 @@
+package cn.lastwhisper.atguigu.queue;
+
+/**
+ * 数组实现循环队列
+ * @author cn.lastwhisper
+ */
+public class CircleArrayQueueDemo {
+
+ public static void main(String[] args) {
+ CircleArrayQueue arrayQueue = new CircleArrayQueue(5);
+ arrayQueue.addQueue(100);
+ arrayQueue.addQueue(200);
+ arrayQueue.addQueue(300);
+ arrayQueue.addQueue(400);
+ System.out.println("=========显示队列信息========");
+ arrayQueue.showQueue();
+ System.out.println("=========显示队列头信息========");
+ System.out.println(arrayQueue.headQueue());
+ System.out.println("=========显示队列长度========");
+ System.out.println(arrayQueue.size());
+ System.out.println("=========取出队头数据========");
+ System.out.println(arrayQueue.getQueue());
+ System.out.println(arrayQueue.getQueue());
+ System.out.println("=========显示队列长度========");
+ System.out.println(arrayQueue.size());
+ System.out.println("=========向循环队列添加元素 500,600========");
+ arrayQueue.addQueue(500);
+ arrayQueue.addQueue(600);
+ System.out.println("=========显示队列信息========");
+ arrayQueue.showQueue();
+ }
+}
+
+class CircleArrayQueue {
+
+ private int maxSize; // 队列最大长度
+ private int front; // 队头指针
+ private int rear; // 队尾指针
+ private int[] arr; // 队列中的数据
+
+ public CircleArrayQueue(int maxSize) {
+ this.maxSize = maxSize;
+ this.arr = new int[maxSize];
+ }
+
+ // 入队,先添加数据再移动尾指针
+ public void addQueue(int data) {
+ // 先判断队列是否满了
+ if (isFull()) {
+ System.out.println("队列已满,不能添加数据");
+ return;
+ }
+ // maxSize=4,rear=3,添加一个元素后rear=0
+ rear = (rear + 1) % maxSize;
+ arr[rear] = data;
+ }
+
+ // 出队,先获取数据再移动头指针
+ public int getQueue() {
+ // 出队前判断队列是否为空
+ if (isEmpty()) {
+ throw new RuntimeException("队列为空,不能取出数据");
+ }
+ front = (front + 1) % maxSize;
+ int data = arr[front];
+ return data;
+ }
+
+ // 查看队头元素
+ public int headQueue() {
+ if (isEmpty()) {
+ throw new RuntimeException("队列为空,不能取出数据");
+ }
+ return arr[front + 1];
+ }
+
+ // 判断队列是否为空
+ public boolean isEmpty() {
+ return front == rear;
+ }
+
+ // 判断队列是否满了
+ public boolean isFull() {
+ return (rear + 1) % maxSize == front;
+ }
+
+ // 显示队列情况
+ public void showQueue() {
+ if (isEmpty()) {
+ System.out.println("空队列");
+ }
+ // 队列中元素个数
+ int counter = size();
+ // 队头
+ int start = front;
+ while (counter > 0) {
+ start = (start + 1) % maxSize;
+ System.out.printf("%d\t", arr[start]);
+ counter--;
+ }
+ System.out.println();
+ }
+
+ public int size() {
+ return (rear + maxSize - front) % maxSize;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Maze.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Maze.java
new file mode 100644
index 00000000..e496578b
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Maze.java
@@ -0,0 +1,87 @@
+package cn.lastwhisper.atguigu.recursion;
+
+/**
+ * 递归解决迷宫问题
+ * @author cn.lastwhisper
+ */
+public class Maze {
+ public static void main(String[] args) {
+ // 1、初始化迷宫 8(row)7(column)
+ int map[][] = new int[8][7];
+ // map.length——》row.length,map[0].length——》 column.length
+ // 左右边界设为1
+ for (int i = 0; i < map.length; i++) {
+ map[i][0] = 1;
+ map[i][map[0].length - 1] = 1;
+ }
+ // 上下边界置为1
+ for (int i = 0; i < map[0].length; i++) {
+ map[0][i] = 1;
+ map[map.length - 1][i] = 1;
+ }
+ // 设置迷宫
+ map[3][1] = 1;
+ map[3][2] = 1;
+
+ //map[3][1] = 1;
+ //map[3][2] = 1;
+ //map[4][3] = 1;
+ //map[3][4] = 1;
+ //map[2][4] = 1;
+ for (int i = 0; i < map.length; i++) {
+ for (int j = 0; j < map[0].length; j++) {
+ System.out.printf("%d ", map[i][j]);
+ }
+ System.out.println();
+ }
+ System.out.println("=============走出迷宫=============");
+ getWay(map, 1, 1);
+ for (int i = 0; i < map.length; i++) {
+ for (int j = 0; j < map[0].length; j++) {
+ System.out.printf("%d ", map[i][j]);
+ }
+ System.out.println();
+ }
+ }
+
+ /**
+ * map表示地图、i,j表示从地图的某个位置开始出发、map[6][5]为终点
+ * 约定:0表示没有走过、1表示不通、2表示通路、3表示已经走过无法走通
+ * 走迷宫策略:下——》右——》上——》左,如果该点走不通再递归回溯
+ * @return boolean
+ */
+ public static boolean getWay(int[][] map, int i, int j) {
+ // 找到终点
+ if (map[6][5] == 2) {
+ return true;
+ } else {
+ // 0表示没有走过、1表示不通、2表示通路、3表示已经走过无法走通
+ if (map[i][j] == 0) {
+ // 尝试 下——》右——》上——》左
+ map[i][j] = 2;
+ if (getWay(map, i + 1, j)) {
+ //下
+ return true;
+ } else if (getWay(map, i, j + 1)) {
+ //右
+ return true;
+ } else if (getWay(map, i - 1, j)) {
+ //上
+ return true;
+ } else if (getWay(map, i, j - 1)) {
+ //左
+ return true;
+ } else {
+ // 下——》右——》上——》左走不通
+ map[i][j] = 3;
+ return false;
+ }
+ } else {
+ // 此路不通或已走过
+ return false;
+ }
+ }
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Queue8.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Queue8.java
new file mode 100644
index 00000000..cec867c9
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Queue8.java
@@ -0,0 +1,77 @@
+package cn.lastwhisper.atguigu.recursion;
+
+/**
+ * 递归解决八皇后问题
+ * @author cn.lastwhisper
+ */
+public class Queue8 {
+
+ static int count = 0;
+ static int judgeCount = 0;
+ // 定义一个max表示有多少个皇后
+ private int max = 8;
+ // 定义一个数组保存皇后解法 arr[8] = { 0, 4, 7, 5, 2, 6, 1, 3}
+ // arr的意义:(index:下标、val:数组取某下标时数组对应的值)
+ // (1)arr的index+1表示第几个皇后(一行一个皇后)。
+ // (2)arr的index对应数组的val表示第index+1个皇后放在第val+1列。
+ // 示例:arr的index=1时表示第2个皇后,arr[index]=val=4表示第2个皇后放在第5列
+ // 一句话:row=下标+1;column=arr[下标]+1
+ int[] arr = new int[max];
+
+ public static void main(String[] args) {
+ Queue8 queue8 = new Queue8();
+ // 测试judge方法是否正确
+ //queue8.arr[0] = 0; //第一皇后在 0,0
+ //queue8.arr[1] = 1; //第二个皇后在 1,1
+ //System.out.println(queue8.judge(1));
+
+ // 测试8皇后是否正确 ;
+ queue8.check(0);
+ System.out.printf("一共有%d解法\n", count);
+ System.out.printf("一共判断冲突的次数%d次", judgeCount); // 1.5w
+ }
+
+ // 放置第index个皇后
+ public void check(int index) {
+ // index从0开始
+ if (index == max) {
+ print();
+ return;
+ }
+ // 每次一check的递归都会重新循环
+ for (int i = 0; i < max; i++) {
+ // 将第index个皇后,放置第i+1列。
+ arr[index] = i;
+ // 判断当前放置的位置与之前的皇后是否冲突
+ if (judge(index)) {
+ this.check(index + 1);
+ }
+ }
+ }
+
+ // 放置第index(n=index+1)个皇后之后,检测是否和前面已经摆放的皇后冲突
+ private boolean judge(int n) {
+ judgeCount++;
+ for (int i = 0; i < n; i++) {
+ // 同一列:arr[i] == arr[n];column=arr[下标]+1
+ // 同一斜线:Math.abs(n - i) == Math.abs(arr[n] - arr[i]),判断第n个皇后是否和第i个皇后在同一斜线
+ // 详解:i=0 arr[i]=0,第1个皇后放在第1列;n=1 arr[n]=1,第2个皇后放在第2列
+ // Math.abs(1 - 0)==Math.abs(arr[1] - arr[0]))
+ // 下标之差等于对应数组数值之差说明在同一对角线上(下标对应row、数组数值对应column)
+ // 同一行:由arr数组的意义来看不可能存在同一行
+ if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //写一个方法,可以将皇后摆放的位置输出
+ private void print() {
+ count++;
+ for (int i = 0; i < arr.length; i++) {
+ System.out.print(arr[i] + " ");
+ }
+ System.out.println();
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Recursion.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Recursion.java
new file mode 100644
index 00000000..3d253071
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/recursion/Recursion.java
@@ -0,0 +1,26 @@
+package cn.lastwhisper.atguigu.recursion;
+
+/**
+ * 递归通俗解释:
+ * 1)、https://www.zhihu.com/question/20507130/answer/31826402
+ * 坐在第几排——》坐在电影院里,你想知道自己坐在第几排,于是你问前面一排的A【你坐在那一排】,这样A的排数+1你就知道自己在第几排了。
+ * A也不知道自己坐在那一排,于是A问他前一排的B【你坐在那一排】,B也不知道自己坐在那一排,于是B...,一直问到第一排的人,
+ * 递归结束,开始回溯,最后大家都知道自己坐在那一排了。
+ * 2)、https://www.zhihu.com/question/20507130/answer/52759136
+ * 查字典——》查一个词,发现这个词的解释中某个词仍不懂,于是查第二个词,第二个词的解释中某个词仍不懂,于是查第三个词
+ * ,直到有一个词你明白了,递归结束,开始回溯,懂了第三个词,懂了第二个词,懂了查的这个词。
+ * @author cn.lastwhisper
+ */
+public class Recursion {
+ public static void main(String[] args) {
+ System.out.println(fun(5));
+ }
+
+ public static int fun(int n) {
+ if (n == 1) {
+ return 1;
+ } else {
+ return fun(n - 1) * n;
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/BinarySearch.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/BinarySearch.java
new file mode 100644
index 00000000..c180a504
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/BinarySearch.java
@@ -0,0 +1,190 @@
+package cn.lastwhisper.atguigu.search;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class BinarySearch {
+ private static int count = 0;
+
+ public static void main(String[] args) {
+ // 无重复值
+ //int[] arr = {1, 8, 10, 89, 1000, 1234};
+
+ int[] arr = ArrayUtil.generateArrByOrder(100);
+ int i = binarySearch1(arr, 35);
+ System.out.println("查找次数:"+count);
+ //int i = binarySearch2(arr, 0, arr.length - 1, 11);
+ if (i == -1) {
+ System.out.println("没有找到");
+ } else {
+ System.out.printf("找到了!下标为:%d,数值为:%d", i, arr[i]);
+ }
+ // 有重复值
+ //int[] arr = {1, 1, 1, 1, 10, 89, 1000, 1234};
+ //int[] arr = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1,};
+ //List indexs = binarySearch3(arr, 1);
+ //List indexs = binarySearch4(arr, 0, arr.length - 1, 1);
+ //for (Integer integer : indexs) {
+ // System.out.println(integer);
+ //}
+ }
+
+ /**
+ * 二分搜索(非递归)
+ * Xae=ro/iA1li
+ * @param arr
+ * @param target
+ * @return int
+ */
+ public static int binarySearch1(int[] arr, int target) {
+ if (target < arr[0] || target > arr[arr.length - 1]) {
+ return -1;
+ }
+ int left = 0; //[0..mid] [mid+1...arr.length-1]
+ int right = arr.length - 1;
+ int mid = (left + right) / 2;
+ while (left < right) {
+ if (target == arr[mid]) {
+ return mid;
+ } else if (target > arr[mid]) {
+ // 在mid的右边找
+ left = mid + 1;
+ mid = (left + right) / 2;
+ } else {
+ right = mid - 1;
+ mid = (left + right) / 2;
+ }
+ count++;
+ }
+ return -1;
+ }
+
+ /**
+ * 二分搜索(递归)
+ *
+ * @param arr
+ * @param left
+ * @param right
+ * @param target
+ * @return int
+ */
+ public static int binarySearch2(int[] arr, int left, int right, int target) {
+ if (left > right || target < arr[0] || target > arr[arr.length - 1]) {
+ return -1;
+ }
+ int mid = left + (right - left) / 2;
+ if (target == arr[mid]) {
+ return mid;
+ } else if (target > arr[mid]) {
+ // 在mid的右边找
+ return binarySearch2(arr, mid + 1, right, target);
+ } else {
+ // 在mid的左边找
+ return binarySearch2(arr, left, mid - 1, target);
+ }
+ }
+
+ /**
+ * 二分搜索(非递归,可重复值)
+ *
+ * @param arr
+ * @param target
+ * @return int
+ */
+ public static List binarySearch3(int[] arr, int target) {
+ int left = 0; //[0..mid] [mid+1...arr.length-1]
+ int right = arr.length - 1;
+ int mid = (left + right) / 2;
+ List indexs = new ArrayList<>();
+ while (left < right) {
+ if (target == arr[mid]) {
+ int midTemp = mid;
+
+ while (midTemp >= left && arr[midTemp] == target) {
+ indexs.add(midTemp);
+ midTemp--;
+ }
+ // 因为[left...mid]已经被扫描过了,所以mid先加,接着扫描[mid+1...right]
+ mid++;
+ while (mid <= right && arr[mid] == target) {
+ indexs.add(mid);
+ mid++;
+ }
+ break;
+ } else if (target > arr[mid]) {
+ // 在mid的右边找
+ left = mid + 1;
+ mid = (left + right) / 2;
+ } else {
+ right = mid - 1;
+ mid = (left + right) / 2;
+ }
+ }
+ return indexs;
+ }
+
+ /**
+ * 二分搜索(递归,可重复值)
+ *
+ * @param arr
+ * @param left
+ * @param right
+ * @param findVal
+ * @return int
+ */
+ public static List binarySearch4(int[] arr, int left, int right, int findVal) {
+
+ System.out.println("hello~");
+ // 当 left > right 时,说明递归整个数组,但是没有找到
+ if (left > right) {
+ return new ArrayList();
+ }
+ int mid = left + (right - left) / 2;
+ int midVal = arr[mid];
+
+ if (findVal > midVal) { // 向 右递归
+ return binarySearch4(arr, mid + 1, right, findVal);
+ } else if (findVal < midVal) { // 向左递归
+ return binarySearch4(arr, left, mid - 1, findVal);
+ } else {
+// * 思路分析
+// * 1. 在找到mid 索引值,不要马上返回
+// * 2. 向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
+// * 3. 向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
+// * 4. 将Arraylist返回
+
+ List resIndexlist = new ArrayList();
+ //向mid 索引值的左边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
+ int temp = mid - 1;
+ while (true) {
+ if (temp < 0 || arr[temp] != findVal) {//退出
+ break;
+ }
+ //否则,就temp 放入到 resIndexlist
+ resIndexlist.add(temp);
+ temp -= 1; //temp左移
+ }
+ resIndexlist.add(mid); //
+
+ //向mid 索引值的右边扫描,将所有满足 1000, 的元素的下标,加入到集合ArrayList
+ temp = mid + 1;
+ while (true) {
+ if (temp > arr.length - 1 || arr[temp] != findVal) {//退出
+ break;
+ }
+ //否则,就temp 放入到 resIndexlist
+ resIndexlist.add(temp);
+ temp += 1; //temp右移
+ }
+
+ return resIndexlist;
+ }
+
+ }
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/FibonacciSearch.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/FibonacciSearch.java
new file mode 100644
index 00000000..c9dcfd56
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/FibonacciSearch.java
@@ -0,0 +1,41 @@
+package cn.lastwhisper.atguigu.search;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+/**
+ *
+ * @author cn.lastwhisper
+ */
+public class FibonacciSearch {
+ private static int count = 0;
+
+ public static void main(String[] args) {
+ int[] arr = ArrayUtil.generateArrByOrder(100);
+ //int[] arr = {1, 1, 1, 1, 10, 89, 1000, 1234};
+ System.out.println(fibonacciSearch(arr, 37));
+ System.out.println("查找次数:" + count);
+ }
+
+ //因为后面我们mid=low+F(k-1)-1,需要使用到斐波那契数列,因此我们需要先获取到一个斐波那契数列
+ //非递归方法得到一个斐波那契数列
+ public static int[] fib() {
+ int[] f = new int[20];
+ f[0] = 1;
+ f[1] = 1;
+ for (int i = 2; i < 20; i++) {
+ f[i] = f[i - 1] + f[i - 2];
+ }
+ return f;
+ }
+
+ /**
+ * 斐波那契(黄金分割法)查找(递归)
+ *
+ * @param arr
+ * @param target
+ * @return int
+ */
+ public static int fibonacciSearch(int[] arr, int target) {
+ return 0;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/InsertValueSearch.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/InsertValueSearch.java
new file mode 100644
index 00000000..904e621f
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/InsertValueSearch.java
@@ -0,0 +1,43 @@
+package cn.lastwhisper.atguigu.search;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+/**
+ *
+ * @author cn.lastwhisper
+ */
+public class InsertValueSearch {
+ private static int count = 0;
+ public static void main(String[] args) {
+ int[] arr = ArrayUtil.generateArrByOrder(100);
+ //int[] arr = {1, 1, 1, 1, 10, 89, 1000, 1234};
+ System.out.println(insertValueSearch(arr, 0, arr.length - 1, 37));
+ System.out.println("查找次数:"+count);
+ }
+
+ /**
+ * 插值查找(递归)
+ *
+ * @param arr
+ * @param left
+ * @param right
+ * @param target
+ * @return int
+ */
+ public static int insertValueSearch(int[] arr, int left, int right, int target) {
+ count++;
+ // 必要条件
+ if (left > right || target < arr[0] || target > arr[arr.length - 1]) {
+ return -1;
+ }
+ // 自适应写法
+ int mid = left + (right - left) * (target - arr[left]) / (arr[right] - arr[left]);
+ if (target > arr[mid]) {
+ return insertValueSearch(arr, mid + 1, right, target);
+ } else if (target < arr[mid]) {
+ return insertValueSearch(arr, left, mid - 1, target);
+ } else {
+ return mid;
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/SeqSearch.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/SeqSearch.java
new file mode 100644
index 00000000..31f1993c
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/search/SeqSearch.java
@@ -0,0 +1,33 @@
+package cn.lastwhisper.atguigu.search;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class SeqSearch {
+
+ public static void main(String[] args) {
+ int[] arr = {1, 8, 10, 89, 1000, 1234};
+ int i = orderSearch(arr, 10);
+ if (i == -1) {
+ System.out.println("没有找到");
+ } else {
+ System.out.printf("找到了!下标为:%d,数值为:%d", i, arr[i]);
+ }
+ }
+
+ /**
+ * 线性查找
+ *
+ * @param
+ * @return int 数组下标
+ */
+ public static int orderSearch(int[] arr, int target) {
+ for (int i = 0; i < arr.length; i++) {
+ if (arr[i] == target) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/DubbleSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/DubbleSort.java
new file mode 100644
index 00000000..fdfc3682
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/DubbleSort.java
@@ -0,0 +1,53 @@
+package cn.lastwhisper.atguigu.sort;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class DubbleSort {
+ public static void main(String[] args) {
+ //int[] arr = {3, 9, -1, 10, -2};
+ //int[] arr = {1, 2, 3, 4, 5, 6, 7};
+ int[] arr = ArrayUtil.generateArrByRandom(100000);
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ bubbleSort(arr);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+ }
+
+ /**
+ * 冒泡排序O(n^2)
+ *
+ * @param arr
+ * @return void
+ */
+ public static void bubbleSort(int[] arr) {
+ int temp;
+ // 标识是否发生交换,默认不交换;即默认数组是升序
+ boolean flag = false;
+ // 外循环是当前已经找到最大值得个数
+ for (int i = 0; i < arr.length; i++) {
+ // 内循环进行冒泡,每循环一次arr最后i位数就是最大的数,可以不进行比较,即arr.length-i。
+ // 10个数两两相邻比较,需要比较9次,即arr.length - 1
+ // 所以内循环为:arr.length - 1 - i
+ for (int j = 0; j < arr.length - 1 - i; j++) {
+ if (arr[j] > arr[j + 1]) {
+ flag = true;
+ temp = arr[j];
+ arr[j] = arr[j + 1];
+ arr[j + 1] = temp;
+ }
+ }
+ //System.out.printf("第%d趟排序后的数组", i + 1);
+ //System.out.println(Arrays.toString(arr));
+ // 内循环未发生交换,说明数组目前已经有序,直接退出外循环
+ if (!flag) {
+ break;
+ } else {
+ flag = false;//重置
+ }
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/HeapSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/HeapSort.java
new file mode 100644
index 00000000..c591191a
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/HeapSort.java
@@ -0,0 +1,71 @@
+package cn.lastwhisper.atguigu.sort;
+
+import java.util.Arrays;
+
+/**
+ * 堆排序
+ *
+ * @author cn.lastwhisper
+ */
+public class HeapSort {
+ public static void main(String[] args) {
+ //int[] arr = {4, 6, 8, 5, 9, 58, -10};
+ int[] arr = {4, 6, 8, 5, 9};
+ heapSort(arr);
+ System.out.println("数组:" + Arrays.toString(arr));
+ }
+
+ public static void heapSort(int[] arr) {
+
+ //adjustHeap(arr, 1, arr.length);
+ //System.out.println("第1次" + Arrays.toString(arr)); // 4,9,8,5,6
+
+ //adjustHeap(arr, 0, arr.length);
+ //System.out.println("第2次" + Arrays.toString(arr)); // 9,6,8,5,4
+ // 1. 满二叉树从右向左、从下往上构建大顶堆;完全二叉树从左向右、从下往上构建大顶堆;
+ // i--意味着,从下到上,每一次都是[i,arr.length]的数组(二叉树)进行重构大顶堆
+ // [0,arr.length]重构了数组(二叉树)头部几个元素的大顶堆,同时也会重构下面的二叉树
+ for (int i = arr.length / 2 - 1; i >= 0; i--) {
+ adjustHeap(arr, i, arr.length);
+ }
+ // 2. 交换堆顶元素与末尾元素,调整堆结构
+ for (int j = arr.length - 1; j >= 0; j--) {
+ // 交换
+ int temp = arr[j];
+ arr[j] = arr[0];
+ arr[0] = temp;
+ adjustHeap(arr, 0, j);
+ }
+ }
+
+
+ /**
+ * 将一个数组(二叉树), 调整成一个大顶堆
+ * 功能: 将以i对应的非叶子节点的树调整成大顶堆
+ * 举例 int arr[] = {4, 6, 8, 5, 9}; => i = 1 => adjustHeap => 得到 {4, 9, 8, 5, 6}
+ * 如果我们再次调用 adjustHeap 传入的是 i = 0 => {4, 9, 8, 5, 6} => {9, 6, 8, 5, 4}
+ * @param arr 待调整的数组
+ * @param i 表示非叶子节点在数组中索引
+ * @param length 表示对多少个元素继续调整, length 是在逐渐的减少
+ */
+ public static void adjustHeap(int[] arr, int i, int length) {
+ // 当前i节点
+ int temp = arr[i];
+ // k是i节点的左子节点,k + 1是右子节点
+ for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
+ // arr[k] < arr[k + 1]左节点小于右节点
+ if (k + 1 < length && arr[k] < arr[k + 1]) {
+ // k指向数值最大的节点
+ k++;
+ }
+ if (arr[k] > temp) {//子节点大于父节点
+ arr[i] = arr[k];//将子节点赋值到父节点
+ i = k;//记录子节点的下标,为了最后 arr[i] = temp;
+ } else {
+ break;
+ }
+ }
+ //i = k; arr[i]此时已经是子节点的位置了,前面父节点已经拿到了子节点的值了,这里要将子节点的值赋为父节点的值
+ arr[i] = temp;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/InsertSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/InsertSort.java
new file mode 100644
index 00000000..24d2e60a
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/InsertSort.java
@@ -0,0 +1,108 @@
+package cn.lastwhisper.atguigu.sort;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class InsertSort {
+ public static void main(String[] args) {
+ //int[] arr = {101, 34, 119, 1};
+ //insertSort1(arr); // 插入排序算法实现步骤
+
+ int[] arr = ArrayUtil.generateArrByRandom(100000);
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ insertSort(arr);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+
+ //System.out.println(Arrays.toString(arr));
+ }
+
+ /**
+ * 插入排序O(n^2)
+ * insertVal < arr[insertIndex降序
+ * insertVal > arr[insertIndex升序
+ * @param arr
+ * @return void
+ */
+ public static void insertSort(int[] arr) {
+ int insertVal;
+ int insertIndex;
+ // 有序列表[0];[1...arr.length]无序列表
+ for (int i = 1; i < arr.length; i++) {
+ // insertVal的意义:缓存无序列表的第一个数,这个数也是待插入有序列表的数
+ insertVal = arr[i];
+ // insertIndex的意义:记录有序列表的最后一个数的下标
+ insertIndex = i - 1;
+ // [insertIndex+1...i-1]后移到[insertIndex+2...i](从前往后移动)
+ while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
+ arr[insertIndex + 1] = arr[insertIndex];
+ insertIndex--;
+ }
+ // insertIndex == i - 1; 说明数组并未后移,无需赋值
+ if (insertIndex != i - 1) {
+ // arr[insertIndex + 1]为待插入位置
+ arr[insertIndex + 1] = insertVal;
+ }
+ }
+ }
+
+ /**
+ * 插入排序的实现步骤
+ *
+ * @param arr
+ * @return void
+ */
+ public static void insertSort1(int[] arr) {
+ // 第1轮
+ // 有序列表{101},无序列表{34, 119, 1}
+ // {101, 34, 119, 1}
+
+ // insertVal的意义:缓存无序列表的第一个数,这个数也是待插入有序列表的数
+ int insertVal = arr[1];
+ // insertIndex的意义:记录有序列表的最后一个数的下标
+ int insertIndex = 1 - 1;
+ // insertIndex >= 0的意义:防止数组越界;
+ // insertVal < arr[insertIndex]的意义:找到insertVal要插入位置的前一个位置
+ // 整个while循环的意义:数组覆盖后移,找到待插入位置的前一个位置
+ while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
+ // 第1轮原数组:{101, 34, 119, 1}
+ // arr[1]=arr[0] {101, 101, 119, 1}
+ arr[insertIndex + 1] = arr[insertIndex];
+ //继续向前
+ insertIndex--;
+ }
+ // arr[0]=insertVal {34, 101, 119, 1}
+ arr[insertIndex + 1] = insertVal;
+
+ System.out.printf("第1轮插入,数组:%s\n", Arrays.toString(arr));
+
+ // 第2轮
+ insertVal = arr[2];
+ insertIndex = 2 - 1;
+ while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
+ arr[insertIndex + 1] = arr[insertIndex];
+ insertIndex--;
+ }
+ arr[insertIndex + 1] = insertVal;
+ System.out.printf("第2轮插入,数组:%s\n", Arrays.toString(arr));
+
+ // 第3轮
+ insertVal = arr[3];
+ insertIndex = 3 - 1;
+ while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
+ // 第三轮原数组:{34, 101, 119, 1}
+ // arr[3]=arr[2] {34, 101, 119, 119}
+ // arr[2]=arr[1] {34, 101, 101, 119}
+ // arr[1]=arr[0] {34, 34, 101, 119}
+ arr[insertIndex + 1] = arr[insertIndex];
+ insertIndex--;
+ }
+ arr[insertIndex + 1] = insertVal;
+ System.out.printf("第3轮插入,数组:%s\n", Arrays.toString(arr));
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/MergeSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/MergeSort.java
new file mode 100644
index 00000000..dfe39e34
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/MergeSort.java
@@ -0,0 +1,79 @@
+package cn.lastwhisper.atguigu.sort;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class MergeSort {
+
+ public static void main(String[] args) {
+ int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1};
+// int[] arr = ArrayUtil.generateArrByRandom(10000000);// 1443 ms
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ mergeSort(arr, 0, arr.length - 1);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+
+ System.out.println(Arrays.toString(arr));
+ }
+
+ /**
+ * 归并排序
+ * 分
+ *
+ * @param arr 排序的原始数组
+ * @param left 左边有序序列的初始索引
+ * @param right 右边索引
+ */
+ public static void mergeSort(int[] arr, int left, int right) {
+ if (left < right) {
+ int mid = (left + right) / 2;
+ mergeSort(arr, left, mid); // 向左分
+ mergeSort(arr, mid + 1, right); // 向右分
+ merge(arr, left, mid, right);// 治
+ }
+ }
+
+ /**
+ * 治
+ *
+ * @param arr 排序的原始数组 arr[left...mid] arr[mid+1...right]
+ * @param left 左边有序序列的初始索引
+ * @param mid 中间索引
+ * @param right 右边索引
+ */
+ public static void merge(int[] arr, int left, int mid, int right) {
+ //开辟额外大集合,设置指针
+ int[] temp = new int[right - left + 1];
+
+ int i = left; //左边有序序列的初始索引
+ int j = mid + 1; // 右边有序序列的初始化序索引
+ int tempIdx = 0; // 指向temp数组的当前位置
+
+ // 1)、将左右两边(有序)的数据按照规则填充到temp数组,任意一边处理完毕为止
+ while (i <= mid && j <= right) {
+ if (arr[i] < arr[j]) {
+ // 左小于右,将左数组第i个元素copy到temp数组的第t个位置
+ temp[tempIdx++] = arr[i++];
+ } else {
+ // 右小于或等于左,将右数组第j个元素copy到temp数组的第t个位置
+ temp[tempIdx++] = arr[j++];
+ }
+ }
+
+ // 2)、把有剩余数据的一边的数据依次填充到temp
+ while (i <= mid) {// 说明 右数组还有剩余元素
+ temp[tempIdx++] = arr[i++];
+ }
+ while (j <= right) {// 说明 右数组还有剩余元素
+ temp[tempIdx++] = arr[j++];
+ }
+
+ // 3)、将temp数组的数据copy到arr,注意从本次数组起始位置left,开始复制
+ for (int k = left; k < temp.length; k++) {
+ arr[k] = temp[k];
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort.java
new file mode 100644
index 00000000..45d903c9
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort.java
@@ -0,0 +1,125 @@
+package cn.lastwhisper.atguigu.sort;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+@Deprecated
+public class QuickSort {
+
+ public static void main(String[] args) {
+ //int[] arr = {-9, 78, 0, 23, -567, 70};
+ //int[] arr = {-9, 78, 0, 23, 60, 70};
+ //int[] arr = {9, 0, -567};
+ int[] arr = {49, 38, 65, 97, 76, 13, 27};
+
+ //int[] arr = ArrayUtil.generateArrByRandom(10000000);//耗时:1519 ms
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ //quickSort(arr, 0, arr.length - 1);
+ qSort(arr, 0, arr.length - 1);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+
+ System.out.println(Arrays.toString(arr));
+ }
+
+ public static void qSort(int[] arr, int head, int tail) {
+ if (head >= tail || arr == null || arr.length <= 1) {
+ return;
+ }
+ //定义俩指针 用于移动
+ int left = head;
+ int right = tail;
+ int pivot = arr[head]; //基准值,也可以arr[(head + tail) / 2]
+
+ while (left <= right) {
+ while (arr[left] < pivot) { //左指针先走,找到大于等于基准数的停止
+ ++left;
+ }
+ while (arr[right] > pivot) { //右指针后走,找到小于等于基准数的停止
+ --right;
+ }
+ if (left < right) {
+ //交换arr[left]和arr[right]的位置
+ int t = arr[left];
+ arr[left] = arr[right];
+ arr[right] = t;
+ //继续遍历
+ ++left;
+ --right;
+ } else if (left == right) {
+ //遍历完,错开两指针,用于退出循环
+ ++left;
+ //break;
+ }
+ }
+
+ qSort(arr, head, right);
+ qSort(arr, left, tail);
+ }
+
+
+ /**
+ * 快速排序,选取数组中间的值
+ * int pivot = arr[(left + right) / 2];
+ *
+ * @param arr
+ * @param left
+ * @param right
+ * @return void
+ */
+ public static void quickSort1(int[] arr, int left, int right) {
+ int l = left; // 左下标
+ int r = right; // 右下标
+ int temp;
+ // 中轴
+ int pivot = arr[(left + right) / 2];
+ // while循环的目的:小于或等于pivot的放左边,大于或等于pivot的放右边
+ while (l < r) {
+ // 在pivot左边找比pivot大的值的下标l
+ while (arr[l] < pivot) { //找到arr[1]=98 >pivot=0
+ l++;
+ }
+ // 在pivot右边找比pivot小的值的下标r
+ while (arr[r] > pivot) { //找到arr[4]=-567 < pivot=0
+ r--;
+ }
+ // 如果l>=r说明pivot左右两边的值,已经满足:
+ // 小于或等于pivot的放左边,大于或等于pivot的放右边 ——递归结束条件
+ if (l >= r) {
+ break;
+ }
+ // 交换
+ temp = arr[l];
+ arr[l] = arr[r];
+ arr[r] = temp;
+ // 如果交换完后,发现arr[l] == pivot,r--,前移 退出循环
+ if (arr[l] == pivot) {
+ r--;
+ }
+
+ // 如果交换完后,发现arr[r] == pivot,l++,后移 退出循环
+ if (arr[r] == pivot) {
+ l++;
+ }
+ }
+
+ // 第二步
+
+ // 如果l==r,必须l++,r--,否则会栈溢出
+ if (l == r) {
+ l = l + 1;
+ r = r - 1;
+ }
+ // 向左递归
+ if (left < r) {
+ quickSort1(arr, left, r);
+ }
+ // 向右递归
+ if (right > l) {
+ quickSort1(arr, l, right);
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort2.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort2.java
new file mode 100644
index 00000000..0c4a1124
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/QuickSort2.java
@@ -0,0 +1,58 @@
+package cn.lastwhisper.atguigu.sort;
+
+/**
+ * @author lastwhisper
+ */
+public class QuickSort2 {
+ public static void main(String[] args) {
+// int[] arr = {7, 3, 2, 8, 1, 9, 5, 4, 6};
+ //int arr[] = {7, 3, 2, 8, 1, 9, 5, 4, 6, 0};//为什么left++和right--条件里面要加 left <= right 限定
+ //int arr[] = {7, 3, 2, 8, 1, 9, 5, 4, 6, 10};
+// int arr[] = {7, 3, 2, 6, 8, 1, 9, 5, 4, 6, 10, 6, 6}; // 为什么不取等 arr[right] > pivot
+// int[] arr = {4, 6}; //小bug测试 // 为什么while (left <= right)里面要取等
+
+// int[] arr = {2, 10, 8, 22, 34, 5, 12, 28, 21, 11};
+ int[] arr = {49, 38, 65, 97, 76, 13, 27};
+// int[] arr = {2, 10, 8, 1000};
+ sort(arr, 0, arr.length - 1);
+ print(arr);
+ }
+
+ private static void sort(int[] arr, int left, int right) {
+ if (left >= right) return;
+ int mid = partition(arr, left, right);
+ sort(arr, left, mid - 1);
+ sort(arr, mid + 1, right);
+ }
+
+ /**
+ * @param arr 待分区数组
+ * @param left 左边界
+ * @param right 右边界
+ */
+ private static int partition(int[] arr, int left, int right) {
+ int pivot = arr[right];
+ int start = left, end = right - 1;
+ //
+ while (start <= end) {
+ while (start <= end && arr[start] <= pivot) start++;
+ while (start <= end && arr[end] > pivot) end--;
+ if (start < end) swap(arr, start, end);
+ }
+ // 此时的start一定>=pivot(right),所以需要交互
+ swap(arr, start, right);
+ return start;
+ }
+
+ private static void swap(int[] arr, int i, int j) {
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
+ }
+
+ private static void print(int[] arr) {
+ for (int value : arr) {
+ System.out.print(value + "\t");
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/RadixSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/RadixSort.java
new file mode 100644
index 00000000..b55a46a2
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/RadixSort.java
@@ -0,0 +1,152 @@
+package cn.lastwhisper.atguigu.sort;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class RadixSort {
+
+ public static void main(String[] args) {
+ // 算法实现步骤
+ //int[] arr = {53, 3, 542, 748, 14, 214};
+ //int[] arr = {3, 205, 14};
+ //radixSortStudy(arr);
+
+ // 计算耗时
+ // 占用内存 10000000 * 10 * 4 /1024 / 1024 /1024 = 0.3725290298461914
+ int[] arr = ArrayUtil.generateArrByRandom(10000000);
+ long start = System.currentTimeMillis();
+ radixSort(arr);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+
+ //System.out.println(Arrays.toString(arr));
+ }
+
+ /**
+ * 基数排序,桶排序的扩展
+ *
+ * @param arr
+ * @return void
+ */
+ public static void radixSort(int[] arr) {
+
+ int[][] bucket = new int[10][arr.length];
+ // 桶元素计数器,记录每个桶中存放了多少数据
+ int[] bucketElementCount = new int[10];
+ // 原arr下标
+ int arrIndex;
+ // 除数
+ int divisor = 1;
+ // 最大值
+ int max = arr[0];
+ // 由于基数排序次数由最大数值的位数决定,所以需要求最大值
+ for (int i = 1; i < arr.length; i++) {
+ if (max < arr[i]) {
+ max = arr[i];
+ }
+ }
+ // 最大值得位数,即排序次数
+ int maxLength = (max + "").length();
+ for (int f = 0; f < maxLength; f++) {
+ for (int i = 0; i < arr.length; i++) {
+ // digitOfElement对应放在哪个bucket中
+ int digitOfElement = arr[i] / divisor % 10;
+ // 放在第digitOfElement个bucket的第bucketElementCount[digitOfElement]下标位置
+ bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[i];
+ // 下次放在bucket的第bucketElementCount[digitOfElement]++下标位置
+ bucketElementCount[digitOfElement]++;
+ }
+
+ arrIndex = 0;
+ // 将桶中数据放回原数组
+ for (int i = 0; i < bucketElementCount.length; i++) {
+ // 对应桶中有数据
+ if (bucketElementCount[i] != 0) {
+ for (int j = 0; j < bucketElementCount[i]; j++) {
+ arr[arrIndex++] = bucket[i][j];
+ }
+ }
+ // 将桶元素计数器清空
+ bucketElementCount[i] = 0;
+ }
+ divisor *= 10;
+ }
+
+ }
+
+ /**
+ * 桶排序算法实现步骤
+ *
+ * @param arr
+ */
+ public static void radixSortStudy(int[] arr) {
+ //
+ int[][] bucket = new int[10][arr.length];
+ // 桶元素计数器,记录每个桶中存放了多少数据
+ int[] bucketElementCount = new int[10];
+ // 原arr下标
+ int arrIndex;
+
+ for (int i = 0; i < arr.length; i++) {
+ // digitOfElement对应放在哪个bucket中
+ int digitOfElement = arr[i] % 10;
+ // 放在第digitOfElement个bucket的第bucketElementCount[digitOfElement]下标位置
+ bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[i];
+ // 下次放在bucket的第bucketElementCount[digitOfElement]++下标位置
+ bucketElementCount[digitOfElement]++;
+ }
+
+ arrIndex = 0;
+ // 将桶中数据放回原数组
+ for (int i = 0; i < bucketElementCount.length; i++) {
+ // 对应桶中有数据
+ if (bucketElementCount[i] != 0) {
+ for (int j = 0; j < bucketElementCount[i]; j++) {
+ arr[arrIndex++] = bucket[i][j];
+ }
+ }
+ // 将桶元素计数器清空
+ bucketElementCount[i] = 0;
+ }
+
+ System.out.println("第1轮,对个位的排序处理 arr =" + Arrays.toString(arr));
+
+ for (int i = 0; i < arr.length; i++) {
+ // digitOfElement对应放在哪个bucket中
+ int digitOfElement = arr[i] / 10 % 10;
+ // 放在第digitOfElement个bucket的第bucketElementCount[digitOfElement]下标位置
+ bucket[digitOfElement][bucketElementCount[digitOfElement]] = arr[i];
+ // 下次放在bucket的第bucketElementCount[digitOfElement]++下标位置
+ bucketElementCount[digitOfElement]++;
+ }
+
+ arrIndex = 0;
+ // 将桶中数据放回原数组
+ for (int i = 0; i < bucketElementCount.length; i++) {
+ // 对应桶中有数据
+ if (bucketElementCount[i] != 0) {
+ for (int j = 0; j < bucketElementCount[i]; j++) {
+ arr[arrIndex++] = bucket[i][j];
+ }
+ }
+ // 将桶元素计数器清空
+ bucketElementCount[i] = 0;
+ }
+
+ System.out.println("第2轮,对个位的排序处理 arr =" + Arrays.toString(arr));
+
+ //for (int i = 0; i < bucket.length; i++) {
+ // for (int j = 0; j < bucket[0].length; j++) {
+ // System.out.printf("%d\t", bucket[i][j]);
+ // }
+ // System.out.println();
+ //}
+
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/SelectSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/SelectSort.java
new file mode 100644
index 00000000..14f8afb9
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/SelectSort.java
@@ -0,0 +1,82 @@
+package cn.lastwhisper.atguigu.sort;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class SelectSort {
+
+ public static void main(String[] args) {
+ //int[] arr = {101, 34, 119, 1};
+ //selectSort1(arr);
+ //System.out.println(Arrays.toString(arr));
+
+ int[] arr = ArrayUtil.generateArrByRandom(100000);
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ selectSort(arr);
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+ }
+
+ /**
+ * 选择排序O(n^2)
+ *
+ * @param arr
+ * @return void
+ */
+ public static void selectSort(int[] arr) {
+ for (int i = 0; i < arr.length; i++) {
+ int min = arr[i];
+ int minIndex = i;
+ // 找出[1+i...arr.length]范围内的最小值,和最小值索引
+ for (int j = i + 1; j < arr.length; j++) {
+ if (min > arr[j]) {
+ min = arr[j];
+ minIndex = j;
+ }
+ }
+ // 交换最小值与第i个数的位置
+ if (minIndex != i) {
+ arr[minIndex] = arr[i];
+ arr[i] = min;
+ }
+ }
+ }
+
+ /**
+ * 算法的实现步骤
+ *
+ * @param arr
+ * @return void
+ */
+ public static void selectSort1(int[] arr) {
+ // 第1轮 {101, 34, 119, 1}
+
+ // 1、找最小值
+ // 最小值
+ int min = arr[0];
+ // 最小值索引
+ int minIndex = 0;
+ // {101, 34, 119, 1} 我们假设待排序数组第一个元素就是最小值,即min=arr[0],minIndex=0
+ // 经过遍历后我们发现 min=1,minIndex=3
+ for (int j = 0 + 1; j < arr.length; j++) {
+ // 找最小的数
+ if (min > arr[j]) {
+ min = arr[j];
+ minIndex = j;
+ }
+ }
+ // 2、交换。minIndex是最小值所在位置,min是最小值
+ if (minIndex != 0) {
+ arr[minIndex] = arr[0];
+ arr[0] = min;
+ }
+
+ System.out.printf("第1轮选择,数组:%s", Arrays.toString(arr));
+ }
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/ShellSort.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/ShellSort.java
new file mode 100644
index 00000000..edd5254a
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/ShellSort.java
@@ -0,0 +1,132 @@
+package cn.lastwhisper.atguigu.sort;
+
+import java.util.Arrays;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class ShellSort {
+ public static void main(String[] args) {
+ int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
+ //int[] arr = ArrayUtil.generateArrByRandom(100000);
+ // 计算耗时
+ long start = System.currentTimeMillis();
+ //shellSort1(arr); // 10490 ms
+ shellSort2(arr); // 10490 ms
+ long end = System.currentTimeMillis();
+ System.out.println("耗时:" + (end - start) + " ms");
+
+ System.out.printf("希尔排序(交换法),数组:%s\n", Arrays.toString(arr));
+ }
+
+
+ /**
+ * 希尔排序(位移法)(插入排序)
+ *
+ * @param arr
+ * @return void
+ */
+ public static void shellSort2(int[] arr) {
+ // {8, 9, 1, 7, 2, 3, 5, 4, 6, 0}
+ int gap = arr.length / 2;
+ int j;
+ int temp;
+ while (gap > 0) {
+ //gap=5,2,1,0
+ // i=5——》{0,5}
+ // i=6——》{1,6}
+ // i=7——》{2,7}
+ // i=8——》{3,8}
+ // i=9——》{4,9}
+ // 对每个数组进行插入排序;由于每个数组并不是真的存在的数组,而是一个大数组
+ // 所以通过gap进行取值
+ for (int i = gap; i < arr.length; i++) {
+ j = i; // 缓存每个数组的起始下标
+ temp = arr[i]; // 缓存每个数组的起始下标的数值
+ if (temp < arr[j - gap]) {
+ while (j - gap >= 0 && temp < arr[j - gap]) {
+ // 从前往后移动
+ arr[j] = arr[j - gap];
+ j = j - gap;
+ }
+ //
+ arr[j] = temp;
+ }
+ }
+ gap = gap / 2;
+ }
+ }
+
+ /**
+ * 希尔排序(交换法)
+ *
+ * @param arr
+ * @return void
+ */
+ public static void shellSort1(int[] arr) {
+ // 第1轮 {8, 9, 1, 7, 2, 3, 5, 4, 6, 0}
+ // 增量 gap=length/2
+ int gap = arr.length / 2;
+ int temp;
+ while (gap > 0) {
+ for (int i = gap; i < arr.length; i++) {
+ // 交换
+ for (int j = i - gap; j >= 0; j -= gap) {
+ if (arr[j] > arr[j + gap]) {
+ temp = arr[j + gap];
+ arr[j + gap] = arr[j];
+ arr[j] = temp;
+ }
+ }
+ }
+ gap = gap / 2;
+ }
+ }
+
+ /**
+ * 交换法希尔排序
+ *
+ * @param arr
+ * @return void
+ */
+ public static void shellSortStudy(int[] arr) {
+ // 第1轮 [8, 9, 1, 7, 2, 3, 5, 4, 6, 0]
+ // 增量 gap=length/2
+ int temp;
+ for (int i = 5; i < arr.length; i++) {
+ // 交换
+ for (int j = i - 5; j >= 0; j -= 5) {
+ if (arr[j] > arr[j + 5]) {
+ temp = arr[j + 5];
+ arr[j + 5] = arr[j];
+ arr[j] = temp;
+ }
+ }
+ }
+ System.out.printf("第1轮插入,数组:%s\n", Arrays.toString(arr));
+ // 第二轮 [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
+ for (int i = 2; i < arr.length; i++) {
+ // 交换
+ for (int j = i - 2; j >= 0; j -= 2) {
+ if (arr[j] > arr[j + 2]) {
+ temp = arr[j + 2];
+ arr[j + 2] = arr[j];
+ arr[j] = temp;
+ }
+ }
+ }
+ System.out.printf("第2轮插入,数组:%s\n", Arrays.toString(arr));
+ // 第三轮 [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
+ for (int i = 1; i < arr.length; i++) {
+ // 交换
+ for (int j = i - 1; j >= 0; j -= 1) {
+ if (arr[j] > arr[j + 1]) {
+ temp = arr[j + 1];
+ arr[j + 1] = arr[j];
+ arr[j] = temp;
+ }
+ }
+ }
+ System.out.printf("第3轮插入,数组:%s\n", Arrays.toString(arr));
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/TimeComplexity.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/TimeComplexity.java
new file mode 100644
index 00000000..36734634
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/TimeComplexity.java
@@ -0,0 +1,58 @@
+package cn.lastwhisper.atguigu.sort;
+
+/**
+ * 时间复杂度举例
+ * @author cn.lastwhisper
+ */
+public class TimeComplexity {
+
+ public static void main(String[] args){
+ new TimeComplexity().n2(5);
+ }
+ // O(1)
+ public void o1() {
+ int x = 1, s;
+ for (int i = 0; i < 10000; i++) {
+ x++;
+ s = 0;
+ }
+ }
+
+ // O(logn)
+ public void logn(int n) {
+ int i = 1;
+ while (i < n) {
+ i = i * 2;
+ }
+ }
+
+ // O(n)
+ public void n(int n) {
+ int j = 0;
+ for (int i = 0; i < n; i++) {
+ j++;
+ }
+ }
+
+ // O(nlogn)
+ public void nlogn(int n) {
+ for (int m = 0; m < n; m++) {
+ int i = 1;
+ while (i < n) {
+ i = i * 2;
+ }
+ }
+ }
+
+ // O(n^2)
+ public void n2(int n) {
+ int k = 0;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ k++;
+ }
+ }
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap.java
new file mode 100644
index 00000000..5e3cb8e0
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap.java
@@ -0,0 +1,100 @@
+package cn.lastwhisper.atguigu.sort.heap;
+
+import java.util.Arrays;
+
+public class Heap {
+ private int[] a; // 数组,从下标 1 开始存储数据
+ private int n; // 堆可以存储的最大数据个数
+ private int count; // 堆中已经存储的数据个数
+
+ public Heap(int capacity) {
+ a = new int[capacity + 1];
+ n = capacity;
+ count = 0;
+ }
+
+ /**
+ * 插入
+ */
+ public void insert(int data) {
+ if (count >= n) return; // 堆满了
+ ++count;
+ a[count] = data;
+ int i = count;
+ while (i / 2 > 0 && a[i] > a[i / 2]) { // 自下往上堆化
+ swap(a, i, i / 2); // swap() 函数作用:交换下标为 i 和 i/2 的两个元素
+ i = i / 2;
+ }
+ }
+
+ /**
+ * 删除堆顶
+ */
+ public void removeMax() {
+ if (count == 0) return; // 堆中没有数据
+ a[1] = a[count];
+ --count;
+ heapify(a, count, 1);
+ }
+
+ /**
+ * 堆化大顶堆
+ * 自上往下堆化
+ *
+ * @param arr 数组
+ * @param n 堆中元素个数
+ * @param i 待堆化的元素
+ */
+ private static void heapify(int[] arr, int n, int i) {
+ while (true) {
+ // 待堆化元素、左、右子树最大值的下标
+ int maxPos = i;
+ // 待堆化元素小于左子树
+ if (i * 2 <= n && arr[i] < arr[i * 2]) {
+ maxPos = i * 2;
+ }
+ // 待堆化元素左子树小于右子树,
+ if (i * 2 + 1 <= n && arr[maxPos] < arr[i * 2 + 1]) {
+ maxPos = i * 2 + 1;
+ }
+ // 最大元素是自己无需向下堆化
+ if (maxPos == i) break;
+ // 最大元素不是自己,交互值
+ swap(arr, i, maxPos);
+ // 将被交互的元素继续向下堆化
+ i = maxPos;
+ }
+ }
+
+ private static void buildHeap(int[] a, int n) {
+ // 对非叶子节点进行,自上往下堆化
+ for (int i = n / 2; i >= 1; --i) {
+ heapify(a, n, i);
+ }
+ }
+
+ // n 表示数据的个数,数组 a 中的数据从下标 1 到 n 的位置。
+ public static void sort(int[] a, int n) {
+ buildHeap(a, n);
+ int k = n;
+ while (k > 1) {
+ swap(a, 1, k);
+ --k;
+ heapify(a, k, 1);
+ }
+ }
+
+ private static void swap(int[] arr, int i, int j) {
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
+ }
+
+ public static void main(String[] args) {
+ int[] arr = new int[]{-99999, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+ System.out.println(Arrays.toString(arr));
+ Heap.sort(arr, arr.length - 1);
+ System.out.println(Arrays.toString(arr));
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap2.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap2.java
new file mode 100644
index 00000000..2340e830
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/Heap2.java
@@ -0,0 +1,69 @@
+package cn.lastwhisper.atguigu.sort.heap;
+
+import java.util.Arrays;
+
+public class Heap2 {
+
+
+ /**
+ * 堆化大顶堆
+ * 自上往下堆化
+ *
+ * @param arr 数组
+ * @param n 堆中元素个数
+ * @param i 待堆化的元素
+ */
+ private static void heapify(int[] arr, int n, int i) {
+ while (true) {
+ // 待堆化元素、左、右子树最大值的下标
+ int maxPos = i;
+ // 待堆化元素小于左子树
+ if (i * 2 + 1 <= n && arr[i] < arr[i * 2 + 1]) {
+ maxPos = i * 2 + 1;
+ }
+ // 待堆化元素左子树小于右子树,
+ if (i * 2 + 2 <= n && arr[maxPos] < arr[i * 2 + 2]) {
+ maxPos = i * 2 + 2;
+ }
+ // 最大元素是自己无需向下堆化
+ if (maxPos == i) break;
+ // 最大元素不是自己,交互值
+ swap(arr, i, maxPos);
+ // 将被交互的元素继续向下堆化
+ i = maxPos;
+ }
+ }
+
+ private static void buildHeap(int[] a, int n) {
+ // 对非叶子节点进行,自上往下堆化
+ for (int i = n / 2 - 1; i >= 0; --i) {
+ heapify(a, n, i);
+ }
+ }
+
+ public static void sort(int[] arr, int n) {
+ // 1. 构建大顶堆
+ buildHeap(arr, n);
+ int disorder = n;
+ // 2. 交换堆顶元素与末尾元素,调整堆结构
+ while (disorder > 0) {
+ swap(arr, 0, disorder);
+ disorder--;
+ heapify(arr, disorder, 0);
+ }
+ }
+
+ private static void swap(int[] arr, int i, int j) {
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
+ }
+
+ public static void main(String[] args) {
+ int[] arr = new int[]{9, 10, 7, 6, 5, 4, 3, 2, 1};
+ System.out.println(Arrays.toString(arr));
+ Heap2.sort(arr, arr.length - 1);
+ System.out.println(Arrays.toString(arr));
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/HeapOperator.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/HeapOperator.java
new file mode 100644
index 00000000..91466e9d
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sort/heap/HeapOperator.java
@@ -0,0 +1,87 @@
+package cn.lastwhisper.atguigu.sort.heap;
+
+import java.util.Arrays;
+
+/**
+ * 对于二叉堆,如下有几种操作:
+ * 插入节点
+ * 删除节点
+ * 构建二叉堆
+ *
+ * @author cunchang
+ * @date 2022/6/12 11:34 PM
+ */
+public class HeapOperator {
+
+
+ /**
+ * 上浮调整
+ * @param array 待调整的堆
+ */
+ public static void upAdjust(int[] array) {
+ int childIndex = array.length - 1;
+ // child = 2*parent+1,则 parent = (child - 1) / 2
+ int parentIndex = (childIndex - 1) / 2;
+ // temp保存插入的叶子节点值,用于最后的赋值
+ int temp = array[childIndex];
+ while (childIndex > 0 && temp < array[parentIndex]) {
+ //无需真正交换,单向赋值即可
+ array[childIndex] = array[parentIndex];
+ childIndex = parentIndex;
+ parentIndex = (parentIndex - 1) / 2;
+ }
+ array[childIndex] = temp;
+ }
+
+
+ /**
+ * 下沉调整
+ * @param array 待调整的堆
+ * @param parentIndex 要下沉的父节点
+ * @param length 堆的有效大小
+ */
+ public static void downAdjust(int[] array, int parentIndex, int length) {
+ // temp保存父节点值,用于最后的赋值
+ int temp = array[parentIndex];
+ int childIndex = 2 * parentIndex + 1;
+ while (childIndex < length) {
+ // 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子
+ if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
+ childIndex++;
+ }
+ // 如果父节点小于任何一个孩子的值,直接跳出
+ if (temp <= array[childIndex]) {
+ break;
+ }
+ //无需真正交换,单向赋值即可
+ array[parentIndex] = array[childIndex];
+ parentIndex = childIndex;
+ childIndex = 2 * childIndex + 1;
+ }
+ array[parentIndex] = temp;
+ }
+
+
+ /**
+ * 构建堆
+ *
+ * @param array 待调整的堆
+ */
+ public static void buildHeap(int[] array) {
+ // 从最后一个非叶子节点开始,依次下沉调整
+ for (int i = array.length / 2; i >= 0; i--) {
+ downAdjust(array, i, array.length - 1);
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] array = new int[]{1, 3, 2, 6, 5, 7, 8, 9, 10, 0};
+ upAdjust(array);
+ System.out.println(Arrays.toString(array));
+ array = new int[]{7, 1, 3, 10, 5, 2, 8, 9, 6};
+ buildHeap(array);
+ System.out.println(Arrays.toString(array));
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/BestSpmatrix.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/BestSpmatrix.java
new file mode 100644
index 00000000..e1fd05b9
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/BestSpmatrix.java
@@ -0,0 +1,81 @@
+package cn.lastwhisper.atguigu.sparsematrix;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class BestSpmatrix {
+ /**
+ * 三元组,保存有效元素的信息
+ */
+ private static class Node {
+ int i, j; // 三元组的行、列坐标
+ int v; // 三元组的值
+
+ public Node(int i, int j, int v) {
+ this.i = i;
+ this.j = j;
+ this.v = v;
+ }
+ }
+
+ private static class Spmatrix {
+ int rows, cols;//稀疏矩阵的行、列数
+ int terms; //稀疏矩阵的非零元素个数
+ Node[] node;//存放稀疏矩阵的三元组表
+
+ public Spmatrix(int rows, int cols, int terms, Node[] node) {
+ this.rows = rows;
+ this.cols = cols;
+ this.terms = terms;
+ this.node = node;
+ }
+ }
+
+ public static void main(String[] args) {
+ // 准备一个稀疏矩阵
+ int rowNum = 11;
+ int columnNum = 11;
+ int[][] chessMatrix = new int[rowNum][columnNum];
+ chessMatrix[1][2] = 1;
+ chessMatrix[2][3] = 2;
+
+ // 压缩
+ int sum = 0;
+ for (int i = 0; i < rowNum; i++) {
+ for (int j = 0; j < columnNum; j++) {
+ if (chessMatrix[i][j] != 0) {
+ sum++;
+ }
+ }
+ }
+ Spmatrix spmatrix = new Spmatrix(rowNum, columnNum, sum, new Node[sum]);
+
+ int index = 0;
+ for (int i = 0; i < rowNum; i++) {
+ for (int j = 0; j < columnNum; j++) {
+ if (chessMatrix[i][j] != 0) {
+ spmatrix.node[index++] = new Node(i, j, chessMatrix[i][j]);
+ }
+ }
+ }
+
+ for (int i = 0; i < spmatrix.node.length; i++) {
+ System.out.printf("%d\t%d\t%d\t\n", spmatrix.node[i].i, spmatrix.node[i].j, spmatrix.node[i].v);
+ }
+
+ // 还原
+ // 取出第一行元素的行、列维度,创建稀疏矩阵
+ int[][] matrix = new int[spmatrix.rows][spmatrix.cols];
+ // 追个取出剩余行并还原稀疏矩阵
+ for (int i = 0; i < spmatrix.node.length; i++) {
+ matrix[spmatrix.node[i].i][spmatrix.node[i].j] = spmatrix.node[i].v;
+ }
+
+ for (int i = 0; i < rowNum; i++) {
+ for (int j = 0; j < columnNum; j++) {
+ System.out.printf("%d\t", matrix[i][j]);
+ }
+ System.out.println();
+ }
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/SparseMatrix.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/SparseMatrix.java
new file mode 100644
index 00000000..50f30722
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/sparsematrix/SparseMatrix.java
@@ -0,0 +1,90 @@
+package cn.lastwhisper.atguigu.sparsematrix;
+
+/**
+ * 稀疏矩阵压缩与还原
+ * @author cn.lastwhisper
+ */
+public class SparseMatrix {
+
+
+ /**
+ * 压缩稀疏矩阵
+ * 稀疏数组第一行保存数组的行、列的维度以及有效元素的个数
+ * 其余行保存有效数据行、列的坐标以及有效数据本身
+ *
+ * @param sparseMatrix 稀疏矩阵
+ * @param rows 行维度
+ * @param cols 列维度
+ * @return int[][]
+ */
+ private int[][] compress(int[][] sparseMatrix, int rows, int cols) {
+ // 计算有效元素的个数
+ int sum = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ if (sparseMatrix[i][j] != 0) {
+ sum++;
+ }
+ }
+ }
+ // 三元组表
+ int[][] tripleTable = new int[sum + 1][3];
+ tripleTable[0][0] = rows;
+ tripleTable[0][1] = cols;
+ tripleTable[0][2] = sum;
+ int index = 0;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cols; j++) {
+ if (sparseMatrix[i][j] != 0) {
+ index++;
+ tripleTable[index][0] = i;
+ tripleTable[index][1] = j;
+ tripleTable[index][2] = sparseMatrix[i][j];
+ }
+ }
+ }
+ return tripleTable;
+ }
+
+ /**
+ * 还原稀疏矩阵
+ *
+ * @param tripleTable
+ * @return int[][]
+ */
+ private int[][] recovery(int[][] tripleTable) {
+ // 取出第一行元素的行、列维度,创建稀疏矩阵
+ int[][] matrix = new int[tripleTable[0][0]][tripleTable[0][1]];
+ // 追个取出剩余行并还原稀疏矩阵
+ for (int i = 1; i < tripleTable.length; i++) {
+ matrix[tripleTable[i][0]][tripleTable[i][1]] = tripleTable[i][2];
+ }
+ return matrix;
+ }
+
+ public static void main(String[] args) {
+ // 准备一个稀疏矩阵
+ int rowNum = 11;
+ int columnNum = 11;
+ int[][] chessMatrix = new int[rowNum][columnNum];
+ chessMatrix[1][2] = 1;
+ chessMatrix[2][3] = 2;
+ // 压缩
+ SparseMatrix sparseMatrix = new SparseMatrix();
+ int[][] tripleTable = sparseMatrix.compress(chessMatrix, rowNum, columnNum);
+
+ for (int i = 0; i < tripleTable.length; i++) {
+ System.out.printf("%d\t%d\t%d\t\n", tripleTable[i][0], tripleTable[i][1], tripleTable[i][2]);
+ }
+ // 还原
+ chessMatrix = sparseMatrix.recovery(tripleTable);
+
+ for (int i = 0; i < rowNum; i++) {
+ for (int j = 0; j < columnNum; j++) {
+ System.out.printf("%d\t", chessMatrix[i][j]);
+ }
+ System.out.println();
+ }
+
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/ArrayStackDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/ArrayStackDemo.java
new file mode 100644
index 00000000..5a20dc99
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/ArrayStackDemo.java
@@ -0,0 +1,74 @@
+package cn.lastwhisper.atguigu.stack;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class ArrayStackDemo {
+ public static void main(String[] args) {
+ ArrayStack arrayStack = new ArrayStack(4);
+ arrayStack.push(1);
+ arrayStack.push(2);
+ arrayStack.push(3);
+ arrayStack.push(4);
+ arrayStack.push(5);
+ System.out.println("当前栈的容量:"+arrayStack.size());
+ arrayStack.push(6);
+ System.out.println(arrayStack.pop());
+ System.out.println(arrayStack.pop());
+ System.out.println(arrayStack.pop());
+ System.out.println(arrayStack.pop());
+ System.out.println("当前栈的容量:"+arrayStack.size());
+ }
+}
+
+class ArrayStack {
+ private int[] arr;
+ private int n = 0;
+
+ public ArrayStack(int initialCapacity) {
+ this.arr = new int[initialCapacity];
+ }
+ //
+ // 栈满
+
+ // 栈空
+ public boolean isEmpty() {
+ return n == 0;
+ }
+
+ // 入栈
+ public void push(int data) {
+ if (n > 0 && n == arr.length) {
+ resize(2 * n);
+ }
+ arr[n++] = data;
+ }
+
+ // 出栈
+ public int pop() {
+ if (isEmpty()) {
+ return -1;
+ }
+ if (n > 0 && n == (arr.length / 4)) {
+ resize(n / 2);
+ }
+ return arr[--n];
+ }
+ // 查看栈顶元素
+ public int peek(){
+ int temp = n;
+ return arr[--temp];
+ }
+
+ // size
+ public int size() {
+ return n;
+ }
+
+ // resize
+ public void resize(int maxSize) {
+ int[] newArr = new int[maxSize];
+ if (size() >= 0) System.arraycopy(arr, 0, newArr, 0, size());
+ this.arr = newArr;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/InCalculator.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/InCalculator.java
new file mode 100644
index 00000000..1288660c
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/InCalculator.java
@@ -0,0 +1,188 @@
+package cn.lastwhisper.atguigu.stack;
+
+/**
+ * 中缀表达式
+ * @author cn.lastwhisper
+ */
+public class InCalculator {
+ public static void main(String[] args) {
+ // 符号栈、数值栈
+ CalcArrayStack operStack = new CalcArrayStack(10);
+ CalcArrayStack numStack = new CalcArrayStack(10);
+ // 计算结果、数值1、数值2、运算符、当前值
+ int res = 0;
+ int num1 = 0;
+ int num2 = 0;
+ int oper = 0;
+ int ch = 0;
+ String keepNum = "";
+ // index辅助遍历表达式
+ int index = 0;
+ // 中缀表达式
+ //String expression = "7*2*2-5+1-5+3-4";
+ String expression = "30+2*6-2";
+ // 解析中缀表达式
+ while (index != expression.length()) {
+ // 1、通过index遍历表达式
+ ch = expression.substring(index, index + 1).charAt(0);
+ if (operStack.isOper(ch)) {
+ // 2、如果是oper进入符号栈,入符号栈分为两种情况
+ // (1)、当前符号栈为空,直接入栈
+ // (2)、当前符号栈不为空,peek出栈顶运算符,进行运算符优先级比较
+ // 如果当前oper优先级大于栈顶运算符,直接入栈
+ // 如果小于,弹出符号栈栈顶运算符,弹出两个数值,进行运算并将运算结果push数值栈,再将oper压入符号栈
+ if (operStack.isEmpty()) {
+ operStack.push(ch);
+ } else {
+ oper = operStack.peek();
+ if (operStack.priority(ch) > operStack.priority(oper)) {
+ operStack.push(ch);
+ } else {
+ oper = operStack.pop();
+ num1 = numStack.pop();
+ num2 = numStack.pop();
+ res = numStack.calc(num1, num2, oper);
+ numStack.push(res);
+ operStack.push(ch);
+ }
+ }
+ } else {
+ // 3、如果是num进入数值栈
+ //numStack.push(ch - 48);
+ // 处理多位数'1'+'5'——》15
+ keepNum += ch - 48;
+ // ch是否是最后一个字符
+ if (index == expression.length() - 1) {
+ numStack.push(Integer.parseInt(keepNum));
+ } else {
+ // 如果后一位是运算符,则将当前keepNum入栈
+ if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
+ numStack.push(Integer.parseInt(keepNum));
+ // 每次清空多位数
+ keepNum = "";
+ }
+ }
+ }
+ index++;
+ }
+ // 4、解析完表达式后,弹出符号栈、数值栈对应的运算符和数值进行运算,再将结果入栈
+ // 重复第四步直至数值栈中只剩一个运算结果
+ while (!operStack.isEmpty()) {
+ oper = operStack.pop();
+ num1 = numStack.pop();
+ num2 = numStack.pop();
+ res = numStack.calc(num1, num2, oper);
+ numStack.push(res);
+ }
+ System.out.printf("%s=%d", expression, numStack.pop());
+ }
+}
+
+
+class CalcArrayStack {
+ private int[] arr;
+ private int n = 0;
+
+ public CalcArrayStack(int initialCapacity) {
+ this.arr = new int[initialCapacity];
+ }
+ // 栈满
+
+ // 入栈
+ public void push(int data) {
+ if (n > 0 && n == arr.length) {
+ resize(2 * arr.length);
+ }
+ arr[n++] = data;
+ }
+
+ // 出栈
+ public int pop() {
+ if (isEmpty()) {
+ throw new RuntimeException("栈空");
+ }
+ if (n > 0 && n == (arr.length / 4)) {
+ resize(arr.length / 2);
+ }
+ return arr[--n];
+ }
+
+ /**
+ * 显示栈中情况
+ */
+ public void list() {
+ for (int i = n; i > 0; i--) {
+ System.out.printf("%d\t", arr[i]);
+ }
+ }
+
+ // 栈空
+ public boolean isEmpty() {
+ return n == 0;
+ }
+
+ // 查看栈顶元素
+ public int peek() {
+ if (isEmpty()) {
+ throw new RuntimeException("栈空");
+ }
+ int temp = n;
+ return arr[--temp];
+ }
+
+ // size
+ public int size() {
+ return n;
+ }
+
+ // resize
+ public void resize(int maxSize) {
+ int[] newArr = new int[maxSize];
+ for (int i = 0; i < size(); i++) {
+ newArr[i] = arr[i];
+ }
+ this.arr = newArr;
+ }
+
+ // 返回运算符的优先级
+ public int priority(int oper) {
+ if (oper == '*' || oper == '/') {
+ return 1;
+ } else if (oper == '+' || oper == '-') {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+
+ // 判断是不是一个运算符
+ public boolean isOper(int oper) {
+ return oper == '*' || oper == '/' || oper == '+' || oper == '-';
+ }
+
+ // 计算方法接收两个数和一个运算符
+ public int calc(int num1, int num2, int oper) {
+ int res = 0;
+ switch (oper) {
+ case '*':
+ res = num1 * num2;
+ break;
+ case '/':
+ if (num2 != 0) {
+ res = num1 / num2;
+ } else {
+ throw new RuntimeException("非法的除零运算");
+ }
+ break;
+ case '+':
+ res = num1 + num2;
+ break;
+ case '-':
+ res = num2 - num1;
+ break;
+ default:
+ break;
+ }
+ return res;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/SuCalculator.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/SuCalculator.java
new file mode 100644
index 00000000..18d7afa0
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/stack/SuCalculator.java
@@ -0,0 +1,226 @@
+package cn.lastwhisper.atguigu.stack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * 后缀表达式
+ * @author cn.lastwhisper
+ */
+public class SuCalculator {
+ public static void main(String[] args) {
+ // 1+((2+3)*4)-5 ——》
+ String infixExpression = "111+((2+3)*4)-5";
+ //String suffixExpression = "";
+ // 中缀String串转中缀List "1+((2+3)*4)-5" ——》[1,+,(,(,2,+,3,),*,4,),-,5]
+ //List infixList = infixStrConvertInfixList(infixExpression);
+ //for (int i = 0; i < infixList.size(); i++) {
+ // String str = infixList.get(i);
+ // if (i == infixList.size() - 1) {
+ // System.out.print(str);
+ // } else {
+ // System.out.print(str + " ");
+ // }
+ //}
+ // 前缀表达式转为后缀表达式 "1+((2+3)*4)-5"——》"1 2 3 + 4 * + 5 -"
+ String suffixExpression = infixConverSufix(infixExpression);
+ System.out.println("前缀:" + infixExpression);
+ System.out.println("后缀:" + suffixExpression);
+ // 后缀表达式解析计算
+ int result = calcSufix(suffixExpression);
+ System.out.println("计算结果:" + result);
+ }
+
+ /**
+ * 中缀表达式转后缀表达式
+ *
+ * @param infixExpression
+ * @return java.lang.String
+ */
+ public static String infixConverSufix(String infixExpression) {
+ List infixExpressionList = infixStrConvertInfixList(infixExpression);
+ // 1、初始化两个栈,s1运算符栈,s2中间结果栈
+ Stack s1 = new Stack<>();
+ //说明:因为s2 这个栈,在整个转换过程中,没有pop操作,而且后面我们还需要逆序输出
+ //因此比较麻烦,这里可以不用 Stack 直接使用 List s2
+ Stack s2 = new Stack<>();
+ // 2、循环遍历前缀表达式
+ for (String infix : infixExpressionList) {
+ // 3、遇到运算符infix,与s1栈顶运算符比较优先级 ")">"*"="/">"+"="-">"("
+ // (1)、如果s1栈顶是空,或者是"(",直接入栈
+ // (2)、如果infix优先级大于s1栈顶运算符,直接入栈
+ // (3)、如果infix优先级小于或等同于s1栈顶运算符,将s1栈顶运算符压入s2栈。再将oper重复(1)(2)(3)
+ if (isOper(infix)) {
+ boolean flag = true;
+ while (flag) {
+ // .trim()是因为,3(3)会将s1栈顶运算符压入s2栈,而s1栈中元素都加上空格了
+ if (s1.isEmpty() || s1.peek().trim().equals("(")) {
+ s1.push(infix + " ");
+ flag = false;
+ } else if (priority(infix) > priority(s1.peek().trim())) {
+ s1.push(infix + " ");
+ flag = false;
+ } else if (priority(infix) <= priority(s1.peek().trim())) {
+ s2.push(s1.pop());
+ }
+ }
+ // 4、遇到括号
+ } else if (infix.equals("(")) {
+ // (1)、左括号直接入s1栈
+ s1.push(infix);
+ } else if (infix.equals(")")) {
+ // (2)、右括号。依次弹出s1栈顶运算符,压入s2中,直到遇到左括号位置,此时将左括号丢弃
+ //while (!s1.peek().equals(")")) {
+ // s2.push(s1.pop() + " ");
+ //}
+ while (true) {
+ s2.push(s1.pop().trim() + " ");
+ if (s1.peek().equals("(")) {
+ break;
+ }
+ }
+ s1.pop();
+ } else if (infix.matches("\\d+")) {
+ // 5、遇到数字,直接入s2
+ s2.push(infix + " ");
+ }
+
+ }
+ // 6、将s1剩余的运算符压入s2中
+ while (!s1.empty()) {
+ s2.push(s1.pop().trim());
+ }
+ // 7、依次弹出s2中的元素,即后缀表达式
+ StringBuffer sb = new StringBuffer();
+ for (String s : s2) {
+ sb.append(s);
+ }
+ return sb.toString().trim();
+ }
+
+ /**
+ * 中缀表达式String转中缀表达式Lsit
+ *
+ * @param infixExpression
+ * @return java.array.List
+ */
+ public static List infixStrConvertInfixList(String infixExpression) {
+ List infixList = new ArrayList<>();
+ // 记录多位数
+ String keepNum = "";
+ for (int i = 0; i < infixExpression.length(); i++) {
+ String subStr = infixExpression.substring(i, i + 1);
+ if (isOper(subStr)) {
+ // 运算符
+ infixList.add(subStr);
+ } else if (subStr.equals("(") || subStr.equals(")")) {
+ infixList.add(subStr);
+ } else {
+ // 数字
+ keepNum += subStr;
+ // 前缀表达式的最后一个字符
+ if (i == infixExpression.length() - 1) {
+ infixList.add(keepNum);
+ } else {
+ // 非前缀表达式的最后一个字符
+ // 后一位是字符
+ String subnextStr = infixExpression.substring(i + 1, i + 2);
+ if (isOper(subnextStr) || subnextStr.equals("(") || subnextStr.equals(")")) {
+ infixList.add(keepNum);
+ keepNum = "";
+ }
+ }
+ }
+
+ }
+ return infixList;
+ }
+
+ /**
+ * 解析并计算后缀表达式
+ *
+ * @param suffixExpression
+ * @return int
+ */
+ public static int calcSufix(String suffixExpression) {
+ String[] expressions = suffixExpression.split(" ");
+ Stack stack = new Stack<>();
+ // 1、遍历表达式
+ for (int i = 0; i < expressions.length; i++) {
+ String str = expressions[i];
+ if (isOper(str)) {
+ // 2、遇到运算符,弹出栈顶两个数,进行运算入栈
+ Integer num1 = stack.pop();
+ Integer num2 = stack.pop();
+ int sum = calc(num1, num2, str);
+ stack.push(sum);
+ } else {
+ // 3、将数字push入栈
+ stack.push(Integer.valueOf(str));
+ }
+ }
+ return stack.pop();
+ }
+
+ /**
+ * 判断是否是运算符
+ *
+ * @param oper
+ * @return boolean
+ */
+ public static boolean isOper(String oper) {
+ return "*".equals(oper) || "/".equals(oper) || "+".equals(oper) || "-".equals(oper);
+ }
+
+ /**
+ * 计算两个num
+ *
+ * @param num1
+ * @param num2
+ * @param oper
+ * @return int
+ */
+ public static int calc(int num1, int num2, String oper) {
+ int res = 0;
+ switch (oper) {
+ case "*":
+ res = num1 * num2;
+ break;
+ case "/":
+ res = num1 / num2;
+ break;
+ case "+":
+ res = num1 + num2;
+ break;
+ case "-":
+ res = num2 - num1;
+ break;
+ default:
+ break;
+ }
+ return res;
+ }
+
+ /**
+ * 计算运算符的优先级
+ * ")">"*"="/">"+"="-">"("
+ * @param oper
+ * @return int
+ */
+ public static int priority(String oper) {
+ int res = -1;
+ if (oper.equals(")")) {
+ res = 3;
+ } else if (oper.equals("*") || oper.equals("/")) {
+ res = 2;
+ } else if (oper.equals("+") || oper.equals("-")) {
+ res = 1;
+ } else if (oper.equals("(")) {
+ res = 0;
+ } else {
+ res = -1;
+ }
+ return res;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ArrBinaryTreeDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ArrBinaryTreeDemo.java
new file mode 100644
index 00000000..59ca8711
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ArrBinaryTreeDemo.java
@@ -0,0 +1,80 @@
+package cn.lastwhisper.atguigu.tree;
+
+import cn.lastwhisper.util.ArrayUtil;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class ArrBinaryTreeDemo {
+ public static void main(String[] args) {
+
+ int[] arr = ArrayUtil.generateArrByOrder(7);
+ ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
+ System.out.println("先序:");
+ arrBinaryTree.preOrder(0);//1 2 4 5 3 6 7
+ //System.out.println();
+ //System.out.println("中序:");
+ //arrBinaryTree.infixOrder(0);
+ //System.out.println();
+ //System.out.println("后序:");
+ //arrBinaryTree.postOrder(0);
+ }
+}
+
+/**
+ * 顺序存储二叉树基本思想:
+ * 将二叉树顺序存储到数组中,并能按照二叉树的规则遍历(先序、中序、后序)
+ */
+class ArrBinaryTree {
+ private int[] arr;
+
+ public ArrBinaryTree(int[] arr) {
+ this.arr = arr;
+ }
+
+ // 先序遍历
+ public void preOrder(int index) {
+ if (arr == null || arr.length == 0) {
+ System.out.println("数组为空");
+ return;
+ }
+ System.out.print(arr[index] + " ");
+ if ((2 * index + 1) < arr.length) {
+ preOrder(2 * index + 1);
+ }
+ if ((2 * index + 2) < arr.length) {
+ preOrder(2 * index + 2);
+ }
+ }
+
+ // 中序遍历
+ public void infixOrder(int index) {
+ if (arr == null || arr.length == 0) {
+ System.out.println("数组为空");
+ return;
+ }
+ if ((2 * index + 1) < arr.length) {
+ infixOrder(2 * index + 1);
+ }
+ System.out.print(arr[index] + " ");
+ if ((2 * index + 2) < arr.length) {
+ infixOrder(2 * index + 2);
+ }
+ }
+
+ // 后序遍历
+ public void postOrder(int index) {
+ if (arr == null || arr.length == 0) {
+ System.out.println("数组为空");
+ return;
+ }
+ if ((2 * index + 1) < arr.length) {
+ postOrder(2 * index + 1);
+ }
+ if ((2 * index + 2) < arr.length) {
+ postOrder(2 * index + 2);
+ }
+ System.out.print(arr[index] + " ");
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/BinaryTreeDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/BinaryTreeDemo.java
new file mode 100644
index 00000000..50ff9a40
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/BinaryTreeDemo.java
@@ -0,0 +1,396 @@
+package cn.lastwhisper.atguigu.tree;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * 二叉树的遍历:先序、中序、后序
+ * 二叉树的查找:查找指定节点
+ *
+ * @author cn.lastwhisper
+ */
+public class BinaryTreeDemo {
+ // 创建节点
+ static HeroNode root = new HeroNode(1, "宋江");
+ static HeroNode heroNode2 = new HeroNode(2, "吴用");
+ static HeroNode heroNode3 = new HeroNode(3, "卢俊义");
+ static HeroNode heroNode4 = new HeroNode(4, "林冲");
+ static HeroNode heroNode5 = new HeroNode(5, "关胜");
+ static HeroNode heroNode6 = new HeroNode(6, "花荣");
+ // 手动创建二叉树
+ static BinaryTree binaryTree = new BinaryTree();
+
+ static {
+ root.setLeft(heroNode2);
+ root.setRight(heroNode3);
+ heroNode3.setRight(heroNode4);
+ heroNode3.setLeft(heroNode5);
+ heroNode4.setRight(heroNode6);
+ binaryTree.setRoot(root);
+ }
+
+ public static void main(String[] args) {
+
+ // 先序遍历
+ System.out.println("===============先序遍历================");
+ binaryTree.preOrder(root); // 1,2,3,5,4
+ // 中序遍历
+ System.out.println("===============中序遍历================");
+ binaryTree.infixOrder(root); // 2,1,5,3,4
+ // 后序遍历
+ System.out.println("===============后序遍历================");
+ binaryTree.postOrder(root); // 2,5,4,3,1
+ // 先序遍历查找
+ System.out.println("===============先序遍历查找================");
+ System.out.println(binaryTree.preOrderSearch(5));
+ System.out.println("===============中序遍历查找================");
+ System.out.println(binaryTree.infixOrderSearch(5));
+ System.out.println("===============后序遍历查找================");
+ System.out.println(binaryTree.postOrderSearch(5));
+ //System.out.println("===============清空子树下的no=3节点================");
+ //binaryTree.clear(3);
+ //binaryTree.preOrder(root);
+ System.out.println("===============二叉树的最大高度================");
+ System.out.println(binaryTree.height());
+ System.out.println("===============二叉树的节点数================");
+ System.out.println(binaryTree.size());
+ }
+
+ // 测试二叉树的层级遍历
+ @Test
+ public void testLayerTransverse() {
+ ArrayList> arrayLists = binaryTree.layerTransverse();
+ for (ArrayList arrayList : arrayLists) {
+ for (String s : arrayList) {
+ System.out.print(s + "\t");
+ }
+ System.out.println();
+ }
+ }
+}
+
+// 二叉树
+class BinaryTree {
+ private HeroNode root;
+
+
+ public HeroNode getRoot() {
+ return root;
+ }
+
+ public void setRoot(HeroNode root) {
+ this.root = root;
+ }
+
+ // 清空子树下的所有节点
+ public void clear(int no) {
+ if (root != null) {
+ if (root.getNo() == no) {
+ root = null;
+ } else {
+ root.clear(no);
+ }
+ } else {
+ System.out.println("空树无法删除");
+ }
+
+ }
+
+ //判断二叉树是否为空
+ public boolean isEmpty() {
+ return root == null;
+ }
+
+ /**
+ * 获取二叉树的高度
+ *
+ * @param
+ * @return int
+ */
+ public int height() {
+ return height(root);
+ }
+
+ /**
+ * 递归遍历直到叶子节点
+ *
+ * @param node
+ * @return int
+ */
+ public int height(HeroNode node) {
+ if (node == null) {
+ return 0;
+ } else {
+ //递归获取左子树高度
+ int l = height(node.getLeft());
+ //递归获取右子树高度
+ int r = height(node.getRight());
+ //高度应该算更高的一边,(+1是因为要算上自身这一层)
+ return l > r ? l + 1 : r + 1;
+ }
+ }
+
+ /**
+ * 获取二叉树的节点数
+ *
+ * @param
+ * @return int
+ */
+ public int size() {
+ return size(root);
+ }
+
+ public int size(HeroNode node) {
+ if (node == null) {
+ return 0;
+ } else {
+ return 1 + size(node.getLeft()) + size(node.getRight());
+ }
+
+ }
+ // 返回某节点的父节点
+
+
+ // 先序遍历查找
+ public HeroNode preOrderSearch(int no) {
+ if (root != null) {
+ return root.preOrderSearch(no);
+ } else {
+ return null;
+ }
+ }
+
+ // 中序遍历查找
+ public HeroNode infixOrderSearch(int no) {
+ if (root != null) {
+ return root.infixOrderSearch(no);
+ } else {
+ return null;
+ }
+ }
+
+ // 后序遍历查找
+ public HeroNode postOrderSearch(int no) {
+ if (root != null) {
+ return root.postOrderSearch(no);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 先序遍历(递归)
+ * 先访问根节点,再先序遍历左子树,再先序遍历右子树
+ *
+ * @param node
+ */
+ public void preOrder(HeroNode node) {
+ if (node != null) {
+ System.out.println(node);
+ preOrder(node.getLeft());
+ preOrder(node.getRight());
+ }
+ }
+
+ /**
+ * 中序遍历(递归)
+ * 先遍历左子树,再访问根节点,再中序遍历右子树
+ *
+ * @param node
+ */
+ public void infixOrder(HeroNode node) {
+ if (node != null) {
+ infixOrder(node.getLeft());
+ System.out.println(node);
+ infixOrder(node.getRight());
+ }
+ }
+
+ /**
+ * 后序遍历(递归)
+ * 先遍历左子树,再后序遍历右子树,再访问根节点
+ *
+ * @param node
+ */
+ public void postOrder(HeroNode node) {
+ if (node != null) {
+ postOrder(node.getLeft());
+ postOrder(node.getRight());
+ System.out.println(node);
+ }
+ }
+
+ public ArrayList> layerTransverse() {
+ ArrayList> tree = new ArrayList>();
+ if (this.root == null) return tree;
+ Queue queue = new LinkedList<>();
+ queue.add(root);
+ while (!queue.isEmpty()) {
+ ArrayList list = new ArrayList();
+ int size = queue.size();
+ for (int i = 0; i < size; i++) {
+ HeroNode node = queue.poll();
+ list.add(node.getName());
+ if (node.getLeft() != null) queue.add(node.getLeft());
+ if (node.getRight() != null) queue.add(node.getRight());
+ }
+ tree.add(list);
+ }
+ return tree;
+ }
+
+}
+
+// 树的节点
+class HeroNode {
+ private int no;
+ private String name;
+ private HeroNode left;
+ private HeroNode right;
+
+ public HeroNode(int no, String name) {
+ this.no = no;
+ this.name = name;
+ }
+
+ public int getNo() {
+ return no;
+ }
+
+ public void setNo(int no) {
+ this.no = no;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public HeroNode getLeft() {
+ return left;
+ }
+
+ public void setLeft(HeroNode left) {
+ this.left = left;
+ }
+
+ public HeroNode getRight() {
+ return right;
+ }
+
+ public void setRight(HeroNode right) {
+ this.right = right;
+ }
+
+ @Override
+ public String toString() {
+ return "HeroNode{" +
+ "no=" + no +
+ ", name='" + name + '\'' +
+ '}';
+ }
+
+ /**
+ * 先序遍历查找节点
+ *
+ * @param no
+ * @return cn.cn.lastwhisper.tree.HeroNode
+ */
+ public HeroNode preOrderSearch(int no) {
+ // 1)、判断当前节点是否为查找的节点
+ if (this.no == no) {
+ return this;
+ }
+ // 返回值
+ HeroNode result = null;
+ // 2)、递归左子树
+ if (this.left != null) {
+ result = this.left.preOrderSearch(no);
+ }
+ if (result != null) {
+ return result;
+ }
+ // 3)、递归右子树
+ if (this.right != null) {
+ result = this.right.preOrderSearch(no);
+ }
+ return result;
+ }
+
+ /**
+ * 中序遍历查找
+ *
+ * @param no
+ * @return cn.cn.lastwhisper.tree.HeroNode
+ */
+ public HeroNode infixOrderSearch(int no) {
+ HeroNode result = null;
+ // 1)、先递归左子树
+ if (this.left != null) {
+ result = this.left.infixOrderSearch(no);
+ }
+ if (result != null) {
+ return result;
+ }
+ // 2)、判断当前节点是否为查找的节点
+ if (this.no == no) {
+ return this;
+ }
+ // 3)、递归右子树
+ if (this.right != null) {
+ result = this.right.infixOrderSearch(no);
+ }
+ return result;
+ }
+
+ // 后序遍历查找
+ public HeroNode postOrderSearch(int no) {
+ // 1)、递归遍历右子树
+ HeroNode result = null;
+ if (this.right != null) {
+ result = this.right.postOrderSearch(no);
+ }
+ if (result != null) {
+ return result;
+ }
+ // 2)、判断该节点是否是寻找的节点
+ if (this.no == no) {
+ return this;
+ }
+ // 3、递归遍历左子树
+ if (this.left != null) {
+ result = this.left.postOrderSearch(no);
+ }
+ return result;
+ }
+
+ // 递归删除节点
+ public void clear(int no) {
+ // 1)、判断当前节点的左节点是否满足条件
+ if (this.left != null && this.left.no == no) {
+ this.left = null;
+ return;
+ }
+ // 2)、判断当前节点的右节点是否满足条件
+ if (this.right != null && this.right.no == no) {
+ this.right = null;
+ return;
+ }
+ // 3)、当前节点的左右节点不满足条件,递归左节点
+ if (this.left != null) {
+ this.left.clear(no);
+ }
+ // 4)、当前节点的左右节点不满足条件,递归右节点
+ if (this.right != null) {
+ this.right.clear(no);
+ }
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ThreadingBinaryTreeDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ThreadingBinaryTreeDemo.java
new file mode 100644
index 00000000..b087fc40
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/ThreadingBinaryTreeDemo.java
@@ -0,0 +1,254 @@
+package cn.lastwhisper.atguigu.tree;
+
+/**
+ * 线索化二叉树
+ * @author cn.lastwhisper
+ */
+public class ThreadingBinaryTreeDemo {
+ public static void main(String[] args) {
+ // 创建节点
+ ThreadingHeroNode root = new ThreadingHeroNode(1, "宋江");
+ ThreadingHeroNode heroNode2 = new ThreadingHeroNode(3, "吴用");
+ ThreadingHeroNode heroNode3 = new ThreadingHeroNode(6, "卢俊义");
+ ThreadingHeroNode heroNode4 = new ThreadingHeroNode(8, "林冲");
+ ThreadingHeroNode heroNode5 = new ThreadingHeroNode(10, "关胜");
+ ThreadingHeroNode heroNode6 = new ThreadingHeroNode(14, "花荣");
+
+ // 手动创建线索二叉树
+ ThreadingBinaryTree ThreadingBinaryTree = new ThreadingBinaryTree();
+ root.setLeft(heroNode2);
+ root.setRight(heroNode3);
+ heroNode2.setLeft(heroNode4);
+ heroNode2.setRight(heroNode5);
+ heroNode3.setLeft(heroNode6);
+ ThreadingBinaryTree.setRoot(root);
+ System.out.println("===============先序线索化二叉树================");
+ ThreadingBinaryTree.preOrderThreading();
+ //System.out.println("===============中序线索化二叉树================");
+ //ThreadingBinaryTree.infixOrderThreading();
+ System.out.println("===============前驱、后继节点================");
+ ThreadingHeroNode heroNode5Left = heroNode5.getLeft();// 3
+ ThreadingHeroNode heroNode5Right = heroNode5.getRight(); //
+ System.out.println("10号节点的前驱:" + heroNode5Left + "\n10号节点的后继:" + heroNode5Right);
+ System.out.println("===============先序遍历线索化二叉树================");
+ ThreadingBinaryTree.preOrder();
+ //System.out.println("===============中序遍历线索化二叉树================");
+ //ThreadingBinaryTree.infixOrder();
+ }
+}
+
+// 线索化二叉树
+class ThreadingBinaryTree {
+ private ThreadingHeroNode root;
+ private ThreadingHeroNode preNode;
+
+
+ public ThreadingHeroNode getRoot() {
+ return root;
+ }
+
+ public void setRoot(ThreadingHeroNode root) {
+ this.root = root;
+ }
+
+ // 构建先序线索化二叉树
+ public void preOrderThreading() {
+ this.preOrderThreading(root);
+ }
+
+ // 构建中序线索化二叉树
+ public void infixOrderThreading() {
+ this.infixOrderThreading(root);
+ }
+
+ /**
+ * 先序遍历先序线索化二叉树
+ *
+ */
+ public void preOrder() {
+ ThreadingHeroNode node = root;
+ while (node != null) {
+ System.out.println(node);
+ // 非线索化节点直接输出
+ while (node.getLeftType() == 0) {
+ node = node.getLeft();
+ System.out.println(node);
+ }
+ // 遍历带有线索化的节点,遍历结束时会输出一个非线索化节点
+ while (node.getRightType() == 1) {
+ node = node.getRight();
+ System.out.println(node);
+ }
+ // 记录非线索化节点右子树
+ node = node.getRight();
+ }
+ }
+
+ /**
+ * 中序遍历中序线索化二叉树
+ *
+ */
+ public void infixOrder() {
+ ThreadingHeroNode node = root;
+ while (node != null) {
+ // 找到type==1的线索化节点
+ while (node.getLeftType() == 0) {
+ node = node.getLeft();
+ }
+ System.out.println(node);
+ // 遍历带有线索化的节点,遍历结束时会输出一个非线索化节点
+ while (node.getRightType() == 1) {
+ node = node.getRight();
+ System.out.println(node);
+ }
+ // 记录非线索化节点右子树
+ node = node.getRight();
+ }
+ }
+
+ /**
+ * 构建先序线索化二叉树
+ *
+ * @param node 当前需要线索化的节点
+ */
+ public void preOrderThreading(ThreadingHeroNode node) {
+ // 数据校验
+ if (node == null) {
+ return;
+ }
+ // 处理当前节点的前驱节点
+ // 如果当前节点的左子树为空,说明出现了空指针域,需要构建线索前驱节点的线索
+ if (node.getLeft() == null) {
+ // 设置当前节点的前驱节点
+ node.setLeft(preNode);
+ node.setLeftType(1);
+ }
+ // 处理前一个节点的后继节点
+ // 如果前一个节点的左子树为空,说明出现了空指针域,需要构建线索后继节点的线索
+ if (preNode != null && preNode.getRight() == null) {
+ // 设置前一个节点的后继节点为当前节点
+ preNode.setRight(node);
+ preNode.setRightType(1);
+ }
+ // 每处理一个当前节点,就让当前节点是下一个节点的前驱节点
+ preNode = node;
+ // type==0表示link
+ if (node.getLeftType() == 0) {
+ // 线索化左子树
+ preOrderThreading(node.getLeft());
+ }
+ if (node.getRightType() == 0) {
+ // 线索化右子树
+ preOrderThreading(node.getRight());
+ }
+
+ }
+
+ /**
+ * 构建中序线索化二叉树
+ *
+ * @param node 当前需要线索化的节点
+ */
+ public void infixOrderThreading(ThreadingHeroNode node) {
+ // 数据校验
+ if (node == null) {
+ return;
+ }
+
+ // 1)、先线索化左子树
+ infixOrderThreading(node.getLeft());
+ // 2)、处理当前节点的前驱节点
+ // 如果当前节点的左子树为空,说明出现了空指针域,需要构建线索前驱节点的线索
+ if (node.getLeft() == null) {
+ // 设置当前节点的前驱节点
+ node.setLeft(preNode);
+ node.setLeftType(1);
+ }
+ // 3)、处理前一个节点的后继节点
+ // 如果前一个节点的左子树为空,说明出现了空指针域,需要构建线索后继节点的线索
+ if (preNode != null && preNode.getRight() == null) {
+ // 设置前一个节点的后继节点为当前节点
+ preNode.setRight(node);
+ preNode.setRightType(1);
+ }
+ // 每处理一个当前节点,就让当前节点是下一个节点的前驱节点
+ preNode = node;
+
+ // 4)、线索化右子树
+ infixOrderThreading(node.getRight());
+ }
+}
+
+// 树的节点
+class ThreadingHeroNode {
+ private int no;
+ private String name;
+ private ThreadingHeroNode left;
+ private ThreadingHeroNode right;
+
+ private int leftType; // leftType==0指向左子树,等于1指向前驱节点
+ private int rightType;// rightType==0指向右子树,等于1指向后继节点
+
+ public ThreadingHeroNode(int no, String name) {
+ this.no = no;
+ this.name = name;
+ }
+
+ public int getLeftType() {
+ return leftType;
+ }
+
+ public void setLeftType(int leftType) {
+ this.leftType = leftType;
+ }
+
+ public int getRightType() {
+ return rightType;
+ }
+
+ public void setRightType(int rightType) {
+ this.rightType = rightType;
+ }
+
+ public int getNo() {
+ return no;
+ }
+
+ public void setNo(int no) {
+ this.no = no;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ThreadingHeroNode getLeft() {
+ return left;
+ }
+
+ public void setLeft(ThreadingHeroNode left) {
+ this.left = left;
+ }
+
+ public ThreadingHeroNode getRight() {
+ return right;
+ }
+
+ public void setRight(ThreadingHeroNode right) {
+ this.right = right;
+ }
+
+ @Override
+ public String toString() {
+ return "HeroNode{" +
+ "no=" + no +
+ ", name='" + name + '\'' +
+ '}';
+ }
+
+
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/avl/AVLTreeDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/avl/AVLTreeDemo.java
new file mode 100644
index 00000000..f50d1106
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/avl/AVLTreeDemo.java
@@ -0,0 +1,378 @@
+package cn.lastwhisper.atguigu.tree.avl;
+
+/**
+ * 平衡二叉树
+ * @author cn.lastwhisper
+ */
+public class AVLTreeDemo {
+ public static void main(String[] args) {
+ // 测试左旋转
+ //int[] arr = {4, 3, 6, 5, 7, 8};
+ // 测试右旋转
+ //int[] arr = {10, 12, 8, 9, 7, 6};
+ // 测试双旋转
+ int[] arr = {10, 11, 7, 6, 8, 9};
+ AVLTree avlTree = new AVLTree();
+ for (int i = 0; i < arr.length; i++) {
+ avlTree.add(new Node(arr[i]));
+ }
+ System.out.println("中序遍历");
+ avlTree.infixOrder();
+ Node avlTreeRoot = avlTree.getRoot();
+ System.out.println("当前树的高度:" + avlTreeRoot.height());
+ System.out.println("当前树的左子树高度:" + avlTreeRoot.left.height());
+ System.out.println("当前树的右子树高度:" + avlTreeRoot.right.height());
+ System.out.println("当前根节点:" + avlTreeRoot);
+ }
+}
+
+/**
+ * 二叉排序树
+ */
+class AVLTree {
+ // 根节点
+ private Node root;
+
+ public Node getRoot() {
+ return root;
+ }
+
+ public void setRoot(Node root) {
+ this.root = root;
+ }
+
+ // 查找节点
+ public Node search(int value) {
+ if (root != null) {
+ return root.search(value);
+ } else {
+ return null;
+ }
+ }
+
+ // 查找父节点
+ public Node searchParent(int value) {
+ if (root != null) {
+ return root.searchParent(value);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 找到该节点左子树最小值,并删除这个最小值节点
+ *
+ * @param node
+ * @return int 最小值节点
+ */
+ public int delRightTreeMin(Node node) {
+ Node temp = node;
+ // 找到右子节点的最小值节点
+ while (temp.left != null) {
+ temp = temp.left;
+ }
+ delNode(temp.value);
+ return temp.value;
+ }
+
+ /**
+ * 找到该节点左子树最大值,并删除这个最大值节点
+ *
+ * @param node
+ * @return int 最大值
+ */
+ public int delLeftTreeMax(Node node) {
+ Node temp = node;
+ while (temp.right != null) {
+ temp = temp.right;
+ }
+ delNode(temp.value);
+ return temp.value;
+ }
+
+
+ // 根据value删除节点
+ public void delNode(int value) {
+ if (root == null) {
+ return;
+ } else {
+ // 待删除节点
+ Node targetNode = search(value);
+ // 没有找到待删除节点
+ if (targetNode == null) {
+ return;
+ }
+ // 待删除节点的父节点
+ Node parentNode = searchParent(value);
+ // 1. 情况一:只有一个根节点
+ if (root.left == null && root.right == null) {
+ root = null;
+ return;
+ }
+ // 2. 情况二:删除的节点是叶子节点(左右节点为空)
+ if (targetNode.left == null && targetNode.right == null) {
+ // 2.1 确定targetNode是parent的左子节点还是右子节点;
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // 左子节点
+ parentNode.left = null;
+ } else if (parentNode.right != null && parentNode.right.value == targetNode.value) {
+ // 右子节点
+ parentNode.right = null;
+ }
+ } else if (targetNode.left != null && targetNode.right != null) {
+ // 3. 情况四:删除带有两个子节点的targetNode
+ // 找到targetNode右子树的最小值,删除最小值节点,并将最小值赋值给targetNode
+ //targetNode.value = delRightTreeMin(targetNode.right);
+ targetNode.value = delLeftTreeMax(targetNode.left);
+ } else {
+ // 4. 情况三:删除只有一个子节点的targetNode
+ // 4.1 确定targetNode的子节点是左子节点还是右子节点
+ // targetNode的子节点是左子节点
+ if (targetNode.left != null) {
+ if (parentNode != null) {
+ // 4.2 确定targetNode在parentNode上是左子节点还是右子节点
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // targetNode在parentNode上是左子节点
+ parentNode.left = targetNode.left;
+ } else {
+ // targetNode在parentNode上是右子节点
+ parentNode.right = targetNode.left;
+ }
+ } else {
+ // targetNode为根节点时,parentNode==null
+ // root = targetNode.left;即为删除target节点
+ root = targetNode.left;
+ }
+ } else {
+ if (parentNode != null) {
+ //targetNode的子节点是右子节点
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // targetNode在parentNode上是左子节点
+ parentNode.left = targetNode.right;
+ } else {
+ // targetNode在parentNode上是右子节点
+ parentNode.right = targetNode.right;
+ }
+ } else {
+ root = targetNode.right;
+ }
+ }
+
+ }
+
+
+ }
+
+ }
+
+ // 中序
+ public void infixOrder() {
+ if (root == null) {
+ System.out.println("树空");
+ } else {
+ root.infixOrder();
+ }
+
+ }
+
+ // 添加节点
+ public void add(Node node) {
+ if (root == null) {
+ root = node;
+ } else {
+ root.add(node);
+ }
+ }
+
+}
+
+
+class Node {
+ int value;
+ Node left;
+ Node right;
+
+ public Node(int value) {
+ this.value = value;
+ }
+
+ /**
+ * 以当前某节点进行右旋转,降低左子树的高度
+ */
+ public void rightRotate() {
+ // 1. 创建一个新节点,值等于当前节点的值
+ Node newNode = new Node(this.value);
+ // 2. 将新节点的右子树设置为当前节点的右子树
+ newNode.right = this.right;
+ // 3. 将新节点的左子树设置为当前节点的左子树的右子树(当前节点左子树最大的节点)
+ newNode.left = this.left.right;
+ // 4. 将当前节点的值设置为当前节点的左子树(节点)的值
+ this.value = this.left.value;
+ // 5. 将当前节点的左子树设置为当前节点左子树的左子树
+ this.left = this.left.left;
+ // 6. 将当期节点的右子树设置为新节点
+ this.right = newNode;
+ }
+
+ /**
+ * 以当前某节点进行左旋转,降低右子树的高度
+ */
+ public void leftRotate() {
+ // 1. 创建一个新节点,值等于当前节点的值
+ Node newNode = new Node(this.value);
+ // 2. 将新节点的左子树设置为当前节点的左子树
+ newNode.left = this.left;
+ // 3. 将新节点的右子树设置为当前节点右子树的左子树
+ newNode.right = this.right.left;
+ // 4. 将当前节点的值设置为当前节点右子树的值
+ this.value = this.right.value;
+ // 5. 将当前节点的右子树设置为当前节点右子树的右子树
+ this.right = this.right.right;
+ // 6. 将当前节点左子树设置为新节点
+ this.left = newNode;
+ }
+
+ /**
+ * @return int 当前某节点左子树的高度
+ */
+ public int leftHeight() {
+ if (this.left != null) {
+ return this.left.height();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return int 当前某节点右子树的高度
+ */
+ public int rightHeight() {
+ if (this.right != null) {
+ return this.right.height();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * @return int 当前某节点的高度
+ */
+ public int height() {
+ int leftHeight = this.left == null ? 0 : this.left.height();
+ int rightHeight = this.right == null ? 0 : this.right.height();
+ return Math.max(leftHeight, rightHeight) + 1;
+ }
+
+ /**
+ * 查找节点
+ *
+ * @param value 节点的value
+ * @return cn.cn.lastwhisper.tree.binarysorttree.Node
+ */
+ public Node search(int value) {
+ // 1. 判断当前节点是否是查找的节点
+ if (this.value == value) {
+ return this;
+ } else if (this.value > value && this.left != null) {
+ // 2.1 当前节点的value大于value,往左边寻找
+ return this.left.search(value);
+ } else if (this.value < value && this.right != null) {
+ // 2.2 当前节点的value小于value,往右边寻找
+ return this.right.search(value);
+ } else {
+ // 3. this.left&&this.right==null,找不到该节点
+ return null;
+ }
+ }
+
+ // 查找节点的父节点
+ public Node searchParent(int value) {
+ // 1. 判断当前节点是否是查找节点的父节点
+ if ((this.left != null && this.left.value == value) ||
+ (this.right != null && this.right.value == value)) {
+ return this;
+ } else {
+ if (this.value > value && this.left != null) {
+ // 2.1 当前节点的value大于value,往左边寻找
+ return this.left.searchParent(value);
+ } else if (this.value < value && this.right != null) {
+ // 2.2 当前节点的value小于value,往右边寻找
+ return this.right.searchParent(value);
+ } else {
+ // 3. this.left&&this.right==null,找不到该节点的父节点
+ return null;
+ }
+ }
+ }
+
+ // 中序
+ public void infixOrder() {
+ if (this.left != null) {
+ this.left.infixOrder();
+ }
+ System.out.println(this);
+ if (this.right != null) {
+ this.right.infixOrder();
+ }
+ }
+
+ /**
+ * 以自身为根节点按规则添加左右子树
+ * 规则:对于非叶子节点p,左节点小于p,右节点大于p
+ * 满足AVL规则!!!
+ * @param node
+ * @return void
+ */
+ public void add(Node node) {
+ // 1. 如果待添加节点为空直接返回
+ if (node == null) {
+ return;
+ }
+ if (this.value > node.value) {
+ // 2.1 如果根节点大于待添加节点,将待添加节点放到左边
+ if (this.left == null) {
+ this.left = node;
+ } else {
+ this.left.add(node);
+ }
+ } else {
+ // 2.2 如果根节点小于待添加节点,将待添加节点放到右边
+ if (this.right == null) {
+ this.right = node;
+ } else {
+ this.right.add(node);
+ }
+ }
+
+ // 当添加完节点后,满足(右子树的高度-左子树的高度) > 1 ,进行左旋转
+ if (this.rightHeight() - this.leftHeight() > 1) {
+ // 如果当前节点的右子树的左子树的高度大于当前节点的右子树的右子树的高度
+ if(this.right!=null&&(this.right.leftHeight()>this.right.rightHeight())){
+ // 先当前节点的右子树进行右旋转,再左旋转
+ this.right.rightRotate();
+ }
+ leftRotate();
+ return;
+ }
+
+ // 当添加完节点后,满足(左子树的高度-右子树的高度) > 1 ,进行右旋转
+ if (this.leftHeight() - this.rightHeight() > 1) {
+ // 如果当前节点的左子树的右子树的高度大于当前节点的左子树的左子树的高度
+ if (this.left != null && (this.left.rightHeight() > this.left.leftHeight())) {
+ // 先对当前节点的左子树进行左旋转,再右旋转
+ this.left.leftRotate();
+ }
+ rightRotate();
+ }
+
+ }
+
+
+ @Override
+ public String toString() {
+ return "Node{" +
+ "value=" + value +
+ '}';
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/binarysorttree/BinarySortTreeDemo.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/binarysorttree/BinarySortTreeDemo.java
new file mode 100644
index 00000000..d582a014
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/binarysorttree/BinarySortTreeDemo.java
@@ -0,0 +1,298 @@
+package cn.lastwhisper.atguigu.tree.binarysorttree;
+
+/**
+ * 二叉排序树
+ * @author cn.lastwhisper
+ */
+public class BinarySortTreeDemo {
+ public static void main(String[] args) {
+ int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
+ BinarySortTree binarySortTree = new BinarySortTree();
+ for (int i = 0; i < arr.length; i++) {
+ binarySortTree.add(new Node(arr[i]));
+ }
+ System.out.println("中序遍历");
+ binarySortTree.infixOrder();
+
+ //binarySortTree.delNode(2); // 删除叶子节点
+ //binarySortTree.delNode(1); // 删除只有一个子节点
+ //binarySortTree.delNode(7); // 删除有两个子节点
+
+ binarySortTree.delNode(2);
+ binarySortTree.delNode(5);
+ binarySortTree.delNode(9);
+ binarySortTree.delNode(12);
+ binarySortTree.delNode(7);
+ binarySortTree.delNode(3);
+ binarySortTree.delNode(1);
+ binarySortTree.delNode(10);
+ // 会报错
+ //binarySortTree.delNode(10);
+ //binarySortTree.delNode(1);
+
+ System.out.println("删除节点后,中序遍历");
+ binarySortTree.infixOrder();
+ }
+
+}
+
+/**
+ * 二叉排序树
+ */
+class BinarySortTree {
+ // 根节点
+ private Node root;
+
+ public Node getRoot() {
+ return root;
+ }
+
+ public void setRoot(Node root) {
+ this.root = root;
+ }
+
+ // 查找节点
+ public Node search(int value) {
+ if (root != null) {
+ return root.search(value);
+ } else {
+ return null;
+ }
+ }
+
+ // 查找父节点
+ public Node searchParent(int value) {
+ if (root != null) {
+ return root.searchParent(value);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * 找到该节点左子树最小值,并删除这个最小值节点
+ *
+ * @param node
+ * @return int 最小值节点
+ */
+ public int delRightTreeMin(Node node) {
+ Node temp = node;
+ // 找到右子节点的最小值节点
+ while (temp.left != null) {
+ temp = temp.left;
+ }
+ delNode(temp.value);
+ return temp.value;
+ }
+
+ /**
+ * 找到该节点左子树最大值,并删除这个最大值节点
+ *
+ * @param node
+ * @return int 最大值
+ */
+ public int delLeftTreeMax(Node node) {
+ Node temp = node;
+ while (temp.right != null) {
+ temp = temp.right;
+ }
+ delNode(temp.value);
+ return temp.value;
+ }
+
+
+ // 根据value删除节点
+ public void delNode(int value) {
+ if (root == null) {
+ } else {
+ // 待删除节点
+ Node targetNode = search(value);
+ // 没有找到待删除节点
+ if (targetNode == null) {
+ return;
+ }
+ // 待删除节点的父节点
+ Node parentNode = searchParent(value);
+ // 1. 情况一:只有一个根节点
+ if (root.left == null && root.right == null) {
+ root = null;
+ return;
+ }
+ // 2. 情况二:删除的节点是叶子节点(左右节点为空)
+ if (targetNode.left == null && targetNode.right == null) {
+ // 2.1 确定targetNode是parent的左子节点还是右子节点;
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // 左子节点
+ parentNode.left = null;
+ } else if (parentNode.right != null && parentNode.right.value == targetNode.value) {
+ // 右子节点
+ parentNode.right = null;
+ }
+ } else if (targetNode.left != null && targetNode.right != null) {
+ // 3. 情况四:删除带有两个子节点的targetNode
+ // 找到targetNode右子树的最小值,删除最小值节点,并将最小值赋值给targetNode
+ //targetNode.value = delRightTreeMin(targetNode.right);
+ targetNode.value = delLeftTreeMax(targetNode.left);
+ } else {
+ // 4. 情况三:待删除节点只有一个子节点
+ // 4.1 确定targetNode的子节点是左子节点还是右子节点
+ // targetNode的子节点是左子节点
+ if (targetNode.left != null) {
+ if (parentNode != null) {
+ // 4.2 确定targetNode在parentNode上是左子节点还是右子节点
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // targetNode在parentNode上是左子节点
+ parentNode.left = targetNode.left;
+ } else {
+ // targetNode在parentNode上是右子节点
+ parentNode.right = targetNode.left;
+ }
+ } else {
+ // targetNode为根节点时,parentNode==null
+ // root = targetNode.left;即为删除target节点
+ root = targetNode.left;
+ }
+ } else {
+ if (parentNode != null) {
+ //targetNode的子节点是右子节点
+ if (parentNode.left != null && parentNode.left.value == targetNode.value) {
+ // targetNode在parentNode上是左子节点
+ parentNode.left = targetNode.right;
+ } else {
+ // targetNode在parentNode上是右子节点
+ parentNode.right = targetNode.right;
+ }
+ } else {
+ root = targetNode.right;
+ }
+ }
+
+ }
+
+
+ }
+
+ }
+
+ // 中序
+ public void infixOrder() {
+ if (root == null) {
+ System.out.println("树空");
+ } else {
+ root.infixOrder();
+ }
+
+ }
+
+ // 添加节点
+ public void add(Node node) {
+ if (root == null) {
+ root = node;
+ } else {
+ root.add(node);
+ }
+ }
+
+}
+
+
+class Node {
+ int value;
+ Node left;
+ Node right;
+
+ public Node(int value) {
+ this.value = value;
+ }
+
+ /**
+ * 查找节点
+ *
+ * @param value 节点的value
+ * @return cn.cn.lastwhisper.tree.binarysorttree.Node
+ */
+ public Node search(int value) {
+ // 1. 判断当前节点是否是查找的节点
+ if (this.value == value) {
+ return this;
+ } else if (this.value > value && this.left != null) {
+ // 2.1 当前节点的value大于value,往左边寻找,因为左子树的值都小于当前节点
+ return this.left.search(value);
+ } else if (this.value < value && this.right != null) {
+ // 2.2 当前节点的value小于value,往右边寻找,因为右子树的值都大于当前节点
+ return this.right.search(value);
+ } else {
+ // 3. this.left&&this.right==null,找不到该节点
+ return null;
+ }
+ }
+
+ // 查找节点的父节点
+ public Node searchParent(int value) {
+ // 1. 判断当前节点是否是查找节点的父节点
+ if ((this.left != null && this.left.value == value) ||
+ (this.right != null && this.right.value == value)) {
+ return this;
+ } else {
+ if (this.value > value && this.left != null) {
+ // 2.1 当前节点的value大于value,往左边寻找
+ return this.left.searchParent(value);
+ } else if (this.value < value && this.right != null) {
+ // 2.2 当前节点的value小于value,往右边寻找
+ return this.right.searchParent(value);
+ } else {
+ // 3. this.left&&this.right==null,找不到该节点的父节点
+ return null;
+ }
+ }
+ }
+
+ // 中序
+ public void infixOrder() {
+ if (this.left != null) {
+ this.left.infixOrder();
+ }
+ System.out.println(this);
+ if (this.right != null) {
+ this.right.infixOrder();
+ }
+ }
+
+ /**
+ * 以自身为根节点按规则添加左右子树
+ * 规则:对于非叶子节点p,左节点小于p,右节点大于p
+ *
+ * @param node
+ * @return void
+ */
+ public void add(Node node) {
+ // 1. 如果待添加节点为空直接返回
+ if (node == null) {
+ return;
+ }
+ if (this.value > node.value) {
+ // 2.1 如果根节点大于待添加节点,将待添加节点放到左边
+ if (this.left == null) {
+ this.left = node;
+ } else {
+ this.left.add(node);
+ }
+ } else {
+ // 2.2 如果根节点小于待添加节点,将待添加节点放到右边
+ if (this.right == null) {
+ this.right = node;
+ } else {
+ this.right.add(node);
+ }
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return "Node{" +
+ "value=" + value +
+ '}';
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmancode/HuffmanCode.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmancode/HuffmanCode.java
new file mode 100644
index 00000000..11ba50fc
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmancode/HuffmanCode.java
@@ -0,0 +1,508 @@
+package cn.lastwhisper.atguigu.tree.huffmancode;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * 哈夫曼编码
+ * @author cn.lastwhisper
+ */
+public class HuffmanCode {
+
+ // 构建哈夫曼编码表的值,左边为0,右边为1,即a:110的110
+ static StringBuffer sb = new StringBuffer();
+ // 哈夫曼编码表,key为字符,value为编码的值。如:a:110,key=a,value=110
+ static Map huffmanCodes = new HashMap<>();
+
+ public static void main(String[] args) {
+
+ /*--------------------------------压缩与解压的实际应用------------------------------------*/
+
+ //测试压缩文件
+ String srcFile = "d://src.bmp";
+ String dstFile = "d://src.bmp.zip";
+ zipFile(srcFile, dstFile);
+ System.out.println("压缩文件ok~~");
+
+
+ //测试解压文件
+ //String zipFile = "d://src.bmp.zip";
+ //String dstFile = "d://src2.bmp";
+ //unZipFile(zipFile, dstFile);
+ //System.out.println("解压成功!");
+
+ /*--------------------------------压缩与解压的实现步骤------------------------------------*/
+
+ //// String在jdk11时byte value[],jdk11以下char value[]
+ //String content = "i like like like java do you like a java";//压缩前 40个char
+ //byte[] compressBytes = huffmanZip(content); //压缩后 17个byte
+ // 哈夫曼编码压缩后的字节数组:[-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
+ //System.out.println("哈夫曼编码压缩后的字节数组:" + Arrays.toString(compressBytes));
+
+ //String bitString = byteToBitString(true, (byte) 1);
+ //System.out.println("夫曼编码压缩后的字节转比特:"+bitString);
+
+ //byte[] unZipBytes = unzip(huffmanCodes, compressBytes);
+ // 哈夫曼编码解压缩后的字节数组:i like like like java do you like a java
+ //System.out.println("哈夫曼编码解压缩后的字节数组:" + new String(unZipBytes));
+
+ /*--------------------------------压缩的实现步骤------------------------------------*/
+
+ //System.out.println("==========测试:待压缩字符串——》二叉树节点========");
+ //// Node{data=32, weight=9}, Node{data=97, weight=5}, Node{data=100, weight=1}, Node{data=101, weight=4}, Node{data=117, weight=1}, Node{data=118, weight=2},
+ //// Node{data=105, weight=5}, Node{data=121, weight=1}, Node{data=106, weight=2}, Node{data=107, weight=4}, Node{data=108, weight=4}, Node{data=111, weight=2}
+ //List listNode = getListNode(content);
+ ////System.out.println(listNode);
+ //System.out.println("==========测试:二叉树节点——》哈夫曼树========");
+ //Node root = createHuffmanTree(listNode);
+ //// 40,17,8,4,4...
+ ////preOrder(root);
+ //System.out.println("==========测试:哈夫曼树——》哈夫曼编码表========");
+ //// a:100 k: 1110
+ //Map huffmanCodes = getCodes(root);
+ //// 32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011
+ ////System.out.println("哈夫曼编码表:" + huffmanCodes);
+ //byte[] huffmanCodeBytes = zip(content, huffmanCodes);
+ //System.out.println("==========测试:哈夫曼编码表——》哈夫曼编码========");
+ //// [-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
+ //System.out.println("哈夫曼编码:" + Arrays.toString(huffmanCodeBytes));
+
+ }
+
+ /**
+ * 对压缩文件的解压
+ * @param zipFile 准备解压的文件
+ * @param dstFile 将文件解压到哪个路径
+ */
+ public static void unZipFile(String zipFile, String dstFile) {
+
+ //定义文件输入流
+ InputStream is = null;
+ //定义一个对象输入流
+ ObjectInputStream ois = null;
+ //定义文件的输出流
+ OutputStream os = null;
+ try {
+ //创建文件输入流
+ is = new FileInputStream(zipFile);
+ //创建一个和 is关联的对象输入流
+ ois = new ObjectInputStream(is);
+ //读取byte数组 huffmanBytes
+ byte[] huffmanBytes = (byte[]) ois.readObject();
+ //读取赫夫曼编码表
+ Map huffmanCodes = (Map) ois.readObject();
+ //解码
+ byte[] bytes = unzip(huffmanCodes, huffmanBytes);
+ //将bytes 数组写入到目标文件
+ os = new FileOutputStream(dstFile);
+ //写数据到 dstFile 文件
+ os.write(bytes);
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ } finally {
+
+ try {
+ if (os != null) os.close();
+ if (ois != null) ois.close();
+ if (is != null) is.close();
+ } catch (Exception e2) {
+ System.out.println(e2.getMessage());
+ }
+
+ }
+ }
+
+ //编写方法,将一个文件进行压缩
+
+ /**
+ *
+ * @param srcFile 你传入的希望压缩的文件的全路径
+ * @param dstFile 我们压缩后将压缩文件放到哪个目录
+ */
+ public static void zipFile(String srcFile, String dstFile) {
+
+ //创建输出流
+ OutputStream os = null;
+ ObjectOutputStream oos = null;
+ //创建文件的输入流
+ FileInputStream is = null;
+ try {
+ //创建文件的输入流
+ is = new FileInputStream(srcFile);
+ //创建一个和源文件大小一样的byte[]
+ byte[] b = new byte[is.available()];
+ //读取文件
+ is.read(b);
+ //直接对源文件压缩
+ byte[] huffmanBytes = huffmanZip(b);
+ //创建文件的输出流, 存放压缩文件
+ os = new FileOutputStream(dstFile);
+ //创建一个和文件输出流关联的ObjectOutputStream
+ oos = new ObjectOutputStream(os);
+ //把 赫夫曼编码后的字节数组写入压缩文件
+ oos.writeObject(huffmanBytes); //我们是把
+ //这里我们以对象流的方式写入 赫夫曼编码,是为了以后我们恢复源文件时使用
+ //注意一定要把赫夫曼编码 写入压缩文件
+ oos.writeObject(huffmanCodes);
+
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ } finally {
+ try {
+ if (is != null) is.close();
+ if (oos != null) oos.close();
+ if (os != null) os.close();
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+ }
+
+ }
+
+ /**
+ *
+ *
+ * @param huffmanCodes 哈夫曼编码表
+ * @param huffmanZipBytes 哈夫曼编码压缩后的字节数组
+ * @return byte[]
+ */
+ public static byte[] unzip(Map huffmanCodes, byte[] huffmanZipBytes) {
+ // 1)、将哈夫曼编码压缩后的字节数组还原为哈夫曼编码
+ StringBuilder sbHuffmanCodeBytes = new StringBuilder();
+ for (int i = 0; i < huffmanZipBytes.length; i++) {
+ // 是否补位、截取;除了最后一个字节都为false
+ boolean flag = (huffmanZipBytes.length - 1) == i;
+ sbHuffmanCodeBytes.append(byteToBitString(!flag, huffmanZipBytes[i]));
+ }
+ // 霍夫曼编码压缩后的字节数组对应的霍夫曼编码:1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
+ //System.out.println("被压缩的哈夫曼编码=" + sbHuffmanCodeBytes.toString());
+ // 2)、将哈夫曼编码表进行反转调换,调转后:100:97
+ Map reverseMap = new HashMap<>();
+ for (Map.Entry entry : huffmanCodes.entrySet()) {
+ reverseMap.put(entry.getValue(), entry.getKey());
+ }
+ // 原map:{32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}
+ // 反转map:{000=108, 01=32, 100=97, 101=105, 11010=121, 0011=111, 1111=107, 11001=117, 1110=101, 11000=100, 11011=118, 0010=106}
+ //System.out.println(reverseMap);
+ // 3)、遍历哈夫曼编码sbHuffmanCodeBytes,遍历到的每一个字符串作为key;
+ // 将key放到反转哈夫曼编码表中查询取出对应有效的value,添加到还原的字节组数中,
+ // 同时下次遍历哈夫曼编码sbHuffmanCodeBytes时从上次遍历到的主串之后开始遍历。
+ // 示例:如子串key=000,在主串10101000中匹配到了000,说明key对应的value=108,ascii 108对应字符'L',将108添加到还原的字节数组中,
+ // 同时下次遍历哈夫曼编码sbHuffmanCodeBytes时下标从10101000之后开始遍历。
+ // 还原的字节数组,这里使用list代替
+ List list = new ArrayList<>();
+ for (int i = 0; i < sbHuffmanCodeBytes.length(); ) {
+ // i与index配合遍历一个字符串
+ int index = 1;
+ // 找到原字节
+ boolean flag = true;
+ // 原字节
+ Byte b = null;
+ // (i + index) <= sbHuffmanCodeBytes.length()防止截取字符串时越界
+ while (flag && (i + index) <= sbHuffmanCodeBytes.length()) {
+ // 第一次for循环i=0,截取字符串[0,1],[0,2]...[0,length-1]
+ // 第二次for循环i=1,截取字符串[1,2],[1,3]...[1,length-1]
+ // 寻找编码对应的原字节
+ String key = sbHuffmanCodeBytes.substring(i, i + index);
+ b = reverseMap.get(key);
+ if (b == null) {
+ index++;
+ } else {
+ flag = false;
+ }
+ }
+ list.add(b);
+ // 在(i, i + index)匹配到编码后,不用i++匹配(i+1,i+index)的主串,
+ // 因为哈夫曼编码是最优前缀编码,所以在(i+1,i+index)的编码不会有重复,直接匹配(i+index,length-1)的主串即可。
+ i += index;
+ }
+ //当for循环结束后,我们list中就存放了所有的字符 "i like like like java do you like a java"
+ //把list 中的数据放入到byte[] 并返回
+ byte[] b = new byte[list.size()];
+ for (int i = 0; i < b.length; i++) {
+ b[i] = list.get(i);
+ }
+ return b;
+ }
+
+ /**
+ * [-88, -65, -56, -65, -56, -65, -55, 77, -57, 6, -24, -14, -117, -4, -60, -90, 28]
+ * 将byte转为bit
+ * 情况一:负数,-1
+ * Integer.toBinaryString(-1)返回的二进制补码:11111111111111111111111111111111
+ * 负数的补码,太长需要截取
+ * 正确:1111 1111
+ * 情况二:正数:1
+ * Integer.toBinaryString(-1)返回的二进制补码:1
+ * 正数的补码就是自己,补码太短,需要补高位之后再截取
+ * 正确:0000 0001
+ * 情况三:最后一个字节不需要补位,28
+ * 因为前面将哈夫曼编码转为byte[]时(zip方法)最后不足八位的字符串直接当做一个字节数组了,
+ * 所以再将byte转为bit时,最后一个字节不需要补位!!!
+ * 正确:11100
+ *
+ * @param flag 是否需要补位,true表示需要补位,false表示不需要补位
+ * @param b 需要转换的byte
+ * @return java.lang.String byte转换后的bitString(补码)
+ */
+ public static String byteToBitString(boolean flag, byte b) {
+ // 将byte转为int
+ int temp = b;
+ // 如果是正数需要补高位
+ if (flag) {
+ temp |= 256; // 按位与:256的二进制1 0000 0000 | 1 = 1 0000 0001
+ }
+ String bitString = Integer.toBinaryString(temp); // 返回的是二进制补码
+ if (flag) {
+ // int负数的补码长度为64,截取[1,8],其中8为byte的长度
+ // 正数补高位后长度为9,截取[1,8],其中8为byte的长度
+ return bitString.substring(bitString.length() - 8);
+ } else {
+ return bitString;
+ }
+ }
+
+
+ /**
+ * 核心方法,以哈夫曼编码的方式压缩字节数组
+ *
+ * @param content 原字符串
+ * @return byte[] 压缩后的字节数组
+ */
+ public static byte[] huffmanZip(String content) {
+ byte[] bytes = content.getBytes();
+ return huffmanZip(bytes);
+ }
+
+ /**
+ * 核心方法,以哈夫曼编码的方式压缩字节数组
+ *
+ * @param bytes 原字节数组
+ * @return byte[] 压缩后的字节数组
+ */
+ public static byte[] huffmanZip(byte[] bytes) {
+ // 1)、将byte[]转为二叉树的节点
+ List listNode = getListNode(bytes);
+ // 2)、将二叉树的节点的节点转为哈夫曼树
+ Node huffmanTreeRoot = createHuffmanTree(listNode);
+ // 3)、根据哈夫曼树,找出哈夫曼编码表
+ Map huffmanCodes = getCodes(huffmanTreeRoot);
+ // 4)、将哈夫曼编码表转为哈夫曼编码字符串
+ // 再根据哈夫曼编码字符串,按每8位一个字节转为字节数组,该字节数组就是压缩后的数据。
+ return zip(bytes, huffmanCodes);
+ }
+
+ /**
+ *
+ *
+ * @param content 待压缩字符串
+ * @param huffmanCodes 哈夫曼编码表
+ * @return byte[] 压缩后的字节数组
+ */
+ public static byte[] zip(String content, Map huffmanCodes) {
+ byte[] bytes = content.getBytes();
+ return zip(bytes, huffmanCodes);
+ }
+
+ /**
+ * // 待压缩字符串——》 "i like like like java do you like a java"
+ * // 待压缩字节数组——》 'i',' ','l',......
+ * 哈夫曼编码表——》 {32=01, 97=100, 100=11000, 117=11001, 101=1110, 118=11011, 105=101, 121=11010, 106=0010, 107=1111, 108=000, 111=0011}
+ * 哈夫曼编码字符串(二进制字符串)——》1010100010111111110010001011111111001000101111111100100101001101110001110000011011101000111100101000101111111100110001001010011011100
+ * 哈夫曼编码byte[](8位一组对应一个byte)huffmanCodeBytes
+ *
+ * Integer.parseInt("10101000", 2)是按补码来看待这个字符串的
+ * huffmanCodeBytes[0]=10101000(补码)——》10101000-1=10100111(反码)——》11011000(原码)=-88
+ *
+ * @param bytes 待压缩字符串
+ * @param huffmanCodes 哈夫曼编码表
+ * @return byte[] 压缩后的字节数组
+ */
+ public static byte[] zip(byte[] bytes, Map huffmanCodes) {
+ // 数据校验
+ if (bytes == null || huffmanCodes == null) {
+ return null;
+ }
+ // 1)、将哈夫曼编码表转为哈夫曼编码字符串
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(huffmanCodes.get(b));
+ }
+ // 2)、将哈夫曼编码字符串转为哈夫曼编码byte[]
+ // 先求出哈夫曼编码byte[]的长度
+ int len;
+ if (sb.length() % 8 == 0) {
+ len = sb.length() / 8;
+ } else {
+ len = sb.length() / 8 + 1;
+ }
+ // 创建哈夫曼编码byte[]
+ byte[] huffmanCodeBytes = new byte[len];
+ // huffmanCodeBytes的下标
+ int index = 0;
+ for (int i = 0; i < sb.length(); i += 8) {
+ String huffmanCodeStr;
+ if (i + 8 > sb.length()) {
+ // 哈夫曼编码字符串剩余不足8位,直接当做一个字节
+ huffmanCodeStr = sb.substring(i);
+ } else {
+ huffmanCodeStr = sb.substring(i, i + 8);
+ }
+ huffmanCodeBytes[index] = (byte) Integer.parseInt(huffmanCodeStr, 2);
+ index++;
+ }
+ return huffmanCodeBytes;
+ }
+
+ /**
+ * 根据哈夫曼树建立哈弗曼编码表
+ *
+ * @param root 根节点
+ * @return java.array.Map
+ */
+ public static Map getCodes(Node root) {
+ if (root == null) {
+ return null;
+ }
+ getCodes(root.left, "0", sb, huffmanCodes);
+ getCodes(root.right, "1", sb, huffmanCodes);
+ return huffmanCodes;
+ }
+
+ /**
+ * 根据哈夫曼树建立哈弗曼编码表(递归)
+ * @param node 以node节点为root开始建立哈夫曼编码表
+ * @param code 左边为0,右边为1
+ * @param stringBuffer 拼接编码的值
+ * @param huffmanCodes 存储哈夫曼编码表
+ */
+ public static void getCodes(Node node, String code, StringBuffer stringBuffer, Map huffmanCodes) {
+ // 拼接编码的值,如 a:1——a:11——a:110
+ StringBuffer sb = new StringBuffer(stringBuffer);
+ sb.append(code);
+ // 非叶子节点,哈夫曼树只有叶子节点有data
+ if (node.data == null) {
+ // 向左递归
+ getCodes(node.left, "0", sb, huffmanCodes);
+ // 向右递归
+ getCodes(node.right, "1", sb, huffmanCodes);
+ } else {
+ // 叶子节点,
+ huffmanCodes.put(node.data, sb.toString());
+ }
+ }
+
+ // 先序遍历
+ public static void preOrder(Node root) {
+ if (root != null) {
+ root.preOrder();
+ } else {
+ System.out.println("空树!");
+ }
+ }
+
+ /**
+ * 创建哈夫曼树,并返回root节点
+ *
+ * @param nodes 二叉树节点
+ * @return Node 哈夫曼树根节点
+ */
+ public static Node createHuffmanTree(List nodes) {
+
+ while (nodes.size() > 1) {
+ // 1)、将序列升序排序,每一个节点都可以看做是一颗最简单的二叉树
+ Collections.sort(nodes);
+ // 2)、找到权值最小的二叉树(节点)
+ Node leftNode = nodes.get(0);
+ // 2)、找到权值次小的二叉树(节点)
+ Node rightNode = nodes.get(1);
+ // 3)、将两个权值最小的二叉树构成一颗新的二叉树,新二叉树的权值为前面两个二叉树权值之和。
+ Node node = new Node(leftNode.weight + rightNode.weight);
+ node.left = leftNode;
+ node.right = rightNode;
+ // 4)、将新二叉树放入序列中,并移除两个权值最小的二叉树
+ nodes.add(node);
+ nodes.remove(leftNode);
+ nodes.remove(rightNode);
+ }
+ return nodes.get(0);
+ }
+
+ /**
+ * 将字符串转换成哈夫曼树节点
+ * @param zipStr 待压缩字符串
+ * @return java.array.List
+ */
+ public static List getListNode(String zipStr) {
+ byte[] zipStrBytes = zipStr.getBytes();
+ return getListNode(zipStrBytes);
+ }
+
+ /**
+ * 将字符串转换成哈夫曼树节点
+ * @param zipBytes 待压缩字节数组
+ * @return java.array.List
+ */
+ public static List getListNode(byte[] zipBytes) {
+ // 将字节数组按字节拆分,统计每个字节出现的次数(select count(byte) weight from zipBytes order by byte)
+ Map map = new HashMap<>();
+ for (int i = 0; i < zipBytes.length; i++) {
+ Integer weight = map.get(zipBytes[i]);
+ if (weight == null) {
+ map.put(zipBytes[i], 1);
+ } else {
+ map.put(zipBytes[i], weight + 1);
+ }
+ }
+ List nodes = new ArrayList<>();
+ // 把每一个键值对转成一个Node对象,并加入到nodes集合
+ for (Map.Entry entry : map.entrySet()) {
+ nodes.add(new Node(entry.getKey(), entry.getValue()));
+ }
+ return nodes;
+ }
+
+
+}
+
+// 哈夫曼树节点
+class Node implements Comparable {
+ Byte data; // 字节
+ int weight; // 权值,主要用于构建哈夫曼树
+ Node left; // 左子树
+ Node right; // 右子树
+
+ public Node(int weight) {
+ this.weight = weight;
+ }
+
+ public Node(Byte data, int weight) {
+ this.data = data;
+ this.weight = weight;
+ }
+
+ // 先序遍历
+ public void preOrder() {
+ System.out.println(this);
+ if (this.left != null) {
+ this.left.preOrder();
+ }
+ if (this.right != null) {
+ this.right.preOrder();
+ }
+ }
+
+ @Override
+ public int compareTo(Node o) {
+ // 升序
+ return this.weight - o.weight;
+ }
+
+ @Override
+ public String toString() {
+ return "Node{" +
+ "data=" + data +
+ ", weight=" + weight +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmantree/HuffmanTree.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmantree/HuffmanTree.java
new file mode 100644
index 00000000..4dd1b9a4
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/atguigu/tree/huffmantree/HuffmanTree.java
@@ -0,0 +1,97 @@
+package cn.lastwhisper.atguigu.tree.huffmantree;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 哈夫曼树
+ * @author cn.lastwhisper
+ */
+public class HuffmanTree {
+ public static void main(String[] args) {
+ int[] arr = {13, 7, 8, 3, 29, 6, 1};
+
+ Node root = createHuffmanTree(arr);
+
+ preOrder(root);// 67,29,38,15,7,8,23,10,4,1,3,6,13
+ }
+
+ public static void preOrder(Node root) {
+ if (root != null) {
+ root.preOrder();
+ } else {
+ System.out.println("空树!");
+ }
+ }
+
+ /**
+ * 创建哈夫曼树,并返回root节点
+ *
+ * @param arr
+ * @return cn.cn.lastwhisper.tree.huffmantree.Node
+ */
+ public static Node createHuffmanTree(int[] arr) {
+
+ List nodes = new LinkedList<>();
+ for (int i = 0; i < arr.length; i++) {
+ nodes.add(new Node(arr[i]));
+ }
+
+ while (nodes.size() > 1) {
+ // 1)、将序列升序排序,每一个节点都可以看做是一颗最简单的二叉树
+ Collections.sort(nodes);
+ // 2)、找到权值最小的二叉树(节点)
+ Node leftNode = nodes.get(0);
+ // 2)、找到权值次小的二叉树(节点)
+ Node rightNode = nodes.get(1);
+ // 3)、将两个权值最小的二叉树构成一颗新的二叉树,新二叉树的权值为前面两个二叉树权值之和。
+ Node node = new Node(leftNode.value + rightNode.value);
+ node.left = leftNode;
+ node.right = rightNode;
+
+ // 4)、将新二叉树放入序列中,并移除两个权值最小的二叉树
+ nodes.add(node);
+ nodes.remove(leftNode);
+ nodes.remove(rightNode);
+
+ }
+ return nodes.get(0);
+ }
+
+}
+
+// 哈夫曼树节点
+class Node implements Comparable {
+ int value;// 权值
+ Node left;//左子树
+ Node right;//右子树
+
+ public Node(int value) {
+ this.value = value;
+ }
+
+ // 先序遍历
+ public void preOrder() {
+ System.out.println(this);
+ if (this.left != null) {
+ this.left.preOrder();
+ }
+ if (this.right != null) {
+ this.right.preOrder();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Node{" +
+ "value=" + value +
+ '}';
+ }
+
+ @Override
+ public int compareTo(Node o) {
+ // 升序
+ return this.value - o.value;
+ }
+}
diff --git a/algorithms/datastructure/src/main/java/cn/lastwhisper/util/ArrayUtil.java b/algorithms/datastructure/src/main/java/cn/lastwhisper/util/ArrayUtil.java
new file mode 100644
index 00000000..b91dfa7c
--- /dev/null
+++ b/algorithms/datastructure/src/main/java/cn/lastwhisper/util/ArrayUtil.java
@@ -0,0 +1,24 @@
+package cn.lastwhisper.util;
+
+/**
+ * @author cn.lastwhisper
+ */
+public class ArrayUtil {
+
+ public static int[] generateArrByRandom(int max) {
+ int[] arr = new int[max];
+ for (int i = 0; i < max; i++) {
+ arr[i] = (int) (Math.random() * 8000000); // 生成一个[0,8000000) 的一个数
+ }
+ return arr;
+ }
+
+ public static int[] generateArrByOrder(int max) {
+ int[] arr = new int[max];
+ for (int i = 0; i < max; i++) {
+ arr[i] = i + 1;
+ }
+
+ return arr;
+ }
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.go"
new file mode 100644
index 00000000..eb316f9f
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.go"
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力搜索,找出所有组合进行对比
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+func twoSum(numbers []int, target int) []int {
+ for i := 0; i < len(numbers); i++ {
+ for j := i + 1; j < len(numbers); j++ { // i + 1 是一个优化点
+ if i != j && numbers[i]+numbers[j] == target {
+ return []int{i + 1, j + 1}
+ }
+ }
+ }
+ return nil
+}
+
+func main() {
+ fmt.Println(twoSum([]int{2, 3, 4}, 6))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.go" "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.go"
new file mode 100644
index 00000000..6ad9d169
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.go"
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:二分搜索
+ * (1)遍历数组,对每一个值 nums[i],在 nums 数组中进行 [i,n] 范围的二分搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*logn)
+ * 空间复杂度:O(1)
+ */
+func twoSum(numbers []int, target int) []int {
+ for i := 0; i < len(numbers); i++ {
+ j := bs(numbers, i, len(numbers)-1, target-numbers[i])
+ if j != -1 {
+ return []int{i + 1, j + 1}
+ }
+ }
+ return nil
+}
+
+func bs(nums []int, l int, r int, target int) int {
+ // 这里是“<=”因为 [l,r] 内所有值都是可能的目标
+ for l <= r {
+ mid := (l-r)/2 + r
+ if nums[mid] == target {
+ return mid
+ } else if nums[mid] > target {
+ r = mid - 1
+ } else {
+ l = mid + 1
+ }
+ }
+ return -1
+}
+
+func main() {
+ fmt.Println(twoSum([]int{2, 3, 4}, 6))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.go" "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.go"
new file mode 100644
index 00000000..f3b51b13
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.go"
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(对撞指针)
+ * 这种思路的前提是数组有序
+ * (1)定义两个初始指针 l,r 分别指向数组头尾。
+ * (2)nums[l]+nums[r] > target,r--
+ * (3)nums[l]+nums[r] < target,l++
+ * (4)nums[l]+nums[r] == target,找到答案
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func twoSum(numbers []int, target int) []int {
+ l, r := 0, len(numbers)-1
+ // 这里不能等于,因为 index1 必须小于 index2
+ for l < r {
+ if numbers[l]+numbers[r] == target {
+ return []int{l + 1, r + 1}
+ } else if numbers[l]+numbers[r] < target {
+ l++
+ } else {
+ r--
+ }
+ }
+ return nil
+}
+
+func main() {
+ fmt.Println(twoSum([]int{2, 3, 4}, 6))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.go"
new file mode 100644
index 00000000..e8e1d727
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.go"
@@ -0,0 +1,34 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/reverse-string/
+ * 编号:344
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:对撞指针交换元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func reverseString(s []byte) {
+ l, r := 0, len(s)-1
+ for l < r {
+ c := s[l]
+ s[l] = s[r]
+ s[r] = c
+ l++
+ r--
+ }
+}
+
+func main() {
+ s := []byte{'h', 'e', 'l', 'l', 'o'}
+ fmt.Println(s)
+ reverseString(s)
+ fmt.Println(s)
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.go"
new file mode 100644
index 00000000..5df7b3ea
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.go"
@@ -0,0 +1,49 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/reverse-vowels-of-a-string/
+ * 编号:345
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:对撞指针条件交换元素
+ * 元音字母:a,e,i,o,u 大小写
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+func reverseVowels(s string) string {
+ l, r := 0, len(s)-1
+ chars := []uint8(s)
+ for l < r {
+ for l < r && !valid(chars[l]) {
+ l++
+ }
+ for l < r && !valid(chars[r]) {
+ r--
+ }
+ if l < r { // 需要判断
+ c := chars[l]
+ chars[l] = chars[r]
+ chars[r] = c
+ l++
+ r--
+ }
+ }
+ return string(chars)
+}
+
+func valid(c uint8) bool {
+ return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'A' ||
+ c == 'E' || c == 'I' || c == 'O' || c == 'U'
+}
+
+func main() {
+ s := "hello"
+ fmt.Println(s)
+ fmt.Println(reverseVowels(s))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.go"
new file mode 100644
index 00000000..19048a82
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.go"
@@ -0,0 +1,60 @@
+package main
+
+import "fmt"
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/merge-sorted-array/
+ * 编号:88
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:数组有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * 归并排序,归的过程。
+ * (1)从前到后比较(找小)复制只用 nums1,需要移动 nums1 中的元素,不如创建中间数据
+ * (2)从后到前比较(找大)复制只用 nums1,无需移动 nums1 中的元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+func merge(nums1 []int, m int, nums2 []int, n int) {
+ temp := make([]int, m+n)
+ // p1 是 num1 已拷贝数据的指针,[0,p1]
+ p1, p2, t := 0, 0, 0
+ // 从前到后找 num1 或 num2 最小的复制到 temp
+ for p1 < m && p2 < n {
+ if nums1[p1] > nums2[p2] {
+ temp[t] = nums2[p2]
+ p2++
+ } else {
+ temp[t] = nums1[p1]
+ p1++
+ }
+ t++
+ }
+ // 将 num1 中剩余的数据 copy 到 temp
+ for p1 < m {
+ temp[t] = nums1[p1]
+ t++
+ p1++
+ }
+ // 将 num2 中剩余的数据 copy 到 temp
+ for p2 < n {
+ temp[t] = nums2[p2]
+ t++
+ p2++
+ }
+ // 将 temp 中所有数据 copy 到 num1
+ for i := 0; i < len(temp); i++ {
+ nums1[i] = temp[i]
+ }
+}
+
+func main() {
+ nums1 := []int{1, 2, 3, 0, 0, 0}
+ nums2 := []int{2, 5, 6}
+ m := 3
+ n := 3
+ merge(nums1, m, nums2, n)
+ fmt.Println(nums1)
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.go" "b/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.go"
new file mode 100644
index 00000000..c0d54a69
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.go"
@@ -0,0 +1,47 @@
+package main
+
+import "fmt"
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/merge-sorted-array/
+ * 编号:88
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:数组有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * 从后向前复制
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func merge(nums1 []int, m int, nums2 []int, n int) {
+ // 从后到前找 num1 或 num2 最大的复制到 num1 末尾
+ p1, p2, end := m-1, n-1, m+n-1
+ for p1 >= 0 && p2 >= 0 {
+ if nums1[p1] > nums2[p2] {
+ nums1[end] = nums1[p1]
+ p1--
+ } else {
+ nums1[end] = nums2[p2]
+ p2--
+ }
+ end--
+ }
+ // 如果 num1 还没复制完,不用管,因为num1自身就是有序的
+ // 将 num2 中剩余的数据 copy 到 temp
+ for p2 >= 0 {
+ nums1[end] = nums2[p2]
+ end--
+ p2--
+ }
+}
+
+func main() {
+ nums1 := []int{1, 2, 3, 0, 0, 0}
+ nums2 := []int{2, 5, 6}
+ m := 3
+ n := 3
+ merge(nums1, m, nums2, n)
+ fmt.Println(nums1)
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.go"
new file mode 100644
index 00000000..6620e6b9
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.go"
@@ -0,0 +1,12 @@
+package main
+
+import "sort"
+
+func findKthLargest(nums []int, k int) int {
+ sort.Ints(nums)
+ return nums[len(nums)-k]
+}
+
+func main() {
+
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.go"
new file mode 100644
index 00000000..eb157e31
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.go"
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * (1)双指针 l、r 维护一个滑动窗口
+ * (2)窗口的左边界在数组范围内,则循环继续
+ * (3)r未到数组尾且 sums 需要动态缩小窗口左边界
+ * (5)循环内每次找 sum >= s 时的最小长度
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // l 和 r 指针同时遍历了一遍数组 O(2n)
+ * 空间复杂度:O(1)
+ */
+func lengthOfLongestSubstring(s string) int {
+ freq := make([]uint8, 256)
+ // 窗口 [l,r] 内无重复元素
+ l, r, res := 0, 0, 0
+ for l < len(s) {
+ if r < len(s) && freq[s[r]] == 0 {
+ freq[s[r]]++
+ r++
+ } else {
+ freq[s[l]]--
+ l++
+ }
+ // 这里 r - l 不加一,因为 l、r 初始化值都是0,并且是先用在加的
+ res = max(res, r-l)
+ }
+ return res
+}
+
+func max(x int, y int) int {
+ if x > y {
+ return x
+ }
+ return y
+}
+
+func main() {
+ s := "abcabcbb"
+ fmt.Println(lengthOfLongestSubstring(s))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.go"
new file mode 100644
index 00000000..c67ee938
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.go"
@@ -0,0 +1,36 @@
+package main
+
+import (
+ "fmt"
+ "math"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/container-with-most-water/
+ * 编号:11
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(对撞指针)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func maxArea(height []int) int {
+ l, r, max := 0, len(height)-1, float64(-1)
+ for l < r {
+ if height[l] < height[r] {
+ max = math.Max(max, float64(height[l]*(r-l)))
+ l++
+ } else {
+ max = math.Max(max, float64(height[r]*(r-l)))
+ r--
+ }
+ }
+ return int(max)
+}
+
+func main() {
+ nums := []int{1, 8, 6, 2, 5, 4, 8, 3, 7}
+ fmt.Println(maxArea(nums))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.go"
new file mode 100644
index 00000000..7b603a86
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.go"
@@ -0,0 +1,60 @@
+package main
+
+import (
+ "fmt"
+ "math"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * (1)双指针 l、r 维护一个滑动窗口
+ * (2)窗口的左边界在数组范围内,则循环继续
+ * (3)r未到数组尾且 sums 需要动态缩小窗口左边界
+ * (5)循环内每次找 sum >= s 时的最小长度
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // l 和 r 指针同时遍历了一遍数组 O(2n)
+ * 空间复杂度:O(1)
+ */
+func minSubArrayLen(s int, nums []int) int {
+ // 滑动窗口[l,r]
+ l, r := 0, -1
+ // 窗口和
+ sum, result := 0, math.MaxInt64
+ // 窗口左边界未越界,说明窗口还可以继续扩容或者缩小
+ for l < len(nums) {
+ // sum= s {
+ result = min(result, r-l+1)
+ }
+ }
+ if result == math.MaxInt64 {
+ return 0
+ }
+ return result
+}
+
+func min(x int, y int) int {
+ if x > y {
+ return y
+ }
+ return x
+}
+
+func main() {
+ s := 7
+ nums := []int{2, 3, 1, 2, 4, 3}
+ fmt.Println(minSubArrayLen(s, nums))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.go" "b/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.go"
new file mode 100644
index 00000000..5e234415
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.go"
@@ -0,0 +1,54 @@
+package main
+
+import (
+ "fmt"
+ "math"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * (1)双指针 l、r 维护一个滑动窗口
+ * (2)窗口的左边界在数组范围内,则循环继续
+ * (3)r未到数组尾且 sums 需要动态缩小窗口左边界
+ * (5)循环内每次找 sum >= s 时的最小长度
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // l 和 r 指针同时遍历了一遍数组 O(2n)
+ * 空间复杂度:O(1)
+ */
+func minSubArrayLen(s int, nums []int) int {
+ // 窗口和
+ l, sum, result := 0, 0, math.MaxInt64
+ // 一直扩容右边界,直到sum >= s,缩小左边界
+ // 每次缩小,都计算最小子数组
+ for r := 0; r < len(nums); r++ {
+ sum += nums[r]
+ for sum >= s {
+ result = min(result, r-l+1)
+ sum -= nums[l]
+ l++
+ }
+ }
+ if result == math.MaxInt64 {
+ return 0
+ }
+ return result
+}
+
+func min(x int, y int) int {
+ if x > y {
+ return y
+ }
+ return x
+}
+
+func main() {
+ s := 7
+ nums := []int{2, 3, 1, 2, 4, 3}
+ fmt.Println(minSubArrayLen(s, nums))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.go"
new file mode 100644
index 00000000..51d91b90
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.go"
@@ -0,0 +1,37 @@
+package main
+
+import "fmt"
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/sort-colors/
+ * 编号:75
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:只有0,1,2
+ * -------------------------------------------------------------------
+ * 思路:
+ * 计数排序,统计每一个数出现的频率
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func sortColors(nums []int) {
+ freq := [3]int{}
+ for i := 0; i < len(nums); i++ {
+ freq[nums[i]]++
+ }
+ idx := 0
+ for i := 0; i < len(freq); i++ {
+ for freq[i] > 0 {
+ nums[idx] = i
+ idx++
+ freq[i]--
+ }
+ }
+}
+
+func main() {
+ var arr = []int{2, 0, 2, 1, 1, 0}
+ sortColors(arr)
+ fmt.Println(arr)
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.go" "b/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.go"
new file mode 100644
index 00000000..cc952a6d
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.go"
@@ -0,0 +1,51 @@
+package main
+
+import "fmt"
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/sort-colors/
+ * 编号:75
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:只有0,1,2
+ * -------------------------------------------------------------------
+ * 思路:
+ * 三路快速排序的思想,对整个数组只遍历了一遍
+ * (1)双指针 left=-1,right=n
+ * (2)下标 i 遍历数组,num[i]==1,i++
+ * (3)num[i]==0,将 0 移动到数组最前位置,swap(num,++left,i++)
+ * i++,即使 num[++left]==0,这次的交换和位移也是正确的
+ * (4)num[i]==2,将 2 移动到数组最后位置,swap(num,--right,i)
+ * i不变,因为有可能num[--right]==2
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func sortColors(nums []int) {
+ left := -1
+ right := len(nums)
+ for i := 0; i < right; {
+ if nums[i] == 1 {
+ i++
+ } else if nums[i] == 0 {
+ left++
+ swap(nums, left, i)
+ i++
+ } else if nums[i] == 2 {
+ right--
+ swap(nums, right, i)
+ }
+ }
+}
+
+func swap(nums []int, i, j int) {
+ temp := nums[i]
+ nums[i] = nums[j]
+ nums[j] = temp
+}
+
+func main() {
+ var arr = []int{2, 0, 2, 1, 1, 0}
+ sortColors(arr)
+ fmt.Println(arr)
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution1.go" "b/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution1.go"
new file mode 100644
index 00000000..e8d5b633
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution1.go"
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/valid-palindrome/
+ * 编号:125
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 对撞指针遍历比较
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+func isPalindrome(s string) bool {
+ upperS := strings.ToUpper(s)
+ l, r := 0, len(s)-1
+ for l < r {
+ for l < r && !valid(upperS[l]) {
+ l++
+ }
+ for l < r && !valid(upperS[r]) {
+ r--
+ }
+ if upperS[l] != upperS[r] {
+ return false
+ }
+ l++
+ r--
+ }
+ return true
+}
+
+func valid(c uint8) bool {
+ return (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
+}
+
+func main() {
+ fmt.Println(isPalindrome("A man, a plan, a canal: Panama"))
+}
diff --git "a/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution2.go" "b/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution2.go"
new file mode 100644
index 00000000..5aebc977
--- /dev/null
+++ "b/algorithms/go/algorithm/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_Easy/Solution2.go"
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "fmt"
+)
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/valid-palindrome/
+ * 编号:125
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 对撞指针遍历比较
+ * 统一转成大写:ch & 0b11011111 简写:ch & 0xDF
+ * 统一转成小写:ch | 0b00100000 简写:ch | 0x20
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+func isPalindrome(s string) bool {
+ l, r := 0, len(s)-1
+ for l < r { // 这里不能等于
+ for l < r && !valid(s[l]) {
+ l++
+ }
+ for l < r && !valid(s[r]) {
+ r--
+ }
+ if (s[l] & 0xDF) != (s[r] & 0xDF) {
+ return false
+ }
+ l++
+ r--
+ }
+ return true
+}
+
+func valid(c uint8) bool {
+ return (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
+}
+
+func main() {
+ fmt.Println(isPalindrome("A man, a plan, a canal: Panama"))
+}
diff --git a/algorithms/go/algorithm/leetcode/lc69.go b/algorithms/go/algorithm/leetcode/lc69.go
new file mode 100644
index 00000000..33b59de5
--- /dev/null
+++ b/algorithms/go/algorithm/leetcode/lc69.go
@@ -0,0 +1,15 @@
+package main
+
+import "fmt"
+
+func mySqrt(x int) int {
+
+
+ return 0
+}
+
+func main() {
+ fmt.Println(mySqrt(4))
+ fmt.Println(mySqrt(8))
+}
+
diff --git a/algorithms/interview/pom.xml b/algorithms/interview/pom.xml
new file mode 100644
index 00000000..9d4710bc
--- /dev/null
+++ b/algorithms/interview/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ algorithms
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ interview
+
+
+
\ No newline at end of file
diff --git a/algorithms/interview/src/main/java/Main.java b/algorithms/interview/src/main/java/Main.java
new file mode 100644
index 00000000..a89bcd4c
--- /dev/null
+++ b/algorithms/interview/src/main/java/Main.java
@@ -0,0 +1,39 @@
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Scanner;
+
+public class Main {
+ //public static void main(String[] args){
+ // Scanner in = new Scanner(System.in);
+ // // 一个while就是一个测试用例
+ // while(in.hasNext()){
+ // int m = in.nextInt(); // 该测试用例后续接收的参数个数
+ // int n = in.nextInt(); // 该测试用例后续接收的参数个数
+ // long[] array = new long[m];
+ // for(int i=0; i set = new HashSet<>();
+ for (int i = 0; i < n; i++) {
+ String line =sc.nextLine();
+ String[] arr = line.split(" ");
+ set.addAll(Arrays.asList(arr));
+ }
+ System.out.println("sum:" + set.size());
+ }
+ sc.close();
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/interview/src/main/java/cn/lastwhisper/interview/lejian/Test1.java b/algorithms/interview/src/main/java/cn/lastwhisper/interview/lejian/Test1.java
new file mode 100644
index 00000000..8d64005c
--- /dev/null
+++ b/algorithms/interview/src/main/java/cn/lastwhisper/interview/lejian/Test1.java
@@ -0,0 +1,113 @@
+package cn.lastwhisper.interview.lejian;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+/**
+ * 最长对称子序列,本质上是在找最长公共子序列
+ *
+ * @author gaojun
+ *
+ */
+public class Test1 {
+
+ /**
+ * 输入一个字符串s,我们可以删除字符串s中的任意字符,让剩下的字符串形成一个对称字符串,且该字符串为最长对称字符串。
+ * 如:输入google,则找到最长对称字符串为goog;
+ * 如:输入abcda则能找到3个最长对称字符串为aba/aca/ada。
+ * 若最长对称字符串存在多个,则输出多个相同长度的最长对称字符串,且字符串中不包含特殊字符。
+ *
+ */
+ public static void main(String[] args) {
+ // TODO 输出最长对称字符串:goog
+ String input1 = "google";
+ System.out.println(findMaxSymmetry(input1));
+ // TODO 输出3个最长对称字符串:aba/aca/ada
+ String input2 = "abcda";
+ System.out.println(findMaxSymmetry(input2));
+ // TODO 输出2个最长对称字符串:pop/upu,不会输出特殊字符的对称字符串p-p
+ String input3 = "pop-upu";
+ System.out.println(findMaxSymmetry(input3));
+ }
+
+ /**
+ * 遍历生成的二维数组,递归查找最长路径
+ * @param a 第一个字符串
+ * @param b 第二个字符串
+ * @param mux 二维中间数组
+ * @param i 二维数组raw位置
+ * @param j 二维数组column位置
+ * @param path 一次查找的字符串
+ * @param paths 最终结果集合
+ */
+ private static void getAllLcs(String a, String b, int[][] mux, int i, int j, String path, Set paths) {
+ StringBuilder pathBuilder = new StringBuilder(path);
+ while (i > 0 && j > 0) {
+ if (a.charAt(i - 1) == b.charAt(j - 1)) {
+ pathBuilder.append(a.charAt(i - 1));
+ --i;
+ --j;
+ } else {
+ if (mux[i - 1][j] > mux[i][j - 1]) {
+ --i;
+ } else if (mux[i - 1][j] < mux[i][j - 1]) {
+ --j;
+ } else {
+ getAllLcs(a, b, mux, i - 1, j, pathBuilder.toString(), paths);
+ getAllLcs(a, b, mux, i, j - 1, pathBuilder.toString(), paths);
+ return;
+ }
+ }
+ }
+ paths.add(pathBuilder.toString());
+ }
+
+ /**
+ * 查找最大公共子序列
+ */
+ private static String findLCS(String input) {
+ if (input == null || input.length() == 0) {
+ return "";
+ }
+ String reverse = new StringBuilder(input).reverse().toString();
+ int len = input.length();
+ // 0行0列分别表示空串和input、reverse的 lcs 长度
+ // 初始状态
+ int[][] dp = new int[len + 1][len + 1];
+
+ for (int i = 1; i < len + 1; i++) {
+ for (int j = 1; j < len + 1; j++) {
+ if (input.charAt(i - 1) == reverse.charAt(j - 1)) {
+ // 找到一个 lcs 的元素
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ // 找 lcs 长度最长的。
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ // 查找路径时,存在多个路径是一个字符串的情况,set去重。
+ Set paths = new HashSet<>();
+ getAllLcs(input, reverse, dp, input.length(), reverse.length(), "", paths);
+
+ return String.join("/", paths);
+ }
+
+ /**
+ * 包括对特殊字符,结果格式生成
+ * @param input 待处理字符串
+ */
+ public static String findMaxSymmetry(String input) {
+ String[] prepare = Pattern.compile("[^a-zA-Z0-9]").split(input);
+ StringBuilder sb = new StringBuilder();
+ for (String str : prepare) {
+ String result = findLCS(str);
+ sb.append(result);
+ sb.append("/");
+ }
+ return sb.substring(0, sb.length() - 1);
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/interview/src/main/java/cn/lastwhisper/interview/other/Main.java b/algorithms/interview/src/main/java/cn/lastwhisper/interview/other/Main.java
new file mode 100644
index 00000000..c98fe0b8
--- /dev/null
+++ b/algorithms/interview/src/main/java/cn/lastwhisper/interview/other/Main.java
@@ -0,0 +1,50 @@
+package cn.lastwhisper.interview.other;
+
+import java.util.Scanner;
+
+/**
+ *给出一个长度为n的数组a,你需要在这个数组中找到一个长度至少为m的区间,
+ * 使得这个区间内的数字的和尽可能小。
+ * @author lastwhisper
+ */
+public class Main {
+
+ public int sum(int[] numbers, int m) {
+ if (numbers.length < m)
+ throw new IllegalArgumentException("Illegal argument numbers");
+ int l = 0, r = numbers.length - 1, x = m;
+ int min = 0;
+ for (int i = l; i <= r; i++) {
+ min += numbers[i];
+ }
+ for (int i = 0; i <= (r - m); i++) {
+ int temp = 0;
+ for (int j = i; j < x; j++) {
+ temp += numbers[j];
+ }
+ if (temp < min) {
+ min = temp;
+ }
+ x++;
+ }
+ return min;
+ }
+
+ public static void main(String[] args) {
+ Scanner scanner = new Scanner(System.in);
+ // 一个while就是一个测试用例
+ while (scanner.hasNext()) {
+ int m = scanner.nextInt();
+ int n = scanner.nextInt();
+ int[] nums = new int[m];
+ for (int i = 0; i < m; i++) {
+ nums[i] = scanner.nextInt();
+ }
+ //int[] nums = {-2, 1, -1, -1, -1};
+ //int n = 3;
+ System.out.println(new Main().sum(nums, n));
+ }
+
+ }
+
+}
diff --git a/algorithms/interview/src/main/java/cn/lastwhisper/interview/scanner/Main.java b/algorithms/interview/src/main/java/cn/lastwhisper/interview/scanner/Main.java
new file mode 100644
index 00000000..e8038c9a
--- /dev/null
+++ b/algorithms/interview/src/main/java/cn/lastwhisper/interview/scanner/Main.java
@@ -0,0 +1,18 @@
+package cn.lastwhisper.interview.scanner;
+
+import java.util.Scanner;
+
+public class Main {
+ public static void main(String[] args) {
+ Scanner sc = new Scanner(System.in);
+ int n = sc.nextInt();
+ int ans = 0, x;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ x = sc.nextInt();
+ ans += x;
+ }
+ }
+ System.out.println(ans);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/interview/src/main/java/cn/lastwhisper/interview/\347\276\216\345\233\242\347\202\271\350\257\204/\345\220\210\345\271\266\351\207\221\345\270\201/Test01.java" "b/algorithms/interview/src/main/java/cn/lastwhisper/interview/\347\276\216\345\233\242\347\202\271\350\257\204/\345\220\210\345\271\266\351\207\221\345\270\201/Test01.java"
new file mode 100644
index 00000000..28c442e5
--- /dev/null
+++ "b/algorithms/interview/src/main/java/cn/lastwhisper/interview/\347\276\216\345\233\242\347\202\271\350\257\204/\345\220\210\345\271\266\351\207\221\345\270\201/Test01.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.interview.美团点评.合并金币;
+
+
+import java.util.Scanner;
+
+public class Test01 {
+ /**
+ * 有 N 堆金币排成一排,第 i 堆中有 C[i]块金币。 每次合并都会将相邻的两堆金币合并为一堆,成本为这两堆金币块数之和。
+ * 经过N-1次合并,最终将所有金币合并为一堆。请找出将金币合并为一堆的最低成本。
+ *
+ * 比如,n=3时表示共有3堆金币,每堆重量分别是2、1、9.
+ * 一种合并方案是2和9合并,新堆重量是11,消耗体力为11;
+ * 接着11和1合并,新堆重量是12,消耗体力为12,因此总消耗体力是11+12=23。
+ * 另一种方案是:1和2合并,新堆重量是3,消耗体力为3;
+ * 接着3和9合并,新堆重量是12,消耗体力为12,因此总消耗体力是3+12=15。可以证明这就是最少消耗体力。
+ *
+ * 其中,1 <= N <= 30,1 <= C[i] <= 100
+ *
+ * f[i,j]=min{f[i,k]+f[k+1,j]+sum[i,j]|i<=k 0)
+ dp[i][i + l] = min + sum[i + l] - sum[i - 1];
+ else dp[i][i + l] = min + sum[l];
+ }
+ }
+ System.out.println(dp[0][n - 1]);
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ System.out.print(dp[i][j] + " ");
+ }
+ System.out.println();
+ }
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/array/pom.xml b/algorithms/leetcode/array/pom.xml
new file mode 100644
index 00000000..6689d525
--- /dev/null
+++ b/algorithms/leetcode/array/pom.xml
@@ -0,0 +1,25 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper.leetcode
+ array
+
+
+
+
+ algorithm-common
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..3114b286
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.array.两数之和_II_输入有序数组_167_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力搜索,找出所有组合进行对比
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public int[] twoSum(int[] numbers, int target) {
+ if (numbers == null || numbers.length < 2) {
+ throw new IllegalArgumentException("Illegal argument numbers");
+ }
+ for (int i = 0; i < numbers.length; i++) {
+ for (int j = i + 1; j < numbers.length; j++) {// i + 1 是一个优化点
+ if (i != j && numbers[i] + numbers[j] == target) {
+ return new int[]{i + 1, j + 1};
+ }
+ }
+ }
+ throw new IllegalStateException("No results found");
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2}, new Solution1().twoSum(new int[]{2, 7, 11, 15}, 9));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..d6b0e31b
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,57 @@
+package cn.lastwhisper.leetcode.array.两数之和_II_输入有序数组_167_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:二分搜索
+ * (1)遍历数组,对每一个值 nums[i],在 nums 数组中进行 [i,n] 范围的二分搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*logn)
+ * 空间复杂度:O(1)
+ */
+ public int[] twoSum(int[] numbers, int target) {
+ if (numbers == null || numbers.length < 2) {
+ throw new IllegalArgumentException("Illegal argument numbers");
+ }
+ for (int i = 0; i < numbers.length; i++) {
+ int j = bs(numbers, i, numbers.length - 1, target - numbers[i]);
+ if (j != -1) {
+ return new int[]{i + 1, j + 1};
+ }
+ }
+ throw new IllegalStateException("No results found");
+ }
+
+ /**
+ * @param nums 待搜索数组
+ * @param l 起始索引
+ * @param r 终止索引
+ * @param target 目标数值
+ * @return int 目标数值在数组中的索引
+ */
+ private int bs(int[] nums, int l, int r, int target) {
+ // 这里是“<=”因为 [l,r] 内所有值都是可能的目标
+ while (l <= r) {
+ int mid = (l - r) / 2 + r;
+ if (nums[mid] == target) {
+ return mid;
+ } else if (nums[mid] > target) {
+ r = mid - 1;
+ } else {
+ l = mid + 1;
+ }
+ }
+ return -1;
+ }
+
+ public static void main(String[] args) {
+ //Assert.assertArrayEquals(new int[]{1, 2}, new Solution2().twoSum(new int[]{2, 7, 11, 15}, 9));
+ Assert.assertArrayEquals(new int[]{1, 3}, new Solution2().twoSum(new int[]{2, 3, 4}, 6));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..3cb95cf3
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\270\244\346\225\260\344\271\213\345\222\214_II_\350\276\223\345\205\245\346\234\211\345\272\217\346\225\260\347\273\204_167_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.array.两数之和_II_输入有序数组_167_简单;
+
+import org.junit.Assert;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
+ * 编号:167
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(对撞指针)
+ * 这种思路的前提是数组有序
+ * (1)定义两个初始指针 l,r 分别指向数组头尾。
+ * (2)nums[l]+nums[r] > target,r--
+ * (3)nums[l]+nums[r] < target,l++
+ * (4)nums[l]+nums[r] == target,找到答案
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int[] twoSum(int[] numbers, int target) {
+ if (numbers == null || numbers.length < 2) {
+ throw new IllegalArgumentException("Illegal argument numbers");
+ }
+ int l = 0, r = numbers.length - 1;
+ // 这里不能等于,因为 index1 必须小于 index2
+ while (l < r) {
+ if (numbers[l] + numbers[r] == target) {
+ return new int[]{l + 1, r + 1};
+ } else if (numbers[l] + numbers[r] < target) {
+ l++;
+ } else {
+ r--;
+ }
+ }
+ throw new IllegalStateException("No results found");
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 3}, new Solution3().twoSum(new int[]{2, 3, 4}, 6));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\275\277\346\225\260\347\273\204\345\224\257\344\270\200\347\232\204\346\234\200\345\260\217\345\242\236\351\207\217_945_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\275\277\346\225\260\347\273\204\345\224\257\344\270\200\347\232\204\346\234\200\345\260\217\345\242\236\351\207\217_945_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..42c50331
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\344\275\277\346\225\260\347\273\204\345\224\257\344\270\200\347\232\204\346\234\200\345\260\217\345\242\236\351\207\217_945_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,25 @@
+package cn.lastwhisper.leetcode.array.使数组唯一的最小增量_945_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-increment-to-make-array-unique/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int minIncrementForUnique(int[] A) {
+
+ return 0;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(6, new Solution1().minIncrementForUnique(new int[]{3, 2, 1, 2, 1, 7}));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..3dab2b3b
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.array.删除排序数组中的重复项_26_简单;
+
+
+import org.junit.Assert;
+
+public class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
+ * 编号:26
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:从小到大有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * 双指针
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int removeDuplicates(int[] nums) {
+ // 特判
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ // 区间[0,...,pos]均为第一次出现元素
+ int pos = 0;
+
+ for (int i = 1; i < nums.length; i++) {
+ if (nums[pos] != nums[i]) {
+ nums[++pos] = nums[i];
+ }
+ }
+ return pos + 1;
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] nums = {0, 0, 1, 1, 2, 3, 4, 4};
+ Assert.assertEquals(5, new Solution1().removeDuplicates(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..8e1ec9fb
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_26_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.array.删除排序数组中的重复项_26_简单;
+
+
+import org.junit.Assert;
+
+public class Solution2 {
+
+ public int removeDuplicates(int[] nums) {
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ // 区间[0,...,pos]均为第一次出现元素
+ int pos = 0;
+ // 数字可出现的次数
+ int frequency = 1;
+ for (int n : nums) {
+ if (pos < frequency || n > nums[pos - frequency]) {
+ nums[pos++] = n;
+ }
+ }
+ return pos;
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] nums = {0, 0, 1, 1, 2, 3, 4, 4};
+ Assert.assertEquals(5, new Solution2().removeDuplicates(nums));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..d8382336
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,40 @@
+package cn.lastwhisper.leetcode.array.删除排序数组中的重复项_II_80_中等;
+
+import org.junit.Assert;
+
+public class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/
+ * 编号:80
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:从小到大有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int removeDuplicates(int[] nums) {
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ // 区间[0,...,pos]每个元素最多出现两次
+ int pos = 0;
+ // 数字可出现的次数
+ int frequency = 2;
+ for (int num : nums) {
+ if (pos < frequency || num > nums[pos - frequency]) {
+ nums[pos++] = num;
+ }
+ }
+ return pos;
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] nums = {0, 0, 1, 1, 1, 1, 2, 3, 3};
+ Assert.assertEquals(7, new Solution1().removeDuplicates(nums));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..0d8258b5
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271_II_80_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.array.删除排序数组中的重复项_II_80_中等;
+
+import org.junit.Assert;
+
+public class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/
+ * 编号:80
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:从小到大有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int removeDuplicates(int[] nums) {
+ // Initialize the counter and the second pointer.
+ int j = 1, count = 1;
+
+ // Start from the second element of the array and process
+ // elements one by one.
+ for (int i = 1; i < nums.length; i++) {
+
+ // If the current element is a duplicate, increment the count.
+ if (nums[i] == nums[i - 1]) {
+ count++;
+ } else {
+ // Reset the count since we encountered a different element
+ // than the previous one.
+ count = 1;
+ }
+ // For a count <= 2, we copy the element over thus
+ // overwriting the element at index "j" in the array
+ if (count <= 2) {
+ nums[j++] = nums[i];
+ }
+ }
+ return j;
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] nums = {0, 0, 1, 1, 1, 1, 2, 3, 3};
+ Assert.assertEquals(7, new Solution2().removeDuplicates(nums));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..3a74c52d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.array.区间合并.矩形重叠_836_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 两个矩阵的重叠是无限的,但是不重叠是有限的
+ * 检查位置
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(1)
+ * 空间复杂度:O(1)
+ */
+ public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
+ // 矩阵投影X轴,矩阵1在矩阵2左侧 或 矩阵1在矩阵2右侧
+ boolean x_overlap = !(rec1[2] <= rec2[0] || rec2[2] <= rec1[0]);
+ // 矩阵投影y轴,矩阵1在矩阵2上侧 或 矩阵1在矩阵2下侧
+ boolean y_overlap = !(rec1[3] <= rec2[1] || rec2[3] <= rec1[1]);
+ return x_overlap && y_overlap;
+ }
+
+ public static void main(String[] args) {
+ int[] rec1 = {0, 0, 2, 2}, rec2 = {1, 1, 3, 3};
+ Assert.assertTrue(new Solution1().isRectangleOverlap(rec1, rec2));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..8107d63e
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\214\272\351\227\264\345\220\210\345\271\266/\347\237\251\345\275\242\351\207\215\345\217\240_836_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,28 @@
+package cn.lastwhisper.leetcode.array.区间合并.矩形重叠_836_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 检查重叠的矩阵区域
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(1)
+ * 空间复杂度:O(1)
+ */
+ public boolean isRectangleOverlap(int[] rec1, int[] rec2) {
+ //相交矩阵的水平边
+ return (Math.min(rec1[2], rec2[2]) > Math.max(rec1[0], rec2[0]) &&
+ //相交矩阵的垂直边
+ Math.min(rec1[3], rec2[3]) > Math.max(rec1[1], rec2[1]));
+ }
+
+ public static void main(String[] args) {
+ int[] rec1 = {0, 0, 2, 2}, rec2 = {1, 1, 3, 3};
+ Assert.assertTrue(new Solution2().isRectangleOverlap(rec1, rec2));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..26542e85
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.array.卡牌分组_914_简单;
+
+import org.junit.Assert;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先将数组排序,对数组进行步长为2~len/2进行分组
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public boolean hasGroupsSizeX(int[] deck) {
+ // 特判
+ if (deck == null || deck.length <= 1) {
+ return false;
+ }
+ if (deck.length == 2) {
+ return true;
+ }
+ Arrays.sort(deck);
+ for (int i = 2; i <= deck.length / 2; i++) {
+ if (deck.length % i != 0) {//deck不能整分为i组
+ continue;
+ }
+ boolean flag = true;
+ //步长为2~len/2进行分组
+ for (int len = 0, start = 0, end = i - 1; len < deck.length; end += i, start += i, len += i) {
+ for (int j = start; j < end; j++) {
+ if (deck[j] != deck[j + 1]) {
+ flag = false;
+ break;
+ }
+ }
+ if (!flag) {
+ break;
+ }
+ }
+ if (flag) return true;
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertTrue(new Solution1().hasGroupsSizeX(new int[]{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}));
+ Assert.assertTrue(new Solution1().hasGroupsSizeX(new int[]{1, 1}));
+ Assert.assertFalse(new Solution1().hasGroupsSizeX(new int[]{1, 1, 1, 2, 2, 2, 3, 3}));
+ //Assert.assertTrue(new Solution1().hasGroupsSizeX(new int[]{0, 0, 0, 1, 1, 1, 2, 2, 2}));
+ //Assert.assertTrue(new Solution1().hasGroupsSizeX(new int[]{1, 2, 3, 4, 4, 3, 2, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..8338cd8c
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\215\241\347\211\214\345\210\206\347\273\204_914_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,57 @@
+package cn.lastwhisper.leetcode.array.卡牌分组_914_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/x-of-a-kind-in-a-deck-of-cards/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:统计每个数出现的次数,次数的最大公约数大于1即可
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean hasGroupsSizeX(int[] deck) {
+ // 特判
+ if (deck == null || deck.length <= 1) {
+ return false;
+ }
+ int[] freq = new int[10000];
+ for (int num : deck) {
+ freq[num]++;
+ }
+
+ int x = freq[deck[0]];
+ for (int num : freq) {
+ // 1个数不能分组
+ if (num == 1) {
+ return false;
+ }
+ if (num > 1) {
+ x = gcd(x, num);
+ // 最小公倍数是1说明也不能按同样的值分组
+ if (x == 1) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private int gcd(int a, int b) {
+ if (b == 0) {
+ return a;
+ }
+ return gcd(b, a % b);
+ }
+
+ public static void main(String[] args) {
+ Assert.assertTrue(new Solution2().hasGroupsSizeX(new int[]{0, 0, 0, 0, 0, 1, 1, 1, 1, 1}));
+ Assert.assertTrue(new Solution2().hasGroupsSizeX(new int[]{1, 1}));
+ Assert.assertFalse(new Solution2().hasGroupsSizeX(new int[]{1, 1, 1, 2, 2, 2, 3, 3}));
+ //Assert.assertTrue(new Solution2().hasGroupsSizeX(new int[]{0, 0, 0, 1, 1, 1, 2, 2, 2}));
+ //Assert.assertTrue(new Solution2().hasGroupsSizeX(new int[]{1, 2, 3, 4, 4, 3, 2, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..ff909dd9
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262_344_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.array.反转字符串_344_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/reverse-string/
+ * 编号:344
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:对撞指针交换元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void reverseString(char[] s) {
+ int l = 0, r = s.length - 1;
+ while (l < r) {
+ char c = s[l];
+ s[l] = s[r];
+ s[r] = c;
+ l++; r--;
+ }
+ }
+
+ public static void main(String[] args) {
+ char[] s = {'h', 'e', 'l', 'l', 'o'};
+ new Solution1().reverseString(s);
+ Assert.assertArrayEquals(new char[]{'o', 'l', 'l', 'e', 'h'}, s);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..7c26f183
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\217\215\350\275\254\345\255\227\347\254\246\344\270\262\344\270\255\347\232\204\345\205\203\351\237\263\345\255\227\346\257\215_345_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.array.反转字符串中的元音字母_345_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/reverse-vowels-of-a-string/
+ * 编号:345
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:对撞指针条件交换元素
+ * 元音字母:a,e,i,o,u 大小写
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public String reverseVowels(String s) {
+ int l = 0, r = s.length() - 1;
+ char[] chars = s.toCharArray();
+ while (l < r) {
+ while (l < r && !valid(s.charAt(l))) {
+ l++;
+ }
+ while (l < r && !valid(s.charAt(r))) {
+ r--;
+ }
+ if (l < r) {// 需要判断
+ char c = chars[l];
+ chars[l] = chars[r];
+ chars[r] = c;
+ l++;
+ r--;
+ }
+ }
+ return new String(chars);
+ }
+
+ private boolean valid(char c) {
+ return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
+ c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U';
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals("holle", new Solution1().reverseVowels("hello"));
+ Assert.assertEquals("leotcede", new Solution1().reverseVowels("leetcode"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..bdba81f3
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,53 @@
+package cn.lastwhisper.leetcode.array.合并两个有序数组_88_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/merge-sorted-array/
+ * 编号:88
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:数组有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * 归并排序,归的过程。
+ * (1)从前到后比较(找小)复制只用 nums1,需要移动 nums1 中的元素,不如创建中间数据
+ * (2)从后到前比较(找大)复制只用 nums1,无需移动 nums1 中的元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public void merge(int[] nums1, int m, int[] nums2, int n) {
+ // 暂存 nums1 和 nums2 的数据
+ int[] temp = new int[m + n];
+ int p1 = 0, p2 = 0, t = 0;
+ // 将 nums1 和 nums2 有序复制到 temp,直到某个数组复制完
+ while (p1 < m && p2 < n) {
+ if (nums1[p1] > nums2[p2]) {
+ temp[t] = nums2[p2++];
+ } else {
+ temp[t] = nums1[p1++];
+ }
+ t++;
+ }
+ // 将 nums1 剩余元素复制到 temp
+ while (p1 < m) {
+ temp[t++] = nums1[p1++];
+ }
+ // 将 nums2 剩余元素复制到 temp
+ while (p2 < n) {
+ temp[t++] = nums2[p2++];
+ }
+ // 将 temp 所有元素复制到 nums1
+ System.arraycopy(temp, 0, nums1, 0, temp.length);
+ }
+
+ public static void main(String[] args) {
+ int[] nums1 = {1, 2, 3, 0, 0, 0}, nums2 = {2, 5, 6};
+ int m = 3, n = 3;
+ new Solution1().merge(nums1, m, nums2, n);
+
+ Assert.assertArrayEquals(new int[]{1, 2, 2, 3, 5, 6}, nums1);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..72a5f842
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204_88_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.array.合并两个有序数组_88_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/merge-sorted-array/
+ * 编号:88
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:数组有序
+ * -------------------------------------------------------------------
+ * 思路:
+ * 从后向前复制
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void merge(int[] nums1, int m, int[] nums2, int n) {
+ int p1 = m - 1, p2 = n - 1, total = m + n - 1;
+ // 将 num1 和 num2 中最大的复制到 nums1的最后面
+ while (p1 >= 0 && p2 >= 0) {
+ nums1[total--] = nums1[p1] > nums2[p2] ? nums1[p1--] : nums2[p2--];
+ }
+ // 如果 num1 还没复制完,不用管,因为num1自身就是有序的
+ // 如果 num2 还没复制完,把剩余复制进去
+ while (p2 >= 0) {
+ nums1[total--] = nums2[p2--];
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] nums1 = {1, 2, 3, 0, 0, 0}, nums2 = {2, 5, 6};
+ int m = 3, n = 3;
+ new Solution2().merge(nums1, m, nums2, n);
+
+ Assert.assertArrayEquals(new int[]{1, 2, 2, 3, 5, 6}, nums1);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..11993f9d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,38 @@
+package cn.lastwhisper.leetcode.array.多数元素_169_简单;
+
+import org.junit.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/majority-element/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:hash表
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int majorityElement(int[] nums) {
+ int half = nums.length / 2;
+ Map map = new HashMap<>();
+ for (int num : nums) {
+ Integer count = map.get(num);
+ count = count == null ? 1 : ++count;
+ map.put(num, count);
+ if (count > half) {
+ return num;
+ }
+ }
+ return 0;
+ }
+
+ public static void main(String[] args) {
+ //Assert.assertEquals(3, new Solution1().majorityElement(new int[]{3, 2, 3}));
+ Assert.assertEquals(2, new Solution1().majorityElement(new int[]{2, 2, 1, 1, 1, 2, 2}));
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..791be615
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\244\232\346\225\260\345\205\203\347\264\240_169_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.array.多数元素_169_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/majority-element/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:摩尔投票法
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int majorityElement(int[] nums) {
+ int candidate = 0;
+ int count = 0;
+ for (int num : nums) {
+ if (count == 0) {
+ candidate = num;
+ }
+ count += candidate == num ? 1 : -1;
+ }
+ return candidate;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(3, new Solution2().majorityElement(new int[]{3, 2, 3}));
+ Assert.assertEquals(2, new Solution2().majorityElement(new int[]{2, 2, 1, 1, 1, 2, 2}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..2a96a74a
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.array.将数组分成和相等的三个部_1013_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:三个和相等的非空部分,0~i == i+1~j == j-1~length-1。
+ * 观察规律:
+ * 1.分成三等分,说明sum%3==0。
+ * 2.sum%3==0有没有可能不能划分成三等分?
+ * 有可能,比如[6,7,8],必须把8拆一份给6才行,这是不允许的。
+ * 坑:
+ * 非空部分!!!不一定要把数组里面值全部都使用
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean canThreePartsEqualSum(int[] A) {
+ int sum = 0;
+ for (int a : A) {
+ sum += a;
+ }
+ if (sum % 3 != 0) {
+ return false;
+ }
+ // 双指针
+ int left = 0, right = A.length - 1;
+ int peer = sum / 3, leftSum = A[left], rightSum = A[right];
+ // 使用left + 1 < right 的原因,防止只能将数组分成两个部分
+ // 例如:[1,-1,1,-1],使用left < right作为判断条件就会出错
+ while (left + 1 < right) {
+ if (leftSum == peer && rightSum == peer) {
+ return true;
+ }
+ while (left + 1 < right && leftSum != peer) {
+ left++;
+ leftSum += A[left];
+ }
+ while (left + 1 < right && rightSum != peer) {
+ right--;
+ rightSum += A[right];
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = new int[]{0,2,1,-6,6,-7,9,1,2,0,1};
+ //int[] nums = new int[]{0, 2, 1, -6, 6, 7, 9, -1, 2, 0, 1};
+ //int[] nums = new int[]{3,3,6,5,-2,2,5,1,-9,4};
+ int[] nums = new int[]{3, 3, 6, 5, -2, 2, 5, 1, -9, 4};
+ System.out.println(new Solution1().canThreePartsEqualSum(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..35b02ccb
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\345\260\206\346\225\260\347\273\204\345\210\206\346\210\220\345\222\214\347\233\270\347\255\211\347\232\204\344\270\211\344\270\252\351\203\250_1013_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.array.将数组分成和相等的三个部_1013_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-array-into-three-parts-with-equal-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean canThreePartsEqualSum(int[] A) {
+ int sum = 0;
+ for (int a : A) {
+ sum += a;
+ }
+ if (sum % 3 != 0) {
+ return false;
+ }
+
+ int freq = 0;
+ int count = 0;
+ for (int a : A) {
+ count += a;
+ if (count == sum / 3) {
+ freq++;
+ count = 0;
+ }
+ }
+ // [10,-10,10,-10,10,-10,10,-10]
+ return freq >= 3;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = new int[]{0,2,1,-6,6,-7,9,1,2,0,1};
+ //int[] nums = new int[]{0, 2, 1, -6, 6, 7, 9, -1, 2, 0, 1};
+ //int[] nums = new int[]{3,3,6,5,-2,2,5,1,-9,4};
+ int[] nums = new int[]{3, 3, 6, 5, -2, 2, 5, 1, -9, 4};
+ System.out.println(new Solution2().canThreePartsEqualSum(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..72bd4c28
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,65 @@
+package cn.lastwhisper.leetcode.array.找到字符串中所有字母异位词_438_中等;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
+ * 编号:438
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(滑动窗口)
+ * (1)双指针 l、r 维护一个滑动窗口,初始窗口扩容至模式串 p 的长度
+ * (2)判断当前 [l,r] 是否满足 异位词,如果满足,记录当前 l 的下标,
+ * (3)窗口整体右移
+ * (4)循环2、3,条件 r 未越界,r 未越界,l 肯定未越界
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2) //n 是 s 的长度、m 是 p 的长度
+ * 空间复杂度:O(1)
+ */
+ public List findAnagrams(String s, String p) {
+ List result = new ArrayList<>();
+ // 特判
+ if (s == null || s.length() < p.length()) {
+ return result;
+ }
+ // 直接让 [l,r] 满足模式串长度
+ int l = 0, r = p.length() - 1;
+ int[] visited = new int[26];
+
+ while (r < s.length()) {
+ // 检查是否是异位词
+ if (anagram(s, l, r, p, visited)) {
+ result.add(l);
+ }
+ l++;
+ r++;
+ }
+ return result;
+ }
+
+ public boolean anagram(String s, int l, int r, String p, int[] visited) {
+ Arrays.fill(visited, 0);
+
+ for (int i = l; i <= r; i++) {
+ visited[s.charAt(i) - 'a']++;
+ visited[p.charAt(i - l) - 'a']--;
+ }
+
+ for (int count : visited) {
+ if (count != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ String s = "cbaebabacd", p = "abc";
+ //String s = "abab", p = "ab";
+ System.out.println(new Solution1().findAnagrams(s, p));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..c3aaa4dd
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,73 @@
+package cn.lastwhisper.leetcode.array.找到字符串中所有字母异位词_438_中等;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
+ * 编号:438
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(滑动窗口)
+ * (1)双指针 l、r 维护一个滑动窗口进行遍历主串 s,
+ * window[] hash 表用于记录 [l,r] 窗口中某字符次数,是否满足模式串 s 中字符次数,
+ * total 用于记录 [l,r] 窗口各个字符数量,是否满足模式串 s 中字符数量,
+ * need[] hash 表用于记录模式串 s 中各个字符数量,
+ * (2)
+ * (3)
+ * (4)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public List findAnagrams(String s, String p) {
+ if (s == null || p == null || s.length() < p.length()) {
+ return new ArrayList<>();
+ }
+ List result = new ArrayList<>();
+ int l = 0, r = 0, total = p.length();
+ int[] window = new int[26];
+ int[] need = new int[26];
+ for (int i = 0; i < p.length(); i++) {
+ need[p.charAt(i) - 'a']++;
+ }
+ while (r < s.length()) {
+ int cr = s.charAt(r) - 'a';
+ // s 中的字符,是否存在 p 中
+ // 如果存在,window 记录该字符出现次数,该字符出现次数小于等于 p 中该字符数,total--
+ if (need[cr] > 0) {
+ window[cr]++;
+ if (window[cr] <= need[cr]) {
+ total--;
+ }
+ }
+ // [l,r] 中包含了 p 中所有字符
+ while (total == 0) {
+ // 必须是和 p 的长度相同才可以算字母异位词
+ // 比如 s=ceba p=abc 也满足 total==0 ,但是并不是异位词
+ if (r - l == p.length() - 1) {
+ result.add(l);
+ }
+ // 缩小窗口,剔除 l 所指向的字符次数,total++
+ int cl = s.charAt(l) - 'a';
+ if (need[cl] > 0) {
+ window[cl]--;
+ if (window[cl] < need[cl]) {
+ total++;
+ }
+ }
+ l++;
+ }
+ r++;
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ String s = "cbaebabacd", p = "abc";
+ //String s = "abab", p = "ab";
+ System.out.println(new Solution2().findAnagrams(s, p));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/backup.md" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/backup.md"
new file mode 100644
index 00000000..90baddd1
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\211\276\345\210\260\345\255\227\347\254\246\344\270\262\344\270\255\346\211\200\346\234\211\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_438_\344\270\255\347\255\211/backup.md"
@@ -0,0 +1,139 @@
+第一步:
+```java
+
+int l = 0, r = 0;
+ int[] visit = new int[26];
+while (l < s.length()) {
+ if (r < s.length() && r - l < p.length() && !anagram(s, l, r, p, visit)) {
+ r++;
+ } else {
+ if (anagram(s, l, r, p, visit)) {
+ result.add(l);
+ }
+ l++;
+ }
+ }
+
+
+public boolean anagram(String s, int l, int r, String p, int[] visit) {
+ if (r - l != p.length()) {
+ return false;
+ }
+...
+}
+```
+
+第二步:
+
+思路:双指针(滑动窗口)
+(1)双指针 l、r 维护一个滑动窗口,窗口的长度不大于模式串 p 的长度
+(2)判断当前 [l,r] 满足 异位词
+(3)如果不满足,窗口向右扩容
+(4)如果满足,记录当前 l 的下标,窗口向右缩小
+(5)循环2、3、4,条件 l 未越界
+
+```java
+int l = 0, r = 0;
+ int[] visit = new int[26];
+if (!anagram(s, l, r, p, visit)) {
+ // [l,r]不满足异位词且窗口大小不满足,扩展右边界
+ if ((r - l+1) != p.length()) {
+ r++;
+ } else {
+ // [l,r]不满足异位词且窗口大小满足,扩展左边界
+ l++;
+ }
+ } else {
+ result.add(l);
+ l++;
+ }
+```
+
+第三步:
+
+
+```java
+
+int l = 0, r = 0;
+ int[] visit = new int[26];
+while (l < s.length() && r < s.length()) {
+ // [l,r]不满足窗口大小,扩展右边界
+ if ((r - l + 1) != p.length()) {
+ r++;
+ } else {
+ // [l,r]满足窗口大小,检查是否是异位词
+ if (anagram(s, l, r, p, visit)) {
+ result.add(l);
+ }
+ l++;
+ }
+ }
+
+```
+
+第四步:
+
+```java
+int l = 0, r = 0;
+ int[] visit = new int[26];
+while (l < s.length() && r < s.length()) {
+ // [l,r]不满足模式串长度,扩展右边界
+ while ((r - l + 1) != p.length()) {
+ r++;
+ }
+ // 此时窗口肯定满足,模式串的长度了,但是有可能越界了
+ if (r < s.length()) {
+ // 检查是否是异位词
+ if (anagram(s, l, r, p, visit)) {
+ result.add(l);
+ }
+ l++;
+ }
+ }
+
+```
+第五步:
+
+```java
+
+int l = 0, r = 0;
+ int[] visit = new int[26];
+ while (l < s.length() && r < s.length()) {
+ // [l,r]不满足模式串长度,扩展右边界到窗口最大长度
+ // (r - l + 1) != p.length() ==》 推导 r = p.length() + l - 1;
+ r = p.length() + l - 1;
+ // 此时窗口肯定满足,模式串的长度了,但是有可能越界了
+ if (r < s.length()) {
+ // 检查是否是异位词
+ if (anagram(s, l, r, p, visit)) {
+ result.add(l);
+ }
+ l++;
+ }
+ }
+
+```
+第六步:
+
+```java
+public List findAnagrams(String s, String p) {
+ List result = new ArrayList<>();
+ // 过滤肯定不符合规则的数据
+ if (s == null || s.length() < p.length()) {
+ return result;
+ }
+ // 直接让 [l,r] 满足模式串长度
+ int l = 0, r = p.length() - 1;
+ int[] visit = new int[26];
+
+ while (r < s.length()) {
+ // 检查是否是异位词
+ if (anagram(s, l, r, p, visit)) {
+ result.add(l);
+ }
+ l++;
+ r++;
+ }
+ return result;
+ }
+```
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/README.md" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/README.md"
new file mode 100644
index 00000000..bfb64b8d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/README.md"
@@ -0,0 +1,24 @@
+分类:稳定性、排序原则、存储器不同
+
+按稳定性:
+
+1. 稳定:冒泡排序、插入排序、归并排序、基数排序、桶排序、计数排序
+
+2. 不稳定:选择排序、希尔排序、堆排序、快速排序
+
+按排序原则:
+1. 插入排序:插入排序、希尔排序
+2. 交换排序:冒泡排序、快速排序
+3. 选择排序:选择排序、堆排序
+4. 归并排序:2-路归并排序
+5. 分配排序:基数排序
+
+ (1)插入类:将无序子序列中的一个或几个记录“插入”到有序序列中,从而增加记录的有序子序列的长度。
+ (2)交换类:通过“交换”无序序列中的记录从而得到其中关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。
+ (3)选择类:从记录的无序子序列中“选择”关键字最小或最大的记录,并将它加入到有序子序列中,以此方法增加记录的有序子序列的长度。
+ (4)归并类:通过“归并”两个或两个以上的记录有序子序列,逐步增加记录有序序列的长度。
+ (5)分配类:是唯一一类不需要进行关键字之间比较的排序方法,排序时主要利用分配和收集两种基本操作来完成。
+
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/BubbleSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/BubbleSort.java"
new file mode 100644
index 00000000..bab4a394
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/BubbleSort.java"
@@ -0,0 +1,40 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.交换;
+
+import org.junit.Assert;
+
+class BubbleSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 冒泡排序——稳定
+ * https://zh.wikipedia.org/wiki/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ // 标识数组是否无序
+ boolean exchanged = true;
+ for (int i = 0; exchanged && i < nums.length; i++) {
+ exchanged = false;
+ // 每次将最大值交换到无序数组最后的位置
+ for (int j = 0; j < nums.length - 1 - i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ int temp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = temp;
+ exchanged = true;
+ }
+ }
+ }
+ return nums;
+ }
+
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new BubbleSort().sortArray(new int[]{5, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/Quick2Sort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/Quick2Sort.java"
new file mode 100644
index 00000000..341d33c9
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/Quick2Sort.java"
@@ -0,0 +1,73 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.交换;
+
+import org.junit.Assert;
+
+class Quick2Sort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 快速排序——不稳定
+ * https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F
+ *
+ * 快速排序(Quick Sort)是由冒泡排序改进而得的。在冒泡排序过程中,只对相邻的两个记录进行比较,因此每次交换两个相邻记录时只能消除一个逆序。
+ * 如果能通过两个(不相邻)记录的一-次交换,消除多个逆序,则会大大加快排序的速度。快速排序方法中的一次交换可能消除多个逆序。
+ *
+ * 基准值可以选择第一个值、中间值、最后一个值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(nlogn)
+ */
+ public int[] sortArray(int[] nums) {
+ quickSort(nums, 0, nums.length - 1);
+ return nums;
+ }
+
+ public void quickSort(int[] nums, int left, int right) {
+ if (left < right) {// 长度大于1
+ int pivotLoc = partition(nums, left, right);//中轴下标
+ quickSort(nums, left, pivotLoc - 1);
+ quickSort(nums, pivotLoc + 1, right);
+ }
+ }
+
+ /**
+ * 选择第一个值、中间值、最后一个值为中轴,这里选择第一个值
+ * 在[low,high]范围内,中轴左边的值都小于中轴,中轴右边的值都大于中轴
+ *
+ * @param nums 待排序数组
+ * @param low 排序起始位置
+ * @param high 排序终止位置
+ * @return int 中轴下标
+ */
+ private int partition(int[] nums, int low, int high) {
+ int pivot = nums[low];
+ while (low < high) {
+ while (low < high && nums[high] >= pivot) high--;
+ nums[low] = nums[high];
+ while (low < high && nums[low] <= pivot) low++;
+ nums[high] = nums[low];
+ }
+ nums[low] = pivot;
+ return low;
+ }
+ // 选最后一个值
+ //private int partition(int[] nums, int low, int high) {
+ // int pivot = nums[high];
+ // while (low < high) {
+ // while (low < high && nums[low] <= pivot) low++;
+ // nums[high] = nums[low];
+ // while (low < high && nums[high] >= pivot) high--;
+ // nums[low] = nums[high];
+ // }
+ // nums[high] = pivot;
+ // return high;
+ //}
+
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new Quick2Sort().sortArray(new int[]{5, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/QuickSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/QuickSort.java"
new file mode 100644
index 00000000..4968ccac
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\344\272\244\346\215\242/QuickSort.java"
@@ -0,0 +1,69 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.交换;
+
+import org.junit.Assert;
+
+class QuickSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 快速排序——不稳定
+ * https://zh.wikipedia.org/wiki/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F
+ *
+ * 快速排序(Quick Sort)是由冒泡排序改进而得的。在冒泡排序过程中,只对相邻的两个记录进行比较,因此每次交换两个相邻记录时只能消除一个逆序。
+ * 如果能通过两个(不相邻)记录的一-次交换,消除多个逆序,则会大大加快排序的速度。快速排序方法中的一次交换可能消除多个逆序。
+ *
+ * 基准值可以选择第一个值、中间值、最后一个值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(nlogn)
+ */
+ public int[] sortArray(int[] nums) {
+ quickSort(nums, 0, nums.length - 1);
+ return nums;
+ }
+
+ public void quickSort(int[] nums, int left, int right) {
+ if (left < right) {// 长度大于1
+ int pivotLoc = partition(nums, left, right);//中轴下标
+ quickSort(nums, left, pivotLoc - 1);
+ quickSort(nums, pivotLoc + 1, right);
+ }
+ }
+
+ /**
+ * 选择第一个值、中间值、最后一个值为中轴,这里选择最后一个值
+ * 在[start,end]范围内,中轴左边的值都小于中轴,中轴右边的值都大于中轴
+ *
+ * @param nums 待排序数组
+ * @param leftBound 排序起始位置
+ * @param rightBound 排序终止位置
+ * @return int 中轴下标
+ */
+ // 分区
+ private static int partition(int[] nums, int leftBound, int rightBound) {
+ int left = leftBound;
+ int pivot = nums[rightBound];
+ int right = rightBound - 1;
+ while (left <= right) {// left <= right取等,[4,6]时进不去while
+ while (left <= right && nums[left] <= pivot) left++;
+ while (left <= right && nums[right] > pivot) right--;
+ if (left < right) swap(nums, left, right);
+ }
+ swap(nums, left, rightBound);
+ return left;
+ }
+
+ private static void swap(int[] arr, int i, int j) {
+ int temp = arr[i];
+ arr[i] = arr[j];
+ arr[j] = temp;
+ }
+
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new QuickSort().sortArray(new int[]{5, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/CountSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/CountSort.java"
new file mode 100644
index 00000000..625e4898
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/CountSort.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.分配;
+
+class CountSort {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 计数排序——稳定
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int[] sortArray(int[] nums) {
+ int max = -50001, min = 50001;
+ for (int num : nums) {
+ max = Math.max(num, max);
+ min = Math.min(num, min);
+ }
+
+ int[] counter = new int[max - min + 1];
+ for (int num : nums) {
+ counter[num - min]++;
+ }
+
+ int idx = 0;
+ for (int num = min; num <= max; num++) {
+ int cnt = counter[num - min];
+ while (cnt-- > 0) {
+ nums[idx++] = num;
+ }
+ }
+ return nums;
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/RadixSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/RadixSort.java"
new file mode 100644
index 00000000..d9afe66d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\210\206\351\205\215/RadixSort.java"
@@ -0,0 +1,67 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.分配;
+
+import org.junit.Assert;
+
+class RadixSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 基数排序——稳定
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ int[][] bucket = new int[10][nums.length];
+ // 桶元素计数器,记录每个桶中存放了多少数据
+ int[] bucketElementCount = new int[10];
+ // 原arr下标
+ int arrIndex;
+ // 除数
+ int divisor = 1;
+ // 最大值
+ int max = nums[0];
+ // 由于基数排序次数由最大数值的位数决定,所以需要求最大值
+ for (int i = 1; i < nums.length; i++) {
+ if (max < nums[i]) {
+ max = nums[i];
+ }
+ }
+
+ // 最大值得位数,即排序次数
+ int maxLength = (max + "").length();
+ for (int f = 0; f < maxLength; f++) {
+ for (int num : nums) {
+ // digitOfElement对应放在哪个bucket中
+ int digitOfElement = num / divisor % 10;
+ // 放在第digitOfElement个bucket的第bucketElementCount[digitOfElement]下标位置
+ bucket[digitOfElement][bucketElementCount[digitOfElement]] = num;
+ // 下次放在bucket的第bucketElementCount[digitOfElement]++下标位置
+ bucketElementCount[digitOfElement]++;
+ }
+
+ arrIndex = 0;
+ // 将桶中数据放回原数组
+ for (int i = 0; i < bucketElementCount.length; i++) {
+ // 对应桶中有数据
+ if (bucketElementCount[i] != 0) {
+ for (int j = 0; j < bucketElementCount[i]; j++) {
+ nums[arrIndex++] = bucket[i][j];
+ }
+ }
+ // 将桶元素计数器清空
+ bucketElementCount[i] = 0;
+ }
+ divisor *= 10;
+ }
+ return nums;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new RadixSort().sortArray(new int[]{5, 2, 3, 1}));
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 5}, new RadixSort().sortArray(new int[]{5, 1, 1, 2, 0, 0}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\275\222\345\271\266/MergeSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\275\222\345\271\266/MergeSort.java"
new file mode 100644
index 00000000..3b8bde6b
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\345\275\222\345\271\266/MergeSort.java"
@@ -0,0 +1,89 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.归并;
+
+import org.junit.Assert;
+
+class MergeSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 归并排序——稳定
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ mergeSort(nums, 0, nums.length - 1, new int[nums.length]);
+ return nums;
+ }
+
+ /**
+ * 归并排序
+ * 分
+ *
+ * @param arr 排序的原始数组
+ * @param left 左边有序序列的初始索引
+ * @param right 右边索引
+ * @param temp 中转数组
+ */
+ public void mergeSort(int[] arr, int left, int right, int[] temp) {
+ if (left < right) {
+ int mid = (left + right) / 2;
+ // 向左分
+ mergeSort(arr, left, mid, temp);
+ // 向右分
+ mergeSort(arr, mid + 1, right, temp);
+ // 治
+ merge(arr, left, mid, right, temp);
+ }
+ }
+
+ /**
+ * 治
+ * @param arr 排序的原始数组 arr[left...mid] arr[mid+1...right]
+ * @param left 左边有序序列的初始索引
+ * @param mid 中间索引
+ * @param right 右边索引
+ * @param temp 中转数组
+ */
+ public void merge(int[] arr, int left, int mid, int right, int[] temp) {
+ int i = left; //左边有序序列的初始索引
+ int j = mid + 1; // 右边有序序列的初始化序索引
+ int t = 0; // 指向temp数组的当前位置
+
+ // 1)、将左右两边(有序)的数据按照规则填充到temp数组,任意一边处理完毕为止
+ while (i <= mid && j <= right) {
+ if (arr[i] < arr[j]) {
+ // 左小于右,将左数组第i个元素copy到temp数组的第t个位置
+ temp[t++] = arr[i++];
+ } else {
+ // 右小于或等于左,将右数组第j个元素copy到temp数组的第t个位置
+ temp[t++] = arr[j++];
+ }
+ }
+
+ // 2)、把有剩余数据的一边的数据依次填充到temp
+ while (i <= mid) {
+ // 说明 左数组还有剩余元素
+ temp[t++] = arr[i++];
+ }
+ while (j <= right) {
+ // 说明 右数组还有剩余元素
+ temp[t++] = arr[j++];
+ }
+
+ // 3)、将temp数组的数据copy到arr,[left,right]是每次copy的范围
+ for (int k = left, l = 0; k <= right; k++, l++) {
+ arr[k] = temp[l];
+ }
+
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new MergeSort().sortArray(new int[]{5, 2, 3, 1}));
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 5}, new MergeSort().sortArray(new int[]{5, 1, 1, 2, 0, 0}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/InsertSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/InsertSort.java"
new file mode 100644
index 00000000..13f03c56
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/InsertSort.java"
@@ -0,0 +1,36 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.插入;
+
+import org.junit.Assert;
+
+class InsertSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 插入排序——稳定
+ * https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ for (int i = 1; i < nums.length; i++) {
+ int num = nums[i];// 无序数组第一个值,待插入数据
+ int idx = i - 1;// 有序数组最后一个值下标
+ // 把待插入的位置腾出来
+ while (idx >= 0 && nums[idx] > num) {
+ nums[idx + 1] = nums[idx];
+ idx--;
+ }
+ nums[idx + 1] = num;
+ }
+ return nums;
+ }
+
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new InsertSort().sortArray(new int[]{5, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/ShellSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/ShellSort.java"
new file mode 100644
index 00000000..a0c870e9
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\346\217\222\345\205\245/ShellSort.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.插入;
+
+import org.junit.Assert;
+
+class ShellSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 希尔排序——不稳定
+ * https://zh.wikipedia.org/wiki/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ int gap = nums.length / 2;
+ int j;
+ int temp;
+ while (gap > 0) {
+ // 对每个数组进行插入排序;由于每个数组并不是真的存在的数组,而是一个大数组
+ // 所以通过gap进行取值
+ for (int i = gap; i < nums.length; i++) {
+ j = i; // 缓存每个数组的起始下标
+ temp = nums[i]; // 缓存每个数组的起始下标的数值
+ if (temp < nums[j - gap]) {
+ while (j - gap >= 0 && temp < nums[j - gap]) {
+ // 从前往后移动
+ nums[j] = nums[j - gap];
+ j = j - gap;
+ }
+ nums[j] = temp;
+ }
+ }
+ gap = gap / 2;
+ }
+ return nums;
+ }
+
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new ShellSort().sortArray(new int[]{5, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/HeapSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/HeapSort.java"
new file mode 100644
index 00000000..39a9521a
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/HeapSort.java"
@@ -0,0 +1,74 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.选择;
+
+import org.junit.Assert;
+
+class HeapSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 堆排序——不稳定
+ * https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F
+ *
+ * 大顶堆升序,小顶堆降序
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ // 1. 满二叉树从右向左、从下往上构建大顶堆;完全二叉树从左向右、从下往上构建大顶堆;
+ // i--意味着,从下到上,每一次都是[i,arr.length]的数组(二叉树)进行重构大顶堆
+ // 将整个数组构建成完整的大顶堆,此时nums[0]是数组最大值
+ for (int i = nums.length / 2 - 1; i >= 0; i--) {
+ adjustHeap(nums, i, nums.length);
+ }
+ // 2. 交换堆顶元素与末尾元素,调整堆结构
+ for (int j = nums.length - 1; j >= 0; j--) {
+ int temp = nums[j];
+ nums[j] = nums[0];
+ nums[0] = temp;
+ adjustHeap(nums, 0, j);
+ }
+
+ return nums;
+ }
+
+
+ /**
+ * 将一个数组(二叉树), 调整成一个大顶堆
+ * 功能: 将以i对应的非叶子节点的树调整成大顶堆
+ * 举例:
+ * i = 1 => {4, 6, 8, 5, 9} => {4, 9, 8, 5, 6}
+ * i = 0 => {4, 9, 8, 5, 6} => {9, 6, 8, 5, 4}
+ * @param arr 待调整的数组
+ * @param i 表示非叶子节点在数组中索引
+ * @param length 表示对多少个元素继续调整, length 是在逐渐的减少
+ */
+ public void adjustHeap(int[] arr, int i, int length) {
+ // 当前非叶子节点值
+ int temp = arr[i];
+ // k = 2*i+1是i节点的左子节点,k+1是右子节点
+ for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {
+ // 找左右子节点最大的值
+ if (k + 1 < length && arr[k] < arr[k + 1]) {
+ k++;
+ }
+ if (arr[k] > temp) {//子节点大于父节点
+ arr[i] = arr[k];//将较大的子节点赋值到当前节点(父节点)
+ i = k;//记录子节点的下标,为了最后 arr[i] = temp;
+ } else {
+ break;//从下至上,下面已经是大顶堆
+ }
+ }
+ //i = k; arr[i]此时已经是子节点的位置了,前面父节点已经拿到了子节点的值了,这里要将子节点的值赋为父节点的值
+ // 当for循环结束后,我们已经将以i为父结点的树的最大值,放在了最顶(局部)
+ arr[i] = temp;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new HeapSort().sortArray(new int[]{5, 2, 3, 1}));
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 5}, new HeapSort().sortArray(new int[]{5, 1, 1, 2, 0, 0}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/SelectSort.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/SelectSort.java"
new file mode 100644
index 00000000..404b1e1a
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\216\222\345\272\217\346\225\260\347\273\204_912_\344\270\255\347\255\211/\351\200\211\346\213\251/SelectSort.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.array.排序数组_912_中等.选择;
+
+import org.junit.Assert;
+
+class SelectSort {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rectangle-overlap/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 选择排序——不稳定
+ * https://zh.wikipedia.org/wiki/%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public int[] sortArray(int[] nums) {
+ for (int i = 0; i < nums.length; i++) {
+
+ int minIdx = i;
+ for (int j = i + 1; j < nums.length; j++) {
+ if (nums[j] < nums[minIdx]) {
+ minIdx = j;
+ }
+ }
+ // 每次找到无序数组最小值的下标,放到已排序序列的末尾
+ if (nums[i] > nums[minIdx]) {
+ int temp = nums[i];
+ nums[i] = nums[minIdx];
+ nums[minIdx] = temp;
+ }
+ }
+ return nums;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertArrayEquals(new int[]{1, 2, 3, 5}, new SelectSort().sortArray(new int[]{5, 2, 3, 1}));
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 5}, new SelectSort().sortArray(new int[]{5, 1, 1, 2, 0, 0}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..7cebc5e1
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,28 @@
+package cn.lastwhisper.leetcode.array.数组中的第K个最大元素_215_中等;
+
+import org.junit.Assert;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
+ * 编号:215
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 升序排序,取n-k个元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*logn)
+ * 空间复杂度:O(1)
+ */
+ public int findKthLargest(int[] nums, int k) {
+ Arrays.sort(nums);
+ return nums[nums.length - k];
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(5, new Solution1().findKthLargest(new int[]{3, 2, 1, 5, 6, 4}, 2));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..fe03e634
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\225\260\347\273\204\344\270\255\347\232\204\347\254\254K\344\270\252\346\234\200\345\244\247\345\205\203\347\264\240_215_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,75 @@
+package cn.lastwhisper.leetcode.array.数组中的第K个最大元素_215_中等;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
+ * 编号:215
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:小顶堆
+ * 利用小顶堆的特性(堆顶元素最小),先对前K个数组元素进行"原地"建小顶堆,建完小顶堆后,堆顶的元素最小,正好是这K个元素的第K大元素。
+ * 然后遍历剩下的元素 nums[k] ~ nums[len-1]
+ * 1、如果比堆顶元素小,跳过
+ * 2、如果比堆顶元素大,和堆顶元素交换后重新堆化
+ * 建堆 buildHeap 时间复杂度 O(K),遍历剩下元素并且堆化 时间复杂度(N-K)*O(logK),总体的时间复杂度 O(NlogK)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(NlogK)
+ * 空间复杂度:O(1)
+ */
+ public int findKthLargest(int[] nums, int k) {
+ //前K个元素原地建小顶堆
+ buildHeap(nums, k);
+ //遍历剩下元素,比堆顶小,跳过;比堆顶大,交换后重新堆化
+ for (int i = k; i < nums.length; i++) {
+ if (nums[i] < nums[0]) continue;
+ swap(nums, i, 0);
+ heapify(nums, k, 0);
+ }
+ //K个元素的小顶堆的堆顶即是第K大元素
+ return nums[0];
+ }
+
+ /**
+ * 建堆函数
+ * 从倒数第一个非叶子节点开始堆化,倒数第一个非叶子节点下标为 K/2-1
+ */
+ public void buildHeap(int[] a, int k) {
+ for (int i = k / 2 - 1; i >= 0; i--) {
+ heapify(a, k, i);
+ }
+ }
+
+ /**
+ * 堆化函数
+ * 父节点下标i,左右子节点的下标分别为 2*i+1 和 2*i+2
+ */
+ public void heapify(int[] a, int k, int i) {
+ //临时变量 minPos 用于存储最小值的下标,先假设父节点最小
+ int minPos = i;
+ while (true) {
+ //和左子节点比较
+ if (i * 2 + 1 < k && a[i * 2 + 1] < a[i]) minPos = i * 2 + 1;
+ //和右子节点比较
+ if (i * 2 + 2 < k && a[i * 2 + 2] < a[minPos]) minPos = i * 2 + 2;
+ //如果minPos没有发生变化,说明父节点已经是最小了,直接跳出
+ if (minPos == i) break;
+ //否则交换
+ swap(a, i, minPos);
+ //父节点下标进行更新,继续堆化
+ i = minPos;
+ }
+ }
+
+ public void swap(int[] arr, int i, int j) {
+ int temp = arr[j];
+ arr[j] = arr[i];
+ arr[i] = temp;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(5, new Solution2().findKthLargest(new int[]{3, 2, 1, 5, 6, 4}, 2));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..2c0f8420
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,64 @@
+package cn.lastwhisper.leetcode.array.无重复字符的最长子串_3_中等;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
+ * 编号:3
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(滑动窗口)
+ * (1)左指针未超出边界,窗口继续扩容或缩小
+ * (2)Hash表每次检查右指针指向的值是否存在
+ * (3)存在则,移除该字符(移除标识),窗口长度减一
+ * (4)不存在,标识该字符已经存在,窗口长度加一
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // 字符串的长度
+ * 空间复杂度:O(1) // 窗口长度
+ */
+ public int lengthOfLongestSubstring(String s) {
+ int[] freq = new int[256];
+ // 窗口 [l,r] 内无重复元素
+ int l = 0, r = 0;
+ int res = 0;
+ while (l < s.length()) {
+ if (r < s.length() && freq[s.charAt(r)] == 0) {
+ freq[s.charAt(r++)]++;
+ } else {
+ freq[s.charAt(l++)]--;
+ }
+ // 这里 r - l 不加一,因为 l、r 初始化值都是0,并且是先用在加的
+ res = Math.max(res, (r - l));
+ }
+ return res;
+ }
+
+ //public int lengthOfLongestSubstring(String s) {
+ // Set hash = new HashSet<>();
+ // // 窗口 [l,r] 内无重复元素
+ // int l = 0, r = 0;
+ // int result = 0;
+ // while (l < s.length()) {
+ // if (r < s.length() && !hash.contains(s.charAt(r))) {
+ // hash.add(s.charAt(r));
+ // r++;
+ // } else {
+ // hash.remove(s.charAt(l));
+ // l++;
+ // }
+ // // 这里 r - l 不加一,因为 l、r 初始化值都是0,并且是先用在加的
+ // result = Math.max(result, r - l);
+ // }
+ // return result;
+ //}
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().lengthOfLongestSubstring("abcabcbb"));
+ System.out.println(new Solution1().lengthOfLongestSubstring("bbbbb"));
+ System.out.println(new Solution1().lengthOfLongestSubstring("pwwkew"));
+ System.out.println(new Solution1().lengthOfLongestSubstring(" "));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..ff9f77a7
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\227\240\351\207\215\345\244\215\345\255\227\347\254\246\347\232\204\346\234\200\351\225\277\345\255\220\344\270\262_3_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,36 @@
+package cn.lastwhisper.leetcode.array.无重复字符的最长子串_3_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
+ * 编号:3
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(滑动窗口)
+ * 优化 Solution1
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // 字符串的长度
+ * 空间复杂度:O(1) // 窗口长度
+ */
+ public int lengthOfLongestSubstring(String s) {
+ byte[] freq = new byte[256];
+ int l = 0, r = 0;
+ int res = 0;
+ while (l < s.length()) {
+ while (r < s.length() && freq[s.charAt(r)] == 0) {
+ freq[s.charAt(r++)]++;
+ }
+ res = Math.max(res, (r - l));
+ freq[s.charAt(l++)]--;
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().lengthOfLongestSubstring("abcabcbb"));
+ System.out.println(new Solution2().lengthOfLongestSubstring("bbbbb"));
+ System.out.println(new Solution2().lengthOfLongestSubstring("pwwkew"));
+ System.out.println(new Solution2().lengthOfLongestSubstring(" "));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution1.java"
new file mode 100644
index 00000000..8026aeef
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution1.java"
@@ -0,0 +1,81 @@
+package cn.lastwhisper.leetcode.array.最小覆盖子串_76_hard;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/minimum-window-substring/
+ * 核心思想:滑动窗口
+ * 超时
+ */
+ public String minWindow(String s, String t) {
+ if (s.length() < t.length()) return "";//"a", "aa"
+ if (s.equals(t)) return s;//"a", "a"
+ // 用于判断s子串是否包含t
+ int[] map = new int[128];
+ String target = "";//"abc", "ac";"a", "b"
+ int l = 0, r = 0;
+ while (l <= s.length() && r <= s.length()) {
+ if (!isSub(s.substring(l, r), t, map)) {
+ // 不断增加 r 指针扩大窗口 [l, r],直到窗口中的字符串符合要求
+ r++;
+ } else {
+ // 不断增加 l 指针缩小窗口 [l, r],直到窗口中的字符串不再符合要求
+ if ("".equals(target)) {
+ target = s.substring(l, r);
+ } else {
+ target = target.length() > s.substring(l, r).length() ? s.substring(l, r) : target;
+ }
+ l++;
+ }
+
+ }
+ return target;
+ }
+
+ /**
+ * sub是否包含t
+ */
+ public boolean isSub(String sub, String t, int[] map) {
+ if (sub.length() < t.length()) return false;//过滤肯定不包含
+ boolean flag = true;
+ for (int i = 0; i < sub.length(); i++) {
+ map[sub.charAt(i)] += 1;
+ }
+ // 检验t在sub是否完全出现过
+ for (int i = 0; i < t.length(); i++) {
+ if (map[t.charAt(i)] > 0) {
+ //t在sub出现过
+ map[t.charAt(i)] -= 1;
+ } else {
+ //t在sub没出现过
+ flag = false;
+ }
+ }
+ // 检验sub是否完全包含t
+ for (int i = 0; i < t.length(); i++) {
+ if (map[t.charAt(i)] < 0) {
+ flag = false;
+ }
+ }
+ // 清空
+ for (int i = 0; i < sub.length(); i++) {
+ map[sub.charAt(i)] = 0;
+ }
+ return flag;
+ }
+
+ public static void main(String[] args) {
+ System.out.println("target:" + new Solution1().minWindow("ADOBECODEBANC", "ABC"));
+ System.out.println("target:" + new Solution1().minWindow("a", "a"));
+ System.out.println("target:" + new Solution1().minWindow("a", "aa"));
+ System.out.println("target:" + new Solution1().minWindow("a", "b"));
+ System.out.println("target:" + new Solution1().minWindow("abc", "ac"));
+ System.out.println("target:" + new Solution1().minWindow("bbaa", "aba"));
+ System.out.println("target:" + new Solution1().minWindow("abbc", "abc"));
+ //int[] arr = new int[128];
+ //System.out.println(new Solution1().isSub("ADCB", "ABC", arr));
+ //System.out.println(new Solution1().isSub("ADQCB", "ABC", arr));
+ //System.out.println(new Solution1().isSub("ADeB", "ABC", arr));
+ //System.out.println(new Solution1().isSub("abbc", "abc", arr));
+ //System.out.println(new Solution1().isSub("bbaa", "aba", arr));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution2.java"
new file mode 100644
index 00000000..91ff8787
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution2.java"
@@ -0,0 +1,82 @@
+package cn.lastwhisper.leetcode.array.最小覆盖子串_76_hard;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/minimum-window-substring/
+ * 核心思想:滑动窗口
+ * 超时
+ */
+ public String minWindow(String s, String t) {
+ if (s.length() < t.length()) return "";//"a", "aa"
+ if (s.equals(t)) return s;//"a", "a"
+ char[] ss = s.toCharArray();
+ // 用于判断s子串是否包含t
+ int[] map = new int[128];
+ String target = "";//"abc", "ac";"a", "b"
+ int l = 0, r = 0;
+ while (l <= ss.length && r <= ss.length) {
+ if (!isSub(ss, l, r, t, map)) {
+ // 不断增加 r 指针扩大窗口 [l, r],直到窗口中的字符串符合要求
+ r++;
+ } else {
+ // 不断增加 l 指针缩小窗口 [l, r],直到窗口中的字符串不再符合要求
+ if ("".equals(target)) {
+ target = s.substring(l, r);
+ } else {
+ target = target.length() > s.substring(l, r).length() ? s.substring(l, r) : target;
+ }
+ l++;
+ }
+
+ }
+ return target;
+ }
+
+ /**
+ * sub是否包含t
+ */
+ public boolean isSub(char[] sub, int start, int end, String t, int[] map) {
+ if (sub.length < t.length()) return false;//过滤肯定不包含
+ boolean flag = true;
+ for (int i = start; i < end; i++) {
+ map[sub[i]] += 1;
+ }
+ // 检验t在sub是否完全出现过
+ for (int i = 0; i < t.length(); i++) {
+ if (map[t.charAt(i)] > 0) {
+ //t在sub出现过
+ map[t.charAt(i)] -= 1;
+ } else {
+ //t在sub没出现过
+ flag = false;
+ }
+ }
+ // 检验sub是否完全包含t
+ for (int i = 0; i < t.length(); i++) {
+ if (map[t.charAt(i)] < 0) {
+ flag = false;
+ }
+ }
+ // 清空
+ for (int i = start; i < end; i++) {
+ map[sub[i]] = 0;
+ }
+ return flag;
+ }
+
+ public static void main(String[] args) {
+ System.out.println("target:" + new Solution2().minWindow("ADOBECODEBANC", "ABC"));
+ System.out.println("target:" + new Solution2().minWindow("a", "a"));
+ System.out.println("target:" + new Solution2().minWindow("a", "aa"));
+ System.out.println("target:" + new Solution2().minWindow("a", "b"));
+ System.out.println("target:" + new Solution2().minWindow("abc", "ac"));
+ System.out.println("target:" + new Solution2().minWindow("bbaa", "aba"));
+ System.out.println("target:" + new Solution2().minWindow("abbc", "abc"));
+ //int[] arr = new int[128];
+ //System.out.println(new Solution2().isSub("ADCB", "ABC", arr));
+ //System.out.println(new Solution2().isSub("ADQCB", "ABC", arr));
+ //System.out.println(new Solution2().isSub("ADeB", "ABC", arr));
+ //System.out.println(new Solution2().isSub("abbc", "abc", arr));
+ //System.out.println(new Solution2().isSub("bbaa", "aba", arr));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution3.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution3.java"
new file mode 100644
index 00000000..5d61574b
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution3.java"
@@ -0,0 +1,87 @@
+package cn.lastwhisper.leetcode.array.最小覆盖子串_76_hard;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution3 {
+ public String minWindow(String s, String t) {
+
+ if (s.length() == 0 || t.length() == 0) {
+ return "";
+ }
+
+ // Dictionary which keeps a count of all the unique characters in t.
+ Map dictT = new HashMap();
+ for (int i = 0; i < t.length(); i++) {
+ int count = dictT.getOrDefault(t.charAt(i), 0);
+ dictT.put(t.charAt(i), count + 1);
+ }
+
+ // Number of unique characters in t, which need to be present in the desired window.
+ int required = dictT.size();
+
+ // Left and Right pointer
+ int l = 0, r = 0;
+
+ // formed is used to keep track of how many unique characters in t
+ // are present in the current window in its desired frequency.
+ // e.g. if t is "AABC" then the window must have two A's, one B and one C.
+ // Thus formed would be = 3 when all these conditions are met.
+ int formed = 0;
+
+ // Dictionary which keeps a count of all the unique characters in the current window.
+ Map windowCounts = new HashMap();
+
+ // ans list of the form (window length, left, right)
+ int[] ans = {-1, 0, 0};
+
+ while (r < s.length()) {
+ // Add one character from the right to the window
+ char c = s.charAt(r);
+ int count = windowCounts.getOrDefault(c, 0);
+ windowCounts.put(c, count + 1);
+
+ // If the frequency of the current character added equals to the
+ // desired count in t then increment the formed count by 1.
+ if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
+ formed++;
+ }
+
+ // Try and co***act the window till the point where it ceases to be 'desirable'.
+ while (l <= r && formed == required) {
+ c = s.charAt(l);
+ // Save the smallest window until now.
+ if (ans[0] == -1 || r - l + 1 < ans[0]) {
+ ans[0] = r - l + 1;
+ ans[1] = l;
+ ans[2] = r;
+ }
+
+ // The character at the position pointed by the
+ // `Left` pointer is no longer a part of the window.
+ windowCounts.put(c, windowCounts.get(c) - 1);
+ if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
+ formed--;
+ }
+
+ // Move the left pointer ahead, this would help to look for a new window.
+ l++;
+ }
+
+ // Keep expanding the window once we are done co***acting.
+ r++;
+ }
+
+ return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
+ }
+
+ public static void main(String[] args) {
+ System.out.println("target:" + new Solution3().minWindow("ADOBECODEBANC", "ABC"));
+ System.out.println("target:" + new Solution3().minWindow("a", "a"));
+ System.out.println("target:" + new Solution3().minWindow("a", "aa"));
+ System.out.println("target:" + new Solution3().minWindow("a", "b"));
+ System.out.println("target:" + new Solution3().minWindow("abc", "ac"));
+ System.out.println("target:" + new Solution3().minWindow("bbaa", "aba"));
+ System.out.println("target:" + new Solution3().minWindow("abbc", "abc"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution4.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution4.java"
new file mode 100644
index 00000000..2c81b182
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\200\345\260\217\350\246\206\347\233\226\345\255\220\344\270\262_76_hard/Solution4.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.array.最小覆盖子串_76_hard;
+
+class Solution4 {
+ public String minWindow(String s, String t) {
+ //把字符串转换为字符数组
+ char[] sChars = s.toCharArray();
+ char[] pChars = t.toCharArray();
+ //
+ int[] pMap = new int[128];
+ //左指针,右指针
+ int i = 0, j = 0; // 考察窗口[i,j-1]
+ //还没有覆盖的字符数量
+ int count = pChars.length;
+
+ //初始化记录答案的变量
+ int minLen = s.length() + 1, l = 0, r = 0;
+
+ //记录t串中每种字符出现的次数
+ for (char pChar : pChars)
+ pMap[pChar]++;
+
+ while (j < sChars.length) {
+
+ //减小计数
+ if (pMap[sChars[j]] > 0)
+ count--;
+ pMap[sChars[j]]--;
+ j++;
+
+ //计数为 0说明区间[i,j-1] 包含 p
+ while (count == 0) {
+
+ //更新答案
+ if (j - i < minLen) {
+ minLen = j - i;
+ l = i;
+ r = j;
+ }
+
+ pMap[sChars[i]]++;
+ // 增加计数
+ if (pMap[sChars[i]] > 0)
+ count++;
+ i++;
+ }
+ }
+ return minLen == s.length() + 1 ? "" : s.substring(l, r);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..9c95a7c8
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.array.有效的字母异位词_242_简单;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ public boolean isAnagram(String s, String t) {
+ char[] sChar = s.toCharArray();
+ char[] tChar = t.toCharArray();
+ HashMap map = new HashMap<>();
+ for (int i = 0; i < sChar.length; i++) {
+ Integer num = map.get(sChar[i]);
+ if (num == null) {
+ map.put(sChar[i], 1);
+ } else {
+ map.put(sChar[i], ++num);
+ }
+ }
+
+ for (int i = 0; i < tChar.length; i++) {
+ Integer num = map.get(tChar[i]);
+ if (num == null) {
+ return false;
+ } else {
+ map.put(tChar[i], --num);
+ }
+ }
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getValue() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ //String s = "anagram", t = "nagaram";
+ String s = "rat", t = "car";
+
+ System.out.println(new Solution1().isAnagram(s, t));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..6c53f39c
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.array.有效的字母异位词_242_简单;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/valid-anagram/submissions/
+ * hash
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isAnagram(String s, String t) {
+ if (s.length() != t.length()) {
+ return false;
+ }
+ char[] sChar = s.toCharArray();
+ char[] tChar = t.toCharArray();
+ byte[] freq = new byte[256];
+ for (int i = 0; i < sChar.length; i++) {
+ freq[sChar[i]]++;
+ }
+
+ for (int i = 0; i < tChar.length; i++) {
+ freq[tChar[i]]--;
+ }
+ for (int i = 0; i < freq.length; i++) {
+ if (freq[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ String s = "anagram", t = "nagaram";
+ //String s = "rat", t = "car";
+
+ System.out.println(new Solution2().isAnagram(s, t));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..5b285110
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,31 @@
+package cn.lastwhisper.leetcode.array.盛最多水的容器_11_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/container-with-most-water/
+ * 编号:11
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力组合高和底
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public int maxArea(int[] height) {
+ int max = Integer.MIN_VALUE, h;
+ for (int i = 0; i < height.length; i++) {
+ for (int j = i + 1; j < height.length; j++) {
+ h = Math.min(height[i], height[j]);// 最小高度
+ max = Math.max(max, h * (j - i));
+ }
+ }
+ return max;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(49, new Solution1().maxArea(new int[]{1, 8, 6, 2, 5, 4, 8, 3, 7}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..2c7c9213
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\233\233\346\234\200\345\244\232\346\260\264\347\232\204\345\256\271\345\231\250_11_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.array.盛最多水的容器_11_中等;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/container-with-most-water/
+ * 编号:11
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:双指针(对撞指针)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int maxArea(int[] height) {
+ int l = 0, r = height.length - 1, max = Integer.MIN_VALUE;
+ while (l < r) {
+ if (height[l] < height[r]) {
+ max = Math.max(max, height[l] * (r - l));
+ l++;
+ } else {
+ max = Math.max(max, height[r] * (r - l));
+ r--;
+ }
+ }
+ return max;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(49, new Solution2().maxArea(new int[]{1, 8, 6, 2, 5, 4, 8, 3, 7}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..08cf49e7
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,44 @@
+package cn.lastwhisper.leetcode.array.移动零_283_简单;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class Solution1 {
+ /**
+ * 题目地址:https://leetcode.cn/problems/move-zeroes/
+ * 编号:283
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public void moveZeroes(int[] nums) {
+ // 缓存非0元素
+ List nonZeroElements = new ArrayList<>();
+ // 找出非0元素并将它缓存进nonZeroElements
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != 0) {
+ nonZeroElements.add(nums[i]);
+ }
+ }
+ // 将非0元素放入原数组中
+ for (int i = 0; i < nonZeroElements.size(); i++) {
+ nums[i] = nonZeroElements.get(i);
+ }
+ // 将原数组剩余的位置放置为0
+ for (int i = nonZeroElements.size(); i < nums.length; i++) {
+ nums[i] = 0;
+ }
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] arr = {0, 1, 0, 3, 12};
+ new Solution1().moveZeroes(arr);
+ System.out.println(Arrays.toString(arr));
+ }
+}
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..b55ff636
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.array.移动零_283_简单;
+
+
+import java.util.Arrays;
+
+public class Solution2 {
+ /**
+ * 题目地址:https://leetcode.cn/problems/move-zeroes/
+ * 编号:283
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void moveZeroes(int[] nums) {
+ // k保证从[0,...,k)均为非零元素
+ int k = 0;
+ // 遍历到第i个元素后,保证[0,...,i]中所有非零元素,
+ // 都按顺序排列在[0,...,k)中
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != 0) {
+ nums[k] = nums[i];
+ k++;
+ }
+ }
+ // 同时保证[k,...,nums.length]中均为零元素
+ for (int i = k; i < nums.length; i++) {
+ nums[i] = 0;
+ }
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ int[] arr = {0, 1, 0, 3, 12};
+ new Solution2().moveZeroes(arr);
+ System.out.println(Arrays.toString(arr));
+ }
+}
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..b96bbb3c
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\345\212\250\351\233\266_283_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,44 @@
+package cn.lastwhisper.leetcode.array.移动零_283_简单;
+
+
+import java.util.Arrays;
+
+public class Solution3 {
+ /**
+ * 题目地址:https://leetcode.cn/problems/move-zeroes/
+ * 编号:283
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:快慢指针
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void moveZeroes(int[] nums) {
+ // k保证从[0,...,k)均为非零元素,k 表示0的索引
+ int k = 0;
+ // temp用于交换时暂存数据
+ int temp;
+ // 遍历到第i个元素后,保证[0,...,i]中所有非零元素,都按顺序排列在[0,...,k)中
+ // 同时保证[k,...,i]中均为零元素
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != 0) {
+ if (i != k) { // 防止数组本身就是“有序”,这里的有序指
+ temp = nums[k];
+ nums[k] = nums[i];
+ nums[i] = temp;
+ }
+ k++;
+ }
+ }
+ }
+
+ //以下为测试代码
+ public static void main(String[] args) {
+ //int[] arr = {0, 1, 0, 3, 12};
+ int[] arr = {0, 0, 0, 0, 0};
+ new Solution3().moveZeroes(arr);
+ System.out.println(Arrays.toString(arr));
+ }
+}
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..e5be0ff3
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.array.移除元素_27_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/min-stack/
+ * 编号:27
+ * -------------------------------------------------------------------
+ * 思考:
+ * 特殊场景1:数组本身“有序”
+ * num=[1,1,1,1,1],Val=1
+ * -------------------------------------------------------------------
+ * 思路:
+ * 快慢指针
+ * 1、记录val的索引slow
+ * 2、遍历,找到非val的索引i后,交换i和pos位置的值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int removeElement(int[] nums, int val) {
+ // 特判
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ // 区间[0,...,slow)均为不等于val的元素,slow为val的索引
+ int slow = 0;
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != val) {
+ if (slow != i) { // 防止数组本身就是“有序”
+ nums[slow] = nums[i];
+ }
+ slow++;
+ }
+ }
+ return slow;
+ }
+
+ public static void main(String[] args) {
+ //Assert.assertEquals(2, new Solution1().removeElement(new int[]{3, 2, 2, 3}, 3));
+ Assert.assertEquals(2, new Solution1().removeElement(new int[]{2, 2, 3, 3}, 3));
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..62da8322
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\247\273\351\231\244\345\205\203\347\264\240_27_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.array.移除元素_27_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/min-stack/
+ * 编号:27
+ * -------------------------------------------------------------------
+ * 思考:
+ * 特殊场景2:数组中只有少量待删除数据
+ * num=[1,2,3,5,4],Val=4
+ * num=[4,1,2,3,5],Val=4
+ * -------------------------------------------------------------------
+ * 思路:
+ * 快慢指针
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int removeElement(int[] nums, int val) {
+ // 特判
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ // 区间[0,...,slow)均为不等于val的元素,slow为val的索引
+ int i = 0;
+ int n = nums.length - 1;
+ while (i < n) {
+ if (nums[i] == val) {
+ nums[i] = nums[n];
+ n--;
+ } else {
+ i++;
+ }
+ }
+
+ return i;
+ }
+
+ public static void main(String[] args) {
+ //Assert.assertEquals(2, new Solution1().removeElement(new int[]{3, 2, 2, 3}, 3));
+ Assert.assertEquals(2, new Solution2().removeElement(new int[]{2, 2, 3, 3}, 3));
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution1.java"
new file mode 100644
index 00000000..c5426aee
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution1.java"
@@ -0,0 +1,61 @@
+package cn.lastwhisper.leetcode.array.缺失的第一个正数_41_困难;
+
+class Solution1 {
+
+ /**
+ * https://leetcode-cn.com/problems/first-missing-positive/
+ * 核心思路:输入数组值映射到数组下标,修改下标对应的值标识该数组值出现过,
+ * 最后统计数组第一个未标识的下标即为结果
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * -------------------------------------------------------------------
+ * 此思路无法应对输入数组中存在一个很大的值
+ */
+ public int firstMissingPositive(int[] nums) {
+ //特殊情况三:输入数组为空
+ if (nums.length <= 0) {
+ return 1;
+ }
+ int max = nums[0];
+ for (int i = 0; i < nums.length; i++) {
+ // 特殊情况一:数组中有负数
+ if (nums[i] < 0) {
+ nums[i] = 0;
+ }
+ if (nums[i] > max) {
+ max = nums[i];
+ }
+ }
+ int[] arr = new int[max + 2];
+ for (int num : nums) {
+ arr[num]++;
+ }
+
+ for (int i = 0; i < arr.length; i++) {
+ // 特殊情况二:输入数组第一个数值大于1
+ if (i == 0 && arr[i] == 0) {
+ continue;
+ }
+ if (arr[i] == 0) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * 题目: 给定一个未排序的整数数组,找出其中没有出现的最小的正整数
+ * 说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
+ */
+ public static void main(String[] args) {
+ //int[] arr = new int[]{1, 2, 0};//3
+ //int[] arr = new int[]{3, 4, -1, 1};//2 //特殊情况一:输入数组中有负数
+ //int[] arr = new int[]{7, 8, 9, 11, 12};//1 //特殊情况二:输入数组第一个数值大于1
+ //int[] arr = new int[]{};//1 //特殊情况三:输入数组为空
+ int[] arr = new int[]{1, 2, 3, 10, 2147483647, 9};//1 //特殊情况四:输入数组值过大
+ System.out.println(new Solution1().firstMissingPositive(arr));
+ //System.out.println(Integer.MAX_VALUE==2147483647);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution2.java"
new file mode 100644
index 00000000..4748e957
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/Solution2.java"
@@ -0,0 +1,80 @@
+package cn.lastwhisper.leetcode.array.缺失的第一个正数_41_困难;
+
+class Solution2 {
+
+ /**
+ * https://leetcode-cn.com/problems/first-missing-positive/
+ * 核心思路:使用数组值当做下标
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ *
+ */
+ public int firstMissingPositive(int[] nums) {
+
+ // 处理负数和0,置为大于输入数组长度的一个整数即可
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] <= 0) {
+ nums[i] = Integer.MAX_VALUE;
+ }
+ }
+
+ // 将所有出现的正整数,且值在数组长度内,置为负数
+ for (int i = 0; i < nums.length; i++) {
+ int absNum = Math.abs(nums[i]);
+ /*
+ * 将[数组值-1]的值当做下标,并将该下标的值置为负,
+ * 没有被置为负数的数组值,说明它没有后一位数
+ *
+ * absNum>nums.length,有三种情况
+ * 1.负数
+ * 2.0
+ * 3.输入数组中本身就有一个很大的值(此时数组肯定不连续)
+ */
+ if (absNum <= nums.length) {
+ /*
+ * 这个负数不能随便写,一定是-nums[absNum - 1]
+ * 比如nums={2,2}
+ * 情况一:nums[absNum - 1] = -Math.abs(nums[absNum - 1])
+ * 第一次循环nums={2,-2},第二次循环还是nums={2,-2}
+ * 情况二:随便写一个负数,nums[absNum - 1] = -1
+ * 第一次循环nums={2,-1},第二次循环还是nums={-1,-1}
+ *
+ */
+ nums[absNum - 1] = -Math.abs(nums[absNum - 1]);
+ }
+ }
+ /*
+ * 处理不连续的数组和连续的数组
+ * (1)长度为5的数组里面有一个100——此时缺失的数一定小于100,
+ * 且为一个没有帮它置为负数的值
+ * (2)长度为100的连续数组,最大值100——此时缺失的数一定为100+1
+ */
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] > 0) {
+ return i + 1;
+ }
+ }
+
+ // 处理空数组
+ return nums.length + 1;
+ }
+
+ /**
+ * 题目: 给定一个未排序的整数数组,找出其中没有出现的最小的正整数
+ * 说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
+ */
+ public static void main(String[] args) {
+ //int[] arr = new int[]{1, 2, 0};//3
+ //int[] arr = new int[]{3, 4, -1, 1};//2 //特殊情况一:输入数组中有负数
+ //int[] arr = new int[]{7, 8, 9, 11, 12};//1 //特殊情况二:输入数组第一个数值大于1
+ //int[] arr = new int[]{};//1 //特殊情况三:输入数组为空
+ //int[] arr = new int[]{1, 2, 3, 10, 2147483647, 9};//1 //特殊情况四:输入数组值过大
+ //int[] arr = new int[]{1};//2
+ //int[] arr = new int[]{0};//1
+ //int[] arr = new int[]{2,2};//1
+ int[] arr = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};//10
+ System.out.println(new Solution2().firstMissingPositive(arr));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/YourVersion1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/YourVersion1.java"
new file mode 100644
index 00000000..84f9bdd0
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\347\274\272\345\244\261\347\232\204\347\254\254\344\270\200\344\270\252\346\255\243\346\225\260_41_\345\233\260\351\232\276/YourVersion1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.array.缺失的第一个正数_41_困难;
+
+class YourVersion1 {
+
+ public int firstMissingPositive(int[] nums) {
+
+ // 处理负数和0
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] <= 0) {
+ nums[i] = Integer.MAX_VALUE;
+ }
+ }
+
+ for (int i = 0; i < nums.length; i++) {
+ int absNum = Math.abs(nums[i]);
+ if (absNum <= nums.length) {
+ nums[absNum - 1] = -Math.abs(nums[absNum - 1]);
+ }
+ }
+
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] > 0) {
+ return i + 1;
+ }
+ }
+
+ return nums.length + 1;
+ }
+
+ /**
+ * 题目: 给定一个未排序的整数数组,找出其中没有出现的最小的正整数
+ * 说明: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
+ */
+ public static void main(String[] args) {
+ //int[] arr = new int[]{1, 2, 0};//3
+ //int[] arr = new int[]{3, 4, -1, 1};//2 //特殊情况一:输入数组中有负数
+ //int[] arr = new int[]{7, 8, 9, 11, 12};//1 //特殊情况二:输入数组第一个数值大于1
+ //int[] arr = new int[]{};//1 //特殊情况三:输入数组为空
+ //int[] arr = new int[]{1, 2, 3, 10, 2147483647, 9};//1 //特殊情况四:输入数组值过大
+ //int[] arr = new int[]{1};//2
+ //int[] arr = new int[]{0};//1
+ int[] arr = new int[]{2, 2};//1
+ System.out.println(new YourVersion1().firstMissingPositive(arr));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\350\236\272\346\227\213\347\237\251\351\230\265_54_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\350\236\272\346\227\213\347\237\251\351\230\265_54_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..494ce3ce
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\350\236\272\346\227\213\347\237\251\351\230\265_54_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.array.螺旋矩阵_54_中等;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/spiral-matrix/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public List spiralOrder(int[][] matrix) {
+ if (matrix == null || matrix.length == 0) return new ArrayList<>();
+ List result = new ArrayList<>(matrix.length * matrix[0].length);
+ // nowColumn当前所在列、totalColumn总列数、nowRow当前所在行、totalRow总行数
+ int nowColumn = 0, totalColumn = matrix[0].length - 1, nowRow = 0, totalRow = matrix.length - 1;
+ // 顺时针遍历
+ while (nowColumn <= totalColumn && nowRow <= totalRow) {
+ //只有一列,不需要从左到右
+ for (int i = nowColumn; i <= totalColumn; i++) {//第一步 从左到右
+ result.add(matrix[nowRow][i]);
+ }
+ //只有一行,不需要从上到下
+ for (int i = nowRow + 1; i <= totalRow; i++) {//第二步 从上到下
+ result.add(matrix[i][totalColumn]);
+ }
+ if (nowRow != totalRow) {//同行,不需要从右向左遍历
+ for (int i = totalColumn - 1; i >= nowColumn; i--) {//第三步 从右向左
+ result.add(matrix[totalRow][i]);
+ }
+ }
+ if (nowColumn != totalColumn) {//同列,不需要从下到上遍历
+ for (int i = totalRow - 1; i > nowRow; i--) {// 第四步 从下到上
+ result.add(matrix[i][nowColumn]);
+ }
+ }
+ nowColumn++;
+ nowRow++;
+ totalColumn--;
+ totalRow--;
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+ System.out.println(new Solution1().spiralOrder(matrix));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..e37115a5
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,62 @@
+package cn.lastwhisper.leetcode.array.错误的集合_645_简单;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/set-mismatch/
+ * -------------------------------------------------------------------
+ * 数据特征:
+ * 输入:数组、1~n整数、无序、存在重复数据、不一定包含丢失数据
+ * 输出:数组[重复数据,丢失数据]、1~n+1整数、
+ * -------------------------------------------------------------------
+ * 思路:
+ * 使用两个set集合,set1找出重复数据,set2记录1~length+1,
+ * set2.removeAll(set1)得到丢失数据。
+ * 特殊情况:[1,1]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int[] findErrorNums(int[] nums) {
+ if (nums.length == 2) {
+ if (nums[0] == 1 && nums[1] == 1) return new int[]{1, 2};
+ }
+
+ Set allSet = new HashSet<>();
+ Set numsSet = new HashSet<>();
+ int duplicate = -1, miss = -1;
+ for (Integer num : nums) {
+ // 找到重复,并将所有元素放到numsSet
+ if (numsSet.contains(num)) {
+ duplicate = num;
+ } else {
+ numsSet.add(num);
+ }
+
+ }
+ for (int i = 1; i <= nums.length + 1; i++) {
+ allSet.add(i);
+ }
+ allSet.removeAll(numsSet);
+ for (Integer num : allSet) {
+ miss = num;
+ break;
+ }
+
+ return new int[]{duplicate, miss};
+ }
+
+ public static void main(String[] args) {
+ // example
+ //int[] arr = {1, 2, 2, 4};//2,3
+ //int[] arr = {2, 2};//2,1
+ // error example
+ //int[] arr = {3, 2, 2};//2,1
+ //int[] arr = {1, 1};//1,2
+ int[] arr = {1, 5, 3, 2, 2, 7, 6, 4, 8, 9};
+ System.out.println(Arrays.toString(new Solution1().findErrorNums(arr)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..b604915c
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.array.错误的集合_645_简单;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/set-mismatch/
+ * -------------------------------------------------------------------
+ * 数据特征:
+ * 输入:数组、1~n整数、无序、存在重复数据、不一定包含丢失数据
+ * 输出:数组[重复数据,丢失数据]、1~n+1整数、
+ * -------------------------------------------------------------------
+ * 思路:
+ * 遍历输入数组,使用map存储数组值出现的频率
+ * 正常数据1~length每一个出现的频率都为1,将1~length每一个值当做key查询map,
+ * frequency=2的为重复数据,frequency=null为丢失数据
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * -------------------------------------------------------------------
+ * 改进:
+ * 将数组值当做数据下标,统计该值出现的频率
+ * map需要使用2n的额外空间,使用额外数组当做map,只需要使用n的额外空间
+ */
+ public int[] findErrorNums(int[] nums) {
+ Map numFreMap = new HashMap<>();
+ int duplicate = -1, miss = -1;
+ for (int num : nums) {
+ numFreMap.put(num, numFreMap.getOrDefault(num, 0) + 1);
+ }
+ for (int i = 1; i <= nums.length; i++) {
+ Integer frequency = numFreMap.get(i);
+ if (frequency == null) {
+ miss = i;
+ } else if (frequency == 2) {
+ duplicate = i;
+ }
+ }
+ return new int[]{duplicate, miss};
+ }
+
+ public static void main(String[] args) {
+ // example
+ //int[] arr = {1, 2, 2, 4};//2,3
+ //int[] arr = {2, 2};//2,1
+ // error example
+ //int[] arr = {3, 2, 2};//2,1
+ //int[] arr = {1, 1};//1,2
+ int[] arr = {1, 5, 3, 2, 2, 7, 6, 4, 8, 9};
+ System.out.println(Arrays.toString(new Solution2().findErrorNums(arr)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..277ee841
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,19 @@
+package cn.lastwhisper.leetcode.array.错误的集合_645_简单;
+
+class Solution3 {
+ public int[] findErrorNums(int[] nums) {
+ int[] copy = new int[nums.length + 1];
+ for (int n : nums) {
+ copy[n]++;
+ }
+
+ int[] res = new int[2];
+ for (int i = 1; i < copy.length; i++) {
+ if (copy[i] == 0)
+ res[1] = i;
+ if (copy[i] >= 2)
+ res[0] = i;
+ }
+ return res;
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..da9c2807
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\224\231\350\257\257\347\232\204\351\233\206\345\220\210_645_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,52 @@
+package cn.lastwhisper.leetcode.array.错误的集合_645_简单;
+
+import java.util.Arrays;
+
+class Solution4 {
+ /**
+ * https://leetcode-cn.com/problems/set-mismatch/
+ * -------------------------------------------------------------------
+ * 数据特征:
+ * 输入:数组、1~n整数、无序、存在重复数据、不一定包含丢失数据
+ * 输出:数组[重复数据,丢失数据]、1~n+1整数、
+ * -------------------------------------------------------------------
+ * 思路:将数组中的每一个值当做下标,按下标-1找到数组对应的值,并更新为负数,
+ * 在更新之前检查这个值是否已经是负数,如果已经是负数,说明这个值是重复数据。
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ * 相似题目:https://leetcode-cn.com/problems/first-missing-positive/
+ */
+ public int[] findErrorNums(int[] nums) {
+ // 丢失数据初始值为1,
+ int duplicate = -1, miss = 1;
+ for (int num : nums) {
+ if (nums[Math.abs(num) - 1] < 0) {
+ // 重复出现的下标
+ duplicate = Math.abs(num);
+ } else {
+ nums[Math.abs(num) - 1] *= -1;
+ }
+ }
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] > 0) {
+ miss = i + 1;
+ }
+ }
+ return new int[]{duplicate, miss};
+ }
+
+ public static void main(String[] args) {
+ // example
+ //int[] arr = {1, 2, 2, 4};//2,3
+ //int[] arr = {2, 2};//2,1
+ // error example
+ //int[] arr = {3, 2, 2};//2,1
+ //int[] arr = {1, 1};//1,2
+ //int[] arr = {1, 5, 3, 2, 2, 7, 6, 4, 8, 9};//10
+ //int[] arr = {2, 3, 2};
+ int[] arr = {3,3,1};
+ System.out.println(Arrays.toString(new Solution4().findErrorNums(arr)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..1c23fde0
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:二层循环暴力组合,一层循环计算组合范围值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(1)
+ */
+ public int minSubArrayLen(int s, int[] nums) {
+ if (s <= 0 || nums == null) {
+ throw new IllegalArgumentException("Illegal Argument");
+ }
+ // 子数组长度有可能等于 nums.length
+ int ans = Integer.MAX_VALUE;
+ for (int i = 0; i < nums.length; i++) {
+ for (int j = i; j < nums.length; j++) {
+ int sum = 0;
+ // 计算 [i,j] 子数组的和
+ for (int k = i; k <= j; k++) {
+ sum += nums[k];
+ }
+ if (sum >= s) {
+ ans = Math.min(ans, j - i + 1);// i=0,j=1 ————> ans=2
+ j = nums.length;// 退出内循环
+ }
+ }
+ }
+ // 子数组长度等于 nums.length+1,说明无解
+ if (ans == Integer.MAX_VALUE)
+ return 0;
+ return ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution1().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..e9082877
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:二层循环暴力组合
+ * 组合的和提前计算
+ * (1)提前将 sum[0...n] 计算出来, sum[0] = 0; sum[i] = sum[i-1] + num[i-1]
+ * (2)nums[i...j] 的和 = sum[j+1]-sum[i]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int minSubArrayLen(int s, int[] nums) {
+ if (s <= 0 || nums == null) {
+ throw new IllegalArgumentException("Illegal Argument");
+ }
+ // nums=> 2, 3, 1, 2, 4, 3
+ // sum=>0, 2, 5, 6, 8, 12, 15
+ // sums[i] 存放 nums[0...i-1] 的和
+ int[] sum = new int[nums.length + 1];
+ sum[0] = 0;
+ for (int i = 1; i <= nums.length; i++) {
+ sum[i] = sum[i - 1] + nums[i - 1];
+ }
+ // 子数组长度有可能等于 nums.length
+ int ans = Integer.MAX_VALUE;
+ for (int i = 0; i < nums.length; i++) {
+ for (int j = i; j < nums.length; j++) {
+ // sum[j+1] - sum[0] = num[0,j]的和,sum[0]==0
+ // sum[j+1] - sum[i] = num[i,j]的和
+ if (sum[j + 1] - sum[i] >= s) {
+ ans = Math.min(ans, j - i + 1);
+ //j = nums.length;// 退出内循环
+ break;
+ }
+ }
+ }
+ // 子数组长度等于 nums.length+1,说明无解
+ if (ans == Integer.MAX_VALUE)
+ return 0;
+ return ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution2().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..f34de047
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+import org.junit.Assert;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * (1)双指针 l、r 维护一个滑动窗口
+ * (2)窗口的左边界在数组范围内,则循环继续
+ * (3)r未到数组尾且 sums 需要动态缩小窗口左边界
+ * (5)循环内每次找 sum >= s 时的最小长度
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // l 和 r 指针同时遍历了一遍数组 O(2n)
+ * 空间复杂度:O(1)
+ */
+ public int minSubArrayLen(int s, int[] nums) {
+ if (s <= 0 || nums == null) {
+ throw new IllegalArgumentException("Illegal Argument");
+ }
+ // 初始窗口 [l,r] 是一个空集,所以 r 初值为 -1,后面需要先加才能使用
+ int l = 0, r = -1;
+ int ans = Integer.MAX_VALUE, sum = 0;
+ // 维护窗口,通过窗口内 sum 与 s 的关系,触发窗口的扩容或缩小
+ // 窗口的左边界在数组范围内,说明窗口还能继续扩容或者缩小
+ while (l < nums.length) {
+ // 因为 r 先加再用所以要保证 r + 1 < nums.length
+ if (r + 1 < nums.length && sum < s) {
+ sum += nums[++r];
+ } else {
+ sum -= nums[l++];
+ }
+ if (sum >= s) {
+ ans = Math.min(ans, (r - l + 1));
+ }
+ }
+ if (ans == Integer.MAX_VALUE) {
+ ans = 0;
+ }
+ return ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution3().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution4.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution4.java"
new file mode 100644
index 00000000..32800e20
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution4.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+import org.junit.Assert;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n) // l 和 r 指针同时遍历了一遍数组 O(2n)
+ * 空间复杂度:O(1)
+ */
+ public int minSubArrayLen(int s, int[] nums) {
+ int l = 0, sum = 0, ans = Integer.MAX_VALUE;
+ for (int r = 0; r < nums.length; r++) {
+ sum += nums[r];
+ while (sum >= s) {
+ ans = Math.min(ans, r - l + 1);
+ sum -= nums[l++];
+ }
+ }
+ return ans == Integer.MAX_VALUE ? 0 : ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution4().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution5.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution5.java"
new file mode 100644
index 00000000..95b5835d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution5.java"
@@ -0,0 +1,69 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+/**
+ * 209. 长度最小的子数组
+ * https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * @author lastwhisper
+ */
+public class Solution5 {
+ // 二分搜索
+ // 扩展 Solution5 的方法。对于每一个l, 可以使用二分搜索法搜索r
+ // 时间复杂度: O(nlogn)
+ // 空间复杂度: O(n)
+ public int minSubArrayLen(int s, int[] nums) {
+ if(s <= 0 || nums == null)
+ throw new IllegalArgumentException("Illegal Arguments");
+
+ // sums[i]存放nums[0...i-1]的和
+ int[] sums = new int[nums.length + 1];
+ sums[0] = 0;
+ for(int i = 1 ; i <= nums.length ; i ++)
+ sums[i] = sums[i-1] + nums[i-1];
+
+ int res = Integer.MAX_VALUE;
+ for(int l = 0 ; l < nums.length - 1 ; l ++){
+ // Java类库中没有内置的lowerBound方法,
+ // 我们需要自己实现一个基于二分搜索的lowerBound:)
+ int r = lowerBound(sums, sums[l] + s);
+ if(r != sums.length){
+ res = Math.min(res, r - l);
+ }
+ }
+
+ if(res == Integer.MAX_VALUE)
+ return 0;
+ return res;
+ }
+
+ // 在有序数组nums中寻找大于等于target的最小值
+ // 如果没有(nums数组中所有值都小于target),则返回nums.length
+ private int lowerBound(int[] nums, int target){
+ if(nums == null /*|| !isSorted(nums)*/)
+ throw new IllegalArgumentException("Illegal argument nums in lowerBound.");
+
+ int l = 0, r = nums.length; // 在nums[l...r)的范围里寻找解
+ while(l != r){
+ int mid = l + (r - l) / 2;
+ if(nums[mid] >= target)
+ r = mid;
+ else
+ l = mid + 1;
+ }
+
+ return l;
+ }
+
+ private boolean isSorted(int[] nums){
+ for(int i = 1 ; i < nums.length ; i ++)
+ if(nums[i] < nums[i-1])
+ return false;
+ return true;
+ }
+
+ public static void main(String[] args) {
+
+ int[] nums = {2, 3, 1, 2, 4, 3};
+ int s = 7;
+ System.out.println((new Solution5()).minSubArrayLen(s, nums));
+ }
+}
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution6.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution6.java"
new file mode 100644
index 00000000..ffb08944
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\225\277\345\272\246\346\234\200\345\260\217\347\232\204\345\255\220\346\225\260\347\273\204_209_\344\270\255\347\255\211/Solution6.java"
@@ -0,0 +1,52 @@
+package cn.lastwhisper.leetcode.array.长度最小的子数组_209_中等;
+
+import org.junit.Assert;
+
+class Solution6 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
+ * 编号:209
+ * -------------------------------------------------------------------
+ * 思考:边界值一定要考虑取还是不取,为什么?
+ * -------------------------------------------------------------------
+ * 思路:滑动窗口
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int minSubArrayLen(int s, int[] nums) {
+ if (s <= 0 || nums == null) {
+ throw new IllegalArgumentException("Illegal Argument");
+ }
+ // 初始窗口 [l,r] 是一个空集,所以 r 初值为 -1,后面需要先加才能使用
+ int l = 0, r = -1;
+ int ans = Integer.MAX_VALUE, sum = 0;
+ // 维护窗口,通过窗口内 sum 与 s 的关系,触发窗口的扩容或缩小
+ // 窗口的右边界在数组范围内,说明窗口还能继续扩容或者缩小
+ while (r + 1 < nums.length) {
+ // 因为 r 先加再用所以 r < nums.length - 1
+ while (r + 1 < nums.length && sum < s) {
+ r++;
+ sum += nums[r];
+ }
+ if (sum >= s) {
+ ans = Math.min(ans, (r + 1 - l));
+ }
+ while (l < nums.length && sum >= s) {
+ sum -= nums[l];
+ l++;
+ if (sum >= s) {
+ ans = Math.min(ans, (r + 1 - l));
+ }
+ }
+ }
+ if (ans == Integer.MAX_VALUE) {
+ ans = 0;
+ }
+ return ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution6().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..17a33f1c
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.array.颜色分类_75_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/sort-colors/
+ * 编号:75
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:只有0,1,2
+ * -------------------------------------------------------------------
+ * 思路:
+ * 计数排序,统计每一个数出现的频率
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void sortColors(int[] nums) {
+ // freq 统计0、1、2的频率
+ int[] freq = new int[3];
+ for (int num : nums) {
+ freq[num]++;
+ }
+ int idx = 0;
+ for (int i = 0; i < freq.length; i++) {
+ while (freq[i]-- > 0) {
+ nums[idx++] = i;
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] arr = new int[]{2, 0, 2, 1, 1, 0};
+ new Solution1().sortColors(arr);
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 2}, arr);
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..d169835d
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\242\234\350\211\262\345\210\206\347\261\273_75_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.array.颜色分类_75_中等;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/sort-colors/
+ * 编号:75
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:只有0,1,2
+ * -------------------------------------------------------------------
+ * 思路:
+ * 三路快速排序的思想,对整个数组只遍历了一遍
+ * (1)双指针 left=-1,right=n
+ * (2)下标 i 遍历数组,num[i]==1,i++
+ * (3)num[i]==0,将 0 移动到数组最前位置,swap(num,++left,i++)
+ * i++,即使 num[++left]==0,这次的交换和位移也是正确的
+ * (4)num[i]==2,将 2 移动到数组最后位置,swap(num,--right,i)
+ * i不变,因为有可能num[--right]==2
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public void sortColors(int[] nums) {
+ int left = -1, right = nums.length;
+ for (int i = 0; i < right; ) {
+ if (nums[i] == 1) {
+ i++;
+ } else if (nums[i] == 0) {
+ swap(nums, ++left, i++);
+ } else if (nums[i] == 2) {
+ swap(nums, --right, i);
+ }
+ }
+ }
+
+ public void swap(int[] num, int i, int j) {
+ if (i != j) {
+ int temp = num[j];
+ num[j] = num[i];
+ num[i] = temp;
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] arr = new int[]{2, 0, 2, 1, 1, 0};
+ new Solution2().sortColors(arr);
+ Assert.assertArrayEquals(new int[]{0, 0, 1, 1, 2, 2}, arr);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..1db21830
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.array.验证回文串_125_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/valid-palindrome/
+ * 编号:125
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 对撞指针遍历比较
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean isPalindrome(String s) {
+ String upperS = s.toUpperCase();
+ int l = 0, r = upperS.length() - 1;
+ // 这里不能等于
+ while (l < r) {
+ while (l < r && !valid(upperS.charAt(l))) {
+ l++;
+ }
+ while (l < r && !valid(upperS.charAt(r))) {
+ r--;
+ }
+ if (upperS.charAt(l) != upperS.charAt(r)) {
+ return false;
+ }
+ l++;
+ r--;
+ }
+ return true;
+ }
+ // 只考虑字母和数字字符
+ private boolean valid(char c) {
+ return ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122));
+ }
+
+ public static void main(String[] args) {
+ //String s = "A man, a plan, a canal: Panama";
+ //String s = "race a car";
+ String s = "a";
+ Assert.assertTrue(new Solution1().isPalindrome(s));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..2720acde
--- /dev/null
+++ "b/algorithms/leetcode/array/src/main/java/cn/lastwhisper/leetcode/array/\351\252\214\350\257\201\345\233\236\346\226\207\344\270\262_125_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.array.验证回文串_125_简单;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/valid-palindrome/
+ * 编号:125
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 对撞指针遍历比较
+ * 统一转成大写:ch & 0b11011111 简写:ch & 0xDF
+ * 统一转成小写:ch | 0b00100000 简写:ch | 0x20
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isPalindrome(String s) {
+ int l = 0, r = s.length() - 1;
+ while (l < r) {// 这里不能等于
+ while (l < r && !valid(s.charAt(l))) l++;
+ while (l < r && !valid(s.charAt(r))) r--;
+ if ((s.charAt(l++) & 0xDF) != (s.charAt(r--) & 0xDF)) return false;
+ }
+ return true;
+ }
+
+ // 只考虑字母和数字字符
+ private boolean valid(char c) {
+ return ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122));
+ }
+
+ public static void main(String[] args) {
+ String s = "A man, a plan, a canal: Panama";
+ //String s = "race a car";
+ //String s = "a";
+ Assert.assertTrue(new Solution2().isPalindrome(s));
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/binarysearch/pom.xml b/algorithms/leetcode/binarysearch/pom.xml
new file mode 100644
index 00000000..2427d876
--- /dev/null
+++ b/algorithms/leetcode/binarysearch/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ binarysearch
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..355ab5b7
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.binarysearch.pow_50_中等;
+
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 一、n=-2147483648无法处理
+ * 二、二分优化n^8=n^4^2=n^2^2^2
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public double myPow(double x, int n) {
+ if (x == 0) {
+ return 0;
+ }
+ // x底数 n指数
+ double result;
+
+ int absExponent = n;
+ if (n < 0) absExponent = -n;
+ result = pow(x, absExponent);
+
+ if (n < 0) {
+ result = 1.0 / result;
+ }
+
+ return result;
+ }
+
+ public double pow(double base, int exponent) {
+ double result = 1.0;
+ for (int i = 0; i < exponent; i++) {
+ result *= base;
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ //System.err.println(new Solution1().myPow(2.00000, 2));
+ //System.err.println(new Solution1().myPow(2.00000, -2));
+ //System.err.println(new Solution1().myPow(2.00000, 0));
+ System.err.println(new Solution1().myPow(0, 0));
+ System.err.println(new Solution1().myPow(-1, 0));
+ System.err.println(new Solution1().myPow(2.00000, 10));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..46b08d04
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.binarysearch.pow_50_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public double myPow(double x, int n) {
+ double res = 1.0;
+ for (int i = n; i != 0; i /= 2) {
+ if (i % 2 != 0) {
+ res *= x;
+ }
+ x *= x;
+ }
+ return n < 0 ? 1 / res : res;
+ }
+
+ public static void main(String[] args) {
+ //System.err.println(new Solution2().myPow(2.00000, 2));
+ //System.err.println(new Solution2().myPow(2.00000, -2));
+ //System.err.println(new Solution2().myPow(2.00000, 0));
+ //System.err.println(new Solution2().myPow(0, 0));
+ //System.err.println(new Solution2().myPow(-1, 0));
+ //System.err.println(new Solution2().myPow(2.00000, 10));
+ System.err.println(new Solution2().myPow(2.00000, -2147483648));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..f78d0303
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/pow_50_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,25 @@
+package cn.lastwhisper.leetcode.binarysearch.pow_50_中等;
+
+class Solution3 {
+ public double myPow(double x, int n) {
+ // 指数-偶数
+ if (n == 0) {
+ return 1;
+ }
+ // 指数-奇数
+ if (n == 1) {
+ return x;
+ }
+ // 指数-负数
+ if (n == -1) {
+ return 1 / x;
+ }
+ double half = myPow(x, n / 2);//n>>1
+ double base = myPow(x, n % 2);
+ return half * half * base;
+ }
+
+ public static void main(String[] args){
+ System.err.println(new Solution3().myPow(34.00515,-3));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..74a71577
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,53 @@
+package cn.lastwhisper.leetcode.binarysearch.二分查找_704_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/binary-search/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 二分搜索防加法溢出,遍历和递归两种写法
+ * -------------------------------------------------------------------
+ * 思路:
+ * 遍历
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(logn)
+ * 空间复杂度:O(1)
+ */
+ public int search(int[] nums, int target) {
+ /*
+ * 为什么是[0,nums.length - 1]?
+ * 仅仅是数组范围
+ */
+ int start = 0, end = nums.length - 1, middle;
+ /*
+ * 为什么是end >= start?
+ * 由于middle+1和middle-1的存在,所以合法的[start,end]范围内肯定是待搜索的
+ */
+ while (end >= start) {
+ /*
+ * 如何避免middle = (end + start) / 2;加法溢出?
+ */
+ middle = (end - start) / 2 + start;
+ if (nums[middle] == target) {
+ return middle;
+ } else if (nums[middle] > target) {
+ /*
+ * 为什么middle - 1?(两个方面)
+ * 1.为了跳出while循环
+ * 2.nums[middle] > target时,middle本身不在搜索范围
+ */
+ end = middle - 1;
+ } else {
+ start = middle + 1;
+ }
+ }
+ return -1;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {-1, 0, 3, 5, 9, 12};
+ //int target = 9;
+ int target = 2;
+ System.out.println(new Solution1().search(nums, target));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..62789934
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\344\272\214\345\210\206\346\237\245\346\211\276_704_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.binarysearch.二分查找_704_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/binary-search/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 二分搜索防加法溢出,遍历和递归两种写法
+ * -------------------------------------------------------------------
+ * 思路:
+ * 递归写法
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(logn)
+ * 空间复杂度:O(logn)
+ */
+ public int search(int[] nums, int target) {
+ return binarySearch(nums, target, 0, nums.length - 1);
+ }
+
+ public int binarySearch(int[] nums, int target, int start, int end) {
+ if (end < start) {
+ return -1;
+ }
+ int middle = (end - start) / 2 + start;
+ if (nums[middle] == target) {
+ return middle;
+ } else if (nums[middle] > target) {
+ end = middle - 1;
+ } else {
+ start = middle + 1;
+ }
+ return binarySearch(nums, target, start, end);
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {-1, 0, 3, 5, 9, 12};
+ int target = 9;
+ //int target = 2;
+ System.out.println(new Solution2().search(nums, target));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution1.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution1.java"
new file mode 100644
index 00000000..a54fd952
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution1.java"
@@ -0,0 +1,57 @@
+package cn.lastwhisper.leetcode.binarysearch.寻找旋转排序数组中的最小值II_154_困难;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 输入特征:
+ * 1、两个连续有序部分
+ * 2、nums[0]>=num[n]
+ * 3、数组内无重复
+ * -------------------------------------------------------------------
+ * 思路:
+ * 一、遍历
+ * 二、二分
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(logn)
+ * 空间复杂度:O(n)
+ */
+ public int findMin(int[] nums) {
+ int left = 0, right = nums.length - 1, middle = left;
+
+ while (nums[left] >= nums[right]) {
+ // 找到最小值
+ if (right - left == 1) {
+ middle = right;
+ break;
+ }
+ middle = (right - left) / 2 + left;
+
+ // 无法界定middle属于前数组还是后数组 {1, 0, 1, 1, 1};//01111
+ if (nums[left] == nums[middle] && nums[right] == nums[middle]) {
+ return sequenceSearch(left, right, nums);
+ }
+
+ if (nums[middle] >= nums[left]) {
+ left = middle;
+ } else if (nums[middle] <= nums[right]) {
+ right = middle;
+ }
+ }
+ return nums[middle];
+ }
+
+ // 顺序查找
+ public int sequenceSearch(int left, int right, int[] nums) {
+ int min = nums[left];
+ for (int i = left + 1; i <= right; i++) {
+ if (nums[i] < min) {
+ min = nums[i];
+ }
+ }
+ return min;
+ }
+
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution2.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution2.java"
new file mode 100644
index 00000000..fa277246
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274II_154_\345\233\260\351\232\276/Solution2.java"
@@ -0,0 +1,23 @@
+package cn.lastwhisper.leetcode.binarysearch.寻找旋转排序数组中的最小值II_154_困难;
+
+class Solution2 {
+ public int findMin(int[] nums) {
+ int left = 0, right = nums.length - 1;
+ while (left < right) {
+ int mid = (left + right) / 2;
+ if (nums[mid] > nums[right]) left = mid + 1;
+ else if (nums[mid] < nums[right]) right = mid;
+ else right = right - 1;
+ }
+ return nums[left];
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = new int[]{3, 4, 5, 1, 2};
+ //int[] nums = new int[]{0,0,0,0};
+ int[] nums = new int[]{1, 0, 1, 1, 1};//01111
+ //int[] nums = new int[]{1,1,1,0,1};//01111
+ //int[] nums = new int[]{0, 1, 1, 1, 1};//01111
+ System.err.println(new Solution2().findMin(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274_153_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274_153_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..0bf96aa3
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\345\257\273\346\211\276\346\227\213\350\275\254\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\346\234\200\345\260\217\345\200\274_153_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,47 @@
+package cn.lastwhisper.leetcode.binarysearch.寻找旋转排序数组中的最小值_153_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 输入特征:
+ * 1、两个连续有序部分
+ * 2、nums[0]>num[n]
+ * 3、数组内无重复
+ * -------------------------------------------------------------------
+ * 思路:
+ * 一、遍历
+ * 二、二分
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(logn)
+ * 空间复杂度:O(n)
+ */
+ public int findMin(int[] nums) {
+ int left = 0, right = nums.length - 1, middle = left;
+
+ while (nums[left] > nums[right]) {
+ // 找到最小值
+ if (right - left == 1) {
+ middle = right;
+ break;
+ }
+ middle = (right - left) / 2 + left;
+
+ if (nums[middle] > nums[left]) {
+ // middle在左数组
+ left = middle;
+ } else if (nums[middle] < nums[right]) {
+ // middle在右数组
+ right = middle;
+ }
+ }
+ return nums[middle];
+ }
+
+ public static void main(String[] args) {
+ int[] nums = new int[]{3,4,5,1,2};
+
+ System.err.println(new Solution1().findMin(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..aaa8dfcb
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.binarysearch.搜索插入位置_35_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/search-insert-position/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:线性遍历
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int searchInsert(int[] nums, int target) {
+ for (int i = 0; i < nums.length; i++) {
+ // 起始,中间
+ if (nums[i] >= target) {
+ return i;
+ }
+ }
+ // 最后
+ return nums.length;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 3, 5, 6};
+ int target = 5;
+ System.out.println(new Solution1().searchInsert(nums, target));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..5f51047c
--- /dev/null
+++ "b/algorithms/leetcode/binarysearch/src/main/java/cn/lastwhisper/leetcode/binarysearch/\346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256_35_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.binarysearch.搜索插入位置_35_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/search-insert-position/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:二分查找
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int searchInsert(int[] nums, int target) {
+ int left = 0, right = nums.length - 1;
+ while (left <= right) {
+ int mid = (left + right) / 2;
+ if (nums[mid] == target) {
+ // 中间
+ return mid;
+ } else if (nums[mid] < target) {
+ left = mid + 1;
+ } else {
+ right = mid - 1;
+ }
+ }
+ //起始和最后
+ return left;
+ }
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/binarytree/pom.xml b/algorithms/leetcode/binarytree/pom.xml
new file mode 100644
index 00000000..0d0a8aab
--- /dev/null
+++ b/algorithms/leetcode/binarytree/pom.xml
@@ -0,0 +1,22 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper.leetcode
+ binarytree
+
+
+
+ cn.lastwhisper
+ leetcode-common
+ 1.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..09897e63
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.binarytree.二叉搜索树的最近公共祖先_235_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归
+ * 二叉树排序树:根大于左子树小于右子树
+ * 情况一:pq同时大于root,说明pq的最近公共祖先在右子树
+ * 情况一:pq同时小于root,说明pq的最近公共祖先在左子树
+ * 情况一:pq不同时大于或小于root,说明root为最近公共祖先
+ * pq分别在左右子树,root为最近公共祖先
+ * p在root上,q在左或右子树,p=root为最近公共祖先
+ * q在root上,p在左或右子树,q=root为最近公共祖先
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
+
+ if (p.val > root.val && q.val > root.val) {
+ return lowestCommonAncestor(root.right, p, q);
+ }
+ if (p.val < root.val && q.val < root.val) {
+ return lowestCommonAncestor(root.left, p, q);
+ }
+
+ return root;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().lowestCommonAncestor(
+ createTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5), new TreeNode(2), new TreeNode(8)).val);
+ System.out.println(new Solution1().lowestCommonAncestor(
+ createTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5), new TreeNode(2), new TreeNode(4)).val);
+
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..e12d84ce
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210_235_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.binarytree.二叉搜索树的最近公共祖先_235_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:迭代-找到分隔点
+ * 二叉树排序树:根大于左子树小于右子树
+ * 情况一:pq同时大于root,说明pq的最近公共祖先在右子树
+ * 情况一:pq同时小于root,说明pq的最近公共祖先在左子树
+ * 情况一:pq不同时大于或小于root,说明root为最近公共祖先
+ * pq分别在左右子树,root为最近公共祖先
+ * p在root上,q在左或右子树,p=root为最近公共祖先
+ * q在root上,p在左或右子树,q=root为最近公共祖先
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(N)
+ * 空间复杂度:O(1)
+ */
+ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
+ while (root!=null){
+ if (p.val > root.val && q.val > root.val) {
+ root=root.right;
+ } else if (p.val < root.val && q.val < root.val) {
+ root=root.left;
+ }else {
+ return root;
+ }
+ }
+ return null;
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().lowestCommonAncestor(
+ createTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5), new TreeNode(2), new TreeNode(8)).val);
+ System.out.println(new Solution2().lowestCommonAncestor(
+ createTree(6, 2, 8, 0, 4, 7, 9, null, null, 3, 5), new TreeNode(2), new TreeNode(4)).val);
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\273\223\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273_783_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\273\223\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273_783_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..0de79d03
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\273\223\347\202\271\346\234\200\345\260\217\350\267\235\347\246\273_783_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,60 @@
+package cn.lastwhisper.leetcode.binarytree.二叉搜索树结点最小距离_783_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-distance-between-bst-nodes/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:bst中序遍历就是从小到大
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int minDiffInBST(TreeNode root) {
+ List list = new ArrayList<>();
+ middleDfs(root, list);
+
+ int min = Integer.MAX_VALUE;
+ for (int i = 0; i < list.size() - 1; i++) {
+ min = Math.min(min, list.get(i + 1) - list.get(i));
+ }
+ return min;
+ }
+
+ public void middleDfs(TreeNode root, List list) {
+ if (root == null) {
+ return;
+ }
+ LinkedList stack = new LinkedList<>();
+ while (root != null || !stack.isEmpty()) {
+ while (root != null) {
+ stack.push(root);
+ root = root.left;
+ }
+ root = stack.pop();
+ list.add(root.val);
+ root = root.right;
+ }
+ }
+
+ //public void middleDfs(TreeNode root, List list) {
+ // if (root != null) {
+ // middleDfs(root.left, list);
+ // list.add(root.val);
+ // middleDfs(root.right, list);
+ // }
+ //}
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().minDiffInBST(createTree(4, 2, 6, 1, 3, null, null)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..ce85bab8
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,47 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的所有路径_257_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.print.PrintUtil.printList;
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/binary-tree-paths/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public List binaryTreePaths(TreeNode root) {
+ List result = new ArrayList<>();
+ if (root == null) {
+ return result;
+ }
+ binaryTreePathsHelper(root, result, String.valueOf(root.val));
+ return result;
+ }
+
+ public void binaryTreePathsHelper(TreeNode root, List result, String path) {
+ if (root.left == null && root.right == null) {
+ result.add(path);
+ }
+ if (root.left != null) {
+ binaryTreePathsHelper(root.left, result, path + "->" + root.left.val);
+ }
+ if (root.right != null) {
+ binaryTreePathsHelper(root.right, result, path + "->" + root.right.val);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ printList(new Solution1().binaryTreePaths(createTree(1, 2, 3, null, 5)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..b5aa12b8
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\211\200\346\234\211\350\267\257\345\276\204_257_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,52 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的所有路径_257_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import javafx.util.Pair;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.print.PrintUtil.printList;
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/binary-tree-paths/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public List binaryTreePaths(TreeNode root) {
+ List result = new ArrayList<>();
+ if (root == null) {
+ return result;
+ }
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, String.valueOf(root.val)));
+
+ while (!stack.isEmpty()) {
+ Pair pair = stack.pop();
+ root = pair.getKey();
+ String path = pair.getValue();
+ if (root.left == null && root.right == null) {
+ result.add(path);
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, path + "->" + root.left.val));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, path + "->" + root.right.val));
+ }
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ printList(new Solution2().binaryTreePaths(createTree(1, 2, 3, null, 5)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..a64d7d02
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,33 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的最大深度_104_简单;
+
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:递归结束条件?
+ * A:当前TreeNode为null
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:不平衡O(n)、平衡O(logn)
+ */
+ public int maxDepth(TreeNode root) {
+ // 递归结束条件
+ if (root == null) {
+ return 0;
+ }
+ // 递归过程
+ return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().maxDepth(TreeUtil.createTree()));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..9f1c06d8
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246_104_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,47 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的最大深度_104_简单;
+
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:不平衡O(n)、平衡O(logn)
+ */
+ public int maxDepth(TreeNode root) {
+ LinkedList> stack = new LinkedList<>();
+ if (root != null) {
+ stack.push(new Pair<>(root, 0));
+ }
+ int maxDepth = 0;
+ // stack模拟递归系统栈
+ while (!stack.isEmpty()) {
+ Pair pair = stack.poll();
+ root = pair.getKey();
+ Integer height = pair.getValue();
+ // dfs过程中更新最大深度
+ maxDepth = Math.max(maxDepth, height);
+ if (root != null) {
+ stack.push(new Pair<>(root.right, height + 1));
+ stack.push(new Pair<>(root.left, height + 1));
+ }
+ }
+
+ return maxDepth;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().maxDepth(TreeUtil.createTree()));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..71dd7713
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,40 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的最小深度_111_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:何为二叉树的深度?
+ * A:从根节点到叶子节点的路径上的节点数量
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int minDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ // 叶子节点
+ if ((root.left == null) && (root.right == null)) {
+ return 1;
+ }
+
+ int min_depth = Integer.MAX_VALUE;
+ if (root.left != null) {
+ min_depth = Math.min(minDepth(root.left), min_depth);
+ }
+ if (root.right != null) {
+ min_depth = Math.min(minDepth(root.right), min_depth);
+ }
+ return min_depth + 1;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().minDepth(TreeUtil.createTree()));
+ }
+}
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..ccba9434
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,57 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的最小深度_111_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:何为二叉树的深度?
+ * A:从根节点到叶子节点的路径上的节点数量
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:最坏O(n),可能保存整颗树
+ */
+ public int minDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, 1));
+
+ int minDepth = Integer.MAX_VALUE;
+ while (!stack.isEmpty()) {
+ Pair pair = stack.pop();
+ root = pair.getKey();
+ Integer depth = pair.getValue();
+ if (root.left == null && root.right == null) {
+ minDepth = Math.min(minDepth, depth);
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, depth + 1));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, depth + 1));
+ }
+ }
+ return minDepth;
+ }
+
+ public static void main(String[] args) {
+ /*
+ * 3
+ * / \
+ * 9 20
+ * / \
+ * 15 7
+ */
+ System.out.println(new Solution2().minDepth(TreeUtil.createTree(3,9,20,null,null,15,7)));
+ }
+}
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..27c4da84
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246_111_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,60 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的最小深度_111_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:从根节点到叶子节点的路径上的节点数量。BFS按照树的层次去迭代,
+ * 第一个访问到的叶子就是最小深度的节点,这样就不要遍历所有的节点了。
+ * -------------------------------------------------------------------
+ * 思路:BFS迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:最多访问n/2个节点,O(n)
+ * 空间复杂度:最多队列中保存n/2个节点,O(n)
+ */
+ public int minDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ LinkedList> queue = new LinkedList<>();
+ queue.add(new Pair<>(root, 1));
+
+ // 当前深度
+ int currentDepth = 0;
+ Pair pair = null;
+ while (!queue.isEmpty()) {
+ pair = queue.poll();
+ root = pair.getKey();
+ currentDepth = pair.getValue();
+ // 找到层中第一个叶子节点
+ if (root.left == null && root.right == null) {
+ break;
+ }
+ if (root.left != null) {
+ queue.add(new Pair<>(root.left, currentDepth + 1));
+ }
+ if (root.right != null) {
+ queue.add(new Pair<>(root.right, currentDepth + 1));
+ }
+
+ }
+ return currentDepth;
+ }
+
+ public static void main(String[] args) {
+ /*
+ * 3
+ * / \
+ * 9 20
+ * / \
+ * 15 7
+ */
+ System.out.println(new Solution2().minDepth(TreeUtil.createTree(3,9,20,null,null,15,7)));
+ }
+}
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204_543_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204_543_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..99d1d4aa
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\344\272\214\345\217\211\346\240\221\347\232\204\347\233\264\345\276\204_543_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.binarytree.二叉树的直径_543_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import cn.lastwhisper.leetcode.common.tree.TreeUtil;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/diameter-of-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ *
+ * -------------------------------------------------------------------
+ * 思路:
+ * 1.普通情况,找左子树和右子树最大高度和
+ * 2.特殊情况,不一定经过根节点
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int diameterOfBinaryTree(TreeNode root) {
+ int max = 0;
+ if (root != null) {
+ max = Math.max(max, sumHeight(root));
+ max = Math.max(max, diameterOfBinaryTree(root.left));
+ max = Math.max(max, diameterOfBinaryTree(root.right));
+ }
+ return max;
+ }
+
+ // node结点左右子树最大高度和
+ private int sumHeight(TreeNode node) {
+ if (node == null) {
+ return 0;
+ }
+ return height(node.left) + height(node.right);
+ }
+
+ // node结点最大高度
+ private int height(TreeNode node) {
+ if (node == null) {
+ return 0;
+ }
+ return Math.max(height(node.left), height(node.right)) + 1;
+ }
+
+ public static void main(String[] args) {
+ Integer[] nums = new Integer[]{4, -7, -3, null, null, -9, -3, 9, -7, -4, null, 6, null, -6, -6, null, null, 0, 6, 5, null, 9, null, null, -1, -4, null, null, null, -2};
+ TreeNode tree = TreeUtil.createTree(nums);
+ System.out.println(new Solution1().diameterOfBinaryTree(tree));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\215\225\350\257\215\347\232\204\345\216\213\347\274\251\347\274\226\347\240\201_820_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\215\225\350\257\215\347\232\204\345\216\213\347\274\251\347\274\226\347\240\201_820_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..691c0a9d
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\215\225\350\257\215\347\232\204\345\216\213\347\274\251\347\274\226\347\240\201_820_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,66 @@
+package cn.lastwhisper.leetcode.binarytree.单词的压缩编码_820_中等;
+
+import org.junit.Assert;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/short-encoding-of-words/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 按长度排序,逆序插入字典树,
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int minimumLengthEncoding(String[] words) {
+ int len = 0;
+ // 先对单词列表根据单词长度由长到短排序
+ Arrays.sort(words, (s1, s2) -> s2.length() - s1.length());
+ Trie trie = new Trie();
+ // 单词插入trie,返回该单词增加的编码长度
+ for (String word : words) {
+ len += trie.insert(word);
+ }
+ return len;
+ }
+
+ static class TrieNode {
+ TrieNode[] children = new TrieNode[26];
+ public TrieNode() {}
+ }
+
+ static class Trie {
+
+ private TrieNode root;
+
+ public Trie() {
+ root = new TrieNode();
+ }
+
+ public int insert(String word) {
+ TrieNode cur = root;
+ boolean isNew = false;
+ // 倒着插入单词
+ for (int i = word.length() - 1; i >= 0; i--) {
+ int c = word.charAt(i) - 'a';
+ if (cur.children[c] == null) {
+ isNew = true;
+ cur.children[c] = new TrieNode();
+ }
+ cur = cur.children[c];
+ }
+ // +1是#
+ return isNew ? word.length() + 1 : 0;
+ }
+ }
+
+ public static void main(String[] args) {
+
+ Assert.assertEquals(10,new Solution1().minimumLengthEncoding(new String[]{"time", "me", "bell"}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..a3f262df
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,28 @@
+package cn.lastwhisper.leetcode.binarytree.完全二叉树的节点个数_222_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:遍历时计数-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:最坏O(n)
+ */
+ public int countNodes(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ return countNodes(root.left) + countNodes(root.right) + 1;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().countNodes(createTree(1, 2, 3, 4, 5, 6, null)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..06775da1
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,40 @@
+package cn.lastwhisper.leetcode.binarytree.完全二叉树的节点个数_222_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:遍历时计数-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:最坏O(n)
+ */
+ public int countNodes(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ LinkedList stack = new LinkedList<>();
+ stack.push(root);
+ int count = 0;
+ // 根-左-右
+ while (!stack.isEmpty()) {
+ root = stack.pop();
+ count++;
+ if (root.right != null) stack.push(root.right);
+ if (root.left != null) stack.push(root.left);
+ }
+ return count;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().countNodes(createTree(1, 2, 3, 4, 5, 6, null)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..a529f89c
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\214\345\205\250\344\272\214\345\217\211\346\240\221\347\232\204\350\212\202\347\202\271\344\270\252\346\225\260_222_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.binarytree.完全二叉树的节点个数_222_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
+ * -------------------------------------------------------------------
+ * 思考:完全二叉树,左右子树肯定存在一颗满二叉树
+ * -------------------------------------------------------------------
+ * 思路:利用二叉树的特点
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:最坏O(n)
+ */
+ public int countNodes(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int left = maxDepth(root.left);
+ int right = maxDepth(root.right);
+ if (left == right) {
+ /*
+ * 左子树肯定是满二叉树,左子树节点总数=2^left-1,加上root,正好是2^left
+ * 再对右子树进行递归统计。
+ */
+ return (1 << left) + countNodes(root.right);//2^left
+ } else {
+ /*
+ * 右子树肯定是满二叉树,右子树节点总数=2^right-1,加上root,正好是2^right
+ * 再对左子树进行递归统计。
+ */
+ return (1 << right) + countNodes(root.left);
+ }
+ }
+
+ private int maxDepth(TreeNode root) {
+ int level = 0;
+ while (root != null) {
+ level++;
+ root = root.left;
+ }
+ return level;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().countNodes(createTree(1, 2, 3, 4, 5, 6, null)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\236\347\216\260Trie\345\211\215\347\274\200\346\240\221_208_\344\270\255\347\255\211/Trie.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\236\347\216\260Trie\345\211\215\347\274\200\346\240\221_208_\344\270\255\347\255\211/Trie.java"
new file mode 100644
index 00000000..f21bad54
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\256\236\347\216\260Trie\345\211\215\347\274\200\346\240\221_208_\344\270\255\347\255\211/Trie.java"
@@ -0,0 +1,101 @@
+package cn.lastwhisper.leetcode.binarytree.实现Trie前缀树_208_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TrieNode;
+import org.junit.Assert;
+
+class Trie {
+
+ private TrieNode root;
+
+ /**
+ * 功能:
+ * 找到具有同一前缀的全部键值。
+ * 按词典序枚举字符串的数据集。
+ *
+ * 与哈希表相比,Trie 树在存储多个具有相同前缀的键时可以使用较少的空间。
+ * 此时 Trie 树只需要 O(m) 的时间复杂度,其中 m 为键长。而在平衡树中查找键值需要 O(mlogn) 时间复杂度。
+ *
+ * https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trie-qian-zhui-shu-by-leetcode/
+ */
+ public Trie() {
+ root = new TrieNode();
+ }
+
+ /**
+ * 通过搜索 Trie 树来插入一个键。我们从根开始搜索它对应于第一个键字符的链接。有两种情况:
+ * 链接存在。沿着链接移动到树的下一个子层。算法继续搜索下一个键字符。
+ * 链接不存在。创建一个新的节点,并将它与父节点的链接相连,该链接与当前的键字符相匹配。
+ * 重复以上步骤,直到到达键的最后一个字符,然后将当前节点标记为结束节点,算法完成。
+ *
+ * 时间复杂度:O(m),其中 m 为键长。在算法的每次迭代中,我们要么检查要么创建一个节点,直到到达键尾。只需要 m 次操作。
+ * 空间复杂度:O(m)。最坏的情况下,新插入的键和 Trie 树中已有的键没有公共前缀。此时需要添加 m 个结点,使用 O(m) 空间。
+ */
+ public void insert(String word) {
+ TrieNode node = root;
+ for (int i = 0; i < word.length(); i++) {
+ char currentChar = word.charAt(i);
+ if (!node.containsKey(currentChar)) {
+ node.put(currentChar, new TrieNode());
+ }
+ node = node.get(currentChar);
+ }
+ node.setEnd();
+ }
+
+ /**
+ * 功能:查找word在是否在前缀树中
+ *
+ * 每个键在trie中表示为从根到内部节点或叶的路径。我们用第一个键字符从根开始,检查当前节点中与键字符对应的链接。
+ * 有两种情况:
+ * 1.存在链接。我们移动到该链接后面路径中的下一个节点,并继续搜索下一个键字符。
+ * 2.不存在链接。若已无键字符,且当前结点标记为 isEnd,则返回 true。否则有两种可能,均返回 false :
+ * 还有键字符剩余,但无法跟随 Trie 树的键路径,找不到键。
+ * 没有键字符剩余,但当前结点没有标记为 isEnd。也就是说,待查找键只是Trie树中另一个键的前缀。
+ */
+ public boolean search(String word) {
+ TrieNode node = searchPrefix(word);
+ return node != null && node.isEnd();
+ }
+
+ // search a prefix or whole key in trie and
+ // returns the node where search ends
+ /**
+ *
+ * 时间复杂度 : O(m)
+ * 空间复杂度 : O(1)
+ */
+ private TrieNode searchPrefix(String word) {
+ TrieNode node = root;
+ for (int i = 0; i < word.length(); i++) {
+ char curLetter = word.charAt(i);
+ if (node.containsKey(curLetter)) {
+ node = node.get(curLetter);
+ } else {
+ return null;
+ }
+ }
+ return node;
+ }
+
+ /**
+ * 功能:查找prefix在是否在前缀树中
+ *
+ * 时间复杂度 : O(m)
+ * 空间复杂度 : O(1)
+ */
+ public boolean startsWith(String prefix) {
+ TrieNode node = searchPrefix(prefix);
+ return node != null;
+ }
+
+ public static void main(String[] args) {
+ Trie trie = new Trie();
+
+ trie.insert("apple");
+ Assert.assertTrue(trie.search("apple")); // 返回 true
+ Assert.assertFalse(trie.search("app")); // 返回 false
+ Assert.assertTrue(trie.startsWith("app")); // 返回 true
+ trie.insert("app");
+ Assert.assertTrue(trie.search("app")); // 返回 true
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..5f3468c6
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.binarytree.对称二叉树_101_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/symmetric-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n),因为我们遍历整个输入树一次,所以总的运行时间为 O(n),其中 nn 是树中节点的总数。
+ * 空间复杂度:递归调用的次数受树的高度限制。在最糟糕情况下,树是线性的,其高度为 O(n)。
+ * 因此,在最糟糕的情况下,由栈上的递归调用造成的空间复杂度为 O(n)。
+ */
+ public boolean isSymmetric(TreeNode root) {
+ if (root == null) {
+ return true;
+ }
+ return isMirror(root.left, root.right);
+ }
+
+ public boolean isMirror(TreeNode p, TreeNode q) {
+ if (p == null && q == null) {
+ return true;
+ }
+ if (p == null || q == null) {
+ return false;
+ }
+ if (p.val != q.val) {
+ return false;
+ }
+ return isMirror(p.left, q.right) && isMirror(p.right, q.left);
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().isSymmetric(createTree(1, 2, 2, 3, 4, 4, 3))); // true
+ System.out.println(new Solution1().isSymmetric(createTree(1, 2, 2, null, 3, null, 3))); //false
+ System.out.println(new Solution1().isSymmetric(createTree(2, 3, 3, 4, 5, 5))); //false
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..b4e7dd56
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.binarytree.对称二叉树_101_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/symmetric-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSymmetric(TreeNode root) {
+ LinkedList stack = new LinkedList<>();
+ stack.push(root);
+ stack.push(root);
+
+ while (!stack.isEmpty()) {
+ TreeNode p = stack.pop();
+ TreeNode q = stack.pop();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ stack.push(p.left);
+ stack.push(q.right);
+ stack.push(q.left);
+ stack.push(p.right);
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().isSymmetric(createTree(1, 2, 2, 3, 4, 4, 3))); // true
+ System.out.println(new Solution2().isSymmetric(createTree(1, 2, 2, null, 3, null, 3))); //false
+ System.out.println(new Solution2().isSymmetric(createTree(2, 3, 3, 4, 5, 5))); //false
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..5e5b322d
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.binarytree.对称二叉树_101_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/symmetric-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:层次遍历(BFS)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n),因为我们遍历整个输入树一次,所以总的运行时间为 O(n),其中 nn 是树中节点的总数。
+ * 空间复杂度:递归调用的次数受树的高度限制。在最糟糕情况下,树是线性的,其高度为 O(n)。
+ * 因此,在最糟糕的情况下,由栈上的递归调用造成的空间复杂度为 O(n)。
+ */
+ public boolean isSymmetric(TreeNode root) {
+ Queue q = new LinkedList<>();
+ q.add(root);
+ q.add(root);
+ while (!q.isEmpty()) {
+ TreeNode t1 = q.poll();
+ TreeNode t2 = q.poll();
+ if (t1 == null && t2 == null) continue;
+ if (t1 == null || t2 == null) return false;
+ if (t1.val != t2.val) return false;
+ q.add(t1.left);
+ q.add(t2.right);
+ q.add(t1.right);
+ q.add(t2.left);
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().isSymmetric(createTree(1, 2, 2, 3, 4, 4, 3))); // true
+ System.out.println(new Solution3().isSymmetric(createTree(1, 2, 2, null, 3, null, 3))); //false
+ System.out.println(new Solution3().isSymmetric(createTree(2, 3, 3, 4, 5, 5))); //false
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution6.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution6.java"
new file mode 100644
index 00000000..42322e65
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\257\271\347\247\260\344\272\214\345\217\211\346\240\221_101_\347\256\200\345\215\225/Solution6.java"
@@ -0,0 +1,58 @@
+package cn.lastwhisper.leetcode.binarytree.对称二叉树_101_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution6 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/symmetric-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 1.将二叉树的左子节点或者右子节点左右翻转
+ * 2.比较左右子节点是否相同
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSymmetric(TreeNode root) {
+ if (root == null) {
+ return true;
+ }
+ TreeNode right = invertTree(root.right);
+ return isSameTree(root.left, right);
+ }
+
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ // 递归结束条件
+ if (p == null && q == null) {
+ return true;
+ }
+ if (p == null || q == null) {
+ return false;
+ }
+ if (p.val != q.val) {
+ return false;
+ }
+ // 递归体
+ return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
+ }
+
+ public TreeNode invertTree(TreeNode root) {
+ if (root == null)
+ return null;
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = root.left;
+
+ root.left = invertTree(root.right);
+ root.right = invertTree(tempNode);
+ return root;
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution6().isSymmetric(createTree()));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution.java"
new file mode 100644
index 00000000..795ff180
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution.java"
@@ -0,0 +1,29 @@
+package cn.lastwhisper.leetcode.binarytree.左叶子之和_404_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution {
+ public int sumOfLeftLeaves(TreeNode root) {
+ return helper(root);
+ }
+
+ public int helper(TreeNode node) {
+ int sum = 0;
+ if (node == null) {
+ return 0;
+ }
+ if (node.left != null && (node.left.left == null && node.left.right == null)) {
+ sum += node.left.val;
+ }
+ sum += helper(node.left) + helper(node.right);
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ //System.out.println(new Solution().sumOfLeftLeaves(createTree(1, 2, 3, 4, 5)));
+ //System.out.println(new Solution().sumOfLeftLeaves(createTree(1, null, 2)));
+ System.out.println(new Solution().sumOfLeftLeaves(createTree(3, 9, 20, null, null, 15, 7)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..ecc08b4f
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,69 @@
+package cn.lastwhisper.leetcode.binarytree.左叶子之和_404_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/sum-of-left-leaves/
+ * -------------------------------------------------------------------
+ * 思考:左叶子之和
+ * 注意:叶子之和
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int sumOfLeftLeaves(TreeNode root) {
+ return sumOfLeftLeavesHelper(root, false);
+ }
+
+ // 先序遍历求所有左叶子节点值之和
+ public int sumOfLeftLeavesHelper(TreeNode root, boolean flag) {
+ if (root == null) {
+ return 0;
+ }
+ int leave = 0;
+ // 左叶子节点
+ if (flag && root.left == null && root.right == null) {
+ leave = root.val;
+ }
+ int left = sumOfLeftLeavesHelper(root.left, true);
+ int right = sumOfLeftLeavesHelper(root.right, false);
+ return left + right + leave;
+ }
+
+ // 先序遍历求所有叶子节点值之和
+ public int sumOfLeaves(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int leave = 0;
+ // 叶子节点
+ if (root.left == null && root.right == null) {
+ leave = root.val;
+ }
+ int left = sumOfLeaves(root.left);
+ int right = sumOfLeaves(root.right);
+ return left + right + leave;
+ }
+
+ // 先序遍历求所有节点值之和
+ public int sumOfTrees(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int leave = root.val;
+ int left = sumOfTrees(root.left);
+ int right = sumOfTrees(root.right);
+ return left + right + leave;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().sumOfLeftLeaves(createTree(1, 2, 3, 4, 5)));
+ //System.out.println(new Solution1().sumOfLeftLeaves(createTree(1, null, 2)));
+ //System.out.println(new Solution1().sumOfLeftLeaves(createTree(3, 9, 20, null, null, 15, 7)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..188e1cf6
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\267\246\345\217\266\345\255\220\344\271\213\345\222\214_404_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,52 @@
+package cn.lastwhisper.leetcode.binarytree.左叶子之和_404_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/sum-of-left-leaves/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:将递归改成迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int sumOfLeftLeaves(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, false));
+
+ int sum = 0;
+ Boolean flag;
+ while (!stack.isEmpty()) {
+ Pair pair = stack.pop();
+ root = pair.getKey();
+ flag = pair.getValue();
+ if (flag && root.left == null && root.right == null) {
+ sum += root.val;
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, true));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, false));
+ }
+ }
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().sumOfLeftLeaves(createTree(1, 2, 3, 4, 5)));
+ //System.out.println(new Solution1().sumOfLeftLeaves(createTree(1, null, 2)));
+ //System.out.println(new Solution1().sumOfLeftLeaves(createTree(3, 9, 20, null, null, 15, 7)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..24674fc9
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,81 @@
+package cn.lastwhisper.leetcode.binarytree.平衡二叉树_110_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isBalanced(TreeNode root) {
+ //它是一棵空树
+ if (root == null) {
+ return true;
+ }
+ //它的左右两个子树的高度差的绝对值不超过1
+ int leftDepth = getTreeDepth(root.left);
+ int rightDepth = getTreeDepth(root.right);
+ if (Math.abs(leftDepth - rightDepth) > 1) {
+ return false;
+ }
+ //左右两个子树都是一棵平衡二叉树
+ return isBalanced(root.left) && isBalanced(root.right);
+
+ }
+
+ private int getTreeDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int leftDepth = getTreeDepth(root.left);
+ int rightDepth = getTreeDepth(root.right);
+ return Math.max(leftDepth, rightDepth) + 1;
+ }
+
+ public static void main(String[] args) {
+ /*
+ * 1
+ * \
+ * 2
+ * /
+ * 3
+ */
+ //System.out.println(new Solution1().isBalanced(createTree(1, null, 2, null, 3)));//false
+ /*
+ * 3
+ * / \
+ * 9 20
+ * / \
+ * 15 7
+ */
+ //System.out.println(new Solution1().isBalanced(createTree(3, 9, 20, null, null, 15, 7)));//true
+ /*
+ * 1
+ * / \
+ * 2 2
+ * / \
+ * 3 3
+ * / \
+ * 4 4
+ */
+ //System.out.println(new Solution1().isBalanced(createTree(1, 2, 2, 3, null, null, 3, 4, null, null, 4)));//false
+ /*
+ * 1
+ * / \
+ * 2 2
+ * / \
+ * 3 3
+ * / \
+ * 4 4
+ */
+ System.out.println(new Solution1().isBalanced(createTree(1, 2, 2, 3, 3, null, null, 4, 4)));//false
+ }
+}
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..9788109f
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\345\271\263\350\241\241\344\272\214\345\217\211\346\240\221_110_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,79 @@
+package cn.lastwhisper.leetcode.binarytree.平衡二叉树_110_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isBalanced(TreeNode root) {
+ return getTreeDepth(root) != -1;
+ }
+
+ private int getTreeDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ int leftDepth = getTreeDepth(root.left);
+ if (leftDepth == -1) {
+ return -1;
+ }
+ int rightDepth = getTreeDepth(root.right);
+ if (rightDepth == -1) {
+ return -1;
+ }
+ if (Math.abs(leftDepth - rightDepth) > 1) {
+ return -1;
+ }
+ return Math.max(leftDepth, rightDepth) + 1;
+ }
+
+
+ public static void main(String[] args) {
+ /*
+ * 1
+ * \
+ * 2
+ * /
+ * 3
+ */
+ //System.out.println(new Solution2().isBalanced(createTree(1, null, 2, null, 3)));//false
+ /*
+ * 3
+ * / \
+ * 9 20
+ * / \
+ * 15 7
+ */
+ //System.out.println(new Solution2().isBalanced(createTree(3, 9, 20, null, null, 15, 7)));//true
+ /*
+ * 1
+ * / \
+ * 2 2
+ * / \
+ * 3 3
+ * / \
+ * 4 4
+ */
+ //System.out.println(new Solution2().isBalanced(createTree(1, 2, 2, 3, null, null, 3, 4, null, null, 4)));//false
+ /*
+ * 1
+ * / \
+ * 2 2
+ * / \
+ * 3 3
+ * / \
+ * 4 4
+ */
+ System.out.println(new Solution2().isBalanced(createTree(1, 2, 2, 3, 3, null, null, 4, 4)));//false
+ }
+}
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..38996df2
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.binarytree.求根到叶子节点数字之和_129_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/binary-tree-paths/
+ * -------------------------------------------------------------------
+ * 思考:二叉树的所有路径_257
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int sumNumbers(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+ List result = new ArrayList<>();
+ binaryTreePathsHelper(root, result, String.valueOf(root.val));
+ int sum = 0;
+ for (String s : result) {
+ sum += Integer.parseInt(s);
+ }
+ return sum;
+ }
+
+ public void binaryTreePathsHelper(TreeNode root, List result, String path) {
+ if (root.left == null && root.right == null) {
+ result.add(path);
+ }
+ if (root.left != null) {
+ binaryTreePathsHelper(root.left, result, path + root.left.val);
+ }
+ if (root.right != null) {
+ binaryTreePathsHelper(root.right, result, path + root.right.val);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().sumNumbers(createTree(1, 2, 3)));
+ System.out.println(new Solution1().sumNumbers(createTree(4,9,0,5,1)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..a4070533
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\346\261\202\346\240\271\345\210\260\345\217\266\345\255\220\350\212\202\347\202\271\346\225\260\345\255\227\344\271\213\345\222\214_129_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,58 @@
+package cn.lastwhisper.leetcode.binarytree.求根到叶子节点数字之和_129_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import javafx.util.Pair;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/
+ * -------------------------------------------------------------------
+ * 思考:二叉树的所有路径_257
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int sumNumbers(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+
+ List result = new ArrayList<>();
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, String.valueOf(root.val)));
+
+ while (!stack.isEmpty()) {
+ Pair pair = stack.pop();
+ root = pair.getKey();
+ String path = pair.getValue();
+ if (root.left == null && root.right == null) {
+ result.add(path);
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, path + root.left.val));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, path + root.right.val));
+ }
+ }
+
+ int sum = 0;
+ for (String s : result) {
+ sum += Integer.parseInt(s);
+ }
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().sumNumbers(createTree(1, 2, 3)));
+ System.out.println(new Solution2().sumNumbers(createTree(4,9,0,5,1)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..55cb44d5
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,40 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.*;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:递归结束条件?
+ * 1.两个节点都为null,返回true
+ * 2.两个节点有一个不为null,返回false
+ * 3.两个节点值不同,返回false
+ * -------------------------------------------------------------------
+ * 思路:先序遍历
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ // 递归结束条件
+ if (p == null && q == null) {
+ return true;
+ }
+ if (p == null || q == null) {
+ return false;
+ }
+ if (p.val != q.val) {
+ return false;
+ }
+ // 递归体
+ return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().isSameTree(createTree(), createTree()));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..17868463
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,75 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:思路三对条件进行优化
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * 情况一:q或p为null false
+ * 情况二:q与p为null true
+ * 情况二:q!=p true
+ * 情况三:q左子树或p左子树为null false
+ * 情况四:q右子树或p右子树为null false
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+
+ if (p == null && q == null) {
+ return true;
+ }
+ if (p == null || q == null) {
+ return false;
+ }
+
+ LinkedList pStaCK = new LinkedList<>();
+ LinkedList qStaCK = new LinkedList<>();
+ pStaCK.push(p);
+ qStaCK.push(q);
+ while (!pStaCK.isEmpty() && !qStaCK.isEmpty()) {
+ p = pStaCK.pop();
+ q = qStaCK.pop();
+ if (p.val != q.val) {
+ return false;
+ }
+
+ //p、q左子树有一个为null
+ if ((p.left == null && q.left != null) || (p.left != null && q.left == null)) {
+ return false;
+ }
+ //p、q右子树有一个为null
+ if ((p.right == null && q.right != null) || (p.right != null && q.right == null)) {
+ return false;
+ }
+
+ if (p.left != null) {
+ pStaCK.push(p.left);
+ }
+ if (p.right != null) {
+ pStaCK.push(p.right);
+ }
+ if (q.left != null) {
+ qStaCK.push(q.left);
+ }
+ if (q.right != null) {
+ qStaCK.push(q.right);
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().isSameTree(createTree(1, 2, 3), createTree(1, 2, 3)));
+ System.out.println(new Solution2().isSameTree(createTree(1, 2), createTree(1, null, 2)));
+ System.out.println(new Solution2().isSameTree(createTree(1, 2, 1), createTree(1, 1, 2)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..6bc50c58
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ LinkedList pStack = new LinkedList<>();
+ LinkedList qStack = new LinkedList<>();
+ pStack.push(p);
+ qStack.push(q);
+ while (!pStack.isEmpty() && !qStack.isEmpty()) {
+ p = pStack.pop();
+ q = qStack.pop();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ pStack.push(p.left);
+ pStack.push(p.right);
+ qStack.push(q.left);
+ qStack.push(q.right);
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().isSameTree(createTree(1, 2, 3), createTree(1, 2, 3)));
+ System.out.println(new Solution3().isSameTree(createTree(1, 2), createTree(1, null, 2)));
+ System.out.println(new Solution3().isSameTree(createTree(1, 2, 1), createTree(1, 1, 2)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..bbf69363
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ LinkedList stack = new LinkedList<>();
+ stack.push(p);
+ stack.push(q);
+ while (!stack.isEmpty()) {
+ p = stack.pop();
+ q = stack.pop();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ stack.push(p.left);
+ stack.push(q.left);
+ stack.push(p.right);
+ stack.push(q.right);
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution4().isSameTree(createTree(1, 2, 3), createTree(1, 2, 3)));
+ System.out.println(new Solution4().isSameTree(createTree(1, 2), createTree(1, null, 2)));
+ System.out.println(new Solution4().isSameTree(createTree(1, 2, 1), createTree(1, 1, 2)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution5.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution5.java"
new file mode 100644
index 00000000..5e70cdcd
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution5.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution5 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:层次遍历-迭代
+ * 使用两个队列分别遍历两颗树
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ LinkedList pQueue = new LinkedList<>();
+ LinkedList qQueue = new LinkedList<>();
+ pQueue.add(p);
+ qQueue.add(q);
+ while (!pQueue.isEmpty() && !qQueue.isEmpty()) {
+ p = pQueue.poll();
+ q = qQueue.poll();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ pQueue.add(p.left);
+ pQueue.add(p.right);
+ qQueue.add(q.left);
+ qQueue.add(q.right);
+ }
+ return true;
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution5().isSameTree(createTree(1, 2, 3), createTree(1, 2, 3)));//true
+ System.out.println(new Solution5().isSameTree(createTree(1, 2), createTree(1, null, 2)));//false
+ System.out.println(new Solution5().isSameTree(createTree(1, 2, 1), createTree(1, 1, 2)));//false
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution6.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution6.java"
new file mode 100644
index 00000000..cfe0f756
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\233\270\345\220\214\347\232\204\346\240\221_100_\347\256\200\345\215\225/Solution6.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.binarytree.相同的树_100_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution6 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/same-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:层次遍历-迭代
+ * 使用1个队列同时遍历两颗树
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public boolean isSameTree(TreeNode p, TreeNode q) {
+ LinkedList queue = new LinkedList<>();
+ queue.add(p);
+ queue.add(q);
+ while (!queue.isEmpty()) {
+ p = queue.poll();
+ q = queue.poll();
+
+ if (p == null && q == null) continue;
+ if (p == null || q == null) return false;
+ if (p.val != q.val) return false;
+
+ queue.add(p.left);
+ queue.add(q.left);
+ queue.add(p.right);
+ queue.add(q.right);
+ }
+ return true;
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution6().isSameTree(createTree(1, 2, 3), createTree(1, 2, 3)));//true
+ System.out.println(new Solution6().isSameTree(createTree(1, 2), createTree(1, null, 2)));//false
+ System.out.println(new Solution6().isSameTree(createTree(1, 2, 1), createTree(1, 1, 2)));//false
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..d1cefea3
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.binarytree.翻转二叉树_226_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.*;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历,递归DFS
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:最坏O(n)、最好O(logn)
+ */
+ public TreeNode invertTree(TreeNode root) {
+ if (root == null)
+ return null;
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = root.left;
+ root.left = root.right;
+ root.right = tempNode;
+ // left与right已经交换,所以right写前面
+ invertTree(root.right);
+ invertTree(root.left);
+ return root;
+ }
+
+ public TreeNode invertTree1(TreeNode root) {
+ if (root == null)
+ return null;
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = root.left;
+ root.left = invertTree1(root.right);
+ root.right = invertTree1(tempNode);
+ return root;
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(4,2,7,1,3,6,9));
+ System.out.println("-------------------------------------");
+ printLevelOrder(new Solution1().invertTree(createTree(4,2,7,1,3,6,9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..14367309
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,44 @@
+package cn.lastwhisper.leetcode.binarytree.翻转二叉树_226_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.*;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:将先序遍历(递归)利用Stack改成迭代。使用Stack模拟系统递归栈
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public TreeNode invertTree(TreeNode root) {
+ LinkedList stack = new LinkedList<>();
+ if (root != null) {
+ stack.push(root);
+ }
+ TreeNode current = null;
+ while (!stack.isEmpty()) {
+ current = stack.poll();
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = current.left;
+ current.left = current.right;
+ current.right = tempNode;
+ // 此时left与right已经交换,栈先进先出,所以入栈顺序left->right
+ if (current.left != null) stack.push(current.left);
+ if (current.right != null) stack.push(current.right);
+ }
+ return root;
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(4,2,7,1,3,6,9));
+ System.out.println("-------------------------------------");
+ printLevelOrder(new Solution2().invertTree(createTree(4,2,7,1,3,6,9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..5fc5055d
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.binarytree.翻转二叉树_226_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.*;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:迭代BFS,层级遍历翻转(二叉树的层次遍历_102)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public TreeNode invertTree(TreeNode root) {
+ Queue queue = new LinkedList<>();
+ if (root != null) {
+ queue.add(root);
+ }
+ TreeNode current = null;
+ while (!queue.isEmpty()) {
+ //int size = queue.size();
+ //for (int i = 0; i < size; i++) {
+ current = queue.poll();
+ // 翻转当前节点的左右子节点
+ TreeNode tempNode = current.left;
+ current.left = current.right;
+ current.right = tempNode;
+
+ if (current.right != null) queue.add(current.right);
+ if (current.left != null) queue.add(current.left);
+ //}
+ }
+ return root;
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(4,2,7,1,3,6,9));
+ System.out.println("-------------------------------------");
+ printLevelOrder(new Solution3().invertTree(createTree(4,2,7,1,3,6,9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..2697a199
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,35 @@
+package cn.lastwhisper.leetcode.binarytree.翻转二叉树_226_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.printLevelOrder;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:中序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public TreeNode invertTree(TreeNode root) {
+ if (root == null) return null;
+ invertTree(root.left); // 递归找到左节点
+ TreeNode rightNode = root.right; // 保存右节点
+ root.right = root.left;
+ root.left = rightNode;
+ // 递归找到右节点 继续交换 : 因为此时左右节点已经交换了,所以此时的右节点为root.left
+ invertTree(root.left);
+ return root;
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(4, 2, 7, 1, 3, 6, 9));
+ System.out.println("-------------------------------------");
+ printLevelOrder(new Solution4().invertTree(createTree(4, 2, 7, 1, 3, 6, 9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution5.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution5.java"
new file mode 100644
index 00000000..60e8195f
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\347\277\273\350\275\254\344\272\214\345\217\211\346\240\221_226_\347\256\200\345\215\225/Solution5.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.binarytree.翻转二叉树_226_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.printLevelOrder;
+
+class Solution5 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:后序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public TreeNode invertTree(TreeNode root) {
+ // 后序遍历-- 从下向上交换
+ if (root == null) return null;
+ TreeNode leftNode = invertTree(root.left);
+ TreeNode rightNode = invertTree(root.right);
+ root.right = leftNode;
+ root.left = rightNode;
+ return root;
+ }
+
+ public static void main(String[] args) {
+ printLevelOrder(createTree(4, 2, 7, 1, 3, 6, 9));
+ System.out.println("-------------------------------------");
+ printLevelOrder(new Solution5().invertTree(createTree(4, 2, 7, 1, 3, 6, 9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..8d057a72
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_112_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归先序遍历递归
+ * 先序遍历,sum-路径上节点的值,减到叶子节点,返回结果是否等于0
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean hasPathSum(TreeNode root, int sum) {
+ // 当前节点不为空
+ if (root == null) {
+ return false;
+ }
+ // 根节点到叶子节点中间路径和等于sum不行,叶子节点为终结才行
+ if (root.left == null && root.right == null) {
+ return sum - root.val == 0;
+ }
+ return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
+ }
+
+ public static void main(String[] args) {
+ TreeNode tree = createTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1);
+ //TreeNode tree = createTree(5, null, 8, null, null, 13, 4);
+ int sum = 22;
+ System.out.println(new Solution1().hasPathSum(tree, sum));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..5e2a58c6
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,52 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_112_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代;
+ * 在根节点到叶子节点的路径总和=sum即可
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean hasPathSum(TreeNode root, int sum) {
+ if (root == null) {
+ return false;
+ }
+ // 节点
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, 0));
+
+ Pair pair;
+ Integer parentVal;
+ while (!stack.isEmpty()) {
+ pair = stack.pop();
+ root = pair.getKey();
+ parentVal = pair.getValue();
+ if (root.left == null && root.right == null) {
+ if (root.val + parentVal == sum) {
+ return true;
+ }
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, root.val + parentVal));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, root.val + parentVal));
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution5.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution5.java"
new file mode 100644
index 00000000..b8b5f2b4
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_112_\347\256\200\345\215\225/Solution5.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_112_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution5 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代;
+ * 在根节点到叶子节点的路径总和=sum即可
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public boolean hasPathSum(TreeNode root, int sum) {
+ if (root == null) {
+ return false;
+ }
+ // 节点
+ LinkedList> stack = new LinkedList<>();
+ stack.push(new Pair<>(root, sum));
+ Pair pair;
+ // 暂存当前位置sum还剩下的值
+ Integer parentSum;
+ while (!stack.isEmpty()) {
+ pair = stack.pop();
+ root = pair.getKey();
+ parentSum = pair.getValue();
+ if (root.left == null && root.right == null) {
+ if (parentSum - root.val == 0) {
+ return true;
+ }
+ }
+ if (root.left != null) {
+ stack.push(new Pair<>(root.left, parentSum - root.val));
+ }
+ if (root.right != null) {
+ stack.push(new Pair<>(root.right, parentSum - root.val));
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ TreeNode tree = createTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1);
+ int sum = 22;
+ System.out.println(new Solution5().hasPathSum(tree, sum));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_III_437_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_III_437_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..b786a6bc
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_III_437_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_III_437_简单;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum-iii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * 找每一个节点的pathSum
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ // 从root找到路径和等于sum的数量
+ public int pathSum(TreeNode root, int sum) {
+ if(root==null){
+ return 0;
+ }
+ int res = findPathSum(root, sum);
+ res+=pathSum(root.left,sum);
+ res+=pathSum(root.right,sum);
+ return res;
+ }
+
+ // 从node找到路径和等于sum的数量
+ public int findPathSum(TreeNode node, int sum) {
+ if (node == null) {
+ return 0;
+ }
+ int res = 0;
+ if (node.val == sum) {
+ res++;
+ }
+ res += findPathSum(node.left, sum - node.val);
+ res += findPathSum(node.right, sum - node.val);
+
+ return res;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().pathSum(createTree(10, 5, -3, 3, 2, null, 11, 3, -2, null, 1), 8));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..704f3ab1
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_II_113_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+import static cn.lastwhisper.leetcode.common.print.PrintUtil.printLists;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum-ii/
+ * -------------------------------------------------------------------
+ * 思考:路径总和(112)+二叉树的所有路劲(257)
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public List> pathSum(TreeNode root, int sum) {
+ List> result = new ArrayList<>();
+ if (root == null) {
+ return result;
+ }
+ pathSumHelper(root, String.valueOf(root.val), root.val, result, sum);
+ return result;
+ }
+
+ public void pathSumHelper(TreeNode root, String path, int partSum, List> result, int sum) {
+ if (root.left == null && root.right == null && partSum == sum) {
+ String[] arr = path.split(",");
+ List list = new ArrayList<>(arr.length);
+ for (String a : arr) {
+ list.add(Integer.parseInt(a));
+ }
+ result.add(list);
+ }
+ if (root.left != null) {
+ pathSumHelper(root.left, path + "," + root.left.val, partSum + root.left.val, result, sum);
+ }
+ if (root.right != null) {
+ pathSumHelper(root.right, path + "," + root.right.val, partSum + root.right.val, result, sum);
+ }
+ }
+
+ public static void main(String[] args) {
+ printLists(new Solution1().pathSum(createTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1), 22));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..c9a5f6ff
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\350\267\257\345\276\204\346\200\273\345\222\214_II_113_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,71 @@
+package cn.lastwhisper.leetcode.binarytree.路径总和_II_113_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+import static cn.lastwhisper.leetcode.common.print.PrintUtil.printLists;
+
+class Solution2 {
+ static class DTO {
+ public TreeNode treeNode;
+ public String path;
+ public Integer sum;
+
+ public DTO(TreeNode treeNode, String path, Integer sum) {
+ this.treeNode = treeNode;
+ this.path = path;
+ this.sum = sum;
+ }
+ }
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/path-sum-ii/
+ * -------------------------------------------------------------------
+ * 思考:路径总和(112)+二叉树的所有路劲(257)
+ * -------------------------------------------------------------------
+ * 思路:先序遍历-迭代
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public List> pathSum(TreeNode root, int sum) {
+ List> result = new ArrayList<>();
+ if (root == null) {
+ return result;
+ }
+ LinkedList stack = new LinkedList<>();
+ stack.push(new DTO(root, String.valueOf(root.val), root.val));
+
+ while (!stack.isEmpty()) {
+ DTO dto = stack.pop();
+ root = dto.treeNode;
+ String path = dto.path;
+ Integer partSum = dto.sum;
+
+ if (root.left == null && root.right == null && partSum == sum) {
+ String[] arr = path.split(",");
+ List list = new ArrayList<>(arr.length);
+ for (String a : arr) {
+ list.add(Integer.parseInt(a));
+ }
+ result.add(list);
+ }
+
+ if (root.left != null) {
+ stack.push(new DTO(root.left, path + "," + root.left.val, partSum + root.left.val));
+ }
+ if (root.right != null) {
+ stack.push(new DTO(root.right, path + "," + root.right.val, partSum + root.right.val));
+ }
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ printLists(new Solution2().pathSum(createTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1), 22));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..5722bed9
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.binarytree.验证二叉搜索树_98_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/validate-binary-search-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:bst中序遍历就是从小到大
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean isValidBST(TreeNode root) {
+ double inorder = -Double.MAX_VALUE;
+ LinkedList stack = new LinkedList<>();
+ while (root != null || !stack.isEmpty()) {
+ while (root != null) {
+ stack.push(root);
+ root = root.left;
+ }
+ root = stack.pop();
+ // 每一个节点值都应该比前一个值大
+ if (root.val <= inorder) return false;
+ inorder = root.val;
+ root = root.right;
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().isValidBST(createTree(5, 1, 4, null, null, 3, 6)));
+ System.out.println(new Solution1().isValidBST(createTree(1, 1)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..dbd6dc14
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,35 @@
+package cn.lastwhisper.leetcode.binarytree.验证二叉搜索树_98_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import static cn.lastwhisper.leetcode.common.tree.TreeUtil.createTree;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/validate-binary-search-tree/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean isValidBST(TreeNode root) {
+ return isValidBST(root, null, null);
+ }
+
+ private boolean isValidBST(TreeNode root, TreeNode min, TreeNode max) {
+ if (root == null) return true;
+ // 右子树>root
+ if (min != null && root.val <= min.val) return false;
+ // 左子树= max.val) return false;
+ return isValidBST(root.left, min, root) && isValidBST(root.right, root, max);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().isValidBST(createTree(5, 1, 4, null, null, 3, 6)));
+ System.out.println(new Solution2().isValidBST(createTree(1, 1)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..8eb75135
--- /dev/null
+++ "b/algorithms/leetcode/binarytree/src/main/java/cn/lastwhisper/leetcode/binarytree/\351\252\214\350\257\201\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221_98_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,36 @@
+package cn.lastwhisper.leetcode.binarytree.验证二叉搜索树_98_中等;
+
+import cn.lastwhisper.leetcode.common.tree.TreeNode;
+
+import java.util.LinkedList;
+
+class Solution3 {
+ private LinkedList stack = new LinkedList<>();
+ private LinkedList uppers = new LinkedList<>();
+ private LinkedList lowers = new LinkedList<>();
+
+ private void update(TreeNode node, Integer lower, Integer upper) {
+ stack.add(node);
+ lowers.add(lower);
+ uppers.add(upper);
+ }
+
+ public boolean isValidBST(TreeNode root) {
+ Integer lower = null, upper = null, val;
+ update(root, lower, upper);
+
+ while (!stack.isEmpty()) {
+ root = stack.poll();
+ lower = lowers.poll();
+ upper = uppers.poll();
+
+ if (root == null) continue;
+ val = root.val;
+ if (lower != null && val <= lower) return false;
+ if (upper != null && val >= upper) return false;
+ update(root.right, val, upper);
+ update(root.left, lower, val);
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/bitoperation/pom.xml b/algorithms/leetcode/bitoperation/pom.xml
new file mode 100644
index 00000000..dc6566c3
--- /dev/null
+++ b/algorithms/leetcode/bitoperation/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ bitoperation
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/bitoperation/src/main/java/cn/lastwhisper/leetcode/bitoperation/\344\275\2151\347\232\204\344\270\252\346\225\260_191_\347\256\200\345\215\225/Solution.java" "b/algorithms/leetcode/bitoperation/src/main/java/cn/lastwhisper/leetcode/bitoperation/\344\275\2151\347\232\204\344\270\252\346\225\260_191_\347\256\200\345\215\225/Solution.java"
new file mode 100644
index 00000000..4980b5ec
--- /dev/null
+++ "b/algorithms/leetcode/bitoperation/src/main/java/cn/lastwhisper/leetcode/bitoperation/\344\275\2151\347\232\204\344\270\252\346\225\260_191_\347\256\200\345\215\225/Solution.java"
@@ -0,0 +1,17 @@
+package cn.lastwhisper.leetcode.bitoperation.位1的个数_191_简单;
+
+class Solution {
+ // you need to treat n as an unsigned value
+ public int hammingWeight(int n) {
+ int count = 0;
+ while (n != 0) {
+ // n=10011101,n-1=10011100
+ // &全1则1,否则0
+ // n & (n - 1)=10011101&10011100=10011100
+ // n=10011100
+ n = n & (n - 1);
+ count++;
+ }
+ return count;
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/dfsbfs/pom.xml b/algorithms/leetcode/dfsbfs/pom.xml
new file mode 100644
index 00000000..7390dea6
--- /dev/null
+++ b/algorithms/leetcode/dfsbfs/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ dfsbfs
+
+
+
+ leetcode-common
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\234\260\345\233\276\345\210\206\346\236\220_1162_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\234\260\345\233\276\345\210\206\346\236\220_1162_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..4a36d1f8
--- /dev/null
+++ "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\234\260\345\233\276\345\210\206\346\236\220_1162_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,55 @@
+package cn.lastwhisper.leetcode.dfsbfs.地图分析_1162_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/as-far-from-land-as-possible/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:广度优先搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int maxDistance(int[][] grid) {
+ int max = 0;
+
+ //boolean[][] visited = new boolean[grid.length][grid[0].length];
+ for (int i = 0; i < grid.length; i++) {
+ for (int j = 0; j < grid[0].length; j++) {
+ if (grid[i][j] == 1) {
+ max = Math.max(dfs(grid, i, j), max);
+ }
+ }
+ }
+ return max;
+ }
+
+ public int dfs(int[][] grid, int i, int j) {
+ int max = 0;
+ for (int k = 0; k < grid.length; k++) {
+ for (int l = 0; l < grid[0].length; l++) {
+ //if (k == i && l == j) {
+ // continue;
+ //}
+ if (grid[k][l] == 0) {
+ max = Math.max(manhattanDistance(i, k, j, l), max);
+ }
+ }
+ }
+ return max;
+ }
+
+ /**
+ * 曼哈顿距离
+ */
+ public int manhattanDistance(int x0, int x1, int y0, int y1) {
+ return Math.abs(x0 - x1) + Math.abs(y0 - y1);
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(2, new Solution1().maxDistance(new int[][]{{1, 0, 1}, {0, 0, 0}, {1, 0, 1}}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257_695_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257_695_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..48665435
--- /dev/null
+++ "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\345\262\233\345\261\277\347\232\204\346\234\200\345\244\247\351\235\242\347\247\257_695_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,64 @@
+package cn.lastwhisper.leetcode.dfsbfs.岛屿的最大面积_695_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/max-area-of-island/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:深度优先搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int maxAreaOfIsland(int[][] grid) {
+ int max = 0;
+ boolean[][] visited = new boolean[grid.length][grid[0].length];
+ for (int i = 0; i < grid.length; i++) {
+ for (int j = 0; j < grid[0].length; j++) {
+ if (grid[i][j] == 1) {
+ max = Math.max(dfs(grid, i, j, visited), max);
+ }
+ }
+ }
+ return max;
+ }
+
+ /**
+ * @param grid 岛屿
+ * @param x 当前所处的x
+ * @param y 当前所处的y
+ * @param visited 已经被访问的
+ */
+ public int dfs(int[][] grid, int x, int y, boolean[][] visited) {
+ if (x < 0 || x > grid.length - 1 || y < 0 || y > grid[0].length - 1 || visited[x][y] || grid[x][y] == 0) {
+ return 0;
+ }
+ int count = 1;
+ visited[x][y] = true;// 不需要回溯,因为到过的地方下次不会再去
+ count += dfs(grid, x - 1, y, visited);//上
+ count += dfs(grid, x, y + 1, visited);//右
+ count += dfs(grid, x + 1, y, visited);//下
+ count += dfs(grid, x, y - 1, visited);//左
+ return count;
+ }
+
+ public static void main(String[] args) {
+ //int[][] grid = {{0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
+ // {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
+ // {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
+ // {0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0},
+ // {0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0},
+ // {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
+ // {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
+ // {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}
+ //};
+ int[][] grid = {{1, 1, 0, 0, 0},
+ {1, 1, 0, 0, 0},
+ {0, 0, 0, 1, 1},
+ {0, 0, 0, 1, 1}};
+ Assert.assertEquals(4, new Solution1().maxAreaOfIsland(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220_994_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220_994_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..f70e4922
--- /dev/null
+++ "b/algorithms/leetcode/dfsbfs/src/main/java/cn/lastwhisper/leetcode/dfsbfs/\350\205\220\347\203\202\347\232\204\346\251\230\345\255\220_994_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,80 @@
+package cn.lastwhisper.leetcode.dfsbfs.腐烂的橘子_994_简单;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/rotting-oranges/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:广度优先搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int orangesRotting(int[][] grid) {
+ int m = grid.length;
+ int n = grid[0].length;
+ Queue queue = new LinkedList<>();
+
+ int count = 0; // count 表示新鲜橘子的数量
+ for (int r = 0; r < m; r++) {
+ for (int c = 0; c < n; c++) {
+ if (grid[r][c] == 1) {//新鲜
+ count++;
+ } else if (grid[r][c] == 2) {//腐烂
+ queue.add(new int[]{r, c});
+ }
+ }
+ }
+
+ int round = 0; // round 表示腐烂的轮数,或者分钟数
+ while (count > 0 && !queue.isEmpty()) {
+ round++;
+ int size = queue.size();
+ for (int i = 0; i < size; i++) {
+ int[] orange = queue.poll();
+ // 腐烂橘子的坐标
+ int r = orange[0];
+ int c = orange[1];
+ // 上下左右查看,是新鲜的就给它腐烂
+ // 腐烂橘子上面的橘子
+ if (r - 1 >= 0 && grid[r - 1][c] == 1) {
+ grid[r - 1][c] = 2;
+ count--;
+ queue.add(new int[]{r - 1, c});
+ }
+ // 腐烂橘子下面的橘子
+ if (r + 1 < m && grid[r + 1][c] == 1) {
+ grid[r + 1][c] = 2;
+ count--;
+ queue.add(new int[]{r + 1, c});
+ }
+ // 腐烂橘子左面的橘子
+ if (c - 1 >= 0 && grid[r][c - 1] == 1) {
+ grid[r][c - 1] = 2;
+ count--;
+ queue.add(new int[]{r, c - 1});
+ }
+ // 腐烂橘子右面的橘子
+ if (c + 1 < n && grid[r][c + 1] == 1) {
+ grid[r][c + 1] = 2;
+ count--;
+ queue.add(new int[]{r, c + 1});
+ }
+ }
+ }
+
+ if (count > 0) {
+ return -1;
+ } else {
+ return round;
+ }
+ }
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/divide/pom.xml b/algorithms/leetcode/divide/pom.xml
new file mode 100644
index 00000000..4fe344f5
--- /dev/null
+++ b/algorithms/leetcode/divide/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ divide
+
+
+
+ cn.lastwhisper
+ leetcode-common
+ 1.0-SNAPSHOT
+
+
+
\ No newline at end of file
diff --git a/algorithms/leetcode/dynamic/README.md b/algorithms/leetcode/dynamic/README.md
new file mode 100644
index 00000000..93d3aba6
--- /dev/null
+++ b/algorithms/leetcode/dynamic/README.md
@@ -0,0 +1,156 @@
+### 动态规划题目特点
+
+##### 1. 计数
+
+- 有多少种方式走到右下角
+- 有多少种方法选出k个数使得和是sum
+
+##### 2.求最大最小值
+
+- 从左上角走到右下角路径的最大数字和
+- 最长上升子序列长度
+
+##### 3.求存在性
+
+- 取石子游戏,先手是否必胜
+- 能不能选出k个数使得和是sum
+
+### 动态规划四个组成部分:
+
+- 确定状态
+ - 研究最优策略的最后一步
+ - 化为子问题
+- 转移方程
+ - 根据子问题定义直接得到
+- **初始条件和边界情况**
+- 计算顺序
+ - 利用之前的计算结果
+
+### 最值型动态规划
+
+> Coin Change
+> 假设你有三种硬币,分别面值2元,5元和7元,每种硬币都有足够多,买一本书需要27元,如何用最少的硬币组合正好付清,不需要对方找钱。
+
+#### 动态规划组成部分一:确定状态
+
+- 状态是动态规划的核心
+- 解动态规划时需要定义一个数组,数组的每一个元素`f[i]`或者`f[j][j]`代表什么
+- 确定状态需要两个意识:
+ - 最后一步
+ - 子问题
+
+> **最后一步**
+> 1-不知道最优策略,但最优策略肯定是K枚硬币面值加起来是27
+> 2-所以肯定有一枚最后的硬币:
+> **关键点1:**不关心前面`k-1`枚硬币如何拼出,甚至不知道,但是确定了前面的硬币拼出了
+> **关键点2:**因为是最优策略,所以拼出的硬币数一定要最少,否则矛盾。
+> **子问题**
+> 1 - 需要求出最少用多少枚硬币可以拼出
+> 2 - 原问题是最少用多少枚硬币拼出27
+> 3 - 原问题转化成了一个规模更小的子问题:
+> 4 - 设状态%20%3D%20%E6%9C%80%E5%B0%91%E7%94%A8%E5%A4%9A%E5%B0%91%E6%9E%9A%E7%A1%AC%E5%B8%81%E6%8B%BC%E5%87%BAX)
+> 最后一枚硬币只可能是2,5或7
+> 若,)应该是%20%2B%201)(加上最后的一枚硬币2)
+> 若,)应该是%20%2B%201)(加上最后的一枚硬币5)
+> 若,)应该是%20%2B%201)(加上最后的一枚硬币7)
+> 要求最少的硬币数:
+> %20%3D%20min%7Bf(27%20-%202)%2C%20f(27%20-%205)%2C%20f(27%20-%207)%7D%20%2B%201)
+
+#### 动态规划组成部分二:转移方程
+
+- 设状态%20%3D) 最少用多少枚硬币拼出
+- 对于任意,%20%3D%20min%7Bf(27%20-%202)%2C%20f(27%20-%205)%2C%20f(27%20-%207)%7D%20%2B%201)
+
+#### 动态规划组成部分三:初始条件和边界情况
+
+- 时,%20%3D%20%2B%5Cinfty)
+- 初始条件:%20%3D%200)
+
+#### 动态规划组成部分三:计算顺序
+
+- 初始条件:%20%3D%200)
+- 然后计算%2Cf(2)%2C...%2Cf(27))
+- 大多数情况都是从小到大地算,这样当算)时,前面的%2Cf(x-5)%E5%92%8Cf(x-7))都已经算过了。
+
+### 求最值型动态规划小结
+
+- 1.确定状态
+ - 最后一步(最优策略中使用的最后一枚硬币)
+ - 化成子问题(最少的硬币拼出更小的面值
+- 2.转移方程
+ - %20%3D%20min%7Bf(X%20-%202)%2C%20f(X%20-%205)%2C%20f(X%20-%207)%7D%20%2B%201)
+- 3.初始条件和边界情况
+ - %20%3D%200),如果不能拼出,%20%3D%20%2B%5Cinfty)
+- 4.计算顺序
+ - %2Cf(1)%2C...)
+
+### 计数型动态规划
+
+> Unique Paths
+>
+>
+>
+> 给定m行n列的网格,有一个机器人从左上角(0, 0)出发,每一步可以向下或者向右走一步,问有多少种不同的方式走到右下角
+>
+> 
+
+#### 动态规划组成部分一:确定状态
+
+- 最后一步:机器人走到右下角之前的最后一步:
+
+ 向右或者向下
+
+ 
+
+- 右下角坐标为(m - 1, n - 1),那么前一步机器人一定是在(m - 2, n - 1)或者(m - 1, n - 2)
+
+- 子问题 ——从左上角走到(m - 2, n - 1)和从左上角走到(m - 1, n - 2);假设从左上角走到(m - 2, n - 1)有种方式,从左上角走到(m - 1, n - 2)有种方式,那么走到(m - 1, n - 1)就有种方式。
+ 问题转化为机器人有多少种方式从左上角走到(m - 2, n - 1)和(m - 1, n - 2)
+
+- 状态:设![f[i][j]](https://math.jianshu.com/math?formula=f%5Bi%5D%5Bj%5D)为机器人有多少种方式从左上角走到)
+
+#### 动态规划组成部分二:转移方程
+
+- 对于任意坐标),![f[i][j] = f[i - 1][j] + f[i][j - 1]](https://math.jianshu.com/math?formula=f%5Bi%5D%5Bj%5D%20%3D%20f%5Bi%20-%201%5D%5Bj%5D%20%2B%20f%5Bi%5D%5Bj%20-%201%5D)
+
+#### 动态规划组成部分三:初始条件和边界情况
+
+- 初始条件:![f[0][0] = 1](https://math.jianshu.com/math?formula=f%5B0%5D%5B0%5D%20%3D%201),机器人只有一种方式到左上角
+- 边界情况:,则前一步只能有一个方向过来
+
+#### 动态规划组成部分四:计算顺序
+
+- ![f[0][0] = 1](https://math.jianshu.com/math?formula=f%5B0%5D%5B0%5D%20%3D%201)
+- 计算第0行:![f[0][0],f[0][1],...,f[0][n-1]](https://math.jianshu.com/math?formula=f%5B0%5D%5B0%5D%2Cf%5B0%5D%5B1%5D%2C...%2Cf%5B0%5D%5Bn-1%5D)
+- 计算第1行:![f[1][0],f[1][1],...,f[1][n-1]](https://math.jianshu.com/math?formula=f%5B1%5D%5B0%5D%2Cf%5B1%5D%5B1%5D%2C...%2Cf%5B1%5D%5Bn-1%5D)
+- 
+- 计算第m-1行:![f[m-1][0],f[m-1][1],...,f[m-1][n-1]](https://math.jianshu.com/math?formula=f%5Bm-1%5D%5B0%5D%2Cf%5Bm-1%5D%5B1%5D%2C...%2Cf%5Bm-1%5D%5Bn-1%5D)
+- 答案是![f[m-1][n-1]](https://math.jianshu.com/math?formula=f%5Bm-1%5D%5Bn-1%5D)
+ 时间复杂度和空间复杂度都是)
+
+### 存在性型动态规划
+
+> 有块石头分别在x轴的位置,一只青蛙在石头,想跳到石头,如果青蛙在第块石头上,它最多可以向右跳距离,问青蛙能否跳到石头。
+
+#### 动态规划组成部分一:确定状态
+
+- 最后一步:如果青蛙能跳到最后一块石头,考虑它跳的最后一步是从石头跳过来的,
+- 需要满足两个条件:1. 青蛙可以跳到石头 2.最后一步不超过跳跃的最大距离:
+- 子问题——青蛙能不能跳到石头))
+
+#### 动态规划组成部分三:初始条件和边界情况
+
+- 设![f[j]](https://math.jianshu.com/math?formula=f%5Bj%5D)表示青蛙能不能跳到石头
+- 初始条件:![f[0] = True](https://math.jianshu.com/math?formula=f%5B0%5D%20%3D%20True),青蛙一开始就在石头
+
+#### 动态规划组成部分四:计算顺序
+
+- 初始化![f[0] = True](https://math.jianshu.com/math?formula=f%5B0%5D%20%3D%20True)
+- 计算![f[1],f[2],...,f[n-1]](https://math.jianshu.com/math?formula=f%5B1%5D%2Cf%5B2%5D%2C...%2Cf%5Bn-1%5D)
+- 答案是![f[n-1]](https://math.jianshu.com/math?formula=f%5Bn-1%5D)
+ 时间复杂度:),空间复杂度:)
\ No newline at end of file
diff --git a/algorithms/leetcode/dynamic/pom.xml b/algorithms/leetcode/dynamic/pom.xml
new file mode 100644
index 00000000..6f55f76b
--- /dev/null
+++ b/algorithms/leetcode/dynamic/pom.xml
@@ -0,0 +1,25 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper.leetcode
+ dynamic
+
+
+
+
+
+ leetcode-common
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+
+
+
\ No newline at end of file
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution1.java b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution1.java
new file mode 100644
index 00000000..a68c90c0
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution1.java
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.dynamic.knapsack01;
+
+/**
+ * 01背包
+ * @author lastwhisper
+ * @date 2020/2/20
+ */
+public class Solution1 {
+
+
+ private int bestValue(int[] w, int[] v, int index, int capacity) {
+ if (index < 0 || capacity <= 0) {
+ return 0;
+ }
+ // 尝试[0,index-1]的商品
+ int res = bestValue(w, v, index - 1, capacity);
+ if (capacity >= w[index]) {
+ // 尝试index的商品和[0,index-1]时capacity等于减去w[index]的商品
+ res = Math.max(res, v[index] + bestValue(w, v, index - 1, capacity - w[index]));
+ }
+ return res;
+ }
+
+ /**
+ * 01背包 递归
+ *
+ * F(n,c)考虑将n个物品放进容量为c的背包,使得价值最大
+ * F(i,c) = max(F(i-1,c),v[i]+F(i-1,c-w[i]))
+ *
+ * @param w 每个商品对应的重量
+ * @param v 每个商品对应的价值
+ * @param capacity 背包容量
+ * @return int 最大价值
+ */
+ public int knapsack01(int[] w, int[] v, int capacity) {
+ int n = w.length;
+ return bestValue(w, v, n - 1,capacity );
+ }
+
+ /**
+ * 容量为:5
+ * 三个物品:
+ * w 1 2 3
+ * v 6 10 12
+ *
+ * 最大价值22
+ */
+ public static void main(String[] args) {
+ int[] w = new int[]{1, 2, 3};
+ int[] v = new int[]{6, 10, 12};
+ int capacity = 5;
+ System.out.println(new Solution1().knapsack01(w, v, capacity));
+ }
+}
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution2.java b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution2.java
new file mode 100644
index 00000000..9c4b23c2
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution2.java
@@ -0,0 +1,57 @@
+package cn.lastwhisper.leetcode.dynamic.knapsack01;
+
+/**
+ * 01背包
+ * @author lastwhisper
+ * @date 2020/2/20
+ */
+public class Solution2 {
+
+
+ private int bestValue(int[] w, int[] v, int index, int capacity, int[][] memo) {
+ if (index < 0 || capacity <= 0) {
+ return 0;
+ }
+ if (memo[index][capacity] != 0) {
+ return memo[index][capacity];
+ }
+ // 尝试[0,index-1]的商品
+ int res = bestValue(w, v, index - 1, capacity, memo);
+ if (capacity >= w[index]) {
+ // 尝试index的商品和[0,index-1]时capacity等于减去w[index]的商品
+ res = Math.max(res,v[index] + bestValue(w, v, index - 1, capacity - w[index], memo));
+ }
+ memo[index][capacity] = res;
+ return res;
+ }
+
+ /**
+ * 01背包
+ * 递归+备忘录
+ *
+ * @param w 每个商品对应的重量
+ * @param v 每个商品对应的价值
+ * @param capacity 背包容量
+ * @return int 最大价值
+ */
+ public int knapsack01(int[] w, int[] v, int capacity) {
+ int n = w.length;
+ int[][] memo = new int[n][capacity + 1];
+ return bestValue(w, v,n-1, capacity, memo);
+ }
+
+ /**
+ * 容量为:5
+ * 三个物品:
+ * w 1 2 3
+ * v 6 10 12
+ *
+ * 最大价值22
+ */
+ public static void main(String[] args) {
+ int[] w = new int[]{1, 2, 3};
+ int[] v = new int[]{6, 10, 12};
+ int capacity = 5;
+ System.out.println(new Solution2().knapsack01(w, v, capacity));
+ }
+}
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution3.java b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution3.java
new file mode 100644
index 00000000..5e60824d
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution3.java
@@ -0,0 +1,67 @@
+package cn.lastwhisper.leetcode.dynamic.knapsack01;
+
+/**
+ * 01背包
+ * @author lastwhisper
+ * @date 2020/2/20
+ */
+public class Solution3 {
+
+ /**
+ * 01背包 动态规划
+ *
+ * 时间复杂度:O(n*c)
+ * 空间复杂度:O(n*c)
+ *
+ * F(n,c)考虑将n个物品放进容量为c的背包,使得价值最大
+ * F(i,c) = max(F(i-1,c),v[i]+F(i-1,c-w[i]))
+ *
+ * @param w 每个商品对应的重量
+ * @param v 每个商品对应的价值
+ * @param c 背包容量
+ * @return int 最大价值
+ */
+ public int knapsack01(int[] w, int[] v, int c) {
+ int n = w.length;
+ if (n == 0) {
+ return 0;
+ }
+
+ int[][] dp = new int[n][c + 1];
+
+ // 初始化第一行
+ for (int i = 0; i <= c; i++) {
+ dp[0][i] = i >= w[0] ? v[0] : 0;
+ }
+
+ // 外循环,当前可选商品
+ for (int i = 1; i < n; i++) {
+ // 内循环,当前可用背包大小
+ for (int j = 0; j <= c; j++) {
+ // 不考虑当前商品
+ dp[i][j] = dp[i - 1][j];
+ // 考虑当前商品+剩余容量时剩下商品
+ if (j >= w[i]) {
+ dp[i][j] = Math.max(dp[i][j], v[i] + dp[i - 1][j - w[i]]);
+ }
+ }
+ }
+
+ return dp[n - 1][c];
+ }
+
+ /**
+ * 容量为:5
+ * 三个物品:
+ * w 1 2 3
+ * v 6 10 12
+ *
+ * 最大价值22
+ */
+ public static void main(String[] args) {
+ int[] w = new int[]{1, 2, 3};
+ int[] v = new int[]{6, 10, 12};
+ int capacity = 5;
+ System.out.println(new Solution3().knapsack01(w, v, capacity));
+ }
+}
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution4.java b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution4.java
new file mode 100644
index 00000000..82ec5b1c
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution4.java
@@ -0,0 +1,71 @@
+package cn.lastwhisper.leetcode.dynamic.knapsack01;
+
+/**
+ * 01背包
+ * @author lastwhisper
+ * @date 2020/2/20
+ */
+public class Solution4 {
+
+ /**
+ * 01背包 动态规划
+ *
+ * 时间复杂度:O(n*c)
+ * 空间复杂度:O(2*c)
+ *
+ * F(n,c)考虑将n个物品放进容量为c的背包,使得价值最大
+ * F(i,c) = max(F(i-1,c),v[i]+F(i-1,c-w[i]))
+ *
+ * 空间优化:
+ * 第i行元素只依赖于第i-1行元素。理论上,只需要保持两行元素。
+ * 空间复杂度:O( 2 * C ) = O(C)
+ *
+ * @param w 每个商品对应的重量
+ * @param v 每个商品对应的价值
+ * @param c 背包容量
+ * @return int 最大价值
+ */
+ public int knapsack01(int[] w, int[] v, int c) {
+ int n = w.length;
+ if (n == 0) {
+ return 0;
+ }
+
+ int[][] dp = new int[2][c + 1];
+
+ // 初始化第一行
+ for (int i = 0; i <= c; i++) {
+ dp[0][i] = i >= w[0] ? v[0] : 0;
+ }
+
+ // 外循环,当前可选商品
+ for (int i = 1; i < n; i++) {
+ // 内循环,当前可用背包大小
+ for (int j = 0; j <= c; j++) {
+ // 不考虑当前商品
+ dp[i % 2][j] = dp[(i - 1) % 2][j];
+ // 考虑当前商品+剩余容量时剩下商品
+ if (j >= w[i % 2]) {
+ dp[i % 2][j] = Math.max(dp[i % 2][j], v[i% 2] + dp[(i - 1) % 2][j - w[i % 2]]);
+ }
+ }
+ }
+
+ return dp[(n - 1) % 2][c];
+ }
+
+ /**
+ * 容量为:5
+ * 三个物品:
+ * w 1 2 3
+ * v 6 10 12
+ *
+ * 最大价值22
+ */
+ public static void main(String[] args) {
+ int[] w = new int[]{1, 2, 3};
+ int[] v = new int[]{6, 10, 12};
+ int capacity = 5;
+ System.out.println(new Solution4().knapsack01(w, v, capacity));
+ }
+}
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution5.java b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution5.java
new file mode 100644
index 00000000..c335d37e
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/Solution5.java
@@ -0,0 +1,66 @@
+package cn.lastwhisper.leetcode.dynamic.knapsack01;
+
+/**
+ * 01背包
+ * @author lastwhisper
+ * @date 2020/2/20
+ */
+public class Solution5 {
+
+ /**
+ * 01背包 动态规划
+ *
+ * 时间复杂度:O(n*c)
+ * 空间复杂度:O(c)
+ *
+ * F(n,c)考虑将n个物品放进容量为c的背包,使得价值最大
+ * F(i,c) = max(F(i-1,c),v[i]+F(i-1,c-w[i]))
+ *
+ * 空间优化:
+ *
+ *
+ * @param w 每个商品对应的重量
+ * @param v 每个商品对应的价值
+ * @param c 背包容量
+ * @return int 最大价值
+ */
+ public int knapsack01(int[] w, int[] v, int c) {
+ int n = w.length;
+ if (n == 0) {
+ return 0;
+ }
+
+ int[] dp = new int[c + 1];
+
+ // 初始化第一行
+ for (int i = 0; i <= c; i++) {
+ dp[i] = i >= w[0] ? v[0] : 0;
+ }
+
+ // 外循环,当前可选商品
+ for (int i = 1; i < n; i++) {
+ // 内循环,当前可用背包大小
+ for (int j = c; j >= w[i]; j--) {
+ // 不考虑当前商品 vs 当前商品+剩余容量时剩下商品
+ dp[j] = Math.max(dp[j], v[i] + dp[j - w[i]]);
+ }
+ }
+
+ return dp[c];
+ }
+
+ /**
+ * 容量为:5
+ * 三个物品:
+ * w 1 2 3
+ * v 6 10 12
+ *
+ * 最大价值22
+ */
+ public static void main(String[] args) {
+ int[] w = new int[]{1, 2, 3};
+ int[] v = new int[]{6, 10, 12};
+ int capacity = 5;
+ System.out.println(new Solution5().knapsack01(w, v, capacity));
+ }
+}
diff --git a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/knapsack01.md b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/knapsack01.md
new file mode 100644
index 00000000..7431ee35
--- /dev/null
+++ b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/knapsack01/knapsack01.md
@@ -0,0 +1,15 @@
+| id | 0 | 1 | 2 |
+| ------ | ---- | ---- | ---- |
+| weight | 1 | 2 | 3 |
+| value | 6 | 10 | 12 |
+
+
+
+
+
+| 物品编号\背包容量 | 0 | 1 | 2 | 3 | 4 | 5 |
+|-----------| ---- | ---- | ---- | ---- | ---- | ---- |
+| 0 | 0 | 6 | 6 | 6 | 6 | 6 |
+| 1 | 0 | 6 | 10 | 16 | 16 | 16 |
+| 2 | 0 | 6 | 10 | 16 | 18 | 22 |
+
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/README.md" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/README.md"
new file mode 100644
index 00000000..68f2ea3d
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/README.md"
@@ -0,0 +1 @@
+https://leetcode-cn.com/problems/triangle/solution/javadong-tai-gui-hua-si-lu-yi-ji-dai-ma-shi-xian-b/
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..a2ac0362
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,82 @@
+package cn.lastwhisper.leetcode.dynamic.三角形最小路径和_120_中等;
+
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.array.ArrayUtil.createList;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/triangle/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——自顶向下
+ * triangle={
+ * [2],
+ * [3,4],
+ * [6,5,7],
+ * [4,1,8,3]
+ * }
+ * 1、状态定义:dp[i][j]表示包含第i行第j列元素的最小路径和
+ * 2、状态分析
+ * 初始化:
+ * dp[0][0]=triangle[0][0]
+ * 常规:
+ * triangle[i][j]一定会经过triangle[i-1][j]或者triangle[i-1][j-1]
+ * 所以状态dp[i][j]一定等于dp[i-1][j]或者dp[i-1][j-1]的最小值+triangle[i][j]
+ * 特殊:
+ * triangle[i][0]没有左上角 只能从triangle[i-1][j]经过
+ * triangle[i][row[0].length]没有上面 只能从triangle[i-1][j-1]经过
+ * 3、转换方程:dp[i][j]=min(dp[i-1][j],dp[i-1][j-1])+triangle[i][j]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public int minimumTotal(List> triangle) {
+ // 特判
+ if (triangle == null || triangle.size() == 0) {
+ return 0;
+ }
+
+ int row = triangle.size();
+ int column = triangle.get(row - 1).size();
+
+ int[][] dp = new int[row][column];
+ dp[0][0] = triangle.get(0).get(0);
+
+ for (int i = 1; i < row; i++) {
+ //对每一行的元素进行最小路径和推导
+ List rows = triangle.get(i);
+ for (int j = 0; j <= i; j++) {
+ if (j == 0) {
+ // 最左端特殊处理
+ dp[i][j] = dp[i - 1][j] + rows.get(j);
+ } else if (j == i) {
+ // 最右端特殊处理
+ dp[i][j] = dp[i - 1][j - 1] + rows.get(j);
+ } else {
+ dp[i][j] = Math.min(dp[i - 1][j], dp[i - 1][j - 1]) + rows.get(j);
+ }
+ }
+ }
+
+ int res = Integer.MAX_VALUE;
+ // dp最后一行记录了最小路径
+ for (int i = 0; i < column; i++) {
+ res = Math.min(res, dp[row - 1][i]);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[][] arrays = new int[][]{
+ {2},
+ {3, 4},
+ {6, 5, 7},
+ {4, 1, 8, 3}
+ };
+ //int[][] arrays = new int[][]{{-10}};
+ List> triangle = createList(arrays);
+ System.err.println(new Solution1().minimumTotal(triangle));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..3a96c641
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,69 @@
+package cn.lastwhisper.leetcode.dynamic.三角形最小路径和_120_中等;
+
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.array.ArrayUtil.createList;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/triangle/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——基于思路一
+ * 空间优化:
+ * prev暂存dp[i-1][j-1],cur暂存dp[i-1][j]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int minimumTotal(List> triangle) {
+ // 特判
+ if (triangle == null || triangle.size() == 0) {
+ return 0;
+ }
+ // dp最大长度==triangle底边长度
+ // 题意:只使用 O(n) 的额外空间(n 为三角形的总行数)
+ int[] dp = new int[triangle.size()];
+ dp[0] = triangle.get(0).get(0);
+
+ // prev暂存dp[i-1][j-1],cur暂存dp[i-1][j]
+ int prev = 0, cur;
+ for (int i = 1; i < triangle.size(); i++) {
+ //对每一行的元素进行推导
+ List rows = triangle.get(i);
+ for (int j = 0; j <= i; j++) {
+ cur = dp[j];
+ if (j == 0) {
+ // 最左端特殊处理
+ dp[j] = cur + rows.get(j);
+ } else if (j == i) {
+ // 最右端特殊处理
+ dp[j] = prev + rows.get(j);
+ } else {
+ dp[j] = Math.min(cur, prev) + rows.get(j);
+ }
+ prev = cur;
+ }
+ }
+
+ int res = Integer.MAX_VALUE;
+ // dp最后一行记录了最小路径
+ for (int i = 0; i < triangle.size(); i++) {
+ res = Math.min(res, dp[i]);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[][] arrays = new int[][]{
+ {2},
+ {3, 4},
+ {6, 5, 7},
+ {4, 1, 8, 3}
+ };
+ //int[][] arrays = new int[][]{{-10}};
+ List> triangle = createList(arrays);
+ System.err.println(new Solution2().minimumTotal(triangle));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..912548cd
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,61 @@
+package cn.lastwhisper.leetcode.dynamic.三角形最小路径和_120_中等;
+
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.array.ArrayUtil.createList;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/triangle/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——自底向上
+ * triangle={
+ * [2],
+ * [3,4],
+ * [6,5,7],
+ * [4,1,8,3]
+ * }
+ * 1、状态定义:dp[i][j]表示包含第i行第j列元素的最小路径和
+ * 2、状态分析
+ * 初始化:
+ * dp[n]=triangle[n]
+ * 常规:
+ * triangle[i][j]一定会到达triangle[i+1][j]或者triangle[i+1][j+1]
+ * 所以状态dp[i][j]一定等于dp[i+1][j]或者dp[i+1][j+1]的最小值+triangle[i][j]
+ * 3、转换方程:dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public int minimumTotal(List> triangle) {
+ // 特判
+ if (triangle == null || triangle.size() == 0) {
+ return 0;
+ }
+ // 加1可以不用初始化最后一行
+ // 根据题意,行列值相同
+ int[][] dp = new int[triangle.size() + 1][triangle.size() + 1];
+
+ for (int i = triangle.size() - 1; i >= 0; i--) {
+ List rows = triangle.get(i);
+ for (int j = 0; j < rows.size(); j++) {
+ dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + rows.get(j);
+ }
+ }
+ return dp[0][0];
+ }
+
+ public static void main(String[] args) {
+ int[][] arrays = new int[][]{
+ {2},
+ {3, 4},
+ {6, 5, 7},
+ {4, 1, 8, 3}
+ };
+ //int[][] arrays = new int[][]{{-10}};
+ List> triangle = createList(arrays);
+ System.err.println(new Solution3().minimumTotal(triangle));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution4.java"
new file mode 100644
index 00000000..d71d3786
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\211\350\247\222\345\275\242\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_120_\344\270\255\347\255\211/Solution4.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.dynamic.三角形最小路径和_120_中等;
+
+import java.util.List;
+
+import static cn.lastwhisper.leetcode.common.array.ArrayUtil.createList;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/triangle/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——基于思路三
+ * 空间优化:
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int minimumTotal(List> triangle) {
+ // 特判
+ if (triangle == null || triangle.size() == 0) {
+ return 0;
+ }
+ // dp中记录了求第i行时,第i+1的最小路径和
+ int[] dp = new int[triangle.size() + 1];
+
+ for (int i = triangle.size() - 1; i >= 0; i--) {
+ List rows = triangle.get(i);
+ for (int j = 0; j < rows.size(); j++) {
+ dp[j] = Math.min(dp[j], dp[j + 1]) + rows.get(j);
+ }
+ }
+ return dp[0];
+ }
+
+ public static void main(String[] args) {
+ int[][] arrays = new int[][]{
+ {2},
+ {3, 4},
+ {6, 5, 7},
+ {4, 1, 8, 3}
+ };
+ //int[][] arrays = new int[][]{{-10}};
+ List> triangle = createList(arrays);
+ System.err.println(new Solution4().minimumTotal(triangle));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..6b1d84f6
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.dynamic.不同路径II_63_中等;
+
+class Solution1 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/unique-paths-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private int m, n;
+ private int[][] dir = {{0, 1}, {1, 0}};
+ public int uniquePathsWithObstacles(int[][] obstacleGrid) {
+ if (obstacleGrid[0][0] == 1) {
+ return 0;
+ }
+ m = obstacleGrid.length;
+ n = obstacleGrid[0].length;
+ return findPath(obstacleGrid, 0, 0);
+ }
+
+ private int findPath(int[][] grid, int startX, int startY) {
+ if (m - 1 == startX && n - 1 == startY) {
+ return 1;
+ }
+ int result = 0;
+ for (int i = 0; i < dir.length; i++) {
+ int newX = startX + dir[i][0];
+ int newY = startY + dir[i][1];
+ if (isValid(newX, newY, grid)) {
+ result += findPath(grid, newX, newY);
+ }
+ }
+ return result;
+ }
+
+ private boolean isValid(int x, int y, int[][] grid) {
+ return x < m && y < n && grid[x][y] != 1;
+ }
+
+ public static void main(String[] args) {
+ //int[][] obstacleGrid = {
+ // {0, 0, 0},
+ // {0, 1, 0},
+ // {0, 0, 0}
+ //};
+ int[][] obstacleGrid = {
+ {1},
+ };
+ System.out.println(new Solution1().uniquePathsWithObstacles(obstacleGrid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..3f67aad1
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204II_63_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,46 @@
+package cn.lastwhisper.leetcode.dynamic.不同路径II_63_中等;
+
+class Solution2 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/unique-paths-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int uniquePathsWithObstacles(int[][] obstacleGrid) {
+ if (obstacleGrid[0][0] == 1) {
+ return 0;
+ }
+ int m = obstacleGrid.length, n = obstacleGrid[0].length;
+ int[][] dp = new int[m][n];
+ dp[0][0] = 1;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ if (obstacleGrid[i][j] == 1)
+ continue;
+ if (i >= 1)
+ dp[i][j] = dp[i][j] + dp[i - 1][j];
+ if (j >= 1)
+ dp[i][j] = dp[i][j] + dp[i][j - 1];
+ }
+ }
+ return dp[m - 1][n - 1];
+ }
+
+ public static void main(String[] args) {
+ int[][] obstacleGrid = {
+ {0, 0, 0},
+ {0, 1, 0},
+ {0, 0, 0}
+ };
+ //int[][] obstacleGrid = {
+ // {1},
+ //};
+ System.out.println(new Solution2().uniquePathsWithObstacles(obstacleGrid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..fd1374c2
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,27 @@
+package cn.lastwhisper.leetcode.dynamic.不同路径_62_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归(自顶向下)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public int uniquePaths(int m, int n) {
+ // m==1||n==1时,只剩下一条路可走,直接返回1
+ if (m == 1 || n == 1) {
+ return 1;
+ }
+ // 向右走+向下走
+ return uniquePaths(m - 1, n) + uniquePaths(m, n - 1);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().uniquePaths(7, 3));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..31e2a3a9
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\270\215\345\220\214\350\267\257\345\276\204_62_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.dynamic.不同路径_62_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 每个位置的路径 = 该位置左边的路径 + 该位置上边的路径
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int uniquePaths(int m, int n) {
+ int[][] dp = new int[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ // 到达[0,1]、[1,0]只有一步
+ if (i == 0 || j == 0) {
+ dp[i][j] = 1;
+ } else {
+ // 每个位置的路径 = 该位置左边的路径 + 该位置上边的路径
+ dp[i][j] = dp[i][j - 1] + dp[i - 1][j];
+ }
+ }
+ }
+ return dp[m - 1][n - 1];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().uniquePaths(7, 3));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..5e0b6bfa
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.dynamic.买卖股票的最佳时机_121_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int maxProfit(int[] prices) {
+ int max = 0;
+ for (int i = prices.length - 1; i > 0; i--) {
+ for (int j = i - 1; j >= 0; j--) {
+ int profit = prices[i] - prices[j];
+ if (profit > max) {
+ max = profit;
+ }
+ }
+ }
+ return max;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = new int[]{7, 1, 5, 3, 6, 4};
+ int[] nums = new int[]{7, 6, 4, 3, 1};
+ System.out.println(new Solution1().maxProfit(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..3e8bbf5d
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272_121_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,33 @@
+package cn.lastwhisper.leetcode.dynamic.买卖股票的最佳时机_121_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int maxProfit(int[] prices) {
+ if (prices == null || prices.length <= 1) {
+ return 0;
+ }
+ // 前i天的最大收益 = max{前i-1天的最大收益,第i天的价格-前i-1天中的最小价格}
+ // min是前i-1天中的最小价格,前i-1天的最大收益
+ int min = prices[0], max = 0;
+ for (int i = 1; i < prices.length; i++) {
+ max = Math.max(max, prices[i] - min);
+ min = Math.min(min, prices[i]);
+ }
+ return max;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = new int[]{7, 1, 5, 3, 6, 4};
+ //int[] nums = new int[]{7, 6, 4, 3, 1};
+ System.out.println(new Solution2().maxProfit(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/416.md" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/416.md"
new file mode 100644
index 00000000..6661c76c
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/416.md"
@@ -0,0 +1,23 @@
+背包容量sum/2,数组中的值是物品的重量。
+
+状态定义:`dp[i][j]`表示从数组的 `[0, i]` 这个子区间内挑选一些正整数,每个数只能用一次,使得这些数的和**恰好等于** `j`。
+
+1. 不选择 `nums[i]`,如果在`[0, i - 1]` 这个子区间内已经有一部分元素,使得它们的和为 j ,那么 `dp[i][j] = true`;
+
+2. 选择 `nums[i]`,如果在 `[0, i - 1]` 这个子区间内就得找到一部分元素,使得它们的和为 `j - nums[i]`。
+
+状态转移方程是:
+
+```
+dp[i][j]=dp[i-1][j] or dp[i][j-num[i]]
+```
+
+
+
+| | (i,j) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
+| ---- | ------ | ----- | ----- | ---- | ---- | ---- | ----- | ----- | ---- | ---- | ---- | ----- | ----- |
+| 1 | num[0] | **T** | **T** | F | F | F | F | F | F | F | F | F | F |
+| 5 | num[1] | **T** | **T** | F | F | F | **T** | **T** | F | F | F | F | F |
+| 11 | num[2] | **T** | **T** | F | F | F | **T** | **T** | F | F | F | F | **T** |
+| 5 | num[3] | **T** | **T** | F | F | F | **T** | **T** | F | F | F | **T** | **T** |
+
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..14a445b7
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.dynamic.分割等和子集_416_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-equal-subset-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 背包问题,在n个物品中选出一定物品,填满sum/2的背包
+ * 1、状态定义;
+ * 2、状态转移方程;
+ * 3、初始化;
+ * 4、输出;
+ * 5、思考状态压缩
+ * F(n,c)考虑将n个物品填满容量为c的背包
+ * F(i,c) = F(i-1,c)||F(i-1,c-w(i))
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*sum/2)
+ * 空间复杂度:O(n*sum/2)
+ */
+ public boolean canPartition(int[] nums) {
+ int sum = 0;
+ for (int num : nums) {
+ sum += num;
+ }
+ if (sum % 2 != 0) {
+ return false;
+ }
+
+ return tryPartition(nums, nums.length - 1, sum / 2);
+ }
+
+ // 使用nums[0,index],是否可以完全填充一个容量为sum的背包
+ private boolean tryPartition(int[] nums, int index, int sum) {
+ if (sum == 0) {
+ return true;
+ }
+ if (index < 0 || sum < 0) {
+ return false;
+ }
+
+ return tryPartition(nums, index - 1, sum) || tryPartition(nums, index - 1, sum - nums[index]);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().canPartition(new int[]{1, 5, 11, 5}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..1c7cb54b
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,59 @@
+package cn.lastwhisper.leetcode.dynamic.分割等和子集_416_中等;
+
+import java.util.Arrays;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-equal-subset-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 背包问题,在n个物品中选出一定物品,填满sum/2的背包
+ * 1、状态定义;
+ * 2、状态转移方程;
+ * 3、初始化;
+ * 4、输出;
+ * 5、思考状态压缩
+ * F(n,c)考虑将n个物品填满容量为c的背包
+ * F(i,c) = F(i-1,c)||F(i-1,c-w(i))
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*sum/2)
+ * 空间复杂度:
+ */
+ public boolean canPartition(int[] nums) {
+ int sum = 0;
+ for (int num : nums) {
+ sum += num;
+ }
+ if (sum % 2 != 0) {
+ return false;
+ }
+ // -1未存储;0 false;1 true
+ int[][] memo = new int[nums.length][sum / 2 + 1];
+ for (int[] ints : memo) {
+ Arrays.fill(ints, -1);
+ }
+ return tryPartition(nums, nums.length - 1, sum / 2, memo);
+ }
+
+ // 使用nums[0,index],是否可以完全填充一个容量为sum的背包
+ private boolean tryPartition(int[] nums, int index, int sum, int[][] memo) {
+ if (sum == 0) {
+ return true;
+ }
+ if (index < 0 || sum < 0) {
+ return false;
+ }
+ if (memo[index][sum] != -1) {
+ return memo[index][sum] == 1;
+ }
+ memo[index][sum] = (tryPartition(nums, index - 1, sum, memo) ||
+ tryPartition(nums, index - 1, sum - nums[index], memo)) ? 1 : 0;
+ return memo[index][sum] == 1;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().canPartition(new int[]{1, 5, 11, 5}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..3e7c3b95
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,62 @@
+package cn.lastwhisper.leetcode.dynamic.分割等和子集_416_中等;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-equal-subset-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 背包问题,在n个物品中选出一定物品,填满sum/2的背包
+ * dp[i][j]考虑将i个物品填满容量为j的背包
+ * dp[i][j]=dp[i-1][j] or dp[i][j-num[i]]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(NC):这里 N 是数组元素的个数,C 是数组元素的和的一半。
+ * 空间复杂度:O(NC)
+ */
+ public boolean canPartition(int[] nums) {
+ int len = nums.length;
+ if (len == 0) {
+ return false;
+ }
+
+ int sum = 0;
+ for (int num : nums) {
+ sum += num;
+ }
+ // 特判:如果是奇数,就不符合要求
+ if (sum % 2 != 0) {
+ return false;
+ }
+ int target = sum / 2;
+
+ // 创建二维状态数组,行:物品索引,列:容量(包括 0)
+ boolean[][] dp = new boolean[len][target + 1];
+
+ // 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
+ if (nums[0] <= target) {
+ dp[0][nums[0]] = true;
+ }
+
+ // 外循环是物品
+ for (int i = 1; i < len; i++) {
+ // 内循环是容量
+ for (int j = 0; j <= target; j++) {
+ // 第i个物品重量num[i]刚好够,背包容量j
+ if (nums[i] == j) {
+ dp[i][j] = true;
+ } else if (nums[i] < j) {
+ dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
+ } else {
+ dp[i][j] = dp[i - 1][j];
+ }
+ }
+ }
+ return dp[len - 1][target];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().canPartition(new int[]{1, 2, 5}));
+ //System.out.println(new Solution3().canPartition(new int[]{1, 5, 11, 5}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution4.java"
new file mode 100644
index 00000000..38c83b99
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\210\206\345\211\262\347\255\211\345\222\214\345\255\220\351\233\206_416_\344\270\255\347\255\211/Solution4.java"
@@ -0,0 +1,60 @@
+package cn.lastwhisper.leetcode.dynamic.分割等和子集_416_中等;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/partition-equal-subset-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 动态规划:
+ * 背包问题,在n个物品中选出一定物品,填满sum/2的背包
+ * dp[i][j]考虑将i个物品填满容量为j的背包
+ * dp[i][j]=dp[i-1][j] or dp[i][j-num[i]]
+ * 时间优化:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(NC):这里 N 是数组元素的个数,C 是数组元素的和的一半。
+ * 空间复杂度:O(C)
+ */
+ public boolean canPartition(int[] nums) {
+ int len = nums.length;
+ if (len == 0) {
+ return false;
+ }
+
+ int sum = 0;
+ for (int num : nums) {
+ sum += num;
+ }
+ // 特判:如果是奇数,就不符合要求
+ if (sum % 2 != 0) {
+ return false;
+ }
+ int target = sum / 2;
+
+ // 创建二维状态数组,行:物品索引,列:容量(包括 0)
+ boolean[] dp = new boolean[target + 1];
+
+ // 先填表格第 0 行,第 1 个数只能让容积为它自己的背包恰好装满
+ if (nums[0] <= target) {
+ dp[nums[0]] = true;
+ }
+
+ // 外循环是物品
+ for (int i = 1; i < len; i++) {
+ // 内循环是容量
+ for (int j = target; j >= nums[i]; j--) {
+ if (dp[target]) {
+ return true;
+ }
+ dp[j] = dp[j] || dp[j - nums[i]];
+ }
+ }
+ return dp[target];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution4().canPartition(new int[]{1, 2, 5}));
+ //System.out.println(new Solution3().canPartition(new int[]{1, 5, 11, 5}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..4fb45d2c
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,53 @@
+package cn.lastwhisper.leetcode.dynamic.完全平方数_279_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/perfect-squares/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 四平方定理:每个正整数均可表为四个整数的平方和(其中有些整数可以为零),满足n=(4^a)*(8b+7)
+ * 1、任何正整数都可以拆分成不超过4个数的平方和 ---> 答案只可能是1,2,3,4
+ * 2、如果一个数正好可以拆成4个数的平方和,则这个数还满足 n = (4^a)*(8b+7) --->
+ * 因此可以先看这个数是否满足上述公式,如果不满足,答案就是1,2,3了
+ * 3、如果这个数本来就是某个数的平方,那么答案就是1,否则答案就只剩2,3了
+ * 4、如果答案是2,即n=a^2+b^2,那么我们可以枚举a,来验证,如果验证通过则答案是2
+ * 5、只能是3
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int numSquares(int n) {
+ // 1、如果n可以拆成四个数的平方,n=(4^a)*(8b+7)
+ while (n % 4 == 0) {
+ n /= 4;
+ }
+ if (n % 8 == 7) {
+ return 4;
+ }
+ /*
+ * 2、如果n本来就是某个数的平方,那么答案就是1
+ * 3、如果答案是2,即n=a^2+b^2,那么我们可以枚举a,来验证,如果验证通过则答案是2
+ */
+ int a = 0,b;
+ while ((a * a) < n) {
+ b = (int) Math.sqrt(n - (a * a));
+ if ((a * a) + (b * b) == n) {
+ if (a != 0 && b != 0) {
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+ a++;
+ }
+ // 4、否则为3
+ return 3;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().numSquares(12));
+ System.out.println(new Solution1().numSquares(13));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..171df1c6
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.dynamic.完全平方数_279_中等;
+
+import javafx.util.Pair;
+
+import java.util.LinkedList;
+
+/**
+ * 题目地址:https://leetcode-cn.com/problems/perfect-squares/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * 图,使用visited数组,记录每一个入队元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+public class Solution2 {
+
+ public int numSquares(int n) {
+
+ LinkedList> queue = new LinkedList>();
+ queue.addLast(new Pair(n, 0));
+
+ boolean[] visited = new boolean[n + 1];
+ visited[n] = true;
+
+ while (!queue.isEmpty()) {
+ Pair front = queue.removeFirst();
+ int num = front.getKey();
+ int step = front.getValue();
+
+ if (num == 0)
+ return step;
+
+ for (int i = 1; num - i * i >= 0; i++)
+ if (!visited[num - i * i]) {
+ queue.addLast(new Pair<>(num - i * i, step + 1));
+ visited[num - i * i] = true;
+ }
+ }
+
+ throw new IllegalStateException("No Solution.");
+ }
+
+ public static void main(String[] args) {
+
+ System.out.println((new Solution2()).numSquares(12));
+ System.out.println((new Solution2()).numSquares(13));
+ }
+}
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..7b63b225
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\345\256\214\345\205\250\345\271\263\346\226\271\346\225\260_279_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,23 @@
+package cn.lastwhisper.leetcode.dynamic.完全平方数_279_中等;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/perfect-squares/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:01背包
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int numSquares(int n) {
+
+ return 3;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().numSquares(12));
+ System.out.println(new Solution3().numSquares(13));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\210\263\346\260\224\347\220\203_312_\345\233\260\351\232\276/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\210\263\346\260\224\347\220\203_312_\345\233\260\351\232\276/Solution1.java"
new file mode 100644
index 00000000..3b134cab
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\210\263\346\260\224\347\220\203_312_\345\233\260\351\232\276/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.dynamic.戳气球_312_困难;
+
+import org.junit.Assert;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+class Solution1 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/burst-balloons/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归回溯
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n!)
+ * 空间复杂度:O(n!)
+ */
+ public int maxCoins(int[] nums) {
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ List arrayList = Arrays.stream(nums)
+ .boxed()
+ .collect(Collectors.toList());
+ return helper(arrayList, 0);
+ }
+
+ private int helper(List nums, int coins) {
+ if (nums.size() == 0) {
+ return coins;
+ }
+ int res = 0;
+ for (int i = 0; i < nums.size(); i++) {
+ int tmp = nums.get(i);
+ int delta = nums.get(i)
+ * (i - 1 < 0 ? 1 : nums.get(i - 1))
+ * (i + 1 > nums.size() - 1 ? 1 : nums.get(i + 1));
+ nums.remove(i);
+ res = Math.max(res, helper(nums, coins + delta));
+ nums.add(i, tmp);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(167, new Solution1().maxCoins(new int[]{3, 1, 5, 8}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..2debb5d8
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,50 @@
+package cn.lastwhisper.leetcode.dynamic.打家劫舍_198_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/house-robber/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 1、状态定义:dp[i]表示偷到至第i个房子,所获取最大的利益
+ * 2、状态分析
+ * 初始:
+ * dp[0] = nums[0];
+ * dp[1] = Math.max(nums[0], nums[1])
+ * 方程:
+ * dp[i]=max(dp[i-2]+num[i],dp[i-1])
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int rob(int[] nums) {
+ // 特判
+ if (nums == null || nums.length == 0) {
+ return 0;
+ }
+ if (nums.length == 1) {
+ return nums[0];
+ }
+ if (nums.length == 2) {
+ return Math.max(nums[0], nums[1]);
+ }
+
+ int[] dp = new int[nums.length];
+ dp[0] = nums[0];
+ dp[1] = Math.max(nums[0], nums[1]);
+
+ for (int i = 2; i < nums.length; i++) {
+ dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
+ }
+
+ return dp[nums.length - 1];
+ }
+
+ public static void main(String[] args) {
+ int[] array = new int[]{2, 1, 1, 2};
+ //int[] array = new int[]{2, 7, 9, 3, 1};
+ //int[] array = new int[]{1, 2, 3, 1};
+ System.out.println(new Solution1().rob(array));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..0992ccf8
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.dynamic.打家劫舍_198_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/house-robber/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int rob(int[] nums) {
+ int n = nums.length;
+ if (n == 0) {
+ return 0;
+ }
+ // memo[i] 表示考虑抢劫 nums[i...n-1] 所能获得最大收益(不是说一定从 i 开始抢劫)
+ int[] memo = new int[n];
+ // 先考虑最简单的情况
+ memo[n - 1] = nums[n - 1];
+ for (int i = n - 2; i >= 0; i--) {
+ // memo[i] 的取值在考虑抢劫 i 号房子和不考虑抢劫之间取最大值
+ for (int j = i; j < n; j++) {
+ memo[i] = Math.max(memo[i], nums[j] + (j + 2 < n ? memo[j + 2] : 0));
+ }
+ }
+
+ return memo[0];
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().rob(new int[]{2, 1, 1, 2}));
+ System.out.println(new Solution2().rob(new int[]{2, 7, 9, 3, 1}));
+ System.out.println(new Solution2().rob(new int[]{1, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution1.java"
new file mode 100644
index 00000000..9a0af284
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution1.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.dynamic.打家劫舍_198_简单.recursion;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/house-robber/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:O()
+ * 空间复杂度:O()
+ */
+ public int rob(int[] nums) {
+ return tryRob(0, nums);
+ }
+
+ public int tryRob(int index, int[] nums) {
+ if (index >= nums.length) {
+ return 0;
+ }
+ int max = 0, res;
+ for (int i = index; i < nums.length; i++) {
+ res = nums[i] + tryRob(i + 2, nums);
+ max = Math.max(max, res);
+ System.out.println("max:" + max + " res:" + res);
+ }
+
+ return max;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().rob(new int[]{2, 1, 1, 2}));
+// System.out.println(new Solution1().rob(new int[]{2, 7, 9, 3, 1}));
+ //System.out.println(new Solution1().rob(new int[]{1, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution2.java"
new file mode 100644
index 00000000..30025c16
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\211\223\345\256\266\345\212\253\350\210\215_198_\347\256\200\345\215\225/recursion/Solution2.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.dynamic.打家劫舍_198_简单.recursion;
+
+import java.util.Arrays;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/house-robber/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归+备忘录
+ * 状态:考虑抢劫 nums[index...num.length) 这个范围内的所有房子
+ * 状态转移:tryRob(n) = Max{rob(0) + tryRob(2), rob(1) + tryRob(3)... rob(n-3) + tryRob(n-1), rob(n-2), rob(n-1)}
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int rob(int[] nums) {
+ int[] memo = new int[nums.length];
+ // 必须先初始化为-1,有特殊用例
+ Arrays.fill(memo, -1);
+ return tryRob(0, nums, memo);
+ }
+
+ public int tryRob(int index, int[] nums, int[] memo) {
+ if (index >= nums.length) {
+ return 0;
+ }
+ if (memo[index] != -1) {
+ return memo[index];
+ }
+ int max = 0;
+ for (int i = index; i < nums.length; i++) {
+ max = Math.max(max, nums[i] + tryRob(i + 2, nums, memo));
+ }
+ memo[index] = max;
+ return max;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().rob(new int[]{2, 1, 1, 2}));
+ System.out.println(new Solution1().rob(new int[]{2, 7, 9, 3, 1}));
+ System.out.println(new Solution2().rob(new int[]{1, 2, 3, 1}));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..f8da2c87
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.dynamic.整数拆分_343_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/integer-break/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:树形递归+回溯(28超时)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private int max = 0;
+
+ public int integerBreak(int n) {
+ dfs(n, n, 1);
+ return max;
+ }
+
+ private void dfs(int n, int temp, int result) {
+ if (temp == 0) {
+ max = Math.max(max, result);
+ }
+ // 至少拆分出1,至多拆分出n-1
+ // temp-i >= 0(剪枝)、i < n(剪枝)
+ for (int i = 1; temp - i >= 0 && i < n; i++) {
+ temp = temp - i;
+ result = result * i;
+ dfs(n, temp, result);
+ temp = temp + i;
+ result = result / i;
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().integerBreak(2));//1
+ System.out.println(new Solution1().integerBreak(3));//2
+ //System.out.println(new Solution1().integerBreak(10));//36
+ //System.out.println(new Solution1().integerBreak(27));//19683
+ //System.out.println(new Solution1().integerBreak(28));//26244
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..2258f9c5
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.dynamic.整数拆分_343_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/integer-break/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:树形递归
+ * n拆分最大乘积 f(n)=max(1*(n-1),2*(n-2),...(n-1)*1)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^n)
+ * 空间复杂度:O(n)
+ */
+ public int integerBreak(int n) {
+ return dfs(n);
+ }
+
+ private int dfs(int n) {
+ if (n == 1) {
+ return 1;
+ }
+ int result = -1;
+ for (int i = 1; i < n; i++) {
+ result = Math.max(result, Math.max(i * (n - i), i * dfs(n - i)));
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().integerBreak(2));//1
+ System.out.println(new Solution2().integerBreak(3));//2
+ System.out.println(new Solution2().integerBreak(10));//36
+ System.out.println(new Solution2().integerBreak(27));//19683
+ System.out.println(new Solution2().integerBreak(28));//26244
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..e75e52ac
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.dynamic.整数拆分_343_中等;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/integer-break/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:树形递归——记忆化搜索
+ * n拆分最大乘积 f(n)=max(1*(n-1),2*(n-2),...(n-1)*1)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int integerBreak(int n) {
+ int[] memo = new int[n + 1];
+ return dfs(n, memo);
+ }
+
+ private int dfs(int n, int[] memo) {
+ if (n == 1) {
+ return 1;
+ }
+ if (memo[n] != 0) {
+ return memo[n];
+ }
+ int result = -1;
+ for (int i = 1; i < n; i++) {
+ // 分解成i、n-i;
+ result = Math.max(result, Math.max(i * (n - i), i * dfs(n - i, memo)));
+ }
+ memo[n] = result;
+ return result;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().integerBreak(2));//1
+ System.out.println(new Solution3().integerBreak(3));//2
+ System.out.println(new Solution3().integerBreak(10));//36
+ System.out.println(new Solution3().integerBreak(27));//19683
+ System.out.println(new Solution3().integerBreak(28));//26244
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution4.java"
new file mode 100644
index 00000000..516a254c
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\225\264\346\225\260\346\213\206\345\210\206_343_\344\270\255\347\255\211/Solution4.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.dynamic.整数拆分_343_中等;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/integer-break/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int integerBreak(int n) {
+ int[] memo = new int[n + 1];
+ memo[1] = 1;
+ for (int i = 2; i <= n; i++) {
+ for (int j = 1; j < i; j++) {
+ memo[i] = Math.max(memo[i], Math.max(j * (i - j), j * memo[i - j]));
+ }
+ }
+ return memo[n];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution4().integerBreak(2));//1
+ System.out.println(new Solution4().integerBreak(3));//2
+ System.out.println(new Solution4().integerBreak(10));//36
+ System.out.println(new Solution4().integerBreak(27));//19683
+ System.out.println(new Solution4().integerBreak(28));//26244
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..6099ecaf
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,28 @@
+package cn.lastwhisper.leetcode.dynamic.斐波那契数_509_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归(自顶向下)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int fib(int N) {
+ if (N == 0) {
+ return 0;
+ }
+ if (N == 1) {
+ return 1;
+ }
+
+ return fib(N - 1) + fib(N - 2);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().fib(3));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..f3f1d81a
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,38 @@
+package cn.lastwhisper.leetcode.dynamic.斐波那契数_509_简单;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归+记忆化搜索(自顶向下)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private Map map = new HashMap<>();
+
+ public int fib(int N) {
+ if (N == 0) {
+ return 0;
+ }
+ if (N == 1) {
+ return 1;
+ }
+
+ Integer result = map.getOrDefault(N, -1);
+ if (result == -1) {
+ result = fib(N - 1) + fib(N - 2);
+ map.put(N, result);
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().fib(10));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..da2b0339
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.dynamic.斐波那契数_509_简单;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划(自底向上)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int fib(int N) {
+ if (N < 2) {
+ return N;
+ }
+ int[] arr = new int[N + 1];
+ arr[0] = 0;
+ arr[1] = 1;
+
+ for (int i = 2; i <= N; i++) {
+ arr[i] = arr[i - 1] + arr[i - 2];
+ }
+ return arr[N];
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().fib(2));//1
+ System.out.println(new Solution3().fib(10));//55
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..ab5d3653
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,31 @@
+package cn.lastwhisper.leetcode.dynamic.斐波那契数_509_简单;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划(自底向上)的另一种写法
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int fib(int N) {
+ if (N < 2) return N;
+ int item1 = 0;
+ int item2 = 1;
+ int sum = 0;
+ for (int i = 2; i <= N; i++) {
+ sum = item1 + item2;
+ item1 = item2;
+ item2 = sum;
+ }
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution4().fib(2));
+ System.out.println(new Solution4().fib(10));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution5.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution5.java"
new file mode 100644
index 00000000..a1a6692d
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260_509_\347\256\200\345\215\225/Solution5.java"
@@ -0,0 +1,29 @@
+package cn.lastwhisper.leetcode.dynamic.斐波那契数_509_简单;
+
+class Solution5 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/fibonacci-number/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:尾递归(自底向上)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public int fib(int n) {
+ return fib(n, 0, 1);
+ }
+
+ // 第n个斐波那契,初始两个值0、1
+ private int fib(int n, int rt1, int rt2) {
+ return n == 0 ? rt1 : fib(n - 1, rt2, rt1 + rt2);
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution5().fib(2));
+ System.out.println(new Solution5().fib(10));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/README.md" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/README.md"
new file mode 100644
index 00000000..3ab287da
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/README.md"
@@ -0,0 +1,15 @@
+# 53
+
+**思路:**动态规划
+
+**状态:**dp[i]表示以num[i]为结尾的连续子序列的最大和
+
+**状态转换方程:**dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
+
+**实例:**
+
+| nums | -2 | 1 | -3 | 4 | -1 | 2 | 1 | -5 | 4 |
+| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
+| dp | -2 | 1 | -2 | 4 | 3 | 5 | `6` | 1 | 5 |
+
+
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..6270648f
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,32 @@
+package cn.lastwhisper.leetcode.dynamic.最大子序和_53_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-subarray/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int maxSubArray(int[] nums) {
+ int res = nums[0];
+ int sum = 0;
+ for (int num : nums) {
+ if (sum > 0) {
+ sum += num;
+ } else {
+ sum = num;
+ }
+ res = Math.max(res, sum);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
+ System.err.println(new Solution1().maxSubArray(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..36c8158e
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,33 @@
+package cn.lastwhisper.leetcode.dynamic.最大子序和_53_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-subarray/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 状态:dp[i]表示以num[i]为结尾的连续子序列的最大和
+ * 状态转换方程:dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public int maxSubArray(int[] nums) {
+ // 初始值
+ int res = nums[0];
+ int[] dp = new int[nums.length];
+ dp[0] = nums[0];
+ for (int i = 1; i < nums.length; i++) {
+ dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
+ res = Math.max(res, dp[i]);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
+ System.err.println(new Solution2().maxSubArray(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..e18c979f
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\244\247\345\255\220\345\272\217\345\222\214_53_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.dynamic.最大子序和_53_简单;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/maximum-subarray/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 状态:dp[i]表示以num[i]为结尾的连续子序列的最大和
+ * 状态转换方程:dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])
+ *
+ * 优化空间:只需要两个变量就可以完成动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int maxSubArray(int[] nums) {
+ // 初始值
+ int res = nums[0];
+ int prev = nums[0], current;
+ for (int i = 1; i < nums.length; i++) {
+ current = Math.max(prev + nums[i], nums[i]);
+ res = Math.max(res, current);
+ prev = current;
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
+ System.err.println(new Solution3().maxSubArray(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..238e1a17
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,66 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——自顶向下
+ * 1、状态定义:dp[i][j]表示包含第i行第j列元素的最小路径和
+ * 2、状态分析
+ * 初始:
+ * dp[0][0]=grid[0][0]
+ * 常规:
+ * grid[i][j]一定会经过grid[i][j-1]或者grid[i-1][j]
+ * 所以状态dp[i][j]一定等于dp[i][j-1]或者dp[i-1][j]的最小值+grid[i][j]
+ * 状态转换方程:
+ * dp[i][j]=min(dp[i][j-1],dp[i-1][j])+grid[i][j]
+ * 特殊:
+ * grid[i][0]没有左边 只能从上边grid[i-1][0]经过
+ * grid[0][j]没有上边 只能从左边grid[0][j-1]经过
+ * 状态转换方程:
+ * dp[i][0]=dp[i-1][0]+grid[i][j]
+ * dp[0][j]=dp[0][j-1]+grid[i][j]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public int minPathSum(int[][] grid) {
+ // 特判
+ if (grid == null || grid.length == 0) {
+ return 0;
+ }
+ int row = grid.length;
+ int column = grid[0].length;
+
+ int[][] dp = new int[row][column];
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < column; j++) {
+ if (i == 0 && j == 0) {
+ // 起点
+ dp[0][0] = grid[0][0];
+ } else if (i == 0) {
+ // 从左边来
+ dp[i][j] = dp[i][j - 1] + grid[i][j];
+ } else if (j == 0) {
+ // 从上面来
+ dp[i][j] = dp[i - 1][j] + grid[i][j];
+ } else {
+ dp[i][j] = Math.min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j];
+ }
+ }
+ }
+
+ return dp[row - 1][column - 1];
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution1().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..eb26a680
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,53 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——基于思路二
+ * 空间优化:
+ * 只需要左边和上边的值保存即可,一维数组dp[j]存储上边的值,
+ * dp[j-1]存储左边的值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int minPathSum(int[][] grid) {
+ // 特判
+ if (grid == null || grid.length == 0) {
+ return 0;
+ }
+ int row = grid.length;
+ int column = grid[0].length;
+
+ int[] dp = new int[column];
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < column; j++) {
+ if (i == 0 && j == 0) {
+ // 起点
+ dp[0] = grid[0][0];
+ } else if (i == 0) {
+ // 从左边来
+ dp[j] = dp[j - 1] + grid[i][j];
+ } else if (j == 0) {
+ // 从上面来
+ dp[j] = dp[j] + grid[i][j];
+ } else {
+ dp[j] = Math.min(dp[j - 1], dp[j]) + grid[i][j];
+ }
+ }
+ }
+ return dp[column - 1];
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution2().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..fd10d4d6
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,63 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——自底向上
+ * 1、状态定义:dp[i][j]表示包含第i行第j列元素的最小路径和
+ * 2、状态分析
+ * 初始:
+ * dp[m][n]=grid[m][n]
+ * 常规:
+ * grid[i][j]一定会经过grid[i][j+1]或者grid[i+1][j]
+ * 所以状态dp[i][j]一定等于dp[i][j+1]或者dp[i+1][j]的最小值+grid[i][j]
+ * 状态转换方程:
+ * dp[i][j]=min(dp[i][j+1],dp[i+1][j])+grid[i][j]
+ * 特殊:
+ * 矩阵m*n
+ * grid[i][n]没有右边 只能从下边grid[i+1][n]经过
+ * grid[m][j]没有下边 只能从右边grid[m][j+1]经过
+ * 状态转换方程:
+ * dp[i][n]=dp[i+1][n]+grid[i][j]
+ * dp[m][j]=dp[m][j+1]+grid[i][j]
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public int minPathSum(int[][] grid) {
+ // 特判
+ if (grid == null || grid.length == 0) {
+ return 0;
+ }
+ int row = grid.length;
+ int column = grid[0].length;
+
+ int[][] dp = new int[row][column];
+ for (int i = row - 1; i >= 0; i--) {
+ for (int j = column - 1; j >= 0; j--) {
+ if (i == row - 1 && j == column - 1) {
+ dp[i][j] = grid[i][j];
+ } else if (j == column - 1) {
+ dp[i][j] = dp[i + 1][j] + grid[i][j];
+ } else if (i == row - 1) {
+ dp[i][j] = dp[i][j + 1] + grid[i][j];
+ } else {
+ dp[i][j] = Math.min(dp[i + 1][j], dp[i][j + 1]) + grid[i][j];
+ }
+ }
+ }
+ return dp[0][0];
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution3().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution4.java"
new file mode 100644
index 00000000..4f29098e
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/Solution4.java"
@@ -0,0 +1,50 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划——基于思路三
+ * 空间优化:
+ * 只需要左边和上边的值保存即可,一维数组dp[j]存储下边的值,
+ * dp[j+1]存储右边的值
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public int minPathSum(int[][] grid) {
+ // 特判
+ if (grid == null || grid.length == 0) {
+ return 0;
+ }
+ int row = grid.length;
+ int column = grid[0].length;
+
+ int[] dp = new int[column ];
+ for (int i = row - 1; i >= 0; i--) {
+ for (int j = column - 1; j >= 0; j--) {
+ if (i == row - 1 && j == column - 1) {
+ dp[j] = grid[i][j];
+ } else if (j == column - 1) {
+ dp[j] = dp[j] + grid[i][j];
+ } else if (i == row - 1) {
+ dp[j] = dp[j + 1] + grid[i][j];
+ } else {
+ dp[j] = Math.min(dp[j], dp[j + 1]) + grid[i][j];
+ }
+ }
+ }
+ return dp[0];
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution4().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution1.java"
new file mode 100644
index 00000000..5b1fab4e
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution1.java"
@@ -0,0 +1,55 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等.recursion;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private int min=Integer.MAX_VALUE, m, n;
+ private int[][] dir = {{0, 1}, {1, 0}};
+
+ public int minPathSum(int[][] grid) {
+ m = grid.length;
+ n = grid[0].length;
+ findMin(grid, 0, 0, grid[0][0]);
+ return min;
+ }
+
+ /**
+ * 此种递归无法缓存中间值重复使用
+ */
+ private void findMin(int[][] grid, int startX, int startY, int result) {
+ // 终点
+ if (m - 1 == startX && n - 1 == startY) {
+ min = Math.min(min, result);
+ return;
+ }
+ for (int i = 0; i < dir.length; i++) {
+ int newX = startX + dir[i][0];
+ int newY = startY + dir[i][1];
+ // 防止不合法坐标
+ if (isValid(newX, newY)) {
+ findMin(grid, newX, newY, grid[newX][newY] + result);
+ }
+ }
+ }
+
+ private boolean isValid(int x, int y) {
+ return x < m && y < n;
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution1().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution2.java"
new file mode 100644
index 00000000..4650b63c
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution2.java"
@@ -0,0 +1,50 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等.recursion;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private int m, n;
+ private int[][] dir = {{0, 1}, {1, 0}};
+
+ public int minPathSum(int[][] grid) {
+ m = grid.length;
+ n = grid[0].length;
+ return findMin(grid, 0, 0);
+ }
+
+ private int findMin(int[][] grid, int startX, int startY) {
+ if (m - 1 == startX && n - 1 == startY) {
+ return grid[startX][startY];
+ }
+ int result = Integer.MAX_VALUE;
+ for (int i = 0; i < dir.length; i++) {
+ int newX = startX + dir[i][0];
+ int newY = startY + dir[i][1];
+ if (isValid(newX, newY)) {
+ result = Math.min(result, grid[startX][startY] + findMin(grid, newX, newY));
+ }
+ }
+ return result;
+ }
+
+ private boolean isValid(int x, int y) {
+ return x < m && y < n;
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution2().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution3.java"
new file mode 100644
index 00000000..859df4d4
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution3.java"
@@ -0,0 +1,16 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等.recursion;
+
+public class Solution3 {
+ public int calculate(int[][] grid, int i, int j) {
+ // 不合法坐标
+ if (i == grid.length || j == grid[0].length) return Integer.MAX_VALUE;
+ // 终点
+ if (i == grid.length - 1 && j == grid[0].length - 1) return grid[i][j];
+ return grid[i][j] + Math.min(calculate(grid, i + 1, j), calculate(grid, i, j + 1));
+ }
+
+ public int minPathSum(int[][] grid) {
+ return calculate(grid, 0, 0);
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution4.java"
new file mode 100644
index 00000000..42cd5d42
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\345\260\217\350\267\257\345\276\204\345\222\214_64_\344\270\255\347\255\211/recursion/Solution4.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.dynamic.最小路径和_64_中等.recursion;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/minimum-path-sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归+记忆化搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(2^(m+n))。每次移动最多可以有两种选择。
+ * 空间复杂度:O(m+n)。递归的深度是 m+n。
+ */
+ private int m, n;
+ private int[][] dir = {{0, 1}, {1, 0}};
+
+ public int minPathSum(int[][] grid) {
+ m = grid.length;
+ n = grid[0].length;
+ int[][] memo = new int[m][n];
+ return findMin(grid, 0, 0, memo);
+ }
+
+ private int findMin(int[][] grid, int startX, int startY, int[][] memo) {
+ // 终点
+ if (m - 1 == startX && n - 1 == startY) {
+ return grid[startX][startY];
+ }
+ if (memo[startX][startY] != 0) {
+ return memo[startX][startY];
+ }
+ int result = Integer.MAX_VALUE;
+ for (int i = 0; i < dir.length; i++) {
+ int newX = startX + dir[i][0];
+ int newY = startY + dir[i][1];
+ if (isValid(newX, newY)) {
+ result = Math.min(result, grid[startX][startY] + findMin(grid, newX, newY, memo));
+ }
+ }
+ memo[startX][startY] = result;
+ return result;
+ }
+
+ private boolean isValid(int x, int y) {
+ return x < m && y < n;
+ }
+
+ public static void main(String[] args) {
+ int[][] grid = {
+ {1, 3, 1},
+ {1, 5, 1},
+ {4, 2, 1}
+ };
+ System.out.println(new Solution4().minPathSum(grid));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227_300_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227_300_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..58130d3c
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\344\270\212\345\215\207\345\255\220\345\272\217\345\210\227_300_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.dynamic.最长上升子序列_300_中等;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-increasing-subsequence/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * 状态定义:dp[i]表示以num[i]为结尾的上升子序列
+ * 转换方程:
+ * j∈[0,i]
+ * dp[i]=max(dp[j]+1,dp[i])
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int lengthOfLIS(int[] nums) {
+ if (nums.length == 0) {
+ return 0;
+ }
+ int res = 1;
+ int[] dp = new int[nums.length];
+ Arrays.fill(dp, 1);
+ // j∈[0,i)
+ for (int i = 1; i < nums.length; i++) {
+ // num[i]是否能做num[0,i)中某个数的结尾
+ for (int j = 0; j < i; j++) {
+ if (nums[i] > nums[j]) {
+ // num[i]能以num[j]为结尾,更新dp[i]
+ // 为什么要Math.max(dp[j] + 1, dp[i]),而不是直接dp[j] + 1?
+ // {1, 3, 6, 7, 9, 4, 10, 5, 6},i=6时,最长子序列是1,3,6,7,9,10,而不是1,3,4,10
+ dp[i] = Math.max(dp[j] + 1, dp[i]);
+ }
+ }
+ // 记录最大系列长度
+ res = Math.max(res, dp[i]);
+ }
+ return res;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = {-2, -1};
+ int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
+ System.err.println(new Solution1().lengthOfLIS(nums));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..57e78b76
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.dynamic.最长公共子序列_1143_中等;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-common-subsequence/
+ * 题号:1143/最长公共子序列(LCS)
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int longestCommonSubsequence(String text1, String text2) {
+ return dfs(text1, text2, text1.length() - 1, text2.length() - 1);
+ }
+
+ public int dfs(String text1, String text2, int i, int j) {
+ if (i == -1 || j == -1) {
+ return 0;
+ }
+ if (text1.charAt(i) == text2.charAt(j)) {
+ // 这边找到一个 lcs 的元素,继续往前找
+ return dfs(text1, text2, i - 1, j - 1) + 1;
+ } else {
+ // 谁能让 lcs 最长,就听谁的
+ return Math.max(dfs(text1, text2, i - 1, j), dfs(text1, text2, i, j - 1));
+ }
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(3, new Solution1().longestCommonSubsequence("abcde", "ace"));
+ Assert.assertEquals(3, new Solution1().longestCommonSubsequence("abc", "abc"));
+ Assert.assertEquals(0, new Solution1().longestCommonSubsequence("abc", "def"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..2467ad54
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\346\234\200\351\225\277\345\205\254\345\205\261\345\255\220\345\272\217\345\210\227_1143_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.dynamic.最长公共子序列_1143_中等;
+
+import org.junit.Assert;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-common-subsequence/
+ * 题号:1143/最长公共子序列(LCS)
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划(自顶向下)
+ * 一、状态定义:dp[i][j]表示,对于 s1[1..i] 和 s2[1..j],它们的 LCS 长度是 dp[i][j]。
+ * 二、初始状态:
+ * 三、转换方程:
+ *
+ *
+ * https://leetcode-cn.com/problems/longest-common-subsequence/solution/dong-tai-gui-hua-zhi-zui-chang-gong-gong-zi-xu-lie/
+ * https://www.jianshu.com/p/6451410be00a
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int longestCommonSubsequence(String text1, String text2) {
+ int m = text1.length() + 1, n = text2.length() + 1;
+ int[][] dp = new int[m][n];
+
+ for (int i = 1; i < m; i++) {
+ for (int j = 1; j < n; j++) {
+ if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
+ // 这边找到一个 lcs 的元素,继续往前找
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ // 谁能让 lcs 最长,就听谁的
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+
+ return dp[m - 1][n - 1];
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(4, new Solution1().longestCommonSubsequence("google", "elgoog"));
+ //Assert.assertEquals(3, new Solution3().longestCommonSubsequence("abcde", "ace"));
+ //Assert.assertEquals(3, new Solution3().longestCommonSubsequence("abc", "abc"));
+ //Assert.assertEquals(0, new Solution3().longestCommonSubsequence("abc", "def"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..999e9afe
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,26 @@
+package cn.lastwhisper.leetcode.dynamic.爬楼梯_70_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归(自顶向下)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int climbStairs(int n) {
+ if (n == 0 || n == 1) {
+ return 1;
+ }
+ return climbStairs(n - 1) + climbStairs(n - 2);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().climbStairs(2));
+ System.out.println(new Solution1().climbStairs(3));
+ System.out.println(new Solution1().climbStairs(4));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..04ffc448
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.dynamic.爬楼梯_70_简单;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归(自顶向下)+记忆化搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ private Map map = new HashMap<>();
+
+ public int climbStairs(int n) {
+ if (n == 0 || n == 1) {
+ return 1;
+ }
+
+ Integer result = map.getOrDefault(n, -1);
+ if (result == -1) {
+ result = climbStairs(n - 1) + climbStairs(n - 2);
+ map.put(n, result);
+ }
+ return result;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().climbStairs(2));//2
+ System.out.println(new Solution2().climbStairs(3));//3
+ System.out.println(new Solution2().climbStairs(4));//5
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..74359179
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,28 @@
+package cn.lastwhisper.leetcode.dynamic.爬楼梯_70_简单;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:尾递归(自底向上)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public int climbStairs(int n) {
+ return climbStairs(n,0,1);
+ }
+
+ private int climbStairs(int n, int rt1, int rt2) {
+ return n == 0 ? rt2 : climbStairs(n - 1, rt2, rt1 + rt2);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().climbStairs(2));//2
+ System.out.println(new Solution3().climbStairs(3));//3
+ System.out.println(new Solution3().climbStairs(4));//5
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..894e4e07
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,35 @@
+package cn.lastwhisper.leetcode.dynamic.爬楼梯_70_简单;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划(自底向上)
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+
+ public int climbStairs(int n) {
+ if (n < 2) {
+ return n;
+ }
+ int[] arr = new int[n + 1];
+ arr[0] = 1;
+ arr[1] = 1;
+
+ for (int i = 2; i <= n; i++) {
+ arr[i] = arr[i - 1] + arr[i - 2];
+ }
+ return arr[n];
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution4().climbStairs(2));//2
+ System.out.println(new Solution4().climbStairs(3));//3
+ System.out.println(new Solution4().climbStairs(4));//5
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution5.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution5.java"
new file mode 100644
index 00000000..432d9bf2
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\347\210\254\346\245\274\346\242\257_70_\347\256\200\345\215\225/Solution5.java"
@@ -0,0 +1,33 @@
+package cn.lastwhisper.leetcode.dynamic.爬楼梯_70_简单;
+
+class Solution5 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/climbing-stairs/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划(自底向上)的另一种写法
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int climbStairs(int n) {
+ if (n < 2) return n;
+ int item1 = 1;
+ int item2 = 1;
+ int sum = 0;
+ for (int i = 2; i <= n; i++) {
+ sum = item1 + item2;
+ item1 = item2;
+ item2 = sum;
+ }
+ return sum;
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution5().climbStairs(2));//2
+ System.out.println(new Solution5().climbStairs(3));//3
+ System.out.println(new Solution5().climbStairs(4));//5
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\350\247\243\347\240\201\346\226\271\346\263\225_91_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\350\247\243\347\240\201\346\226\271\346\263\225_91_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..13b0fad0
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\350\247\243\347\240\201\346\226\271\346\263\225_91_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,33 @@
+package cn.lastwhisper.leetcode.dynamic.解码方法_91_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/decode-ways/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int numDecodings(String s) {
+ if (s.charAt(0) == '0') return 0;
+ int pre = 1, curr = 1;//dp[-1]= dp[0]= 1
+ for (int i = 1; i < s.length(); i++) {
+ int tmp = curr;
+ if (s.charAt(i) == '0')
+ if (s.charAt(i - 1) == '1' || s.charAt(i - 1) == '2') curr = pre;
+ else return 0;
+ else if (s.charAt(i - 1) == '1' || (s.charAt(i - 1) == '2' && s.charAt(i) >= '1' && s.charAt(i) <= '6'))
+ curr = curr + pre;
+ pre = tmp;
+ }
+ return curr;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().numDecodings("12"));//"AB"(1 2)或者 "L"(12)
+ System.out.println(new Solution1().numDecodings("226"));// "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6)
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..e2508b5e
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,45 @@
+package cn.lastwhisper.leetcode.dynamic.零钱兑换_322_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/coin-change/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归dfs-暴力搜索
+ * 先找到子问题
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^k*k),n^k个结点
+ * 空间复杂度:O(n^k)
+ */
+ public int coinChange(int[] coins, int amount) {
+ return tryCoinChange(coins, amount);
+ }
+
+ public int tryCoinChange(int[] coins, int amount) {
+ if (amount == 0) {
+ return 0;
+ }
+ if (amount < 0) {
+ return -1;
+ }
+ int freq = Integer.MAX_VALUE;
+ for (int coin : coins) {
+ int subFreq = tryCoinChange(coins, amount - coin);
+ if (subFreq == -1) {
+ continue;
+ }
+ freq = Math.min(freq, 1 + subFreq);
+ }
+
+ return freq == Integer.MAX_VALUE ? -1 : freq;
+ }
+
+ public static void main(String[] args) {
+ //int[] coins = {1, 2, 5};
+ //int amount = 11;
+ int[] coins = {2};
+ int amount = 3;
+ System.err.println(new Solution1().coinChange(coins, amount));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..71b89a41
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.dynamic.零钱兑换_322_中等;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/coin-change/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:递归dfs-暴力搜索-备忘录
+ * 先找到子问题
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^k*k),n^k个结点
+ * 空间复杂度:O()
+ */
+ public int coinChange(int[] coins, int amount) {
+ int[] memo = new int[amount + 1];
+ return tryCoinChange(coins, amount, memo);
+ }
+
+ public int tryCoinChange(int[] coins, int amount, int[] memo) {
+ if (amount == 0) {
+ return 0;
+ }
+ if (amount < 0) {
+ return -1;
+ }
+ if (memo[amount] != 0) {
+ return memo[amount];
+ }
+ int freq = Integer.MAX_VALUE;
+ for (int coin : coins) {
+ int subFreq = tryCoinChange(coins, amount - coin, memo);
+ // 子问题无解,不更新freq
+ if (subFreq == -1) {
+ continue;
+ }
+ freq = Math.min(freq, 1 + subFreq);
+ }
+ // 所有子问题都无解,返回-1
+ memo[amount] = freq == Integer.MAX_VALUE ? -1 : freq;
+ return memo[amount];
+ }
+
+ public static void main(String[] args) {
+ //int[] coins = {1, 2, 5};
+ //int amount = 11;
+ int[] coins = {2};
+ int amount = 3;
+ System.err.println(new Solution2().coinChange(coins, amount));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..7c669b3f
--- /dev/null
+++ "b/algorithms/leetcode/dynamic/src/main/java/cn/lastwhisper/leetcode/dynamic/\351\233\266\351\222\261\345\205\221\346\215\242_322_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.dynamic.零钱兑换_322_中等;
+
+import java.util.Arrays;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/coin-change/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int coinChange(int[] coins, int amount) {
+ int max = amount + 1;
+ // 确定状态
+ int[] dp = new int[amount + 1];
+ Arrays.fill(dp, max);
+ dp[0] = 0;
+ for (int i = 1; i <= amount; i++) {
+ for (int coin : coins) {
+ if (i >= coin) {
+ dp[i] = Math.min(dp[i], dp[i - coin] + 1);
+ }
+ }
+ }
+ return dp[amount] > amount ? -1 : dp[amount];
+ }
+
+ public static void main(String[] args) {
+ int[] coins = {1, 2, 5};
+ int amount = 11;
+ //int[] coins = {2};
+ //int amount = 3;
+ System.err.println(new Solution3().coinChange(coins, amount));
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/greedy/README.md b/algorithms/leetcode/greedy/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/algorithms/leetcode/greedy/pom.xml b/algorithms/leetcode/greedy/pom.xml
new file mode 100644
index 00000000..8db3234b
--- /dev/null
+++ b/algorithms/leetcode/greedy/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper.leetcode
+ greedy
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..b20adb00
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,59 @@
+package cn.lastwhisper.leetcode.greedy.买卖股票的最佳时机II_122_简单;
+
+class Solution1 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力搜索
+ * 1、买入之后:1.不操作 2.卖出
+ * 2、不操作之后:1.不操作 2.买入
+ * 3、卖出之后:1.不操作 2.买入
+ * 4、初始:1.不操作 2.买入
+ *
+ * 共有两种状态:
+ * 1 持股
+ * 2、3、4 未持股
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(2^n)
+ * 空间复杂度:O(1)
+ */
+ public int maxProfit(int[] prices) {
+ tryProfit(prices, 0, false, 0);
+ return profit;
+ }
+
+ int profit = 0;
+
+ /**
+ * @param prices 股价数组
+ * @param index 第几天
+ * @param status true持股,false不持股
+ * @param profit 当前收益
+ */
+ private void tryProfit(int[] prices, int index, boolean status, int profit) {
+ if (index == prices.length ) {
+ this.profit = Math.max(this.profit, profit);
+ return;
+ }
+ // 不卖也不买
+ tryProfit(prices, index + 1, status, profit);
+ if (status) {
+ // 尝试卖出
+ tryProfit(prices, index + 1, false, profit + prices[index]);
+ } else {
+ // 尝试买入
+ tryProfit(prices, index + 1, true, profit - prices[index]);
+ }
+ }
+
+
+ public static void main(String[] args) {
+ //int[] prices = {7, 1, 5, 3, 6, 4};
+ int[] prices = {1, 2, 3, 4, 5};
+ System.err.println(new Solution1().maxProfit(prices));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..c5097fde
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.greedy.买卖股票的最佳时机II_122_简单;
+
+class Solution2 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:暴力搜索
+ * 1、买入之后:1.不操作 2.卖出
+ * 2、不操作之后:1.不操作 2.买入
+ * 3、卖出之后:1.不操作 2.买入
+ * 4、初始:1.不操作 2.买入
+ *
+ * 共有两种状态:
+ * 1 持股
+ * 2、3、4 未持股
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(2^n)
+ * 空间复杂度:O(1)
+ */
+ public int maxProfit(int[] prices) {
+ return tryProfit(prices, 0, false, 0);
+ }
+
+ /**
+ * @param prices 股价数组
+ * @param index 第几天
+ * @param status true持股,false不持股
+ * @param profit 当前收益
+ */
+ private int tryProfit(int[] prices, int index, boolean status, int profit) {
+ if (index == prices.length) {
+ return profit;
+ }
+ // 不操作
+ int currentMax = tryProfit(prices, index + 1, status, profit);
+ if (status) {
+ // 尝试卖出
+ currentMax = Math.max(currentMax, tryProfit(prices, index + 1, false, profit + prices[index]));
+ } else {
+ // 尝试买入
+ currentMax = Math.max(currentMax, tryProfit(prices, index + 1, true, profit - prices[index]));
+ }
+ return currentMax;
+ }
+
+
+ public static void main(String[] args) {
+ //int[] prices = {7, 1, 5, 3, 6, 4};
+ int[] prices = {1, 2, 3, 4, 5};
+ System.err.println(new Solution2().maxProfit(prices));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..3d42e75f
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,25 @@
+package cn.lastwhisper.leetcode.greedy.买卖股票的最佳时机II_122_简单;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:动态规划
+ *
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int maxProfit(int[] prices) {
+
+ return 0;
+ }
+
+ public static void main(String[] args) {
+ //int[] prices = {7, 1, 5, 3, 6, 4};
+ int[] prices = {1, 2, 3, 4, 5};
+ System.err.println(new Solution3().maxProfit(prices));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution4.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution4.java"
new file mode 100644
index 00000000..cf2187c5
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II_122_\347\256\200\345\215\225/Solution4.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.greedy.买卖股票的最佳时机II_122_简单;
+
+class Solution4 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:贪心算法
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public int maxProfit(int[] prices) {
+ int profit = 0, temp;
+ for (int i = prices.length - 1; i > 0; i--) {
+ temp = prices[i] - prices[i - 1];
+ if (temp > 0) {
+ profit += temp;
+ }
+ }
+ return profit;
+ }
+
+ public static void main(String[] args) {
+ //int[] prices = {7, 1, 5, 3, 6, 4};
+ int[] prices = {1, 2, 3, 4, 5};
+ System.err.println(new Solution4().maxProfit(prices));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\206\345\217\221\351\245\274\345\271\262_455_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\206\345\217\221\351\245\274\345\271\262_455_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..ea182c46
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\206\345\217\221\351\245\274\345\271\262_455_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,36 @@
+package cn.lastwhisper.leetcode.greedy.分发饼干_455_简单;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/assign-cookies/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:贪心算法
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ */
+ public int findContentChildren(int[] g, int[] s) {
+ int count = 0;
+ // 排序,从小到大
+ Arrays.sort(g);
+ Arrays.sort(s);
+ for (int i = g.length - 1, j = s.length - 1; i >= 0 && j >= 0; i--) {
+ // 最大的饼干,满足最贪心的孩子
+ if (s[j] >= g[i]) {
+ count++;
+ j--;
+ }
+ }
+ return count;
+ }
+
+ public static void main(String[] args) {
+ //int[] g = {1,2},s={1,2,3};
+ int[] g = {10, 9, 8, 7}, s = {5, 6, 7, 8};
+ System.out.println(new Solution1().findContentChildren(g, s));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..c7b20b07
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,37 @@
+package cn.lastwhisper.leetcode.greedy.判断子序列_392_简单;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/is-subsequence/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:贪心算法
+ * 将s依次放入t搜索
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isSubsequence(String s, String t) {
+ // 记录上一个字符在t中的位置
+ int prev = 0;
+ for (int i = 0; i < s.length(); i++) {
+ // 从上一个字符的位置之后进行搜索
+ int index = t.indexOf(s.charAt(i), prev);
+ if (index == -1) {
+ return false;
+ }
+ prev = index + 1;
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ //String s = "abc", t = "ahbgdc";//true
+ //String s = "axc", t = "ahbgdc";//false
+ String s = "acb", t = "ahbgdc";//false
+ //String s = "leetcode", t = "ylyeyeyeytycyoydye";//true
+ //String s = "leeeeetcode", t = "yyyyylyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyycyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyoyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyydyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
+ System.err.println(new Solution1().isSubsequence(s, t));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..f6800edb
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\345\210\244\346\226\255\345\255\220\345\272\217\345\210\227_392_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,35 @@
+package cn.lastwhisper.leetcode.greedy.判断子序列_392_简单;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/is-subsequence/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:贪心算法
+ * 如果s是t的子序列,也就是说s中的所有字符都会按照顺序出现在t中
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isSubsequence(String s, String t) {
+ int sIndex = 0, tIndex = 0;
+ // 如果s是t的子序列,也就是说s中的所有字符都会按照顺序出现在t中
+ while (sIndex < s.length() && tIndex < t.length()) {
+ if (s.charAt(sIndex) == t.charAt(tIndex)) {
+ sIndex++;
+ }
+ tIndex++;
+ }
+ return sIndex == s.length();
+ }
+
+ public static void main(String[] args) {
+ //String s = "abc", t = "ahbgdc";//true
+ //String s = "axc", t = "ahbgdc";//false
+ //String s = "acb", t = "ahbgdc";//false
+ String s = "leetcode", t = "ylyeyeyeytycyoydye";//true
+ //String s = "leeeeetcode", t = "yyyyylyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyycyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyoyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyydyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyeyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
+ System.err.println(new Solution2().isSubsequence(s, t));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264_435_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264_435_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..49a435ae
--- /dev/null
+++ "b/algorithms/leetcode/greedy/src/main/java/cn/lastwhisper/leetcode/greedy/\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264_435_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,22 @@
+package cn.lastwhisper.leetcode.greedy.无重叠区间_435_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/non-overlapping-intervals/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int eraseOverlapIntervals(int[][] intervals) {
+
+ return 0;
+ }
+
+ public static void main(String[] args) {
+
+ }
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/hashtable/pom.xml b/algorithms/leetcode/hashtable/pom.xml
new file mode 100644
index 00000000..fcce6862
--- /dev/null
+++ b/algorithms/leetcode/hashtable/pom.xml
@@ -0,0 +1,16 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ cn.lastwhisper.leetcode
+ hashtable
+
+
+
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..6d490bfd
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,62 @@
+package cn.lastwhisper.leetcode.hashtable.三数之和_15_中等;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/3sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:如何将数组中的任意三个数,进行组合?
+ * A:三层循环,第一层i~n-2,第二层i+1~n-1,第一层i+2~n
+ * Q:如何保证无重复组合?
+ * A:先对数组排序。每一层循环时,和数组前一个数进行对比是否相等,相等说明重复了,直接跳过
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(n^2)
+ */
+ public List> threeSum(int[] nums) {
+ if (nums == null) throw new IllegalArgumentException("非法参数");
+ Arrays.sort(nums);
+ // 结果
+ List> resultList = new ArrayList<>();
+ for (int i = 0; i < nums.length - 2; i++) {
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+ for (int j = i + 1; j < nums.length - 1; j++) {
+ if (j > i + 1 && nums[j] == nums[j - 1]) {
+ continue; // 对j去重
+ }
+ for (int k = j + 1; k < nums.length; k++) {
+ if (k > j + 1 && nums[k] == nums[k - 1]) {
+ continue; // 对k去重
+ }
+ if (nums[i] + nums[j] + nums[k] == 0) {
+ resultList.add(Arrays.asList(nums[i], nums[j], nums[k]));
+ }
+ }
+ }
+ }
+
+ return resultList;
+ }
+
+ public static void main(String[] args) {
+ //new Solution1().threeSum(new int[]{-1, 0, 1, 2, -1, -4}).forEach((list) -> {
+ // System.out.println();
+ // list.forEach(System.out::print);
+ //});
+
+ // error example
+ new Solution1().threeSum(new int[]{0, 0, 0, 0}).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..25f77fb3
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,64 @@
+package cn.lastwhisper.leetcode.hashtable.三数之和_15_中等;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/3sum/
+ * -------------------------------------------------------------------
+ *
+ * -------------------------------------------------------------------
+ * 优化思路:三三组合改进,排序+双索引,并对特殊情况进行剔除
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ */
+ public List> threeSum(int[] nums) {
+ // 存储结果
+ List> resultList = new ArrayList<>();
+ if (nums == null || nums.length < 3) return resultList;
+ // 数组排序
+ Arrays.sort(nums);
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] > 0) {
+ break; //此时说明数组后面都是正值,三个数相加不可能为0
+ }
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+
+ int l = i + 1, r = nums.length - 1;
+ while (l < r) {
+
+ int sum = nums[i] + nums[l] + nums[r];
+ if (sum == 0) {
+ resultList.add(Arrays.asList(nums[i], nums[l], nums[r]));
+ while (l < r && nums[l] == nums[l + 1]) l++; // 对l去重
+ while (l < r && nums[r] == nums[r - 1]) r--; // 对r去重
+ l++;
+ r--;
+ } else if (sum < 0) {
+ l++;
+ } else {
+ r--;
+ }
+
+ }
+ }
+ return resultList;
+ }
+
+ public static void main(String[] args) {
+ //new Solution2().threeSum(new int[]{-1, 0, 1, 2, -1, -4}).forEach((list) -> {
+ // System.out.println();
+ // list.forEach(System.out::print);
+ //});
+
+ new Solution2().threeSum(new int[]{0,0,0,0}).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..c3ca0a1d
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\211\346\225\260\344\271\213\345\222\214_15_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,77 @@
+package cn.lastwhisper.leetcode.hashtable.三数之和_15_中等;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/3sum/
+ * -------------------------------------------------------------------
+ *
+ * -------------------------------------------------------------------
+ * 优化思路:根据排序后的数据规律,剔除掉不必要的循环
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ * -------------------------------------------------------------------
+ * 执行用时 :33 ms, 在所有 java 提交中击败了92.67%的用户
+ * 内存消耗 :48.1 MB, 在所有 java 提交中击败了92.84%的用户
+ */
+ public List> threeSum(int[] nums) {
+ // 存储结果
+ List> resultList = new ArrayList<>();
+ if (nums == null || nums.length < 3) return resultList;
+ // 数组排序
+ Arrays.sort(nums);
+ for (int i = 0; i < nums.length - 2; i++) { // i < nums.length - 2
+
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+
+ // 最小值比0大,不管nums[i]再怎么递增都没戏
+ int min1 = nums[i] + nums[i + 1] + nums[i + 2];
+ if (min1 > 0) {
+ break;
+ }
+
+ // 最大值比0小,当前nums[i]肯定没戏了,找下一个nums[i]试试
+ int max1 = nums[i] + nums[nums.length - 2] + nums[nums.length - 1];
+ if (max1 < 0) {
+ continue;
+ }
+
+ int l = i + 1, r = nums.length - 1;
+ while (l < r) {
+
+ int sum = nums[i] + nums[l] + nums[r];
+ if (sum == 0) {
+ resultList.add(Arrays.asList(nums[i], nums[l], nums[r]));
+ while (l < r && nums[l] == nums[l + 1]) l++; // 对l去重
+ while (l < r && nums[r] == nums[r - 1]) r--; // 对r去重
+ l++;
+ r--;
+ } else if (sum < 0) {
+ l++;
+ } else {
+ r--;
+ }
+
+ }
+ }
+ return resultList;
+ }
+
+ public static void main(String[] args) {
+ //new Solution2().threeSum(new int[]{-1, 0, 1, 2, -1, -4}).forEach((list) -> {
+ // System.out.println();
+ // list.forEach(System.out::print);
+ //});
+
+ new Solution3().threeSum(new int[]{0, 0, 0, 0}).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_349_Esay/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_349_Esay/Solution1.java"
new file mode 100644
index 00000000..8954b872
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_349_Esay/Solution1.java"
@@ -0,0 +1,38 @@
+package cn.lastwhisper.leetcode.hashtable.两个数组的交集_349_Esay;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution1 {
+ /**
+ *
+ */
+ public int[] intersection(int[] nums1, int[] nums2) {
+ Set recorde = new HashSet();
+ for (int num : nums1) {
+ recorde.add(num);
+ }
+ Set resSet = new HashSet();
+ for (int num : nums2) {
+ if (recorde.contains(num)) {
+ resSet.add(num);
+ }
+ }
+ int[] res = new int[resSet.size()];
+
+ int index = 0;
+ for (Integer num : resSet)
+ res[index++] = num;
+ return res;
+ }
+
+ public static void main(String[] args) {
+ int[] nums1 = {1, 2, 2, 1};
+ int[] nums2 = {2, 2};
+ int[] res = (new Solution1()).intersection(nums1, nums2);
+
+ for (int re : res) {
+ System.out.println(re);
+ }
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_II_350_Esay/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_II_350_Esay/Solution1.java"
new file mode 100644
index 00000000..028560f8
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\344\270\252\346\225\260\347\273\204\347\232\204\344\272\244\351\233\206_II_350_Esay/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.hashtable.两个数组的交集_II_350_Esay;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class Solution1 {
+ public int[] intersect(int[] nums1, int[] nums2) {
+ Map record = new HashMap<>();
+ for (int i = 0; i < nums1.length; i++) {
+ int num = nums1[i];
+ if(!record.containsKey(num)){
+ record.put(num,1);
+ }else {
+ record.put(num, record.get(num) + 1);
+ }
+
+ }
+
+ List result = new ArrayList<>();
+ for (int i = 0; i < nums2.length; i++) {
+ int num = nums2[i];
+ if(record.containsKey(num) && record.get(num)>0){
+ result.add(num);
+ record.put(num, record.get(num) - 1);
+ }
+
+ }
+
+ int[] res = new int[result.size()];
+ int index=0;
+ for (Integer integer : result) {
+ res[index] = integer;
+ index++;
+ }
+
+ return res;
+ }
+
+ public static void main(String[] args){
+ int[] nums1 = {1, 2, 2, 1};
+ int[] nums2 = {2, 2};
+ int[] res = (new Solution1()).intersect(nums1, nums2);
+
+ for (int i = 0; i < res.length; i++) {
+ int re = res[i];
+ System.out.println(re);
+ }
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..9be5d0c5
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,29 @@
+package cn.lastwhisper.leetcode.hashtable.两数之和_1_简单;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/two-sum/
+ * 核心思想:找出所有组合进行对比,取其中一个即可
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ *
+ *
+ */
+ public int[] twoSum(int[] nums, int target) {
+ if (nums == null) throw new IllegalArgumentException("非法参数");
+ for (int i = 0; i < nums.length - 1; i++) {
+ for (int j = i + 1; j < nums.length; j++) {
+ if (nums[i] + nums[j] == target) {
+ return new int[]{i, j};
+ }
+ }
+ }
+ return null;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Arrays.toString(new Solution1().twoSum(new int[]{2, 7, 11, 15}, 9)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..4b3aa980
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,48 @@
+package cn.lastwhisper.leetcode.hashtable.两数之和_1_简单;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/two-sum/
+ * 核心思想:先排序O(nlogn),在对撞指针O(n)
+ * 时间复杂度:O(nlogn)
+ * 空间复杂度:O(1)
+ * 相关题型:两数之和_II_输入有序数组_167
+ */
+ public int[] twoSum(int[] nums, int target) {
+ if (nums == null) throw new IllegalArgumentException("非法参数");
+ // 存储原数组,下标和值映射关系(map中重复的数据会覆盖数组值与下标的对应关系)
+ Map map = new HashMap<>();
+ for (int i = 0; i < nums.length; i++) {
+ map.put(nums[i], i);
+ }
+ Arrays.sort(nums);
+ // 对撞指针
+ int l = 0, r = nums.length - 1;
+ while (l < r) {
+ int left = nums[l];
+ int right = nums[r];
+ if ((left + right) == target) {
+ return new int[]{map.get(left), map.get(right)};
+ } else if ((left + right) > target) {
+ r--;
+ } else {
+ l++;
+ }
+ }
+ return null;
+ }
+
+ // 没做出来,暂时保留
+ public static void main(String[] args) {
+ //int[] arr = new int[]{3, 2, 4};
+ //int target = 6;
+
+ int[] arr = new int[]{3, 3};//此时发生错误
+ int target = 6;
+ System.out.println(Arrays.toString(new Solution2().twoSum(arr, target)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..a52e83d2
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\344\270\244\346\225\260\344\271\213\345\222\214_1_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.hashtable.两数之和_1_简单;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution3 {
+ /**
+ * https://leetcode-cn.com/problems/two-sum/
+ * 核心思想:将所有元素放入查找表,之后对于每一个元素a,查找 target - a 是否存在
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * 执行用时 : 3 ms , 在所有 Java 提交中击败了 99.01% 的用户
+ * 内存消耗 : 37.9 MB , 在所有 Java 提交中击败了 79.09% 的用户
+ */
+ public int[] twoSum(int[] nums, int target) {
+ if (nums == null) throw new IllegalArgumentException("非法参数");
+ // map存储数字与对应下标
+ Map map = new HashMap<>();
+ // 边记录边找
+ for (int i = 0; i < nums.length; i++) {
+ int key = target-nums[i];
+ if(map.containsKey(key)){
+ return new int[]{i,map.get(key)};
+ }
+ map.put(nums[i],i);
+ }
+ throw new IllegalArgumentException("No two sum solution");
+ }
+
+ public static void main(String[] args) {
+ //int[] arr = new int[]{3, 2, 4};
+ //int target = 6;
+
+ int[] arr = new int[]{3,3};//此时发生错误
+ int target = 6;
+
+ //int[] arr = new int[]{2, 7, 11, 15};
+ //int target = 9;
+
+ System.out.println(Arrays.toString(new Solution2().twoSum(arr, target)));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\215\225\350\257\215\350\247\204\345\276\213_290_Easy/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\215\225\350\257\215\350\247\204\345\276\213_290_Easy/Solution1.java"
new file mode 100644
index 00000000..951df703
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\215\225\350\257\215\350\247\204\345\276\213_290_Easy/Solution1.java"
@@ -0,0 +1,41 @@
+package cn.lastwhisper.leetcode.hashtable.单词规律_290_Easy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/word-pattern/
+ * 核心思想:map
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean wordPattern(String pattern, String str) {
+ if (pattern == null || str == null) return false;
+ String[] strArr = str.split(" ");
+ if (pattern.length() != strArr.length) return false;
+ Map map = new HashMap<>();
+ for (int i = 0; i < strArr.length; i++) {
+ if (map.containsKey(pattern.charAt(i))) {
+ // key存在,value不相同false
+ // a=dog,a=cat false
+ if (!strArr[i].equals(map.get(pattern.charAt(i)))) return false;
+ } else {
+ // key不存在,value相同false
+ // a=dog,b=dog false
+ if (map.containsValue(strArr[i])) return false;
+ map.put(pattern.charAt(i), strArr[i]);
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().wordPattern("abba", "dog cat cat dog"));
+ System.out.println(new Solution1().wordPattern("abba", "dog cat cat fish"));
+ System.out.println(new Solution1().wordPattern("aaaa", "dog cat cat dog"));
+ System.out.println(new Solution1().wordPattern("abba", "dog dog dog dog"));
+ System.out.println(new Solution1().wordPattern("", "dog"));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution1.java"
new file mode 100644
index 00000000..5c0f8927
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution1.java"
@@ -0,0 +1,35 @@
+package cn.lastwhisper.leetcode.hashtable.同构字符串_205_Esay;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/isomorphic-strings/
+ * 核心思想:map
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean isIsomorphic(String s, String t) {
+ if (s == null || t == null || s.length() != t.length()) return false;
+ Map map = new HashMap<>();
+ for (int i = 0; i < s.length(); i++) {
+ if (map.containsKey(s.charAt(i))) {
+ // key存在,value不相同false
+ if (map.get(s.charAt(i)) != t.charAt(i)) return false;
+ } else {
+ // key不存在,value相同false
+ if (map.containsValue(t.charAt(i))) return false;
+ map.put(s.charAt(i), t.charAt(i));
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().isIsomorphic("egg", "add"));
+ System.out.println(new Solution1().isIsomorphic("foo", "bar"));
+ System.out.println(new Solution1().isIsomorphic("paper", "title"));
+ System.out.println(new Solution1().isIsomorphic("ab", "bb"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution2.java"
new file mode 100644
index 00000000..18a14cdd
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\220\214\346\236\204\345\255\227\347\254\246\344\270\262_205_Esay/Solution2.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.hashtable.同构字符串_205_Esay;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/isomorphic-strings/
+ * 核心思想:位图
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean isIsomorphic(String s, String t) {
+ if (s == null || t == null || s.length() != t.length()) return false;
+ char[] sc = s.toCharArray();
+ char[] tc = t.toCharArray();
+ int[] map = new int[256];
+ for (int i = 0; i < s.length(); i++) {
+ if(map[sc[i]]!=map[tc[i]+128]){
+ return false;
+ }
+ map[sc[i]]=map[tc[i]+128]=t.charAt(i);
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().isIsomorphic("egg", "add"));
+ System.out.println(new Solution2().isIsomorphic("foo", "bar"));
+ System.out.println(new Solution2().isIsomorphic("paper", "title"));
+ System.out.println(new Solution2().isIsomorphic("ab", "bb"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Example.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Example.java"
new file mode 100644
index 00000000..e48cd6c0
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Example.java"
@@ -0,0 +1,109 @@
+package cn.lastwhisper.leetcode.hashtable.四数之和_18_Middle;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Example {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/4sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:数组、无序、所有整数
+ * 输出:二维数组
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public List> fourSum(int[] nums, int target) {
+ /*定义一个返回值*/
+ List> result = new ArrayList<>();
+ /*当数组为null或元素小于4个时,直接返回*/
+ if (nums == null || nums.length < 4) {
+ return result;
+ }
+ /*对数组进行从小到大排序*/
+ Arrays.sort(nums);
+ /*数组长度*/
+ int length = nums.length;
+ /*定义4个指针k,i,j,h k从0开始遍历,i从k+1开始遍历,留下j和h,j指向i+1,h指向数组最大值*/
+ for (int k = 0; k < length - 3; k++) {
+ /*当k的值与前面的值相等时忽略*/
+ if (k > 0 && nums[k] == nums[k - 1]) {
+ continue;
+ }
+ /*获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏*/
+ int min1 = nums[k] + nums[k + 1] + nums[k + 2] + nums[k + 3];
+ if (min1 > target) {
+ break;
+ }
+ /*获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略*/
+ int max1 = nums[k] + nums[length - 1] + nums[length - 2] + nums[length - 3];
+ if (max1 < target) {
+ continue;
+ }
+ /*第二层循环i,初始值指向k+1*/
+ for (int i = k + 1; i < length - 2; i++) {
+ /*当i的值与前面的值相等时忽略*/
+ if (i > k + 1 && nums[i] == nums[i - 1]) {
+ continue;
+ }
+ /*定义指针j指向i+1*/
+ int j = i + 1;
+ /*定义指针h指向数组末尾*/
+ int h = length - 1;
+ /*获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏,忽略*/
+ int min = nums[k] + nums[i] + nums[j] + nums[j + 1];
+ if (min > target) {
+ continue;
+ }
+ /*获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略*/
+ int max = nums[k] + nums[i] + nums[h] + nums[h - 1];
+ if (max < target) {
+ continue;
+ }
+ /*开始j指针和h指针的表演,计算当前和,如果等于目标值,j++并去重,h--并去重,当当前和大于目标值时h--,当当前和小于目标值时j++*/
+ while (j < h) {
+ int curr = nums[k] + nums[i] + nums[j] + nums[h];
+ if (curr == target) {
+ result.add(Arrays.asList(nums[k], nums[i], nums[j], nums[h]));
+ j++;
+ while (j < h && nums[j] == nums[j - 1]) {
+ j++;
+ }
+ h--;
+ while (j < h && i < h && nums[h] == nums[h + 1]) {
+ h--;
+ }
+ } else if (curr > target) {
+ h--;
+ } else {
+ j++;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+
+ public static void main(String[] args) {
+ //int[] nums = {1, 0, -1, 0, -2, 2};
+ //int target = 0;
+
+ // error example
+ //int[] nums = {-3, -1, 0, 2, 4, 5};
+ //int target = 2;
+
+ int[] nums = {-3, -2, -1, 0, 0, 1, 2, 3};
+ int target = 0;
+
+ new Example().fourSum(nums, target).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution1.java"
new file mode 100644
index 00000000..f65d1da1
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution1.java"
@@ -0,0 +1,72 @@
+package cn.lastwhisper.leetcode.hashtable.四数之和_18_Middle;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/4sum/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:在数组中任意取四个数(参考三数之和)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(n^2)
+ */
+ public List> fourSum(int[] nums, int target) {
+ // 存储结果
+ List> resultList = new ArrayList<>();
+ if (nums == null || nums.length < 4) return resultList;
+ // 数组排序
+ Arrays.sort(nums);
+ for (int i = 0; i < nums.length - 3; i++) {
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+ for (int j = i + 1; j < nums.length - 2; j++) {
+ if (j > i + 1 && nums[j] == nums[j - 1]) {
+ continue; // 对j去重
+ }
+ int l = j + 1, r = nums.length - 1;
+ while (l < r) {
+
+ int sum = nums[i] + nums[j] + nums[l] + nums[r];
+ if (sum == target) {
+ resultList.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
+ while (l < r && nums[l] == nums[l + 1]) l++; // 对l去重
+ while (l < r && nums[r] == nums[r - 1]) r--; // 对r去重
+ l++;
+ r--;
+ } else if (sum < target) {
+ l++;
+ } else {
+ r--;
+ }
+
+ }
+
+ }
+ }
+
+ return resultList;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = {1, 0, -1, 0, -2, 2};
+ //int target = 0;
+
+ // error example
+ //int[] nums = {-3, -1, 0, 2, 4, 5};
+ //int target = 2;
+
+ int[] nums = {-3, -2, -1, 0, 0, 1, 2, 3};
+ int target = 0;
+
+ new Solution1().fourSum(nums, target).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution2.java"
new file mode 100644
index 00000000..9a0c41c0
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\344\271\213\345\222\214_18_Middle/Solution2.java"
@@ -0,0 +1,99 @@
+package cn.lastwhisper.leetcode.hashtable.四数之和_18_Middle;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/4sum/
+ * -------------------------------------------------------------------
+ *
+ * -------------------------------------------------------------------
+ * 优化思路:根据排序后的数据特征,在循环时可以进行跳过不必要的循环
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(n^2)
+ * -------------------------------------------------------------------
+ * 执行用时 : 4 ms, 在所有 java 提交中击败了99.54%的用户
+ * 内存消耗 : 36.5 MB, 在所有 java 提交中击败了99.09%的用户
+ */
+ public List> fourSum(int[] nums, int target) {
+ // 存储结果
+ List> resultList = new ArrayList<>();
+ if (nums == null || nums.length < 4) return resultList;
+ // 数组排序
+ Arrays.sort(nums);
+ for (int i = 0; i < nums.length - 3; i++) {
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+ // 最小值比target大,不管nums[i]再怎么递增都没戏
+ int min1 = nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3];
+ if (min1 > target) {
+ break;
+ }
+
+ // 最大值比target小,当前nums[i]肯定没戏了,找下一个nums[i]试试
+ int max1 = nums[i] + nums[nums.length - 3] + nums[nums.length - 2] + nums[nums.length - 1];
+ if (max1 < target) {
+ continue;
+ }
+
+ for (int j = i + 1; j < nums.length - 2; j++) {
+ if (j > i + 1 && nums[j] == nums[j - 1]) {
+ continue; // 对j去重
+ }
+
+ // 最小值比target大,不管nums[j]再怎么递增都没戏
+ int min2 = nums[i] + nums[j] + nums[j + 1] + nums[j + 2];
+ if (min2 > target) {
+ break;
+ }
+
+ // 最大值比target小,当前nums[j]肯定没戏了,找下一个nums[j]试试
+ int max2 = nums[i] + nums[j] + nums[nums.length - 2] + nums[nums.length - 1];
+ if (max2 < target) {
+ continue;
+ }
+ int l = j + 1, r = nums.length - 1;
+ while (l < r) {
+
+ int sum = nums[i] + nums[j] + nums[l] + nums[r];
+ if (sum == target) {
+ resultList.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
+ while (l < r && nums[l] == nums[l + 1]) l++; // 对l去重
+ while (l < r && nums[r] == nums[r - 1]) r--; // 对r去重
+ l++;
+ r--;
+ } else if (sum < target) {
+ l++;
+ } else {
+ r--;
+ }
+
+ }
+
+ }
+ }
+
+ return resultList;
+ }
+
+ public static void main(String[] args) {
+ //int[] nums = {1, 0, -1, 0, -2, 2};
+ //int target = 0;
+
+ // error example
+ //int[] nums = {-3, -1, 0, 2, 4, 5};
+ //int target = 2;
+
+ int[] nums = {-3, -2, -1, 0, 0, 1, 2, 3};
+ int target = 0;
+
+ new Solution2().fourSum(nums, target).forEach((list) -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..5f44c6a7
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.hashtable.四数相加_II_454_中等;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/4sum-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:数组、无序、所有整数
+ * 输出:二维数组
+ * -------------------------------------------------------------------
+ * 优化思路:
+ * (1)四层循环,时间复杂度O(n^4)——超时
+ * (2)使用hash存储D的所有情况,时间复杂度O(n^3)——超时
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
+ // key:数组中某个数、value:该数出现的频率
+ Map dMap = new HashMap<>();
+ for (int dKey : D) {
+ if (dMap.containsKey(dKey)) {
+ dMap.put(dKey, dMap.get(dKey) + 1);
+ } else {
+ dMap.put(dKey, 1);
+ }
+ }
+ int count = 0;
+ for (int a : A) {
+ for (int b : B) {
+ for (int c : C) {
+ int dKey = 0 - a - b - c;
+ Integer dCount = dMap.get(dKey);
+ if (dCount != null) {
+ count += dCount;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ public static void main(String[] args) {
+ int[] A = {1, 2};
+ int[] B = {-2, -1};
+ int[] C = {-1, 2};
+ int[] D = {0, 2};
+ System.out.println(new Solution1().fourSumCount(A, B, C, D));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..6b487488
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\233\346\225\260\347\233\270\345\212\240_II_454_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,53 @@
+package cn.lastwhisper.leetcode.hashtable.四数相加_II_454_中等;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/4sum-ii/
+ * -------------------------------------------------------------------
+ * 优化思路:
+ * (3)使用hash存储CD的所有情况,时间复杂度O(n^2)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n^2)
+ * -------------------------------------------------------------------
+ * 执行用时 :142 ms, 在所有 java 提交中击败了27.37%的用户
+ * 内存消耗 :59 MB, 在所有 java 提交中击败了90.62%的用户
+ */
+ public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {
+ // key:数组中某个数、value:该数出现的频率
+ Map cdMap = new HashMap<>();
+ for (int c : C) {
+ for (int d : D) {
+ int cd = c + d;
+ if (cdMap.containsKey(cd)) {
+ cdMap.put(cd, cdMap.get(cd) + 1);
+ } else {
+ cdMap.put(cd, 1);
+ }
+ }
+ }
+
+ int count = 0;
+ for (int a : A) {
+ for (int b : B) {
+ int cd = 0 - a - b;
+ Integer cdCount = cdMap.get(cd);
+ if (cdCount != null) {
+ count += cdCount;
+ }
+ }
+ }
+ return count;
+ }
+
+ public static void main(String[] args) {
+ int[] A = {1, 2};
+ int[] B = {-2, -1};
+ int[] C = {-1, 2};
+ int[] D = {0, 2};
+ System.out.println(new Solution2().fourSumCount(A, B, C, D));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217_447_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217_447_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..124817c3
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\233\236\346\227\213\351\225\226\347\232\204\346\225\260\351\207\217_447_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.hashtable.回旋镖的数量_447_简单;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/number-of-boomerangs/
+ * -------------------------------------------------------------------
+ * 思考:计算两点问题开根号时会有精度问题
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度: O(n^2)
+ * 空间复杂度: O(n)
+ */
+ public int numberOfBoomerangs(int[][] points) {
+ int res = 0;
+
+ // record中存储 点i 到所有其他点的距离出现的频次
+ Map record = new HashMap<>();
+ for (int i = 0; i < points.length; i++) {
+
+ for (int j = 0; j < points.length; j++) {
+ if (i != j) {
+ // 计算距离时不进行开根运算, 以保证精度
+ int dis = dis(points[i], points[j]);
+ if (record.containsKey(dis)) {
+ record.put(dis, record.get(dis) + 1);
+ } else {
+ record.put(dis, 1);
+ }
+ }
+ }
+ for (Integer dis : record.keySet()) {
+ res += record.get(dis) * (record.get(dis) - 1);
+ }
+
+ // 复用map
+ record.clear();
+ }
+ return res;
+ }
+
+ private int dis(int[] pa, int[] pb) {
+ return (pa[0] - pb[0]) * (pa[0] - pb[0]) +
+ (pa[1] - pb[1]) * (pa[1] - pb[1]);
+ }
+
+ public static void main(String[] args) {
+ int[][] points = {{0, 0}, {1, 0}, {2, 0}};
+ System.out.println(new Solution1().numberOfBoomerangs(points));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204_49_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204_49_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..e88ccb72
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215\345\210\206\347\273\204_49_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,49 @@
+package cn.lastwhisper.leetcode.hashtable.字母异位词分组_49_中等;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/group-anagrams/
+ * -------------------------------------------------------------------
+ * 思路:排序字符串相等时,两个字符串是字母异位词
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(N*K*logK) 其中 N 是 strs 的长度,而 K 是 strs 中字符串的最大长度
+ * 空间复杂度:O(N*K)
+ * -------------------------------------------------------------------
+ * 执行用时 :11 ms, 在所有 java 提交中击败了97.16%的用户
+ * 内存消耗 :42.2 MB, 在所有 java 提交中击败了97.29%的用户
+ */
+ public List> groupAnagrams(String[] strs) {
+ // 空值处理
+ if (strs.length == 0) return new ArrayList>();
+
+ // key:排序后的字符串、value:未排序的字符串集List
+ HashMap> map = new HashMap<>();
+
+ for (String str : strs) {
+ char[] chars = str.toCharArray();
+ // 排序后如果是字母异位词都长一个样
+ Arrays.sort(chars);
+ String key = String.valueOf(chars);
+ if (!map.containsKey(key)) {
+ map.put(key, new ArrayList<>());
+ }
+ map.get(key).add(str);
+ }
+
+ return new ArrayList<>(map.values());
+ }
+
+ public static void main(String[] args) {
+ String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
+
+ new Solution1().groupAnagrams(strs).forEach(list -> {
+ System.out.println();
+ list.forEach(System.out::print);
+ });
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution1.java"
new file mode 100644
index 00000000..5af33bd9
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution1.java"
@@ -0,0 +1,47 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_III_220_中等;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-iii/
+ * 题意:nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:数组、无序、所有整数(long)
+ * 输出:boolean
+ * -------------------------------------------------------------------
+ * 思路:双重for循环,满足k、t即可
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*min(k,n))
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ * 执行用时 :510 ms, 在所有 Java 提交中击败了5.79%的用户
+ * 内存消耗 :36.2 MB, 在所有 Java 提交中击败了98.89%的用户
+ */
+ public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
+ // 投机取巧,跳过耗时的测试用例
+ //if(k==10000) return false;
+
+ for (int i = 0; i < nums.length; i++) {
+ for (int j = i + 1; j <= i + k && j < nums.length; j++) {
+ if (Math.abs((long) nums[i] - (long) nums[j]) <= t) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 0, 1, 1};
+ int k = 1, t = 2;
+
+ //int[] nums = {1,2,3,1};
+ //int k = 3, t = 0;
+ //int[] nums = {1, 5, 9, 1, 5, 9};
+ //int k = 2, t = 3;
+
+ System.out.println(new Solution1().containsNearbyAlmostDuplicate(nums, k, t));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution2.java"
new file mode 100644
index 00000000..47435020
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution2.java"
@@ -0,0 +1,54 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_III_220_中等;
+
+import java.util.TreeSet;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-iii/
+ * 题意:nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:数组、无序、所有整数(long)
+ * 输出:boolean
+ * -------------------------------------------------------------------
+ * 思路:在思路1中维护的滑动窗口中,找到满足t,需要线性搜索耗时O(o)
+ * 可以优化为维护一个BST,BST搜索耗时O(log(n))
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n*log(min(k,n)))
+ * 空间复杂度:O(min(k,n))
+ * -------------------------------------------------------------------
+ * 执行用时 :44 ms, 在所有 Java 提交中击败了25.79%的用户
+ * 内存消耗 :36.9 MB, 在所有 Java 提交中击败了89.89%的用户
+ */
+ public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
+ TreeSet set = new TreeSet<>();
+ for (int i = 0; i < nums.length; ++i) {
+ // Find the successor of current element
+ Long s = set.ceiling((long) nums[i]);//返回大于或等于给定键值的最小键值
+ if (s != null && s - nums[i] <= t) return true;
+
+ // Find the predecessor of current element
+ Long g = set.floor((long) nums[i]);//返回小于或等于给定键值的最大键值
+ if (g != null && nums[i] - g <= t) return true;
+
+ set.add((long) nums[i]);
+ if (set.size() > k) {
+ set.remove((long) nums[i - k]);
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 0, 1, 1};
+ int k = 1, t = 2;//true
+
+ //int[] nums = {1,2,3,1};//true
+ //int k = 3, t = 0;
+ //int[] nums = {1, 5, 9, 1, 5, 9};
+ //int k = 2, t = 3;//false
+
+ System.out.println(new Solution2().containsNearbyAlmostDuplicate(nums, k, t));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution3.java"
new file mode 100644
index 00000000..4fd24c48
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_III_220_\344\270\255\347\255\211/Solution3.java"
@@ -0,0 +1,60 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_III_220_中等;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-iii/
+ * 题意:nums [i] 和 nums [j] 的差的绝对值最大为 t,并且 i 和 j 之间的差的绝对值最大为 ķ
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:数组、无序、所有整数(long)
+ * 输出:boolean
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ * -------------------------------------------------------------------
+ */
+ public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
+ if (t < 0) return false;
+ Map d = new HashMap<>();
+ long w = (long) t + 1;
+ for (int i = 0; i < nums.length; ++i) {
+ long m = getID(nums[i], w);
+ // check if bucket m is empty, each bucket may contain at most one element
+ if (d.containsKey(m))
+ return true;
+ // check the nei***or buckets for almost duplicate
+ if (d.containsKey(m - 1) && Math.abs(nums[i] - d.get(m - 1)) < w)
+ return true;
+ if (d.containsKey(m + 1) && Math.abs(nums[i] - d.get(m + 1)) < w)
+ return true;
+ // now bucket m is empty and no almost duplicate in nei***or buckets
+ d.put(m, (long) nums[i]);
+ if (i >= k) d.remove(getID(nums[i - k], w));
+ }
+ return false;
+ }
+
+ // Get the ID of the bucket from element value x and bucket width w
+ // In Java, `-3 / 5 = 0` and but we need `-3 / 5 = -1`.
+ private long getID(long x, long w) {
+ return x < 0 ? (x + 1) / w - 1 : x / w;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 0, 1, 1};
+ int k = 1, t = 2;//true
+
+ //int[] nums = {1,2,3,1};//true
+ //int k = 3, t = 0;
+ //int[] nums = {1, 5, 9, 1, 5, 9};
+ //int k = 2, t = 3;//false
+
+ System.out.println(new Solution3().containsNearbyAlmostDuplicate(nums, k, t));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..163ecb0f
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_II_219_简单;
+
+import java.util.HashMap;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:hash表记录值和下标,出现重复值,查看k是否符合下标相减
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ */
+ public boolean containsNearbyDuplicate(int[] nums, int k) {
+ // 投机取巧,跳过耗时的测试用例
+ //if (k == 35000 || nums.length < 1) {
+ // return false;
+ //}
+
+ HashMap map = new HashMap<>();
+ for (int j = 0; j < nums.length; j++) {
+ Integer i = map.get(nums[j]);
+ // 以及存在,且k >= j - i
+ if (i != null && k >= j - i) {
+ return true;
+ }
+ map.put(nums[j], j);
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 2, 3, 1};
+ int k = 3;
+ System.out.println(new Solution1().containsNearbyDuplicate(nums, k));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution2.java"
new file mode 100644
index 00000000..9f5089bd
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution2.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_II_219_简单;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:维持一个大小为k的队列,在队列大小中存在重复元素
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * -------------------------------------------------------------------
+ */
+ public boolean containsNearbyDuplicate(int[] nums, int k) {
+ // 投机取巧,跳过耗时的测试用例
+ //if (k == 35000 || nums.length < 1) {
+ // return false;
+ //}
+
+ Queue queue = new ArrayDeque<>();
+ for (int num : nums) {
+ if (queue.contains(num)) {
+ return true;
+ }
+ queue.add(num);
+ // 维持队列大小
+ if (queue.size() > k) {
+ queue.poll();
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 2, 3, 1};
+ int k = 3;
+ System.out.println(new Solution2().containsNearbyDuplicate(nums, k));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution3.java"
new file mode 100644
index 00000000..91f1f1ca
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\255\230\345\234\250\351\207\215\345\244\215\345\205\203\347\264\240_II_219_\347\256\200\345\215\225/Solution3.java"
@@ -0,0 +1,24 @@
+package cn.lastwhisper.leetcode.hashtable.存在重复元素_II_219_简单;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/contains-duplicate-ii/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:维持一个大小为k的窗口,查看窗口中存在重复元素(滑动窗口解法)
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * -------------------------------------------------------------------
+ */
+ public boolean containsNearbyDuplicate(int[] nums, int k) {
+ return false;
+ }
+
+ public static void main(String[] args) {
+ int[] nums = {1, 2, 3, 1};
+ int k = 3;
+ System.out.println(new Solution3().containsNearbyDuplicate(nums, k));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution1.java"
new file mode 100644
index 00000000..4c69ba49
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution1.java"
@@ -0,0 +1,56 @@
+package cn.lastwhisper.leetcode.hashtable.快乐数_202_Easy;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/happy-number/submissions/
+ * 核心思路:直接按题意来
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public boolean isHappy(int n) {
+ Set set = new HashSet();
+ while (true) {
+ int[] arr = split(n);
+ int sum = 0;
+ for (int i = 0; i < arr.length; i++) {
+ sum += arr[i] * arr[i];
+ if (i == arr.length - 1) {
+ System.out.printf("%d^2 = ", arr[i]);
+ } else {
+ System.out.printf("%d^2 + ", arr[i]);
+ }
+ }
+ System.out.println(sum);
+ if (sum == 1) {
+ return true;
+ } else if (set.contains(sum)) {
+ return false;
+ } else {
+ set.add(sum);
+ }
+ n = sum;
+ }
+ }
+
+ public int[] split(int num) {
+ int bit, ten, hundred;
+ if (num < 10) {
+ return new int[]{num};
+ } else if (num < 100) {
+ bit = num % 10;
+ ten = num / 10;
+ return new int[]{bit, ten};
+ } else {
+ bit = num % 10;
+ ten = num / 10 % 10;
+ hundred = num / 100;
+ return new int[]{bit, ten, hundred};
+ }
+ }
+ public static void main(String[] args) {
+ System.out.println(new Solution1().isHappy(18));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution2.java"
new file mode 100644
index 00000000..b0d42fd3
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution2.java"
@@ -0,0 +1,44 @@
+package cn.lastwhisper.leetcode.hashtable.快乐数_202_Easy;
+
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution2 {
+
+ /**
+ * https://leetcode-cn.com/problems/happy-number/submissions/
+ * 核心思路:hash
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ */
+ public boolean isHappy(int n) {
+ Set set = new HashSet();
+ while (true) {
+ int sum = 0, bit = 0;
+ while (n > 0) {
+ bit = n % 10;
+ sum += bit * bit;
+ if (n / 10 > 0) {
+ System.out.printf("%s^2 + ", bit);
+ } else {
+ System.out.printf("%s^2 = ", bit);
+ }
+ n /= 10;
+ }
+ System.out.println(sum);
+ if (sum == 1) {
+ return true;
+ } else if (set.contains(sum)) {
+ return false;
+ } else {
+ set.add(sum);
+ }
+ n = sum;
+ }
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().isHappy(1812));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution3.java"
new file mode 100644
index 00000000..81c4b49e
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Solution3.java"
@@ -0,0 +1,34 @@
+package cn.lastwhisper.leetcode.hashtable.快乐数_202_Easy;
+
+class Solution3 {
+ /**
+ * https://leetcode-cn.com/problems/happy-number/
+ * 核心思路:快慢指针
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isHappy(int n) {
+ int slow = n, fast = bitSquareSum(n);
+ while (slow != fast) {
+ slow = bitSquareSum(slow);
+ fast = bitSquareSum(fast);
+ fast = bitSquareSum(fast);
+ }
+ return slow == 1;
+ }
+
+ public int bitSquareSum(int num) {
+ int sum = 0;
+ while (num > 0) {
+ int bit = num % 10;
+ sum += bit * bit;
+ num = num / 10;
+ }
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Solution3().isHappy(18));
+ //System.out.println(new Solution3().bitSquareSum(4328));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test.java"
new file mode 100644
index 00000000..d07ef10f
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test.java"
@@ -0,0 +1,42 @@
+package cn.lastwhisper.leetcode.hashtable.快乐数_202_Easy;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author lastwhisper
+ */
+public class Test {
+
+ public boolean isHappy(int n) throws InterruptedException {
+ System.out.println(n);
+ while (true) {
+ String[] arr = split(n);
+ int sum = 0;
+ for (int i = 0; i < arr.length; i++) {
+ sum += Integer.parseInt(arr[i]) * Integer.parseInt(arr[i]);
+ if (i == arr.length - 1) {
+ System.out.printf("%s^2 = ", arr[i]);
+ } else {
+ System.out.printf("%s^2 + ", arr[i]);
+ }
+ }
+ System.out.println(sum);
+ if (sum == 1) {
+ return true;
+ }
+ n = sum;
+ Thread.sleep(100);
+ }
+ }
+
+ public String[] split(int num) {
+ String numStr = String.valueOf(num);
+ return numStr.split("");
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+ System.out.println(new Test().isHappy(18));
+ }
+}
+
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test2.java"
new file mode 100644
index 00000000..633c71e5
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/Test2.java"
@@ -0,0 +1,30 @@
+package cn.lastwhisper.leetcode.hashtable.快乐数_202_Easy;
+
+class Test2 {
+ public boolean isHappy(int n) {
+ int slow = n, fast = bitSquareSum(n,"fast");
+ while (slow != fast) {
+ slow = bitSquareSum(slow,"slow");
+ fast = bitSquareSum(fast,"fast");
+ fast = bitSquareSum(fast,"fast");
+ }
+ return slow == 1;
+ }
+
+ public int bitSquareSum(int num,String who) {
+ int sum = 0;
+ while (num > 0) {
+ int bit = num % 10;
+ sum += bit * bit;
+ System.out.printf("%d^2 + ", bit);
+ num = num / 10;
+
+ }
+ System.out.println(sum+"\t"+who);
+ return sum;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Test2().isHappy(18));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/data" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/data"
new file mode 100644
index 00000000..b6a41e8b
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\345\277\253\344\271\220\346\225\260_202_Easy/data"
@@ -0,0 +1,40 @@
+18
+1^2 + 8^2 = 65 f
+6^2 + 5^2 = 61
+6^2 + 1^2 = 37 f1
+3^2 + 7^2 = 58
+5^2 + 8^2 = 89 f2
+8^2 + 9^2 = 145
+1^2 + 4^2 + 5^2 = 42 f3
+4^2 + 2^2 = 20
+2^2 + 0^2 = 4 f4
+4^2 = 16
+1^2 + 6^2 = 37 f5
+3^2 + 7^2 = 58
+5^2 + 8^2 = 89 f6
+8^2 + 9^2 = 145
+1^2 + 4^2 + 5^2 = 42 f7
+4^2 + 2^2 = 20
+2^2 + 0^2 = 4
+4^2 = 16
+
+
+18 s
+1^2 + 8^2 = 65 s1
+6^2 + 5^2 = 61 s2
+6^2 + 1^2 = 37 s3
+3^2 + 7^2 = 58 s4
+5^2 + 8^2 = 89 s5
+8^2 + 9^2 = 145 s6
+1^2 + 4^2 + 5^2 = 42 s7
+4^2 + 2^2 = 20
+2^2 + 0^2 = 4
+4^2 = 16
+1^2 + 6^2 = 37
+3^2 + 7^2 = 58
+5^2 + 8^2 = 89
+8^2 + 9^2 = 145
+1^2 + 4^2 + 5^2 = 42
+4^2 + 2^2 = 20
+2^2 + 0^2 = 4
+4^2 = 16
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\211\276\345\207\272\347\254\254k\345\260\217\347\232\204\350\267\235\347\246\273\345\257\271_719_\345\233\260\351\232\276/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\211\276\345\207\272\347\254\254k\345\260\217\347\232\204\350\267\235\347\246\273\345\257\271_719_\345\233\260\351\232\276/Solution1.java"
new file mode 100644
index 00000000..65d20e9f
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\211\276\345\207\272\347\254\254k\345\260\217\347\232\204\350\267\235\347\246\273\345\257\271_719_\345\233\260\351\232\276/Solution1.java"
@@ -0,0 +1,21 @@
+package cn.lastwhisper.leetcode.hashtable.找出第k小的距离对_719_困难;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/
+ * -------------------------------------------------------------------
+ * 思考:
+ * 数据特征:
+ * 输入:
+ * 输出:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ */
+ public int smallestDistancePair(int[] nums, int k) {
+
+ return 0;
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\213\274\345\206\231\345\215\225\350\257\215_1160_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\213\274\345\206\231\345\215\225\350\257\215_1160_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..2f759693
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\213\274\345\206\231\345\215\225\350\257\215_1160_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,51 @@
+package cn.lastwhisper.leetcode.hashtable.拼写单词_1160_简单;
+
+import org.junit.Assert;
+
+import java.util.Arrays;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/find-words-that-can-be-formed-by-characters/
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(n)
+ */
+ public int countCharacters(String[] words, String chars) {
+ int[] charsFreq = new int[26];
+ // 统计频率
+ for (int i = 0; i < chars.length(); i++) {
+ charsFreq[chars.charAt(i) - 'a']++;
+ }
+ int ans = 0;
+
+ int[] wordFreq = new int[26];
+ for (String word : words) {
+ // 字母表必须比单词长,字母只许出现一次
+ if (chars.length() >= word.length()) {
+ Arrays.fill(wordFreq, 0);
+ boolean flag = true;
+ for (int i = 0; i < word.length(); i++) {
+ wordFreq[word.charAt(i) - 'a']++;
+ // 先加,在比较
+ if (wordFreq[word.charAt(i) - 'a'] > charsFreq[word.charAt(i) - 'a']) {
+ flag = false;
+ break;
+ }
+ }
+ if (flag) {
+ ans += word.length();
+ }
+ }
+ }
+ return ans;
+ }
+
+ public static void main(String[] args) {
+ Assert.assertEquals(6, new Solution1().countCharacters(new String[]{"cat", "bt", "hat", "tree"}, "atach"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\200\346\216\245\350\277\221\347\232\204\344\270\211\346\225\260\344\271\213\345\222\214_16_Middle/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\200\346\216\245\350\277\221\347\232\204\344\270\211\346\225\260\344\271\213\345\222\214_16_Middle/Solution1.java"
new file mode 100644
index 00000000..3426999d
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\200\346\216\245\350\277\221\347\232\204\344\270\211\346\225\260\344\271\213\345\222\214_16_Middle/Solution1.java"
@@ -0,0 +1,65 @@
+package cn.lastwhisper.leetcode.hashtable.最接近的三数之和_16_Middle;
+
+import java.util.Arrays;
+
+class Solution1 {
+
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/3sum-closest/
+ * -------------------------------------------------------------------
+ * 思考:
+ * Q:什么叫做接近?
+ * A: abs((n1+n2+n3)-target)>abs((n2+n3+n4)-target)
+ * n2+n3+n4比n1+n2+n3接近target
+ * 数据特征:
+ * 输入:数组、无序、所有整数
+ * 输出:整数
+ * -------------------------------------------------------------------
+ * 思路:在三数之和的基础上,理解什么叫做“接近”即可
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1):
+ * -------------------------------------------------------------------
+ * 执行用时 :7 ms, 在所有 java 提交中击败了58.60%的用户
+ * 内存消耗 :36.6 MB, 在所有 java 提交中击败了84.07%的用户
+ */
+ public int threeSumClosest(int[] nums, int target) {
+
+ int closest = nums[0] + nums[1] + nums[2];
+ // 数组排序2
+ Arrays.sort(nums);
+ for (int i = 0; i < nums.length; i++) {
+
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue; // 对i去重
+ }
+
+ int l = i + 1, r = nums.length - 1;
+ while (l < r) {
+ int sum = nums[i] + nums[l] + nums[r];
+ if (Math.abs(target - sum) < Math.abs(target - closest)) {
+ closest = sum;
+ }
+
+ if (sum < target) {
+ l++;
+ } else if (sum > target) {
+ r--;
+ } else {
+ return closest;
+ }
+
+ }
+ }
+
+ return closest;
+ }
+
+ public static void main(String[] args) {
+ //example
+ int[] nums = {-1, 2, 1, -4};
+ int target = 1;
+
+ System.out.println(new Solution1().threeSumClosest(nums, target));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution1.java"
new file mode 100644
index 00000000..d38c116e
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution1.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.hashtable.有效的字母异位词_242_Easy;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution1 {
+ public boolean isAnagram(String s, String t) {
+ char[] sChar = s.toCharArray();
+ char[] tChar = t.toCharArray();
+ HashMap map = new HashMap<>();
+ for (int i = 0; i < sChar.length; i++) {
+ Integer num = map.get(sChar[i]);
+ if (num == null) {
+ map.put(sChar[i], 1);
+ } else {
+ map.put(sChar[i], ++num);
+ }
+ }
+
+ for (int i = 0; i < tChar.length; i++) {
+ Integer num = map.get(tChar[i]);
+ if (num == null) {
+ return false;
+ } else {
+ map.put(tChar[i], --num);
+ }
+ }
+ for (Map.Entry entry : map.entrySet()) {
+ if (entry.getValue() != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ //String s = "anagram", t = "nagaram";
+ String s = "rat", t = "car";
+
+ System.out.println(new Solution1().isAnagram(s, t));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution2.java"
new file mode 100644
index 00000000..688ca122
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\234\211\346\225\210\347\232\204\345\255\227\346\257\215\345\274\202\344\275\215\350\257\215_242_Easy/Solution2.java"
@@ -0,0 +1,39 @@
+package cn.lastwhisper.leetcode.hashtable.有效的字母异位词_242_Easy;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/valid-anagram/submissions/
+ * hash
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(1)
+ */
+ public boolean isAnagram(String s, String t) {
+ if (s.length() != t.length()) {
+ return false;
+ }
+ char[] sChar = s.toCharArray();
+ char[] tChar = t.toCharArray();
+ byte[] freq = new byte[256];
+ for (int i = 0; i < sChar.length; i++) {
+ freq[sChar[i]]++;
+ }
+
+ for (int i = 0; i < tChar.length; i++) {
+ freq[tChar[i]]--;
+ }
+ for (int i = 0; i < freq.length; i++) {
+ if (freq[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void main(String[] args) {
+ String s = "anagram", t = "nagaram";
+ //String s = "rat", t = "car";
+
+ System.out.println(new Solution2().isAnagram(s, t));
+ }
+
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution1.java"
new file mode 100644
index 00000000..4753700c
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution1.java"
@@ -0,0 +1,63 @@
+package cn.lastwhisper.leetcode.hashtable.根据字符出现频率排序_451_Middle;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class Solution1 {
+ /**
+ * https://leetcode-cn.com/problems/sort-characters-by-frequency/
+ * 核心思想:桶排序,数组下标代表频率
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * 执行用时 :32 ms, 在所有 Java 提交中击败了74.69%的用户
+ * 内存消耗 :38.9 MB, 在所有 Java 提交中击败了95.29%的用户
+ */
+ public String frequencySort(String s) {
+ if (s == null || s.equals("")) return "";
+ Map map = new HashMap<>();
+ // 记录字符出现的最大频率
+ int maxFreq = 0;
+ // 统计所有字符出现的频率
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (map.containsKey(c)) {
+ map.put(c, map.get(c) + 1);
+ } else {
+ map.put(c, 1);
+ }
+ Integer freq = map.get(c);
+ maxFreq = freq > maxFreq ? freq : maxFreq;
+ }
+ // 按频率下标将字符存入对应桶中
+ List[] buckets = new ArrayList[maxFreq + 1];
+ for (Map.Entry entry : map.entrySet()) {
+ Integer freq = entry.getValue();
+ if (buckets[freq] == null) {
+ buckets[freq] = new ArrayList<>();
+ }
+ buckets[freq].add(entry.getKey());
+ }
+ // 倒序组装字符串
+ StringBuilder sb = new StringBuilder();
+ for (int i = maxFreq ; i > 0; i--) {
+ List bucket = buckets[i];
+ if (bucket != null) {
+ for (Character c : bucket) {
+ for (int j = 0; j < i; j++) {
+ sb.append(c);
+ }
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution1().frequencySort("tree"));
+ System.out.println(new Solution1().frequencySort("cccaaa"));
+ System.out.println(new Solution1().frequencySort("Aabb"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution2.java"
new file mode 100644
index 00000000..d2f21fcb
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\346\240\271\346\215\256\345\255\227\347\254\246\345\207\272\347\216\260\351\242\221\347\216\207\346\216\222\345\272\217_451_Middle/Solution2.java"
@@ -0,0 +1,64 @@
+package cn.lastwhisper.leetcode.hashtable.根据字符出现频率排序_451_Middle;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+class Solution2 {
+ /**
+ * https://leetcode-cn.com/problems/sort-characters-by-frequency/
+ * 使用char数组代替StringBuilder
+ * 时间复杂度:O(n)
+ * 空间复杂度:O(n)
+ * 执行用时 : 26 ms , 在所有 Java 提交中击败了 79.49% 的用户
+ * 内存消耗 : 39.5 MB , 在所有 Java 提交中击败了 92.03% 的用户
+ */
+ public String frequencySort(String s) {
+ if (s == null || s.equals("")) return "";
+ char[] sc = s.toCharArray();
+ Map map = new HashMap<>();
+ // 记录字符出现的最大频率
+ int maxFreq = 0;
+ // 统计所有字符出现的频率
+ for (int i = 0; i < sc.length; i++) {
+ char c = sc[i];
+ if (map.containsKey(c)) {
+ map.put(c, map.get(c) + 1);
+ } else {
+ map.put(c, 1);
+ }
+ Integer freq = map.get(c);
+ maxFreq = freq > maxFreq ? freq : maxFreq;
+ }
+ // 按频率下标将字符存入对应桶中
+ List[] buckets = new ArrayList[maxFreq + 1];
+ for (Map.Entry entry : map.entrySet()) {
+ Integer freq = entry.getValue();
+ if (buckets[freq] == null) {
+ buckets[freq] = new ArrayList();
+ }
+ buckets[freq].add(entry.getKey());
+ }
+ // 倒序组装字符串
+ int index = 0;
+ for (int i = maxFreq; i > 0; i--) {
+ List bucket = buckets[i];
+ if (bucket != null) {
+ for (Character c : bucket) {
+ for (int j = 0; j < i; j++) {
+ sc[index++] = c;
+ }
+ }
+ }
+ }
+ return new String(sc);
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(new Solution2().frequencySort("tree"));
+ System.out.println(new Solution2().frequencySort("cccaaa"));
+ System.out.println(new Solution2().frequencySort("Aabb"));
+ }
+}
\ No newline at end of file
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution1.java"
new file mode 100644
index 00000000..a0a8594c
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution1.java"
@@ -0,0 +1,126 @@
+package cn.lastwhisper.leetcode.hashtable.直线上最多的点数_149_困难;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/max-points-on-a-line/
+ * -------------------------------------------------------------------
+ * 思路:暴力解法,任意取两个点构成一条直线,判断其它点在不在这个直线上(斜率)
+ * -------------------------------------------------------------------
+ * 思考:
+ * 1、统计斜率相同且点不同的点数,并输出最大的点数
+ * 2、Q:计算斜率时如何保证浮点数的精确度?
+ * A:(1)将除法意义转为乘法
+ * Q:如何保证乘法不溢出?(65535*65535)
+ * A:long、BigInteger
+ * (2)将直接除法转为求分子分母同时除以(分子分母的)最大公约数后,等式两边分子分母是否同时相等问题
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ * 执行用时 :24 ms, 在所有 Java 提交中击败了42.54%的用户
+ * 内存消耗 :34.5 MB, 在所有 Java 提交中击败了82.67%的用户
+ */
+ public int maxPoints(int[][] points) {
+ // 特殊情况一:0、1、2个点,直接返回点数
+ if (points.length < 3) {
+ return points.length;
+ }
+
+ // 特殊情况二:全为相同的点,直接返回点数
+ int equalNum = 0;
+ for (; equalNum < points.length - 1; equalNum++) {
+ if (points[equalNum][0] != points[equalNum + 1][0] || points[equalNum][1] != points[equalNum + 1][1]) {
+ break;
+ }
+ }
+ if (equalNum == points.length - 1) {
+ return points.length;
+ }
+
+ int max = 0;
+ for (int i = 0; i < points.length; i++) {
+ // 防止数组下标相同i==j时,ij并不构成一根直线
+ for (int j = i + 1; j < points.length; j++) {
+ // 防止数组值相同arr[i]==arr[j]时,ij并不构成一根直线
+ if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
+ continue;
+ }
+ // 前两层循环构成直线
+ int record = 2;
+ for (int k = 0; k < points.length; k++) {
+ // 防止数组下标相同k==i或者k==j时,仅剩两个点(i=0j=2k=0、i=1j=2k=2)
+ if (k != i && k != j) {
+ if (isExist(points[i][0], points[i][1], points[j][0]
+ , points[j][1], points[k][0], points[k][1])) {
+ record++;
+ }
+ }
+ }
+ if (record > max) {
+ max = record;
+ }
+ }
+ }
+ // 加上直线本身的两个点
+ return max;
+ }
+
+ /**
+ * 判断一个点是否在一条直线上
+ */
+ //private boolean isExist(int x1, int y1, int x2, int y2, int x, int y) {
+ // return (y2 - y1) * (x - x2) == (y - y2) * (x2 - x1);
+ //}
+
+ private boolean isExist(long x1, long y1, long x2, long y2, long x, long y) {
+ return (y2 - y1) * (x - x2) == (y - y2) * (x2 - x1);
+ }
+
+ //private boolean isExist(int x1, int y1, int x2, int y2, int x, int y) {
+ // BigInteger x11 = BigInteger.valueOf(x1);
+ // BigInteger x22 = BigInteger.valueOf(x2);
+ // BigInteger y11 = BigInteger.valueOf(y1);
+ // BigInteger y22 = BigInteger.valueOf(y2);
+ // BigInteger x0 = BigInteger.valueOf(x);
+ // BigInteger y0 = BigInteger.valueOf(y);
+ // return y22.subtract(y11).multiply(x0.subtract(x22)).equals(y0.subtract(y22).multiply(x22.subtract(x11)));
+ //}
+
+ //private boolean isExist(int x1, int y1, int x2, int y2, int x, int y) {
+ // int g1 = gcdlcm(y2 - y1, x2 - x1);
+ // if(y == y2 && x == x2){
+ // return true;
+ // }
+ // int g2 = gcdlcm(y - y2, x - x2);
+ // return (y2 - y1) / g1 == (y - y2) / g2 && (x2 - x1) / g1 == (x - x2) / g2;
+ //}
+
+
+ /**
+ * 求a、b最大公约数
+ */
+ private int gcd(int a, int b) {
+ while (b != 0) {
+ int temp = a % b;
+ a = b;
+ b = temp;
+ }
+ return a;
+ }
+
+
+ public static void main(String[] args) {
+ // example
+ //int[][] points = {{1, 1}, {2, 2}, {3, 3}};
+ // error example
+ //int[][] points = {{0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {1, 65536}, {65536, 0}};
+ int[][] points = {{1, 1}, {1, 1}, {2, 3}};
+ System.out.println(new Solution1().maxPoints(points));
+
+ //System.out.println(new Solution1().isExist(3, 3, 2, 2, 1, 1));
+ }
+}
+
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution2.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution2.java"
new file mode 100644
index 00000000..4045a88a
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution2.java"
@@ -0,0 +1,119 @@
+package cn.lastwhisper.leetcode.hashtable.直线上最多的点数_149_困难;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class Solution2 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/max-points-on-a-line/
+ * -------------------------------------------------------------------
+ * 思考:虽然已经保证ij一定构成一条直线,但是ij会重复出现。
+ * -------------------------------------------------------------------
+ * 思路:基于思路1,使用Hash表记录暴力解法中已经出现的直线。
+ * 利用"斜截式"求得直线的k、b,将k、b拼接放入Hash表
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^3)
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ * 执行用时 :78 ms, 在所有 Java 提交中击败了7.91%的用户
+ * 内存消耗 :39.2 MB, 在所有 Java 提交中击败了44.56%的用户
+ */
+ public int maxPoints(int[][] points) {
+ // 特殊情况一:0、1、2个点,直接返回点数
+ if (points.length < 3) {
+ return points.length;
+ }
+
+ // 特殊情况二:全为相同的点,直接返回点数
+ int equalNum = 0;
+ for (; equalNum < points.length - 1; equalNum++) {
+ if (points[equalNum][0] != points[equalNum + 1][0] || points[equalNum][1] != points[equalNum + 1][1]) {
+ break;
+ }
+ }
+ if (equalNum == points.length - 1) {
+ return points.length;
+ }
+
+ Set hash = new HashSet<>();
+
+ int max = 0;
+ for (int i = 0; i < points.length; i++) {
+ // 防止数组下标相同i==j时,ij并不构成一根直线
+ for (int j = i + 1; j < points.length; j++) {
+ // 防止数组值相同arr[i]==arr[j]时,ij并不构成一根直线
+ if (points[i][0] == points[j][0] && points[i][1] == points[j][1]) {
+ continue;
+ }
+
+ String key = getK(points[i][0], points[i][1], points[j][0], points[j][1])
+ + "@"
+ + getB(points[i][0], points[i][1], points[j][0], points[j][1]);
+ if (hash.contains(key)) {
+ continue;
+ }
+
+ hash.add(key);
+
+ // 前两层循环构成直线
+ int record = 2;
+ for (int k = 0; k < points.length; k++) {
+ // 防止数组下标相同k==i或者k==j时,仅剩两个点(i=0j=2k=0、i=1j=2k=2)
+ if (k != i && k != j) {
+ if (isExist(points[i][0], points[i][1], points[j][0]
+ , points[j][1], points[k][0], points[k][1])) {
+ record++;
+ }
+ }
+ }
+ if (record > max) {
+ max = record;
+ }
+ }
+ }
+ // 加上直线本身的两个点
+ return max;
+ }
+
+ /**
+ * 一个点是否在一条直线上
+ */
+ private boolean isExist(long x1, long y1, long x2, long y2, long x, long y) {
+ return (y2 - y1) * (x - x2) == (y - y2) * (x2 - x1);
+ }
+
+ /**
+ * 点斜式常数b
+ */
+ private double getB(int x1, int y1, int x2, int y2) {
+ if (y2 == y1) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return (double) (x2 - x1) * (-y1) / (y2 - y1) + x1;
+ }
+
+ /**
+ * 点斜式斜率k
+ */
+ private double getK(int x1, int y1, int x2, int y2) {
+ if (x2 - x1 == 0) {
+ return Double.POSITIVE_INFINITY;
+ }
+ return (double) (y2 - y1) / (x2 - x1);
+ }
+
+ public static void main(String[] args) {
+ // example
+ //int[][] points = {{1, 1}, {2, 2}, {3, 3}};
+ // error example
+ //int[][] points = {{0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {1, 65536}, {65536, 0}};
+ int[][] points = {{1, 1}, {1, 1}, {2, 3}};
+ System.out.println(new Solution2().maxPoints(points));
+
+ //System.out.println(new Solution1().isExist(3, 3, 2, 2, 1, 1));
+ }
+}
+
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution3.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution3.java"
new file mode 100644
index 00000000..0c71c9d7
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\347\233\264\347\272\277\344\270\212\346\234\200\345\244\232\347\232\204\347\202\271\346\225\260_149_\345\233\260\351\232\276/Solution3.java"
@@ -0,0 +1,81 @@
+package cn.lastwhisper.leetcode.hashtable.直线上最多的点数_149_困难;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Solution3 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/max-points-on-a-line/
+ * -------------------------------------------------------------------
+ * 思考:统计斜率相同的点
+ * -------------------------------------------------------------------
+ * 思路:
+ * Q:计算斜率时如何保证浮点数的精确度?
+ * A:将斜率换成x、y相关的字符串
+ * -------------------------------------------------------------------
+ * 时间复杂度:O(n^2)
+ * 空间复杂度:O(1)
+ * -------------------------------------------------------------------
+ * 执行用时 :20 ms, 在所有 Java 提交中击败了68.23%的用户
+ * 内存消耗 :36.2 MB, 在所有 Java 提交中击败了61.39%的用户
+ */
+ public int maxPoints(int[][] points) {
+ if (points.length < 3) {
+ return points.length;
+ }
+ int res = 0;
+ //遍历每个点
+ for (int i = 0; i < points.length; i++) {
+ int duplicate = 0;
+ int max = 0;//保存经过当前点的直线中,最多的点
+ Map map = new HashMap<>();
+ for (int j = i + 1; j < points.length; j++) {
+ //求出分子分母
+ int x = points[j][0] - points[i][0];
+ int y = points[j][1] - points[i][1];
+ // 相同点
+ if (x == 0 && y == 0) {
+ duplicate++;
+ continue;
+ }
+ //进行约分
+ int gcd = gcd(x, y);
+ x = x / gcd;
+ y = y / gcd;
+ String key = x + "@" + y;
+ map.put(key, map.getOrDefault(key, 0) + 1);
+ max = Math.max(max, map.get(key));
+ }
+ //1 代表当前考虑的点,duplicate 代表和当前的点重复的点
+ res = Math.max(res, max + duplicate + 1);
+ }
+ return res;
+ }
+
+ /**
+ * 求a、b最大公约数
+ */
+ private int gcd(int a, int b) {
+ while (b != 0) {
+ int temp = a % b;
+ a = b;
+ b = temp;
+ }
+ return a;
+ }
+
+ public static void main(String[] args) {
+ // example
+ //int[][] points = {{1, 1}, {2, 2}, {3, 3}};
+ // error example
+ //int[][] points = {{0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {0, 0}, {0, 0}};
+ //int[][] points = {{0, 0}, {1, 65536}, {65536, 0}};
+ int[][] points = {{1, 1}, {1, 1}, {2, 3}};
+ System.out.println(new Solution3().maxPoints(points));
+
+ //System.out.println(new Solution3().gcdlcm(3,6));
+ }
+}
+
diff --git "a/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\351\225\277\345\233\236\346\226\207\344\270\262_409_\347\256\200\345\215\225/Solution1.java" "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\351\225\277\345\233\236\346\226\207\344\270\262_409_\347\256\200\345\215\225/Solution1.java"
new file mode 100644
index 00000000..ffe0ae4c
--- /dev/null
+++ "b/algorithms/leetcode/hashtable/src/main/java/cn/lastwhisper/leetcode/hashtable/\351\225\277\345\233\236\346\226\207\344\270\262_409_\347\256\200\345\215\225/Solution1.java"
@@ -0,0 +1,43 @@
+package cn.lastwhisper.leetcode.hashtable.长回文串_409_简单;
+
+import org.junit.Assert;
+
+class Solution1 {
+ /**
+ * 题目地址:https://leetcode-cn.com/problems/longest-palindrome/
+ * -------------------------------------------------------------------
+ * 思路:
+ * -------------------------------------------------------------------
+ * 思考:
+ * -------------------------------------------------------------------
+ * 时间复杂度:
+ * 空间复杂度:
+ * -------------------------------------------------------------------
+ */
+ public int longestPalindrome(String s) {
+ int[] freq = new int[58];
+ for (int i = 0; i < s.length(); i++) {
+ freq[s.charAt(i) - 'A']++;
+ }
+ int ans = 0;
+ for (int num : freq) {
+ // 偶数直接可以组成回文串,奇数取最大偶数组成回文串
+ //不等价 if (num != 0) ans += num;return ans ;
+ //简写 if (num != 0) ans += num - (num & 1);
+ if (num != 0) {
+ if (num % 2 == 0) {
+ ans += num;
+ } else {
+ ans += num - 1;//大于1的奇数串只取偶数个,return补偿
+ }
+ }
+ }
+ return ans < s.length() ? ans + 1 : ans;
+ }
+
+ public static void main(String[] args) {
+ //Assert.assertEquals(7, new Solution1().longestPalindrome("abccccdd"));
+ Assert.assertEquals(3, new Solution1().longestPalindrome("ccc"));
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/leetcode-common/pom.xml b/algorithms/leetcode/leetcode-common/pom.xml
new file mode 100644
index 00000000..9fc1cf2e
--- /dev/null
+++ b/algorithms/leetcode/leetcode-common/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ leetcode
+ cn.lastwhisper
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ leetcode-common
+
+
+
\ No newline at end of file
diff --git a/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtil.java b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtil.java
new file mode 100644
index 00000000..70e5f2bc
--- /dev/null
+++ b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/array/ArrayUtil.java
@@ -0,0 +1,72 @@
+package cn.lastwhisper.leetcode.common.array;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author lastwhisper
+ * @date 2020/2/4
+ */
+public class ArrayUtil {
+
+ /**
+ * {"ab","cd"}=>
+ * 创建二维字符数组
+ */
+ public static char[][] createCharArrays(String[] arr) {
+ int m = arr.length, n = arr[0].length();
+ char[][] chars = new char[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ chars[i][j] = arr[i].charAt(j);
+ }
+ }
+ return chars;
+ }
+
+ /**
+ * {{"a","b","c","d"}}=>
+ * 创建二维字符数组
+ */
+ public static char[][] createCharArrays(String[][] arr) {
+ int m = arr.length, n = arr[0].length;
+ char[][] chars = new char[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ chars[i][j] = arr[i][j].charAt(0);
+ }
+ }
+ return chars;
+ }
+
+ /**
+ * 创建二维数组
+ */
+ public static int[][] createIntArrays(String[] arr) {
+ int m = arr.length, n = arr[0].length();
+ int[][] ints = new int[m][n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ ints[i][j] = arr[i].charAt(j) - '0';
+ }
+ }
+ return ints;
+ }
+
+
+ /**
+ * 创建list
+ */
+ public static List> createList(int[][] arrays) {
+ List> lists = new ArrayList<>(arrays.length);
+ for (int[] array : arrays) {
+ List list = new ArrayList<>(array.length);
+ for (int num : array) {
+ list.add(num);
+ }
+ lists.add(list);
+ }
+ return lists;
+ }
+}
diff --git a/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtil.java b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtil.java
new file mode 100644
index 00000000..de66fc6c
--- /dev/null
+++ b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/LinkedListUtil.java
@@ -0,0 +1,151 @@
+package cn.lastwhisper.leetcode.common.linkedlist;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 操作链表工具类
+ * @author lastwhisper
+ * @date 1/7/2020
+ */
+public class LinkedListUtil {
+
+ /**
+ * 创建单链表
+ */
+ public static ListNode createListNode(int... arr) {
+ if (arr == null) {
+ return null;
+ }
+ ListNode head = new ListNode(arr[0]);
+ ListNode current = head;
+
+ for (int i = 1; i < arr.length; i++) {
+ current.next = new ListNode(arr[i]);
+ current = current.next;
+ }
+ return head;
+ }
+
+ /**
+ * 创建环形链表
+ * 应用题目:https://leetcode-cn.com/problems/linked-list-cycle/
+ * @param pos 成环位置
+ * @param arr 链表元素
+ */
+ public static ListNode createLoopListNode(int pos, int... arr) {
+ if (arr == null) {
+ return null;
+ }
+ ListNode head = new ListNode(arr[0]);
+ ListNode current = head;
+ // 锚点,记录成环的位置
+ ListNode anchor = null;
+ if (pos == 0) {
+ anchor = head;
+ }
+ for (int i = 1; i < arr.length; i++) {
+ current.next = new ListNode(arr[i]);
+ // 记录成环位置
+ if (pos == i) {
+ if (pos == arr.length - 1) {
+ anchor = current;
+ } else {
+ anchor = current.next;
+ }
+ }
+ current = current.next;
+ }
+ current.next = anchor;
+ return head;
+ }
+
+ /**
+ * 创建两个相交链表
+ * 应用题目:https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
+ * @param intersectVal 相交点的值
+ * @param listA 链表A元素数组
+ * @param listB 链表B元素数组
+ * @param skipA 链表A相交前节点数
+ * @param skipB 链表B相交前节点数
+ */
+ public static List createIntersectListNode(
+ int intersectVal, int[] listA, int[] listB, int skipA, int skipB) {
+ List listNodes = new ArrayList<>(2);
+
+ ListNode listNodeA = new ListNode(listA[0]);
+ ListNode currentA = listNodeA;
+ ListNode listNodeB = new ListNode(listB[0]);
+ ListNode currentB = listNodeB;
+
+ for (int i = 1; i < skipA; i++) {
+ currentA.next = new ListNode(listA[i]);
+ currentA = currentA.next;
+ }
+
+ for (int i = 1; i < skipB; i++) {
+ currentB.next = new ListNode(listB[i]);
+ currentB = currentB.next;
+ }
+
+ ListNode intersectListNode = new ListNode(listA[skipA]);
+ ListNode currentIntersect = intersectListNode;
+
+ for (int i = skipA + 1; i < listA.length; i++) {
+ currentIntersect.next = new ListNode(listA[i]);
+ currentIntersect = currentIntersect.next;
+ }
+
+ currentA.next = intersectListNode;
+ currentB.next = intersectListNode;
+
+ listNodes.add(listNodeA);
+ listNodes.add(listNodeB);
+
+ return listNodes;
+ }
+
+
+ /**
+ * 打印单链表
+ */
+ public static void printListNode(String msg, ListNode head) {
+ System.out.println(msg + appendVal(head));
+ }
+
+ /**
+ * 打印单链表
+ */
+ public static void printListNode(ListNode head) {
+ System.out.println(appendVal(head));
+ }
+
+ private static String appendVal(ListNode head) {
+ StringBuilder sb = new StringBuilder();
+ ListNode current = head;
+ while (current != null) {
+ sb.append(current.val);
+ sb.append("->");
+ current = current.next;
+ }
+ sb.append("NULL");
+ return sb.toString();
+ }
+
+
+ public static void main(String[] args) {
+ //printListNode(createListNode(1, 2, 3, 4, 5, 6, 7));
+ //ListNode headLoop = createLoopListNode(0, 4, 5, 6);
+ //ListNode centerLoop = createLoopListNode(1, 4, 5, 6);
+
+ int intersectVal = 8, skipA = 2, skipB = 3;
+ int[] listA = {4, 1, 8, 4, 5};
+ int[] listB = {5, 0, 1, 8, 4, 5};
+ List intersectListNode = createIntersectListNode(intersectVal, listA, listB, skipA, skipB);
+ for (ListNode listNode : intersectListNode) {
+ printListNode(listNode);
+ }
+
+ }
+}
diff --git a/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java
new file mode 100644
index 00000000..4d78a4b2
--- /dev/null
+++ b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/linkedlist/ListNode.java
@@ -0,0 +1,11 @@
+package cn.lastwhisper.leetcode.common.linkedlist;
+
+public class ListNode {
+ public int val;
+ public ListNode next;
+
+ public ListNode(int x) {
+ val = x;
+ }
+
+}
\ No newline at end of file
diff --git a/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtil.java b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtil.java
new file mode 100644
index 00000000..cbed2538
--- /dev/null
+++ b/algorithms/leetcode/leetcode-common/src/main/java/cn/lastwhisper/leetcode/common/print/PrintUtil.java
@@ -0,0 +1,93 @@
+package cn.lastwhisper.leetcode.common.print;
+
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * 打印工具类
+ * @author lastwhisper
+ * @date 2020/2/1
+ */
+public class PrintUtil {
+
+ /**
+ * 层次打印二维数组
+ */
+ public static void printLists(List> lists) {
+ System.out.println("[");
+ int counter1 = 1;
+ for (List