├── 1.排序
├── 冒泡排序.md
├── 十大排序对比.md
├── 堆排序.md
├── 希尔排序.md
├── 归并排序.md
├── 快速排序.md
├── 插入排序.md
└── 选择排序.md
├── 2.二分查找
└── 二分查找.md
├── 3.栈、队列
├── 双栈模拟队列.md
├── 将无序栈转为有序栈.md
└── 有效的括号.md
├── 4.链表
├── 两个链表的第一个公共节点.md
├── 两数相加.md
├── 判断链表是否有环.md
├── 反转链表.md
├── 合并两个有序链表.md
├── 手撕单向链表.md
└── 链表中倒数第K个节点.md
├── 5.二叉树
├── 中序遍历.md
├── 之字形打印二叉树.md
├── 二叉搜索树的后序遍历序列.md
├── 二叉搜索树的第K个节点.md
├── 二叉树.md
├── 二叉树的下一个节点.md
├── 二叉树的最大宽度.md
├── 二叉树的深度.md
├── 二叉树的镜像.md
├── 前序遍历.md
├── 后序遍历.md
├── 对称二叉树.md
├── 层次遍历.md
├── 平衡二叉树.md
├── 按层打印二叉树.md
└── 重建二叉树.md
├── 6.双指针
├── 和为S的两个数(微改版).md
├── 和为S的连续正整数.md
├── 字符串压缩.md
├── 接雨水.md
├── 滑动窗口的最大值.md
├── 盛最多水的容器.md
└── 验证回文串.md
├── 7.动态规划
├── 不同路径.md
├── 斐波那契数列、跳台阶、矩形覆盖.md
├── 最小路径和.md
├── 最长回文子串.md
├── 最长有效括号.md
└── 编辑距离.md
├── 8.前端
├── Promise封装异步上传图片.md
├── new的过程.md
├── spawn函数(Async函数实现原理).md
├── thunk函数(Generator函数实现自动流程管理原理).md
├── 函数柯里化.md
├── 实现Instanceof.md
├── 封装apply.md
├── 封装bind.md
├── 封装call.md
├── 封装map.md
├── 手撕Promise(A+规范).md
├── 手撕Promise(简易版).md
├── 手撕Promise.all.md
├── 数组去重.md
├── 数组扁平化.md
├── 深拷贝.md
├── 红黄绿灯(字节).md
├── 节流.md
└── 防抖.md
└── README.md
/1.排序/冒泡排序.md:
--------------------------------------------------------------------------------
1 | # 冒泡排序
2 |
3 | ## 思路
4 |
5 | > 外层控制循环次数,内层用来比较
6 |
7 | ## 代码
8 |
9 | > ### 平均时间复杂度:O(n^2) 空间复杂度:O(1)
10 |
11 | ```js
12 | function bubbleSort(arr) {
13 | for (let i = arr.length - 1, temp; i > 0; i--) {
14 | for (let j = 0; j < i; j++) {
15 | temp = arr[j];
16 | if (temp > arr[j + 1]) {
17 | arr[j] = arr[j + 1];
18 | arr[j + 1] = temp;
19 | }
20 | }
21 | }
22 | return arr;
23 | }
24 | ```
25 |
--------------------------------------------------------------------------------
/1.排序/十大排序对比.md:
--------------------------------------------------------------------------------
1 | # 十大排序对比
2 |
3 | ## 术语说明
4 |
5 | > 稳定:如果 a 原本在 b 前面,且 a=b,排序之后 a 仍然在 b 的前面
6 | > 不稳定:如果 a 原本在 b 的前面,且 a=b,排序之后 a 可能会出现在 b 的后面
7 | > 内排序:所有排序操作都在内存中完成
8 | > 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
9 | > 时间复杂度:一个算法执行所耗费的时间
10 | > 空间复杂度:运行完一个程序所需内存的大小
11 |
12 | 
13 |
14 | ### 上述名词解释
15 |
16 | > n: 数据规模
17 | > k: “桶”的个数
18 | > In-place: 占用常数内存,不占用额外内存
19 | > Out-place: 占用额外内存
20 |
--------------------------------------------------------------------------------
/1.排序/堆排序.md:
--------------------------------------------------------------------------------
1 | # 堆排序
2 |
3 | ## 思路
4 |
5 | > (1) 将初始待排序关键字序列(R1,R2,…,Rn)构建成大顶堆,此堆为初始堆无序区
6 | > (2) 将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新堆无序区(R1,R2,…,Rn-1)和新堆有序区(Rn),且满足 R[1,2,…,n-1]
7 | > (3) 由于交换后新堆堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,…,Rn-1)调整为新堆,
8 | > 然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2,…,Rn-2)和新的有序区(Rn-1,Rn)。
9 | > 不断重复此过程,直到有序区的元素个数为 n-1,则整个排序过程完成
10 |
11 | ## 代码
12 |
13 | > ### 平均时间复杂度:O(n log n) 空间复杂度:O(1)
14 |
15 | ```js
16 | function heapSort(arr) {
17 | const len = arr.length;
18 | for (let i = len; i > 1; i--) {
19 | buildHeap(arr, i);
20 | let temp = arr[0];
21 | arr[0] = arr[i - 1];
22 | arr[i - 1] = temp;
23 | }
24 | return arr;
25 | }
26 |
27 | function buildHeap(arr, end) {
28 | for (let i = end - 1; i > 0; i--) {
29 | if (arr[i] > arr[i - 1]) {
30 | let temp = arr[i];
31 | arr[i] = arr[i - 1];
32 | arr[i - 1] = temp;
33 | }
34 | }
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/1.排序/希尔排序.md:
--------------------------------------------------------------------------------
1 | # 希尔排序
2 |
3 | ## 思路
4 |
5 | > 只是给插入排序外加了增量 gap
6 |
7 | ## 代码
8 |
9 | > ### 平均时间复杂度:O(n log n) 空间复杂度:O(1)
10 |
11 | ```js
12 | function shellSort(arr){
13 | let gap = arr.length;
14 | while(gap > 1){
15 | gap = parseInt(gap/2);
16 | for(let i = gap;i < arr.length;i++){
17 | let temp = arr[i];
18 | let j = i-gap;
19 | while(arr[j] > temp && j >= 0){
20 | arr[j+gap] = arr[j];
21 | j- = gap;
22 | }
23 | arr[j+gap] = temp;
24 | }
25 | }
26 | return arr;
27 | }
28 | ```
29 |
--------------------------------------------------------------------------------
/1.排序/归并排序.md:
--------------------------------------------------------------------------------
1 | # 归并排序
2 |
3 | ## 思路
4 |
5 | > (1) 把长度为 n 的输入序列分为两个长度为 n/2 的子序列
6 | > (2) 对这两个子序列分别采用归并排序
7 | > (3) 将两个排序好的子序列合并成一个最终的排序序列
8 |
9 | ## 代码
10 |
11 | > ### 平均时间复杂度:O(n log n) 空间复杂度:O(n)
12 |
13 | ```js
14 | function mergeSort(arr) {
15 | const len = rr.length;
16 | if (len < 2) {
17 | return arr;
18 | }
19 | let middle = parseInt(len / 2);
20 | let left = arr.slice(0, middle);
21 | let right = arr.slice(middle);
22 | return merge(mergeSort(left), mergeSort(right));
23 | }
24 | function merge(left, right) {
25 | let result = [];
26 | while (left.length && right.length) {
27 | if (left[0] < right[0]) {
28 | result.push(left.shift());
29 | } else {
30 | result.push(right.shift());
31 | }
32 | }
33 | if (left.length === 0) {
34 | result = result.concat(right);
35 | } else if (right.length === 0) {
36 | result = result.concat(left);
37 | }
38 | return result;
39 | }
40 | ```
41 |
--------------------------------------------------------------------------------
/1.排序/快速排序.md:
--------------------------------------------------------------------------------
1 | # 快速排序
2 |
3 | ## 思路
4 |
5 | > 选择一个元素作为基准,把小于基准的元素放在基准的左边,把大于基准的元素放在基准的右边,对于基准左右两边
6 | > 的值重复递归一二步,直至正序排序
7 |
8 | ## 代码
9 |
10 | > ### 平均时间复杂度:O(n log n) 空间复杂度:O(log n)
11 |
12 | ```js
13 | function quickSort(arr) {
14 | const len = arr.length;
15 | if (len < 2) {
16 | return arr;
17 | } else {
18 | let flag = arr[0];
19 | let left = [];
20 | let right = [];
21 | for (let i = 1; i < len; i++) {
22 | if (arr[i] < flag) {
23 | left.push(arr[i]);
24 | } else {
25 | right.push(arr[i]);
26 | }
27 | }
28 | return quickSort(left).concat(flag, quickSort(right));
29 | }
30 | return arr;
31 | }
32 | ```
33 |
--------------------------------------------------------------------------------
/1.排序/插入排序.md:
--------------------------------------------------------------------------------
1 | # 插入排序
2 |
3 | ## 思路
4 |
5 | > 核心思想:通过构建有序序列,对于未排序数据,在已排序序列中从后往前扫描,找到相应位置插入
6 | >
7 | > (1) 从第一个元素开始,该元素可以认为已经被排序
8 | > (2) 取出下一个元素,在已排序的元素队列中从后往前扫描
9 | > (3) 如果已排序的元素大于新元素,将已排序元素移到下一个位置
10 | > (4) 重复步骤 3,直到找找到已排序的元素小于或等于新元素的位置
11 | > (5) 将新元素插入到该位置后
12 | > (6) 重复步骤 2-5,直至正序排序
13 |
14 | ## 代码
15 |
16 | > ### 平均时间复杂度:O(n^2) 空间复杂度:O(1)
17 |
18 | ```js
19 | function insertionSort(arr) {
20 | for (let i = 1; i < arr.length; i++) {
21 | let temp = arr[i];
22 | let j = i - 1;
23 | while (arr[j] > temp && j >= 0) {
24 | arr[j + 1] = arr[j--];
25 | }
26 | arr[j + 1] = temp;
27 | }
28 | return arr;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/1.排序/选择排序.md:
--------------------------------------------------------------------------------
1 | # 选择排序
2 |
3 | ## 思路
4 |
5 | > 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后再从剩余未排序元素中继续寻找最小(大)
6 | > 元素,放到已排序队列的末尾。以次类推,直到所有元素均排序完毕。
7 |
8 | ## 代码
9 |
10 | > ### 平均时间复杂度:O(n^2) 空间复杂度:O(1)
11 |
12 | ```js
13 | function selectionSort(arr) {
14 | for (let i = 0, len = arr.length - 1, min; i < len; i++) {
15 | min = arr[i];
16 | for (let j = i + 1; j < len; j++) {
17 | if (arr[j] < min) {
18 | let c = min;
19 | min = arr[j];
20 | arr[j] = c;
21 | }
22 | }
23 | arr[i] = min;
24 | }
25 | return arr;
26 | }
27 | ```
28 |
--------------------------------------------------------------------------------
/2.二分查找/二分查找.md:
--------------------------------------------------------------------------------
1 | # 二分查找(折半查找)
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/binary-search/)
6 |
7 | ## 思路
8 |
9 | > (1)从有序数组的中间元素开始搜素,如果该元素正好是目标元素,则搜索过程结束,否则进行下一步
10 | > (2)如果目标元素大于或小于中间元素,则在数组大于或小于中间元素的那一半区域进行查找,然后重复第一步
11 | > (3)如果某一步数组为空,则表示找不到指定元素
12 |
13 | ## 代码
14 |
15 | > ### 时间复杂度:O(log n) 空间复杂度:O(1)
16 |
17 | ```js
18 | function binarySearch(arr, target) {
19 | let low = 0,
20 | high = arr.length - 1;
21 | while (low <= high) {
22 | let middle = parseInt((low + high) / 2);
23 | if (target === arr[middle]) {
24 | return middle;
25 | } else if (target > arr[middle]) {
26 | low = middle + 1;
27 | } else {
28 | high = middle - 1;
29 | }
30 | }
31 | return -1;
32 | }
33 | ```
34 |
--------------------------------------------------------------------------------
/3.栈、队列/双栈模拟队列.md:
--------------------------------------------------------------------------------
1 | # 双栈模拟队列
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/)
6 |
7 | ## 思路
8 |
9 | > 栈 1 用作入队,栈 2 用作出队,当栈 2 为空时,将栈 1 全部压栈到栈 2,栈 2 在出栈(即出队列)
10 |
11 | ## 代码
12 |
13 | ```js
14 | let stack1 = [],
15 | stack2 = [];
16 | function push(node) {
17 | stack1.push(node);
18 | }
19 | function pop() {
20 | if (stack2.length === 0) {
21 | if (stack1.length === 0) {
22 | return null;
23 | } else {
24 | let len = stack1.length;
25 | for (let i = 0; i < len; i++) {
26 | stack2.push(stack1.pop());
27 | }
28 | return stack2.pop();
29 | }
30 | } else {
31 | return stack2.pop();
32 | }
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/3.栈、队列/将无序栈转为有序栈.md:
--------------------------------------------------------------------------------
1 | # 将无序栈转为有序栈
2 |
3 | ## 来源
4 |
5 | > 华为手撕代码 (要求空间复杂度 O(1))
6 |
7 | ## 思路
8 |
9 | > 只有递归空间复杂度才是 O(1)
10 |
11 | ## 代码
12 |
13 | ```js
14 | function sort(stack) {
15 | const _sort = () => {
16 | if (stack.length <= 1) return stack;
17 | let num = stack.pop();
18 | if (num < stack[stack.length - 1]) {
19 | let temp = stack.pop();
20 | stack.push(num);
21 | num = temp;
22 | _sort(stack);
23 | } else {
24 | _sort(stack);
25 | }
26 | stack.push(num);
27 | };
28 | let index = stack.length;
29 | while (index > 0) {
30 | _sort(); //每一次排序完栈底元素最小
31 | index--;
32 | }
33 | return stack;
34 | }
35 | ```
36 |
--------------------------------------------------------------------------------
/3.栈、队列/有效的括号.md:
--------------------------------------------------------------------------------
1 | # 有效的括号
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/valid-parentheses/)
6 |
7 | ## 思路
8 |
9 | > 只处理左半边括号
10 |
11 | ## 代码
12 |
13 | ```js
14 | const isValid = (s) => {
15 | let stack = [];
16 | for (let i = 0; i < s.length; i++) {
17 | if (s[i] === "(") {
18 | stack.push(")");
19 | } else if (s[i] === "[") {
20 | stack.push("]");
21 | } else if (s[i] === "{") {
22 | stack.push("}");
23 | } else if (stack.pop() !== s[i]) {
24 | return false;
25 | }
26 | }
27 | return !stack.length;
28 | };
29 | ```
30 |
--------------------------------------------------------------------------------
/4.链表/两个链表的第一个公共节点.md:
--------------------------------------------------------------------------------
1 | # 两个链表的第一个公共节点
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof/)
6 |
7 | ## 思路
8 |
9 | > 用两个指针扫描两个链表,最终两个指针到达 null 或者到达公共节点
10 |
11 | ## 代码
12 |
13 | ```js
14 | function FindFirstCommonNode(pHead1, pHead2) {
15 | let p1 = pHead1;
16 | let p2 = pHead2;
17 | while (p1 !== p2) {
18 | p1 = p1 == null ? pHead2 : p1.next;
19 | p2 = p2 == null ? pHead1 : p2.next;
20 | }
21 | return p1;
22 | }
23 | ```
24 |
--------------------------------------------------------------------------------
/4.链表/两数相加.md:
--------------------------------------------------------------------------------
1 | # 两数相加
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/add-two-numbers/)
6 |
7 | ## 思路
8 |
9 | > 链表转数组 => 数组转数字 => 求和 => 数组转链表
10 | >
11 | > 时间复杂度:O(Max(m,n)) 空间复杂度:O(Max(m,n))
12 |
13 | ## 代码
14 |
15 | ```js
16 | // 链表节点
17 | function ListNode(val) {
18 | this.val = val;
19 | this.next = null;
20 | }
21 | function addTowNumbers(l1, l2) {
22 | // 链表转数组
23 | let arr1 = [],
24 | arr2 = [];
25 | while (l1) {
26 | arr1.push(l1.val);
27 | l1 = l1.next;
28 | }
29 | while (l2) {
30 | arr2.push(l2.val);
31 | l2 = l2.next;
32 | }
33 | // 数组转数字并求和
34 | const num1 = BigInt(arr1.reverse().join(""));
35 | const num2 = BigInt(arr2.reverse().join(""));
36 | const res = String(num1 + num2).split("");
37 | // 数组转链表
38 | let result = null;
39 | for (let i = 0; i < res.length; i++) {
40 | let current = new ListNode(res[i]);
41 | current.next = result;
42 | result = current;
43 | }
44 | return result;
45 | }
46 | ```
47 |
--------------------------------------------------------------------------------
/4.链表/判断链表是否有环.md:
--------------------------------------------------------------------------------
1 | # 判断链表是否有环
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/linked-list-cycle/)
6 |
7 | ## 解法一
8 |
9 | > ES6 的 Set
10 | >
11 | > 时间复杂度:O(n) 空间复杂度:O(n)
12 |
13 | ## 代码
14 |
15 | ```js
16 | function hasCycle(head) {
17 | let st = new Set();
18 | while (head) {
19 | if (st.has(head)) {
20 | return true;
21 | }
22 | st.add(head);
23 | head = head.next;
24 | }
25 | return false;
26 | }
27 | ```
28 |
29 | ## 解法二:双指针
30 |
31 | ## 思路
32 |
33 | > 快慢指针起初都在头节点,慢指针一步走,快指针两步走,判断快慢指针是否相遇,相遇则链表有环,否则无环
34 | >
35 | > 时间复杂度:O(n) 空间复杂度:O(1)
36 |
37 | ## 代码
38 |
39 | ```js
40 | function hasCycle(head) {
41 | if (head == null || head.next == null) {
42 | return false;
43 | }
44 | let fast = head;
45 | let low = head;
46 | while (fast != null && fast.next != null) {
47 | fast = fast.next.next;
48 | low = low.next;
49 | if (fast === low) {
50 | return true;
51 | }
52 | }
53 | return false;
54 | }
55 | ```
56 |
--------------------------------------------------------------------------------
/4.链表/反转链表.md:
--------------------------------------------------------------------------------
1 | # 反转链表
2 |
3 | ## 来源
4 |
5 | > leetcode:[传送门](https://leetcode-cn.com/problems/reverse-linked-list/)
6 |
7 | ## 思路
8 |
9 | > 迭代
10 |
11 | ## 代码
12 |
13 | ```js
14 | const reverseList = (head) => {
15 | let temp = null;
16 | while (head) {
17 | temp = {
18 | val: head.val,
19 | next: temp,
20 | };
21 | head = head.next;
22 | }
23 | return temp;
24 | };
25 | ```
26 |
--------------------------------------------------------------------------------
/4.链表/合并两个有序链表.md:
--------------------------------------------------------------------------------
1 | # 合并两个有序链表
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/merge-two-sorted-lists/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | const mergeTwoLists = function (A, B) {
11 | if (A == null) {
12 | return B;
13 | }
14 | if (B == null) {
15 | return A;
16 | }
17 | if (A.val < B.val) {
18 | A.next = mergeTwoLists(A.next, B);
19 | return A;
20 | } else {
21 | B.next = mergeTwoLists(A, B.next);
22 | return B;
23 | }
24 | };
25 | ```
26 |
--------------------------------------------------------------------------------
/4.链表/手撕单向链表.md:
--------------------------------------------------------------------------------
1 | # 手撕单向链表
2 |
3 | ## 代码
4 |
5 | ```js
6 | // 定义节点
7 | class Node {
8 | constructor(v, next) {
9 | this.val = v;
10 | this.next = next;
11 | }
12 | }
13 | // 定义链表
14 | class LinkList {
15 | constructor() {
16 | //链表长度
17 | this.size = 0;
18 | //虚拟头部
19 | this.dummyNode = new Node(null, null);
20 | }
21 | //查找节点
22 | find(header, currentIndex, index) {
23 | if (index === currentIndex) {
24 | return header;
25 | }
26 | return this.find(header.next, currentIndex + 1, index);
27 | }
28 | //查找某一节点的后继节点
29 | getNode(index) {
30 | this.checkIndex(index);
31 | if (this.isEmpty()) {
32 | return null;
33 | }
34 | return this.find(this.dummyNode, 0, index).next;
35 | }
36 | //插入节点
37 | insertNode(v, index) {
38 | this.checkIndex(index);
39 | let prev = this.find(this.dummyNode, 0, index);
40 | prev.next = new Node(v, prev.next);
41 | this.size + 1;
42 | return prev.next;
43 | }
44 | //插入头节点
45 | insertFirstNode(v) {
46 | return addNode(v, 0);
47 | }
48 | //插入尾节点
49 | insertLastNode(v) {
50 | return addNode(v, this.size);
51 | }
52 | //在链表中非首尾的任意位置插入节点
53 | insertAnyNode(v, index) {
54 | return addNode(v, index);
55 | }
56 | //删除节点
57 | removeNode(index, isLast) {
58 | this.checkIndex(index);
59 | index = isLast ? index - 1 : index;
60 | let prev = this.find(this.dummyNode, 0, index);
61 | let node = prev.next;
62 | prev.next = node.next;
63 | node.next = null;
64 | this.size--;
65 | return node;
66 | }
67 | //删除头节点
68 | removeFirstNode() {
69 | return this.removeNode(0);
70 | }
71 | //删除尾节点
72 | removeLastNode() {
73 | return this.removeNode(this.size, true);
74 | }
75 | //删除非首尾的任意位置节点
76 | removeAnyNode(index) {
77 | return this.removeNode(index, false);
78 | }
79 | checkIndex(index) {
80 | if (index < 0 || index > this.size) {
81 | throw Error("Index Error");
82 | }
83 | }
84 | //链表长度
85 | getSize() {
86 | return this.size;
87 | }
88 | //链表判空
89 | isEmpty() {
90 | return this.size === 0;
91 | }
92 | }
93 | ```
94 |
--------------------------------------------------------------------------------
/4.链表/链表中倒数第K个节点.md:
--------------------------------------------------------------------------------
1 | # 链表中倒数第 K 个节点
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/)
6 |
7 | ## 解法一:队列
8 |
9 | ## 思路
10 |
11 | > 拿一个队列保存
12 |
13 | ```js
14 | function findKthToTail(head, k) {
15 | let result = [];
16 | while (head) {
17 | result.unshift(head);
18 | head = head.next;
19 | }
20 | return result[k - 1];
21 | }
22 | ```
23 |
24 | ## 解法二:双指针
25 |
26 | ## 思路
27 |
28 | > 快指针先走 k-1 步,之后快慢指针同步走,当快指针走到最后一个结点的时候,慢指针也走到了倒数第 k 个结点
29 |
30 | ## 代码
31 |
32 | ```js
33 | function findKthToTail(head, k) {
34 | if (!head || !k || k <= 0) {
35 | return null;
36 | }
37 | let low = head;
38 | let fast = head;
39 | for (let i = 0; i < k - 1; i++) {
40 | if (fast.next) {
41 | fast = fast.next;
42 | } else {
43 | return null;
44 | }
45 | }
46 | while (fast.next) {
47 | fast = fast.next;
48 | low = low.next;
49 | }
50 | return low;
51 | }
52 | ```
53 |
--------------------------------------------------------------------------------
/5.二叉树/中序遍历.md:
--------------------------------------------------------------------------------
1 | # 中序遍历
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function inOrderTraversal(root) {
11 | let res = [];
12 | let stack = [];
13 | while (root != null || stack.length) {
14 | if (root) {
15 | stack.push(root);
16 | root = root.left;
17 | } else {
18 | let node = stack.pop();
19 | res.push(node.val);
20 | root = root.right;
21 | }
22 | }
23 | return res;
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/5.二叉树/之字形打印二叉树.md:
--------------------------------------------------------------------------------
1 | # 之字形打印二叉树
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/)
6 |
7 | ## 思路
8 |
9 | > 奇数行用栈存储,偶数行用队列存储
10 |
11 | ## 代码
12 |
13 | ```js
14 | function Print(pRoot) {
15 | if (pRoot == null) {
16 | return [];
17 | }
18 |
19 | let queue = [],
20 | temp = [],
21 | res = [],
22 | level = 0,
23 | isEven = true,
24 | isBePrinted = 1;
25 | queue.push(pRoot);
26 |
27 | while (queue.length) {
28 | let node = queue.shift();
29 |
30 | //判断奇、偶行,奇数行
31 | if (isEven) {
32 | temp.push(node.val);
33 | } else {
34 | temp.unshift(node.val);
35 | }
36 | //行数加一
37 | if (node.left) {
38 | queue.push(node.left);
39 | level++;
40 | }
41 | if (node.right) {
42 | queue.push(node.right);
43 | level++;
44 | }
45 | //当前被操作行数已遍历完
46 | isBePrinted--;
47 | //处理行数、奇偶、temp置空
48 | if (isBePrinted === 0) {
49 | res.push(temp);
50 | temp = [];
51 | isBePrinted = level;
52 | level = 0;
53 | isEven = !isEven;
54 | }
55 | }
56 | return res;
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/5.二叉树/二叉搜索树的后序遍历序列.md:
--------------------------------------------------------------------------------
1 | # 二叉搜索树的后序遍历序列
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/)
6 |
7 | ## 思路
8 |
9 | > 1. 找出根节点,
10 | > 2. 在不包括根节点的数组中遍历,找到第一个比根节点大的位置,该位置左边为左子树,右边为右子树
11 | > 3. 遍历右子树,如果发现有小于根节点的值,直接返回 false
12 | > 4. 递归以上步骤,分别判断左右子树是否为二叉搜索树
13 |
14 | ## 代码
15 |
16 | ```js
17 | function VerifySquenceOfBST(arr) {
18 | if (arr == null || arr.length < 1) {
19 | return false;
20 | }
21 |
22 | return judge(arr, 0, arr.length - 1);
23 | }
24 |
25 | function judge(arr, left, right) {
26 | // 递归终止条件:只有一个节点
27 | if (left >= right) {
28 | return true;
29 | }
30 |
31 | // 找出根节点
32 | let node = arr[right];
33 | // 用来记录序列中第一个比根节点大节点的下标
34 | let index = right;
35 |
36 | for (let i = left; i < right - 1; i++) {
37 | // 找到根节点的右孩子
38 | if (arr[i] > node) {
39 | index = i;
40 | i++;
41 |
42 | // 如果右子树中有比根节点还小的树的话,显然是不成立的
43 | while (i <= right - 1) {
44 | if (arr[i] < node) {
45 | return false;
46 | }
47 | i++;
48 | }
49 | }
50 | }
51 |
52 | // 递归检查左右子树
53 | return judge(arr, left, index - 1) && judge(arr, index, right - 1);
54 | }
55 | ```
56 |
--------------------------------------------------------------------------------
/5.二叉树/二叉搜索树的第K个节点.md:
--------------------------------------------------------------------------------
1 | # 二叉搜索树第 K 个节点
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/)
6 |
7 | ## 思路
8 |
9 | > 利用二叉搜索树的中序遍历是一个递增序列来解决,用数组存储遍历结果,返回第 k 个节点
10 |
11 | ## 代码
12 |
13 | ```js
14 | function KthNode(pRoot, k) {
15 | if (pRoot == null || k < 1) {
16 | return null;
17 | }
18 |
19 | const arr = [];
20 |
21 | //中序遍历
22 | function getTree(root) {
23 | if (root.left) {
24 | getTree(root.left);
25 | }
26 | arr.push(root);
27 | if (root.right) {
28 | getTree(root.right);
29 | }
30 | }
31 |
32 | getTree(pRoot);
33 |
34 | return arr[k - 1];
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/5.二叉树/二叉树.md:
--------------------------------------------------------------------------------
1 | ## 十一、二叉搜索树的后序遍历序列
2 |
3 | ## 思路
4 |
5 | > 1. 找出根节点,
6 | > 2. 在不包括根节点的数组中遍历,找到第一个比根节点大的位置,该位置左边为左子树,右边为右子树
7 | > 3. 遍历右子树,如果发现有小于根节点的值,直接返回 false
8 | > 4. 递归以上步骤,分别判断左右子树是否为二叉搜索树
9 |
10 | ## 代码
11 |
12 | ```js
13 | function VerifySquenceOfBST(arr) {
14 | if (arr == null || arr.length < 1) {
15 | return false;
16 | }
17 | return judge(arr, 0, arr.length - 1);
18 | }
19 | function judge(arr, left, right) {
20 | // 递归终止条件:只有一个节点
21 | if (left >= right) {
22 | return true;
23 | }
24 | // 找出根节点
25 | let node = arr[right];
26 | // 用来记录序列中第一个比根节点大节点的下标
27 | let index = right;
28 | for (let i = left; i < right - 1; i++) {
29 | // 找到根节点的右孩子
30 | if (arr[i] > node) {
31 | index = i;
32 | i++;
33 | // 如果右子树中有比根节点还小的树的话,显然是不成立的
34 | while (i <= right - 1) {
35 | if (arr[i] < node) {
36 | return false;
37 | }
38 | i++;
39 | }
40 | }
41 | }
42 | // 递归检查左右子树
43 | return judge(arr, left, index - 1) && judge(arr, index, right - 1);
44 | }
45 | ```
46 |
47 | ## 十二、二叉搜索树第 K 个节点
48 |
49 | ## 思路:利用二叉搜索树的中序遍历是一个递增序列来解决,用数组存储遍历结果,返回第 k 个节点
50 |
51 | ## 代码
52 |
53 | ```js
54 | function KthNode(pRoot, k) {
55 | if (pRoot == null || k < 1) {
56 | return null;
57 | }
58 | var arr = [];
59 | //中序遍历
60 | function getTree(root) {
61 | if (root.left) {
62 | getTree(root.left);
63 | }
64 | arr.push(root);
65 | if (root.right) {
66 | getTree(root.right);
67 | }
68 | }
69 | getTree(pRoot);
70 | return arr[k - 1];
71 | }
72 | ```
73 |
74 | ## 十三、二叉树的最大宽度
75 |
76 | ## 思路
77 |
78 | > 对节点进行编号,根节点编号是 0,左子树编号 = 根节点编号*2+1,右子树编号 = 根节点编号*2+2,
79 | > 最大宽度 = 左子树编号 - 右子树编号 + 1
80 |
81 | ## 代码
82 |
83 | ```js
84 | function maxWidthOfBinaryTree(root) {
85 | if (!root) {
86 | return 0;
87 | }
88 | let res = [],
89 | maxWidth = 1;
90 | recusion(root, 0, 0);
91 | return maxWidth;
92 | function recusion(root, level, num) {
93 | if (res[level]) {
94 | res[level].push(num);
95 | } else {
96 | res[level] = [num];
97 | }
98 | let tempArr = res[level];
99 | let tempWidth = tempArr[tempArr.length - 1] - tempArr[0] + 1;
100 | if (tempWidth > maxWidth) {
101 | maxWidth = tempWidth;
102 | }
103 | if (root.left) {
104 | recusion(root.left, level + 1, num * 2 + 1);
105 | }
106 | if (root.right) {
107 | recusion(root.right, level + 1, num * 2 + 2);
108 | }
109 | }
110 | }
111 | ```
112 |
113 | ## 十四、二叉树的镜像
114 |
115 | ## 代码
116 |
117 | ```js
118 | function Mirror(root) {
119 | //判断根节点
120 | if (root == null) {
121 | return;
122 | }
123 | //终止条件为当前节点为叶子节点
124 | if (root.left == null && root.right == null) {
125 | return root;
126 | }
127 | //交换左右子树
128 | let temp = root.left;
129 | root.left = root.right;
130 | root.right = temp;
131 | //递归左右子树镜像
132 | Mirror(root.left);
133 | Mirror(root.right);
134 | }
135 | ```
136 |
137 | ## 十五、二叉树的下一个节点
138 |
139 | ## 思路
140 |
141 | > 1. 二叉树为空,返回 null
142 | > 2. 右子树存在,返回右子树的最左节点
143 | > 3. 节点不是根节点。是父节点的左孩子,则返回父节点;否则向上去遍历父节点的父节点,重复之前的判断,返回结果。
144 |
145 | ## 代码
146 |
147 | ```js
148 | function GetNext(pNode) {
149 | if (pNode == null) {
150 | return null;
151 | }
152 | if (pNode.right != null) {
153 | pNode = pNode.right;
154 | while (pNode.left != null) {
155 | pNode = pNode.left;
156 | }
157 | return pNode;
158 | }
159 | while (pNode.next != null) {
160 | let pRoot = pNode.next;
161 | if (pRoot.left === pNode) {
162 | return pRoot;
163 | }
164 | pNode = pNode.next;
165 | }
166 | return null;
167 | }
168 | ```
169 |
--------------------------------------------------------------------------------
/5.二叉树/二叉树的下一个节点.md:
--------------------------------------------------------------------------------
1 | # 二叉树的下一个节点
2 |
3 | ## 来源
4 |
5 | > 牛客网:[传送门](https://www.nowcoder.com/questionTerminal/9023a0c988684a53960365b889ceaf5e)
6 |
7 | ## 思路
8 |
9 | > 1. 二叉树为空,返回 null
10 | > 2. 右子树存在,返回右子树的最左节点
11 | > 3. 节点不是根节点。是父节点的左孩子,则返回父节点;否则向上去遍历父节点的父节点,重复之前的判断,返回结果。
12 |
13 | ## 代码
14 |
15 | ```js
16 | function GetNext(pNode) {
17 | if (pNode == null) {
18 | return null;
19 | }
20 |
21 | if (pNode.right != null) {
22 | pNode = pNode.right;
23 | while (pNode.left != null) {
24 | pNode = pNode.left;
25 | }
26 | return pNode;
27 | }
28 |
29 | while (pNode.next != null) {
30 | let pRoot = pNode.next;
31 | if (pRoot.left === pNode) {
32 | return pRoot;
33 | }
34 | pNode = pNode.next;
35 | }
36 |
37 | return null;
38 | }
39 | ```
40 |
--------------------------------------------------------------------------------
/5.二叉树/二叉树的最大宽度.md:
--------------------------------------------------------------------------------
1 | # 二叉树的最大宽度
2 |
3 | ## 来源
4 |
5 | > 暂时迷失
6 |
7 | ## 思路
8 |
9 | > 对节点进行编号,根节点编号是 0,左子树编号 = 根节点编号*2+1,右子树编号 = 根节点编号*2+2,
10 | > 最大宽度 = 左子树编号 - 右子树编号 + 1
11 |
12 | ## 代码
13 |
14 | ```js
15 | function maxWidthOfBinaryTree(root) {
16 | if (!root) {
17 | return 0;
18 | }
19 |
20 | let res = [],
21 | maxWidth = 1;
22 | recusion(root, 0, 0);
23 |
24 | return maxWidth;
25 |
26 | function recusion(root, level, num) {
27 | if (res[level]) {
28 | res[level].push(num);
29 | } else {
30 | res[level] = [num];
31 | }
32 |
33 | let tempArr = res[level];
34 | let tempWidth = tempArr[tempArr.length - 1] - tempArr[0] + 1;
35 |
36 | if (tempWidth > maxWidth) {
37 | maxWidth = tempWidth;
38 | }
39 | if (root.left) {
40 | recusion(root.left, level + 1, num * 2 + 1);
41 | }
42 | if (root.right) {
43 | recusion(root.right, level + 1, num * 2 + 2);
44 | }
45 | }
46 | }
47 | ```
48 |
--------------------------------------------------------------------------------
/5.二叉树/二叉树的深度.md:
--------------------------------------------------------------------------------
1 | # 二叉树的深度
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/)
6 |
7 | ## 思路
8 |
9 | > 一旦没有找到节点就返回 0,每弹出一次递归函数就会加 1,树有三层就会得到 3
10 |
11 | ## 代码
12 |
13 | ```js
14 | function maxDepth(root) {
15 | if (!root) {
16 | return 0;
17 | }
18 | return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
19 | }
20 | ```
21 |
--------------------------------------------------------------------------------
/5.二叉树/二叉树的镜像.md:
--------------------------------------------------------------------------------
1 | # 二叉树的镜像
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function Mirror(root) {
11 | //判断根节点
12 | if (root == null) {
13 | return;
14 | }
15 |
16 | //终止条件为当前节点为叶子节点
17 | if (root.left == null && root.right == null) {
18 | return root;
19 | }
20 |
21 | //交换左右子树
22 | let temp = root.left;
23 | root.left = root.right;
24 | root.right = temp;
25 |
26 | //递归左右子树镜像
27 | Mirror(root.left);
28 | Mirror(root.right);
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/5.二叉树/前序遍历.md:
--------------------------------------------------------------------------------
1 | # 前序遍历
2 |
3 | ## 来源
4 |
5 | > leetocde: [传送门](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)
6 |
7 | ## 思路
8 |
9 | > 需要一个栈来辅助,把遍历结果 push 进数组中作为返回结果。
10 | >
11 | > 1. 将根节点放进栈中
12 | > 2. 如果栈不为空,出栈一个节点 push 进数组中;如果该节点右子树不为空,把右子树放进栈中;
13 | > 如果该节点左子树不为空,把左子树放进栈中
14 | > 3. 重复步骤二,直到栈为空,返回数组
15 |
16 | ## 代码
17 |
18 | ```js
19 | function preOrderTraversal(root) {
20 | let res = [];
21 | let stack = [];
22 | if (root == null) {
23 | return res;
24 | }
25 | stack.push(root);
26 | while (stack.length) {
27 | let node = stack.pop();
28 | res.push(node.val);
29 | if (node.right) {
30 | stack.push(node.right);
31 | }
32 | if (node.left) {
33 | stack.push(node.right);
34 | }
35 | }
36 | return res;
37 | }
38 | ```
39 |
--------------------------------------------------------------------------------
/5.二叉树/后序遍历.md:
--------------------------------------------------------------------------------
1 | # 后序遍历
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function postOrderTraversal(root) {
11 | let res = [];
12 | let stack = [];
13 | if (root == null) {
14 | return res;
15 | }
16 | stack.push(root);
17 | while (stack.length) {
18 | let node = stack.pop();
19 | res.unshift(node.val);
20 | if (root.left) {
21 | stack.push(node.left);
22 | }
23 | if (root.right) {
24 | stack.push(node.right);
25 | }
26 | }
27 | return res;
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/5.二叉树/对称二叉树.md:
--------------------------------------------------------------------------------
1 | # 对称二叉树
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function isSymmetrical(pRoot) {
11 | return pRoot == null || judge(pRoot.left, pRoot.right);
12 | }
13 | function judge(node1, node2) {
14 | if (node1 == null && node2 == null) {
15 | return true;
16 | } else if (node1 == null || node2 == null) {
17 | return false;
18 | }
19 | if (node1.val !== node2.val) {
20 | return false;
21 | } else {
22 | return judge(node1.left, node2.right) && judge(node1.right, node2.left);
23 | }
24 | }
25 | ```
26 |
--------------------------------------------------------------------------------
/5.二叉树/层次遍历.md:
--------------------------------------------------------------------------------
1 | # 层次遍历
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function printFromTopToBottom(root) {
11 | let res = [];
12 | let queue = [];
13 | if (root == null) {
14 | return res;
15 | }
16 | queue.push(root);
17 | while (queue.length) {
18 | let node = queue.shift();
19 | res.push(node.val);
20 | if (node.left != null) {
21 | queue.push(node.left);
22 | }
23 | if (node.right != null) {
24 | queue.push(node.right);
25 | }
26 | }
27 | return res;
28 | }
29 | ```
30 |
--------------------------------------------------------------------------------
/5.二叉树/平衡二叉树.md:
--------------------------------------------------------------------------------
1 | # 平衡二叉树
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/solution/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function IsBalanced_Solution(pRoot) {
11 | return depth(pRoot) !== -1;
12 | }
13 |
14 | function depth(pRoot) {
15 | if (pRoot == null) {
16 | return 0;
17 | }
18 |
19 | let left = depth(pRoot.left);
20 | if (left === -1) {
21 | return -1;
22 | }
23 | let right = depth(pRoot.right);
24 | if (right === -1) {
25 | return -1;
26 | }
27 |
28 | return Math.abs(left - right) > 1 ? -1 : Math.max(left, right) + 1;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/5.二叉树/按层打印二叉树.md:
--------------------------------------------------------------------------------
1 | # 按层打印二叉树
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/)
6 |
7 | ## 思路
8 |
9 | > 用队列存储二叉树每层的值,用数组存储每个队列的值
10 |
11 | ## 代码
12 |
13 | ```js
14 | function Print(pRoot) {
15 | let res = [];
16 | let queue = [];
17 | if (!pRoot) {
18 | return res;
19 | }
20 | queue.push(pRoot);
21 |
22 | while (queue.length) {
23 | let len = queue.length;
24 | let temp = [];
25 |
26 | for (let i = 0; i < len; i++) {
27 | let node = queue.shift();
28 | temp.push(node.val);
29 | if (node.left) {
30 | queue.push(node.left);
31 | }
32 | if (node.right) {
33 | queue.push(node.right);
34 | }
35 | }
36 |
37 | res.push(temp);
38 | }
39 |
40 | return res;
41 | }
42 | ```
43 |
--------------------------------------------------------------------------------
/5.二叉树/重建二叉树.md:
--------------------------------------------------------------------------------
1 | # 重建二叉树 (已知前序、中序遍历)
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/solution/)
6 |
7 | ## 代码
8 |
9 | ```js
10 | function reConstructorBinaryTree(pre, vin){
11 | let result = null;
12 |
13 | if(pre.length > 1){
14 | let root = pre[0];
15 | let index = vin.indexOf(root);
16 | let vinLeft = vin.slice(0, index);
17 | let vinRight = vin.slice(index + 1);
18 | pre.shift();
19 | let preLeft = pre.slice(0, vinLeft.length);
20 | let preRight = pre.slice(vinLeft.length);
21 |
22 | result = {
23 | val:root,
24 | left:reConstructorBinaryTree(preLeft, vinLeft),
25 | right:reConstructorBinaryTree(preRight, vinRight);
26 | }
27 | }else if(pre.length === 1){
28 | result = {
29 | val:pre[0],
30 | left:null,
31 | right:null
32 | }
33 | }
34 |
35 | return result
36 | }
37 | ```
38 |
--------------------------------------------------------------------------------
/6.双指针/和为S的两个数(微改版).md:
--------------------------------------------------------------------------------
1 | # 和为 S 的两个数(微改版)
2 |
3 | > 题目描述: 在递增数组中找出两个之和的数,返回乘积最小的两项
4 |
5 | ## 来源
6 |
7 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/he-wei-sde-liang-ge-shu-zi-lcof/)
8 |
9 | ## 双指针类题目通用思路
10 |
11 | > 1. 确定双指针位置(都在起始位置、一个在起始一个在末尾)
12 | > 2. 确定终止条件(双指针重合、high 走到末尾)
13 | > 3. while 语句中的条件判断(双指针走向 =>同向走、向中间靠拢)
14 |
15 | ## 思路(类似于二分查找)
16 |
17 | > 1. 确定双指针位置(一个在起始、一个在末尾)
18 | > 2. 确定终止条件(双指针重合)
19 | > 3. while 语句中的条件判断(双指针向中间聚拢)
20 |
21 | ## 代码
22 |
23 | ```js
24 | function FindNumbersWithSum(arr, sum) {
25 | let low = 0,
26 | high = arr.length - 1;
27 | let res = [];
28 |
29 | while (low < high) {
30 | let current = arr[low] + arr[high];
31 |
32 | if (sum === current) {
33 | res.push(arr[low]);
34 | res.push(arr[high]);
35 | break;
36 | } else if (current < sum) {
37 | low++;
38 | } else if (current > sum) {
39 | high--;
40 | }
41 | }
42 |
43 | return res;
44 | }
45 | ```
46 |
--------------------------------------------------------------------------------
/6.双指针/和为S的连续正整数.md:
--------------------------------------------------------------------------------
1 | # 和为 S 的连续正整数
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/)
6 |
7 | ## 双指针类题目通用思路
8 |
9 | > 1. 确定双指针位置(都在起始位置、一个在起始一个在末尾)
10 | > 2. 确定终止条件(双指针重合、high 走到末尾)
11 | > 3. while 语句中的条件判断(双指针走向 =>同向走、向中间靠拢)
12 |
13 | ## 思路
14 |
15 | > 双指针与滑动窗口 => 窗口的左右两边就是两个指针,根据窗口内值之和来确定窗口的位置和宽
16 | >
17 | > 终止条件:双指针重合
18 |
19 | ## 代码
20 |
21 | ```js
22 | function FindContinuousSequence(sum) {
23 | let low = 1,
24 | high = 2;
25 | let temp = [],
26 | res = [];
27 |
28 | while (low < high) {
29 | let s = ((low + high) * (high - low + 1)) / 2;
30 |
31 | if (s === sum) {
32 | let set = new Set();
33 |
34 | for (let i = low; i <= high; i++) {
35 | set.add(i);
36 | }
37 |
38 | temp = Array.from(set);
39 | res.push(temp);
40 | low++;
41 | } else if (s < sum) {
42 | high++;
43 | } else if (s > sum) {
44 | low++;
45 | }
46 | }
47 |
48 | return res;
49 | }
50 | ```
51 |
--------------------------------------------------------------------------------
/6.双指针/字符串压缩.md:
--------------------------------------------------------------------------------
1 | # 字符串压缩
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/compress-string-lcci/)
6 |
7 | ## 难度类型:Easy
8 |
9 | ## 代码
10 |
11 | ```js
12 | var compressString = function (S) {
13 | let p = 0;
14 | let count = 1;
15 | let res = [];
16 | while (p <= S.length - 1) {
17 | if (S[p] === S[p + 1]) {
18 | count++;
19 | } else {
20 | res.push(S[p]);
21 | res.push(count);
22 | count = 1;
23 | }
24 | p++;
25 | }
26 | if (res.length >= S.length) return S;
27 | return res.join("");
28 | };
29 | ```
30 |
--------------------------------------------------------------------------------
/6.双指针/接雨水.md:
--------------------------------------------------------------------------------
1 | # 接雨水
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/trapping-rain-water/)
6 |
7 | ## 难度类型:困难
8 |
9 | ## 双指针类题目通用思路
10 |
11 | > 1. 确定双指针位置(都在起始位置、一个在起始一个在末尾)
12 | > 2. 确定终止条件(双指针重合、high 走到末尾)
13 | > 3. while 语句中的条件判断(双指针走向 =>同向走、向中间靠拢)
14 |
15 | ## 思路
16 |
17 | > 1. 确定双指针位置(一个在起始、一个在末尾)
18 | > 2. 确定终止条件(双指针重合)
19 | > 3. while 语句中的条件判断(双指针向中间聚拢)
20 | >
21 | > 时间复杂度:O(n)
22 |
23 | ## 代码
24 |
25 | ```js
26 | const trap = function(arr) {
27 | let left = 0;
28 | let right = arr.length-1;
29 | let res = 0;
30 | let left_max = 0;
31 | let right_max = 0;
32 |
33 | while(left < right){
34 |
35 | if(arr[left] < arr[right]){
36 | if(arr[left] >= left_max){
37 | left_max = arr[left]
38 | }
39 |
40 | res+ = (left_max-arr[left]);
41 | left++;
42 | }else{
43 | if(arr[right] >= right_max){
44 | right_max = arr[right]
45 | }
46 |
47 | res+ = (right_max-arr[right]);
48 | right--;
49 | }
50 |
51 | }
52 |
53 | return res
54 | };
55 | ```
56 |
--------------------------------------------------------------------------------
/6.双指针/滑动窗口的最大值.md:
--------------------------------------------------------------------------------
1 | # 滑动窗口最大值
2 |
3 | ## 来源
4 |
5 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/)
6 |
7 | ## 双指针类题目通用思路
8 |
9 | > 1. 确定双指针位置(都在起始位置、一个在起始一个在末尾)
10 | > 2. 确定终止条件(双指针重合、high 走到末尾)
11 | > 3. while 语句中的条件判断(双指针走向 =>同向走、向中间靠拢)
12 |
13 | ## 思路
14 |
15 | > 双指针间距 size,之后同步走,取出动态数组 temp 的最大值,push 进 res
16 | >
17 | > 终止条件:high 指针走到末尾
18 |
19 | ## 代码
20 |
21 | ```js
22 | function maxInWindows(num, size) {
23 | let res = [];
24 | if (num == null || size < 1) {
25 | return [];
26 | }
27 |
28 | let low = 0;
29 | let high = low + size - 1;
30 |
31 | while (high < num.length) {
32 | let temp = num.slice(low, high + 1);
33 | let maxNum = temp.reduce((prev, next) => {
34 | return Math.max(prev, next);
35 | });
36 |
37 | res.push(maxNum);
38 | temp = [];
39 | low++;
40 | high++;
41 | }
42 |
43 | return res;
44 | }
45 | ```
46 |
--------------------------------------------------------------------------------
/6.双指针/盛最多水的容器.md:
--------------------------------------------------------------------------------
1 | # 盛最多水的容器
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/container-with-most-water/)
6 |
7 | ## 难度类型:中等
8 |
9 | ## 解法一:双指针
10 |
11 | ## 双指针类题目通用思路
12 |
13 | > 1. 确定双指针位置(都在起始位置、一个在起始一个在末尾)
14 | > 2. 确定终止条件(双指针重合、high 走到末尾)
15 | > 3. while 语句中的条件判断(双指针走向 =>同向走、向中间靠拢)
16 |
17 | ## 思路
18 |
19 | > 1. 确定双指针位置(一个在起始、一个在末尾)
20 | > 2. 确定终止条件(双指针重合)
21 | > 3. while 语句中的条件判断(双指针向中间聚拢)
22 | >
23 | > 时间复杂度:O(n)
24 |
25 | ## 代码
26 |
27 | ```js
28 | const maxArea = function (arr) {
29 | let low = 0;
30 | let high = arr.length - 1;
31 | let res = 0;
32 |
33 | while (low < high) {
34 | let s = (high - low) * Math.min(arr[low], arr[high]);
35 | res = Math.max(res, s);
36 |
37 | if (arr[low] < arr[high]) {
38 | low++;
39 | } else {
40 | high--;
41 | }
42 | }
43 |
44 | return res;
45 | };
46 | ```
47 |
48 | ## 解法二:暴力解法
49 |
50 | > 时间复杂度:O(n^2)
51 |
52 | ## 代码
53 |
54 | ```js
55 | const maxArea = function (arr) {
56 | let s;
57 | let temp = [];
58 |
59 | for (let low = 0; low < arr.length; low++) {
60 | for (let high = low + 1; high < arr.length; high++) {
61 | s = (high - low) * Math.min(arr[low], arr[high]);
62 | temp.push(s);
63 | }
64 | }
65 |
66 | let res = 0;
67 |
68 | for (let i = 0; i < temp.length; i++) {
69 | res = Math.max(res, temp[i]);
70 | }
71 |
72 | return res;
73 | };
74 | ```
75 |
--------------------------------------------------------------------------------
/6.双指针/验证回文串.md:
--------------------------------------------------------------------------------
1 | # 验证回文串
2 |
3 | ## 来源
4 |
5 | > leetcode: [传送门](https://leetcode-cn.com/problems/valid-palindrome/)
6 |
7 | ## 难度类型:Easy
8 |
9 | ## 代码
10 |
11 | ```js
12 | var isPalindrome = function (s) {
13 | s = s.replace(/[^0-9a-zA-Z]/g, "").toLowerCase();
14 | let low = 0;
15 | let high = s.length - 1;
16 | while (low <= high) {
17 | if (s[low] !== s[high]) {
18 | return false;
19 | }
20 | low++;
21 | high--;
22 | }
23 | return true;
24 | };
25 | ```
26 |
--------------------------------------------------------------------------------
/7.动态规划/不同路径.md:
--------------------------------------------------------------------------------
1 | # 不同路径
2 |
3 | ## 动态规划
4 |
5 | #### 通用解题思路
6 |
7 | > 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > leetcode: [传送门](https://leetcode-cn.com/problems/unique-paths/)
21 |
22 | ## 难度类型:中等
23 |
24 | ## 代码
25 |
26 | ```js
27 | const uniquePaths = function (m, n) {
28 | if (m <= 0 || n <= 0) {
29 | return 0;
30 | }
31 |
32 | //新建二维DP
33 | let dp = [];
34 | for (let i = 0; i < m; i++) {
35 | dp[i] = new Array();
36 | for (let j = 0; j < n; j++) {
37 | dp[i][j] = null;
38 | }
39 | }
40 |
41 | //初始值
42 | for (let i = 0; i < m; i++) {
43 | dp[i][0] = 1;
44 | }
45 | for (let j = 0; j < n; j++) {
46 | dp[0][j] = 1;
47 | }
48 |
49 | //状态转移方程
50 | for (let i = 1; i < m; i++) {
51 | for (let j = 1; j < n; j++) {
52 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
53 | }
54 | }
55 |
56 | return dp[m - 1][n - 1];
57 | };
58 | ```
59 |
--------------------------------------------------------------------------------
/7.动态规划/斐波那契数列、跳台阶、矩形覆盖.md:
--------------------------------------------------------------------------------
1 | # 斐波那契数列/跳台阶/矩形覆盖
2 |
3 | ## 动态规划
4 |
5 | #### 通用解题思路
6 |
7 | > 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > 剑指 Offer: [传送门](https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/)\_
21 |
22 | ## 难度类型:简单
23 |
24 | ## 递归解法
25 |
26 | > 时间复杂度:O(2n)
27 |
28 | ## 代码
29 |
30 | ```js
31 | function fib(n) {
32 | if (n < 0) {
33 | return -1;
34 | } else if (n < 2 && n >= 0) {
35 | return n;
36 | } else {
37 | return fib(n - 1) + fib(n - 2);
38 | }
39 | }
40 | ```
41 |
42 | ## 动态规划解法
43 |
44 | > 时间复杂度:O(n)
45 |
46 | ## 代码
47 |
48 | ```js
49 | function fib(n) {
50 | //新建一维数组
51 | let arr = new Array(n + 1).fill(null);
52 |
53 | //初始值
54 | arr[0] = 0;
55 | arr[1] = 1;
56 |
57 | //数组元素间关系式
58 | for (let i = 2; i <= n; i++) {
59 | arr[i] = arr[i - 1] + arr[i - 2];
60 | }
61 |
62 | return arr[n];
63 | }
64 | ```
65 |
--------------------------------------------------------------------------------
/7.动态规划/最小路径和.md:
--------------------------------------------------------------------------------
1 | # 最小路径和
2 |
3 | ## 动态规划
4 |
5 | #### 通用解题思路
6 |
7 | > 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > leetcode: [传送门](https://leetcode-cn.com/problems/minimum-path-sum/)
21 |
22 | ## 题目描述
23 |
24 | > 输入:
25 | > [
26 | > [1,3,1],
27 | > [1,5,1],
28 | > [4,2,1]
29 | > ]
30 | > 输出: 7
31 |
32 | ## 难度类型:中等
33 |
34 | ## 代码
35 |
36 | ```js
37 | function minPathSum(arr){
38 | let m = arr.length;
39 | let n =a rr[0].length;
40 | if(m <= 0 || n <= 0){
41 | return 0
42 | }
43 |
44 | //新建二维数组
45 | let dp = [];
46 | for(let i=0; i 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > leetcode: [传送门](https://leetcode-cn.com/problems/longest-palindromic-substring/)
21 |
22 | ## 难度类型:中等
23 |
24 | ## 代码
25 |
26 | ```js
27 | function longestPalindRome(str) {
28 | let len = str.length;
29 | if (len === 1) {
30 | return str;
31 | }
32 | let start = 0;
33 | let longest = 1;
34 |
35 | //新建二维数组
36 | let dp = [];
37 | for (let i = 0; i < len; i++) {
38 | dp[i] = [];
39 | }
40 |
41 | //处理单个字符、两个相等的字符
42 | for (let i = 0; i < len; i++) {
43 | dp[i][i] = 1;
44 | if (i < len - 1) {
45 | if (str[i] === str[i + 1]) {
46 | dp[i][i + 1] = 1;
47 | start = i;
48 | longest = 2;
49 | }
50 | }
51 | }
52 |
53 | //处理状态转移方程
54 | for (let l = 3; l < len; l++) {
55 | for (let i = 0; i + l - 1 < len; i++) {
56 | let j = i + l - 1;
57 | if ((str[i] === str[j] && dp[i + 1][j - 1] = 1)) {
58 | dp[i][j] = 1;
59 | start = i;
60 | longest = l;
61 | }
62 | }
63 | }
64 |
65 | return str.substr(start, longest);
66 | }
67 | ```
68 |
--------------------------------------------------------------------------------
/7.动态规划/最长有效括号.md:
--------------------------------------------------------------------------------
1 | # 最长有效括号
2 |
3 | ## 动态规划
4 |
5 | #### 通用解题思路
6 |
7 | > 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > leetcode: [传送门](https://leetcode-cn.com/problems/longest-valid-parentheses/)
21 |
22 | ## 难度类型:困难
23 |
24 | ## 代码
25 |
26 | ```js
27 | const longestValidParentheses = function (s) {
28 | let len = s.length;
29 | if (len <= 0) {
30 | return 0;
31 | }
32 |
33 | //新建一维dp,填充值为null
34 | let dp = new Array(len + 1).fill(null);
35 |
36 | //初始值--表示只有一位括号时返回0
37 | dp[0] = 0;
38 | let max = 0;
39 |
40 | //状态转移方程
41 | for (let i = 1; i < len; i++) {
42 | if (s.charAt(i) === ")") {
43 | if (s.charAt(i - 1) === "(") {
44 | dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
45 | } else if (i - dp[i - 1] > 0 && s.charAt(i - 1 - dp[i - 1]) === "(") {
46 | dp[i] =
47 | dp[i - 1] + (i - dp[i - 1] >= 2 ? dp[i - 2 - dp[i - 1]] : 0) + 2;
48 | }
49 | }
50 | max = Math.max(dp[i], max);
51 | }
52 |
53 | return max;
54 | };
55 | ```
56 |
--------------------------------------------------------------------------------
/7.动态规划/编辑距离.md:
--------------------------------------------------------------------------------
1 | # 编辑距离
2 |
3 | ## 动态规划
4 |
5 | #### 通用解题思路
6 |
7 | > 1. 定义数组元素含义
8 | > 2. 找出关系数组元素间的关系式,并明确所求结果
9 | > 3. 找出初始值(二维 dp 找边,一维 dp 找点)
10 |
11 | #### 通用写代码思路
12 |
13 | > 1. 边界条件
14 | > 2. 定义一维/二维 dp,填充值为 null
15 | > 3. 初始值
16 | > 4. 状态转移方程
17 |
18 | ## 来源
19 |
20 | > leetcode: [传送门](https://leetcode-cn.com/problems/edit-distance/)
21 |
22 | ## 难度类型:困难
23 |
24 | ## 代码
25 |
26 | ```js
27 | const minDistance = function (word1, word2) {
28 | let m = word1.length;
29 | let n = word2.length;
30 | if (m <= 0 && n <= 0) {
31 | return 0;
32 | }
33 |
34 | //新建二维数组
35 | let dp = [];
36 | for (let i = 0; i <= m; i++) {
37 | dp[i] = new Array();
38 | for (let j = 0; j <= n; j++) {
39 | dp[i][j] = null;
40 | }
41 | }
42 |
43 | //初始值
44 | dp[0][0] = 0;
45 | for (let i = 1; i <= m; i++) {
46 | dp[i][0] = dp[i - 1][0] + 1;
47 | }
48 | for (let j = 1; j <= n; j++) {
49 | dp[0][j] = dp[0][j - 1] + 1;
50 | }
51 |
52 | //状态转移方程
53 | for (let i = 1; i <= m; i++) {
54 | for (let j = 1; j <= n; j++) {
55 | if (word1.charAt(i - 1) === word2.charAt(j - 1)) {
56 | dp[i][j] = dp[i - 1][j - 1];
57 | } else {
58 | dp[i][j] =
59 | Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
60 | }
61 | }
62 | }
63 |
64 | return dp[m][n];
65 | };
66 | ```
67 |
--------------------------------------------------------------------------------
/8.前端/Promise封装异步上传图片.md:
--------------------------------------------------------------------------------
1 | # `Promise`封装异步上传图片
2 |
3 | ```js
4 | function loadImageAsync(url) {
5 | return new Promise(function (resolve, reject) {
6 | let image = new Image();
7 | image.onload = function () {
8 | resolve(image);
9 | };
10 |
11 | image.onerror = function () {
12 | reject(new Error("Could not load image at" + url));
13 | };
14 |
15 | image.src = url;
16 | });
17 | }
18 | ```
19 |
--------------------------------------------------------------------------------
/8.前端/new的过程.md:
--------------------------------------------------------------------------------
1 | # `new`的过程
2 |
3 | ## 原理
4 |
5 | > 1. 创建一个空对象
6 | > 2. 链接到原型
7 | > 3. 绑定 this
8 | > 4. 返回一个新对象
9 |
10 | ```js
11 | function create() {
12 | let obj = Object.create();
13 | let Con = [].shift.call(arguments);
14 | obj._proto_ = Con.prototype;
15 | let result = Con.apply(obj, arguments);
16 | return typeof result === "object" ? result : obj;
17 | }
18 | ```
19 |
--------------------------------------------------------------------------------
/8.前端/spawn函数(Async函数实现原理).md:
--------------------------------------------------------------------------------
1 | # `spawn` 函数(`Async` 函数实现原理)
2 |
3 | > ## 说明:`spwan` 函数--自动执行器,需先了解 `Generator` 函数,`Async/await` 是 `Generator` 函数的语法糖
4 |
5 | ```js
6 | function spawn(genF) {
7 | return new Promise((resolve, reject) => {
8 | var gen = genF();
9 |
10 | function step(nextF) {
11 | try {
12 | var next = nextF();
13 | } catch (e) {
14 | return reject(e);
15 | }
16 |
17 | if (next.done) {
18 | return resolve(next.value);
19 | }
20 |
21 | Promise.resolve(next.value).then(
22 | (v) => {
23 | step(() => gen.next(v));
24 | },
25 | (e) => {
26 | step(() => gen.throw(e));
27 | }
28 | );
29 | }
30 |
31 | step(() => {
32 | gen.next(undefined);
33 | });
34 | });
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/8.前端/thunk函数(Generator函数实现自动流程管理原理).md:
--------------------------------------------------------------------------------
1 | # `thunk` 函数(`Generator` 函数实现自动流程管理原理)
2 |
3 | ## 说明
4 |
5 | > Thunk 函数是用来解决 JS 中传名调用的一种实现方式
6 | > Thunk 函数是一个单参数函数,只接受回调函数作为参数
7 | > Thunk 函数用于 Generator 函数的自动流程管理
8 |
9 | (ES5)
10 |
11 | ```js
12 | var Thunk = function (fn) {
13 | return function () {
14 | var args = Array.prototype.slice.call(arguments);
15 |
16 | return function (callback) {
17 | args.push(callback);
18 | return fn.apply(this, args);
19 | };
20 | };
21 | };
22 | ```
23 |
24 | (ES6)
25 |
26 | ```js
27 | const Thunk = function(fn){
28 | return fucntion(...args){
29 | return function(callback){
30 | return fn.call(this, ...args, callback);
31 | }
32 | }
33 | }
34 | ```
35 |
--------------------------------------------------------------------------------
/8.前端/函数柯里化.md:
--------------------------------------------------------------------------------
1 | # 函数柯里化
2 |
3 | ## 描述
4 |
5 | > 实现 add()方法,使计算结果能够满足如下预期:
6 | > add(1)(2)(3) = 6;
7 | > add(1, 2, 3)(4) = 10;
8 | > add(1)(2)(3)(4)(5) = 15;
9 |
10 | ```js
11 | function add() {
12 | // 第一次执行时,定义一个数组专门用来存储所有的参数
13 | let _args = Array.prototype.slice.call(arguments);
14 |
15 | // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
16 | let _adder = function () {
17 | _args.push(...arguments);
18 | return _adder;
19 | };
20 |
21 | // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
22 | _adder.toString = function () {
23 | return _args.reduce(function (a, b) {
24 | return a + b;
25 | });
26 | };
27 |
28 | return _adder;
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/8.前端/实现Instanceof.md:
--------------------------------------------------------------------------------
1 | # 实现`Instanceof`
2 |
3 | ## 核心:原型链的向上查找
4 |
5 | ```js
6 | function myInstanceof(left, right) {
7 | if (typeof left !== "object" || left == null) return false;
8 |
9 | let proto = Object.getPrototypeOf(left);
10 | while (true) {
11 | if (proto == null) return false;
12 | if (proto === right.prototype) return true;
13 | proto = Object.getPrototypeOf(proto);
14 | }
15 | }
16 | ```
17 |
--------------------------------------------------------------------------------
/8.前端/封装apply.md:
--------------------------------------------------------------------------------
1 | # 封装 apply
2 |
3 | ```js
4 | Function.prototype.myApply = function (context) {
5 | let context = context || window;
6 | context.fn = this;
7 |
8 | let result;
9 | if (arguments[1]) {
10 | result = context.fn(...arguments[1]);
11 | } else {
12 | result = context.fn();
13 | }
14 | delete context.fn;
15 |
16 | return result;
17 | };
18 | ```
19 |
--------------------------------------------------------------------------------
/8.前端/封装bind.md:
--------------------------------------------------------------------------------
1 | # 封装 bind
2 |
3 | ```js
4 | Function.prototype.myBind = function (context) {
5 | if (typeof this !== "function") {
6 | throw new TypeError("Error");
7 | }
8 |
9 | let _this = this;
10 | let args = [...arguments].slice(1);
11 | return function F() {
12 | if (this instanceof F) {
13 | return new _this(...args, ...arguments);
14 | }
15 |
16 | return _this.apply(context, args.concat(...arguments));
17 | };
18 | };
19 | ```
20 |
--------------------------------------------------------------------------------
/8.前端/封装call.md:
--------------------------------------------------------------------------------
1 | # 封装 call
2 |
3 | ```js
4 | Function.prototype.myCall = function (context) {
5 | let context = context || window;
6 | context.fn = this;
7 |
8 | let args = [...arguments].slice(1);
9 | let result = context.fn(...args);
10 | delete context.fn;
11 |
12 | return result;
13 | };
14 | ```
15 |
--------------------------------------------------------------------------------
/8.前端/封装map.md:
--------------------------------------------------------------------------------
1 | # 封装 Map
2 |
3 | > fn: 回调 context:回调作用域指定的 this
4 |
5 | ```js
6 | Array.prototype.myMap = function (fn, context) {
7 | // 1. 获取调用者this,并转为数组
8 | let arr = [].slice.call(this);
9 |
10 | // 2. 遍历调用者
11 | let arrMap = [];
12 | for (let i = 0; i < arr.length; i++) {
13 | if (!arr.hasOwnProperty(i)) {
14 | continue;
15 | }
16 | arrMap.push(fn.call(context, arr[i], i, this));
17 | }
18 |
19 | return arrMap;
20 | };
21 | ```
22 |
--------------------------------------------------------------------------------
/8.前端/手撕Promise(A+规范).md:
--------------------------------------------------------------------------------
1 | # 手撕`Promise`
2 |
3 | > ### Promise A+规范
4 |
5 | ```js
6 | class Promise {
7 | constructor(executor) {
8 | this.state = "pending";
9 | this.value = undefined;
10 | this.reason = undefined;
11 | this.onResolvedCallbacks = [];
12 | this.onRejectedCallbacks = [];
13 |
14 | let resolve = (value) => {
15 | if (this.state === "pending") {
16 | this.state = "resolved";
17 | this.value = value;
18 | this.onResolvedCallbacks.forEach((fn) => fn());
19 | }
20 | };
21 |
22 | let reject = (reason) => {
23 | if (this.state === "pending") {
24 | this.state = "rejected";
25 | this.reason = reason;
26 | this.onRejectedCallbacks.forEach((fn) => fn());
27 | }
28 | };
29 |
30 | try {
31 | executor(resolve, reject);
32 | } catch (err) {
33 | reject(err);
34 | }
35 | }
36 |
37 | then(onResolved, onRejected) {
38 | // onResolved如果不是函数,就忽略onResolved,直接返回value
39 | onResolved =
40 | typeof onResolved === "function" ? onResolved : (value) => value;
41 | // onRejected如果不是函数,就忽略onRejected,直接扔出错误
42 | onRejected =
43 | typeof onRejected === "function"
44 | ? onRejected
45 | : (err) => {
46 | throw err;
47 | };
48 |
49 | let promise2 = new Promise((resolve, reject) => {
50 | if (this.state === "resolved") {
51 | // 异步
52 | setTimeout(() => {
53 | try {
54 | let x = onResolved(this.value);
55 | resolvePromise(promise2, x, resolve, reject);
56 | } catch (e) {
57 | reject(e);
58 | }
59 | }, 0);
60 | }
61 |
62 | if (this.state === "rejected") {
63 | // 异步
64 | setTimeout(() => {
65 | // 如果报错
66 | try {
67 | let x = onRejected(this.reason);
68 | resolvePromise(promise2, x, resolve, reject);
69 | } catch (e) {
70 | reject(e);
71 | }
72 | }, 0);
73 | }
74 |
75 | if (this.state === "pending") {
76 | this.onResolvedCallbacks.push(() => {
77 | // 异步
78 | setTimeout(() => {
79 | try {
80 | let x = onResolved(this.value);
81 | resolvePromise(promise2, x, resolve, reject);
82 | } catch (e) {
83 | reject(e);
84 | }
85 | }, 0);
86 | });
87 | this.onRejectedCallbacks.push(() => {
88 | // 异步
89 | setTimeout(() => {
90 | try {
91 | let x = onRejected(this.reason);
92 | resolvePromise(promise2, x, resolve, reject);
93 | } catch (e) {
94 | reject(e);
95 | }
96 | }, 0);
97 | });
98 | }
99 | });
100 |
101 | // 返回promise,完成链式
102 | return promise2;
103 | }
104 | }
105 | ```
106 |
--------------------------------------------------------------------------------
/8.前端/手撕Promise(简易版).md:
--------------------------------------------------------------------------------
1 | # 手撕`Promise`
2 |
3 | > ### `Promise`简易版
4 |
5 | ```js
6 | class Promise {
7 | constructor(executor) {
8 | this.state = "pending";
9 | this.value = undefined;
10 | this.reason = undefined;
11 |
12 | let resolve = (value) => {
13 | if (this.state === "pending") {
14 | this.state = "resolved";
15 | this.value = value;
16 | }
17 | };
18 |
19 | let reject = (reason) => {
20 | if (this.state === "pending") {
21 | this.state = "rejected";
22 | this.reason = reason;
23 | }
24 | };
25 |
26 | try {
27 | executor(resolve, reject);
28 | } catch (err) {
29 | reject(err);
30 | }
31 | }
32 |
33 | then(onResolved, onRejected) {
34 | if (this.state === "resolved") {
35 | onResolved(this.value);
36 | }
37 |
38 | if (this.state === "rejected") {
39 | onRejected(this.reason);
40 | }
41 | }
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/8.前端/手撕Promise.all.md:
--------------------------------------------------------------------------------
1 | # 手撕`Promise.all`
2 |
3 | ```js
4 | function promiseAll(promises) {
5 | return new Promise(function (resolve, reject) {
6 | if (!Array.isArray(promises)) {
7 | return reject(new Error("Promises must be an array"));
8 | }
9 |
10 | let resolvedCount = 0;
11 | let promiseNum = promises.length;
12 | let resloveValue = [];
13 |
14 | for (let i = 0; i < promiseNum; i++) {
15 | Promise.resolve(promises[i]).then(
16 | (value) => {
17 | resloveValue[i] = value;
18 | resolvedCount++;
19 | if (resolvedCount === promiseNum) {
20 | return resloveValue;
21 | }
22 | },
23 | (err) => {
24 | return reject(err);
25 | }
26 | );
27 | }
28 | });
29 | }
30 | ```
31 |
--------------------------------------------------------------------------------
/8.前端/数组去重.md:
--------------------------------------------------------------------------------
1 | # 数组去重
2 |
3 | ## 第一种:数组的`indxOf`方法
4 |
5 | > #### 时间复杂度:O(n^2)
6 |
7 | ```js
8 | function unique(arr) {
9 | const temp = [];
10 |
11 | for (let i = 0; i < arr.length; i++) {
12 | if (temp.indexOf(arr[i]) === -1) {
13 | temp.push(arr[i]);
14 | }
15 | }
16 |
17 | return temp;
18 | }
19 | ```
20 |
21 | ## 第二种:排序后相邻去重法
22 |
23 | > #### 时间复杂度:O(n log n)
24 |
25 | ```js
26 | function unique(arr) {
27 | arr.sort();
28 | const temp = [arr[0]];
29 |
30 | for (let i = 1; i < arr.length; i++) {
31 | if (arr[i] !== temp[temp.length - 1]) {
32 | temp.push(arr[i]);
33 | }
34 | }
35 |
36 | return temp;
37 | }
38 | ```
39 |
40 | ## 第三种:`ES6`的`Set`方法
41 |
42 | > #### 时间复杂度:O(n)
43 |
44 | ```js
45 | function unique(arr) {
46 | return [...new Set(arr)];
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/8.前端/数组扁平化.md:
--------------------------------------------------------------------------------
1 | # 数组扁平化
2 |
3 | ## 第一种:循环+递归
4 |
5 | ```js
6 | function flattenDeep(arr) {
7 | let newArr = [];
8 |
9 | for (let i = 0; i < arr.length; i++) {
10 | if (Array.isArray(arr[i])) {
11 | newArr.push.apply(newArr, flattenDeep(arr[i]));
12 | } else {
13 | newArr.push(arr[i]);
14 | }
15 | }
16 |
17 | return newArr;
18 | }
19 | ```
20 |
21 | ## 第二种:`apply` + `some`
22 |
23 | ```js
24 | function flattenDeep(arr) {
25 | while (arr.some((item) => Array.isArray(item))) {
26 | arr = [].concat.apply([], arr);
27 | }
28 |
29 | return arr;
30 | }
31 | ```
32 |
33 | ## 第三种:扩展运算符(...)
34 |
35 | ```js
36 | function flattenDeep(arr) {
37 | while (arr.some((item) => Array.isArray(item))) {
38 | arr = [].concat(...arr);
39 | }
40 |
41 | return arr;
42 | }
43 | ```
44 |
45 | ## 第四种:数组的`reduce`
46 |
47 | ```js
48 | function flattenDeep(arr) {
49 | return arr.reduce((prev, next) => {
50 | return prev.concat(Array.isArray(next) ? flattenDeep(next) : next);
51 | }, []);
52 | }
53 | ```
54 |
55 | ## 第五种:`ES10`的`flat`
56 |
57 | ```js
58 | function flattenDeep(arr) {
59 | return arr.flat(Infinity);
60 | }
61 | ```
62 |
--------------------------------------------------------------------------------
/8.前端/深拷贝.md:
--------------------------------------------------------------------------------
1 | # 深拷贝
2 |
3 | ## 第一种:递归
4 |
5 | ```js
6 | function deepClone(obj) {
7 | let data;
8 |
9 | // 处理数组
10 | if (Object.prototype.toString.call(obj) === "[Object Array]") {
11 | data = [];
12 | for (let index = 0; index < obj.length; index++) {
13 | data.push(deepClone(obj[index]));
14 | }
15 | } else if (Object.prototype.toString.call(obj) === "[Object Object]") {
16 | // 处理对象
17 | data = {};
18 | for (let key in obj) {
19 | data[key] = deepClone(obj[key]);
20 | }
21 | } else {
22 | // 基本数据类型
23 | return obj;
24 | }
25 |
26 | return data;
27 | }
28 | ```
29 |
30 | ## 第二种:序列化与反序列化
31 |
32 | ```js
33 | function deepClone(obj) {
34 | return JSON.parse(JSON.strigify(obj));
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/8.前端/红黄绿灯(字节).md:
--------------------------------------------------------------------------------
1 | # 红黄绿灯(字节跳动面试题)
2 |
3 | > ### 题目:红灯 3 秒亮一次,绿灯 2 秒亮一次,黄灯 1 秒亮一次;如何让三个灯不断交替重复亮灯?
4 |
5 | > ### 思路:`Promise` + 递归
6 |
7 | ```js
8 | function red() {
9 | console.log("red");
10 | }
11 | function green() {
12 | console.log("green");
13 | }
14 | function yellow() {
15 | console.log("yellow");
16 | }
17 |
18 | const light = (timmer, cb) => {
19 | return new Promise((resolve, reject) => {
20 | setTimeout(() => {
21 | cb();
22 | resolve();
23 | }, timmer);
24 | });
25 | };
26 |
27 | const step = () => {
28 | Promise.resolve()
29 | .then(() => {
30 | light(3000, red);
31 | })
32 | .then(() => {
33 | light(2000, green);
34 | })
35 | .then(() => {
36 | light(1000, yellow);
37 | })
38 | .then(() => {
39 | step();
40 | });
41 | };
42 |
43 | step();
44 | ```
45 |
--------------------------------------------------------------------------------
/8.前端/节流.md:
--------------------------------------------------------------------------------
1 | # 节流
2 |
3 | ```js
4 | /**
5 | * @desc 函数节流
6 | * @param func 函数
7 | * @param wait 延迟执行毫秒数
8 | * @param type 1 表示时间戳版,2 表示定时器版
9 | * @returns {Void}
10 | */
11 | const throttle = (func, wait, type) => {
12 | let timeout;
13 | let previous = 0;
14 |
15 | return function () {
16 | let context = this;
17 | let args = arguments;
18 |
19 | if (type === 1) {
20 | let now = Date.now();
21 |
22 | if (now - previous > wait) {
23 | func.apply(context, args);
24 | previous = now;
25 | }
26 | } else if (type === 2) {
27 | if (!timeout) {
28 | timeout = setTimeout(() => {
29 | timeout = null;
30 | func.apply(context, args);
31 | }, wait);
32 | }
33 | }
34 | };
35 | };
36 | ```
37 |
--------------------------------------------------------------------------------
/8.前端/防抖.md:
--------------------------------------------------------------------------------
1 | # 防抖
2 |
3 | ```js
4 | /**
5 | * @desc 函数防抖
6 | * @param func 函数
7 | * @param wait 延迟执行毫秒数
8 | * @param immediate true 表立即执行,false 表非立即执行
9 | * @returns {Void}
10 | */
11 | const debounce = (func, wait, immediate = true) => {
12 | let timeout;
13 |
14 | return function () {
15 | let context = this;
16 | let args = arguments;
17 | if (timeout) clearTimeout(timeout);
18 |
19 | if (immediate) {
20 | let callNow = !timeout;
21 |
22 | timeout = setTimeout(() => {
23 | timeout = null;
24 | }, wait);
25 |
26 | if (callNow) func.apply(context, args);
27 | } else {
28 | timeout = setTimeout(() => {
29 | func.apply(context, args);
30 | }, wait);
31 | }
32 | };
33 | };
34 | ```
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # leetcode
2 |
3 | > ## 校招/社招前端算法试题分类
4 |
5 | ### 排序
6 |
7 | - [冒泡排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/冒泡排序.md)
8 | - [选择排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/选择排序.md)
9 | - [插入排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/插入排序.md)
10 | - [希尔排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/希尔排序.md)
11 | - [归并排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/归并排序.md)
12 | - [快速排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/快速排序.md)
13 | - [堆排序](https://github.com/xingpengchao/leetcode/blob/master/1.排序/堆排序.md)
14 | - [十大排序对比](https://github.com/xingpengchao/leetcode/blob/master/1.排序/十大排序对比.md)
15 |
16 | ### 二分查找
17 |
18 | - [二分查找](https://github.com/xingpengchao/leetcode/blob/master/2.二分查找/二分查找.md)
19 |
20 | ### 栈、队列
21 |
22 | - [双栈模拟队列](https://github.com/xingpengchao/leetcode/blob/master/3.栈、队列/双栈模拟队列.md)
23 | - [有效的括号](https://github.com/xingpengchao/leetcode/blob/master/3.栈、队列/有效的括号.md)
24 | - [将无序栈转为有序栈](https://github.com/xingpengchao/leetcode/blob/master/3.栈、队列/将无序栈转为有序栈.md)
25 |
26 | ### 链表
27 |
28 | - [反转链表](https://github.com/xingpengchao/leetcode/blob/master/4.链表/反转链表.md)
29 | - [手撕单向链表](https://github.com/xingpengchao/leetcode/blob/master/4.链表/手撕单向链表.md)
30 | - [合并两个有序链表](https://github.com/xingpengchao/leetcode/blob/master/4.链表/合并两个有序链表.md)
31 | - [链表中倒数第 K 个节点](https://github.com/xingpengchao/leetcode/blob/master/4.链表/链表中倒数第K个节点.md)
32 | - [两个链表的第一个公共节点](https://github.com/xingpengchao/leetcode/blob/master/4.链表/两个链表的第一个公共节点.md)
33 | - [判断链表是否有环](https://github.com/xingpengchao/leetcode/blob/master/4.链表/判断链表是否有环.md)
34 | - [两数相加](https://github.com/xingpengchao/leetcode/blob/master/4.链表/两数相加.md)
35 |
36 | ### 二叉树
37 |
38 | - [前序遍历](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/前序遍历.md)
39 | - [中序遍历](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/中序遍历.md)
40 | - [后序遍历](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/后序遍历.md)
41 | - [层次遍历](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/层次遍历.md)
42 | - [二叉树的深度](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉树的深度.md)
43 | - [对称二叉树](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/对称二叉树.md)
44 | - [平衡二叉树](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/平衡二叉树.md)
45 | - [重建二叉树](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/重建二叉树.md)
46 | - [按层打印二叉树](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/按层打印二叉树.md)
47 | - [之字形打印二叉树](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/之字形打印二叉树.md)
48 | - [二叉搜索树的后序遍历序列](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉搜索树的后序遍历序列.md)
49 | - [二叉搜索树的第 K 个节点](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉搜索树的第K个节点.md)
50 | - [二叉树的镜像](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉树的镜像.md)
51 | - [二叉树的最大宽度](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉树的最大宽度.md)
52 | - [二叉树的下一个节点](https://github.com/xingpengchao/leetcode/blob/master/5.二叉树/二叉树的下一个节点.md)
53 |
54 | ### 双指针
55 |
56 | - [字符串压缩](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/字符串压缩.md)
57 | - [验证回文串](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/验证回文串.md)
58 | - [滑动窗口的最大值](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/滑动窗口的最大值.md)
59 | - [和为 S 的连续正整数](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/和为S的连续正整数.md)
60 | - [和为 S 的两个数(微改版)]()
61 | - [盛最多水的容器](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/盛最多水的容器.md)
62 | - [接雨水](https://github.com/xingpengchao/leetcode/blob/master/6.双指针/接雨水.md)
63 |
64 | ### 动态规划
65 |
66 | - [斐波那契数列/跳台阶/矩形覆盖](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/斐波那契数列、跳台阶、矩形覆盖.md)
67 | - [最小路径和](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/最小路径和.md)
68 | - [不同路径](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/不同路径.md)
69 | - [最长回文子串](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/最长回文子串.md)
70 | - [编辑距离](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/编辑距离.md)
71 | - [最长有效括号](https://github.com/xingpengchao/leetcode/blob/master/7.动态规划/最长有效括号.md)
72 |
73 |
95 |
--------------------------------------------------------------------------------