├── leetCode.js
├── README.md
├── leetCode-1-0292-nim-game.md
├── leetCode-0-083-remove-duplicates-from-sorted-list.md
├── leetCode-1-0125-valid-palindrome.md
├── leetCode-0-094-binary-tree-inorder-traversal.md
├── leetCode-1-0144-binary-tree-preorder-traversal.md
├── leetCode-1-0145-binary-tree-postorder-traversal.md
├── leetCode-0-080-remove-duplicates-from-sorted-array-ii.md
├── leetCode-1-0231-power-of-two.md
├── leetCode-1-0680-valid-palindrome-ii.md
├── leetCode-1-0283-move-zeroes.md
├── leetCode-0-008-string-to-integer-atoi.md
├── leetCode-0-027-remove-element.md
├── leetCode-0-011-container-with-most-water.md
├── leetCode-1-0709-to-lower-case.md
├── leetCode-0-056-merge-intervals.md
├── leetCode-1-0153-find-minimum-in-rotated-sorted-array.md
├── leetCode-1-0455-assign-cookies.md
├── leetCode-0-016-3sum-closest.md
├── leetCode-1-0179-largest-number.md
├── leetCode-1-0860-lemonade-change.md
├── leetCode-0-017-letter-combinations-of-a-phone-number.md
├── leetCode-0-007-reverse-integer.md
├── leetCode-1-0147-insertion-sort-list.md
├── leetCode-0-046-permutations.md
├── leetCode-0-013-roman-to-integer.md
├── leetCode-0-034-find-first-and-last-position-of-element-in-sorted-array.md
├── leetCode-0-058-length-of-last-word.md
├── leetCode-0-012-integer-to-roman.md
├── leetCode-0-036-valid-sudoku.md
├── leetCode-1-1122-relative-sort-array.md
├── leetCode-0-018-4sum.md
├── leetCode-0-009-palindrome-number.md
├── leetCode-1-0718-maximum-length-of-repeated-subarray.md
├── leetCode-0-066-plus-one.md
├── leetCode-0-049-group-anagrams.md
├── leetCode-1-0115-distinct-subsequences.md
├── leetCode-1-0107-binary-tree-level-order-traversal-ii.md
├── leetCode-0-074-search-a-2d-matrix.md
├── leetCode-0-006-zigzag-conversion.md
├── leetCode-0-021-merge-two-sorted-lists.md
├── leetCode-0-098-validate-binary-search-tree.md
├── leetCode-0-057-insert-interval.md
├── leetCode-1-0236-lowest-common-ancestor-of-a-binary-tree.md
├── leetCode-1-0714-best-time-to-buy-and-sell-stock-with-transaction-fee.md
├── leetCode-0-088-merge-sorted-array.md
├── leetCode-0-024-swap-nodes-in-pairs.md
├── leetCode-1-0159-longest-substring-with-at-most-two-distinct-characters.md
├── leetCode-1-0309-best-time-to-buy-and-sell-stock-with-cooldown.md
├── leetCode-1-0154-find-minimum-in-rotated-sorted-array-ii.md
├── leetCode-1-0876-middle-of-the-linked-list.md
├── leetCode-0-037-sudoku-solver.md
├── leetCode-1-0191-number-of-1-bits.md
├── leetCode-1-0242-valid-anagram.md
├── leetCode-1-0141-linked-list-cycle.md
├── leetCode-1-0340-longest-substring-with-at-most-k-distinct-characters.md
├── leetCode-1-0102-binary-tree-level-order-traversal.md
├── leetCode-1-0123-best-time-to-buy-and-sell-stock-iii.md
├── leetCode-0-003-longest-substring-without-repeating-characters.md
├── leetCode-1-0190-reverse-bits.md
├── leetCode-1-0515-find-largest-value-in-each-tree-row.md
├── leetCode-0-040-combination-sum-ii.md
├── leetCode-1-0350-intersection-of-two-arrays-ii.md
├── leetCode-1-0105-construct-binary-tree-from-preorder-and-inorder-traversal.md
├── leetCode-1-0727-minimum-window-subsequence.md
├── leetCode-1-0632-smallest-range-covering-elements-from-k-lists.md
├── leetCode-1-0279-perfect-squares.md
├── leetCode-1-0438-find-all-anagrams-in-a-string.md
├── leetCode-0-030-substring-with-concatenation-of-all-words.md
├── leetCode-0-070-climbing-stairs.md
├── leetCode-1-0746-min-cost-climbing-stairs.md
├── leetCode-0-069-sqrtx.md
├── leetCode-0-020-valid-parentheses.md
├── leetCode-0-064-minimum-path-sum.md
├── leetCode-1-0152-maximum-product-subarray.md
├── leetCode-0-001-two-sum.md
├── leetCode-0-082-remove-duplicates-from-sorted-list-ii.md
├── leetCode-1-0106-construct-binary-tree-from-inorder-and-postorder-traversal.md
├── leetCode-0-026-remove-duplicates-from-sorted-array.md
├── leetCode-0-050-powx-n.md
├── leetCode-0-075-sort-colors.md
├── leetCode-1-0121-best-time-to-buy-and-sell-stock.md
├── leetCode-0-044-wildcard-matching.md
├── leetCode-0-047-permutations-ii.md
├── leetCode-0-052-n-queens-ii.md
├── leetCode-1-1143-longest-common-subsequence.md
├── leetCode-1-0387-first-unique-character-in-a-string.md
├── leetCode-0-091-decode-ways.md
├── leetCode-1-0213-house-robber-ii.md
├── leetCode-1-0874-walking-robot-simulation.md
├── leetCode-1-0142-linked-list-cycle-ii.md
└── leetCode-0-039-combination-sum.md
/leetCode.js:
--------------------------------------------------------------------------------
1 | // 回首向来萧瑟处,归去,也无风雨也无晴。
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 逻辑顺序刷题
2 | + [配合知识点一起学习更佳 戳看](https://github.com/Alex660/Algorithms-and-data-structures)
3 |
--------------------------------------------------------------------------------
/leetCode-1-0292-nim-game.md:
--------------------------------------------------------------------------------
1 | # Nim 游戏(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:
7 | + 博弈论
8 | ```javascript
9 | /**
10 | * @param {number} n
11 | * @return {boolean}
12 | */
13 | var canWinNim = function(n) {
14 | // 或 return (n & 3) !== 0
15 | return n % 4 !== 0
16 | };
17 | ```
--------------------------------------------------------------------------------
/leetCode-0-083-remove-duplicates-from-sorted-list.md:
--------------------------------------------------------------------------------
1 | # 删除排序链表中的重复元素(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 链表
7 | + [戳看链表各种操作大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
8 | #### 解法:指针遍历
9 | + 时间复杂度:O(n)
10 | + 空间复杂度:O(1)
11 | ```javascript
12 | /**
13 | * Definition for singly-linked list.
14 | * function ListNode(val) {
15 | * this.val = val;
16 | * this.next = null;
17 | * }
18 | */
19 | /**
20 | * @param {ListNode} head
21 | * @return {ListNode}
22 | */
23 | var deleteDuplicates = function(head) {
24 | let cur = head
25 | while(cur && cur.next) {
26 | if(cur.next.val === cur.val) {
27 | cur.next = cur.next.next
28 | }
29 | else{cur = cur.next}
30 | }
31 | return head
32 | };
33 | ```
--------------------------------------------------------------------------------
/leetCode-1-0125-valid-palindrome.md:
--------------------------------------------------------------------------------
1 | # 验证回文串(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:调用函数懒蛋法
7 | ```javascript
8 | /**
9 | * @param {string} s
10 | * @return {boolean}
11 | */
12 | var isPalindrome = function(s) {
13 | let strArr = s.replace(/[^0-9a-zA-Z]/g,"").toLowerCase().split('');
14 | return strArr.join('') == strArr.reverse().join('');
15 | };
16 | ```
17 | #### 解法二:格式化 + 双指针夹逼
18 | ```javascript
19 | /**
20 | * @param {string} s
21 | * @return {boolean}
22 | */
23 | var isPalindrome = function(s) {
24 | s = s.replace(/[^0-9a-zA-Z]/g,'').toLowerCase();
25 | let n = s.length;
26 | let left = 0;
27 | let right = n-1;
28 | while(left < right){
29 | if(s[left] != s[right]){
30 | return false;
31 | }
32 | left++;
33 | right--;
34 | }
35 | return true;
36 | };
37 | ```
--------------------------------------------------------------------------------
/leetCode-0-094-binary-tree-inorder-traversal.md:
--------------------------------------------------------------------------------
1 | # 二叉树的中序遍历(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 定义
7 | + 前序
8 | + 根-左-右
9 | + 中序
10 | + 左-根-右
11 | + 后序
12 | + 左-右-根
13 | ## 例题
14 | + [144. 二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
15 | + [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
16 | + [145. 二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
17 | ## 图解
18 | + 
19 | ## 三序遍历解法
20 | + 递归
21 | + 模拟栈/迭代
22 | + I
23 | + II
24 | + III
25 | + IV
26 | + 转换
27 | + 只针对前、后序遍历
28 | + 线索二叉树/莫里斯遍历
29 | #### 以上题解尽在下方
30 | + [二叉树的前、中、后序遍历 - 解法大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%89%E5%BA%8F%E9%81%8D%E5%8E%86.md)
--------------------------------------------------------------------------------
/leetCode-1-0144-binary-tree-preorder-traversal.md:
--------------------------------------------------------------------------------
1 | # 二叉树的前序遍历(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 定义
7 | + 前序
8 | + 根-左-右
9 | + 中序
10 | + 左-根-右
11 | + 后序
12 | + 左-右-根
13 | ## 例题
14 | + [144. 二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
15 | + [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
16 | + [145. 二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
17 | ## 图解
18 | + 
19 | ## 三序遍历解法
20 | + 递归
21 | + 模拟栈/迭代
22 | + I
23 | + II
24 | + III
25 | + IV
26 | + 转换
27 | + 只针对前、后序遍历
28 | + 线索二叉树/莫里斯遍历
29 | #### 以上题解尽在下方
30 | + [二叉树的前、中、后序遍历 - 解法大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%89%E5%BA%8F%E9%81%8D%E5%8E%86.md)
--------------------------------------------------------------------------------
/leetCode-1-0145-binary-tree-postorder-traversal.md:
--------------------------------------------------------------------------------
1 | # 二叉树的后序遍历(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 定义
7 | + 前序
8 | + 根-左-右
9 | + 中序
10 | + 左-根-右
11 | + 后序
12 | + 左-右-根
13 | ## 例题
14 | + [144. 二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
15 | + [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
16 | + [145. 二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
17 | ## 图解
18 | + 
19 | ## 三序遍历解法
20 | + 递归
21 | + 模拟栈/迭代
22 | + I
23 | + II
24 | + III
25 | + IV
26 | + 转换
27 | + 只针对前、后序遍历
28 | + 线索二叉树/莫里斯遍历
29 | #### 以上题解尽在下方
30 | + [二叉树的前、中、后序遍历 - 解法大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%89%E5%BA%8F%E9%81%8D%E5%8E%86.md)
--------------------------------------------------------------------------------
/leetCode-0-080-remove-duplicates-from-sorted-array-ii.md:
--------------------------------------------------------------------------------
1 | # 删除排序数组中的重复项 II (中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:双指针
8 | + [解法同 - 26. 删除排序数组中的重复项 - 解法二](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/26-shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xian-6/)
9 | + 唯一区别是从第三、一个元素起开始比对
10 | ```javascript
11 | /**
12 | * @param {number[]} nums
13 | * @return {number}
14 | */
15 | var removeDuplicates = function(nums) {
16 | let count = 0
17 | let n = nums.length
18 | if(n < 3) return n
19 | let j = 1
20 | for(let i = 2;i < n;i++) {
21 | if(nums[i] != nums[j-1]) {
22 | j++
23 | nums[j] = nums[i]
24 | }
25 | }
26 | return j + 1
27 | };
28 | ```
--------------------------------------------------------------------------------
/leetCode-1-0231-power-of-two.md:
--------------------------------------------------------------------------------
1 | # 2的幂(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:2的幂数的数字的二进制特点 + 位操作
7 | + 2的幂数的数字的二进制有且只有一个1,其余均是0
8 | + n & (n-1):清零最低位的1
9 | + 合起来 n & (n-1) == 0
10 | ```javascript
11 | /**
12 | * @param {number} n
13 | * @return {boolean}
14 | */
15 | var isPowerOfTwo = function(n) {
16 | return n > 0 && (n & (n-1)) == 0;
17 | };
18 | ```
19 | #### 解法二:调用函数懒蛋法
20 | ```javascript
21 | /**
22 | * @param {number} n
23 | * @return {boolean}
24 | */
25 | var isPowerOfTwo = function(n) {
26 | return Number.isInteger(Math.log2(n));
27 | };
28 | ```
29 | #### 解法三:位运算
30 | ```javascript
31 | /**
32 | * @param {number} n
33 | * @return {boolean}
34 | */
35 | var isPowerOfTwo = function(n) {
36 | return n > 0 && (n & (-n)) == n
37 | };
38 | ```
39 | #### 解法四:取模
40 | ```javascript
41 | /**
42 | * @param {number} n
43 | * @return {boolean}
44 | */
45 | var isPowerOfTwo = function(n) {
46 | while(n > 1){
47 | n /= 2;
48 | }
49 | if(n == 1){
50 | return true;
51 | }else{
52 | return false;
53 | }
54 | };
55 | ```
--------------------------------------------------------------------------------
/leetCode-1-0680-valid-palindrome-ii.md:
--------------------------------------------------------------------------------
1 | # 验证回文字符串 Ⅱ(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:双指针 + 判断回文
7 | + 判断回文数
8 | + [9. 回文数](https://leetcode-cn.com/problems/palindrome-number/solution/9-hui-wen-shu-jiang-ti-jie-fu-zhi-dao-liu-lan-qi-k/)
9 | + 双指针
10 | + 当s[left] != s[right]
11 | + s[left+1,right] || s[left,right-1]
12 | + 有一个为真
13 | + 说明需要删除一个多余的字符,符合
14 | + 均为真
15 | + 说明字符串本身为合法的回文串
16 | + 均为假
17 | + 说明至少需要删除一个以上的字符,不合题意
18 | ```javascript
19 | /**
20 | * @param {string} s
21 | * @return {boolean}
22 | */
23 | var validPalindrome = function(s) {
24 | let n = s.length;
25 | if(n < 2){
26 | return s;
27 | }
28 | let isPalindrome = (left,right)=> {
29 | while(left < right){
30 | if(s[left++] != s[right--]){
31 | return false;
32 | }
33 | }
34 | return true;
35 | }
36 | for(let i = 0;i < n;i++){
37 | if(s[i] != s[n-i-1]){
38 | return isPalindrome(i+1,n-i-1) || isPalindrome(i,n-1-i-1);
39 | }
40 | }
41 | return true;
42 | };
43 | ```
--------------------------------------------------------------------------------
/leetCode-1-0283-move-zeroes.md:
--------------------------------------------------------------------------------
1 | # 爬楼梯(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 解法一:
7 | .png)
8 |
9 | #### 解法一:双指针法
10 | > 分析:输入[0,1,0,3,12] 输出[1,3,12,0,0]
11 | + 1、维护一个总是指向0的动态指针 i
12 | + 2、每次遇到非0位置的数将其位置的数与0位置指针索引上的数进行交换值并更新1的指针i++
13 | + 2处交换 一处总为0 所以直接赋值为0 不用存储临时变量 但如此就需要判断 i 是否等于 j 去掉为自己的情况
14 | + 代码实现
15 | ```javascript
16 | /**
17 | * @param {number[]} nums
18 | * @return {void} Do not return anything, modify nums in-place instead.
19 | */
20 | var moveZeroes = function(nums) {
21 | var i = 0;
22 | for(var r = 0;r
8 | #### 解法:正则
9 |
10 | + 去首尾控格
11 | + 正则
12 | + ([]) 一个中括号表达式
13 | + (?) 匹配前面字表达式零次或一次,或指明一个非贪婪限定符
14 | + (\d) 匹配一个数字字符 等价于 <=> [0-9]
15 | + (+) 匹配前面的子表达式一次或多次 等价于 <=> {1,}
16 | ```javascript
17 | /**
18 | * @param {string} str
19 | * @return {number}
20 | */
21 | var myAtoi = function(str) {
22 | if(!str){
23 | return 0;
24 | }
25 | str = str.trim();
26 | var reg = new RegExp(/^[+|-]?\d+/g);
27 | if(!reg.test(str)){
28 | return 0;
29 | }
30 | var num = str.match(reg)[0];
31 | var max = Math.pow(2,31);
32 | if(num>max-1){
33 | return max-1;
34 | }else if(num < -max){
35 | return -max;
36 | }
37 | return num;
38 | };
39 | ```
--------------------------------------------------------------------------------
/leetCode-0-027-remove-element.md:
--------------------------------------------------------------------------------
1 | # 移除元素(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:双指针
8 | ```javascript
9 | /**
10 | * @param {number[]} nums
11 | * @param {number} val
12 | * @return {number}
13 | */
14 | var removeElement = function(nums, val) {
15 | let i = 0,j = 0
16 | while(j < nums.length) {
17 | if(nums[j] !== val) {
18 | nums[i++] = nums[j]
19 | }
20 | j++
21 | }
22 | return i
23 | };
24 | ```
25 | + 优化
26 | + 当原数组等于给定值的元素较少时,上面的方法仍旧会遍历复制每个元素到前排
27 | + 只有当遇到有给定值的元素时做复制操作
28 | + 将遇到要删除的元素(首指针++)丢到数组最后,并同时缩小数组长度(用指针模拟实现即:尾指针--)
29 | ```javascript
30 | /**
31 | * @param {number[]} nums
32 | * @param {number} val
33 | * @return {number}
34 | */
35 | var removeElement = function(nums, val) {
36 | let i = 0,j = nums.length
37 | while(i < j) {
38 | if(nums[i] !== val) {
39 | i++
40 | }else {
41 | nums[i] = nums[j-1]
42 | j--
43 | }
44 | }
45 | return j
46 | };
47 | ```
--------------------------------------------------------------------------------
/leetCode-0-011-container-with-most-water.md:
--------------------------------------------------------------------------------
1 | # 盛最多水的容器(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:暴力枚举
7 | + 时间复杂度:O(n^2)<=双重循环【计算1,2,3,4...n中高度组合的面积为 n(n-1)/2 】
8 | + 空间复杂度:O(1)
9 | ```javascript
10 | var maxArea = function(height) {
11 | var maxarea = 0;
12 | for(var i = 0;i=height[end]){
36 | end--;
37 | }else{
38 | start++;
39 | }
40 | }
41 | return maxarea;
42 | };
43 | ```
--------------------------------------------------------------------------------
/leetCode-1-0709-to-lower-case.md:
--------------------------------------------------------------------------------
1 | # 转换成小写字母 (简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:暴力枚举 + ASCII码转换
7 | + [a,z] = [97,122]
8 | + [A,Z] = [65,90]
9 | ```javascript
10 | /**
11 | * @param {string} str
12 | * @return {string}
13 | */
14 | var toLowerCase = function(str) {
15 | let result = '';
16 | for(let i = 0;i < str.length;i++){
17 | let code = str.charCodeAt(i);
18 | if(code <= 90 && code >= 65){
19 | result += String.fromCharCode(code + 32);
20 | }else{
21 | result += str[i];
22 | }
23 | }
24 | return result;
25 | };
26 | ```
27 | #### 解法二:位运算
28 | + 大写变小写、小写变大写 : ASCII码 ^= 32
29 | + 大写变小写、小写变小写 : ASCII码 |= 32
30 | + 小写变大写、大写变大写 : ASCII码 &= -33
31 | ```javascript
32 | /**
33 | * @param {string} str
34 | * @return {string}
35 | */
36 | var toLowerCase = function(str) {
37 | let result = '';
38 | for(let i = 0;i < str.length;i++){
39 | result += String.fromCharCode(str.charCodeAt(i) | 32);
40 | }
41 | return result;
42 | };
43 | ```
44 | #### 解法三:库函数
45 | ```javascript
46 | /**
47 | * @param {string} str
48 | * @return {string}
49 | */
50 | var toLowerCase = function(str) {
51 | return str.toLowerCase();
52 | };
53 | ```
--------------------------------------------------------------------------------
/leetCode-0-056-merge-intervals.md:
--------------------------------------------------------------------------------
1 | # 合并区间(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:排序
7 | + 时间复杂度:O( nlogn )
8 | + 空间复杂度:O(n)
9 | + 思路
10 | + 可以重叠的空间
11 | + a = [1,4]、b = [2,3] => [1,4]
12 | + a = [1,3]、b = [2,6] => [1,6]
13 | + a = [1,2]、b = [2,5] => [1,5]
14 | + 不可重叠
15 | + a = [1,4]、b = [5,7]
16 | + 归纳
17 | + 当a[1] >= b[0]时,两个区间一定有重叠
18 | + 重叠后的区间
19 | + 左边 == a[0]
20 | + 右边 == Max(a[1],b[1])
21 | + 解题技巧
22 | + 通过子数组首元素排序,确保左边相对确定下来
23 | + 下一个子数组的第一个元素小于等于当前子数组的第二个元素时,会有重叠
24 | + 需要循环穷举
25 | ```javascript
26 | /**
27 | * @param {number[][]} intervals
28 | * @return {number[][]}
29 | */
30 | var merge = function(intervals) {
31 | let result = [];
32 | let len = intervals.length;
33 | if(len == 0){
34 | return [];
35 | }
36 | intervals.sort( (a,b) => a[0] - b[0]);
37 | let i = 0;
38 | while( i < len){
39 | let currLeft = intervals[i][0];
40 | let currRight = intervals[i][1];
41 | while(i < len - 1 && intervals[i+1][0] <= currRight){
42 | i++;
43 | currRight = Math.max(intervals[i][1],currRight);
44 | }
45 | result.push([currLeft,currRight]);
46 | i++;
47 | }
48 | return result;
49 | };
50 | ```
--------------------------------------------------------------------------------
/leetCode-1-0153-find-minimum-in-rotated-sorted-array.md:
--------------------------------------------------------------------------------
1 | # 寻找旋转排序数组中的最小值(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 解法:二分查找法
7 | + 分析
8 | + 设置low,high左右边界,算出中间数nums[mid]
9 | + 当nums[mid] > nums[high]时,说明出现了无序的地方在右边
10 | + low = mid+1
11 | + 否则无序点在左侧
12 | + high = mid
13 | + 两边夹逼直到low == high ,剩下的一个元素即为无序点
14 | + 类似题型
15 | 1. [33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/33-sou-suo-xuan-zhuan-pai-xu-shu-zu-by-alexer-660/)
16 | 2. [69. x 的平方根](https://leetcode-cn.com/problems/sqrtx/solution/69-x-de-ping-fang-gen-by-alexer-660/)
17 | 3. [154. 寻找旋转排序数组中的最小值 II](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/solution/154-xun-zhao-xuan-zhuan-pai-xu-shu-zu-zhong-de-z-3/)
18 | + 归纳解题技巧
19 | + while(left < right) 在**循环体外**输出
20 | + while(left <= right) 在**循环体内**输出
21 | + n除以2^k可以换成位运算,提升代码性能
22 | + n>>k
23 | ```javascript
24 | /**
25 | * @param {number[]} nums
26 | * @return {number}
27 | */
28 | var findMin = function(nums) {
29 | var low = 0;
30 | var high = nums.length-1;
31 | while(low < high){
32 | var mid = (low+high)>>1;
33 | if(nums[mid] > nums[high]){
34 | low = mid+1;
35 | }else{
36 | high = mid
37 | }
38 | }
39 | return nums[low];
40 | };
41 | ```
--------------------------------------------------------------------------------
/leetCode-1-0455-assign-cookies.md:
--------------------------------------------------------------------------------
1 | # 分发饼干(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:贪心算法
8 | + 类似题型
9 | + [860. 柠檬水找零](https://leetcode-cn.com/problems/lemonade-change/solution/860-ning-meng-shui-zhao-ling-by-alexer-660/)
10 | + 解题关键
11 | + 优先满足胃口小的小朋友的需求,即贪心问题。
12 | + 设最大可满足的孩子数量为maxNum = 0
13 | + 胃口小的拿小的,胃口大的拿大的
14 | + 两边升序,然后一一对比
15 | + 当饼干j >= 胃口i 时
16 | + i++、j++
17 | + maxNum++
18 | + 当饼干j < 胃口i时,说明饼干不够吃,换更大的
19 | + j++
20 | + 到边界后停止
21 | + 小问题
22 | + js可以调用sort()函数默认是返回从小到大排序的
23 | + 但在leetCode里不知道为啥不行
24 | + 只能加一个函数参数了
25 | ```javascript
26 | /**
27 | * @param {number[]} g
28 | * @param {number[]} s
29 | * @return {number}
30 | */
31 | var findContentChildren = function(g, s) {
32 | g = g.sort((a,b) => a-b);
33 | s = s.sort((a,b) => a-b);
34 | var gLen = g.length;
35 | var sLen = s.length;
36 | var i = 0;
37 | var j = 0;
38 | var maxNum = 0;
39 | while(i < gLen && j < sLen){
40 | if(s[j] >= g[i]){
41 | i++;
42 | j++;
43 | maxNum++;
44 | }else{
45 | j++;
46 | }
47 | }
48 | return maxNum;
49 | };
50 | ```
--------------------------------------------------------------------------------
/leetCode-0-016-3sum-closest.md:
--------------------------------------------------------------------------------
1 | # 最接近的三数之和(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:双指针
7 | + 思路
8 | + 确定第一个数,在左右指针移动过程中,更新与target差值最小的结果
9 | + 技巧
10 | + 排序原数组
11 | + nums[right] >= nums[left]
12 | + 确定一个数 x
13 | + res = x + nums[left] + nums[right]
14 | + 当 sum - target < res - target 时
15 | + res = sum
16 | + 当 sum == target 时
17 | + 返回 sum 即为所求
18 | + 当 sum > target
19 | + 根据从小到大的排序方式,左右指针不能再增大,只有右指针能够缩小,进而缩小 sum 值
20 | + right--
21 | + 当 sum < target
22 | + 原理同上,只不过先从小的元素累加起
23 | + left++
24 | ```javascript
25 | /**
26 | * @param {number[]} nums
27 | * @param {number} target
28 | * @return {number}
29 | */
30 | var threeSumClosest = function(nums, target) {
31 | nums.sort((a,b) => a - b);
32 | let res = nums[0] + nums[1] + nums[2];
33 | let n = nums.length;
34 | for(let i = 0;i < n;i++){
35 | let left = i + 1;
36 | let right = n - 1;
37 | while(left < right){
38 | let sum = nums[i] + nums[left] + nums[right];
39 | if(Math.abs(res - target) > Math.abs(sum - target)){
40 | res = sum;
41 | }else if(sum > target){
42 | right--;
43 | }else if(sum < target){
44 | left++;
45 | }else if(sum === target){
46 | return res;
47 | }
48 | }
49 | }
50 | return res;
51 | };
52 | ```
--------------------------------------------------------------------------------
/leetCode-1-0179-largest-number.md:
--------------------------------------------------------------------------------
1 | # 最大数(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一
7 | + 拼接对比
8 | + 结尾判断结果是否是正常的number
9 | ```javascript
10 | /**
11 | * @param {number[]} nums
12 | * @return {string}
13 | */
14 | var largestNumber = function(nums) {
15 | nums.sort( (a,b) => {
16 | let aStr = a + '';
17 | let bStr = b + '';
18 | return (b * Math.pow(10,aStr.length) + a) - (a * Math.pow(10,bStr.length) + b);
19 | })
20 | return nums.join('').replace(/^0+/,'') || '0';
21 | };
22 | ```
23 | #### 解法二
24 | + 思路
25 | + 直觉上,构建最大数字,希望越高位的数字越大越好
26 | + 降序
27 | + 关键
28 | + 首先排序原数组
29 | + 直接在排序时,对比拼接降序的相邻两个元素后的两个值的大小
30 | + 大的排前面,如30和3
31 | + 330 > 303 => 3、30
32 | ```javascript
33 | /**
34 | * @param {number[]} nums
35 | * @return {string}
36 | */
37 | var largestNumber = function(nums) {
38 | nums.sort((a,b) => {
39 | let t1 = a + '' + b;
40 | let t2 = b + '' + a;
41 | if(t1 < t2) return 1;
42 | else if(t1 > t2) return -1;
43 | else return 0;
44 | })
45 | let ans = nums.join('');
46 | return ans[0] === '0' ? '0' : ans;
47 | };
48 | ```
49 | #### 解法三
50 | + 只是把拼接过程换成了map(String)
51 | ```javascript
52 | /**
53 | * @param {number[]} nums
54 | * @return {string}
55 | */
56 | var largestNumber = function(nums) {
57 | return nums.map(String).sort((a,b) => (b+a) - (a+b)).join('').replace(/^0+/,'') || '0';
58 | };
59 | ```
--------------------------------------------------------------------------------
/leetCode-1-0860-lemonade-change.md:
--------------------------------------------------------------------------------
1 | # 柠檬水找零(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法一:贪心选择
8 | + 题意每张账单只能是5、10、20
9 | + 柠檬水均是5元一份
10 | + 店家自己没有零钱
11 | + 解法与思路
12 | + 当收到20时
13 | + 优先匹配店家手里的一张10和一张5,如有返回true
14 | + 店家手里的10--
15 | + 店家手里的5--
16 | + 如没有重新匹配3张15,如有返回true
17 | + 店家手里的5-=3
18 | + 如都没有返回false
19 | + 当收到10时
20 | + 优先匹配一张5如有返回true
21 | + 店家手里的5--,10++
22 | + 如没有返回false
23 | + 当收到5时
24 | + 店家手里的5++
25 | ```javascript
26 | /**
27 | * @param {number[]} bills
28 | * @return {boolean}
29 | */
30 | var lemonadeChange = function(bills) {
31 | var five = 0;
32 | var ten = 0;
33 | var len = bills.length;
34 | for(var i = 0;i 0 && five > 0){
45 | ten--;
46 | five--;
47 | }else if(five >= 3){
48 | five -= 3;
49 | }else{
50 | return false;
51 | }
52 | }
53 | }
54 | return true;
55 | };
56 | ```
--------------------------------------------------------------------------------
/leetCode-0-017-letter-combinations-of-a-phone-number.md:
--------------------------------------------------------------------------------
1 | # 电话号码的字母组合(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:递归
7 | + 回溯
8 | 
9 | ```javascript
10 | /**
11 | * @param {string} digits
12 | * @return {string[]}
13 | */
14 | var letterCombinations = function(digits) {
15 | if(!digits){
16 | return [];
17 | }
18 | var len = digits.length;
19 | var map = new Map();
20 | map.set('2','abc');
21 | map.set('3','def');
22 | map.set('4','ghi');
23 | map.set('5','jkl');
24 | map.set('6','mno');
25 | map.set('7','pqrs');
26 | map.set('8','tuv');
27 | map.set('9','wxyz');
28 | var result = [];
29 | function _generate(i,str){
30 | // terminator
31 | if(i == len){
32 | result.push(str);
33 | return;
34 | }
35 | // process
36 | // drill down
37 | var tmp = map.get(digits[i]);
38 | for(var r = 0;r
6 |
7 | > 反转一个整数s 要去新整数范围在 [-2^31,2^31-1]
8 | > 例如:123 => 321 -124 => 421
9 |
10 | + 假设要求范围在 [19,63]
11 |
12 | + 反转整数等于y = y*10 +pop
13 |
14 | + 如果 y>63 || y<19 就代表如题 溢出 =>
15 | + y>63
16 | + y*10 > 63/10 && pop 存在 则一定溢出 <= y*10 > maxValue/10
17 | + y*10 == maxValue【除去个位】 && pop > maxValue%10 【个位】
18 |
19 | + y<19
20 | + y*10 < 19/10 && pop 存在 则更小了就溢出了 <= y*10 < minValue/10
21 | + y*10 == minValue【除去个位】 && pop < minValue%10 【个位】
22 |
23 | + y = y/10 每次自动去掉最后一位 => 更新 拼接的 pop = y%10
24 |
25 |
26 | + 分析:
27 | + 求倒数位置开始数字的每一位 => x%10 => pop = x%10
28 | + 从倒数位置开始去掉最后一位 => x/10
29 |
30 | + 从原整数最后一位数字开始取每一位pop,从上次看是*10并动态拼接起来位数成反转的部分或整个整数s_f &&
31 | + 每一步判断是否超出范围 ? 溢出false : 返回拼接后的反转字符串
32 | #### 解法一
33 | ```javascript
34 | function reverse(x){
35 | var ans = 0;
36 | var pop = 0;
37 | var maxValue = Math.pow(2,31)-1;
38 | var minValue = -Math.pow(2,31);
39 | var maxValuePre = parseInt(maxValue/10);
40 | var maxValueEnd = maxValue%10;
41 | var minValuePre = parseInt(minValue/10);
42 | var minValueEnd = minValue%10;
43 | while(x!=0){
44 | pop = x %10
45 | if(ans > maxValuePre && pop){
46 | return false;
47 | }else if(ans == maxValuePre && pop > maxValueEnd){
48 | return false;
49 | }else if(ans < minValuePre && pop){
50 | return false;
51 | }else if(ans == minValuePre && pop < minValueEnd){
52 | return false;
53 | }else{
54 | x = parseInt(x/10)
55 | ans = ans * 10 + pop
56 | }
57 | }
58 | return ans;
59 | }
60 | ```
--------------------------------------------------------------------------------
/leetCode-1-0147-insertion-sort-list.md:
--------------------------------------------------------------------------------
1 | # 对链表进行插入排序(中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:从左往右插入排序
8 | + [经典排序算法讲解 - 插入排序](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/BitOperation%E4%BD%8D%E8%BF%90%E7%AE%97%E3%80%81Bloom%20Filter%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E3%80%81LRU%20Cache%E7%BC%93%E5%AD%98%E3%80%81Sorting%20algorithm%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md)
9 | + 由上述讲解可知
10 | + 如果此题是双链表,那么很容易根据前驱模仿插入排序实现,即从右往左插入实现
11 | + 此题是单链表,只能每次从开头开始依次比较
12 | ```javascript
13 | /**
14 | * Definition for singly-linked list.
15 | * function ListNode(val) {
16 | * this.val = val;
17 | * this.next = null;
18 | * }
19 | */
20 | /**
21 | * @param {ListNode} head
22 | * @return {ListNode}
23 | */
24 | var insertionSortList = function(head) {
25 | // 边界条件
26 | if(head === null) return head;
27 | // 哨兵节点
28 | let preHead = new ListNode(0);
29 | // 将要移动重新插入链表中的元素
30 | let curr = head;
31 | // 插入链表中的前驱位置 插入pre和pre.next之间
32 | let pre = preHead;
33 | // 下一个将要移动插入的元素
34 | let next = null;
35 | // 遍历
36 | while(curr){
37 | // 保存下一个将要插入的元素
38 | next = curr.next;
39 | // 寻找插入的前驱位置
40 | while(pre.next && pre.next.val < curr.val){
41 | pre = pre.next;
42 | }
43 | // 插入
44 | curr.next = pre.next;
45 | pre.next = curr;
46 | // 从头或者从左到右开始遍历
47 | pre = preHead;
48 | // 下一个
49 | curr = next;
50 | }
51 | return preHead.next;
52 | };
53 | ```
--------------------------------------------------------------------------------
/leetCode-0-046-permutations.md:
--------------------------------------------------------------------------------
1 | # 全排列(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 回溯算法系列
7 | + [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/solution/39-zu-he-zong-he-by-alexer-660/)
8 | + [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/solution/40-zu-he-zong-he-ii-by-alexer-660/)
9 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
10 | + [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
11 | + [77. 组合](https://leetcode-cn.com/problems/combinations/solution/77-zu-he-by-alexer-660/)
12 | + [78. 子集](https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-by-alexer-660/)
13 | + [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/solution/90-zi-ji-ii-by-alexer-660/)
14 | #### 解法:递归回溯
15 | + 代码写法
16 | + [参看各类算法模板 - 递归一节 - Python&Java版](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
17 | ```javascript
18 | /**
19 | * @param {number[]} nums
20 | * @return {number[][]}
21 | */
22 | var permute = function(nums) {
23 | let n = nums.length;
24 | let res = [];
25 | let tmpPath = [];
26 | let backtrack = (tmpPath) => {
27 | if(tmpPath.length == n){
28 | res.push(tmpPath);
29 | return;
30 | }
31 | for(let i = 0;i < n;i++){
32 | if(!tmpPath.includes(nums[i])){
33 | tmpPath.push(nums[i]);
34 | backtrack(tmpPath.slice());
35 | tmpPath.pop();
36 | }
37 | }
38 | }
39 | backtrack(tmpPath);
40 | return res;
41 | };
42 | ```
--------------------------------------------------------------------------------
/leetCode-0-013-roman-to-integer.md:
--------------------------------------------------------------------------------
1 | # 罗马数字转整数(简单)
2 | # 题目描述
3 |
4 | 
5 | # 题目地址
6 |
7 | >给定一个罗马数字,将其转为整数,输入的数字在1到3999范围内。
8 | 已知:罗马数字包含以下七种字符:I,V,X,L,C,D,M
9 | ```javascript
10 | 转换表为:
11 | 字符 数值 进位
12 | I 1 个位值为1
13 | V 5 个位值为5
14 | X 10 十位值为1
15 | L 50 十位值为5
16 | C 100 百位值为1
17 | D 500 百位值为5
18 | M 1000 千位值为1
19 | ```
20 |
21 | + 由我12题的分析思路直接摘抄如下
22 | + 由罗马数字表示规则可知
23 | + 一般情况下 数字组成:大的数字位+小的数字位 ---- <1>
24 | + 例如:6:VI 7:VII 8:VIII
25 | + 可知:数值大小 == 大的数字位+小的数字位 ---- 《1》
26 | + 再如:1:I 2:II 3: III
27 | + 但当罗马数字进位值(个、十、百、千)== 9 或 == 4 时,数字组成变为:小的数字+大的数字 ---- <2>
28 | ```javascript
29 | 例如:4:IV 9:IX
30 | 40:XL 90:XC
31 | 400:CD 900:CM
32 | 可知:数值大小 == 大的数字位-小的数字位 ---- 《2》
33 | ```
34 | + 由以上分析可知
35 | + 《1》 即 左边的罗马数字 > 右边的罗马数字时 => 罗马数 == 左边罗马数字对应的阿拉伯数字 + 右边罗马数字对应的阿拉伯数字
36 | + 《2》 即 左边的罗马数字 < 右边的罗马数字时 => 罗马数 == 左边罗马数字对应的阿拉伯数字的反数即负数 + 右边罗马数字对应的阿拉伯数字
37 | + 且 罗马数字的转换表在上意味着 所有数字都可以有其中的罗马数字字符组成 => 建立罗马数字和阿拉伯数字 Hash对照表
38 | + 遍历罗马数字 因为转换表里对应的阿拉伯数字带有千、百、十和个位 所以 不需考虑9,99,999临界位 直接求出总和即可
39 |
40 | + 代码
41 | ```javascript
42 | /**
43 | * @param {string} s
44 | * @return {number}
45 | */
46 | var romanToInt = function(s) {
47 | var hashNum = {
48 | "I":1,
49 | "V":5,
50 | "X":10,
51 | "L":50,
52 | "C":100,
53 | "D":500,
54 | "M":1000
55 | }
56 | var result = 0;
57 | for(let i = 0;i
6 | ## 各种算法模板
7 | + [算法模板](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
8 | #### 解法:二分查找
9 | ```javascript
10 | /**
11 | * @param {number[]} nums
12 | * @param {number} target
13 | * @return {number[]}
14 | */
15 | var searchRange = function(nums, target) {
16 | let n = nums.length;
17 | if(n == 0) return [-1,-1];
18 | let leftBound = () => {
19 | let left = 0;
20 | let right = n;
21 | while(left < right){
22 | let mid = (left + right) >> 1;
23 | if(nums[mid] == target){
24 | right = mid;
25 | }else if(nums[mid] < target){
26 | left = mid + 1;
27 | }else if(nums[mid] > target){
28 | right = mid;
29 | }
30 | }
31 | if(nums[left] != target) return -1;
32 | return left;
33 | }
34 | let rightBound = () => {
35 | let left = 0;
36 | let right = n;
37 | while(left < right){
38 | let mid = (left + right) >> 1;
39 | if(nums[mid] == target){
40 | left = mid + 1;
41 | }else if(nums[mid] < target){
42 | left = mid + 1;
43 | }else if(nums[mid] > target){
44 | right = mid;
45 | }
46 | }
47 | return left - 1;
48 | }
49 | let left = leftBound();
50 | if(left == -1) return [-1,-1];
51 | let right = rightBound();
52 | return [left,right];
53 | };
54 | ```
--------------------------------------------------------------------------------
/leetCode-0-058-length-of-last-word.md:
--------------------------------------------------------------------------------
1 | # 最后一个单词的长度(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:库函数 A
7 | + 首尾去空
8 | + 长度为0,则不存在
9 | + 找到最后一个空字符的位置,slice之后的即为最后一个单词
10 | ```javascript
11 | /**
12 | * @param {string} s
13 | * @return {number}
14 | */
15 | var lengthOfLastWord = function(s) {
16 | let str = s.trim();
17 | if(str.length == 0){
18 | return 0;
19 | }
20 | return str.slice(str.lastIndexOf(' ')+1).length;
21 | };
22 | ```
23 | #### 解法二:库函数 B
24 | ```javascript
25 | /**
26 | * @param {string} s
27 | * @return {number}
28 | */
29 | var lengthOfLastWord = function(s) {
30 | let str = s.trim().split(' ');
31 | return str.length > 0 ? str[str.length-1].length : 0;
32 | };
33 | ```
34 | #### 解法三:库函数 C
35 | ```javascript
36 | /**
37 | * @param {string} s
38 | * @return {number}
39 | */
40 | var lengthOfLastWord = function(s) {
41 | return s.trim().split(' ').pop().length;
42 | };
43 | ```
44 | #### 解法四:遍历
45 | + 从右往左最后一个字符位置开始,求最后一个单词的开头和结尾字符索引位置,相减即为所求
46 | + 结尾字符记录索引为end,初始化为字符串结尾字符索引
47 | + 遇到空字符,end--
48 | + 否则
49 | + end < 0,越界,字符串为空或不存在,返回0
50 | + end > 0
51 | + 继续end--判断字符串是否为空
52 | + 不为空,则说明找到了最后一个单词的结尾字符索引位置
53 | + 开头字符初始化为 start = end,依旧向前遍历
54 | + 不为空,则 end--
55 | + 为空,则说明找到了最后一个单词的首字符位置 start
56 | + 结果
57 | + end - start
58 | ```javascript
59 | /**
60 | * @param {string} s
61 | * @return {number}
62 | */
63 | var lengthOfLastWord = function(s) {
64 | let end = s.length - 1;
65 | while(end >= 0 && s[end] == ' '){
66 | end--;
67 | }
68 | if(end < 0){
69 | return 0;
70 | }
71 | let start = end;
72 | while(start >=0 && s[start] != ' '){
73 | start--;
74 | }
75 | return end - start;
76 | };
77 | ```
--------------------------------------------------------------------------------
/leetCode-0-012-integer-to-roman.md:
--------------------------------------------------------------------------------
1 | # 整数转罗马数字(中等)
2 | # 题目描述
3 |
4 | 
5 | # 题目地址
6 |
7 |
8 | > 给定一个整数,将其转为罗马数字,输入数字在1到3999范围内。
9 | > 已知:罗马数字包含以下七种字符:I,V,X,L,C,D,M
10 | ```javascript
11 | //转换表为:
12 | 字符 数值 进位
13 | I 1 个位值为1
14 | V 5 个位值为5
15 | X 10 十位值为1
16 | L 50 十位值为5
17 | C 100 百位值为1
18 | D 500 百位值为5
19 | M 1000 千位值为1
20 | ```
21 | #### 由罗马数字表示规则可知
22 | + 一般情况下 数字组成:大的数字位+小的数字位 ---- <1>
23 | + 例如:6:VI 7:VII 8:VIII
24 | + 可知:数值大小 == 大的数字位+小的数字位
25 | + 再如:1:I 2:II 3: III
26 | + 但当罗马数字进位值(个、十、百、千)== 9 或 == 4 时,数字组成变为:小的数字+大的数字 ---- <2>
27 | ```javascript
28 | // 例如:
29 | 4:IV 9:IX
30 | 40:XL 90:XC
31 | 400:CD 900:CM
32 | ```
33 | + 可知:数值大小 == 大的数字位-小的数字位
34 | + 由上述可以推出罗马数字每个进位值1-9的表示法:
35 | + 个位【1-9】:I、II、III、IV、V、VI、VII、VIII、IX (对应阿拉伯数字 X%10)
36 | + 十位【1-9】:X、XX、XXX、XL、L、LX、LXX、LXXX、XC (对应阿拉伯数字 (X%100)/10)
37 | + 百位【1-9】:C、CC、CCC、CD、D、DC、DCC、DCCC、CM (对应阿拉伯数字 (X%1000)/100)
38 | + 千位【1-9】:M、MM、MMM (对应阿拉伯数字 X/1000)
39 |
40 | + 由以上可知一个阿拉伯数字 => 罗马数字
41 | + num => num/1000 + (num%1000)/100 +(num%100)/10 + num%10
42 |
43 | #### 代码
44 | ```javascript
45 | /**
46 | * @param {number} num
47 | * @return {string}
48 | */
49 | var intToRoman = function(num) {
50 | var Q = ["", "M", "MM", "MMM"];
51 | var B = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"];
52 | var S = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
53 | var G = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
54 | return Q[Math.floor(num/1000)] + B[Math.floor((num%1000)/100)] + S[Math.floor((num%100)/10)] + G[num%10];
55 | };
56 | ```
--------------------------------------------------------------------------------
/leetCode-0-036-valid-sudoku.md:
--------------------------------------------------------------------------------
1 | # 有效的数独(中等)
2 | # 题目描述
3 | 
4 | 
5 | 
6 | # 题目地址
7 |
8 | #### 解法一:哈希判重
9 | + 行
10 | + 当前行9个数字不能有重复数字
11 | + 列
12 | + 当前列9个数字不能有重复数字
13 | + 九宫格
14 | + 当前子数独内没有重复数字
15 | + 9*9的数独划分为9个小的子数独
16 | + boxIndex = Math.floor(row/3) * 3 + Math.floor(columns/3)
17 | + 借用官方图
18 | + 
19 | ```javascript
20 | /**
21 | * @param {character[][]} board
22 | * @return {boolean}
23 | */
24 | var isValidSudoku = function(board) {
25 | // 三个方向判重
26 | let rows = {};
27 | let columns = {};
28 | let boxes = {};
29 | // 遍历数独
30 | for(let i = 0;i < 9;i++){
31 | for(let j = 0;j < 9;j++){
32 | let num = board[i][j];
33 | if(num != '.'){
34 | // 子数独序号
35 | let boxIndex = parseInt((i/3)) * 3 + parseInt(j/3);
36 | if(rows[i+'-'+num] || columns[j+'-'+num] || boxes[boxIndex+'-'+num]){
37 | return false;
38 | }
39 | // 以各自方向 + 不能出现重复的数字 组成唯一键值,若出现第二次,即为重复
40 | rows[i+'-'+num] = true;
41 | columns[j+'-'+num] = true;
42 | boxes[boxIndex+'-'+num] = true;
43 | }
44 | }
45 | }
46 | return true;
47 | };
48 | ```
49 | #### 解法二:位运算
50 | + 待更
--------------------------------------------------------------------------------
/leetCode-1-1122-relative-sort-array.md:
--------------------------------------------------------------------------------
1 | # 数组的相对排序(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:计数排序 A
7 | + 将输入的数据值转化为键存储在额外开辟的数组空间中;
8 | + 然后依次把计数大于 1 的填充回原数组
9 | + 此题填充过程中有所不同
10 | + 先把arr2出现的元素对应的键值依次取出
11 | + 最后把计数数组里剩余的数据依次取出
12 | ```javascript
13 | /**
14 | * @param {number[]} arr1
15 | * @param {number[]} arr2
16 | * @return {number[]}
17 | */
18 | var relativeSortArray = function(arr1, arr2) {
19 | let maxValue = Math.max(...arr1);
20 | let bucket = new Array(maxValue+1).fill(0);
21 | let result = [];
22 | for(let i = 0;i < arr1.length;i++){
23 | bucket[arr1[i]]++;
24 | }
25 | for(let j = 0;j < arr2.length;j++){
26 | while(bucket[arr2[j]] > 0){
27 | result.push(arr2[j]);
28 | bucket[arr2[j]]--;
29 | }
30 | }
31 | for(let r = 0;r <= maxValue;r++){
32 | while(bucket[r] > 0){
33 | result.push(r);
34 | bucket[r]--;
35 | }
36 | }
37 | return result;
38 | };
39 | ```
40 | #### 解法二:计数排序 B
41 | ```javascript
42 | /**
43 | * @param {number[]} arr1
44 | * @param {number[]} arr2
45 | * @return {number[]}
46 | */
47 | var relativeSortArray = function(arr1, arr2) {
48 | let bucket = {};
49 | let result = [];
50 | for(let i = 0;i < arr1.length;i++){
51 | if(bucket[arr1[i]]){
52 | bucket[arr1[i]]++;
53 | }else{
54 | bucket[arr1[i]] = 1;
55 | }
56 | }
57 | for(let j = 0;j < arr2.length;j++){
58 | while(bucket[arr2[j]]){
59 | result.push(arr2[j]);
60 | bucket[arr2[j]]--;
61 | }
62 | }
63 | for(let key in bucket){
64 | while(bucket[key]){
65 | result.push(key);
66 | bucket[key]--;
67 | }
68 | }
69 | return result;
70 | };
71 | ```
--------------------------------------------------------------------------------
/leetCode-0-018-4sum.md:
--------------------------------------------------------------------------------
1 | # 四数之和(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:双指针
7 | + 类似题型解法
8 | + [16. 最接近的三数之和](https://leetcode-cn.com/problems/3sum-closest/solution/16-zui-jie-jin-de-san-shu-zhi-he-by-alexer-660/)
9 | + 思路
10 | + 与16题类似,16是固定第一个数,另外两个数用左右指针
11 | + 此题固定两个数,另外两个数同样用移动的双指针寻找,更新比较与 target 的大小
12 | + 固定的两个数用双循环
13 | + 注意剪枝和去重
14 | ```javascript
15 | /**
16 | * @param {number[]} nums
17 | * @param {number} target
18 | * @return {number[][]}
19 | */
20 | var fourSum = function(nums, target) {
21 | let res = [];
22 | nums.sort((a,b) => a - b);
23 | let n = nums.length;
24 | for(let i = 0;i < n - 3;i++){
25 | if(i > 0 && nums[i] === nums[i-1]) continue;
26 | if(nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break;
27 | if(nums[i] + nums[n-1] + nums[n-2] + nums[n-3] < target) continue;
28 | for(let j = i + 1;j < n - 2;j++){
29 | if(j - i > 1 && nums[j] === nums[j-1]) continue;
30 | if(nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) break;
31 | if(nums[i] + nums[j] + nums[n-1] + nums[n-2] < target) continue;
32 | let left = j + 1;
33 | let right = n - 1;
34 | while(left < right){
35 | let tmpRes = nums[i] + nums[j] + nums[left] + nums[right];
36 | if(tmpRes === target){
37 | res.push([nums[i],nums[j],nums[left],nums[right]]);
38 | while(left < right && nums[left] === nums[left + 1]) left++;
39 | while(left < right && nums[right] === nums[right - 1]) right--;
40 | left++;
41 | right--;
42 | }else if(tmpRes > target){
43 | right--;
44 | }else{
45 | left++;
46 | }
47 | }
48 | }
49 | }
50 | return res;
51 | };
52 | ```
--------------------------------------------------------------------------------
/leetCode-0-009-palindrome-number.md:
--------------------------------------------------------------------------------
1 | # 回文数(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:字符串反转法
7 | ```javascript
8 | /**
9 | * @param {number} x
10 | * @return {boolean}
11 | */
12 | var isPalindrome = function(x) {
13 | if(x<0){
14 | return false;
15 | }else if(String.prototype.split.call(x,'').reverse().join('')!=x){
16 | return false;
17 | }
18 | return true;
19 | }
20 | ```
21 | #### 解法二:中心扩展法
22 | ```javascript
23 | /**
24 | * @param {number} x
25 | * @return {boolean}
26 | */
27 | var isPalindrome = function(x) {
28 | let str = x.toString();
29 | let n = str.length;
30 | let mid = n >> 1;
31 | let left = mid-1;
32 | let right = (n & 1) == 0 ? mid : mid + 1;
33 | while(left>=0 && right 是回文数
48 | ```javascript
49 | var isPalindrome = function(x) {
50 | if(x<0 || (x%10 == 0 && x!=0)){
51 | return false;
52 | }
53 | var reverseNumber = 0;
54 | while(x>reverseNumber){
55 | reverseNumber = reverseNumber*10 + x%10;
56 | x = parseInt(x/10);
57 | }
58 | return x == reverseNumber || x == parseInt(reverseNumber/10)
59 | };
60 | ```
61 | #### 解法四:双指针夹逼法
62 | ```javascript
63 | /**
64 | * @param {number} x
65 | * @return {boolean}
66 | */
67 | var isPalindrome = function(x) {
68 | let str = x.toString();
69 | let n = str.length;
70 | let left = 0;
71 | let right = n-1;
72 | while(left < right){
73 | if(str[left++] != str[right--]){
74 | return false;
75 | }
76 | }
77 | return true;
78 | }
79 | ```
--------------------------------------------------------------------------------
/leetCode-1-0718-maximum-length-of-repeated-subarray.md:
--------------------------------------------------------------------------------
1 | # 最长重复子数组(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:动态规划
7 | + 状态定义
8 | + dp[i][j]:为A[i:]和B[j:]最长公共前缀
9 | + 转移方程
10 | + A[i] == B[j]时
11 | + dp[i][j] = dp[i-1][j-1] + 1;
12 | + A[i] != B[j]时
13 | + dp[i][j] = 0;
14 | ```javascript
15 | /**
16 | * @param {number[]} A
17 | * @param {number[]} B
18 | * @return {number}
19 | */
20 | var findLength = function(A, B) {
21 | let resMax = 0;
22 | let n1 = A.length,n2 = B.length;
23 | let dp = Array.from(new Array(n1 + 1),() => new Array(n2 + 1).fill(0));
24 | for(let i = 1;i <= n1;i++){
25 | for(let j = 1;j <= n2;j++){
26 | if(A[i - 1] === B[j - 1]){
27 | dp[i][j] = dp[i-1][j-1] + 1;
28 | resMax = Math.max(resMax,dp[i][j]);
29 | }
30 | }
31 | }
32 | return resMax;
33 | };
34 | ```
35 | #### 解法二:遍历矩阵
36 | + [参考这里](https://leetcode.com/problems/maximum-length-of-repeated-subarray/discuss/109040/Java-O(mn)-time-O(1)-space)
37 | ```javascript
38 | /**
39 | * @param {number[]} A
40 | * @param {number[]} B
41 | * @return {number}
42 | */
43 | var findLength = function(A, B) {
44 | let resMax = 0,match = 0;
45 | let n1 = A.length,n2 = B.length;
46 | for(let j = 0;j < n2;j++){
47 | match = 0;
48 | for(let i = 0,k = j;i < n1 && k < n2;i++,k++){
49 | if(A[i] != B[k]) match = 0;
50 | else{
51 | match++;
52 | resMax = Math.max(resMax,match);
53 | }
54 | }
55 | }
56 | for(let i = 1;i < n1;i++){
57 | match = 0;
58 | for(let j = 0,k = i;k < n1 && j < n2;j++,k++){
59 | if(A[k] != B[j]) match = 0;
60 | else{
61 | match++;
62 | resMax = Math.max(resMax,match);
63 | }
64 | }
65 | }
66 | return resMax;
67 | };
68 | ```
--------------------------------------------------------------------------------
/leetCode-0-066-plus-one.md:
--------------------------------------------------------------------------------
1 | # 加一(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | + 思路
7 | + 一个整数加1,无论进不进位,都是从尾部开始加1的
8 | + 不进位
9 | + 末尾加1 < 10,加后直接返回,对应数组末尾元素+1即可
10 | + 进位
11 | + 对需要进位的原数组中的位的数修改值为0
12 | + 进位位数 < 整数位数
13 | + 数组相应位置元素+1即可
14 | + 何时+1?
15 | + ==9
16 | + 即加1后模10为0,说明原位数字为9
17 | + 进位位数 = 整数位数
18 | + 例如9,99,999,9999
19 | + 此时直接变为10,100,1000,10000即可
20 | + 在遍历中修改
21 | + 判断是否能遍历到整数的位数,如果能,原数组直接左边插入1个1即可
22 | + 在结果中修改
23 | + 直接重新新建一个原数组长度加1长度的新数组,设置首元素为1,其余元素为0,返回新数组即可
24 | #### 解法一:
25 | ```javascript
26 | /**
27 | * @param {number[]} digits
28 | * @return {number[]}
29 | */
30 | var plusOne = function(digits) {
31 | for(let i = digits.length - 1;i >= 0;i--){
32 | digits[i]++;
33 | digits[i] = digits[i]%10;
34 | if(digits[i] != 0) return digits;
35 | }
36 | digits = new Array(digits.length+1).fill(0);
37 | digits[0] = 1;
38 | return digits;
39 | };
40 | ```
41 | #### 解法二:
42 | ```javascript
43 | /**
44 | * @param {number[]} digits
45 | * @return {number[]}
46 | */
47 | var plusOne = function(digits) {
48 | for(let i = digits.length - 1;i >= 0;i--){
49 | if(digits[i] == 9){
50 | digits[i] = 0;
51 | }else{
52 | digits[i]++;
53 | return digits;
54 | }
55 | }
56 | digits.unshift(1);
57 | return digits;
58 | };
59 | ```
60 | #### 解法三:
61 | ```javascript
62 | /**
63 | * @param {number[]} digits
64 | * @return {number[]}
65 | */
66 | var plusOne = function(digits) {
67 | let n =digits.length;
68 | digits[n-1]++;
69 | for(let i = digits.length - 1;i >= 0;i--){
70 | if(digits[i] == 10){
71 | digits[i] = 0;
72 | if(i == 0){
73 | digits.unshift(1);
74 | }else{
75 | digits[i-1]++;
76 | }
77 | }
78 | }
79 | return digits;
80 | };
81 | ```
--------------------------------------------------------------------------------
/leetCode-0-049-group-anagrams.md:
--------------------------------------------------------------------------------
1 | # 字母异位词分组(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:暴力排序 sort
7 | + [242题的变种-戳看解法一](https://leetcode-cn.com/problems/valid-anagram/solution/242-you-xiao-de-zi-mu-yi-wei-ci-by-alexer-660/)
8 | ```javascript
9 | /**
10 | * @param {string[]} strs
11 | * @return {string[][]}
12 | */
13 | var groupAnagrams = function(strs) {
14 | var tmpCode = '';
15 | var hashMap = {};
16 | var result = [];
17 | for(var i = 0;i
7 | #### 解法:动态规划
8 | + 类似题型
9 | + [72. 编辑距离](https://leetcode-cn.com/problems/edit-distance/solution/72-bian-ji-ju-chi-by-alexer-660/)
10 | + 思路
11 | + 状态定义
12 | + dp[i][j]:代表 T 前 i 字符串可以由 S 前 j 字符串组成最多个数
13 | + 定义缘由
14 | + 由题意可知,从 S 中可以找出 n个 T字符串,通过摘取 S中n个字符组成,且这n个字符取出的顺序要和 T相同,且绝对位置不能变即从左到右,可以跳着取
15 | + S 字符串的长度 大于等于 T字符串
16 | + **说明 S 中包含 n 个 T子序列**
17 | + **当T长度为1时,最小子序列为一个字符,此为递推关键**
18 | + 可以从S中**删除或不删除n个字符**,以拼接为T的样子
19 | + 状态方程
20 | + 那么从1开始遍历 T
21 | + 当 **T[i] == S[j]** 时
22 | + **dp[i][j] = dp[i-1][j-1]**
23 | + 说明当前位置的两个字符相同,所以两边同时可以减少1位继续比较
24 | + 相当于同时删除一个相同最小子序列字符
25 | + **dp[i][j] = dp[i][j-1]**
26 | + 或者将大串S减少一位继续比较,相当于子序列个数少算一个
27 | + 因为S 中可以包含 n 个 T子序列,那么倒退回去也是正确的,直到只剩一个子序列
28 | + 相当于S 删除一个不同的最小子序列字符
29 | + 当 **T[i] != S[j]** 时
30 | + **dp[i][j] = dp[i][j-1]**
31 | + 同上,S中前i位置不符合,则说明要继续后退,直到上面找到一个相同的子序列
32 | + 相当于S 删除一个不同的最小子序列字符
33 | + 初始化
34 | + 当T为空时,S中无论有多少个字符
35 | + 无论遍历或者从0到i位置中可能截取几个字符,只能都删除掉成为空,才能与空字符表示匹配
36 | + 子序列个数为1
37 | + **此为递推起点值**
38 | ```javascript
39 | /**
40 | * @param {string} s
41 | * @param {string} t
42 | * @return {number}
43 | */
44 | var numDistinct = function(s, t) {
45 | let n = s.length;
46 | let m = t.length;
47 | let dp = Array.from(new Array(m+1),() => new Array(n+1).fill(0));
48 | for(let i = 0;i <= n;i++){
49 | dp[0][i] = 1;
50 | }
51 | for(let i = 1;i <= m;i++){
52 | for(let j = 1;j <= n;j++){
53 | if(t[i-1] == s[j-1]){
54 | dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
55 | }else{
56 | dp[i][j] = dp[i][j-1];
57 | }
58 | }
59 | }
60 | return dp[m][n];
61 | };
62 | ```
--------------------------------------------------------------------------------
/leetCode-1-0107-binary-tree-level-order-traversal-ii.md:
--------------------------------------------------------------------------------
1 | # 二叉树的层次遍历 II(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:DFS
7 | + 类似题型
8 | + [102. 二叉树的层序遍历 - 解法二](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/102-er-cha-shu-de-ceng-ci-bian-li-by-alexer-660/)
9 | ```javascript
10 | /**
11 | * Definition for a binary tree node.
12 | * function TreeNode(val) {
13 | * this.val = val;
14 | * this.left = this.right = null;
15 | * }
16 | */
17 | /**
18 | * @param {TreeNode} root
19 | * @return {number[][]}
20 | */
21 | var levelOrderBottom = function(root) {
22 | if(!root || root.length === 0) {
23 | return []
24 | }
25 | let res = []
26 | let dfs = (curr,lev) => {
27 | if(curr) {
28 | !res[lev] && (res[lev] = [])
29 | res[lev].push(curr.val)
30 | if(curr.left) dfs(curr.left,lev+1)
31 | if(curr.right) dfs(curr.right,lev+1)
32 | }
33 | }
34 | dfs(root,0)
35 | return res.reverse()
36 | };
37 | ```
38 | #### 解法二:BFS
39 | + 类似题型
40 | + [102. 二叉树的层序遍历 - 解法一](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/102-er-cha-shu-de-ceng-ci-bian-li-by-alexer-660/)
41 | ```javascript
42 | /**
43 | * Definition for a binary tree node.
44 | * function TreeNode(val) {
45 | * this.val = val;
46 | * this.left = this.right = null;
47 | * }
48 | */
49 | /**
50 | * @param {TreeNode} root
51 | * @return {number[][]}
52 | */
53 | var levelOrderBottom = function(root) {
54 | if(!root || root.length === 0) {
55 | return []
56 | }
57 | let res = []
58 | let currNodes = [root]
59 | while(currNodes.length !== 0) {
60 | let subRes = []
61 | let nextSubRes = []
62 | for(let i = 0;i < currNodes.length;i++) {
63 | subRes.push(currNodes[i].val)
64 | if(currNodes[i].left) nextSubRes.push(currNodes[i].left)
65 | if(currNodes[i].right) nextSubRes.push(currNodes[i].right)
66 | }
67 | res.push(subRes)
68 | currNodes = nextSubRes
69 | }
70 | return res.reverse()
71 | };
72 | ```
--------------------------------------------------------------------------------
/leetCode-0-074-search-a-2d-matrix.md:
--------------------------------------------------------------------------------
1 | # 搜索二维矩阵(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 解法:二分查找法
7 | + 时间复杂度:O(log(mn))
8 | + 空间复杂度:O(1)
9 | + 由题意可得,在一个有序的二维数组中查找一个值
10 | + 类似在一维数组中查找一个值
11 | + 这类题基本可以用二分查找法
12 | + [戳看此法用到的题](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/33-sou-suo-xuan-zhuan-pai-xu-shu-zu-by-alexer-660/)
13 | + 本题关键在于两点
14 | + 想到用二分查找法
15 | + 模板
16 | ```java
17 | public int bsearch(int[] a, int n, int value) {
18 | int low = 0;
19 | int high = n - 1;
20 |
21 | while (low <= high) {
22 | int mid = (low + high) / 2;
23 | if (a[mid] == value) {
24 | return mid;
25 | } else if (a[mid] < value) {
26 | low = mid + 1;
27 | } else {
28 | high = mid - 1;
29 | }
30 | }
31 |
32 | return -1;
33 | }
34 | ```
35 | + 怎么求二维数组的中间值
36 | + 试想二维数组被压缩成一维数组后依旧是有序
37 | + 那么mid = (0+matrix.length-1)/2 = (low + high)/2
38 | + 中间值即为matrix[mid]
39 | + 而此处是二维,
40 | + 设m= matrix.length、n = matrix[0].length;
41 | + 所以low = 0,high = matrix.length * matrix[0].length - 1 = m * n -1
42 | + 中间值索引为 (low+high)/2
43 | + 中间值为 matrix[mid/n][mid%n]
44 | + 解题技巧
45 | + n除以2^k可以换成位运算,提升代码性能
46 | + n>>k
47 | ```javascript
48 | /**
49 | * @param {number[][]} matrix
50 | * @param {number} target
51 | * @return {boolean}
52 | */
53 | var searchMatrix = function(matrix, target) {
54 | var m = matrix.length;
55 | if(m == 0)return false;
56 | var n = matrix[0].length;
57 | var low = 0;
58 | var high = m*n - 1;
59 | while(low<=high){
60 | var mid = (low+high)>>1;
61 | var row = parseInt(mid/n);
62 | var col = mid%n;
63 | var matrixMid = matrix[row][col];
64 | if(matrixMid < target){
65 | low = mid + 1;
66 | }else if(matrixMid > target){
67 | high = mid -1;
68 | }else if(matrixMid == target){
69 | return true;
70 | }
71 | }
72 | return false;
73 | };
74 | ```
--------------------------------------------------------------------------------
/leetCode-0-006-zigzag-conversion.md:
--------------------------------------------------------------------------------
1 | # Z 字形变换(中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | ```javascript
8 | //Input => s = L E E T C O D E I S H I R I N G
9 | //=> j = 3 ,len_s = 16
10 | i 0 1 2 3 4 5 6 7
11 | j
12 | 0 L C I R
13 | 1 E T O E S I I G
14 | 2 E D H N
15 | //Output => L C I R E T O E S I I G E D H N
16 | ```
17 | ```javascript
18 | //Input => s = L E E T C O D E I S H I R I N G
19 | //=> j = 4 ,len_s = 16
20 | i 0 1 2 3 4 5 6
21 | j
22 | 0 L D R
23 | 1 E O E I I
24 | 2 E C I H N
25 | 3 T S G
26 | //Output => L D R E O E I I E C I H N T S G
27 | ```
28 | ```javascript
29 | //Input => s = A B K D E I J P A K B C D E J O
30 | //=> numRows = 5 ,len_s = 16
31 | i 0 1 2 3 4 5 6 7
32 | j
33 | 0 A A
34 | 1 B P K O
35 | 2 K J B J
36 | 3 D I C E
37 | 4 E D
38 | //Output => A A B P K O K J B J D I C E E D
39 | ```
40 | ```javascript
41 | //Intput => A B C D
42 | //=> numRows = 2
43 |
44 | i 0 1 2
45 | j
46 | 0 A C
47 | 1 B D
48 | //Output => A C B D
49 | ```
50 | #### 解法:
51 | + 时间复杂度O(n)
52 | + 输入原str
53 | + 从上往下 && 从左往右 改造 原str
54 | + 从左往右 && 从上往下 输出 新str
55 | ```javascript
56 | /**
57 | * @param {string} s
58 | * @param {number} numRows
59 | * @return {string}
60 | */
61 | var convert = function(s, numRows) {
62 | if(numRows ==1){
63 | return s;
64 | }
65 | var result = Array(numRows);
66 | var resultStr = '';
67 | var len = s.length;
68 | for(var i=0;i=numRows){
81 | j-=2;
82 | j==0 ? needDown=true : needDown = false;
83 | }else if(j==0){
84 | needDown = true;
85 | }
86 | }
87 | // console.log(result)
88 | for(var i=0;i
6 | #### 各类算法模板
7 | + [递归算法模板](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
8 | #### 解法一:双指针法
9 | + 时间复杂度:O(a+b) 循环比较两个子问题的次数为 a+b a,b为两个子问题的长度
10 | + 空间复杂度:O(1) 双指针,常数级别复杂度
11 | ```javascript
12 | /**
13 | * Definition for singly-linked list.
14 | * function ListNode(val) {
15 | * this.val = val;
16 | * this.next = null;
17 | * }
18 | */
19 | /**
20 | * @param {ListNode} l1
21 | * @param {ListNode} l2
22 | * @return {ListNode}
23 | */
24 | var mergeTwoLists = function(l1, l2) {
25 | var prevHead = new ListNode(-1);
26 | var prevNode = prevHead;
27 | while (l1 != null && l2 != null) {
28 | if(l1.val <= l2.val){
29 | prevNode.next = l1;
30 | l1 = l1.next
31 | }else{
32 | prevNode.next = l2;
33 | l2 = l2.next;
34 | }
35 | prevNode = prevNode.next;
36 | }
37 | prevNode.next = l1 ? l1 :l2;
38 | return prevHead.next;
39 | };
40 | ```
41 | #### 解法二:递归
42 | + 时间复杂度:O(n)(n为l1和l2的每个元素的遍历次数和)
43 | + 空间复杂度:O(n)(n为l1和l2的空间和)
44 | + 编程技巧:递归 + 原地斩链相连
45 | + 递归比较查看两个链表哪个元素先小 就斩断此元素位置链条⛓️连接到另一链表上 如此也不需要另外开辟存储空间
46 | + 斩断后 重连铁链的动作因为要自动非人工 所以需要程序自己调用自己 即为递归
47 | + 斩断后需要连的结点 通过 return 最小结点 即动态更新 斩断结点位置
48 | + 随时连接下一个符合要求的位置(x.next = 求下一个需要连接的结点位置(即程序自动搜索即递归) && x = 下一个需要连接的结点位置)
49 | + 返回修改后的 l1头结点的链表 或 l2头结点的链表
50 | ```javascript
51 | /**
52 | * Definition for singly-linked list.
53 | * function ListNode(val) {
54 | * this.val = val;
55 | * this.next = null;
56 | * }
57 | */
58 | /**
59 | * @param {ListNode} l1
60 | * @param {ListNode} l2
61 | * @return {ListNode}
62 | */
63 | var mergeTwoLists = function(l1, l2) {
64 | if(l1 == null){
65 | return l2;
66 | }
67 | if(l2 == null){
68 | return l1;
69 | }
70 | if(l1.val <= l2.val){
71 | l1.next = mergeTwoLists(l1.next,l2);
72 | return l1;
73 | }else{
74 | l2.next = mergeTwoLists(l1,l2.next);
75 | return l2;
76 | }
77 | }
78 | ```
--------------------------------------------------------------------------------
/leetCode-0-098-validate-binary-search-tree.md:
--------------------------------------------------------------------------------
1 | # 验证二叉搜索树(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:二叉树的中序遍历基于栈
7 | + [戳看94.二叉树的中序遍历题解](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/94-er-cha-shu-de-zhong-xu-bian-li-by-alexer-660/)
8 | + 中序遍历又叫升序遍历
9 | + 所以仅需判断当前出栈的节点与上一个节点的大小,大于等于则不符合
10 | ```javascript
11 | /**
12 | * Definition for a binary tree node.
13 | * function TreeNode(val) {
14 | * this.val = val;
15 | * this.left = this.right = null;
16 | * }
17 | */
18 | /**
19 | * @param {TreeNode} root
20 | * @return {boolean}
21 | */
22 | var isValidBST = function(root) {
23 | if(root && root.right == null && root.left == null){
24 | return true;
25 | }
26 | var result = [];
27 | var tmpStack = [];
28 | var currNode = root;
29 | var lastNode = '';
30 | while(currNode != null || tmpStack.length != 0){
31 | while(currNode != null){
32 | tmpStack.push(currNode);
33 | currNode = currNode.left;
34 | }
35 | currNode = tmpStack.pop();
36 | if(lastNode!=='' && currNode.val <= lastNode){
37 | return false;
38 | }
39 | lastNode = currNode.val;
40 | currNode = currNode.right;
41 | }
42 | return true;
43 | };
44 | ```
45 | #### 解法二:递归验证
46 | + [戳看94二叉树的中序遍历指解法二](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/94-er-cha-shu-de-zhong-xu-bian-li-by-alexer-660/)
47 | ```javascript
48 | /**
49 | * Definition for a binary tree node.
50 | * function TreeNode(val) {
51 | * this.val = val;
52 | * this.left = this.right = null;
53 | * }
54 | */
55 | /**
56 | * @param {TreeNode} root
57 | * @return {boolean}
58 | */
59 | var isValidBST = function(root) {
60 | var lastNode = '';
61 | function pushRoot(root){
62 | if(root == null){
63 | return true;
64 | }
65 | if(pushRoot(root.left)){
66 | if(lastNode < root.val || lastNode === ''){
67 | lastNode = root.val;
68 | return pushRoot(root.right);
69 | }
70 | }
71 | return false;
72 | }
73 | return pushRoot(root);
74 | };
75 | ```
--------------------------------------------------------------------------------
/leetCode-0-057-insert-interval.md:
--------------------------------------------------------------------------------
1 | # 插入区间(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:合并区间
7 | + [思路完全同 - 56. 合并区间](https://leetcode-cn.com/problems/merge-intervals/solution/56-he-bing-qu-jian-by-alexer-660/)
8 | + 无非是将两个区间合到一起去,换汤不换药。
9 | ```javascript
10 | /**
11 | * @param {number[][]} intervals
12 | * @param {number[]} newInterval
13 | * @return {number[][]}
14 | */
15 | var insert = function(intervals, newInterval) {
16 | let res = [];
17 | intervals.push(newInterval);
18 | let len = intervals.length;
19 | if(len == 0) return [];
20 | intervals.sort((a,b) => a[0] - b[0]);
21 | let i = 0;
22 | while(i < len){
23 | let currLeft = intervals[i][0];
24 | let currRight = intervals[i][1];
25 | while(i < len - 1 && intervals[i+1][0] <= currRight){
26 | i++;
27 | currRight = Math.max(intervals[i][1],currRight);
28 | }
29 | res.push([currLeft,currRight]);
30 | i++;
31 | }
32 | return res;
33 | };
34 | ```
35 | #### 解法二:见缝插针
36 | + 思路
37 | + 在有序区间列表里插入一个区间,取得新区间的左边界newStart,右边界newEnd
38 | + 遍历原区间
39 | + 当newStart 大于 当前区间的右边界时
40 | + 说明两个区间没有交集,不用合并,又因为原区间列表有序
41 | + 所以直接将当前区间排入新的区间列表中
42 | + 否则当newEnd 大于 当前区间的左边界时
43 | + 两个区间有重合
44 | + 所以将两个区间的最小左边界和最大右边界重新组合为新的区间,即为合并
45 | + 将其排入新区间列表中
46 | + 否则当区间列表没有遍历完时
47 | + 将剩下的排入新区间列表中去
48 | ```javascript
49 | /**
50 | * @param {number[][]} intervals
51 | * @param {number[]} newInterval
52 | * @return {number[][]}
53 | */
54 | var insert = function(intervals, newInterval) {
55 | let res = [];
56 | let i = 0;
57 | let n = intervals.length;
58 | let newStart = newInterval[0];
59 | let newEnd = newInterval[1];
60 | while(i < n && newStart > intervals[i][1]){
61 | res.push(intervals[i]);
62 | i++;
63 | }
64 | while(i < n && newEnd >= intervals[i][0]){
65 | newStart = Math.min(newStart,intervals[i][0]);
66 | newEnd = Math.max(newEnd,intervals[i][1]);
67 | i++;
68 | }
69 | res.push([newStart,newEnd]);
70 | while(i < n){
71 | res.push(intervals[i]);
72 | i++;
73 | }
74 | return res;
75 | };
76 | ```
--------------------------------------------------------------------------------
/leetCode-1-0236-lowest-common-ancestor-of-a-binary-tree.md:
--------------------------------------------------------------------------------
1 | # 二叉树的最近公共祖先(中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:递归
8 | + 临界条件:最近公共祖先为根节点
9 | + 根节点是空节点
10 | + 根节点是q节点
11 | + 根节点是p节点
12 | + 根据临界条件
13 | + 此题相当于查找以 root 为根节点的树上是否有p节点或者q节点
14 | + 有,返回p节点或q节点
15 | + 无,返回null
16 | + 求解
17 | + 从左右子树分别进行递归,即查找左右子树上是否有p节点或者q节点
18 | + 左右子树均无p节点或q节点
19 | + 左子树找到,右子树没有找到,返回左子树的查找结果
20 | + 右子树找到,左子树没有找到,返回右子树的查找结果
21 | + 左、右子树均能找到
22 | + 说明此时的p节点和q节点在当前root节点两侧,返回root节点
23 | ```javascript
24 | /**
25 | * Definition for a binary tree node.
26 | * function TreeNode(val) {
27 | * this.val = val;
28 | * this.left = this.right = null;
29 | * }
30 | */
31 | /**
32 | * @param {TreeNode} root
33 | * @param {TreeNode} p
34 | * @param {TreeNode} q
35 | * @return {TreeNode}
36 | */
37 | var lowestCommonAncestor = function(root, p, q) {
38 | if(root == null || root == p || root == q){
39 | return root;
40 | }
41 | let left = lowestCommonAncestor(root.left,p,q);
42 | let right = lowestCommonAncestor(root.right,p,q);
43 | if(left != null && right != null){
44 | return root;
45 | }
46 | return left != null ? left : right;
47 | };
48 | ```
49 | + 或者这样写
50 | ```javascript
51 | /**
52 | * Definition for a binary tree node.
53 | * function TreeNode(val) {
54 | * this.val = val;
55 | * this.left = this.right = null;
56 | * }
57 | */
58 | /**
59 | * @param {TreeNode} root
60 | * @param {TreeNode} p
61 | * @param {TreeNode} q
62 | * @return {TreeNode}
63 | */
64 | var lowestCommonAncestor = function(root, p, q) {
65 | if(root == null || root == p || root == q){
66 | return root;
67 | }
68 | let left = lowestCommonAncestor(root.left,p,q);
69 | let right = lowestCommonAncestor(root.right,p,q);
70 | if (left && right) {
71 | return root;
72 | } else if (left) {
73 | return left;
74 | } else if (right) {
75 | return right;
76 | }
77 | return null;
78 | };
79 | ```
--------------------------------------------------------------------------------
/leetCode-1-0714-best-time-to-buy-and-sell-stock-with-transaction-fee.md:
--------------------------------------------------------------------------------
1 | # 买卖股票的最佳时机含手续费(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ### 股票6道
7 | + 1、[121. 买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
8 | + 2、[122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
9 | + 3、[123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/submissions/)
10 | + 4、[309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/submissions/)
11 | + 5、[188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/submissions/)
12 | + 6、[714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/submissions/)
13 | ## [卍解👇敬请戳看](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E8%82%A1%E7%A5%A86%E9%81%93.md)
14 | ___
15 | #### 解法一:动态规划
16 | ```javascript
17 | /**
18 | * @param {number[]} prices
19 | * @param {number} fee
20 | * @return {number}
21 | */
22 | var maxProfit = function(prices, fee) {
23 | let n = prices.length;
24 | if(n == 0){
25 | return 0;
26 | }
27 | let dp = Array.from(new Array(n),() => new Array(2));
28 | for(let i = 0;i < n;i++){
29 | if(i == 0){
30 | dp[0][0] = Math.max(0,-Infinity+prices[0]);
31 | dp[0][1] = Math.max(-Infinity,0 - prices[0] - fee);
32 | continue;
33 | }
34 | dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]);
35 | dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] - prices[i] - fee);
36 | }
37 | return dp[n-1][0];
38 | };
39 | ```
40 | #### 解法二:动态规划 + 降维
41 | ```javascript
42 | /**
43 | * @param {number[]} prices
44 | * @param {number} fee
45 | * @return {number}
46 | */
47 | var maxProfit = function(prices, fee) {
48 | let n = prices.length;
49 | if(n == 0){
50 | return 0;
51 | }
52 | let dp_i_0 = 0;
53 | let dp_i_1 = -Infinity;
54 | for(let i = 0;i < n;i++){
55 | let tmp = dp_i_0;
56 | dp_i_0 = Math.max(dp_i_0,dp_i_1 + prices[i]);
57 | dp_i_1 = Math.max(dp_i_1,tmp - prices[i] - fee);
58 | }
59 | return dp_i_0;
60 | };
61 | ```
--------------------------------------------------------------------------------
/leetCode-0-088-merge-sorted-array.md:
--------------------------------------------------------------------------------
1 | # 合并两个有序数组(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:双指针 - 从前往后
7 | + 归并排序
8 | + 从小到大按顺序缓存到一个数组中
9 | + nums1按序替换
10 | + 归并排序不太懂的可以看看我的这篇文章
11 | + [排序算法](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/BitOperation%E4%BD%8D%E8%BF%90%E7%AE%97%E3%80%81Bloom%20Filter%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E3%80%81LRU%20Cache%E7%BC%93%E5%AD%98%E3%80%81Sorting%20algorithm%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md#%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95)
12 | ```javascript
13 | /**
14 | * @param {number[]} nums1
15 | * @param {number} m
16 | * @param {number[]} nums2
17 | * @param {number} n
18 | * @return {void} Do not return anything, modify nums1 in-place instead.
19 | */
20 | var merge = function(nums1, m, nums2, n) {
21 | let left = 0;
22 | let right = 0;
23 | let tmp_nums1 = nums1.slice(0,m);
24 | let tmp_nums2 = nums2.slice(0,n);
25 | let result = [];
26 | while(left < m && right < n){
27 | if(tmp_nums1[left] < tmp_nums2[right]){
28 | result.push(tmp_nums1[left]);
29 | left++;
30 | }else{
31 | result.push(tmp_nums2[right]);
32 | right++;
33 | }
34 | }
35 | result = result.concat(tmp_nums1.slice(left)).concat(tmp_nums2.slice(right));
36 | for(let i = 0;i < result.length;i++){
37 | nums1[i] = result[i];
38 | }
39 | };
40 | ```
41 | #### 解法二:双指针 + 从后向前
42 | + 思路
43 | + 两个数组从小到大排序
44 | + 且题目要求 修改nums1为合并排好序的nums1+nums2
45 | + 双指针
46 | + 两个分别指向两个数组尾部的指针
47 | + 从后向前
48 | + 比较两指针位置的值
49 | + 大的一定是结果数组的最大值
50 | + 一一填充到 nums1的末尾
51 | + 遍历完后
52 | + 当 n > 0 时
53 | + 说明 nums2 中还有剩余没有比较的数字
54 | + 将其插入替换 nums1 数组前面n个数字即可
55 | ```javascript
56 | /**
57 | * @param {number[]} nums1
58 | * @param {number} m
59 | * @param {number[]} nums2
60 | * @param {number} n
61 | * @return {void} Do not return anything, modify nums1 in-place instead.
62 | */
63 | var merge = function(nums1, m, nums2, n) {
64 | let count = m + n;
65 | while(m > 0 && n > 0){
66 | nums1[--count] = nums1[m-1] < nums2[n-1] ? nums2[--n] : nums1[--m];
67 | }
68 | if(n > 0){
69 | nums1.splice(0,n,...nums2.slice(0,n));
70 | }
71 | };
72 | ```
--------------------------------------------------------------------------------
/leetCode-0-024-swap-nodes-in-pairs.md:
--------------------------------------------------------------------------------
1 | # 两两交换链表中的节点(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 链表
7 | + [参看链表各种操作大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
8 | #### 解法一:非递归
9 | + 时间复杂度:O(n)
10 | + 空间复杂度:O(1)
11 | + 思路
12 | + 添加一个哨兵节点
13 | + 三个节点外加一个哨兵节点之间作指针指向变换操作
14 | + 图解
15 | + 
16 | ```javascript
17 | /**
18 | * Definition for singly-linked list.
19 | * function ListNode(val) {
20 | * this.val = val;
21 | * this.next = null;
22 | * }
23 | */
24 | /**
25 | * @param {ListNode} head
26 | * @return {ListNode}
27 | */
28 | var swapPairs = function(head) {
29 | let thead = new ListNode(0);
30 | thead.next = head;
31 | let tmp = thead;
32 | while(tmp.next != null && tmp.next.next != null){
33 | let start = tmp.next;
34 | let end = start.next;
35 | tmp.next = end;
36 | start.next = end.next;
37 | end.next = start;
38 | tmp = start;
39 | }
40 | return thead.next;
41 | };
42 | ```
43 | #### 解法二:递归
44 | + 时间复杂度:O(n)
45 | + 空间复杂度:O(n)
46 | + 思路
47 | + 终止
48 | + 同解法一:至少三个节点之间才可以互换
49 | + 只有一个节点或没有节点,返回此节点
50 | + 交换
51 | + 设需要交换的两个节点为head、next
52 | + head -> next -> c -> ...
53 | + head -> c -> ... && next -> head
54 | + next -> head -> c -> ...
55 | + head 连接( -> )后面交换完成的子链表
56 | + next 连接( -> )head 完成交换
57 | + 对子链表重复上述过程即可
58 | ```javascript
59 | /**
60 | * Definition for singly-linked list.
61 | * function ListNode(val) {
62 | * this.val = val;
63 | * this.next = null;
64 | * }
65 | */
66 | /**
67 | * @param {ListNode} head
68 | * @return {ListNode}
69 | */
70 | var swapPairs = function(head) {
71 | if(head == null || head.next == null){
72 | return head;
73 | }
74 | // 获得第 2 个节点
75 | let next = head.next;
76 | // next.next = head.next.next
77 | // 第1个节点指向第 3 个节点,并从第3个节点开始递归
78 | head.next = swapPairs(next.next);
79 | // 第2个节点指向第 1 个节点
80 | next.next = head;
81 | // 或者 [head.next,next.next] = [swapPairs(next.next),head]
82 | return next;
83 | };
84 | ```
--------------------------------------------------------------------------------
/leetCode-1-0159-longest-substring-with-at-most-two-distinct-characters.md:
--------------------------------------------------------------------------------
1 | # 至多包含两个不同字符的最长子串(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口
24 | ```javascript
25 | /**
26 | * @param {string} s
27 | * @return {number}
28 | */
29 | var lengthOfLongestSubstringTwoDistinct = function(s) {
30 | let n = s.length;
31 | if(n < 3) return n;
32 | let left = 0,right = 0;
33 | let windows = {};
34 | let match = 0;
35 | let maxLen = Number.MIN_SAFE_INTEGER;
36 | while(right < n){
37 | let c1 = s[right];
38 | windows[c1] ? windows[c1]++ : (windows[c1] = 1) && match++;
39 | right++;
40 | while(match > 2){
41 | let c2 = s[left];
42 | if(windows[c2] === 1){
43 | match--;
44 | }
45 | windows[c2]--;
46 | left++;
47 | }
48 | maxLen = Math.max(maxLen,right - left);
49 | }
50 | return maxLen;
51 | };
52 | ```
--------------------------------------------------------------------------------
/leetCode-1-0309-best-time-to-buy-and-sell-stock-with-cooldown.md:
--------------------------------------------------------------------------------
1 | # 最佳买卖股票时机含冷冻期(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ### 股票6道
7 | + 1、[121. 买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
8 | + 2、[122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
9 | + 3、[123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/submissions/)
10 | + 4、[309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/submissions/)
11 | + 5、[188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/submissions/)
12 | + 6、[714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/submissions/)
13 | ## [卍解👇敬请戳看](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E8%82%A1%E7%A5%A86%E9%81%93.md)
14 | ___
15 | #### 解法一:动态规划
16 | ```javascript
17 | /**
18 | * @param {number[]} prices
19 | * @return {number}
20 | */
21 | var maxProfit = function(prices) {
22 | let n = prices.length;
23 | if(n == 0){
24 | return 0;
25 | }
26 | let dp = Array.from(new Array(n),() => new Array(2));
27 | for(var i = 0;i < n;i++){
28 | if(i == 0){
29 | dp[0][0] = 0;
30 | dp[0][1] = -prices[i];
31 | continue;
32 | }else if(i == 1){
33 | dp[1][0] = Math.max(dp[0][0],dp[0][1]+prices[i]);
34 | dp[1][1] = Math.max(dp[0][1], - prices[i]);
35 | continue;
36 | }
37 | dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i]);
38 | dp[i][1] = Math.max(dp[i-1][1],dp[i-2][0] - prices[i]);
39 | }
40 | return dp[n-1][0];
41 | };
42 | ```
43 | #### 解法二:动态规划 - 降维
44 | ```javascript
45 | /**
46 | * @param {number[]} prices
47 | * @return {number}
48 | */
49 | var maxProfit = function(prices) {
50 | let n = prices.length;
51 | if(n == 0){
52 | return 0;
53 | }
54 | let dp_i_0 = 0;
55 | let dp_i_1 = -Infinity;
56 | let dp_pre = 0;
57 | for(var i = 0;i < n;i++){
58 | let tmp = dp_i_0;
59 | dp_i_0 = Math.max(dp_i_0,dp_i_1 + prices[i]);
60 | dp_i_1 = Math.max(dp_i_1,dp_pre - prices[i]);
61 | dp_pre = tmp;
62 | }
63 | return dp_i_0;
64 | };
65 | ```
--------------------------------------------------------------------------------
/leetCode-1-0154-find-minimum-in-rotated-sorted-array-ii.md:
--------------------------------------------------------------------------------
1 | # 寻找旋转排序数组中的最小值 II(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 |
7 |
8 | #### 解法:二分查找法
9 | + 相关题型
10 | + [153. 寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/solution/153-xun-zhao-xuan-zhuan-pai-xu-shu-zu-zhong-de-z-4/)
11 | + 分析
12 | + 跟153题解题思路基本一模一样
13 | + 设置low,high左右边界,算出中间数nums[mid]
14 | + 当nums[mid] > nums[high]时,说明出现了无序的地方在右边
15 | + low = mid+1
16 | + 当nums[mid] == nums[high]时,说明遇到了重复元素,无法判断mid在哪一边的排序数组中
17 | + 区别是上一题数组没有重复元素
18 | + 此时我们只有有两种选择,不然还遍不遍历了
19 | + 理论证明
20 | + 当最小值只有一个时
21 | + 因为nums[mid] = nums[high],所以nums[high]一定不是数组的最小值
22 | + 最小值出现在[mid,high-1]
23 | + high--,一定不会越过最小值
24 | + low++,可能会越过最小值
25 | + 最小值出现在[0,mid]
26 | + high--,一定不会越过最小值
27 | + low++,可能会越过最小值
28 | + 当最小值不止一个时
29 | + 因为nums[mid] = nums[high],所以nums[high]可能是数组的最小值
30 | + 最小值出现在[mid,high]
31 | + high--,一定不会越过最小值,因为不止一个,在[mid,high-1]中还有重复的最小值
32 | + low++,可能会越过最小值
33 | + 最小值出现在[0,mid]
34 | + high--,一定不会越过最小值
35 | + low++,可能会越过最小值
36 | + 示例证明
37 | + 当最小值在左侧时
38 | + low++会刚好被越过,最后夹逼剩一个元素一定是不是这个被越过当最小值
39 | + 例如[6,1,6,6,6]
40 | + high--则不会
41 | + 例如 [6,1,6,6,6]、[6,6,6,0,6],均成立
42 | + 当最小值在右侧时
43 | + low++不会越过
44 | + 例如[6,6,6,1,6]
45 | + high--
46 | + 例如 [6,1,6,6,6]、[6,6,6,0,6],均成立
47 | + 因此综上所述:high--
48 | + 否则无序点在左侧
49 | + high = mid
50 | + 两边夹逼直到low == high ,剩下的一个元素即为无序点
51 | ```javascript
52 | /**
53 | * @param {number[]} nums
54 | * @return {number}
55 | */
56 | var findMin = function(nums) {
57 | var low = 0;
58 | var high = nums.length-1;
59 | while(low < high){
60 | var mid = (low+high)>>1;
61 | if(nums[mid] > nums[high]){
62 | low = mid+1;
63 | }else if(nums[mid] == nums[high]){
64 | high--;
65 | }else{
66 | high = mid
67 | }
68 | }
69 | return nums[low];
70 | };
71 | ```
--------------------------------------------------------------------------------
/leetCode-1-0876-middle-of-the-linked-list.md:
--------------------------------------------------------------------------------
1 | # 链表的中间结点(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:双指针
7 | + [参看链表各种操作大全 - 删掉链表倒数第n个节点 - 解法三](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
8 | + 思路
9 | + 最后一个节点的索引位置是中间节点索引位置的两倍距离
10 | + 因此可以想到用两个指针
11 | + **快指针始终比慢指针更快一步**
12 | + 当两个指针其中的快指针走到末尾时,**快指针走过的距离一定是慢指针的两倍**
13 | + 注意
14 | + 求第二个中间节点时,fast指针终止条件正常为fast.next
15 | + 求第一个中间节点时,fast指针终止条件为fast.next.next,多跳一个节点的距离提前终止
16 | ```javascript
17 | /**
18 | * Definition for singly-linked list.
19 | * function ListNode(val) {
20 | * this.val = val;
21 | * this.next = null;
22 | * }
23 | */
24 | /**
25 | * @param {ListNode} head
26 | * @return {ListNode}
27 | */
28 | var middleNode = function(head) {
29 | let fast = head;
30 | let slow = head;
31 | while(fast && fast.next){
32 | fast = fast.next.next;
33 | slow = slow.next;
34 | }
35 | return slow;
36 | };
37 | ```
38 | #### 解法二:两次遍历
39 | + [参看链表各种操作大全 - 删掉链表倒数第n个节点 - 解法二](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
40 | ```javascript
41 | /**
42 | * Definition for singly-linked list.
43 | * function ListNode(val) {
44 | * this.val = val;
45 | * this.next = null;
46 | * }
47 | */
48 | /**
49 | * @param {ListNode} head
50 | * @return {ListNode}
51 | */
52 | var middleNode = function(head) {
53 | let len = 0;
54 | let tmpHead = head;
55 | while(tmpHead){
56 | len++;
57 | tmpHead = tmpHead.next;
58 | }
59 | let center = (len >> 1);
60 | let pos = 0;
61 | tmpHead = head;
62 | while(center--){
63 | tmpHead = tmpHead.next;
64 | }
65 | return tmpHead;
66 | };
67 | ```
68 | #### 解法三:数组
69 | + 解法二的简化版
70 | + 找到了中间节点的位置,那么可以想到利用数组下标取值实现
71 | ```javascript
72 | /**
73 | * Definition for singly-linked list.
74 | * function ListNode(val) {
75 | * this.val = val;
76 | * this.next = null;
77 | * }
78 | */
79 | /**
80 | * @param {ListNode} head
81 | * @return {ListNode}
82 | */
83 | var middleNode = function(head) {
84 | let len = 0;
85 | let tmpHead = head;
86 | let res = [];
87 | while(tmpHead){
88 | res[len++] = tmpHead;
89 | tmpHead = tmpHead.next;
90 | }
91 | return res[len >> 1];
92 | };
93 | ```
--------------------------------------------------------------------------------
/leetCode-0-037-sudoku-solver.md:
--------------------------------------------------------------------------------
1 | # 解数独(困难)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法一:递归回溯
8 | + 类似题型
9 | + [36. 有效的数独](https://leetcode-cn.com/problems/valid-sudoku/solution/36-you-xiao-de-shu-du-by-alexer-660/)
10 | + 思路
11 | + 与36题区别的是:
12 | + 不止要判断填入的原本数字是否有效,且如果无效
13 | + 要重新回到第一次填的数字,重置重新填过,如1不行,填2,。。。填n(1= {
36 | for(let i = 0;i < 9;i++){
37 | let boxRow = parseInt(row/3)*3;
38 | let boxCol = parseInt(col/3)*3;
39 | if(board[row][i] == num || board[i][col] == num || board[boxRow+parseInt(i/3)][boxCol+i%3] == num){
40 | return false;
41 | }
42 | }
43 | return true;
44 | }
45 | let solve = () => {
46 | for(let i = 0;i < 9;i++){
47 | for(let j = 0;j < 9;j++){
48 | if(board[i][j] == '.'){
49 | for(let num = 1;num <10;num++){
50 | if(isValid(i,j,num)){
51 | board[i][j] = String(num);
52 | if(solve(board)){
53 | return true;
54 | }
55 | board[i][j] = '.';
56 | }
57 | }
58 | return false;
59 | }
60 | }
61 | }
62 | return true;
63 | }
64 | solve(board);
65 | return board;
66 | };
67 | ```
68 | #### 解法二:递归回溯优化版
69 | + 待更
--------------------------------------------------------------------------------
/leetCode-1-0191-number-of-1-bits.md:
--------------------------------------------------------------------------------
1 | # 位1的个数(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法一:循环和位移动
8 | + 时间复杂度:O(1)
9 | + 空间复杂度:O(1)
10 | + 任何数字跟掩码1进行逻辑与运算,都可以获得这个数字的最低位
11 | + 检查下一位时,将掩码左移一位
12 | + 0000 0000 0000 0000 0000 0000 0000 0001 =>
13 | + 0000 0000 0000 0000 0000 0000 0000 0010
14 | ```javascript
15 | /**
16 | * @param {number} n - a positive integer
17 | * @return {number}
18 | */
19 | var hammingWeight = function(n) {
20 | let count = 0;
21 | let mask = 1;
22 | for(let i = 0;i < 32;i++){
23 | if((n & mask) != 0){
24 | count++;
25 | }
26 | mask <<= 1;
27 | }
28 | return count;
29 | };
30 | ```
31 | #### 解法二:位操作技巧
32 | + 时间复杂度:O(1)(最坏情况下n中所有位均为1)
33 | + 空间复杂度:O(1)
34 | + 每次把数字最后一个二进制位1反转为0,sum++
35 | + 当没有1可反的时候,数字变成了0
36 | + n & (n-1)
37 | + 清零最低位的1
38 | + 借用官方一张图说明
39 | + 
40 | + n数字的二进制的最低位的1总是对应n-1数字的二进制的0
41 | + 相与后,其它位不变,当前位变成0
42 | ```javascript
43 | /**
44 | * @param {number} n - a positive integer
45 | * @return {number}
46 | */
47 | var hammingWeight = function(n) {
48 | let sum = 0
49 | while(n != 0){
50 | sum++
51 | n &= (n-1)
52 | }
53 | return sum
54 | };
55 | ```
56 | #### 解法三:调用函数懒蛋法
57 | + toString(2)转二进制
58 | + 正则匹配二进制某个数出现的次数
59 | ```javascript
60 | /**
61 | * @param {number} n - a positive integer
62 | * @return {number}
63 | */
64 | var hammingWeight = function(n) {
65 | return ((n.toString(2).match(/1/g)) ||[]).length;
66 | };
67 | ```
68 | #### 解法四:模拟十转二进制、取模
69 | + [如何从十进制转换为二进制](https://zh.wikihow.com/%E4%BB%8E%E5%8D%81%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2%E4%B8%BA%E4%BA%8C%E8%BF%9B%E5%88%B6)
70 | + ">>>"为无符号左边填充0,">>"为有符号填充
71 | ```javascript
72 | /**
73 | * @param {number} n - a positive integer
74 | * @return {number}
75 | */
76 | var hammingWeight = function(n) {
77 | let count = 0;
78 | while(n){
79 | // n % 2 == 1
80 | if(n & 1 == 1){
81 | count++;
82 | }
83 | n >>>= 1;
84 | }
85 | return count;
86 | };
87 | ```
--------------------------------------------------------------------------------
/leetCode-1-0242-valid-anagram.md:
--------------------------------------------------------------------------------
1 | # 有效的字母异位词(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ### 分析题意
7 | + 一边单词字母出现的频率等于另一边单词出现的频率但字母位置可以不一样
8 | + 两边单词个数不同肯定为 false
9 | #### 解法一:暴力排序 sort O(NlogN)
10 | + 两边字母转换为数组再排序拼接判断两边值是否相等即可
11 | ```javascript
12 | /**
13 | * @param {string} s
14 | * @param {string} t
15 | * @return {boolean}
16 | */
17 | var isAnagram = function(s, t) {
18 | if(s.length != t.length){
19 | return false;
20 | }
21 | var sSort = s.split('').sort();
22 | var tSort = t.split('').sort();
23 | return sSort.join('') == tSort.join('');
24 | };
25 | ```
26 | #### 解法二:计数排序 + 哈希表
27 | + 类似题型
28 | + [1122. 数组的相对排序](https://leetcode-cn.com/problems/relative-sort-array/solution/1122-shu-zu-de-xiang-dui-pai-xu-by-alexer-660/)
29 | + 时间复杂度 O(n)
30 | + 空间复杂度 O(1) O(26)==O(1) 表大小不变复杂性不变
31 | + 分别对其中一个单词每个字母出现的字符次数进行增加
32 | + 对另一给单词每个字母出现的次数进行减少
33 | + 维护一个26位的数组,且初始化为0,方便遍历时直接进行++,而不用判断是否存在
34 | + 最后遍历哈希结果数组只要有不为0的就是false
35 | ```javascript
36 | /**
37 | * @param {string} s
38 | * @param {string} t
39 | * @return {boolean}
40 | */
41 | var isAnagram = function(s, t) {
42 | if(s.length != t.length){
43 | return false;
44 | }
45 | var result = new Array(26);
46 | for(var i = 0;i<26;i++){
47 | result[i] = 0;
48 | }
49 | var aCode = 'a'.charCodeAt();
50 | for(var i = 0;i
7 | ## 链表
8 | + [参看链表各种操作大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
9 | #### 解法一:数组判重
10 | + 环中两个节点相遇,说明在不同的时空内会存在相遇的那一刻,过去与未来相遇,自己和前世回眸,即是重复
11 | ```javascript
12 | /**
13 | * Definition for singly-linked list.
14 | * function ListNode(val) {
15 | * this.val = val;
16 | * this.next = null;
17 | * }
18 | */
19 |
20 | /**
21 | * @param {ListNode} head
22 | * @return {boolean}
23 | */
24 | var hasCycle = function(head) {
25 | let res = [];
26 | while(head != null){
27 | if(res.includes(head)){
28 | return true;
29 | }else{
30 | res.push(head);
31 | }
32 | head = head.next;
33 | }
34 | return false;
35 | };
36 | ```
37 | #### 解法二:标记法
38 | + 思路
39 | + 一路遍历,走过的地方,标识走过,当下次再遇到,说明鬼打墙了,在绕圈子!!!妈呀,吓死宝宝👶了,😭
40 | ```javascript
41 | /**
42 | * Definition for singly-linked list.
43 | * function ListNode(val) {
44 | * this.val = val;
45 | * this.next = null;
46 | * }
47 | */
48 |
49 | /**
50 | * @param {ListNode} head
51 | * @return {boolean}
52 | */
53 | var hasCycle = function(head) {
54 | while(head && head.next){
55 | if(head.flag){
56 | return true;
57 | }else{
58 | head.flag = 1;
59 | head = head.next;
60 | }
61 | }
62 | return false;
63 | };
64 | ```
65 | #### 解法三:双指针
66 | + 思路
67 | + 在一个圆里,运动快的点在跑了n圈后,一定能相遇运动慢的点
68 | + 对应链表,就是两个指针重合
69 | + 如果不是圆,哪怕是非闭合的圆弧,快点一定先到达终点🏁,则说明不存在环
70 | + 对应链表,就是结尾指针指向null
71 | ```javascript
72 | /**
73 | * Definition for singly-linked list.
74 | * function ListNode(val) {
75 | * this.val = val;
76 | * this.next = null;
77 | * }
78 | */
79 |
80 | /**
81 | * @param {ListNode} head
82 | * @return {boolean}
83 | */
84 | var hasCycle = function(head) {
85 | if(!head || !head.next) return false;
86 | let fast = head.next;
87 | let slow = head;
88 | while(fast != slow){
89 | if(!fast || !fast.next){
90 | return false;
91 | }
92 | fast = fast.next.next;
93 | slow = slow.next;
94 | }
95 | return true;
96 | };
97 | ```
--------------------------------------------------------------------------------
/leetCode-1-0340-longest-substring-with-at-most-k-distinct-characters.md:
--------------------------------------------------------------------------------
1 | # 至多包含 K 个不同字符的最长子串(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口经典解法
24 | + [参考解法 - 159.至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/solution/159-zhi-duo-bao-han-liang-ge-bu-tong-zi-fu-de-zu-2/)
25 | ```javascript
26 | /**
27 | * @param {string} s
28 | * @param {number} k
29 | * @return {number}
30 | */
31 | var lengthOfLongestSubstringKDistinct = function(s, k) {
32 | let n = s.length;
33 | if(n < k || n === 0) return n;
34 | let left = 0,right = 0;
35 | let windows = {};
36 | let match = 0,maxLen = Number.MIN_SAFE_INTEGER;
37 | while(right < n){
38 | let c1 = s[right];
39 | windows[c1] ? windows[c1]++ : (windows[c1] = 1) && match++;
40 | right++;
41 | while(match > k){
42 | let c2 = s[left];
43 | if(windows[c2] === 1){
44 | match--;
45 | }
46 | windows[c2]--;
47 | left++;
48 | }
49 | maxLen = Math.max(maxLen,right - left);
50 | }
51 | return maxLen;
52 | };
53 | ```
--------------------------------------------------------------------------------
/leetCode-1-0102-binary-tree-level-order-traversal.md:
--------------------------------------------------------------------------------
1 | # 二叉树的层次遍历(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:BFS
7 | + 本题难度在于广度优先遍历和根据层次返回结果集
8 | + 广度优先 从左到右
9 | + 层次封装
10 | + 此处方法采用直观的对每层的节点进行遍历加入子集数组 循环后加入结果数组
11 | + 每层的节点要提前放入1个临时数组中方便循环遍历
12 | + 循环每个节点时,当前节点为当前层,当前节点的左右子节点为下一层的临时数组
13 | + 递归 两层循环,外层判断是否还有值,内层遍历每层节点数组的每个节点取值入子集
14 | + 出了内层循环 便成就了一层节点子集 将其加入结果数组
15 | + 返回结果数组即为所求
16 | ```javascript
17 | /**
18 | * Definition for a binary tree node.
19 | * function TreeNode(val) {
20 | * this.val = val;
21 | * this.left = this.right = null;
22 | * }
23 | */
24 | /**
25 | * @param {TreeNode} root
26 | * @return {number[][]}
27 | */
28 | var levelOrder = function(root) {
29 | if(!root || root.length == 0){
30 | return [];
31 | }
32 | var result = [];
33 | var currNodes = [root];
34 | while(currNodes.length != 0){
35 | var subResult = [];
36 | var nextSubResult = [];
37 | for(var i = 0;i
6 | ### 股票6道
7 | + 1、[121. 买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
8 | + 2、[122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
9 | + 3、[123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/submissions/)
10 | + 4、[309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/submissions/)
11 | + 5、[188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/submissions/)
12 | + 6、[714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/submissions/)
13 | ## [卍解👇敬请戳看](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E8%82%A1%E7%A5%A86%E9%81%93.md)
14 | ___
15 | #### 解法一:动态规划
16 | ```javascript
17 | /**
18 | * @param {number[]} prices
19 | * @return {number}
20 | */
21 | var maxProfit = function(prices) {
22 | let n = prices.length;
23 | if(n == 0){
24 | return 0;
25 | }
26 | let maxTime = 2;
27 | let dp = Array.from(new Array(n),() => new Array(maxTime+1));
28 | for(let i = 0;i < n;i++){
29 | for(let r = 0;r <= maxTime;r++){
30 | dp[i][r] = new Array(2).fill(0);
31 | }
32 | }
33 | for(let i = 0;i < n;i++){
34 | for(let k = maxTime;k >= 1;k--){
35 | if(i == 0){
36 | dp[i][k][0] = 0;
37 | dp[i][k][1] = -prices[i];
38 | continue;
39 | }
40 | dp[i][k][0] = Math.max(dp[i-1][k][0],dp[i-1][k][1] + prices[i]);
41 | dp[i][k][1] = Math.max(dp[i-1][k][1],dp[i-1][k-1][0] - prices[i]);
42 | }
43 | }
44 | return dp[n-1][maxTime][0];
45 | };
46 | ```
47 | #### 解法二:动态规划-降维
48 | ```javascript
49 | /**
50 | * @param {number[]} prices
51 | * @return {number}
52 | */
53 | var maxProfit = function(prices) {
54 | let n = prices.length;
55 | if(n == 0){
56 | return 0;
57 | }
58 | let dp_i_1_0 = 0;
59 | let dp_i_1_1 = -Infinity;
60 | let dp_i_2_0 = 0;
61 | let dp_i_2_1 = -Infinity;
62 | for(let i = 0;i < n;i++){
63 | dp_i_1_0 = Math.max(dp_i_1_0,dp_i_1_1 + prices[i]);
64 | dp_i_1_1 = Math.max(dp_i_1_1,0 - prices[i]);
65 | dp_i_2_0 = Math.max(dp_i_2_0,dp_i_2_1+prices[i]);
66 | dp_i_2_1 = Math.max(dp_i_2_1,dp_i_1_0-prices[i]);
67 | }
68 | return dp_i_2_0;
69 | };
70 | ```
--------------------------------------------------------------------------------
/leetCode-0-003-longest-substring-without-repeating-characters.md:
--------------------------------------------------------------------------------
1 | # 无重复字符的最长子串(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口
24 | ```javascript
25 | /**
26 | * @param {string} s
27 | * @return {number}
28 | */
29 | var lengthOfLongestSubstring = function(s) {
30 | if(s.length == 0) return 0;
31 | let hash = {};
32 | let max = 0;
33 | let left = 0;
34 | for(let right = 0;right < s.length;right++){
35 | let moveLeft = hash[s[right]];
36 | if(moveLeft){
37 | left = Math.max(left,moveLeft);
38 | }
39 | hash[s[right]] = right + 1;
40 | max = Math.max(max,right - left + 1);
41 | }
42 | return max;
43 | };
44 | ```
45 | + 亦可这样写
46 | ```javascript
47 | /**
48 | * @param {string} s
49 | * @return {number}
50 | */
51 | var lengthOfLongestSubstring = function(s) {
52 | let windows = {};
53 | let res = 0;
54 | let left = 0,right = 0;
55 | while(right < s.length){
56 | let c1 = s[right];
57 | windows[c1] != undefined ? windows[c1]++ : windows[c1] = 1;
58 | right++;
59 | while(windows[c1] > 1){
60 | let c2 = s[left];
61 | windows[c2]--;
62 | left++;
63 | }
64 | res = Math.max(res,right - left);
65 | }
66 | return res;
67 | };
68 | ```
--------------------------------------------------------------------------------
/leetCode-1-0190-reverse-bits.md:
--------------------------------------------------------------------------------
1 | # 颠倒二进制位(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法一:位移 + 拼接
8 | + “>>”运算符执行有符号右移位运算。与左移运算操作相反,它把 32 位数字中的所有有效位整体右移,再使用符号位的值填充空位。移动过程中超出的值将被丢弃。
9 | + 
10 | + “<<”运算符执行左移位运算。在移位运算过程中,符号位始终保持不变。如果右侧空出位置,则自动填充为 0;超出 32 位的值,则自动丢弃。
11 | + 
12 | + 
13 | + “>>>”运算符执行五符号右移位运算。它把无符号的 32 位整数所有数位整体右移。对于无符号数或正数右移运算,无符号右移与有符号右移运算的结果是相同的。
14 | + 对于负数,左侧空位不再用符号位的值来填充,而是用 0 来填充。
15 | + 
16 | + 每次获取原数的最低位,拼接到结果数字结尾里
17 | + 获得下一位时右移原数一位
18 | + 拼接下一位时提前左移结果数一位
19 | ```javascript
20 | /**
21 | * @param {number} n - a positive integer
22 | * @return {number} - a positive integer
23 | */
24 | var reverseBits = function(n) {
25 | let result = 0;
26 | for(let i = 0;i < 32;i++){
27 | result = (result << 1) + (n & 1);
28 | n >>= 1;
29 | }
30 | return result >>> 0;
31 | };
32 | ```
33 | #### 解法二:位移 + 换位
34 | ```javascript
35 | /**
36 | * @param {number} n - a positive integer
37 | * @return {number} - a positive integer
38 | */
39 | var reverseBits = function(n) {
40 | let result = 0;
41 | // result从右往移动空出末位 + n从左往右移动获取末位 + n次 = 倒序
42 | for(let i = 0;i < 32;i++){
43 | // 左移空出一位
44 | result <<= 1
45 | // n&1获取n的末位,result的末位换成n的末位
46 | result |= n & 1;
47 | // 右移1位
48 | n >>= 1;
49 | }
50 | return result >>> 0;
51 | };
52 | ```
53 | #### js中表达式 >>> 0 浅析
54 | + ">>>" 是无符号右移
55 | + 负数移动n位,总是非负数
56 | + ">>> 0"意义
57 | + 将数据转换为number类型
58 | + 将number转换为无符号的32bit数据
59 | + 即Uint32类型
60 | + 如不能转换为Number,则为0
61 | + 如果为非整数,先转换为整数
62 | + parseInt(string,radix)
63 | + Math.trunc()
64 | + ~~n
65 | + n | n
66 | + n | 0
67 | + n << 0
68 | + n >> 0
69 | + n & n
70 | + ">>" 是有符号右移
71 | + 负数移位是负数
--------------------------------------------------------------------------------
/leetCode-1-0515-find-largest-value-in-each-tree-row.md:
--------------------------------------------------------------------------------
1 | # 在每个树行中找最大值(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:BFS
7 | + 类似题型解法
8 | + [102. 二叉树的层次遍历-解法一](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/102-er-cha-shu-de-ceng-ci-bian-li-by-alexer-660/)
9 | + 本质和102题一模一样
10 | + 只是本题取的是每层的最大值
11 | + 因此只需要每次更新当前层遍历元素的最大值即可
12 | + 合并解即为所求
13 | ```javascript
14 | /**
15 | * Definition for a binary tree node.
16 | * function TreeNode(val) {
17 | * this.val = val;
18 | * this.left = this.right = null;
19 | * }
20 | */
21 | /**
22 | * @param {TreeNode} root
23 | * @return {number[]}
24 | */
25 | var largestValues = function(root) {
26 | if(!root || root.length == 0){
27 | return [];
28 | }
29 | var result = [];
30 | var leverMax = 0;
31 | var currNodes = [root];
32 | while(currNodes.length != 0){
33 | var subresultMax = -Infinity;
34 | var nextSubresult = [];
35 | for(var i = 0;i
6 | #### 回溯算法系列
7 | + [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/solution/39-zu-he-zong-he-by-alexer-660/)
8 | + [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/solution/40-zu-he-zong-he-ii-by-alexer-660/)
9 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
10 | + [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
11 | + [77. 组合](https://leetcode-cn.com/problems/combinations/solution/77-zu-he-by-alexer-660/)
12 | + [78. 子集](https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-by-alexer-660/)
13 | + [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/solution/90-zi-ji-ii-by-alexer-660/)
14 | #### 解法:递归回溯 + 双重剪枝
15 | + 递归代码模板
16 | + [参看各类算法模板 - 递归一节 - Python&Java版](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
17 | + 类似题型
18 | + [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/solution/39-zu-he-zong-he-by-alexer-660/)
19 | + [47. 全排列 II - 解法二](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
20 | + 思路
21 | + 一重减枝
22 | + 本题和39题唯一不同的是
23 | + 39题原数组的每个元素可以重复使用
24 | + 此题只能使用一次
25 | + 由39题题解可知
26 | + **且原数组的单个元素可以重复使用**
27 | + 意味着下一个for循环中的元素选取,要从前一个元素开始,因为可以重复使用,不然如果跟着for的自增变量i走,会漏掉可能解
28 | + 将自增变量i传递下去
29 | + 此题不能重复,意思就是当前自增变量不能传递下去,要传递下一个自增变量 i + 1
30 | + 二重减枝
31 | + 和47题一样,既然不能重复当前元素,那么利用排序,将相邻两个相同的元素只取前一个去组合,当前直接跳过,直接进入下一个元素进行组合
32 | + 题外话
33 | + break 语句用于跳出循环,即当前整个for循环终止,如有递归里使用for,则直接进入下一个出栈函数中的for循环
34 | + continue 用于跳过循环中的一个迭代,即跳过当前自增的元素,直接进入下一个元素,当前for循环还在运行中
35 | ```javascript
36 | /**
37 | * @param {number[]} candidates
38 | * @param {number} target
39 | * @return {number[][]}
40 | */
41 | var combinationSum2 = function(candidates, target) {
42 | let n = candidates.length;
43 | let res = [];
44 | let tmpPath = [];
45 | candidates = candidates.sort((a,b) => {return a - b})
46 | let backtrack = (tmpPath,target,start) => {
47 | if(target == 0){
48 | res.push(tmpPath);
49 | return;
50 | }
51 | for(let i = start;i < n;i++){
52 | if(target < candidates[i]) break;
53 | if(i > start && candidates[i-1] == candidates[i]) continue;
54 | tmpPath.push(candidates[i]);
55 | backtrack(tmpPath.slice(),target - candidates[i],i + 1);
56 | tmpPath.pop();
57 | }
58 | }
59 | backtrack(tmpPath,target,0);
60 | return res;
61 | };
62 | ```
--------------------------------------------------------------------------------
/leetCode-1-0350-intersection-of-two-arrays-ii.md:
--------------------------------------------------------------------------------
1 | # 两个数组的交集 II(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:对应进阶一 :排序 + 双指针
7 | + [思路同 349. 两个数组的交集 - 解法四](https://leetcode-cn.com/problems/intersection-of-two-arrays/solution/349-liang-ge-shu-zu-de-jiao-ji-by-alexer-660/)
8 | + 区别是不能去重, 重复的相同元素也要
9 | ```javascript
10 | /**
11 | * @param {number[]} nums1
12 | * @param {number[]} nums2
13 | * @return {number[]}
14 | */
15 | var intersect = function(nums1, nums2) {
16 | nums1 = nums1.sort((a,b) => a - b);
17 | nums2 = nums2.sort((a,b) => a - b);
18 | let i = 0;
19 | let j = 0;
20 | let res = [];
21 | while(i < nums1.length && j < nums2.length){
22 | if(nums1[i] < nums2[j]){
23 | i++;
24 | }else if(nums1[i] > nums2[j]){
25 | j++;
26 | }
27 | else{
28 | res.push(nums1[i]);
29 | i++;
30 | j++;
31 | }
32 | }
33 | return res;
34 | };
35 | ```
36 | #### 解法二:对应进阶二 :哈希
37 | + 时间复杂度:O(n+m)
38 | + 空间复杂度:O(min(n,m))
39 | + [思路同 349. 两个数组的交集 - 解法三](https://leetcode-cn.com/problems/intersection-of-two-arrays/solution/349-liang-ge-shu-zu-de-jiao-ji-by-alexer-660/)
40 | + 区别是不能去重, 重复的相同元素也要
41 | + 对应哈希,就是对重复的元素进行计数
42 | + 返回
43 | + 不需额外设置存储空间,直接对nums1进行覆盖,最后返回相应元素的数即可
44 | ```javascript
45 | /**
46 | * @param {number[]} nums1
47 | * @param {number[]} nums2
48 | * @return {number[]}
49 | */
50 | var intersect = function(nums1, nums2) {
51 | if(nums1.length > nums2.length) [nums1,nums2] = [nums2,nums1];
52 | let hash = {};
53 | for(let i = 0;i < nums1.length;i++){
54 | if(hash[nums1[i]]){
55 | hash[nums1[i]]++;
56 | }else{
57 | hash[nums1[i]] = 1;
58 | }
59 | }
60 | let r = 0;
61 | for(let i = 0;i < nums2.length;i++){
62 | if(hash[nums2[i]]){
63 | nums1[r++] = nums2[i];
64 | hash[nums2[i]]--;
65 | }
66 | }
67 | return nums1.slice(0,r);
68 | };
69 | ```
70 | #### 解法三:对应进阶三 :双指针 + 归并排序
71 | + 其实就是将解法一定义的res去掉
72 | + 换做和解法二一样,不利用额外空间
73 | ```javascript
74 | /**
75 | * @param {number[]} nums1
76 | * @param {number[]} nums2
77 | * @return {number[]}
78 | */
79 | var intersect = function(nums1, nums2) {
80 | nums1 = nums1.sort((a,b) => a - b);
81 | nums2 = nums2.sort((a,b) => a - b);
82 | let i = 0;
83 | let j = 0;
84 | let k = 0;
85 | while(i < nums1.length && j < nums2.length){
86 | if(nums1[i] < nums2[j]){
87 | i++;
88 | }else if(nums1[i] > nums2[j]){
89 | j++;
90 | }
91 | else{
92 | nums1[k++] = nums1[i];
93 | i++;
94 | j++;
95 | }
96 | }
97 | return nums1.slice(0,k);
98 | };
99 | ```
--------------------------------------------------------------------------------
/leetCode-1-0105-construct-binary-tree-from-preorder-and-inorder-traversal.md:
--------------------------------------------------------------------------------
1 | # 从前序与中序遍历序列构造二叉树(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 二叉树
7 | + [二叉树的前、中、后序遍历 - 解法大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%89%E5%BA%8F%E9%81%8D%E5%8E%86.md)
8 | #### 解法一:递归
9 | + 思路
10 | + 前序遍历:根-左-右
11 | + 中序遍历:左-根-右
12 | + 因此
13 | + 对于preorder,每个首元素即为一个子树的根元素
14 | + 对于inorder,查找preorder中的根元素
15 | + 左边为preorder当前根元素的左子树
16 | + 右边为preorder当前根元素的右子树
17 | + 据此递归构造出一颗二叉树即可
18 | ```javascript
19 | /**
20 | * Definition for a binary tree node.
21 | * function TreeNode(val) {
22 | * this.val = val;
23 | * this.left = this.right = null;
24 | * }
25 | */
26 | /**
27 | * @param {number[]} preorder
28 | * @param {number[]} inorder
29 | * @return {TreeNode}
30 | */
31 | var buildTree = function(preorder, inorder) {
32 | if(!inorder.length) return null
33 | let tmp = preorder[0],mid = inorder.indexOf(tmp)
34 | let root = new TreeNode(tmp)
35 | root.left = buildTree(preorder.slice(1,mid+1),inorder.slice(0,mid))
36 | root.right = buildTree(preorder.slice(mid+1),inorder.slice(mid + 1))
37 | return root
38 | };
39 | ```
40 | #### 解法二:递归简便版
41 | ```javascript
42 | /**
43 | * Definition for a binary tree node.
44 | * function TreeNode(val) {
45 | * this.val = val;
46 | * this.left = this.right = null;
47 | * }
48 | */
49 | /**
50 | * @param {number[]} preorder
51 | * @param {number[]} inorder
52 | * @return {TreeNode}
53 | */
54 | var buildTree = function(preorder, inorder) {
55 | let build = (inorder) => {
56 | if(!inorder || !inorder.length) return null
57 | let tmp = preorder.shift(),mid = inorder.indexOf(tmp)
58 | let root = new TreeNode(tmp)
59 | root.left = build(inorder.slice(0,mid))
60 | root.right = build(inorder.slice(mid + 1))
61 | return root
62 | }
63 | return build(inorder)
64 | };
65 | ```
66 | #### 解法三:参考解法
67 | ```javascript
68 | /**
69 | * Definition for a binary tree node.
70 | * function TreeNode(val) {
71 | * this.val = val;
72 | * this.left = this.right = null;
73 | * }
74 | */
75 | /**
76 | * @param {number[]} preorder
77 | * @param {number[]} inorder
78 | * @return {TreeNode}
79 | */
80 | var buildTree = function(preorder, inorder) {
81 | let p = i = 0;
82 | let build = (stop) => {
83 | if(inorder[i] != stop) {
84 | let root = new TreeNode(preorder[p++])
85 | root.left = build(root.val)
86 | i++
87 | root.right = build(stop)
88 | return root
89 | }
90 | return null
91 | }
92 | return build()
93 | };
94 | ```
--------------------------------------------------------------------------------
/leetCode-1-0727-minimum-window-subsequence.md:
--------------------------------------------------------------------------------
1 | # 最小窗口子序列(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口解法
24 | + 思路
25 | + 此题 和 [76题 - 解法二](https://leetcode-cn.com/problems/minimum-window-substring/solution/76-zui-xiao-fu-gai-zi-chuan-by-alexer-660/)几乎一摸一样
26 | + 之所以写法不同,是因为和上题的一点区别
27 | + 76 题 算出的结果可以不按照模式串中出现的字符顺序出现
28 | + 而本题必须按照模式串T中所有字符出现的位置相同
29 | + 因此要减枝和优化,直接看代码吧
30 | + 
31 | ```javascript
32 | /**
33 | * @param {string} S
34 | * @param {string} T
35 | * @return {string}
36 | */
37 | var minWindow = function(S, T) {
38 | if(S === T) return S;
39 | let start = 0;
40 | let end = S.length - 1;
41 | let s = 0,t = 0;
42 | while(s < S.length){
43 | if(S[s] === T[t]){
44 | t++;
45 | }
46 | if(t === T.length){
47 | let right = s;
48 | t--;
49 | while(t >= 0){
50 | if(S[s] === T[t]){
51 | t--;
52 | }
53 | s--;
54 | }
55 | s++;
56 | if(right - s + 1 < end - start + 1){
57 | start = s;
58 | end = right;
59 | }
60 | t = 0;
61 | }
62 | s++;
63 | }
64 | return end -start + 1 === S.length ? "" : S.substr(start,end - start + 1);
65 | };
66 | ```
--------------------------------------------------------------------------------
/leetCode-1-0632-smallest-range-covering-elements-from-k-lists.md:
--------------------------------------------------------------------------------
1 | # 最小区间(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口
24 | + [参考这里](https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104920/Java-8-Sliding-window)
25 | + 
26 | ```javascript
27 | /**
28 | * @param {number[][]} nums
29 | * @return {number[]}
30 | */
31 | var smallestRange = function(nums) {
32 | let points = [];
33 | for(let i = 0;i < nums.length;i++){
34 | for(let j = 0;j < nums[i].length;j++){
35 | points.push([nums[i][j],i]);
36 | }
37 | }
38 | points.sort((a,b) => a[0] - b[0]);
39 | let counts = new Array(nums.length).fill(0);
40 | let countUnique = 0,minStart = -1,minLen = Number.MAX_SAFE_INTEGER;
41 | for(let i = 0,j = 0;j < points.length;j++){
42 | if(counts[points[j][1]]++ === 0) countUnique++;
43 | while(countUnique === counts.length){
44 | if(points[j][0] - points[i][0] + 1 < minLen){
45 | minStart = points[i][0];
46 | minLen = points[j][0] - points[i][0] + 1;
47 | }
48 | let prev = points[i][0];
49 | while(i <= j && prev === points[i][0]){
50 | if(--counts[points[i++][1]] === 0) countUnique--;
51 | }
52 | }
53 | }
54 | return [minStart,minStart + minLen - 1];
55 | };
56 | ```
--------------------------------------------------------------------------------
/leetCode-1-0279-perfect-squares.md:
--------------------------------------------------------------------------------
1 | # 完全平方数(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 各类算法模板
7 | + [BFS](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
8 | #### 解法一:动态规划
9 | + 时间复杂度:O(n$\sqrt{n}$)
10 | + 空间复杂度:O(n)
11 | + 思路
12 | + 状态定义:```dp[i]```:表示当前数字```i```最少有几个平方数构成
13 | + 转移方程:```dp[i] = min(dp[i],dp[i - j*j] + 1)```
14 | ```javascript
15 | /**
16 | * @param {number} n
17 | * @return {number}
18 | */
19 | var numSquares = function(n) {
20 | let dp = new Array(n+1).fill(0);
21 | for(let i = 1;i <= n;i++){
22 | dp[i] = i;
23 | for(let j = 1;j*j <= i;j++){
24 | dp[i] = Math.min(dp[i],dp[i - j*j] + 1);
25 | }
26 | }
27 | return dp[n];
28 | };
29 | ```
30 | #### 解法二:BFS
31 | ```javascript
32 | /**
33 | * @param {number} n
34 | * @return {number}
35 | */
36 | var numSquares = function(n) {
37 | let queue = [n];
38 | let visited = {};
39 | let level = 0;
40 | while(queue.length > 0) {
41 | // 层序遍历
42 | level++;
43 | let len = queue.length;
44 | for(let i = 0;i < len;i++){
45 | let cur = queue.pop();
46 | for(let j = 1;j*j <= cur;j++){
47 | let tmp = cur - j*j;
48 | // 找到答案
49 | if(tmp === 0) {
50 | return level;
51 | }
52 | if(!visited[tmp]){
53 | queue.unshift(tmp);
54 | visited[tmp] = true;
55 | }
56 | }
57 | }
58 | }
59 | return level;
60 | };
61 | ```
62 | #### 解法三:拉格朗日四平方和定理
63 | + 定义
64 | + 每个正整数均可表示为4个整数的平方和。
65 | + 它是费马多边形数定理和华林问题的特例。
66 | + 参考文献
67 | + [拉格朗日四平方和定理证明](https://zhuanlan.zhihu.com/p/104030654)
68 | ```javascript
69 | /**
70 | * @param {number} n
71 | * @return {number}
72 | */
73 | var numSquares = function(n) {
74 | if(Math.pow(Math.floor(Math.sqrt(n)),2) === n) return 1;
75 | while(n%4 === 0){
76 | n = n/4;
77 | }
78 | if((n-7)%8 === 0){
79 | return 4;
80 | }
81 | for(let y,x = 1;x*x < n;x++){
82 | y = Math.floor(Math.sqrt(n - x*x));
83 | if(x*x + y*y === n) return 2;
84 | }
85 | return 3;
86 | };
87 | ```
88 | + 位运算进阶版
89 | ```javascript
90 | /**
91 | * @param {number} n
92 | * @return {number}
93 | */
94 | var numSquares = function(n) {
95 | if(Math.pow(Math.floor(Math.sqrt(n)),2) === n) return 1;
96 | while((n & 3) === 0){
97 | n >>= 2;
98 | }
99 | if((n & 7) === 7){
100 | return 4;
101 | }
102 | for(let y,x = 1;x*x < n;x++){
103 | y = Math.floor(Math.sqrt(n - x*x));
104 | if(x*x + y*y === n) return 2;
105 | }
106 | return 3;
107 | };
108 | ```
--------------------------------------------------------------------------------
/leetCode-1-0438-find-all-anagrams-in-a-string.md:
--------------------------------------------------------------------------------
1 | # 找到字符串中所有字母异位词(中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | ## 滑动窗口思想
8 | + 讲解
9 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
10 | + 类似题型
11 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
12 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
13 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
14 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
15 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
16 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
17 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
18 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
19 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
20 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
21 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
22 | + 戳看👇
23 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
24 | #### 解法:滑动窗口经典解法
25 | + [类似解法 76. 最小覆盖子串 - 解法二](https://leetcode-cn.com/problems/minimum-window-substring/solution/76-zui-xiao-fu-gai-zi-chuan-by-alexer-660/)
26 | ```javascript
27 | /**
28 | * @param {string} s
29 | * @param {string} p
30 | * @return {number[]}
31 | */
32 | var findAnagrams = function(s, p) {
33 | let res = [];
34 | let left = 0,right = 0;
35 | let needs = {},windows = {};
36 | let match = 0;
37 | for(let i = 0;i < p.length;i++){
38 | needs[p[i]] ? needs[p[i]]++ : needs[p[i]] = 1;
39 | }
40 | let needsLen = Object.keys(needs).length;
41 | while(right < s.length){
42 | let c1 = s[right];
43 | if(needs[c1]){
44 | windows[c1] ? windows[c1]++ : windows[c1] = 1;
45 | if(windows[c1] === needs[c1]){
46 | match++;
47 | }
48 | }
49 | right++;
50 | while(match === needsLen){
51 | if(right - left === p.length){
52 | res.push(left);
53 | }
54 | let c2 = s[left];
55 | if(needs[c2]){
56 | windows[c2]--;
57 | if(windows[c2] < needs[c2]){
58 | match--;
59 | }
60 | }
61 | left++;
62 | }
63 | }
64 | return res;
65 | };
66 | ```
--------------------------------------------------------------------------------
/leetCode-0-030-substring-with-concatenation-of-all-words.md:
--------------------------------------------------------------------------------
1 | # 串联所有单词的子串(困难)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 滑动窗口思想
7 | + 讲解
8 | + [滑动窗口11道](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A311%E9%81%93.md)
9 | + 类似题型
10 | + 1、[3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/)
11 | + 2、[30. 串联所有单词的子串](https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/)
12 | + 3、[76. 最小覆盖子串](https://leetcode-cn.com/problems/minimum-window-substring/)
13 | + 4、[159. 至多包含两个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-two-distinct-characters/)
14 | + 5、[209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)
15 | + 6、[239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
16 | + 7、[340. 至多包含 K 个不同字符的最长子串](https://leetcode-cn.com/problems/longest-substring-with-at-most-k-distinct-characters/)
17 | + 8、[438. 找到字符串中所有字母异位词](https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/)
18 | + 9、[567. 字符串的排列](https://leetcode-cn.com/problems/permutation-in-string/)
19 | + 10、[632. 最小区间](https://leetcode-cn.com/problems/smallest-range-covering-elements-from-k-lists/)
20 | + 11、[727. 最小窗口子序列](https://leetcode-cn.com/problems/minimum-window-subsequence/)
21 | + 戳看👇
22 | + [leetCode所有题解](https://github.com/Alex660/leetcode)
23 | #### 解法:滑动窗口
24 | ```javascript
25 | /**
26 | * @param {string} s
27 | * @param {string[]} words
28 | * @return {number[]}
29 | */
30 | var findSubstring = function(s, words) {
31 | let left = 0,right = 0,wordsLen = words.length;
32 | if(wordsLen == 0) return [];
33 | let res = [];
34 | let gapLen = words[0].length;
35 | let needs = {};
36 | let windows = {};
37 | for(let i = 0;i < wordsLen;i++){
38 | needs[words[i]] ? needs[words[i]]++ : needs[words[i]] = 1;
39 | }
40 | let needsLen = Object.keys(needs).length;
41 | let match = 0;
42 | for(let i = 0;i < gapLen;i++){
43 | right = left = i;
44 | match = 0;
45 | while(right <= s.length - gapLen){
46 | let c1 = s.substring(right,right + gapLen);
47 | right += gapLen;
48 | windows[c1] ? windows[c1]++ : windows[c1] = 1;
49 | if(windows[c1] === needs[c1]){
50 | ++match;
51 | }
52 | while(left < right && match == needsLen){
53 | if(Math.floor((right - left) / gapLen) == wordsLen){
54 | res.push(left);
55 | }
56 | let c2 = s.substring(left,left + gapLen);
57 | left += gapLen;
58 | windows[c2]-- ;
59 | if(needs[c2] && windows[c2] < needs[c2]){
60 | match--;
61 | }
62 | }
63 | }
64 | windows = {};
65 | }
66 | return res;
67 | };
68 | ```
--------------------------------------------------------------------------------
/leetCode-0-070-climbing-stairs.md:
--------------------------------------------------------------------------------
1 | # 爬楼梯(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 解法一
7 | 
8 | # 解法二公式
9 | 
10 |
11 | > 爬楼梯
12 | 爬楼梯设需要n阶才能到达楼顶,每次可以爬 1 或 2 个 台阶,问有多少种不同的方法才能爬到楼顶?
13 | + input 2 output 2 1+1、2
14 | + input 3 output 3 1+1+1、1+2、2+1
15 |
16 | #### 解法一:<=> 求第i个斐波那契数
17 | + 维护3个变量 每次递归更新前两个子问题所需步数
18 | + 可知递推公式 == f(n) = f(n-2) + f(n-1),n>=1
19 | + 1 1 2 3 5 8 13 21
20 | + 时间复杂度:O(n)
21 | + 空间复杂度:O(1)
22 | + 代码
23 | ```javascript
24 | /**
25 | * @param {number} n
26 | * @return {number}
27 | */
28 | var climbStairs = function(n) {
29 | var f1 = 2;
30 | var f2 = 3;
31 | var f3 = 0;
32 | if(n <= 3){
33 | return n;
34 | }
35 | while(n>3){
36 | f3 = f2 + f1;
37 | f1 = f2;
38 | f2 = f3;
39 | n--;
40 | }
41 | return f2;
42 | };
43 | ```
44 |
45 | #### 解法二:由二阶递推 可以得出 斐波那契数列的 特征方程为 x^2 = x^1 +1;
46 | + 由数学证明公式可得代码如下
47 | ```javascript
48 | /**
49 | * @param {number} n
50 | * @return {number}
51 | */
52 | var climbStairs = function(n) {
53 | var sqrt5 = Math.sqrt(5);
54 | var pow = Math.pow((1+sqrt5)/2,n+1) - Math.pow((1-sqrt5)/2,n+1);
55 | return Math.round( pow/sqrt5 );
56 | };
57 | ```
58 |
59 | #### 解法三:动态规划
60 | + 时间复杂度:O(n)
61 | + 空间复杂度:O(n)
62 | + 符合:
63 | + 最优子结构(子问题的最优解推出问题的最优解)
64 | + 重复子问题(递归求解过程成会重复计算之前已经计算过的问题)
65 | + 无后效性(前面的状态一旦确定不会因为后面的状态改变而改变)
66 | + 代码
67 | ```javascript
68 | /**
69 | * @param {number} n
70 | * @return {number}
71 | */
72 | var climbStairs = function(n) {
73 | // 求第n步 所以索引到n
74 | var dp = new Array(n+1);
75 | if(n <= 3){
76 | return n;
77 | }
78 | dp[1] = 1;
79 | dp[2] = 2;
80 | for(var i = 3;i<=n;i++){
81 | dp[i] = dp[i-2] + dp[i-1];
82 | }
83 | return dp[n];
84 | };
85 | ```
86 |
87 | #### 解法四:暴力求解
88 | ```javascript
89 | /**
90 | * @param {number} n
91 | * @return {number}
92 | */
93 | var climbStairs = function(n) {
94 | var climb = function(i,n){
95 | if(i > n){
96 | return 0;
97 | }
98 | if(i == n){
99 | return 1;
100 | }
101 | return climb(i+1,n) + climb(i+2,n);
102 | }
103 | return climb(0,n);
104 | };
105 | ```
106 | #### 解法五:解法一的简化版
107 | + 分析:同样不断更新前后两个数值
108 | ```javascript
109 | /**
110 | * @param {number} n
111 | * @return {number}
112 | */
113 | var climbStairs = function(n) {
114 | var a = 1;
115 | var b = 1;
116 | while(n--){
117 | a = (b+=a) -a;
118 | }
119 | return a;
120 | };
121 | ```
--------------------------------------------------------------------------------
/leetCode-1-0746-min-cost-climbing-stairs.md:
--------------------------------------------------------------------------------
1 | # 使用最小花费爬楼梯(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ## 解法四:
7 | 
8 | #### 解法一:动态规划 - 分解
9 | + 思路
10 | + 如果走一步或两步上来有两种方法
11 | + [70. 爬楼梯-解法三](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-by-alexer-660/)
12 | + 与70题一模一样
13 | + 动态转移方程为
14 | + **dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i]**
15 | + 由题意分解可知
16 | + 如果最后一步正好是在最后一个台阶上时,最后一个台阶的花费值不算在内
17 | + 动态转移方程为
18 | + **i == n && dp[i] = Math.min(dp[i-2] , dp[i-1])**
19 | ```javascript
20 | /**
21 | * @param {number[]} cost
22 | * @return {number}
23 | */
24 | var minCostClimbingStairs = function(cost) {
25 | let n = cost.length;
26 | let dp = new Array(n+1).fill(0);
27 | dp[0] = cost[0];
28 | dp[1] = cost[1];
29 | for(let i = 2;i <= n;i++){
30 | if(i == n){
31 | dp[i] = Math.min(dp[i-2] , dp[i-1]);
32 | }else{
33 | dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i];
34 | }
35 | }
36 | return dp[n];
37 | };
38 | ```
39 | #### 解法二:动态规划 - 合并
40 | + 由解法一可知
41 | + 既然有最后一阶台阶不算的情况在内,就直接在最后加一个0(表示包含在这种特殊情况在内,加0就等于花费值为0嘛)
42 | + 这样动态转移方程就不用变了
43 | + **dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i]**
44 | ```javascript
45 | /**
46 | * @param {number[]} cost
47 | * @return {number}
48 | */
49 | var minCostClimbingStairs = function(cost) {
50 | cost.push(0);
51 | let n = cost.length;
52 | let dp = [];
53 | dp[0] = cost[0];
54 | dp[1] = cost[1];
55 | for(let i = 2;i < n;i++){
56 | dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i];
57 | }
58 | return dp[n-1];
59 | };
60 | ```
61 | #### 解法三:动态规划 - 比较
62 | + 由以上解法可知
63 | + 更通俗的解法是
64 | + 唯一不同的情况是最后一阶的台阶的花费值是否被算在内
65 | + 那么我统一加最后一阶台阶,只在最后取结果的时候
66 | + 比较判断去掉最后一个台阶的走法情况是否花费更少即可
67 | ```javascript
68 | /**
69 | * @param {number[]} cost
70 | * @return {number}
71 | */
72 | var minCostClimbingStairs = function(cost) {
73 | let n = cost.length;
74 | let dp = [];
75 | dp[0] = cost[0];
76 | dp[1] = cost[1];
77 | for(let i = 2;i < n;i++){
78 | dp[i] = Math.min(dp[i-2] , dp[i-1]) + cost[i];
79 | }
80 | return dp[n-1] > dp[n-2] ? dp[n-2] : dp[n-1];
81 | };
82 | ```
83 | #### 解法四:去维 - 变量
84 | + 通过以上分析
85 | + 用两个变量,结合解法三的比较,可得当前所有解法中时间复杂度最优的解法如下
86 | ```javascript
87 | /**
88 | * @param {number[]} cost
89 | * @return {number}
90 | */
91 | var minCostClimbingStairs = function(cost) {
92 | let n = cost.length;
93 | let pre = cost[0];
94 | let next = cost[1];
95 | for(let i = 2;i < n;i++){
96 | let tmp = next;
97 | next = Math.min(pre,next)+cost[i];
98 | pre = tmp;
99 | }
100 | return Math.min(pre,next);
101 | };
102 | ```
--------------------------------------------------------------------------------
/leetCode-0-069-sqrtx.md:
--------------------------------------------------------------------------------
1 | # x的平方根(简单)
2 | # 题目描述
3 | # 题目地址
4 |
5 | #### 解法一:二分查找
6 | + 话不多说直接上简便二分法模板,公式不多说
7 | + java
8 | ```java
9 | public int bsearch(int[] a, int n, int value) {
10 | int low = 0;
11 | int high = n - 1;
12 |
13 | while (low <= high) {
14 | int mid = (low + high) / 2;
15 | if (a[mid] == value) {
16 | return mid;
17 | } else if (a[mid] < value) {
18 | low = mid + 1;
19 | } else {
20 | high = mid - 1;
21 | }
22 | }
23 |
24 | return -1;
25 | }
26 | ```
27 | + python
28 | ```python
29 | left, right = 0, len(array) - 1
30 | while left <= right:
31 | mid = (left + right) / 2
32 | if array[mid] == target:
33 | # find the target!!
34 | break or return result
35 | elif array[mid] < target:
36 | left = mid + 1
37 | else:
38 | right = mid - 1
39 | ```
40 | + 注意
41 | + 此处代码没有用“/2”而是“>>1”
42 | + 属于位运算,且更加精确
43 | + [可参考我的总结](https://github.com/Alex660/Algorithms-and-data-structures/tree/master/theoreticalKnowledge)
44 | ```javascript
45 | /**
46 | * @param {number} x
47 | * @return {number}
48 | */
49 | var mySqrt = function(x) {
50 | if(x == 0 || x ==1){
51 | return x;
52 | }
53 | var left = 1;
54 | var right = x;
55 | while(left <= right){
56 | var middle = left + ((right-left)>>1);
57 | if(middle*middle == x){
58 | return middle;
59 | }else if(middle*middle > x){
60 | right = middle-1;
61 | }else{
62 | left = middle+1;
63 | }
64 | }
65 | return right;
66 | };
67 | ```
68 | #### 解法二:牛顿迭代法
69 | + 公式:x= (x+tmp_x/x)/2
70 | + [戳看公式参考文献](https://www.zhihu.com/question/20690553)
71 | + [英文文献](https://www.beyond3d.com/content/articles/8/)
72 | ```javascript
73 | /**
74 | * @param {number} x
75 | * @return {number}
76 | */
77 | var mySqrt = function(x) {
78 | if(x == 0 || x ==1){
79 | return x;
80 | }
81 | var tmp = x;
82 | function sqrt(x){
83 | var sqrtx = (x+tmp/x)/2;
84 | if(sqrtx == x){
85 | return parseInt(x);
86 | }else{
87 | return sqrt(sqrtx);
88 | }
89 | }
90 | return sqrt(x);
91 | };
92 | ```
93 | + 还有这样写
94 | ```javascript
95 | /**
96 | * @param {number} x
97 | * @return {number}
98 | */
99 | var mySqrt = function(x) {
100 | var tmp = x;
101 | while (x*x > tmp)
102 | x = ((x + tmp/x) / 2) | 0;
103 | return x;
104 | };
105 | ```
106 | + 当然也可以这样写
107 | + 不过这个会超时!
108 | ```javascript
109 | /**
110 | * @param {number} x
111 | * @return {number}
112 | */
113 | var mySqrt = function(x) {
114 | if(x == 0 || x ==1){
115 | return x;
116 | }
117 | var tmp = x;
118 | while(x*x > tmp){
119 | x = (x+tmp/x)>>1;
120 | }
121 | return x;
122 | };
123 | ```
--------------------------------------------------------------------------------
/leetCode-0-020-valid-parentheses.md:
--------------------------------------------------------------------------------
1 | # 有效的括号(简单)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:洋葱栈
8 | + 由题意可知此题是判断输入的字符串是否有效
9 | + 即 **n个洋葱** 结构
10 | + 形如()、(())、()((())) 【左开右闭】
11 | + 即遇到的第一个右闭字符其左边一定能找到一个左开字符与其一一对应合成**一层洋葱结构**
12 | + 如果将找到的一组字符去掉,那么下次寻找操作和上一步一摸一样
13 | + 找到后去掉并循往复
14 | + 如果找到的最近的左边字符不是与其一一对应的左开字符,则说明**整体洋葱结构**受到了破坏,不是有效的字符串
15 | + 如果找到了,继续遍历
16 | + 直到循环结束后,没有剩余的**左半洋葱皮**就说明是有效的字符串
17 | + 总体合并起来若符合题意即为**n层洋葱结构**
18 | + **具象化**上述遇到右闭字符而去寻找左开字符且找到后去掉并循往复查找的动作
19 | + 想象一下有哪一种数据结构,可以做到
20 | + 遍历**洋葱**即原字符串
21 | + 遇到左开字符时,加入进去
22 | + 遇到右闭字符时
23 | + 左边是空的
24 | + 则**洋葱结构**受到破坏,返回false
25 | + 左边不是空的,为左开字符
26 | + 如果这个左开字符与当前右闭字符一一对应,则找到了符合题意的**一层洋葱结构**;且去掉当前左字符,继续遍历下一个遇到的右闭字符
27 | + 否则,因**局部洋葱结构**受到了破坏,因而**整体洋葱结构**破坏,是一个无效的洋葱,即无效字符串
28 | + 栈能实现
29 | + 找最近的左开字符
30 | + 对应先入后出
31 | + 去掉最近的左开字符
32 | + 对应出栈
33 | + 而题中只不过是把"()"换成了"()"、"[]"和"{}"而已
34 | + 因此只需再增加一层hashMap对应法,即可
35 | + "(" ---> ")"
36 | + "[" ---> "]"
37 | + "{" ---> "}"
38 | ```javascript
39 | /**
40 | * @param {string} s
41 | * @return {boolean}
42 | */
43 | var isValid = function(s) {
44 | var stack = [];
45 | var map = new Map();
46 | map.set("(",")");
47 | map.set("{","}");
48 | map.set("[","]");
49 | for(var i = 0;i < s.length;i++){
50 | if(!map.get(s[i])){
51 | if(stack.length == 0){
52 | return false;
53 | }
54 | var topEle = stack.pop();
55 | if(map.get(topEle) != s[i]){
56 | return false;
57 | }
58 | }else{
59 | stack.push(s[i]);
60 | }
61 | }
62 | return stack.length == 0;
63 | };
64 | ```
65 | + 上述遇到左边为空的情况时,可以合并到判断 右闭字符 是否对应 左开字符的情况中去
66 | + 即给栈增加初始值:‘#’ != 右闭字符
67 | + 则循环会在中途直接退出
68 | + 若没有遇到
69 | + 则循环会遍历完
70 | + 左开字符组是否被匹配完全部出栈
71 | + 是,则true
72 | + 否,false
73 | ```javascript
74 | /**
75 | * @param {string} s
76 | * @return {boolean}
77 | */
78 | var isValid = function(s) {
79 | var stack = [];
80 | var map = new Map();
81 | map.set("(",")");
82 | map.set("{","}");
83 | map.set("[","]");
84 | for(var i = 0;i < s.length;i++){
85 | // 闭括号
86 | if(!map.get(s[i])){
87 | var topEle = stack.length == 0 ? '#' : stack.pop();
88 | if(map.get(topEle) != s[i]){
89 | return false;
90 | }
91 | }
92 | // 开括号
93 | else{
94 | stack.push(s[i]);
95 | }
96 | }
97 | return stack.length == 0;
98 | };
99 | ```
100 |
--------------------------------------------------------------------------------
/leetCode-0-064-minimum-path-sum.md:
--------------------------------------------------------------------------------
1 | # 最小路径和(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:动态规划
7 | + 思路
8 | + 状态定义:
9 | + 设dp[i][j]为走到当前位置的最小路径和
10 | + 递推公式:
11 | + 只能向下或向右走,意味着当前格子只能由上边或者左边走过来
12 | + **dp[i][j] = Min(dp[i-1][j],dp[i][j-1]) + grid[i][j]**
13 | + 初始化
14 | + 第一行第n列和第一列第n行为均原数组值
15 | + 边界条件
16 | + 格子有边界,因此当i==0 或j==0时,i-1和j-1会越界
17 | + **i = 0,j != 0时,dp[i][j] = dp[i][j-1]+grid[i][j]**
18 | + **i !=0,j == 0时,dp[i][j] = dp[i-1][j]+grid[i][j]**
19 | + **i !=0 && j != 0时,dp[i][j] = Min(dp[i-1][j],dp[i][j-1])+grid[i][j]**
20 | + **i == 0 && j == 0时,dp[i][j]=grid[i][j]**
21 | + 返回值
22 | + dp最后一个元素值
23 | ```javascript
24 | /**
25 | * @param {number[][]} grid
26 | * @return {number}
27 | */
28 | var minPathSum = function(grid) {
29 | var n = grid.length;
30 | var m = grid[0].length;
31 | var dp = Array.from(new Array(n),() => new Array(m));
32 | for(var i = 0;i < n;i++){
33 | for(var j = 0;j < m;j++){
34 | if( i != 0 && j!= 0){
35 | dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
36 | }else if(i == 0 && j!=0){
37 | dp[i][j] = dp[i][j-1]+grid[i][j];
38 | }else if(i != 0 && j==0){
39 | dp[i][j] = dp[i-1][j]+grid[i][j];
40 | }else if(i == 0 && j==0){
41 | dp[i][j] = grid[i][j];
42 | }
43 | }
44 | }
45 | return dp[n-1][m-1];
46 | };
47 | ```
48 | + 空间复杂度优化版+1
49 | + 降维处理
50 | ```javascript
51 | /**
52 | * @param {number[][]} grid
53 | * @return {number}
54 | */
55 | var minPathSum = function(grid) {
56 | var dp = new Array(grid.length);
57 | for(var i = 0;i < grid.length;i++){
58 | for(var j = 0;j < grid[0].length;j++){
59 | if( i != 0 && j!= 0){
60 | dp[j] = Math.min(dp[j-1],dp[j])+grid[i][j];
61 | }else if(i == 0 && j!=0){
62 | dp[j] = dp[j-1]+grid[i][j];
63 | }else if(i != 0 && j==0){
64 | dp[j] = dp[j]+grid[i][j];
65 | }else if(i == 0 && j==0){
66 | dp[j] = grid[i][j];
67 | }
68 | }
69 | }
70 | return dp[grid[0].length-1];
71 | };
72 | ```
73 | + 空间复杂度优化版+2
74 | + 时间复杂度:O(M*N)
75 | + 空间复杂度:O(1)
76 | + 直接修改原数组即可
77 | ```javascript
78 | /**
79 | * @param {number[][]} grid
80 | * @return {number}
81 | */
82 | var minPathSum = function(grid) {
83 | for(var i = 0;i < grid.length;i++){
84 | for(var j = 0;j < grid[0].length;j++){
85 | if( i != 0 && j!= 0){
86 | grid[i][j] = Math.min(grid[i-1][j],grid[i][j-1])+grid[i][j];
87 | }else if(i == 0 && j!=0){
88 | grid[i][j] = grid[i][j-1]+grid[i][j];
89 | }else if(i != 0 && j==0){
90 | grid[i][j] = grid[i-1][j]+grid[i][j];
91 | }else if(i == 0 && j==0){
92 | continue;
93 | }
94 | }
95 | }
96 | return grid[grid.length-1][grid[0].length-1];
97 | };
98 | ```
--------------------------------------------------------------------------------
/leetCode-1-0152-maximum-product-subarray.md:
--------------------------------------------------------------------------------
1 | # 乘积最大子序列(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法:动态规划
7 | + [类似题型:53. 最大子序和-解法二](https://leetcode-cn.com/problems/maximum-subarray/solution/53-zui-da-zi-xu-he-by-alexer-660/)
8 | + 如53题求和
9 | ```javascript
10 | /**
11 | * @param {number[]} nums
12 | * @return {number}
13 | */
14 | var maxSubArray = function(nums) {
15 | var max = nums[0];
16 | var dp = new Array(nums.length);
17 | dp[0] = max;
18 | for(var i = 1;i < nums.length;i++){
19 | dp[i] = Math.max(dp[i-1] + nums[i],nums[i]);
20 | max = Math.max(max,dp[i]);
21 | }
22 | return max;
23 | };
24 | ```
25 | + 此题换成了求积
26 | + 求和
27 | + 和特点
28 | + 加正数和越来越大,加负数和越来越小
29 | + 遇到前面序列和为负数时,不加重置为新的子序列起点为当前节点
30 | + 或者为正数时,加上当前节点
31 | + 因为当前节点也有正负之分
32 | + 所动态转移方程为**dpMax[i] = Max(dp[i-1]+nums[i],nums[i])**
33 | + 求积
34 | + ***子序列必须连续***
35 | + 当前值为负数时,要想乘积最大,则必须乘以前n-1个连续子序列中的最小值
36 | + 乘以正数,正数越大,积会越来越小,不合题意
37 | + 因此本题要多维护一个dpMIn数组,用以表示前n-1个连续子序列中的最小值
38 | + dpMax[i-1] > 0
39 | + nums[i] > 0
40 | + **dpMax[i] = dpMax[i-1] * nums[i]**
41 | + nums[i] <= 0
42 | + **dpMax[i] = dpMin[i-1] * nums[i]**
43 | + **dpMin[i] = nums[i]**
44 | + dpMax[i-1] <= 0
45 | + nums[i] > 0
46 | + **dpMax[i] = nums[i]**
47 | + **dpMin[i] = dpMax[i-1] * nums[i]**
48 | + nums[i] <= 0
49 | + **dpMax[i] = dpMin[i-1] * nums[i]**
50 | + **dpMin[i] = nums[i]**
51 | + 因此可以直接省去以上应用于代码中的if/else判断
52 | + **dpMax[i] = Max(dpMax[i-1] * nums[i],dpMin[i-1] * nums[i],nums[i])**
53 | + **dpMin[i] = Max(dpMax[i-1] * nums[i],dpMin[i-1] * nums[i],nums[i])**
54 | ```javascript
55 | /**
56 | * @param {number[]} nums
57 | * @return {number}
58 | */
59 | var maxProduct = function(nums) {
60 | var n = nums.length;
61 | var dpMax = new Array(n);
62 | var dpMin = new Array(n);
63 | dpMax[0] = nums[0];
64 | dpMin[0] = nums[0];
65 | var max = dpMax[0];
66 | for(var i = 1;i
6 | #### 链表
7 | + [戳看链表各种操作大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
8 | #### 类似题型
9 | + [83. 删除排序链表中的重复元素](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/solution/83-shan-chu-pai-xu-lian-biao-zhong-de-zhong-fu--15/)
10 | #### 解法一:快慢指针
11 | + 思路
12 | + 快指针:跳过重复数,记录下一个前面没有重复数的节点位置
13 | + 慢指针:标记重复数字出现起点,根据链表特性,负责与下一个快指针相连
14 | ```javascript
15 | /**
16 | * Definition for singly-linked list.
17 | * function ListNode(val) {
18 | * this.val = val;
19 | * this.next = null;
20 | * }
21 | */
22 | /**
23 | * @param {ListNode} head
24 | * @return {ListNode}
25 | */
26 | var deleteDuplicates = function(head) {
27 | let dummy = new ListNode(-1)
28 | dummy.next = head
29 | let fast = dummy.next
30 | let slow = dummy
31 | while(fast) {
32 | if(fast.next && fast.val === fast.next.val) {
33 | let sameVal = fast.val
34 | while(fast && sameVal === fast.val) {
35 | fast = fast.next
36 | }
37 | }else{
38 | slow.next = fast
39 | slow = fast
40 | fast = fast.next
41 | }
42 | }
43 | slow.next = fast
44 | return dummy.next
45 | };
46 | ```
47 | + 也可以这样写
48 | ```javascript
49 | /**
50 | * Definition for singly-linked list.
51 | * function ListNode(val) {
52 | * this.val = val;
53 | * this.next = null;
54 | * }
55 | */
56 | /**
57 | * @param {ListNode} head
58 | * @return {ListNode}
59 | */
60 | var deleteDuplicates = function(head) {
61 | let dummy = new ListNode(-1)
62 | dummy.next = head
63 | let fast = dummy.next
64 | let slow = dummy
65 | while(fast) {
66 | while(fast.next && fast.val === fast.next.val) {
67 | fast = fast.next
68 | }
69 | if(slow.next === fast) slow = slow.next;
70 | else slow.next = fast.next;
71 | fast = fast.next
72 | }
73 | return dummy.next
74 | };
75 | ```
76 | #### 解法二:递归
77 | + 思路
78 | + 将解法一断点位置保存在递归函数栈中,去拼接前后符合题意节点
79 | + [参看各类算法模板 - 递归一节 - Python&Java版](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
80 | ```javascript
81 | /**
82 | * Definition for singly-linked list.
83 | * function ListNode(val) {
84 | * this.val = val;
85 | * this.next = null;
86 | * }
87 | */
88 | /**
89 | * @param {ListNode} head
90 | * @return {ListNode}
91 | */
92 | var deleteDuplicates = function(head) {
93 | if(!head) return null
94 | if(head.next && head.val === head.next.val) {
95 | while (head.next && head.val === head.next.val) {
96 | head = head.next
97 | }
98 | return deleteDuplicates(head.next)
99 | }else {
100 | head.next = deleteDuplicates(head.next)
101 | }
102 | return head
103 | };
104 | ```
--------------------------------------------------------------------------------
/leetCode-1-0106-construct-binary-tree-from-inorder-and-postorder-traversal.md:
--------------------------------------------------------------------------------
1 | # 从中序与后序遍历序列构造二叉树(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 二叉树
7 | + [二叉树的前、中、后序遍历 - 解法大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E4%B8%89%E5%BA%8F%E9%81%8D%E5%8E%86.md)
8 | #### 解法一:递归
9 | + [思路同 105. 从前序与中序遍历序列构造二叉树 - 解法一](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/105-cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou--6/)
10 | ```javascript
11 | /**
12 | * Definition for a binary tree node.
13 | * function TreeNode(val) {
14 | * this.val = val;
15 | * this.left = this.right = null;
16 | * }
17 | */
18 | /**
19 | * @param {number[]} inorder
20 | * @param {number[]} postorder
21 | * @return {TreeNode}
22 | */
23 | var buildTree = function(inorder, postorder) {
24 | if(!inorder.length) return null
25 | let n = postorder.length
26 | let tmp = postorder[n-1],mid = inorder.indexOf(tmp)
27 | let root = new TreeNode(tmp)
28 | root.left = buildTree(inorder.slice(0,mid),postorder.slice(0,mid))
29 | root.right = buildTree(inorder.slice(mid + 1),postorder.slice(mid,n-1))
30 | return root
31 | };
32 | ```
33 | #### 解法二:递归简便版
34 | + [思路同 105. 从前序与中序遍历序列构造二叉树 - 解法二](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/105-cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou--6/)
35 | ```javascript
36 | /**
37 | * Definition for a binary tree node.
38 | * function TreeNode(val) {
39 | * this.val = val;
40 | * this.left = this.right = null;
41 | * }
42 | */
43 | /**
44 | * @param {number[]} inorder
45 | * @param {number[]} postorder
46 | * @return {TreeNode}
47 | */
48 | var buildTree = function(inorder, postorder) {
49 | let build = (inorder) => {
50 | if(!inorder.length) return null
51 | let tmp = postorder.pop(),mid = inorder.indexOf(tmp)
52 | let root = new TreeNode(tmp)
53 | root.right = build(inorder.slice(mid + 1))
54 | root.left = build(inorder.slice(0,mid))
55 | return root
56 | }
57 | return build(inorder)
58 | };
59 | ```
60 | #### 解法三:参考解法
61 | + [思路同 105. 从前序与中序遍历序列构造二叉树 - 解法三](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/105-cong-qian-xu-yu-zhong-xu-bian-li-xu-lie-gou--6/)
62 | ```javascript
63 | /**
64 | * Definition for a binary tree node.
65 | * function TreeNode(val) {
66 | * this.val = val;
67 | * this.left = this.right = null;
68 | * }
69 | */
70 | /**
71 | * @param {number[]} inorder
72 | * @param {number[]} postorder
73 | * @return {TreeNode}
74 | */
75 | var buildTree = function(inorder, postorder) {
76 | let p = i = postorder.length - 1;
77 | let build = (stop) => {
78 | if(inorder[i] != stop) {
79 | let root = new TreeNode(postorder[p--])
80 | root.right = build(root.val)
81 | i--
82 | root.left = build(stop)
83 | return root
84 | }
85 | return null
86 | }
87 | return build()
88 | };
89 | ```
--------------------------------------------------------------------------------
/leetCode-0-026-remove-duplicates-from-sorted-array.md:
--------------------------------------------------------------------------------
1 | # 删除排序数组中的重复项(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | # 解法一
7 | 
8 | # 解法二
9 | 
10 |
11 | > 给定一个 排序数组, 需要 原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度 -- <1>
12 | > 不得使用额外的数组空间, 必须 在原地修改输入数组并在使用O(1)额外空间的条件下完成 -- <2>
13 | > 示例: 给定 nums = [1,1,2]
14 | > 函数应该返回新的长度 2 && 原数组 nums 的前两个元素被修改为 1,2 即 nums = [1.2,2]--<3>
15 | > 不需要考虑数组中超出新长度后面的元素 --<4>
16 | > 摘抄题目解释内部操作 根据返回函数的长度len,会打印出该长度范围内的元素 --<5>
17 | ```javascript
18 | for(var i = 0; i<2><3> 可知 要求的函数是对原数组中的元素进行修改 是虚拟替换重复元素从而达到删除的效果
25 | + 由<2><4><5> 可知 返回结果为实际数组新长度的必要意义
26 | + 1、函数调用之后 原数组长度不变
27 | + 2、不能新建一个数组存储 删除重复数据后的数组,因为空间复杂度要求为 O(1)
28 | + 3、根据修改替换原数组重复元素 和 返回长度 动态遍历出 不准新建的新数组里应该有的元素
29 |
30 | + 示例:
31 | ```javascript
32 | [1,1,2] => [1,2,2] return [1,2].length == 2
33 | [0,0,1,1,1,2,2,3,3,4] => [0,1,2,3,4,2,2,3,3,4] return [0,1,2,3,4].length == 5
34 | [1,2,3,3,4,5,6,6,7] => [1,2,3,4,5,6,7,6,7] return [1,2,3,4,5,6,7].length == 7
35 | ```
36 | + 由示例规律可得:
37 | + 返回的长度 = 原数组的长度 - 重复元素的长度
38 | + 元素被修改后的数组 = 原数组经历右边操作:
39 | + <1> 设置数组重复元素的个数为 count = 0
40 | + <2> 从数组第二个元素开始遍历 i = 1;i 当遇到第一个重复的元素时 count++
42 | + <4> 当遍历到与上一个的元素不重复即不相同时 此时当前元素需要向前移 n 个重复元素的位置即替换即相当于替换最近一次重复元素第一次出现的索引位置上的元素 最近一次重复元素第一次出现的位置上的元素 = 当前元素 => nums[i-count] = nums[i] && 符合循环条件则继续遍历回到第<2>步 否则进入下一步
43 | + <5> 返回长度 n - count
44 | + 由此可以生成解法一:
45 | ```javascript
46 | /**
47 | * @param {number[]} nums
48 | * @return {number}
49 | */
50 | var removeDuplicates = function(nums) {
51 | var count = 0;
52 | var n = nums.length;
53 | for(let i = 1;i
67 | + 增加一个 是重复元素且是第一次出现的位置指针 r 默认初始化为 0 ,数组遍历从 i = 1 开始
68 | + 当且仅当遇到下一个不相同即不重复的元素时,更新指针位置为下一个元素(虽然是重复元素但是还是要保留第一个不能被替换) && nums[r] = nums[i]
69 | + 否则指针位置不动,原数组继续遍历
70 | + 数组遍历完后 返回 r+1 (为什么加1?因为是索引位置,而题目要求返回的是长度)
71 | + 由此可生成解法二:【即所谓的双指针法】
72 | ```javascript
73 | /**
74 | * @param {number[]} nums
75 | * @return {number}
76 | */
77 | var removeDuplicates = function(nums) {
78 | var j = 0;
79 | var n = nums.length;
80 | for(let i = 1;i
7 | #### 解法一:暴力加乘
8 | + 时间复杂度 O(n) 原数自身连乘n次
9 | + 空间复杂度 O(1)
10 | + 注意:有可能超出时间限制 js数字只有Number类型 双精度浮点数存储 在2的-53次方到2的53次方之间
11 | + 新增的BigInt 内置对象可以表示任意大的数
12 | ```javascript
13 | /**
14 | * @param {number} x
15 | * @param {number} n
16 | * @return {number}
17 | */
18 | var myPow = function(x, n) {
19 | if( n == 0){
20 | return 1;
21 | }
22 | x = parseFloat(x);
23 | if(n < 0){
24 | x = parseFloat(1/x);
25 | n = -n;
26 | }
27 | var tmp = x;
28 | while(n > 1){
29 | x *= tmp;
30 | n--;
31 | }
32 | return x;
33 | };
34 | ```
35 | #### 解法二:分治
36 | + 时间复杂度:O(logn)
37 | + 每次计算自己的一半
38 | + 空间复杂度:O(logn)
39 | + 因为每次递归要存储 x^(n/2) 的结果 计算O(logn)次
40 | + 技巧递归
41 | 1. terminator
42 | 2. process(split your big problem
43 | 3. drill down (subproblmes)
44 | 4. merge(subresult)
45 | 5. reverse states
46 | > x^n --> 2^10 == (2^5)*(2^5)
47 | 2^5 == (2^2)*(2^2)*2
48 | 2^2 == (2^1)*(2^1)
49 | -->
50 | pow(x,n):
51 | subproblem:subresult = pow(x,n/2)
52 | merge:
53 | if( n%2 == 1){
54 | //odd
55 | result = subresult * subresult * x
56 | }else{
57 | //even
58 | result = subresult*subresult
59 | }
60 | ```javascript
61 | /**
62 | * @param {number} x
63 | * @param {number} n
64 | * @return {number}
65 | */
66 | function divide(x,n){
67 | if( n == 0){
68 | return 1;
69 | }
70 | var subresult = divide(x,parseInt(n/2));
71 | if(n&1 == 1){
72 | return subresult * subresult *x;
73 | }else{
74 | return subresult * subresult;
75 | }
76 | }
77 | var myPow = function(x, n) {
78 | x = parseFloat(x);
79 | if(n < 0){
80 | x = parseFloat(1/x);
81 | n = -n;
82 | }
83 | return divide(x,n);
84 | };
85 | ```
86 | #### 解法三:迭代法 解法二的优化版
87 | + 据说是牛顿迭代法 有兴趣的可以去查查哈
88 | + 时间复杂度:O(logn) 本质跟解法二一样
89 | + 空间复杂度:只需要两个额外变量,所以是O(1)
90 | + 由解法二核心函数divide可知
91 | 1. 每次取一半相当于循环计算了n/2次
92 | 2. 奇数和偶数n的结果差异只是多乘了一个自己而已
93 | 3. 1步可知每次n/2 无论是偶数n还是奇数n最后除2取整都为1
94 | + 但奇数n不除2之前先取膜但话就为1
95 | + 如 n = 5 --> 5,2,1
96 | + 如 n = 4 --> 4,2,1
97 | 4. 所以迭代中可以设置循环体内第一次就取余,为奇数则乘以它自己
98 | + 不论奇数还是偶数n,循环体内每次都要乘以自身
99 | 5. 由4可知,如果是奇数那第一次就会多乘一个自己,不是则每次乘以x即n个x相乘的结果
100 | ```javascript
101 | /**
102 | * @param {number} x
103 | * @param {number} n
104 | * @return {number}
105 | */
106 | var myPow = function(x, n) {
107 | if( n == 0){
108 | return 1;
109 | }
110 | x = parseFloat(x);
111 | if(n < 0){
112 | x = parseFloat(1/x);
113 | n = -n;
114 | }
115 | var subresult = x;
116 | var result = 1;
117 | for(var i = n;i>0;i=parseInt(i/2)){
118 | if(i&1==1){
119 | result = result*subresult;
120 | }
121 | subresult = subresult * subresult;
122 | }
123 | return result;
124 | };
125 | ```
--------------------------------------------------------------------------------
/leetCode-0-075-sort-colors.md:
--------------------------------------------------------------------------------
1 | # 颜色分类(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:计数排序
7 | + [思路完全同 - 经典排序算法讲解 - 计数排序](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/BitOperation%E4%BD%8D%E8%BF%90%E7%AE%97%E3%80%81Bloom%20Filter%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8%E3%80%81LRU%20Cache%E7%BC%93%E5%AD%98%E3%80%81Sorting%20algorithm%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95.md)
8 | ```javascript
9 | /**
10 | * @param {number[]} nums
11 | * @return {void} Do not return anything, modify nums in-place instead.
12 | */
13 | var sortColors = function(nums) {
14 | let countSort = (arr,maxVal) => {
15 | let bucketLen = maxVal + 1;
16 | let bucket = new Array(bucketLen).fill(0);
17 | let sortedI = 0;
18 | let arrLen = arr.length;
19 | for(let i = 0;i < arrLen;i++){
20 | bucket[arr[i]]++;
21 | }
22 | for(let j = 0;j < bucketLen;j++){
23 | while(bucket[j] > 0){
24 | arr[sortedI++] = j;
25 | bucket[j]--;
26 | }
27 | }
28 | return arr;
29 | }
30 | return countSort(nums,2);
31 | };
32 | ```
33 | #### 解法二:两路替换
34 | ```javascript
35 | /**
36 | * @param {number[]} nums
37 | * @return {void} Do not return anything, modify nums in-place instead.
38 | */
39 | var sortColors = function(nums) {
40 | let left = 0;
41 | let n = nums.length;
42 | for(let i = 0;i < n;i++){
43 | if(nums[i] === 0){
44 | [nums[left],nums[i]] = [nums[i],nums[left]];
45 | left++;
46 | }
47 | }
48 | let right = n - 1;
49 | for(let i = right;i >= left;i--){
50 | if(nums[i] === 2){
51 | [nums[right],nums[i]] = [nums[i],nums[right]];
52 | right--;
53 | }
54 | }
55 | };
56 | ```
57 | #### 解法三:一次遍历
58 | + 解法二的 while 版
59 | ```javascript
60 | /**
61 | * @param {number[]} nums
62 | * @return {void} Do not return anything, modify nums in-place instead.
63 | */
64 | var sortColors = function(nums) {
65 | let left = 0;
66 | let right = nums.length - 1;
67 | let i = 0;
68 | while(i <= right){
69 | if(nums[i] === 0){
70 | [nums[left],nums[i]] = [nums[i],nums[left]];
71 | left++;
72 | i++;
73 | }
74 | else if(nums[i] === 2){
75 | [nums[right],nums[i]] = [nums[i],nums[right]];
76 | right--;
77 | }
78 | else {
79 | i++;
80 | }
81 | }
82 | };
83 | ```
84 | + 或者这样写也可
85 | ```javascript
86 | /**
87 | * @param {number[]} nums
88 | * @return {void} Do not return anything, modify nums in-place instead.
89 | */
90 | var sortColors = function(nums) {
91 | let left = 0;
92 | let right = nums.length - 1;
93 | let i = 0;
94 | while(i <= right){
95 | while(nums[i] == 2 && i < right){
96 | [nums[right],nums[i]] = [nums[i],nums[right]];
97 | right--;
98 | }
99 | while(nums[i] == 0 && i > left){
100 | [nums[left],nums[i]] = [nums[i],nums[left]];
101 | left++;
102 | }
103 | i++;
104 | }
105 | };
106 | ```
--------------------------------------------------------------------------------
/leetCode-1-0121-best-time-to-buy-and-sell-stock.md:
--------------------------------------------------------------------------------
1 | # 买卖股票的最佳时机(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | ### 股票6道
7 | + 1、[121. 买卖股票的最佳时机](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
8 | + 2、[122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
9 | + 3、[123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/submissions/)
10 | + 4、[309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/submissions/)
11 | + 5、[188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/submissions/)
12 | + 6、[714. 买卖股票的最佳时机含手续费](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/submissions/)
13 | ## [卍解👇敬请戳看](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/demos/%E8%82%A1%E7%A5%A86%E9%81%93.md)
14 | ___
15 | #### 解法一:动态规划
16 | ```javascript
17 | /**
18 | * @param {number[]} prices
19 | * @return {number}
20 | */
21 | var maxProfit = function(prices) {
22 | let n = prices.length;
23 | if(n == 0){
24 | return 0;
25 | }
26 | let dp = Array.from(new Array(n),() => new Array(2));
27 | for(let i = 0;i < n;i++){
28 | if(i == 0){
29 | dp[i][0] = 0;
30 | dp[i][1] = -prices[i];
31 | continue;
32 | }
33 | dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
34 | dp[i][1] = Math.max(-prices[i],dp[i-1][1]);
35 | }
36 | return dp[n-1][0];
37 | };
38 | ```
39 | #### 解法二:动态规划 + 降维
40 | ```javascript
41 | /**
42 | * @param {number[]} prices
43 | * @return {number}
44 | */
45 | var maxProfit = function(prices) {
46 | let n = prices.length;
47 | if(n == 0){
48 | return 0;
49 | }
50 | var dp_i_0 = 0;
51 | var dp_i_1 = -Infinity;
52 | for(let i = 0;i < n;i++){
53 | dp_i_0 = Math.max(dp_i_0,dp_i_1 + prices[i]);
54 | dp_i_1 = Math.max(-prices[i],dp_i_1);
55 | }
56 | return dp_i_0;
57 | };
58 | ```
59 | #### 解法三:暴力法
60 | + 空间复杂度:O(n^2)
61 | + 时间复杂度:O(1)
62 | + 因限制交易一笔
63 | + 所以可以枚举出所有的交易组合维护max = max(prices[j] = prices[i])即可
64 | ```javascript
65 | /**
66 | * @param {number[]} prices
67 | * @return {number}
68 | */
69 | var maxProfit = function(prices) {
70 | let max = 0;
71 | for(var i = 0;i
7 | #### 解法一:动态规划
8 | + 类似题型
9 | + [10、正则表达式匹配-解法三](https://leetcode-cn.com/problems/regular-expression-matching/solution/10-zheng-ze-biao-da-shi-pi-pei-jiang-ti-jie-fu-zhi/)
10 | + 思路
11 | + 状态定义
12 | + dp[i][j]:表示 s 的前i个字符是否与 p的前j个字符是否匹配
13 | + 状态方程
14 | + **当 s[i] == p[j] 或者p[j] == '?' 时**
15 | + **dp[i][j] = dp[i-1][j-1]**
16 | + **当 s[i] != p[j] && p[j] == '*' 时**
17 | + **dp[i][j] = dp[i-1][j] || dp[i][j-1]**
18 | + dp[i-1][j]:匹配任意非空字符,例如abkk,ab*
19 | + dp[i][j-1]:匹配空字符相当于0个,例如ab,ab*
20 | + 初始化
21 | + dp[0][0]:两个空字符串,为true
22 | + dp[0][j]:当s为空,p为*号时,为true
23 | + p[j-1] == '*' && dp[0][j] = dp[0][j-1]
24 | ```javascript
25 | /**
26 | * @param {string} s
27 | * @param {string} p
28 | * @return {boolean}
29 | */
30 | var isMatch = function(s, p) {
31 | let n = s.length;
32 | let m = p.length;
33 | let dp = Array.from(new Array(n+1),() => new Array(m+1).fill(false));
34 | dp[0][0] = true;
35 | for(let j = 1;j <= m;j++){
36 | if(p[j-1] == '*'){
37 | dp[0][j] = dp[0][j-1];
38 | }
39 | }
40 | for(let i = 1;i <= n;i++){
41 | for(let j = 1;j <= m;j++){
42 | if(s[i-1] == p[j-1] || p[j-1] == '?'){
43 | dp[i][j] = dp[i-1][j-1];
44 | }else if(p[j-1] == '*'){
45 | dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
46 | }
47 | }
48 | }
49 | return dp[n][m];
50 | };
51 | ```
52 | #### 解法二:双指针
53 | + 时间复杂度:O(s*p)
54 | + 空间复杂度:O(1)
55 | + 思路
56 | + 和解法一类似,通过指针逐个儿匹配
57 | + 最后通过都匹配的指针位置与p的末尾位置比较,
58 | + 相同则说明s全部匹配
59 | + 否则,部分匹配不合题意
60 | ```javascript
61 | /**
62 | * @param {string} s
63 | * @param {string} p
64 | * @return {boolean}
65 | */
66 | var isMatch = function(s, p) {
67 | // 字符串s的起点位置
68 | let i = 0;
69 | // 字符串p的起点位置
70 | let j = 0;
71 | // s的当前位置
72 | let s_match = 0;
73 | // *的位置
74 | let startIdx = -1;
75 | let n = s.length;
76 | let m = p.length;
77 | while(i < n){
78 | // 同解法一:dp[i][j] = dp[i-1][j-1] 两指针同时后移
79 | if(j < m && (p[j] == '?' || s[i] == p[j])){
80 | i++;
81 | j++;
82 | }
83 | // 同解法一:dp[i][j] = dp[i][j-1]
84 | // 匹配空串,更新*号的位置,更新s的当前位置,p后移
85 | else if(j < m && p[j] == '*'){
86 | startIdx = j;
87 | s_match = i;
88 | j++;
89 | }
90 | // 同解法一:dp[i][j] = dp[i-1][j]
91 | // 当前字符不匹配,并且也不是*号,回退到上一步
92 | // 匹配一个字符,例如abky,ab*z
93 | // j 回到 * 号的下一个位置
94 | // s_match继续下一个字符,i同样更新
95 | else if(startIdx != -1){
96 | j = startIdx + 1;
97 | s_match++;
98 | i = s_match;
99 | }
100 | // 字符不匹配,也没有 *
101 | else{
102 | return false;
103 | }
104 | }
105 | // 将末尾如有,多余的 * 加上去,相当于匹配空串
106 | // ab,a*******
107 | while(j < m && p[j] == '*'){
108 | j++;
109 | }
110 | // 判断是否能够匹配全部
111 | return j == m;
112 | };
113 | ```
--------------------------------------------------------------------------------
/leetCode-0-047-permutations-ii.md:
--------------------------------------------------------------------------------
1 | # 全排列 II(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 回溯算法系列
7 | + [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/solution/39-zu-he-zong-he-by-alexer-660/)
8 | + [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/solution/40-zu-he-zong-he-ii-by-alexer-660/)
9 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
10 | + [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
11 | + [77. 组合](https://leetcode-cn.com/problems/combinations/solution/77-zu-he-by-alexer-660/)
12 | + [78. 子集](https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-by-alexer-660/)
13 | + [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/solution/90-zi-ji-ii-by-alexer-660/)
14 | #### 解法一:递归回溯
15 | + 类似题型
16 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
17 | + 递归代码模板
18 | + [参看各类算法模板 - 递归一节 - Python&Java版](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
19 | + 超时
20 | + 和46题解法几乎一模一样
21 | + 唯一不同的就是此题可能有重复组合
22 | + 在结果时判断重复的组合不再次进入
23 | ```javascript
24 | /**
25 | * @param {number[]} nums
26 | * @return {number[][]}
27 | */
28 | var permuteUnique = function(nums) {
29 | let n = nums.length;
30 | let res = [];
31 | let tmpPath = [];
32 | let hash = {};
33 | let backtrack = (tmpPath) => {
34 | if(tmpPath.length == n && JSON.stringify(res).indexOf(JSON.stringify(tmpPath)) == -1){
35 | res.push(tmpPath);
36 | return;
37 | }
38 | for(let i = 0;i < n;i++){
39 | if(!hash[i+'-'+nums[i]]){
40 | hash[i+'-'+nums[i]] = true;
41 | tmpPath.push(nums[i]);
42 | backtrack(tmpPath.slice());
43 | hash[i+'-'+nums[i]] = false;
44 | tmpPath.pop();
45 | }
46 | }
47 | }
48 | backtrack(tmpPath);
49 | return res;
50 | };
51 | ```
52 | #### 解法二:递归回溯 + 减枝
53 | + 重复问题
54 | + [1,1,2]
55 | + i = 0 => [ [1,1,2],***[1,2,1]*** ]
56 | + i = 1 => [ ***[1,2,1]*** ]
57 | + i = 2 => [ [2,1,1] ]
58 | + 结果
59 | + [ [1,1,2],[1,2,1],[2,1,1]]
60 | + 剪枝
61 | + 排序原数组
62 | + 相邻两个相同的元素,跳过当前元素,从下一个元素开始组合
63 | + 从源头减少重复数组进入 res
64 | + 例如[1,1,2]
65 | + 当i = 1时, nums[i-1] == nums[i],此时不组合,即i = 1时的组合[1,2,1]不进入结果
66 | + **也说明了排序的意义所在**
67 | + 区别
68 | + 相比解法一,此题从源头判重,重复则子组合都无需进行组合
69 | ```javascript
70 | /**
71 | * @param {number[]} nums
72 | * @return {number[][]}
73 | */
74 | var permuteUnique = function(nums) {
75 | let n = nums.length;
76 | nums = nums.sort((a,b) => {return a - b});
77 | let res = [];
78 | let tmpPath = [];
79 | let hash = {};
80 | let backtrack = (tmpPath) => {
81 | if(tmpPath.length == n){
82 | res.push(tmpPath);
83 | return;
84 | }
85 | for(let i = 0;i < n;i++){
86 | if(hash[i] || (i > 0 && !hash[i-1] && nums[i-1] == nums[i])) continue;
87 | hash[i] = true;
88 | tmpPath.push(nums[i]);
89 | backtrack(tmpPath.slice());
90 | hash[i] = false;
91 | tmpPath.pop();
92 | }
93 | }
94 | backtrack(tmpPath);
95 | return res;
96 | };
97 | ```
--------------------------------------------------------------------------------
/leetCode-0-052-n-queens-ii.md:
--------------------------------------------------------------------------------
1 | # N皇后 II(困难)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法一:递归回溯
8 | + [参考-51. N皇后-解法一](https://leetcode-cn.com/problems/n-queens/solution/51-nhuang-hou-by-alexer-660/)
9 | + 
10 | ```javascript
11 | /**
12 | * @param {number} n
13 | * @return {number}
14 | */
15 | var totalNQueens = function(n) {
16 | let result = new Array(n);
17 | let results = 0;
18 | let dfs = (row,column) => {
19 | let leftColumn = column-1;
20 | let rightColumn = column+1;
21 | for(let i = row - 1;i >= 0;i--){
22 | if(result[i] == column){
23 | return false;
24 | }
25 | if(leftColumn >= 0 && result[i] == leftColumn){
26 | return false;
27 | }
28 | if(rightColumn < n && result[i] == rightColumn){
29 | return false;
30 | }
31 | leftColumn--;
32 | rightColumn++;
33 | }
34 | return true;
35 | }
36 | let Nqueens = (row) => {
37 | if(row == n){
38 | results++;
39 | return;
40 | }
41 | for(let j = 0;j < n;j++){
42 | if(dfs(row,j)){
43 | result[row] = j;
44 | Nqueens(row+1)
45 | }
46 | }
47 | }
48 | Nqueens(0);
49 | return results;
50 | };
51 | ```
52 | #### 解法二:对角线约束
53 | + [参考-51. N皇后-解法二](https://leetcode-cn.com/problems/n-queens/solution/51-nhuang-hou-by-alexer-660/)
54 | ```javascript
55 | /**
56 | * @param {number} n
57 | * @return {number}
58 | */
59 | var totalNQueens = function(n) {
60 | let result = 0;
61 | const dfs = ( subResult = [], row = 0) => {
62 | if (row === n) {
63 | result++;
64 | return;
65 | }
66 | for (let col = 0; col < n; col++) {
67 | if (!subResult.some((j, k) => j === col || j - col === row - k || j - col === k - row)) {
68 | subResult.push(col);
69 | dfs( subResult, row + 1);
70 | subResult.pop();
71 | }
72 | }
73 | }
74 | dfs();
75 | return result;
76 | };
77 | ```
78 | #### 解法三:位运算
79 | + DFS
80 | + 当前列、左斜对角线、右斜对角线
81 | + 二进制为1代表不可放置,0相反
82 | + x & -x :得到最低位的1
83 | + x & (x-1):清零最低位的1
84 | + x & ((1 << n) - 1):将x最高位至第n位(含)清零
85 | ```javascript
86 | /**
87 | * @param {number} n
88 | * @return {number}
89 | */
90 | var totalNQueens = function(n) {
91 | let res = 0;
92 | const dfs = (n,row,cols,pie,na) => {
93 | if (row >= n) {
94 | res++;
95 | return;
96 | }
97 | // 得到当前所有的空位
98 | let bits = (~(cols | pie | na)) & ((1 << n) -1)
99 | while(bits){
100 | // 取最低位的1
101 | let p = bits & -bits
102 | // 把P位置上放入皇后
103 | bits = bits & (bits - 1)
104 | dfs(n,row+1,cols | p,(pie | p) << 1,(na | p) >> 1)
105 | }
106 | }
107 | dfs(n,0,0,0,0);
108 | return res;
109 | };
110 | ```
--------------------------------------------------------------------------------
/leetCode-1-1143-longest-common-subsequence.md:
--------------------------------------------------------------------------------
1 | # 最长公共子序列(中等)
2 | # 题目描述
3 | 
4 | 
5 | # 题目地址
6 |
7 | #### 解法:动态规划
8 | + 思路分析
9 | + 图解
10 | 
11 | + 数学归纳法(***以下dp{str1,str2}代表对str1和str2求最长子序列***)
12 | + 1、text1 = "任意字符串"、 text2 = "任意字符串"
13 | + text1为**单个**字符,则所求为text1
14 | + 如图所示,紫色字母
15 | + text1:B
16 | + text2:A、AB、ABA、ABAZ、ABAZD、ABAZDC
17 | + 最长即为1
18 | + text1为**多个**字符,则
19 | + text1 = "......A"、text2= "..A/......A"
20 | + 末尾字符相同
21 | + 则所求最长公共子序列为
22 | + dp{ text1[n-1] , text2[n-1] } + 1
23 | + 最后一个字符相同,则最长公共子序列一定有这个数
24 | + 如图所示,左边笑脸和上面笑脸处
25 | + text1:BAC
26 | + text2:ABAZDC
27 | + 最长即为:dp{ text1[B~A] , text2[A~D] } + 1 = 2 +1 = 3
28 | + text1 = "......A"、text2= "......B"
29 | + 末尾字符不相同
30 | + 则所求最长公共子序列为
31 | + Math.max( dp{ text1[n-1] , text2[n] } , dp{ text1[n] , text2[n-1] } )
32 | + 如图所示
33 | + text1:BAC
34 | + text2:ABA
35 | + 最长即为:Math.max( dp{ text1[B~A] , text2[A~A] } , dp{ text1[B~C] , text2[A~B] } ) = Math.max(2,1) = 2
36 | + 2、归纳公式
37 | + 设 s1 = text1
38 | + s2 = text2
39 | + row = s1.length
40 | + col = s2.length
41 | + dp[s1,s2]为求接方程
42 | + 当 **s1[row-1] != s2[row-1]** 时
43 | + **dp[s1,s2] = Max( dp[s1-1,s2] , dp[s1,s2-1] )**
44 | + 或者,考虑末尾字符都不要的情况
45 | + dp[s1,s2] = Max( dp[s1-1,s2] , dp[s1,s2-1] , dp[s1-1,s2-1] )
46 | + 即要么两个字符串都不考虑最后一个字符,要么其中一个考虑最后一个字符
47 | + 事实上前面dp[s1-1,s2]求解子问题时候,会包含dp[s1-1,s2-1],且最后一种末尾字符相同的情况下也包含此子问题
48 | + 所以按照第一个dp[s1,s2]方程来即可。
49 | + 当 **s1[row-1] == s2[row-1]** 时
50 | + **dp[s1,s2] = dp[s1-1,s2-1] + 1**
51 | + 或者,考虑剩余所以可能子情况
52 | + dp[s1,s2] = Max( dp[s1-1,s2] , dp[s1,s2-1] , dp[s1-1,s2-1] , (dp[s1-1,s2-1] + 1) )
53 | + 事实上,由以上分析可知
54 | + **最后一个字符相同,则最长公共子序列一定有这个数**
55 | + 但是如果不考虑最后一个字符或者其中一个少考虑最后一个字符,
56 | + 意味着求出的最长公共子序列可能会少一个
57 | + 所以Max()里最大的一定是(dp[s1-1,s2-1] + 1)这种情况,即只需要第一个dp[s1,s2]方程即可。
58 | + 类似题型
59 | + [62. 不同路径](https://leetcode-cn.com/problems/unique-paths/solution/62-bu-tong-lu-jing-by-alexer-660/)
60 | + [63. 不同路径 II](https://leetcode-cn.com/problems/unique-paths-ii/solution/63-bu-tong-lu-jing-ii-by-alexer-660/)
61 | + [70. 爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/solution/70-pa-lou-ti-by-alexer-660/)
62 | ```javascript
63 | /**
64 | * @param {string} text1
65 | * @param {string} text2
66 | * @return {number}
67 | */
68 | var longestCommonSubsequence = function(text1, text2) {
69 | let n = text1.length;
70 | let m = text2.length;
71 | let dp = Array.from(new Array(n+1),() => new Array(m+1).fill(0));
72 | for(let i = 1;i <= n;i++){
73 | for(let j = 1;j <= m;j++){
74 | if(text1[i-1] == text2[j-1]){
75 | dp[i][j] = dp[i-1][j-1] + 1;
76 | }else{
77 | dp[i][j] = Math.max(dp[i][j-1],dp[i-1][j]);
78 | }
79 | }
80 | }
81 | return dp[n][m];
82 | };
83 | ```
--------------------------------------------------------------------------------
/leetCode-1-0387-first-unique-character-in-a-string.md:
--------------------------------------------------------------------------------
1 | # 字符串中的第一个唯一字符(简单)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:哈希 + Map
7 | + 哈希
8 | + 唯一性、查询O(1)
9 | + Map
10 | + 按插入顺序存储数据
11 | + 从左到右遍历
12 | + 第一次存在于哈希表,则存进去,并插入到map中
13 | + 非首次,从map中删除,说明当前字符不属于结果中
14 | + 最后返回map中第一个值
15 | ```javascript
16 | /**
17 | * @param {string} s
18 | * @return {number}
19 | */
20 | var firstUniqChar = function(s) {
21 | let hash = {};
22 | let result = new Map();
23 | for(let i = 0;i < s.length;i++){
24 | if(!hash[s[i]]){
25 | hash[s[i]] = 1;
26 | result.set(s[i],i);
27 | }else{
28 | result.delete(s[i]);
29 | }
30 | }
31 | if(result.size == 0){
32 | return -1;
33 | }
34 | return result.values().next().value;
35 | };
36 | ```
37 | #### 解法二:哈希 + 遍历
38 | + 与上面唯一不同的是,这里最后是通过遍历来查询第一个符合的结果
39 | ```javascript
40 | /**
41 | * @param {string} s
42 | * @return {number}
43 | */
44 | var firstUniqChar = function(s) {
45 | let hash = {};
46 | let result = [];
47 | for(let i = 0;i < s.length;i++){
48 | if(!hash[s[i]]){
49 | hash[s[i]] = 1;
50 | }else{
51 | hash[s[i]]++;
52 | }
53 | }
54 | for(let j = 0;j < s.length;j++){
55 | if(hash[s[j]] == 1){
56 | return j;
57 | }
58 | }
59 | return -1;
60 | };
61 | ```
62 | #### 解法三:库函数
63 | + 从首字符开始寻找和从末尾开始寻找得到的相同字符的两个索引如果一样,说明只有一个字符,且一定是第一个出现的
64 | + 虽然方便,但当前所有解法中,解法一是最快的
65 | ```javascript
66 | /**
67 | * @param {string} s
68 | * @return {number}
69 | */
70 | var firstUniqChar = function(s) {
71 | for(let i = 0;i < s.length;i++){
72 | if(s.indexOf(s[i]) == s.lastIndexOf(s[i])){
73 | return i;
74 | }
75 | }
76 | return -1;
77 | };
78 | ```
79 | #### 解法四:计数排序
80 | + 是当前所有解法中,执行用时最少的
81 | ```javascript
82 | /**
83 | * @param {string} s
84 | * @return {number}
85 | */
86 | var firstUniqChar = function(s) {
87 | let countingSort = (arr,maxValue) => {
88 | let bucket = new Array(maxValue).fill(0);
89 | let arrLen = arr.length;
90 | for(let i = 0;i < arrLen;i++){
91 | bucket[arr[i].charCodeAt() - 97]++;
92 | }
93 | for(let j = 0;j < arrLen;j++){
94 | if(bucket[arr[j].charCodeAt() - 97] == 1){
95 | return j;
96 | }
97 | }
98 | return -1;
99 | }
100 | return countingSort(s,26)
101 | };
102 | ```
103 | #### 解法五:遍历 + 比较
104 | + 题意只有小写字母
105 | + 因此最多只会出现26个字母中的一个
106 | + 遍历26个字母,原字符串中首尾搜索是同一个字符且是同一索引值,
107 | + 且索引最小的最靠前,也就是第一个只出现一个的字符字母
108 | + 通过比较最小值
109 | ```javascript
110 | /**
111 | * @param {string} s
112 | * @return {number}
113 | */
114 | var firstUniqChar = function(s) {
115 | if(s.length === 1) {
116 | return 0;
117 | }
118 | let base = ['a', 'b', 'c', 'd', 'e', 'f', 'g',
119 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
120 | 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
121 | 'z'
122 | ];
123 | let minIndex = Number.MAX_SAFE_INTEGER, firstIndex;
124 | for(let i = 0; i < base.length; i++) {
125 | firstIndex = s.indexOf(base[i]);
126 | if(firstIndex >=0 && firstIndex === s.lastIndexOf(base[i])) {
127 | minIndex = Math.min(minIndex, firstIndex);
128 | }
129 | }
130 | return (minIndex ^ Number.MAX_SAFE_INTEGER) == 0 ? -1 : minIndex;
131 | };
132 | ```
--------------------------------------------------------------------------------
/leetCode-0-091-decode-ways.md:
--------------------------------------------------------------------------------
1 | # 解码方法(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 解法一:动态规划
7 | + 思路
8 | >'A' -> 1
9 | 'B' -> 2
10 | ...
11 | 'Z' -> 26
12 | + 由示例和题中转码公式可知
13 | + 数字转换为字母,最大支持26,最小支持1:
14 | + 数字组合**最大为两位数,且小于27,大于0**
15 | + 当为1位数时,形如**X**
16 | + 0
17 | + 首字母,返回0
18 | + 非首字母,跳过,此处组合为0
19 | + 非0
20 | + 本身为1个组合
21 | + **转移方程**
22 | + **s[i-1] != 0 && dp[i] = dp[i-1] + dp[i]**
23 | + 当为2位数时,形如**nX**
24 | + 1X
25 | + 10、11、12...19 所以X可以是任意值
26 | + **转移方程**
27 | + **s[i-2] == 1 && dp[i] = dp[i] + dp[i-2]**
28 | + 2X
29 | + 20、21、21...26 所以 X < 7
30 | + **转移方程**
31 | + **s[i-2] == 2 && s[i-1] < 7 && dp[i] = dp[i] + dp[i-2]**
32 | + 3X、4X、5X、6X、7X、8X、9X
33 | + 30、40、50、60、70、80、90
34 | + 所以 n > 3 时,X只能为0,可以直接跳过
35 | + 因此可以归纳到第一种情况中去,为1位数:形如**X**,直接跳过
36 | + 数字为 0 时,无法解码
37 | + 又根据题意,首数字必须可以解码,如果数字第一位为0,则解码总数为0,视为无效
38 | ```javascript
39 | /**
40 | * @param {string} s
41 | * @return {number}
42 | */
43 | var numDecodings = function(s) {
44 | if(s[0] == 0){
45 | return 0;
46 | }
47 | let n = s.length;
48 | let dp = new Array(n+1).fill(0);
49 | dp[0] = dp[1] = 1;
50 | for(let i = 2;i <= n;i++){
51 | if(s[i-1] != 0){
52 | dp[i] += dp[i-1];
53 | }
54 | if((s[i-2] == 1) || (s[i-2] == 2 && s[i-1] <= 6)){
55 | dp[i] += dp[i-2];
56 | }
57 | }
58 | return dp[n];
59 | };
60 | ```
61 | #### 解法二:递归
62 | + 通过索引 穷举 i-2和i-1的所有可能组合情况
63 | ```javascript
64 | /**
65 | * @param {string} s
66 | * @return {number}
67 | */
68 | var numDecodings = function(s) {
69 | if(s[0] == 0){
70 | return 0;
71 | }
72 | let n = s.length;
73 | let helper = (start) => {
74 | if(start == n){
75 | return 1;
76 | }
77 | if(s[start] == 0){
78 | return 0;
79 | }
80 | let odd = helper(start+1);
81 | let even = 0;
82 | if(start < n - 1){
83 | let ten = s[start];
84 | let one = s[start+1];
85 | if((ten+''+one) < 27){
86 | even = helper(start+2);
87 | }
88 | }
89 | return odd + even;
90 | }
91 | return helper(0);
92 | };
93 | ```
94 | #### 解法三:递归 + 备忘录
95 | ```javascript
96 | /**
97 | * @param {string} s
98 | * @return {number}
99 | */
100 | var numDecodings = function(s) {
101 | if(s[0] == 0){
102 | return 0;
103 | }
104 | let n = s.length;
105 | let memo = new Map();
106 | let helper = (start) => {
107 | if(start == n){
108 | return 1;
109 | }
110 | if(s[start] == 0){
111 | return 0;
112 | }
113 | let memoVal = memo.get(start);
114 | if(memoVal){
115 | return memoVal;
116 | }
117 | let odd = helper(start+1,memo);
118 | let even = 0;
119 | if(start < n - 1){
120 | let ten = s[start];
121 | let one = s[start+1];
122 | if((ten+''+one) < 27){
123 | even = helper(start+2,memo);
124 | }
125 | }
126 | let res = odd + even;
127 | memo.set(start,res);
128 | return res;
129 | }
130 | return helper(0,memo);
131 | };
132 | ```
--------------------------------------------------------------------------------
/leetCode-1-0213-house-robber-ii.md:
--------------------------------------------------------------------------------
1 | # 打家劫舍 II(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 类似题型
7 | + [198. 打家劫舍](https://leetcode-cn.com/problems/house-robber/solution/198-da-jia-jie-she-by-alexer-660/)
8 | + 所有解法跟198题解法几乎完全一样
9 | + 区别就是本题第一个房子和最后一个房子连在一起,而且同样保留198题的相邻房子不能同时偷的原则
10 | + 所以此题可以分为两种情况
11 | + 偷第一家,不能偷最后一家
12 | + 不偷第一家,能偷最后一家
13 | + 因此在代码中,直接截取掉第一个和最后一个数字分解成两个子问题求解即可。
14 | #### 解法一:动态规划
15 | ```javascript
16 | /**
17 | * @param {number[]} nums
18 | * @return {number}
19 | */
20 | var rob = function(nums) {
21 | var n = nums.length;
22 | if(n == 1){
23 | return nums[0];
24 | }else if(n == 0){
25 | return 0;
26 | }
27 | function dpGO(nums){
28 | var n = nums.length;
29 | var dp = Array.from(new Array(n),() => new Array(n));
30 | dp[0][0] = 0;
31 | dp[0][1] = nums[0];
32 | for(var i = 1;i < nums.length;i++){
33 | dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]);
34 | dp[i][1] = nums[i]+dp[i-1][0];
35 | }
36 | return Math.max(dp[n-1][0],dp[n-1][1]);
37 | }
38 | var need1 = dpGO(nums.slice(1));
39 | var need2 = dpGO(nums.slice(0,nums.length-1));
40 | return Math.max(need1,need2);
41 | };
42 | ```
43 | #### 解法二:动态规划降维
44 | ```javascript
45 | /**
46 | * @param {number[]} nums
47 | * @return {number}
48 | */
49 | var rob = function(nums) {
50 | var n = nums.length;
51 | if(n == 1){
52 | return nums[0];
53 | }else if(n == 0){
54 | return 0;
55 | }
56 | function dpGO(nums){
57 | var dp = new Array(n-1);
58 | dp[0] = 0;
59 | dp[1] = nums[0];
60 | for(var i = 2;i < n;i++){
61 | dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
62 | }
63 | return dp[n-1];
64 | }
65 | var need1 = dpGO(nums.slice(1));
66 | var need2 = dpGO(nums.slice(0,nums.length-1));
67 | return Math.max(need1,need2);
68 | };
69 | ```
70 | #### 解法三:动态规划-去维
71 | ```javascript
72 | /**
73 | * @param {number[]} nums
74 | * @return {number}
75 | */
76 | var rob = function(nums) {
77 | var n = nums.length;
78 | if(n == 1){
79 | return nums[0];
80 | }else if(n == 0){
81 | return 0;
82 | }
83 | function dpGO(nums){
84 | var prevMax = 0;
85 | var currMax = 0;
86 | for(var i = 0;i < nums.length;i++){
87 | var tmp = currMax;
88 | currMax = Math.max(currMax,prevMax+nums[i]);
89 | prevMax = tmp;
90 | }
91 | return currMax;
92 | }
93 | var need1 = dpGO(nums.slice(1));
94 | var need2 = dpGO(nums.slice(0,nums.length-1));
95 | return Math.max(need1,need2);
96 | };
97 | ```
98 | #### 解法四:间隔步数
99 | ```javascript
100 | /**
101 | * @param {number[]} nums
102 | * @return {number}
103 | */
104 | var rob = function(nums) {
105 | var n = nums.length;
106 | if(n == 1){
107 | return nums[0];
108 | }else if(n == 0){
109 | return 0;
110 | }
111 | function dpGO(nums){
112 | var oddSum = 0;
113 | var evenSum = 0;
114 | for(var i = 0;i
6 | #### 解法:
7 | + 类似题型
8 | + [200.岛屿数量](https://leetcode-cn.com/problems/number-of-islands/solution/200-dao-yu-shu-liang-by-alexer-660/)
9 | + 思路
10 | 
11 | + 分析
12 | + 由题意可知,初始化机器人位置为原点(0,0)
13 | + 初始化坐标系
14 | + dx = [0,1,0,-1]
15 | + dy = [1,0,-1,0]
16 | + 其实就是坐标系点从北部方向顺时针方向存放点坐标的方向向量数组
17 | + dx和dy一一对应一个坐标点
18 | + 机器人行走方向
19 | + (-2):左转,O->W,Wx = (Ox+3)%4
20 | + (-1):右转,O->E,Wx = (Ox+1)%4
21 | + 解释
22 | + 方向只有东西南北四个方向,所以无论怎么走,都不会超出方向向量数组里的取值范围
23 | + 因此通过取模来循环取对应数组的位置
24 | + (x): 机器人不转弯并向前走X个格子
25 | + 一步一步走,从1开始遍历
26 | + 判断下一步是否有障碍物
27 | + 有,继续走
28 | + 无,进入下一轮
29 | + 解题技巧
30 | + 存储障碍物为hash表,提升程序性能
31 | ```javascript
32 | /**
33 | * @param {number[]} commands
34 | * @param {number[][]} obstacles
35 | * @return {number}
36 | */
37 | var robotSim = function(commands, obstacles) {
38 | var dx = [0,1,0,-1];
39 | var dy = [1,0,-1,0];
40 | var di = 0;
41 | var endX = 0;
42 | var endY = 0;
43 | var result = 0;
44 | var hashObstacle = {};
45 | for(var r = 0;r
7 | ## 链表
8 | + [参看链表各种操作大全](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/algo/%E9%93%BE%E8%A1%A8_linkedList.md)
9 | #### 解法一:标记法
10 | + [思路同-141. 环形链表-解法二](https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-by-alexer-660/)
11 | ```javascript
12 | /**
13 | * Definition for singly-linked list.
14 | * function ListNode(val) {
15 | * this.val = val;
16 | * this.next = null;
17 | * }
18 | */
19 |
20 | /**
21 | * @param {ListNode} head
22 | * @return {ListNode}
23 | */
24 | var detectCycle = function(head) {
25 | while(head && head.next){
26 | if(head.flag){
27 | return head;
28 | }else{
29 | head.flag = 1;
30 | head = head.next;
31 | }
32 | }
33 | return null;
34 | };
35 | ```
36 | #### 解法二:数组判重
37 | + [思路同-141. 环形链表-解法一](https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-by-alexer-660/)
38 | ```javascript
39 | /**
40 | * Definition for singly-linked list.
41 | * function ListNode(val) {
42 | * this.val = val;
43 | * this.next = null;
44 | * }
45 | */
46 |
47 | /**
48 | * @param {ListNode} head
49 | * @return {ListNode}
50 | */
51 | var detectCycle = function(head) {
52 | let res = [];
53 | while(head != null){
54 | if(res.includes(head)){
55 | return head;
56 | }else{
57 | res.push(head);
58 | }
59 | head = head.next;
60 | }
61 | return null;
62 | };
63 | ```
64 | #### 解法三:双指针
65 | + 思路
66 | + 图解
67 | + 
68 | + 公式
69 | + S:初始点到环的入口点A的距离
70 | + m:环的入口点到快慢双指针在环内的相遇点B的距离
71 | + L:环的周长
72 | + 如果 S == L - m
73 | + 那么可以设置两个步数相同的指针分别从,链表入口节点和快慢双指针相遇节点同时出发
74 | + 当他们第一次相遇时,即是环的入口节点A所在
75 | + 因此,我们需要证明 S == L - m
76 | + 已知快指针的行走距离是慢指针行走距离的两倍
77 | + 那么他们在环内第一次相遇时
78 | + 慢指针走过了:S + xL
79 | + 快指针走过了:S + yL
80 | + 那么,设C为指针走过的距离
81 | + C(快) - C(慢) = (y-x)L = nL
82 | + C(慢) = S + m
83 | + 因为C(快) == 2C(慢)
84 | + 所以C(快) - C(慢) == C(慢)
85 | + S + m = nL
86 | + S = nL - m
87 | + 而L为环的周长 ,n为任意正整数
88 | + 所以 S == L - m 成立
89 | + 解即为反证法的操作
90 | + 判断链表是否有环
91 | + [思路同-141. 环形链表-解法三](https://leetcode-cn.com/problems/linked-list-cycle/solution/141-huan-xing-lian-biao-by-alexer-660/)
92 | ```javascript
93 | /**
94 | * Definition for singly-linked list.
95 | * function ListNode(val) {
96 | * this.val = val;
97 | * this.next = null;
98 | * }
99 | */
100 |
101 | /**
102 | * @param {ListNode} head
103 | * @return {ListNode}
104 | */
105 | var detectCycle = function(head) {
106 | if(!head || !head.next) return null;
107 | let slow = head;
108 | let fast = head;
109 | let start = head;
110 | while (fast != null && fast.next != null) {
111 | slow = slow.next;
112 | fast = fast.next.next;
113 | if (slow == fast) {
114 | while (start != slow) {
115 | slow = slow.next;
116 | start = start.next;
117 | }
118 | return slow;
119 | }
120 | }
121 | return null;
122 | };
123 | ```
--------------------------------------------------------------------------------
/leetCode-0-039-combination-sum.md:
--------------------------------------------------------------------------------
1 | # 39. 组合总和(中等)
2 | # 题目描述
3 | 
4 | # 题目地址
5 |
6 | #### 回溯算法系列
7 | + [39. 组合总和](https://leetcode-cn.com/problems/combination-sum/solution/39-zu-he-zong-he-by-alexer-660/)
8 | + [40. 组合总和 II](https://leetcode-cn.com/problems/combination-sum-ii/solution/40-zu-he-zong-he-ii-by-alexer-660/)
9 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
10 | + [47. 全排列 II](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
11 | + [77. 组合](https://leetcode-cn.com/problems/combinations/solution/77-zu-he-by-alexer-660/)
12 | + [78. 子集](https://leetcode-cn.com/problems/subsets/solution/78-zi-ji-by-alexer-660/)
13 | + [90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/solution/90-zi-ji-ii-by-alexer-660/)
14 | #### 解法一:递归回溯
15 | + 类似题型
16 | + [46. 全排列](https://leetcode-cn.com/problems/permutations/solution/46-quan-pai-lie-by-alexer-660/)
17 | + 递归代码模板
18 | + [参看各类算法模板 - 递归一节 - Python&Java版](https://github.com/Alex660/Algorithms-and-data-structures/blob/master/theoreticalKnowledge/AlgorithmTemplate%E7%AE%97%E6%B3%95%E6%A8%A1%E6%9D%BF.md)
19 | + 思路
20 | + 由题可知,原数组
21 | + 元素不重复
22 | + 寻找一个符合条件的组合
23 | + 且原数组的单个元素可以重复使用
24 | + 只要结果中的子组合互不相同即可
25 | + 求解
26 | + **且原数组的单个元素可以重复使用**
27 | + 意味着下一个for循环中的元素选取,要从前一个元素开始,因为可以重复使用,不然如果跟着for的自增变量i走,会漏掉可能解
28 | + 将自增变量i传递下去
29 | + 终止条件
30 | + target 一一减去符合组合的元素,最终为 0 ,才是一个符合题意的组合
31 | ```javascript
32 | /**
33 | * @param {number[]} candidates
34 | * @param {number} target
35 | * @return {number[][]}
36 | */
37 | var combinationSum = function(candidates, target) {
38 | let n = candidates.length;
39 | let res = [];
40 | let tmpPath = [];
41 | let backtrack = (tmpPath,target,start) => {
42 | if(target < 0){
43 | return;
44 | }
45 | if(target == 0){
46 | res.push(tmpPath);
47 | return;
48 | }
49 | for(let i = start;i < n;i++){
50 | tmpPath.push(candidates[i]);
51 | backtrack(tmpPath.slice(),target - candidates[i],i);
52 | tmpPath.pop();
53 | }
54 | }
55 | backtrack(tmpPath,target,0);
56 | return res;
57 | };
58 | ```
59 | #### 解法二:递归回溯 + 减枝
60 | + 类似题型
61 | + [47. 全排列 II - 解法二](https://leetcode-cn.com/problems/permutations-ii/solution/47-quan-pai-lie-ii-by-alexer-660/)
62 | + 重复问题
63 | + [1,3,5,6] target = 8
64 | + 当tmpPath = [1,3]时
65 | + target = 8 - 1 - 3 = 4
66 | + 此时 4 < 5
67 | + 因此之后比5更大的元素也是不合题意的,对于所有组合中以[1,3]为首的组合无需再进行下一步组合,直接进行下一轮组合
68 | + 当以[1,3]两个元素为尾部或中间部分的可能组合就有可能是正确的
69 | + 结果
70 | + [[1,1,1,1,1,1,1,1],[1,1,1,1,1,3],[1,1,1,5],[1,1,3,3],[1,1,6],[3,5]]
71 | + 剪枝
72 | + 排序原数组
73 | + 如重复问题示例操作,翻译成代码即可
74 | + **也说明了排序的意义所在**
75 | ```javascript
76 | /**
77 | * @param {number[]} candidates
78 | * @param {number} target
79 | * @return {number[][]}
80 | */
81 | var combinationSum = function(candidates, target) {
82 | let n = candidates.length;
83 | let res = [];
84 | let tmpPath = [];
85 | candidates = candidates.sort((a,b) => {return a - b})
86 | let backtrack = (tmpPath,target,start) => {
87 | if(target == 0){
88 | res.push(tmpPath);
89 | return;
90 | }
91 | for(let i = start;i < n;i++){
92 | if(target < candidates[i]) break;
93 | tmpPath.push(candidates[i]);
94 | backtrack(tmpPath.slice(),target - candidates[i],i);
95 | tmpPath.pop();
96 | }
97 | }
98 | backtrack(tmpPath,target,0);
99 | return res;
100 | };
101 | ```
--------------------------------------------------------------------------------