diff --git a/Week01/NOTE.md b/Week01/NOTE.md
index 50de30414..3ead73a99 100644
--- a/Week01/NOTE.md
+++ b/Week01/NOTE.md
@@ -1 +1,41 @@
-学习笔记
\ No newline at end of file
+## 数据结构时间复杂度
+
+
+| 类型 | add | remove | query | modify | peculiarity |
+| :----: | :----: | :----: | :----: | :----: | :----: |
+| Array | O(n) | O(n) | O(1) | O(1) | 随机加速访问 |
+| Linked List | O(1) | O(1) | O(n) | O(1) | next指针 |
+| Double Linked List | O(1) | O(1) | O(n) | O(1) | pre,next指针 |
+| Skip List | O(logn) | O(logn) | O(logn) | -- | 元素有序 |
+| Stack | O(1) | O(1) | O(n) | -- | 后进先出 |
+| Queue | O(1) | O(1) | O(n) | -- | 先进先出 |
+| Deque | O(1) | O(1) | O(n) | -- | 两端都可进出的Quue |
+| Priority Queue | O(1) | -- | O(logn) | -- | 两端都可进出的Quue |
+
+
+## PriorityQueue分析
+```
+balanced binary heap:
+ 父结点的键值总是小于或等于任何一个子节点的键值
+ 基于数组实现的二叉堆,对于数组中任意位置的n上元素,其左孩子在[2n+1]位置上,右孩子[2(n+1)]位置,它的父亲则在[n-1/2]上,而根的位置则是[0]
+fields:
+ comparator--比较器,无此参数元素使用自然排序
+ queue--数组(存数据)
+ size--元素个数
+method:
+ #grow -- 数组扩容
+ #hugeCapacity -- 容量:0-Integer.MAX_VALUE
+ #add -- 调用offer(E e)
+ #offer -- size >= queue.length (扩容),size == 0直接添加为根,否则从队尾开始上移 --> siftUp
+ #siftUp
+ #siftUpComparable -- 一直上移至找到父节点
+ #siftUpUsingComparator -- 操作类似,自然排序
+ #peek -- 查看队列首元素,空时返回null
+ #indexOf -- 查看元素对应下标
+ #remove -- 获取要移除元素下标i,--size,提取e = queue[size],queue[size] = null, i < size / 2将queue[i]不断下移,否则上移
+ #poll -- 移除根, --size,提取e = queue[size],queue[size] = null,queue[i]不断下移,直到queue[i]的最小孩子比e 大为止
+ #siftDown
+ #siftDownComparable -- 以k为父节点找子节点(left, right),c = queue[left],
+ 如果left < right && queue[left] > queue[right] { c = queue[right] },交换父节点和c;直至队尾元素比c的子节点都要小
+ #siftDownUsingComparator -- 操作类似,自然排序
+```
\ No newline at end of file
diff --git a/Week01/homework.md b/Week01/homework.md
new file mode 100644
index 000000000..ee0b074c7
--- /dev/null
+++ b/Week01/homework.md
@@ -0,0 +1,129 @@
+## Week01 homework
+
+
+### 删除排序数组中的重复项
+```java
+public int removeDuplicates(int[] nums) {
+ if (nums == null && nums.length == 0) {
+ return 0;
+ }
+ int left = 0;
+ for (int i = 1; i < nums.length; i++) {
+ if (nums[i] != nums[left]) {
+ nums[++left] = nums[i];
+ }
+ }
+ return ++left;
+}
+```
+
+
+### 旋转数组
+```java
+public void rotate(int[] nums, int k) {
+ // 使用环状替代,k %= nums.length
+ k %= nums.length;
+ int count = 0;
+ for (int start = 0; count < nums.length; start++) {
+ int current = start;
+ int pre = nums[start];
+ do {
+ int next = (current + k) % nums.length;
+ int temp = nums[next];
+ nums[next] = pre;
+ pre = temp;
+ current = next;
+ count++;
+ } while (start != current);
+ }
+}
+```
+
+
+### 合并两个有序链表
+```java
+public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
+ ListNode preHead = new ListNode(-1);
+ ListNode pre = preHead;
+ while (l1 != null && l2 != null) {
+ if (l1.val < l2.val) {
+ pre.next = l1;
+ l1 = l1.next;
+ } else {
+ pre.next = l2;
+ l2 = l2.next;
+ }
+ pre = pre.next;
+ }
+ pre.next = l1 == null ? l2 : l1;
+ return preHead.next;
+}
+```
+
+
+### 合并两个有序数组
+```java
+public void merge(int[] nums1, int m, int[] nums2, int n) {
+ while (m > 0 && n > 0) {
+ nums1[m + n - 1] = nums1[m - 1] > nums2[n - 1] ? nums1[--m] : nums2[--n];
+ }
+ for (int i = 0; i < n; i++) {
+ nums1[i] = nums2[i];
+ }
+}
+```
+
+
+### 两数之和
+```java
+public int[] twoSum(int[] nums, int target) {
+ // 循环遍历数组,num[i]存在于map中,直接返回map中的value和i
+ // 否则,将key:target - nums[i] value:i 存入map中
+ int length = nums.length;
+ Map map = new HashMap<>(length);
+ for (int i = 0; i < length; i++) {
+ if (map.containsKey(nums[i])) {
+ return new int[]{map.get(nums[i]), i};
+ }
+ map.put(target - nums[i], i);
+ }
+ return null;
+}
+```
+
+
+### 移动零
+```java
+public void moveZeroes(int[] nums) {
+ if (nums == null) {
+ return;
+ }
+ int index = 0;
+ for (int i = 0; i < nums.length; i++) {
+ if (nums[i] != 0) {
+ if (index != i) {
+ nums[index] = nums[i];
+ nums[i] = 0;
+ }
+ index++;
+ }
+ }
+}
+```
+
+### 加一
+```java
+public int[] plusOne(int[] digits) {
+ // digits数组中所有位全进1,则新new一个digits.length + 1长度的数组,并把首位置为1
+ for (int i = digits.length - 1; i >= 0; i--) {
+ digits[i] += 1;
+ if (digits[i] % 10 != 0) {
+ return digits;
+ }
+ digits[i] = 0;
+ }
+ digits = new int[digits.length + 1];
+ digits[0] = 1;
+ return digits;
+}
+```
\ No newline at end of file
diff --git a/Week02/NOTE.md b/Week02/NOTE.md
index 50de30414..1dadffeba 100644
--- a/Week02/NOTE.md
+++ b/Week02/NOTE.md
@@ -1 +1,95 @@
-学习笔记
\ No newline at end of file
+### 树
+```
+父节点中有所有子节点的指针
+ 前序遍历
+ 后序遍历
+ 层序遍历
+```
+
+### 二叉树
+```
+父节点中有左右子节点的指针
+ 前序遍历 -- 根-左-右
+ 中序遍历 -- 左-根-右
+ 后续遍历 -- 左-右-根
+ 层序遍历
+```
+
+### 二叉搜索树
+```
+1.左节点小于根节点
+2.根节点小于右节点
+中序遍历为升序排列
+```
+
+### 堆
+```
+常见的有二叉堆(完全二叉树),斐波那契堆等
+ 一般用于找最值,PriorityQueue有二叉堆实现
+ 1.根为最大值:大顶堆
+ 父节点 >= 子节点
+ 2.根为最小值:小顶堆
+ 父节点 <= 子节点
+若父节点索引为i:
+ 左节点索引:2 * i + 1 or i << 1 + 1
+ 右节点索引:2 * i + 1 or i << 1 + 2
+若子节点序索引i:
+ 父节点索引:(i - 1) / 2 or (i - 1) >> 1
+```
+
+### HashMap
+#### 常量
+```
+链表+数组(红黑树)
+```
+
+```java
+// 默认的初始容量是16
+static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
+// 最大容量
+static final int MAXIMUM_CAPACITY = 1 << 30;
+// 默认加载充因子
+static final float DEFAULT_LOAD_FACTOR = 0.75f;
+// 当桶(bucket)上的结点数大于这个值时会转成红黑树
+static final int TREEIFY_THRESHOLD = 8;
+// 当桶(bucket)上的结点数小于这个值时树转链表
+static final int UNTREEIFY_THRESHOLD = 6;
+// 桶中结构转化为红黑树对应的table的最小大小
+static final int MIN_TREEIFY_CAPACITY = 64;
+// 存储元素的数组,总是2的幂次倍
+transient Node[] table;
+// 存放具体元素的集
+transient Set> entrySet;
+// 存放元素的个数,注意这个不等于数组的长度。
+transient int size;
+// 每次扩容和更改map结构的计数器
+transient int modCount;
+// 临界值 当实际大小(容量*加载因子)超过临界值时,会进行扩容
+int threshold;
+// 加载因子
+final float loadFactor;
+```
+
+#### putVal
+```
+1.计算hash值,确定在table中的位置
+2.将元素插入指定位置
+ 1) 如果没有元素,直接插入
+ a.插入后,数组元素超过扩容阈值(threshold),对table进行扩容
+ b.插入后,小于threshold,结束
+ 2) 如果有元素,挂在已有元素后
+ a.如果之前已形成树,直接插入树对应的位置
+ b.如果之前元素组成链表
+ a) 加入元素后链表长度 > 8,将链表转为红黑树插入
+ b) 加入元素后链表长度 <= 8, 直接插入
+```
+
+#### get
+```
+1.根据key,hash计算出存放在数组中的位置
+2.取出指定位置的元素
+ 1) key完全相同,取出对应的value
+ 2) 若key不相同,判断挂载的是链表还是红黑树
+ a. 若为树,按照树的方法查找
+ b. 若为链表,按照链表的方法查找
+```
\ No newline at end of file
diff --git a/Week02/homework.md b/Week02/homework.md
new file mode 100644
index 000000000..221abb12d
--- /dev/null
+++ b/Week02/homework.md
@@ -0,0 +1,322 @@
+### 有效的字母异位词
+```
+异位词:
+ 两个字符串s与t,s和t相同字母出现次数相同
+```
+
+```java
+/**
+ * 暴力
+ */
+public boolean isAnagram(String s, String t) {
+ if (s == null || t == null || s.length() != t.length()) {
+ return false;
+ }
+ // 字符串排序,比较字符串是否相等
+ char[] ss = s.toCharArray();
+ char[] tt = t.toCharArray();
+ Arrays.sort(ss);
+ Arrays.sort(tt);
+ return Arrays.equals(ss, tt);
+}
+```
+
+
+```java
+public boolean isAnagram(String s, String t) {
+ if (s == null || t == null || s.length() != t.length()) {
+ return false;
+ }
+ /*
+ * 只有小写字母(a-z) 26位,数组result存字母出现次数的计数器
+ * 1.当s中出现该字母,则对应下标值+1
+ * 2.当t中出现该字母,则对应下标值-1
+ */
+ int[] result = new int[26];
+ for (int i = 0; i < s.length(); i++) {
+ result[s.charAt(i) - 'a']++;
+ }
+ for (int i = 0; i < t.length(); i++) {
+ result[t.charAt(i) - 'a']--;
+ if (result[t.charAt(i) - 'a'] < 0) {
+ return false;
+ }
+ }
+ return true;
+}
+```
+
+
+### 字母异位词分组
+
+```java
+public List> groupAnagrams(String[] strs) {
+ if (strs == null || strs.length == 0) {
+ return new ArrayList<>();
+ }
+ Map> map = new HashMap<>();
+ for (int i = 0; i < strs.length; i++) {
+ // 字符串排序
+ char[] arr = new char[26];
+ for (char ch : strs[i].toCharArray()) {
+ arr[ch - 'a']++;
+ }
+ String key = String.valueOf(arr);
+ // map中不存在key,添加空集合
+ if (!map.containsKey(key)) {
+ map.put(key, new ArrayList<>());
+ }
+ map.get(key).add(strs[i]);
+ }
+ return new ArrayList<>(map.values());
+}
+```
+
+
+### 二叉树的前序遍历
+```java
+public List preorderTraversal(TreeNode root) {
+ List result = new ArrayList<>();
+ preOrder(root, result);
+ return result;
+}
+
+public void preOrder(TreeNode node, List list) {
+ if (node == null) {
+ return;
+ }
+ // 前序遍历:根-左-右
+ list.add(node.val);
+ preOrder(node.left, list);
+ preOrder(node.right, list);
+}
+```
+
+
+### 二叉树的中序遍历
+```java
+public List inorderTraversal(TreeNode root) {
+ List result = new ArrayList<>();
+ inOrder(root, result);
+ return result;
+}
+
+public void inOrder(TreeNode node, List list) {
+ if (node == null) {
+ return;
+ }
+ // 中序遍历:左-根-右
+ inOrder(node.left, list);
+ list.add(node.val);
+ inOrder(node.right, list);
+}
+```
+
+
+### N叉树的前序遍历
+```java
+public List preorder(Node root) {
+ List result = new ArrayList<>();
+ preOrder(root, result);
+ return result;
+}
+public void preOrder(Node node, List list){
+ if (node == null) {
+ return;
+ }
+ // N叉树的前序遍历:根-从左到右子节点遍历
+ list.add(node.val);
+ if (node.children != null) {
+ for (Node childNode : node.children) {
+ preOrder(childNode, list);
+ }
+ }
+}
+```
+
+
+### N叉树的后序遍历
+```java
+public List postorder(Node root) {
+ List result = new ArrayList<>();
+ postOrder(root, result);
+ return result;
+}
+
+public void postOrder(Node node, List list) {
+ if (node == null) {
+ return;
+ }
+ // N叉树后序遍历:从左到右子节点遍历-根
+ if (node.children != null) {
+ for (Node childNode : node.children) {
+ postOrder(childNode, list);
+ }
+ }
+ list.add(node.val);
+}
+```
+
+
+### N叉树的层序遍历
+```java
+public List> levelOrder(Node root) {
+ if (root == null) {
+ return new ArrayList<>();
+ }
+ // 双端队列,存子节点
+ Deque deque = new LinkedList<>();
+ deque.add(root);
+ List> result = new ArrayList<>();
+ while(!deque.isEmpty()){
+ int size = deque.size();
+ List list = new ArrayList<>();
+ for (int i = 0; i < size; i++) {
+ Node node = deque.poll();
+ list.add(node.val);
+ deque.addAll(node.children);
+ }
+ result.add(list);
+ }
+ return result;
+}
+```
+
+
+### 最小的k个数
+
+```java
+/**
+ * 暴力
+ */
+public int[] getLeastNumbers(int[] arr, int k) {
+ if(arr == null || k < 1 || arr.length < k){
+ return new int[]{};
+ }
+ int[] result = new int[k];
+ Arrays.sort(arr);
+ for(int i = 0; i < k; i++){
+ result[i] = arr[i];
+ }
+ return result;
+}
+```
+
+```java
+/**
+ * 最小堆
+ */
+public int[] getLeastNumbers(int[] arr, int k) {
+ if(arr == null || k < 1 || arr.length < k){
+ return new int[]{};
+ }
+ PriorityQueue queue = new PriorityQueue<>(k, (i1, i2) -> Integer.compare(i2, i1));
+ for (int i = 0; i < arr.length; i++) {
+ if (queue.size() > k - 1) {
+ if (queue.peek() > arr[i]) {
+ queue.poll();
+ queue.offer(arr[i]);
+ }
+ } else {
+ queue.offer(arr[i]);
+ }
+ }
+ int[] result = new int[k];
+ int index = 0;
+ for (Integer num : queue) {
+ result[index++] = num;
+ }
+ return result;
+}
+```
+
+
+### 滑动窗口最大值
+```java
+public int[] maxSlidingWindow(int[] nums, int k) {
+ if (nums == null || k < 1 || nums.length < k) return new int[0];
+ if (k == 1) return nums;
+ // max:移动框中的最大值 count:移动框中的最大值数量
+ int max = 0, count = 0;
+ int[] result = new int[nums.length - k + 1];
+ Deque queue = new LinkedList<>();
+ for (int i = 0; i < k; i++) {
+ queue.offer(nums[i]);
+ max = Math.max(max, nums[i]);
+ count = max == nums[i] ? ++count : 1;
+ }
+ result[0] = max;
+ for (int i = k; i < nums.length; i++) {
+ int removeNum = queue.poll();
+ queue.offer(nums[i]);
+ max = Math.max(max, nums[i]);
+ count = max == nums[i] ? ++count : 1;
+ // 添加新元素后,移除元素值为max且框中无重复max时,重新计算框中的max和count
+ if (max == removeNum && --count < 1) {
+ max = queue.peek();
+ for (Integer num : queue) {
+ if (max <= num) {
+ count = max == num ? ++count : 1;
+ max = num;
+ }
+ }
+ }
+ result[i - k + 1] = max;
+ }
+ return result;
+}
+```
+
+
+### 丑数 II
+```java
+public int nthUglyNumber(int n) {
+ int[] ugly = new int[n];
+ ugly[0] = 1;
+ int index2 = 0, index3 = 0, index5 = 0;
+ int factor2 = 2, factor3 = 3, factor5 = 5;
+ for (int i = 1; i < n; i++) {
+ int min = Math.min(Math.min(factor2, factor3), factor5);
+ ugly[i] = min;
+ if (min == factor2) {
+ factor2 = 2 * ugly[++index2];
+ }
+ if (min == factor3) {
+ factor3 = 3 * ugly[++index3];
+ }
+ if (min == factor5) {
+ factor5 = 5 * ugly[++index5];
+ }
+ }
+ return ugly[n - 1];
+}
+```
+
+
+### 前 K 个高频元素
+```java
+public int[] topKFrequent(int[] nums, int k) {
+ // key:元素 value:元素出现次数
+ Map map = new HashMap<>();
+ for (Integer num : nums) {
+ map.put(num, map.containsKey(num) ? map.get(num) + 1 : 1);
+ }
+ // map中前k高频率元素
+ PriorityQueue heap = new PriorityQueue<>((n1, n2) -> map.get(n1) - map.get(n2));
+ for (Integer key : map.keySet()) {
+ if (heap.size() < k) {
+ heap.offer(key);
+ }else if (map.get(heap.peek()) < map.get(key)) {
+ heap.poll();
+ heap.offer(key);
+ }
+ }
+ // 结果
+ int[] result = new int[k];
+ int index = k - 1;
+ while(!heap.isEmpty()){
+ result[index--] = heap.poll();
+ }
+ return result;
+}
+```
\ No newline at end of file
diff --git a/Week03/NOTE.md b/Week03/NOTE.md
index 50de30414..6b6f67005 100644
--- a/Week03/NOTE.md
+++ b/Week03/NOTE.md
@@ -1 +1,39 @@
-学习笔记
\ No newline at end of file
+### 泛型递归模板
+```java
+public void recurse(int level, int param) {
+ // terminator
+ if (level > MAX_VALUE) {
+ // process result
+ return;
+ }
+ // process logic
+ process(level, param);
+ // drill down
+ recurse(level + 1, new Param);
+ // restore current status if needed
+}
+```
+
+
+### 分治代码模板
+```java
+public void divideConquer(int problem, int param) {
+ // terminator
+ if (level > MAX_VALUE) {
+ // process result
+ return;
+ }
+ // prepare data
+ int data = prepareData(problem);
+ Integer[] splitProblem(problem, data);
+ // conquer subProblem
+ int subResult1 = divideConquer(splitProblem[0], newparam);
+ int subResult2 = divideConquer(splitProblem[1], newparam);
+ ...
+
+ // process and generate the final result
+ int result = processResult(subResult1, subResult2, ...);
+
+ // revert the current level status if needed
+}
+```
\ No newline at end of file
diff --git a/Week03/homework.md b/Week03/homework.md
new file mode 100644
index 000000000..2f3070a69
--- /dev/null
+++ b/Week03/homework.md
@@ -0,0 +1,330 @@
+### 爬楼梯
+```java
+/**
+ * 递归
+ */
+public int climbStairs(int n) {
+ return fibonacci(n, 1, 1);
+}
+
+public int fibonacci(int n, int a, int b) {
+ return n <= 1 ? b : fibonacci(n - 1, b, a + b);
+}
+
+/**
+ * 循环累加 f(n) = f(n - 1) + f(n - 2);
+ */
+public int climbStairs(int n) {
+ int first = 1, second = 1;
+ for (int i = 1; i < n; i++) {
+ int temp = second;
+ second += first;
+ first = temp;
+ }
+ return second;
+}
+```
+
+
+### 括号生成
+```java
+List result = new ArrayList<>();
+public List generateParenthesis(int n) {
+ recurse(n, n, "");
+ return result;
+}
+
+public void recurse(int left, int right, String str) {
+ if (left == 0 && right == 0) {
+ result.add(str);
+ return;
+ }
+ // add "("
+ if (left > 0) recurse(left - 1, right, str + "(");
+ // add ")"
+ if (right > left) recurse(left, right - 1, str + ")");
+}
+```
+
+
+### 翻转二叉树
+```java
+public TreeNode invertTree(TreeNode root) {
+ recurse(root);
+ return root;
+}
+public void recurse(TreeNode node) {
+ if (node == null) return;
+ // 翻转左右子节点
+ TreeNode left = node.left;
+ node.left = node.right;
+ node.right = left;
+ // 遍历左子树
+ recurse(node.left);
+ // 遍历右子树
+ recurse(node.right);
+}
+```
+
+
+### 验证二叉搜索树
+```java
+public boolean isValidBST(TreeNode root) {
+ return recurse(root, null, null);
+}
+
+public boolean recurse(TreeNode node, Integer lowwer, Integer upper) {
+ if (node == null) return true;
+ int val = node.val;
+ /*
+ * 根节点的左子树中:
+ * 右子节点大于父节点且小于最近节点是父节点左节点的值
+ * 左子节点小于父节点
+ * 根节点的右子树中:
+ * 左子节点小于父节点且大于最近节点是父节点右节点的值
+ * 右子节点大于父节点
+ */
+ if (lowwer != null && val <= lowwer) return false;
+ if (upper != null && val >= upper) return false;
+ return recurse(node.left, lowwer, val) && recurse(node.right, val, upper);
+}
+```
+
+
+### 二叉树的最大深度
+```java
+int max;
+
+public int maxDepth(TreeNode root) {
+ recurse(root, 0);
+ return max;
+}
+
+public void recurse(TreeNode node, int curDepth) {
+ if (node == null) {
+ max = Math.max(max, curDepth);
+ return;
+ }
+ // left child
+ recurse(node.left, curDepth + 1);
+ // right child
+ recurse(node.right, curDepth + 1);
+}
+```
+
+
+### 二叉树的最小深度
+```java
+public int minDepth(TreeNode root) {
+ if (root == null) return 0;
+ // left child
+ int s1 = minDepth(root.left);
+ // right child
+ int s2 = minDepth(root.right);
+ /*
+ * 根节点到最小子节点的距离
+ * 无左节点时,从右节点中找,反之亦然
+ */
+ return root.left == null || root.right == null ? s1 + s2 + 1 : Math.min(s1, s2) + 1;
+}
+```
+
+
+### Pow(x, n)
+```java
+public double myPow(double x, int n) {
+ return n > 0 ? recurse(x, n) : 1 / recurse(x, -n);
+}
+public double recurse(double x, int n) {
+ if (n == 0) return 1;
+ double result = recurse(x, n / 2);
+ return n % 2 == 0 ? result * result : result * result * x;
+}
+```
+
+
+### 子集
+```java
+/**
+ * 前序遍历
+ */
+List> result = new ArrayList<>();
+public List> subsets(int[] nums) {
+ result.add(new ArrayList<>());
+ recurse(nums, 0, new ArrayList<>());
+ return result;
+}
+
+public void recurse(int[] nums, int index, List subSet) {
+ if (index >= nums.length) {
+ return;
+ }
+ subSet = new ArrayList<>(subSet);
+ result.add(subSet);
+ recurse(nums, index + 1, subSet);
+ subSet.add(nums[index]);
+ recurse(nums, index + 1, subSet);
+}
+
+/**
+ * 回溯
+ */
+List> result = new ArrayList<>();
+public List> subsets(int[] nums) {
+ result.add(new ArrayList<>());
+ recurse(nums, 0, new ArrayList<>());
+ return result;
+}
+
+public void recurse(int[] nums, int index, List subSet) {
+ if (index >= nums.length) {
+ return;
+ }
+ subSet.add(nums[index]);
+ result.add(new ArrayList<>(subSet));
+ recurse(nums, index + 1, subSet);
+ subSet.remove(subSet.size() - 1);
+ recurse(nums, index + 1, subSet);
+}
+```
+
+
+### 多数元素
+```java
+public int majorityElement(int[] nums) {
+ int flag = nums[0], count = 1;
+ for (int i = 1; i < nums.length; i++) {
+ if (count == 0) flag = nums[i];
+ count += nums[i] == flag ? 1 : -1;
+ }
+ return flag;
+}
+```
+
+
+### 电话号码的字母组合
+```java
+List result = new ArrayList<>();
+Map map = new HashMap<>();
+{
+ map.put('2', new String[]{"a", "b", "c"});
+ map.put('3', new String[]{"d", "e", "f"});
+ map.put('4', new String[]{"g", "h", "i"});
+ map.put('5', new String[]{"j", "k", "l"});
+ map.put('6', new String[]{"m", "n", "o"});
+ map.put('7', new String[]{"p", "q", "r", "s"});
+ map.put('8', new String[]{"t", "u", "v"});
+ map.put('9', new String[]{"w", "x", "y", "z"});
+}
+
+public List letterCombinations(String digits) {
+ if (digits == null || digits.length() == 0) return result;
+ recurse(digits, 0, "");
+ return result;
+}
+
+public void recurse(String digits, int index, String str) {
+ if (index >= digits.length()) {
+ result.add(str);
+ return;
+ }
+ for (String s : map.get(digits.charAt(index))) {
+ recurse(digits, index + 1, str + s);
+ }
+}
+```
+
+
+### 二叉树的最近公共祖先
+```java
+public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
+ if (root == null || root == p || root == q) return root;
+ TreeNode leftNode = lowestCommonAncestor(root.left, p, q);
+ TreeNode rightNode = lowestCommonAncestor(root.right, p, q);
+ return leftNode != null && rightNode != null ? root : (leftNode == null ? rightNode : leftNode);
+}
+```
+
+
+### 从前序与中序遍历序列构造二叉树
+```java
+Map indexMap = new HashMap<>();
+public TreeNode buildTree(int[] preorder, int[] inorder) {
+ for (int i = 0; i < inorder.length; i++) {
+ indexMap.put(inorder[i], i);
+ }
+ return recurse(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
+}
+
+public TreeNode recurse(int[] preorder, int preStart, int preEnd, int[] inorder, int inStart, int inEnd) {
+ if (preStart > preEnd) return null;
+ // 获取根节点在inorder中的位置
+ int rootVal = preorder[preStart];
+ // 创建根节点
+ TreeNode root = new TreeNode(rootVal);
+ int pIndex = indexMap.get(rootVal);
+ // 左子树
+ root.left = recurse(preorder, preStart + 1, pIndex - inStart + preStart, inorder, inStart, pIndex - 1);
+ // 右子树
+ root.right = recurse(preorder, preEnd + pIndex - inEnd + 1, preEnd, inorder, pIndex + 1, inEnd);
+ return root;
+}
+```
+
+
+### 组合
+```java
+List> result = new ArrayList<>();
+public List> combine(int n, int k) {
+ if (n < k) throw new RuntimeException("Incorrect input data.");
+ recurse(n, 1, k, new ArrayList<>());
+ return result;
+}
+
+public void recurse(int n, int cur, int k, List list) {
+ if (list.size() == k) {
+ result.add(list);
+ return;
+ }
+ list.add(cur);
+ if (cur <= n) recurse(n, cur + 1, k, new ArrayList<>(list));
+ if (n - cur + list.size() - 1 >= k) {
+ // 回溯
+ list.remove(list.size() - 1);
+ recurse(n, cur + 1, k, new ArrayList<>(list));
+ }
+}
+```
+
+
+### 全排列
+```java
+List> result = new ArrayList<>();
+public List> permute(int[] nums) {
+ recurse(nums, 0);
+ return result;
+}
+
+public void recurse(int[] nums, int index) {
+ if (index == nums.length - 1) {
+ List list = new ArrayList<>();
+ for (Integer num : nums) {
+ list.add(num);
+ }
+ result.add(list);
+ return;
+ }
+ for (int i = index; i< nums.length; i++) {
+ swap(nums, i, index);
+ recurse(nums, index + 1);
+ swap(nums, i, index);
+ }
+}
+
+private void swap(int[] nums, int i, int j) {
+ // 交换数值
+ int temp = nums[i];
+ nums[i] = nums[j];
+ nums[j] = temp;
+}
+```
\ No newline at end of file
diff --git a/Week04/NOTE.md b/Week04/NOTE.md
index 50de30414..6f18f9afe 100644
--- a/Week04/NOTE.md
+++ b/Week04/NOTE.md
@@ -1 +1,9 @@
-学习笔记
\ No newline at end of file
+#### 二分查找一个半有序数组中间无序的地方
+```java
+/**
+ * 思路:
+ * mid = (left + right) / 2;
+ * mid < nums[right], right = mid;
+ * mid > nums[left], left = mid
+ */
+```
\ No newline at end of file
diff --git a/Week04/homework.md b/Week04/homework.md
new file mode 100644
index 000000000..90dbe79ae
--- /dev/null
+++ b/Week04/homework.md
@@ -0,0 +1,359 @@
+### 二叉树的层序遍历
+```java
+/**
+ * bfs (广度优先)
+ */
+public List> levelOrder(TreeNode root) {
+ List> result = new ArrayList<>();
+ if (root == null) return result;
+ // 存每层元素
+ Deque deque = new LinkedList<>();
+ deque.offer(root);
+ while (!deque.isEmpty()) {
+ int size = deque.size();
+ List levelList = new ArrayList<>();
+ result.add(levelList);
+ // 处理树的当前层元素
+ for (int i = 0; i < size; i++) {
+ TreeNode node = deque.pop();
+ levelList.add(node.val);
+ // 向队列中添加下一层的元素
+ if (node.left != null) deque.offer(node.left);
+ if (node.right != null) deque.offer(node.right);
+ }
+ }
+ return result;
+}
+
+/**
+ * dfs (深度优先)
+ */
+List> result = new ArrayList<>();
+public List> levelOrder(TreeNode root) {
+ dfs(root, 0);
+ return result;
+}
+private void dfs(TreeNode node, int level) {
+ if (node == null) return;
+ if (level == result.size()) {
+ // 当前深度的元素还未存过元素,添加新集合
+ List list = new ArrayList<>();
+ list.add(node.val);
+ result.add(list);
+ } else {
+ result.get(level).add(node.val);
+ }
+ // 左右子节点继续下探
+ dfs(node.left, level + 1);
+ dfs(node.right, level + 1);
+}
+```
+
+
+### 在每个树行中找最大值
+```java
+/**
+ * bfs (广度优先)
+ */
+public List largestValues(TreeNode root) {
+ List result = new ArrayList<>();
+ if (root == null) return result;
+ Deque deque = new LinkedList<>();
+ deque.offer(root);
+ while (!deque.isEmpty()) {
+ int size = deque.size(), levelMax = Integer.MIN_VALUE;
+ for (int i = 0; i < size; i++) {
+ TreeNode node = deque.pop();
+ if (levelMax < node.val) levelMax = node.val;
+ // 向队列中添加下一层元素
+ if (node.left != null) deque.offer(node.left);
+ if (node.right != null) deque.offer(node.right);
+ }
+ result.add(levelMax);
+ }
+ return result;
+}
+
+/**
+ * dfs (深度优先)
+ */
+List result = new ArrayList<>();
+public List largestValues(TreeNode root) {
+ dfs(root, 0);
+ return result;
+}
+private void dfs(TreeNode node, int level) {
+ if (node == null) return;
+ if (level == result.size()) {
+ result.add(node.val);
+ } else if (result.get(level) < node.val) {
+ result.set(level, node.val);
+ }
+ // 左右子节点下探
+ dfs(node.left, level + 1);
+ dfs(node.right, level + 1);
+}
+```
+
+
+### 柠檬水找零
+```java
+public boolean lemonadeChange(int[] bills) {
+ /*
+ * 排队找零
+ * 1. 5美元, five++
+ * 2. 10美元, ten++, five--
+ * 3. 20美元, (ten-- and five--) or (five - 3)
+ */
+ int five = 0, ten = 0;
+ for (int bill : bills) {
+ if (bill == 5) {
+ five++;
+ } else if (bill == 10) {
+ ten++;
+ five--;
+ } else if (ten > 0) {
+ ten--;
+ five--;
+ } else {
+ five -=3;
+ }
+ if (five < 0 || ten < 0) return false;
+ }
+ return true;
+}
+```
+
+
+### 买卖股票的最佳时机 II
+```java
+public int maxProfit(int[] prices) {
+ /*
+ * 当天股票价格如果小于后一天的股票价格
+ * 则买入当天并在后一天卖出
+ */
+ if (prices == null || prices.length < 2) return 0;
+ int count = 0;
+ for (int i = 1; i < prices.length; i++) {
+ int temp = prices[i] - prices[i - 1];
+ if (temp > 0) count += temp;
+ }
+ return count;
+}
+```
+
+
+### 分发饼干
+```java
+public int findContentChildren(int[] g, int[] s) {
+ /*
+ * 对g,s进行排序
+ * 饼干尺寸大的满足胃口大的孩子, 分发饼干数量就是满足数量
+ */
+ if (g == null || s == null) return 0;
+ Arrays.sort(g);
+ Arrays.sort(s);
+ int gIndex = g.length - 1, sIndex = s.length - 1;
+ while (gIndex >= 0 && sIndex >= 0) {
+ if (g[gIndex--] <= s[sIndex]) {
+ sIndex--;
+ }
+ }
+ return s.length - sIndex - 1;
+}
+```
+
+
+### 模拟行走机器人
+```java
+public int robotSim(int[] commands, int[][] obstacles) {
+ /*
+ * 1. 机器人出发方向(转向指令确定机器人行走方向):
+ * y轴正方向(0, 1), x轴正方向(1,0),y轴负方向(0,-1),x轴负方向(-1,0)
+ * 2. 机器人从(0, 0)向y轴正方向(北方)出发
+ * 3. 判断当前移动路线(一格一格移动判断)中是否存在障碍物
+ * 存在: 停在障碍物前一格, 继续下一条命令
+ * 不存在: 在x轴或y轴上行走
+ * 4. 当前移动命令结束后计算距离, 与之前的最大距离值作比较
+ */
+ // 存障碍点
+ Set set = new HashSet<>();
+ for (int[] obs : obstacles) {
+ // 计算原理? 位运算学习后需要明白, 存String类型耗时是位运算的5倍左右
+ long ox = (long) obs[0] + 30000;
+ long oy = (long) obs[1] + 30000;
+ set.add((ox << 16) + oy);
+ }
+ // 方向对应的x、y轴的前进值
+ int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1 ,0}};
+ int d = 0, x = 0, y = 0, result = 0;
+ for (int cmd : commands) {
+ if (cmd == -1) {
+ d = (d + 1) % 4;
+ } else if (cmd == -2) {
+ d = (d + 3) % 4;
+ } else {
+ while (cmd-- > 0 && !set.contains((((long) x + dirs[d][0] + 30000) << 16) + ((long) y + dirs[d][1] + 30000))) {
+ x += dirs[d][0];
+ y += dirs[d][1];
+ }
+ result = Math.max(result, x * x + y * y);
+ }
+ }
+ return result;
+}
+```
+
+
+### 跳跃游戏
+```java
+public boolean canJump(int[] nums) {
+ /*
+ * 从后往前计算
+ * 需要到达位置下标targetIndex, targetIndex - curIndex <= nums[curIndex]时
+ * targetIndex = curIndex, curIndex--, 否则 curIndex--
+ * targetIndex == 0时, 可以到达
+ */
+ int targetIndex = nums.length - 1, curIndex = nums.length - 2;
+ while (curIndex >= 0) {
+ if (targetIndex - curIndex <= nums[curIndex])
+ targetIndex = curIndex;
+ curIndex--;
+ }
+ return targetIndex == 0;
+}
+```
+
+
+### x 的平方根
+```java
+public int mySqrt(int x) {
+ // 二分法 x的平方根整数部分 < x / 2 (1除外)
+ long left = 0, right = x / 2 + 1;
+ while (left < right) {
+ // 取右中位数,防止死循环,例: x = 9, left = 3, right = 4时死循环
+ long mid = (left + right + 1) / 2;
+ long square = mid * mid;
+ if (square > x) {
+ right = mid - 1;
+ } else {
+ left = mid;
+ }
+ }
+ return (int)left;
+}
+
+public int mySqrt(int x) {
+ // 牛顿迭代法
+ long curNum = x;
+ while (curNum * curNum > x) {
+ curNum = (curNum + x / curNum) / 2;
+ }
+ return (int)curNum;
+}
+
+public int mySqrt(int x) {
+ // 数学公式换底计算
+ if ( x == 0) return 0;
+ int ans = (int) Math.exp(0.5 * Math.log(x));
+ return (long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans;
+}
+```
+
+
+### 有效的完全平方数
+```java
+public boolean isPerfectSquare(int num) {
+ /*
+ * num < 2, 返回true
+ * 左边界left = 2, 右边界 right = num / 2
+ * left <= right时
+ * mid = (left + right) >> 1, square = mid * mid, square与square比较
+ * square == num返回true
+ * square > num, right = mid - 1
+ * square < num, left = mid + 1
+ */
+ if (num < 2) return true;
+ long left = 2, right = num >> 1;
+ while (left <= right) {
+ long mid = (left + right) >> 1;
+ long square = mid * mid;
+ if (square == num) {
+ return true;
+ }else if (square > num) {
+ right = mid - 1;
+ } else {
+ left = mid + 1;
+ }
+ }
+ return false;
+}
+
+public boolean isPerfectSquare(int num) {
+ // 牛顿迭代公式
+ long curNum = num;
+ while (curNum * curNum > num) {
+ curNum = (curNum + num / curNum) / 2;
+ }
+ return curNum * curNum == num;
+}
+
+public boolean isPerfectSquare(int num) {
+ // 数学公式换底
+ int ans = (int)Math.exp(0.5 * Math.log(num));
+ ans = (long)(ans + 1) * (ans + 1) <= num ? ans + 1 : ans;
+ return ans * ans == num;
+}
+```
+
+
+### 搜索旋转排序数组
+```java
+public int search(int[] nums, int target) {
+ int left = 0, right = nums.length - 1, index = 0;
+ while (left <= right) {
+ index = (left + right + 1) / 2;
+ int curNum = nums[index];
+ if (curNum == target) {
+ return index;
+ } else if (curNum > nums[left]) {
+ // index左边数据有序
+ if (target >= nums[left] && target < curNum) {
+ right = index - 1;
+ } else {
+ left = index + 1;
+ }
+ } else {
+ // index 右边数据有序
+ if (target <= nums[right] && target > curNum) {
+ left = index + 1;
+ } else {
+ right = index - 1;
+ }
+ }
+ }
+ return -1;
+}
+```
+
+
+### 搜索二维矩阵
+```java
+public boolean searchMatrix(int[][] matrix, int target) {
+ if ( matrix.length == 0) return false;
+ int col = matrix.length, row = matrix[0].length;
+ int left = 0, right = col * row - 1;
+ while (left <= right) {
+ int mid = (left + right) / 2;
+ if (matrix[mid / row][mid % row] == target) {
+ return true;
+ } else if (matrix[mid / row][mid % row] < target) {
+ left = mid + 1;
+ } else {
+ right = mid - 1;
+ }
+ }
+ return false;
+}
+```
+
\ No newline at end of file
diff --git a/Week06/NOTE.md b/Week06/NOTE.md
index 50de30414..6684dc9cd 100644
--- a/Week06/NOTE.md
+++ b/Week06/NOTE.md
@@ -1 +1,12 @@
-学习笔记
\ No newline at end of file
+### 动态规划
+```
+定义
+ 分治 + 最优子结构
+解题步骤
+ 1. 重复问题 (数学归纳法思想)
+ 2. 定义状态数组
+ 3. dp方程
+动态规划 与 递归或分治没有本质的区别(关键看有无最优子结构)
+ 共性:找重复子问题
+ 差异:dp--最优子结构, 中途介意淘汰次优解(dp大多数时候复杂度更低)
+```
\ No newline at end of file
diff --git a/Week06/homework.md b/Week06/homework.md
new file mode 100644
index 000000000..59642dc53
--- /dev/null
+++ b/Week06/homework.md
@@ -0,0 +1,479 @@
+#### 地下城游戏
+```java
+public int calculateMinimumHP(int[][] dungeon) {
+ int row = dungeon.length;
+ if (row == 0) return 0;
+ int col = dungeon[0].length;
+ if (col == 0) return 0;
+ int[][] dp = new int[row + 1][col + 1];
+ // 赋值 O(M * N)
+ for (int i = 0; i < row + 1; i++) {
+ Arrays.fill(dp[i], Integer.MAX_VALUE);
+ }
+ dp[row][col - 1] = 1;
+ dp[row - 1][col] = 1;
+ // O(M * N)
+ for (int i = row - 1; i >= 0; i--) {
+ for (int j = col - 1; j >= 0; j--) {
+ // dp方程 f(i,j) = max(min(f(i,j+1), f(i+1,j)-dungeon(i,j)), 1);
+ int min = Math.min(dp[i][j + 1], dp[i + 1][j]);
+ dp[i][j] = Math.max(min - dungeon[i][j], 1);
+ }
+ }
+ return dp[0][0];
+}
+```
+
+
+
+#### 不同路径
+```java
+public int uniquePaths(int m, int n) {
+ /*
+ * 分析: 从start到当前网格的走法是只有两种(从上或者从左到达当前网格)
+ * 重复性: 到达当前网格的走法 = 上一个网格的走法 + 左一格网格的走法
+ * 状态数组: int[][] dp = new int[m][n];
+ * DP 方程: f(i, j) = f(i - 1, j) + f(i, j - 1);
+ */
+ int[][] dp = new int[m][n];
+ // 第一排和第一列只有一种走法能到达,全设为1
+ for (int i = 1; i < m; i++) {
+ dp[i][0] = 1;
+ }
+ Arrays.fill(dp[0], 1);
+ // 时间:O(M * N) 空间: O(M * N)
+ for (int i = 1; i < m; i++) {
+ for (int j = 1; j < n; j++) {
+ dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
+ }
+ }
+ return dp[m - 1][n - 1];
+}
+```
+
+
+
+#### 不同路径 II
+```java
+public int uniquePathsWithObstacles(int[][] obstacleGrid) {
+ int row = obstacleGrid.length, col = obstacleGrid[0].length;
+ // 状态数组
+ int[][] dp = new int[row][col];
+ // dp 第一行赋值,遇到障碍后面位置不用赋值 O(M)
+ for (int i = 0; i < col; i++) {
+ if (obstacleGrid[0][i] == 1) break;
+ dp[0][i] = 1;
+ }
+ // dp 第一列赋值,遇到障碍后面的位置不用赋值 O(N)
+ for (int i = 0; i < row; i++) {
+ if (obstacleGrid[i][0] == 1) break;
+ dp[i][0] = 1;
+ }
+ /*
+ * 重复问题:
+ * 当前位置有障碍物: dp[i][j] = 0
+ * 当前位置无障碍物: dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
+ * 时间 O(M * N), 空间 O(M * N)
+ */
+ for (int i = 1; i < row; i++) {
+ for (int j = 1; j < col; j++) {
+ /*
+ * dp 方程:
+ * f(i, j) = 0 obstacleGrid[i][j] = 1;
+ * f(i, j) = f(i - 1, j) + f(i, j - 1) other
+ */
+ if (obstacleGrid[i][j] == 0)
+ dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
+ }
+ }
+ return dp[row - 1][col - 1];
+}
+```
+
+
+
+#### 最长公共子序列
+```java
+public int longestCommonSubsequence(String text1, String text2) {
+ /*
+ * 重复问题:
+ * 当前位置两字符串的最大重复子序列长度
+ * 1. 上一个位置和左一个位置的最大长度 text1.charAt(i) != text2.charAt(j)
+ * 2. 左上角位置长度值 + 1 text1.charAt(i) == text2.charAt(j)
+ */
+ int row = text1.length(), col = text2.length();
+ // 状态数组
+ int[][] dp = new int[row + 1][col + 1];
+ char[] rowChar = text1.toCharArray();
+ char[] colChar = text2.toCharArray();
+ // 时间: O(M * N), 空间: O(M * N)
+ for (int i = 1; i < row + 1; i++) {
+ for (int j = 1; j < col + 1; j++) {
+ /*
+ * dp 方程
+ * f(i, j) = f(i - 1, j - 1) + 1 rowChar[i - 1] == colChar[j - 1]
+ * f(i, j) = max(f(i - 1, j), f(i - 1, j)) rowChar[i - 1] != colChar[j - 1]
+ */
+ if (rowChar[i - 1] == colChar[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1] + 1;
+ } else {
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+ }
+ }
+ }
+ return dp[row][col];
+}
+```
+
+
+
+#### 三角形最小路径和
+```java
+public int minimumTotal(List> triangle) {
+ /*
+ * 重复问题
+ * 当前位置值等于下一层i位置值和i+1位置值得最小值加当前位置值
+ * 状态数组
+ * int[] dp = new int[triangle.get(triangle.size() - 1).size()]
+ * dp 方程
+ * f(i, j) = min(f(i + 1, j), f(i + 1, j + 1)) + triangle(i, j)
+ * time:O(n) space:O(n)
+ */
+ int[] dp = new int[triangle.get(triangle.size() - 1).size() + 1];
+ for (int i = triangle.size() - 1; i >= 0; i--) {
+ List current = triangle.get(i);
+ for (int j = 0; j < current.size(); j++) {
+ dp[j] = Math.min(dp[j], dp[j + 1]) + current.get(j);
+ }
+ }
+ return dp[0];
+}
+```
+
+
+
+#### 最大子序和
+```java
+public int maxSubArray(int[] nums) {
+ /*
+ * 重复问题:
+ * 当前位置之前连续子数组和sum大于0时,sum += nums[i];
+ * 否则,sum = nums[i];
+ * 最后sum跟ans比较,取最大值
+ * 状态数组
+ * 简化为变量
+ * dp 方程
+ * f(i) = max(ans, f(i - 1) + nums[i]) f(i - 1) > 0
+ * f(i) = max(ans, nums[i]) f(i - 1) <= 0
+ */
+ int sum = 0, ans = nums[0];
+ for (int i = 0; i < nums.length; i++) {
+ if (sum > 0) {
+ sum += nums[i];
+ } else {
+ sum = nums[i];
+ }
+ ans = Math.max(ans, sum);
+ }
+ return ans;
+}
+```
+
+
+
+#### 乘积最大子数组
+```java
+public int maxProduct(int[] nums) {
+ /*
+ * ans 当前位置最大连续子数组乘积
+ * maxPre 当前位置前一个位置的最大连续子数组乘积
+ * minPre 当前位置前一个位置的最大连续子数组乘积的绝对值,当前位置为0时重新计算
+ *
+ * 重复问题
+ * crrentMax = max(maxPre * current, minPre * current, current)
+ * ans = max(ans, currentMax)
+ * 状态数组 变量
+ * dp 方程
+ * swap(maxPre, minPre) nums[i] < 0
+ * f(i) = max(maxPre * nums[i], nums[i])
+ */
+ int ans = nums[0], maxPre = nums[0], minPre = nums[0];
+ for (int i = 1; i < nums.length; i++) {
+ if (nums[i] < 0) {
+ int temp = maxPre;
+ maxPre = minPre;
+ minPre = temp;
+ }
+ maxPre = Math.max(maxPre * nums[i], nums[i]);
+ minPre = Math.min(minPre * nums[i], nums[i]);
+ ans = Math.max(ans, maxPre);
+ }
+ return ans;
+}
+```
+
+
+
+#### 零钱兑换
+```java
+public int coinChange(int[] coins, int amount) {
+ // 重复问题:当前金额, 循环coins数组, 获取当前金额所需最小硬币个数
+ int max = amount + 1;
+ // 状态数组
+ int[] dp = new int[max];
+ Arrays.fill(dp, max);
+ dp[0] = 0;
+ // time: O(S * N) space: O(S)
+ for (int i = 1; i < max; i++) {
+ for (int j = 0; j < coins.length; j++) {
+ // dp方程: f(i) = min(f(i), f(i - coins[j]) + 1)
+ if (coins[j] <= i) {
+ dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
+ }
+ }
+ }
+ return dp[amount] > amount ? -1 : dp[amount];
+}
+```
+
+
+
+#### 打家劫舍
+```java
+public int rob(int[] nums) {
+ /*
+ * 重复问题:
+ * 偷 i 房子的金额最大值取一下情况的最大值
+ * 偷 i - 2房子金额加 i 房子的金额
+ * 偷 i - 1房子金额且不偷i房子金额
+ */
+ // 状态数组
+ int[] dp = new int[nums.length + 2];
+ // time: O(n) space: O(n)
+ for (int i = 0; i < nums.length; i++) {
+ // dp方程 f(i) = max(f(i - 1), f(i - 2) + nums[i])
+ dp[i + 2] = Math.max(dp[i] + nums[i], dp[i + 1]);
+ }
+ return dp[nums.length + 1];
+}
+```
+
+
+
+#### 打家劫舍 II
+```java
+public int rob(int[] nums) {
+ /*
+ * 重复问题:
+ * 分两种情况
+ * 偷nums[0], 不偷nums[nums.length - 1]
+ * 偷nums[nums.length - 1], 不偷nums[0]
+ * 偷 i 房子的金额最大值取一下情况的最大值
+ * 偷 i - 2房子金额加 i 房子的金额
+ * 偷 i - 1房子金额且不偷i房子金额
+ */
+ if (nums == null || nums.length == 0) return 0;
+ if (nums.length == 1) return nums[0];
+ // 状态数组
+ int[] dp = new int[nums.length + 2];
+ // time: O(n) space: O(n)
+ for (int i = 1; i < nums.length; i++) {
+ // dp方程 f(i) = max(f(i - 1), f(i - 2) + nums[i])
+ dp[i + 2] = Math.max(dp[i] + nums[i], dp[i + 1]);
+ }
+ for (int i = 0; i < nums.length - 1; i++) {
+ dp[i + 2] = Math.max(dp[i] + nums[i], dp[i + 1]);
+ }
+ return Math.max(dp[nums.length], dp[nums.length + 1]);
+}
+```
+
+
+
+#### 卖股票的最佳时机
+```java
+public int maxProfit(int[] prices) {
+ /*
+ * 重复问题:
+ * 第i天前买入股票, 计算第i天的股票价格卖出, 获利最大值
+ * 与第i - 1天的获利最大值比较, 保存最大值
+ */
+ if (prices == null || prices.length == 0) return 0;
+ // 状态数组
+ int[] dp = new int[prices.length];
+ int lowwer = prices[0];
+ // time: O(n) space: O(n)
+ for (int i = 1; i < prices.length; i++) {
+ // f(i) = max(prices[i] - lowwer, f(i - 1))
+ dp[i] = Math.max(prices[i] - lowwer, dp[i - 1]);
+ if (prices[i] < lowwer) {
+ lowwer = prices[i];
+ }
+ }
+ return dp[prices.length - 1];
+}
+
+/**
+ * 状态数组优化
+ */
+public int maxProfit(int[] prices) {
+ /*
+ * 重复问题:
+ * 第i天前买入股票, 计算第i天的股票价格卖出, 获利最大值
+ * 与第i - 1天的获利最大值比较, 保存最大值
+ */
+ if (prices == null || prices.length == 0) return 0;
+ int max = 0, lowwer = prices[0];
+ // time: O(n) space: O(1)
+ for (int i = 1; i < prices.length; i++) {
+ // f(i) = max(prices[i] - lowwer, f(i - 1))
+ max = Math.max(prices[i] - lowwer, max);
+ if (prices[i] < lowwer) {
+ lowwer = prices[i];
+ }
+ }
+ return max;
+}
+```
+
+
+
+#### 最小路径和
+```java
+public int minPathSum(int[][] grid) {
+ /*
+ * 重复问题
+ * 当前位置到右下角的最小路径 = min(下一格路径, 右一格路径) + grid[i][j]
+ */
+ int row = grid.length;
+ if (row == 0) return 0;
+ int col = grid[0].length;
+ // 状态数组
+ int[][] dp = new int[row + 1][col + 1];
+ for (int i = 0; i < row; i++) {
+ dp[i][col] = Integer.MAX_VALUE;
+ }
+ Arrays.fill(dp[row], Integer.MAX_VALUE);
+ dp[row - 1][col] = 0;
+ dp[row][col - 1] = 0;
+ // time: O(M * N) space: O(M * N)
+ for (int i = row - 1; i >= 0; i--) {
+ for (int j = col - 1; j >= 0; j--) {
+ // dp方程: f(i, j) = min(f(i, j + 1), f(i + 1, j)) + grid[i][j]
+ dp[i][j] = Math.min(dp[i][j + 1], dp[i + 1][j]) + grid[i][j];
+ }
+ }
+ return dp[0][0];
+}
+```
+
+
+
+#### 解码方法
+```java
+public int numDecodings(String s) {
+ /*
+ * 重复问题:
+ * 当前位置解码种数 = 前一位之前的解码种数 + 前两位之前的解码种数
+ */
+ int len = s.length();
+ if (len == 0) return 0;
+ // 状态数组
+ int[] dp = new int[len];
+ char[] charArr = s.toCharArray();
+ if (charArr[0] == '0') return 0;
+ dp[0] = 1;
+ // time: O(n) space: O(n)
+ for (int i = 1; i < len; i++) {
+ /*
+ * dp方程
+ * f(i) = 0 s(i) == 0
+ * f(i) = f(i - 1) + f(i - 2) 10 < s(i - 1, i) < 27 && s(i) != '0'
+ * f(i) = f(i - 1) !(10 < s(i - 1, i) < 27) && s(i) != '0'
+ */
+ if (charArr[i] != '0') dp[i] = dp[i - 1];
+ int num = 10 * (charArr[i - 1] - '0') + charArr[i] - '0';
+ if (num > 9 && num < 27) {
+ if (i == 1) dp[i]++;
+ else dp[i] += dp[i - 2];
+ }
+ }
+ return dp[len - 1];
+}
+```
+
+
+
+#### 最大正方形
+```java
+public int maximalSquare(char[][] matrix) {
+ int row = matrix.length;
+ if (row == 0) return 0;
+ int col = matrix[0].length, maxSide = 0;
+ // 状态数组
+ int[][] dp = new int[row + 1][col + 1];
+ // time: o(M * N) space: O(M * N)
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ if (matrix[i][j] == '1') {
+ /*
+ * dp方程:
+ * f(i, j) = min(min(f(i - 1, j), f(i, j - 1)), f(i - 1, j - 1)) + 1 matrix[i][j] == '1'
+ * f(i, j) = 0 others
+ */
+ dp[i + 1][j + 1] = Math.min(Math.min(dp[i + 1][j], dp[i][j + 1]), dp[i][j]) + 1;
+ maxSide = Math.max(maxSide, dp[i + 1][j + 1]);
+ }
+ }
+ }
+ return maxSide * maxSide;
+}
+```
+
+
+
+#### 回文子串
+```java
+public int countSubstrings(String s) {
+ if (s == null || s.length() == 0) return 0;
+ int len = s.length();
+ char[] charArr = s.toCharArray();
+ // 状态数组
+ boolean[][] dp = new boolean[len][len];
+ int result = len;
+ for (int i = 0; i < len; i++) {
+ dp[i][i] = true;
+ }
+ // time: O(n ^ 2) space: O(n ^ 2)
+ for (int i = len - 1; i >= 0; i--) {
+ for (int j = i + 1; j < len; j++) {
+ // dp方程: f(i, j) = (j - i == 1) ? true : f(i + 1, j - 1)
+ if (charArr[i] == charArr[j]) {
+ dp[i][j] = j - i == 1 ? true : dp[i + 1][j - 1];
+ if (dp[i][j]) result++;
+ }
+ }
+ }
+ return result;
+}
+```
+
+
+
+#### 任务调度器
+```java
+public int leastInterval(char[] tasks, int n) {
+ int[] nums = new int[26];
+ for (int i = 0; i < tasks.length; i++) {
+ nums[tasks[i] - 'A']++;
+ }
+ Arrays.sort(nums);
+ int maxVal = nums[25] - 1, idelSlot = maxVal * n;
+ for (int i = 24; i >= 0 && nums[i] > 0; i--) {
+ idelSlot -= Math.min(nums[i], maxVal);
+ }
+ return idelSlot > 0 ? idelSlot + tasks.length : tasks.length;
+}
+```
+
+
\ No newline at end of file
diff --git a/Week07/NOTE.md b/Week07/NOTE.md
deleted file mode 100644
index 50de30414..000000000
--- a/Week07/NOTE.md
+++ /dev/null
@@ -1 +0,0 @@
-学习笔记
\ No newline at end of file
diff --git a/Week07/week07-homework.md b/Week07/week07-homework.md
new file mode 100644
index 000000000..69b6f60b0
--- /dev/null
+++ b/Week07/week07-homework.md
@@ -0,0 +1,155 @@
+### 实现 Trie (前缀树)
+```java
+class Trie {
+ private boolean isEnd;
+ private Trie[] next;
+ /** Initialize your data structure here. */
+ public Trie() {
+ isEnd = false;
+ next = new Trie[26];
+ }
+
+ /** Inserts a word into the trie. */
+ public void insert(String word) {
+ if (word == null || word.length() == 0) return;
+ Trie cur = this;
+ char[] words = word.toCharArray();
+ for (int i = 0; i < words.length; i++) {
+ int n = words[i] - 'a';
+ if (cur.next[n] == null) cur.next[n] = new Trie();
+ cur = cur.next[n];
+ }
+ cur.isEnd = true;
+ }
+
+ /** Returns if the word is in the trie. */
+ public boolean search(String word) {
+ Trie node = searchPrefix(word);
+ return node != null && node.isEnd;
+ }
+
+ /** Returns if there is any word in the trie that starts with the given prefix. */
+ public boolean startsWith(String prefix) {
+ Trie node = searchPrefix(prefix);
+ return node != null;
+ }
+
+ private Trie searchPrefix(String word) {
+ if (word == null) return null;
+ Trie cur = this;
+ char[] words = word.toCharArray();
+ for (int i = 0; i < words.length; i++) {
+ cur = cur.next[words[i] - 'a'];
+ if (cur == null) return null;
+ }
+ return cur;
+ }
+}
+```
+
+
+### 朋友圈
+```java
+class Solution {
+ private int find(int[] parent, int p) {
+ while (p != parent[p]) {
+ parent[p] = parent[parent[p]];
+ p = parent[p];
+ }
+ return p;
+ }
+
+ private void union(int[] parent, int p, int q) {
+ int rootP = find(parent, p);
+ int rootQ = find(parent, q);
+ if (rootP == rootQ) return;
+ parent[rootP] = rootQ;
+ }
+
+ public int findCircleNum(int[][] M) {
+ int[] parent = new int[M.length];
+ for (int i = 0; i < M.length; i++) {
+ parent[i] = i;
+ }
+ for (int i = 0; i < M.length - 1; i++) {
+ for (int j = i + 1; j < M.length; j++) {
+ if (M[i][j] == 1) union(parent, i, j);
+ }
+ }
+ int count = 0;
+ for (int i = 0; i < parent.length; i++) {
+ if (parent[i] == i) count++;
+ }
+ return count;
+ }
+}
+```
+
+
+### 岛屿数量
+```java
+class Solution {
+ public int numIslands(char[][] grid) {
+ if (grid == null || grid[0].length == 0) return 0;
+ int row = grid.length, col = grid[0].length, count = 0;
+ // O(M * N)
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ if (grid[i][j] == '1') {
+ count++;
+ dfs(grid, i, j);
+ }
+ }
+ }
+ return count;
+ }
+
+ private void dfs(char[][] grid, int i, int j) {
+ if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return;
+ grid[i][j] = '0';
+ int[][] dirs = {{0, 1}, {1,0}, {-1, 0}, {0, -1}};
+ for (int[] dir : dirs) {
+ dfs(grid, i + dir[0], j + dir[1]);
+ }
+ }
+}
+```
+
+
+### 被围绕的区域
+```java
+class Solution {
+ public void solve(char[][] board) {
+ if (board == null || board.length == 0) return;
+ int row = board.length, col = board[0].length;
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ boolean isBoard = i == 0 || j == 0 || i == row - 1 || j == col - 1;
+ if (isBoard && board[i][j] == 'O') {
+ dfs(board, i, j);
+ }
+ }
+ }
+ for (int i = 0; i < row; i++) {
+ for (int j = 0; j < col; j++) {
+ if (board[i][j] == 'O') {
+ board[i][j] = 'X';
+ }
+ if (board[i][j] == '#') {
+ board[i][j] = 'O';
+ }
+ }
+ }
+ }
+
+ private void dfs(char[][] board, int i, int j) {
+ if (i < 0 || j < 0 || i >= board.length || j >= board[0].length || board[i][j] == 'X' || board[i][j] == '#') return;
+ board[i][j] = '#';
+ int[][] dirs = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
+ for (int[] dir : dirs) {
+ dfs(board, i + dir[0], j + dir[1]);
+ }
+ }
+}
+```
+
diff --git a/Week07/week07-note.md b/Week07/week07-note.md
new file mode 100644
index 000000000..18b58020c
--- /dev/null
+++ b/Week07/week07-note.md
@@ -0,0 +1,228 @@
+### 并查集
+#### 模板
+```java
+/**
+ * 1.find: 确定元素子集
+ * 2.union: 合并两个子集
+ * 3.makeSet: 简历单元素集合
+ * 路径压缩优化: 认老大, 减少深度
+ */
+class unionFind {
+ private int count = 0;
+ private int[] parent;
+ public unionFind(int n) {
+ count = n;
+ parent = new int[n];
+ for (int i = 0; i < n; i++) {
+ parent[i] = i;
+ }
+ }
+
+ public int find(int p) {
+ while (p != parent[p]) {
+ // 路径压缩
+ parent[p] = parent[parent[p]];
+ p = parent[p];
+ }
+ return p;
+ }
+
+ public void union(int p, int q) {
+ int rootP = find(p);
+ int rootQ = find(q);
+ if (rootP == rootQ) return;
+ parent[rootP] = rootQ;
+ count--;
+ }
+}
+```
+
+
+### A*
+#### 模板
+```java
+/**
+ * BFS + 优先级
+ */
+public class AStar {
+ public final static int BAR = 1; // 障碍值
+ public final static int PATH = 2; // 路径
+ public final static int DIRECT_VALUE = 10; // 横竖移动代价
+ public final static int OBLIQUE_VALUE = 14; // 斜移动代价
+
+ Queue openList = new PriorityQueue(); // 优先队列(升序)
+ List closeList = new ArrayList();
+
+ /**
+ * 开始算法
+ */
+ public void start(MapInfo mapInfo) {
+ if(mapInfo==null) return;
+ // clean
+ openList.clear();
+ closeList.clear();
+ // 开始搜索
+ openList.add(mapInfo.start);
+ moveNodes(mapInfo);
+ }
+
+
+ /**
+ * 移动当前结点
+ */
+ private void moveNodes(MapInfo mapInfo) {
+ while (!openList.isEmpty()) {
+ Node current = openList.poll();
+ closeList.add(current);
+ addNeighborNodeInOpen(mapInfo,current);
+ if (isCoordInClose(mapInfo.end.coord)) {
+ drawPath(mapInfo.maps, mapInfo.end);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 在二维数组中绘制路径
+ */
+ private void drawPath(int[][] maps, Node end) {
+ if(end==null||maps==null) return;
+ System.out.println("总代价:" + end.G);
+ while (end != null) {
+ Coord c = end.coord;
+ maps[c.y][c.x] = PATH;
+ end = end.parent;
+ }
+ }
+
+
+ /**
+ * 添加所有邻结点到open表
+ */
+ private void addNeighborNodeInOpen(MapInfo mapInfo,Node current) {
+ int x = current.coord.x;
+ int y = current.coord.y;
+ // 左
+ addNeighborNodeInOpen(mapInfo,current, x - 1, y, DIRECT_VALUE);
+ // 上
+ addNeighborNodeInOpen(mapInfo,current, x, y - 1, DIRECT_VALUE);
+ // 右
+ addNeighborNodeInOpen(mapInfo,current, x + 1, y, DIRECT_VALUE);
+ // 下
+ addNeighborNodeInOpen(mapInfo,current, x, y + 1, DIRECT_VALUE);
+ // 左上
+ addNeighborNodeInOpen(mapInfo,current, x - 1, y - 1, OBLIQUE_VALUE);
+ // 右上
+ addNeighborNodeInOpen(mapInfo,current, x + 1, y - 1, OBLIQUE_VALUE);
+ // 右下
+ addNeighborNodeInOpen(mapInfo,current, x + 1, y + 1, OBLIQUE_VALUE);
+ // 左下
+ addNeighborNodeInOpen(mapInfo,current, x - 1, y + 1, OBLIQUE_VALUE);
+ }
+
+
+ /**
+ * 添加一个邻结点到open表
+ */
+ private void addNeighborNodeInOpen(MapInfo mapInfo,Node current, int x, int y, int value) {
+ if (canAddNodeToOpen(mapInfo,x, y)) {
+ Node end=mapInfo.end;
+ Coord coord = new Coord(x, y);
+ int G = current.G + value; // 计算邻结点的G值
+ Node child = findNodeInOpen(coord);
+ if (child == null) {
+ int H=calcH(end.coord,coord); // 计算H值
+ if(isEndNode(end.coord,coord)) {
+ child=end;
+ child.parent=current;
+ child.G=G;
+ child.H=H;
+ } else {
+ child = new Node(coord, current, G, H);
+ }
+ openList.add(child);
+ } else if (child.G > G) {
+ child.G = G;
+ child.parent = current;
+ openList.add(child);
+ }
+ }
+ }
+
+
+ /**
+ * 从Open列表中查找结点
+ */
+ private Node findNodeInOpen(Coord coord) {
+ if (coord == null || openList.isEmpty()) return null;
+ for (Node node : openList) {
+ if (node.coord.equals(coord)) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 计算H的估值:“曼哈顿”法,坐标分别取差值相加
+ */
+ private int calcH(Coord end,Coord coord) {
+ return Math.abs(end.x - coord.x)
+ + Math.abs(end.y - coord.y);
+ }
+
+ /**
+ * 判断结点是否是最终结点
+ */
+ private boolean isEndNode(Coord end,Coord coord) {
+ return coord != null && end.equals(coord);
+ }
+
+
+ /**
+ * 判断结点能否放入Open列表
+ */
+ private boolean canAddNodeToOpen(MapInfo mapInfo,int x, int y) {
+ // 是否在地图中
+ if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.hight) return false;
+ // 判断是否是不可通过的结点
+ if (mapInfo.maps[y][x] == BAR) return false;
+ // 判断结点是否存在close表
+ if (isCoordInClose(x, y)) return false;
+
+ return true;
+ }
+
+
+ /**
+ * 判断坐标是否在close表中
+ */
+ private boolean isCoordInClose(Coord coord) {
+ return coord!=null&&isCoordInClose(coord.x, coord.y);
+ }
+
+
+ /**
+ * 判断坐标是否在close表中
+ */
+ private boolean isCoordInClose(int x, int y) {
+ if (closeList.isEmpty()) return false;
+ for (Node node : closeList) {
+ if (node.coord.x == x && node.coord.y == y) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+```
+
+### 红黑树 AVL
+```
+旋转
+ 左旋
+ 右旋
+ 左右旋
+ 右左旋
+```
diff --git a/Week08/NOTE.md b/Week08/NOTE.md
index 50de30414..137db8d30 100644
--- a/Week08/NOTE.md
+++ b/Week08/NOTE.md
@@ -1 +1,375 @@
-学习笔记
\ No newline at end of file
+#### XOR异或 ^
+```
+x ^ 0 = x
+# 1s = ~0
+x ^ 1s = ~x
+x ^ (~x) = 1s
+x ^ x = 0
+c = a ^ b --> a ^ c = b --> b ^ c = a
+a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
+```
+
+
+### 指定位置的位运算
+```
+# 将x最右边的n位清零
+x & (~0 << n)
+# 获取x第n位的值(0 or 1)
+x >> n & 1
+# 获取第n位的幂值
+x & (1 << n)
+# 仅将第n位置为 1
+x | (1 << n)
+# 仅将第n位置为 0
+x & (~(1 << n)
+# 将x最高位至n位(含n)清零
+x & ((1 << n) - 1)
+# 将x第n位至0位(含n)清零
+x & (~((1 << n + 1) - 1))
+```
+
+
+### 实战运用
+```
+# 清零最低位 1
+X = X & (X - 1)
+# 得到最低位 1
+X = X & -X
+```
+
+
+### 布隆过滤器
+```
+# 核心:
+ 超大位数组 + hash函数
+
+# 添加元素
+ 1. 将添加元素给k个hash函数
+ 2. 得到对应位数组的k个位置
+ 3. 将对应位置设为 1
+# 查询元素
+ 1. 将下旬元素给k个hash函数
+ 2. 得到对应位数组的k个位置
+ 3. 只要有一个位置值为 0, 则元素不存在
+ 3. 如果k个位置值全为 1, 则元素可能存在(存在误判, 用于最外层过滤)
+```
+
+```
+/**
+ * 示例代码
+ */
+public class BloomFilter {
+ private static final int DEFAULT_SIZE = 2 << 24;
+ private static final int[] seeds = new int[] { 5, 7, 11, 13, 31, 37, 61 };
+ private BitSet bits = new BitSet(DEFAULT_SIZE);
+ private SimpleHash[] func = new SimpleHash[seeds.length];
+ public BloomFilter() {
+ for (int i = 0; i < seeds.length; i++) {
+ func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);
+ }
+ }
+ public void add(String value) {
+ for (SimpleHash f : func) {
+ bits.set(f.hash(value), true);
+ }
+ }
+ public boolean contains(String value) {
+ if (value == null) {
+ return false;
+ }
+ boolean ret = true;
+ for (SimpleHash f : func) {
+ ret = ret && bits.get(f.hash(value));
+ }
+ return ret;
+ }
+ // 内部类,simpleHash
+ public static class SimpleHash {
+ private int cap;
+ private int seed;
+ public SimpleHash(int cap, int seed) {
+ this.cap = cap;
+ this.seed = seed;
+ }
+ public int hash(String value) {
+ int result = 0;
+ int len = value.length();
+ for (int i = 0; i < len; i++) {
+ result = seed * result + value.charAt(i);
+ }
+ return (cap - 1) & result;
+ }
+ }
+}
+```
+
+
+### LRU Cache
+```java
+/**
+ * 示例代码
+ */
+class LRUCache {
+ /**
+ * 缓存映射表
+ */
+ private Map cache = new HashMap<>();
+ /**
+ * 缓存大小
+ */
+ private int size;
+ /**
+ * 缓存容量
+ */
+ private int capacity;
+ /**
+ * 链表头部和尾部
+ */
+ private DLinkNode head, tail;
+
+ public LRUCache(int capacity) {
+ //初始化缓存大小,容量和头尾节点
+ this.size = 0;
+ this.capacity = capacity;
+ head = new DLinkNode();
+ tail = new DLinkNode();
+ head.next = tail;
+ tail.prev = head;
+ }
+
+ /**
+ * 获取节点
+ * @param key 节点的键
+ * @return 返回节点的值
+ */
+ public int get(int key) {
+ DLinkNode node = cache.get(key);
+ if (node == null) {
+ return -1;
+ }
+ //移动到链表头部
+ (node);
+ return node.value;
+ }
+
+ /**
+ * 添加节点
+ * @param key 节点的键
+ * @param value 节点的值
+ */
+ public void put(int key, int value) {
+ DLinkNode node = cache.get(key);
+ if (node == null) {
+ DLinkNode newNode = new DLinkNode(key, value);
+ cache.put(key, newNode);
+ //添加到链表头部
+ addToHead(newNode);
+ ++size;
+ //如果缓存已满,需要清理尾部节点
+ if (size > capacity) {
+ DLinkNode tail = removeTail();
+ cache.remove(tail.key);
+ --size;
+ }
+ } else {
+ node.value = value;
+ //移动到链表头部
+ moveToHead(node);
+ }
+ }
+
+ /**
+ * 删除尾结点
+ *
+ * @return 返回删除的节点
+ */
+ private DLinkNode removeTail() {
+ DLinkNode node = tail.prev;
+ removeNode(node);
+ return node;
+ }
+
+ /**
+ * 删除节点
+ * @param node 需要删除的节点
+ */
+ private void removeNode(DLinkNode node) {
+ node.next.prev = node.prev;
+ node.prev.next = node.next;
+ }
+
+ /**
+ * 把节点添加到链表头部
+ *
+ * @param node 要添加的节点
+ */
+ private void addToHead(DLinkNode node) {
+ node.prev = head;
+ node.next = head.next;
+ head.next.prev = node;
+ head.next = node;
+ }
+
+ /**
+ * 把节点移动到头部
+ * @param node 需要移动的节点
+ */
+ private void moveToHead(DLinkNode node) {
+ removeNode(node);
+ addToHead(node);
+ }
+
+ /**
+ * 链表节点类
+ */
+ private static class DLinkNode {
+ Integer key;
+ Integer value;
+ DLinkNode prev;
+ DLinkNode next;
+
+ DLinkNode() {
+ }
+
+ DLinkNode(Integer key, Integer value) {
+ this.key = key;
+ this.value = value;
+ }
+ }
+}
+```
+
+
+### 排序算法
+
+#### 冒泡排序
+```java
+public void bubbleSort(int[] nums) {
+ int len = nums.length;
+ // 相邻两个数比较, 顺序错误则交换位置
+ // time:O(n ^ 2) space:O(1)
+ for (int i = 0; i < len; i++) {
+ for (int j = 0; j < len - 1 - i; j++) {
+ if (nums[j] > nums[j + 1]) {
+ int temp = nums[j];
+ nums[j] = nums[j + 1];
+ nums[j + 1] = temp;
+ }
+ }
+ }
+}
+```
+
+
+#### 选择排序
+```java
+public void selectionSort(int[] nums) {
+ int len = nums.length;
+ // 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
+ // time:O(n ^ 2) space:O(1)
+ for (int i = 0; i < len; i++) {
+ int minIndex = i;
+ for (int j = i + 1; j < len; j++) {
+ // 找从i开始后的最小数的索引
+ if (nums[j] < nums[minIndex]) minIndex = j;
+ }
+ // 交换位置
+ int temp = nums[i];
+ nums[i] = nums[minIndex];
+ nums[minIndex] = temp;
+ }
+}
+```
+
+
+#### 插入排序
+```java
+public void selectionSort(int[] nums) {
+ int len = nums.length;
+ // 构建有序序列, 对于未排序数据, 在已排序序列中从后向前扫描, 找到相应位置并插入
+ // time:O(n ^ 2) space:O(1)
+ for (int i = 0; i < len; i++) {
+ int curNum = nums[i], curIndex = i - 1;
+ while (curIndex >= 0 && nums[curIndex] > curNum) {
+ nums[curIndex + 1] = nums[curIndex];
+ curIndex--;
+ }
+ nums[curIndex + 1] = curNum;
+ }
+}
+```
+
+
+#### 快速排序
+```java
+/**
+ * 通过一趟排序将待排记录分隔成独立的两部分,
+ * 其中一部分记录的关键字均比另一部分的关键字小,
+ * 则可分别对这两部分记录继续进行排序,以达到整个序列有序
+ */
+public void quickSort(int[] nums) {
+ // time:O(nlog n) space:O(nlog n)
+ quickSort(nums, 0, nums.length - 1);
+}
+
+private void quickSort(int[] nums, int begin, int end) {
+ if (begin <= end) return;
+ // 寻找标杆位置
+ int pivot = partition(nums, begin, end);
+ // pivot左边元素下探
+ quickSort(nums, begin, pivot - 1);
+ // pivot右边元素下探
+ quickSort(nums, pivot + 1, end);
+}
+
+private int partition(int[] nums, int begin, int end) {
+ // pivot: 标杆位置, counter: 小于pivot的元素的个数
+ int pivot = end, counter = begin;
+ for (int i = begin; i < end; i++) {
+ if (nums[i] < nums[pivot]) {
+ int temp = nums[i]; nums[i] = nums[counter]; nums[counter] = temp;
+ counter++;
+ }
+ }
+ int temp = nums[pivot]; nums[pivot] = nums[counter]; nums[counter] = temp;
+ return counter;
+}
+```
+
+
+#### 归并排序
+```java
+/**
+ * 采用分治法(Divide and Conquer)的一个非常典型的应用。
+ * 将已有序的子序列合并,得到完全有序的序列;
+ * 即先使每个子序列有序,再使子序列段间有序
+ */
+public void mergeSort(int[] nums) {
+ // time:O(nlog n) space:O(n)
+ mergeSort(nums, 0, nums.length - 1);
+}
+
+private void mergeSort(int[] nums, int left, int right) {
+ if (left <= right) return;
+ int mid = ((right - left) >> 1) + left;
+ // 左半部分下探
+ mergeSort(nums, left, mid);
+ // 右半部分下探
+ mergeSort(nums, mid + 1, right);
+ merge(nums, left, mid, right);
+}
+
+/**
+ * 合并两个有序数组
+ */
+private int merge(int[] nums, int left, int mid, int right) {
+ int[] temp = new int[right - left + 1];
+ int i = left, j = mid + 1, k = 0;
+ while (i <= mid && j <= right) {
+ temp[k++] = nums[i] < nums[j] ? nums[i++] : nums[j++];
+ }
+ while (i <= left) temp[k++] = nums[i++];
+ while (j <= right) temp[k++] = nums[j++];
+ System.arraycopy(temp, 0, nums, left, right - left + 1);
+}
+```
\ No newline at end of file
diff --git a/Week08/homework.md b/Week08/homework.md
new file mode 100644
index 000000000..fd4e5913e
--- /dev/null
+++ b/Week08/homework.md
@@ -0,0 +1,241 @@
+### 位1的个数
+```java
+public class Solution {
+ // you need to treat n as an unsigned value
+ public int hammingWeight(int n) {
+ int count = 0;
+ // time:O(num(1)) space:O(1)
+ while (n != 0) {
+ count++;
+ n &= n - 1;
+ }
+ return count;
+ }
+}
+```
+
+
+### 2的幂
+```java
+class Solution {
+ public boolean isPowerOfTwo(int n) {
+ // time:O(1) space:O(1)
+ return n > 0 && (n & (n - 1)) == 0;
+ }
+}
+```
+
+
+### 颠倒二进制位
+```java
+public class Solution {
+ // you need treat n as an unsigned value
+ public int reverseBits(int n) {
+ int ans = 0;
+ // time:O(1) space:O(1)
+ for (int i = 0; i < 32; i++) {
+ ans = (ans << 1) + (n & 1);
+ n >>= 1;
+ }
+ return ans;
+ }
+}
+```
+
+
+### N皇后
+```java
+/**
+ * hash表记录 "列状态" 、 "主对角线状态" 、 "副对角线状态"
+ */
+class Solution {
+ List> result = new ArrayList<>();
+ Set cols = new HashSet<>();
+ Set pies = new HashSet<>();
+ Set nas = new HashSet<>();
+ Deque stack = new LinkedList<>();
+ public List> solveNQueens(int n) {
+ // time:O(N!) space:O(N)
+ dfs(n, 0);
+ return result;
+ }
+ private void dfs(int n, int row) {
+ if (row == n) {
+ List board = convertToBoard(n);
+ result.add(board);
+ return;
+ }
+ // 查找row行n列是否可以放皇后
+ for (int i = 0; i < n; i++) {
+ if (cols.contains(i) || pies.contains(row + i) || nas.contains(row - i)) continue;
+ stack.push(i);
+ // 列 撇 捺
+ cols.add(i); pies.add(row + i); nas.add(row - i);
+ dfs(n, row + 1);
+ // 状态还原
+ cols.remove(i); pies.remove(row + i); nas.remove(row - i);
+ stack.pop();
+ }
+ }
+ private List convertToBoard(int n) {
+ List board = new ArrayList<>();
+ for (Integer num : stack) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < n; i++) {
+ builder.append(".");
+ }
+ builder.replace(num, num + 1, "Q");
+ board.add(builder.toString());
+ }
+ return board;
+ }
+}
+
+/**
+ * 数组记录 "列状态" 、 "主对角线状态" 、 "副对角线状态"
+ */
+class Solution {
+ char[][] chars;
+ boolean[] cols, pies, nas;
+ List> result;
+
+ public List> solveNQueens(int n) {
+ result = new ArrayList<>();
+ if (n <= 0) return result;
+ chars = new char[n][n];
+ for (int i = 0; i < n; i++) {
+ Arrays.fill(chars[i], '.');
+ }
+ // 列 撇 捺
+ cols = new boolean[n]; pies = new boolean[2 * n - 1]; nas = new boolean[2 * n - 1];
+ // time:O(N!) space:O(N ^ 2)
+ dfs(n, 0);
+ return result;
+ }
+ private void dfs(int n, int row) {
+ if (row == n) {
+ List board = convertToBoard(n);
+ result.add(board);
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (cols[i] || pies[row + i] || nas[row - i + n - 1]) continue;
+ chars[row][i] = 'Q';
+ cols[i] = pies[row + i] = nas[row - i + n - 1] = true;
+ dfs(n, row + 1);
+ // 状态还原
+ cols[i] = pies[row + i] = nas[row - i + n - 1] = false;
+ chars[row][i] = '.';
+ }
+ }
+ private List convertToBoard(int n) {
+ List board = new ArrayList<>();
+ for (int i = 0; i < n; i++) {
+ board.add(new String(chars[i]));
+ }
+ return board;
+ }
+}
+```
+
+
+### 比特位计数
+```java
+class Solution {
+ public int[] countBits(int num) {
+ int[] ans = new int[num + 1];
+ ans[0] = 0;
+ // time:O(n) space:O(1)
+ for (int i = 1; i < ans.length; i++) {
+ int lowestBit = i & 1;
+ if (lowestBit == 1) {
+ ans[i] = ans[i - 1] + 1;
+ } else {
+ int temp = i;
+ while (temp != 0) {
+ ans[i]++;
+ temp &= temp - 1;
+ }
+ }
+ }
+ return ans;
+ }
+}
+
+/**
+ * 优化代码
+ */
+class Solution {
+ public int[] countBits(int num) {
+ int[] ans = new int[num + 1];
+ // time:O(n) space:O(1)
+ for (int i = 0; i < ans.length; i++) {
+ ans[i] = ans[i >> 1] + (i & 1);
+ }
+ return ans;
+ }
+}
+```
+
+
+### N皇后 II
+```java
+class Solution {
+ int count = 0;
+ boolean[] cols, pies, nas;
+ public int totalNQueens(int n) {
+ if (n < 1) return 0;
+ cols = new boolean[n];
+ pies = new boolean[2 * n - 1];
+ nas = new boolean[2 * n - 1];
+ // time:O(N!) space:O(N)
+ dfs(n, 0);
+ return count;
+ }
+ private void dfs(int n, int row) {
+ if (row == n) {
+ count++;
+ return;
+ }
+ for (int i = 0; i < n; i++) {
+ if (cols[i] || pies[row + i] || nas[row - i + n - 1]) continue;
+ // 列 撇 捺
+ cols[i] = pies[row + i] = nas[row - i + n - 1] = true;
+ dfs(n, row + 1);
+ // 状态还原
+ cols[i] = pies[row + i] = nas[row - i + n - 1] = false;
+ }
+ }
+}
+```
+
+
+### 数组的相对排序
+```java
+class Solution {
+ public int[] relativeSortArray(int[] arr1, int[] arr2) {
+ int[] count = new int[1001];
+ // 计数
+ for (int num1 : arr1) {
+ count[num1]++;
+ }
+ int index = 0;
+ // 处理arr2中的数
+ for (int num2 : arr2) {
+ while (count[num2] > 0) {
+ arr1[index++] = num2;
+ count[num2]--;
+ }
+ }
+ // 处理剩余的数
+ for (int i = 0; i < count.length; i++) {
+ while (count[i] > 0) {
+ arr1[index++] = i;
+ count[i]--;
+ }
+ }
+ return arr1;
+ }
+}
+```
+
\ No newline at end of file
diff --git a/summary.md b/summary.md
new file mode 100644
index 000000000..5ea3fdfed
--- /dev/null
+++ b/summary.md
@@ -0,0 +1,19 @@
+### 期末总结
+
+#### 刷题量
+ 截止目前,刷题量有130+
+
+#### 谨记
+ 过遍数
+ 做笔记画脑图 -- 多看NB代码
+ 模板烂熟于心
+ 坚持刷题(至少 1 / day) -- 由量变到质变
+
+#### 收获
+ 通过学习,巩固基础,完善知识体系。做新题时,有一定的思路,不再是白纸一张
+
+#### 改变
+ 70天的集中学习,仿佛回到了高中时代。这种授课方式很适合我这种想要努力提高自己,但还未养成自学习惯和学习方法的人。开课后,明显感觉自己有了要学习的想法,不再是只想不动。月底复报了‘开课吧’的架构师课程,为了自己的‘钱途’,继续加油!!!
+
+#### 结语
+ 感谢老师、助教在学习上的引导和经验分享,也很荣幸和大家一起共同营造的学习氛围。祝大家‘钱途’光明,节节高升
\ No newline at end of file