🌙 算法训练
🌙 一、位运算 (opens new window)
🌙 1.位1的个数 (opens new window)
🌙 二、数组 (opens new window)
🌙 1.合并两个有序数组 (opens new window)
🌙 2.最小路径和 (opens new window)
🌙 3.两数之和 (opens new window)
🌙 4.两数之和 II (opens new window)
🌙 5.三数之和 (opens new window)
🌙 6.四数之和 (opens new window)
🌙 7.滑动窗口的最大值 (opens new window)
🌙 8.最长上升子序列 (opens new window)
🌙 9.最长公共前缀 (opens new window)
🌙 10.最长公共子序列 (opens new window)
/**
* @param {string} text1
* @param {string} text2
* @return {number}
*/
var longestCommonSubsequence = function(text1, text2) {
let len1 = text1.length;
let len2 = text2.length;
let dp = new Array(len1 + 1).fill(0).map(d => new Array(len2 + 1).fill(0));
for(let i=1; i<=len1; i++) {
for(let j=1; j<=len2; j++) {
if(text1[i-1] === text2[j-1]) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[len1][len2];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
🌙 11.乘积最大子数组 (opens new window)
🌙 12.俄罗斯套娃信封问题【排序+最长上升子序列】 (opens new window)
/**
* @param {number[][]} envelopes
* @return {number}
*/
var maxEnvelopes = function(envelopes) {
if (envelopes.length === 1) return 1;
// 安左侧升序,相同,安右侧降序
// [1,5],[2,5],[2,4], [3,4]
envelopes.sort((a, b) => {
if (a[0] !== b[0]) return a[0] - b[0];
else return b[1] - a[1];
});
let LISArr = [];
for (let [key, value] of envelopes) {
LISArr.push(value);
}
console.log( LISArr);
return LIS(LISArr);
};
// 计算最长上升子序列
function LIS(arr) {
let dp = [];
let maxAns = 0;
for (let i = 0; i < arr.length; i++) {
dp[i] = 1;
}
for (let i = 1; i < arr.length; i++) {
for (let j = i; j >= 0; j--) {
if (arr[i] > arr[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1)
}
maxAns = Math.max(maxAns, dp[i]);
}
}
return maxAns;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
🌙 13.最长连续递增序列【快慢指针】 (opens new window)
/**
* @param {number[]} nums
* @return {number}
*/
var findLengthOfLCIS = function(nums) {
if (nums.length === 0) return 0;
const n = nums.length;
let left = 0, right = 1;
let globalMaxLen = 1, maxLen = 1;
while (right < n) {
if (nums[right] > nums[left]) maxLen++;
else {
maxLen = 1;
}
left++;
right++;
globalMaxLen = Math.max(globalMaxLen, maxLen);
}
return globalMaxLen;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
🌙 14.最长连续序列 【哈希表】 (opens new window)
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
if (nums.length === 0) return 0;
const set = new Set(nums);
const n = nums.length;
let globalLongest = 1;
for (let i = 0; i < n; i++) {
if (!set.has(nums[i] - 1)) {
let longest = 1;
let currentNum = nums[i];
while (set.has(currentNum + 1)) {
currentNum += 1;
longest++;
}
globalLongest = Math.max(globalLongest, longest);
}
}
return globalLongest;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
🌙 15.盛最多水的容器【双指针】 (opens new window)
/**
* @param {number[]} height
* @return {number}
*/
var maxArea = function(height) {
let n = height.length;
let left = 0, right = n - 1;
let maxOpacity = 0;
while (left < right) {
let res = Math.min(height[left], height[right]) * (right - left);
maxOpacity = Math.max(maxOpacity, res);
if (height[left] < height[right]) left++
else right--;
}
return maxOpacity;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🌙 16.删除有序数组中的重复项【快慢指针】 (opens new window)
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
if (nums.length <= 1) return nums.length;
let lo = 0, hi = 0;
while (hi < nums.length) {
while (nums[lo] === nums[hi] && hi < nums.length) hi++;
if (nums[lo] !== nums[hi] && hi < nums.length) {
lo++;
nums[lo] = nums[hi];
}
hi++;
}
return lo + 1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
🌙 17.和为K的子数组【哈希表】 (opens new window)
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(nums, k) {
let cnt = 0;
let sum0_i = 0, sum0_j = 0;
let map = new Map();
map.set(0, 1);
for (let i = 0; i <= nums.length; i++) {
sum0_i += nums[i];
sum0_j = sum0_i - k;
console.log('map', sum0_j, map.get(sum0_j))
if (map.has(sum0_j)) {
cnt += map.get(sum0_j);
}
let sumCnt = map.get(sum0_i) || 0;
map.set(sum0_i, sumCnt + 1);
}
return cnt;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
🌙 17.接雨水【双指针】 (opens new window)
/**
* @param {number[]} height
* @return {number}
*/
var trap = function(height) {
let ans = 0;
let left = 0, right = height.length - 1;
let leftMax = 0, rightMax = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (height[left] < height[right]) {
ans += leftMax - height[left];
++left;
} else {
ans += rightMax - height[right];
--right;
}
}
return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
🌙 18.跳跃游戏(贪心) (opens new window)
/**
* @param {number[]} nums
* @return {boolean}
*/
var canJump = function(nums) {
let faster = 0;
for (let i = 0; i < nums.length - 1; i++) {
faster = Math.max(faster, i + nums[i]);
if (faster <= i) return false;
}
return faster >= nums.length - 1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
🌙 19.用最少数量的箭引爆气球 (opens new window)
/**
* @param {number[][]} points
* @return {number}
*/
var findMinArrowShots = function(points) {
if(points.length < 1) return 0;
points.sort((a,b) => a[0] - b[0]);
let ans = 1;
for(let i=1; i<points.length; i++) {
if(points[i][0] > points[i-1][1]) {
ans++;
} else {
points[i][1] = Math.min(points[i][1], points[i-1][1])
}
}
return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
🌙 20.合并区间 (opens new window)
/**
* @param {number[][]} intervals
* @return {number[][]}
*/
var merge = function(intervals) {
if(intervals.length < 2) return intervals;
intervals.sort((a,b) => a[0] - b[0]);
let ans = [intervals[0]];
for(let i=1; i<intervals.length; i++) {
let last = ans[ans.length - 1];
if(last[1] >= intervals[i][0]) {
last[1] = Math.max(last[1], intervals[i][1])
} else {
ans.push(intervals[i])
}
}
return ans;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
🌙 21.在排序数组中查找元素的第一个和最后一个位置 (opens new window)
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
const left = leftBound(nums, target);
const right = rightBound(nums, target);
return [left, right];
};
function leftBound(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] === target) {
right = mid - 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
if (left >= nums.length || nums[left] !== target) {
return -1;
}
return left;
}
function rightBound(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (nums[mid] === target) {
left = mid + 1;
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
}
}
if (right < 0 || nums[right] !== target) {
return -1;
}
return right;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
🌙 四、栈 (opens new window)
🌙 1.有效的括号 (opens new window)
🌙 2.每日温度 (opens new window)
🌙 3.逆波兰表达式求值 (opens new window)
🌙 4.接雨水 (opens new window)
🌙 五、队列 (opens new window)
🌙 1.设计循环队列 (opens new window)
🌙 2.设计循环双端队列 (opens new window)
🌙 六.[队列和广度优先搜索(BFS)]
🌙 1.完全平方数 (opens new window)
🌙 2.路径总和 (opens new window)
🌙 3.二叉树的层序遍历 (opens new window)
🌙 4.[解数独]
/**
* @param {string[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solveSudoku = function(/** @type {any} */ board) {
backtrack(board);
};
/**
* @param {string | any[]} board
*/
function backtrack(board) {
for(let i = 0; i < board.length; i++) {
for(let j = 0; j < board[0].length; j++) {
if(board[i][j] !== '.') continue;
for(let k = 1; k <= 9; k++) {
if(isValid(board, i, j, k.toString())) {
board[i][j] = k.toString();
if(backtrack(board)) return true;
board[i][j] = '.';
}
}
return false;
}
}
return true;
}
/**
* @param {string | any[]} board
* @param {number} row
* @param {number} col
* @param {string} val
*/
function isValid(board, row, col, val) {
for(let i = 0; i < 9; i++) {
if(board[row][i] === val) {
return false;
}
}
for(let i = 0; i < 9; i++) {
if(board[i][col] === val) {
return false;
}
}
const m = Math.floor(row / 3) * 3;
const n = Math.floor(col / 3) * 3;
for(let i = m; i < m + 3; i++) {
for(let j = n; j < n + 3; j++) {
if(board[i][j] === val) {
return false;
}
}
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
🌙 5.迷宫问题
/**
* @param {number[][]} [grid]
* @param {number} [row]
* @param {number} [col]
*/
const grid = [
[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 1],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
];
const row = 5;
const col = 5;
function resolveMaze(grid = [], row = 1, col = 1) {
/** @type {number[][]} */
const res = [];
const visited = new Map();
const direction = [[-1, 0], [1, 0], [0, -1], [0, 1]];
backtrack(grid, 0, 0);
/**
* @param {number[][]} arr
* @param {number} m
* @param {number} n
*/
function backtrack(arr, m, n) {
// 到达终点
if(m === row - 1 && n === col - 1) {
res.push([m, n]);
for(const item of res) {
console.log(`(${item[0]},${item[1]})`);
}
return true;
}
if(m < 0 || n < 0 || m >= row || n >= col || visited.has(`${m},${n}`)){
return false;
}
if(grid[m][n] === 1) return false;
// 记录
visited.set(`${m},${n}`, true);
res.push([m, n]);
// 做选择
for(let i = 0; i < direction.length; i++) {
const _row = m + direction[i][0];
const _col = n + direction[i][1];
if(backtrack(grid, _row, _col)) {
return true;
}
}
// 撤销
visited.delete(`${m},${n}`);
res.pop();
return false;
}
return res;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
🌙 七、树 (opens new window)
🌙 1.二叉树的前序遍历 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var preorderTraversal = function(root) {
var res = [];
preOrder(root, res);
return res;
};
var preOrder = function(root, res) {
if(!root) return;
// 根 - 左 - 右
res.push(root.val);
preOrder(root.left, res);
preOrder(root.right, res);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🌙 2.二叉树的中序遍历 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var inorderTraversal = function(root) {
var res = [];
inOrder(root, res);
return res;
};
var inOrder = function(root, res) {
if(!root) return;
// 左 - 根 - 右
inOrder(root.left, res);
res.push(root.val);
inOrder(root.right, res);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🌙 3.二叉树的后序遍历 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number[]}
*/
var postorderTraversal = function(root) {
var res = [];
postOrder(root, res);
return res;
};
var postOrder = function(root, res) {
if(!root) return;
// 左 - 右 - 根
postOrder(root.left, res);
postOrder(root.right, res);
res.push(root.val)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
🌙 4.二叉树的层序遍历 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number[][]}
*/
var levelOrder = function(root) {
var res = [];
// 广度优先遍历
bfs(root, res);
return res;
};
var bfs = function(root, res) {
if(!root) return;
// 根入队列
var queue = [root];
while(queue.length > 0) {
// 记录size
var size = queue.length;
// 保存当前层数据
var data = [];
// 此处使用size,不要使用queue.length,因为他是一直变化的
for(var i=0; i < size; i++) {
// 出队列
var cur = queue.shift();
// 保存数据
data.push(cur.val);
// 左子树入队列
if(cur.left) queue.push(cur.left);
// 右子树入队列
if(cur.right) queue.push(cur.right);
}
res.push(data);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
🌙 5.二叉树的最大深度 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var maxDepth = function(root) {
if(!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right))
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌙 6.对称二叉树 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isSymmetric = function(root) {
if(!root) return true;
return helper(root.left, root.right);
};
// 判断左右节点是否对称
var helper = function(root1, root2) {
// 都不存在
if(!root1 && !root2) return true;
// 有一个不存在
if(!root1 || !root2) return false;
// 都存在,值不同
if(root1.val !== root2.val) return false;
// 都存在,值相同,继续比较子节点
return helper(root1.left, root2.right) && helper(root1.right, root2.left);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
🌙 7.验证二叉搜索树 (opens new window)
递归:
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {boolean} */ var isValidBST = function(root) { return helper(root, -Infinity, Infinity); }; var helper = function(root, lower, higher) { if(!root) return true; if(root.val <= lower || root.val >= higher) return false; // 维护这个最值,左节点的最大值为root.val;右节点的最小值为root.val return helper(root.left, lower, root.val) && helper(root.right, root.val, higher); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21迭代:中序遍历二叉搜索树的结果序列是一个升序序列
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {boolean} */ var isValidBST = function(root) { // 中序遍历 let inorder = -Infinity; let stack = []; while(root !== null || stack.length) { // 先遍历左子树 while(root !== null) { stack.push(root); root = root.left; } // 左子树出栈 root = stack.pop(); // 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树 if(root.val <= inorder) return false; inorder = root.val; root = root.right; } return true; };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
🌙 8.二叉搜索树的最近公共祖先 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
//如果根节点和p,q的差相乘是正数,说明这两个差值要么都是正数要么都是负数,也就是说
//他们肯定都位于根节点的同一侧,就继续往下找
while ((root.val - p.val) * (root.val - q.val) > 0) {
root = p.val < root.val ? root.left : root.right;
}
//如果相乘的结果是负数,说明p和q位于根节点的两侧,如果等于0,说明至少有一个就是根节点
return root;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
🌙 9.二叉树的最近公共祖先 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {TreeNode} p
* @param {TreeNode} q
* @return {TreeNode}
*/
var lowestCommonAncestor = function(root, p, q) {
if(root === null || root === p || root === q) return root;
// 分别在left和right中找p和q的公共祖先
var left = lowestCommonAncestor(root.left, p, q);
var right = lowestCommonAncestor(root.right, p, q);
//如果left为空,说明这两个节点在cur结点的右子树上,我们只需要返回右子树查找的结果即可
if(left === null) return right;
//同理
if(right === null) return left;
//如果left和right都不为空,说明这两个节点一个在root的左子树上一个在root的右子树上,
//我们只需要返回root结点即可。
return root;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
🌙 10.平衡二叉树 (opens new window)
🌙 11.路径总和 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} sum
* @return {boolean}
*/
var hasPathSum = function(root, sum) {
if(!root) return false;
if(!root.left && !root.right && root.val === sum) return true;
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
🌙 12.从中序与后序遍历序列构造二叉树 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number[]} inorder
* @param {number[]} postorder
* @return {TreeNode}
*/
var buildTree = function(inorder, postorder) {
return helper(inorder, postorder, 0, inorder.length - 1, 0, postorder.length - 1);
};
var helper = function(inorder, postorder, inorderStart, inorderEnd, postorderStart, postorderEnd) {
// 判断越界
if(inorderStart > inorderEnd || postorderStart > postorderEnd) return null;
// 根节点
const rootVal = postorder[postorderEnd];
const root = new TreeNode(rootVal);
// 判断是不是只有一个节点
if(postorderStart === postorderEnd) return root;
// **在inorder中找到root对应的index将inorder分为左右两个子节点**
const rootIndex = inorder.indexOf(rootVal);
root.left = helper(inorder, postorder, inorderStart, rootIndex - 1, postorderStart, postorderStart + ((rootIndex - 1) - inorderStart)); // ((rootIndex - 1) - inorderStart) 即左子树的长度
root.right = helper(inorder, postorder, rootIndex + 1, inorderEnd, postorderEnd - 1 - (inorderEnd - (rootIndex + 1)), postorderEnd - 1); // (inorderEnd - (rootIndex + 1)) 即右子树的长度
return root;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
🌙 13.从前序与中序遍历序列构造二叉树 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number[]} preorder
* @param {number[]} inorder
* @return {TreeNode}
*/
var buildTree = function(preorder, inorder) {
return helper(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
};
var helper = function(preorder, inorder, preorderStart, preorderEnd, inorderStart, inorderEnd) {
// 判断越界
if(preorderStart > preorderEnd || inorderStart > inorderEnd) return null;
// 根节点
const rootVal = preorder[preorderStart];
const root = new TreeNode(rootVal);
// 判断preorder是不是只有一个节点
if(preorderStart === preorderEnd) return root;
// **在中序遍历中找到rootIndex,将inorder分为左右两个子节点**
const rootIndex = inorder.indexOf(rootVal);
root.left = helper(preorder, inorder, preorderStart + 1, preorderStart + 1 + ((rootIndex - 1) - inorderStart), inorderStart, rootIndex - 1); // (rootIndex - 1) - inorderStart)即左子树长度
root.right = helper(preorder, inorder,(preorderEnd - (inorderEnd - (rootIndex + 1))),preorderEnd, rootIndex + 1, inorderEnd); // (inorderEnd - (rootIndex + 1)) 即右子树长度
return root;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
🌙 14.从前序和后序遍序列历构造二叉树 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {number[]} pre
* @param {number[]} post
* @return {TreeNode}
*/
var constructFromPrePost = function(pre, post) {
return helper(pre, post, 0, pre.length - 1, 0, post.length - 1);
};
var helper = function(pre, post, preStart, preEnd, postStart, postEnd) {
// 判断越界
if(preStart > preEnd || postStart > postEnd) return null;
// 根节点
const rootVal = pre[preStart];
const root = new TreeNode(rootVal);
// 判断是不是只有一个节点
if(preStart === preEnd) return root;
// 先在preorder中找到左子树的leftRootVal
const leftRootVal = pre[preStart + 1];
// **然后在post中找到leftRootIndex, 然后根据这个index将post分为左右两个子节点**
const index = post.indexOf(leftRootVal);
root.left = helper(pre, post, preStart + 1, preStart + 1 + (index - postStart) , postStart, index); // (index - postStart) 即左子树长度
root.right = helper(pre, post, (preEnd - (postEnd - 1 - (index + 1))), preEnd, index + 1, postEnd - 1); // (postEnd - 1 - (index + 1))即右子树长度
return root;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
🌙 15.二叉搜索树转为单链表 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
// 正确解法1
var convertBiNode1 = function(root) {
let head = new TreeNode(null);
let pre = head;
var inOrder = function(root) {
if(!root) return;
// 左 - 右 - 根
inOrder(root.left);
root.left = null;
pre.right = root;
pre = root;
inOrder(root.right);
}
inOrder(root);
return head.right;
};
// 将inOrder提取出来:
// 错误解法
var convertBiNode = function(root) {
let head = new TreeNode(null);
let pre = head;
inOrder(root, pre);
return head.right;
};
var inOrder = function(root, pre) {
if(!root) return;
// 左 - 根 - 右
inOrder(root.left, pre);
root.left = null;
pre.right = root;
pre = root;
inOrder(root.right, pre);
}
// 正确解法2
var convertBiNode2 = function(root) {
let head = new TreeNode(null);
inOrder(root, head);
return head.right;
};
var pre;
var inOrder = function(root, head) {
if(!root) return;
pre = head;
// 左 - 根 - 右
inOrder(root.left, pre);
root.left = null;
pre.right = root;
pre = root;
inOrder(root.right, pre);
}
// 正确解法3
var convertBiNode = function(root) {
let head = new TreeNode(null);
inOrder(root, head);
return head.right;
};
// var pre;
var inOrder = function(root, pre) {
if(!root) return pre;
// 左 - 根 - 右
pre = inOrder(root.left, pre);
root.left = null;
pre.right = root;
pre = root;
pre = inOrder(root.right, pre);
return pre;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
🌙 16.二叉树转为单链表 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {void} Do not return anything, modify root in-place instead.
*/
var flatten = function(root) {
let pre = new TreeNode();;
var preOrder = function(root) {
if(!root) return;
let left = root.left;
let right = root.right;
root.left = null;
pre.right = root;
pre = root;
preOrder(left);
preOrder(right);
}
// 调用
preOrder(root);
root = pre.right;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
🌙 17.二叉搜索树转为双向链表 (opens new window)
思路:先中序遍历二叉树,在将结果转为循环双向链表
/** * // Definition for a Node. * function Node(val,left,right) { * this.val = val; * this.left = left; * this.right = right; * }; */ /** * @param {Node} root * @return {Node} */ var treeToDoublyList = function(root) { if(!root) return root; let res = []; inOrder(root, res); // 方式一: // var head = res[0]; // var tail = res[res.length - 1]; // tail.right = head; // head.left = tail; // for(var i=1; i < res.length; i++) { // // 更改left和right指向 // head.right = res[i]; // res[i].left = head; // head = res[i]; // } // 方式二: // var pre = null; // var head = null; // for(var i=0; i < res.length; i++) { // if(pre) { // // 更改left和right指向 // pre.right = res[i]; // res[i].left = pre; // } else { // head = res[i] // } // pre = res[i]; //} // 方式三: var pre = res[0]; var head = res[0]; for(var i=1; i < res.length; i++) { // 更改left和right指向 pre.right = res[i]; res[i].left = pre; pre = res[i]; } // 循环完毕,pre指向了最后一个节点 // 处理首尾节点 pre.right = head; head.left = pre; return res[0]; }; var inOrder = function(root, res) { if(!root) return; var left = root.left; var right = root.right; // 左 - 根 - 右 inOrder(left, res); res.push(root); inOrder(right, res); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75改进:在中序遍历的过程中把二叉树转为双向链表,然后再处理首尾链表,转为循环双向链表
/** * // Definition for a Node. * function Node(val,left,right) { * this.val = val; * this.left = left; * this.right = right; * }; */ /** * @param {Node} root * @return {Node} */ var treeToDoublyList = function(root) { if(!root) return root; let pre = null; let head = null; var inOrder = function(cur) { if(!cur) return; // 左 - 根 - 右 inOrder(cur.left); // 修改为链表 if(pre) { pre.right = cur; cur.left = pre; } else { // 记录第一个节点 head = cur; } // 保存当前节点 pre = cur; inOrder(cur.right); } inOrder(root); head.left = pre; // 此时pre指向了最后一个节点 pre.right = head; return head; };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
🌙 18.有序链表转换二叉搜索树 (opens new window)
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {ListNode} head
* @return {TreeNode}
*/
var sortedListToBST = function(head) {
// 链表为空
if(!head) return null;
// 链表只有一个节点
if(!head.next) return new TreeNode(head.val);
// 找到中继节点
var pre = head; // pre慢slow一步
// slow每次走一步 fast每次走两步,当fast走到终点时,slow刚好在中途
var slow = head.next;
var fast = head.next.next;
while(fast && fast.next) {
pre = pre.next;
slow = slow.next;
fast = fast.next.next;
}
// 此时pre的下一个节点就是中继节点slow,将其打断把链表一分为二
pre.next = null;
// 将中继节点作为树根节点
var root = new TreeNode(slow.val);
// 左子树开始节点
var left = head;
// 右子树开始节点
var right = slow.next;
root.left = sortedListToBST(left);
root.right = sortedListToBST(right);
return root;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
🌙 19.搜索树中的 二分查找 (opens new window)
/**
* Definition for a binary tree node.
* function TreeNode(val) {
* this.val = val;
* this.left = this.right = null;
* }
*/
/**
* @param {TreeNode} root
* @param {number} val
* @return {TreeNode}
*/
var searchBST = function(root, val) {
if (root == null) return null;
if (root.val === val) return root;
if (root.val > val) {
return searchBST(root.left, val);
} else if (root.val < val) {
return searchBST(root.right, val);
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
🌙 八、动态规划 (opens new window)
🌙 1.编辑距离 (opens new window)
🌙 2.滑动窗口最大值 (opens new window)
🌙 3.滑动窗口中位数 (opens new window)
🌙 4.最长上升子序列 (opens new window)
/**
* @param {number[]} nums
* @return {number}
*/
var lengthOfLIS = function(nums) {
if(!nums || !nums.length) return 0;
let dp = new Array(nums.length).fill(1);
for(let i=0; i<nums.length; i++) {
for(let j=i; j>=0; j--) {
if(nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return Math.max(...dp)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
🌙 5.剪绳子 (opens new window)
🌙 6.剪绳子 II (opens new window)
🌙 7.有效的字母异位词 (opens new window)
🌙 8.N 皇后 (opens new window)
🌙 9.N皇后 II (opens new window)
🌙 10.买卖股票的最佳时机 (opens new window)
🌙 11.买卖股票的最佳时机 II (opens new window)
🌙 12.买卖股票的最佳时机 III (opens new window)
🌙 13.买卖股票的最佳时机 IV (opens new window)
🌙 14.买卖股票的最佳时机含手续费 (opens new window)
🌙 15.最佳买卖股票时机含冷冻期 (opens new window)
🌙 16.打家劫舍 (opens new window)
🌙 17.打家劫舍 II (opens new window)
🌙 18.打家劫舍 III (opens new window)
🌙 19.岛屿数量 (opens new window)
🌙 20.零钱兑换 (opens new window)
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
if (amount === 0) return 0;
let dp = [];
for (let i = 0; i <= amount; i++) {
dp[i] = amount + 1;
}
dp[0] = 0;
for (let i = 0; i <= amount; i++) {
for (let j = 0; j < coins.length; j++) {
if (i >= coins[j]) {
dp[i] = Math.min(dp[i - coins[j]] + 1, dp[i])
}
}
}
return dp[amount] > amount ? -1 : dp[amount];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
🌙 21.零钱兑换 II (opens new window)
🌙 22.有效的数独 (opens new window)
🌙 23.路径总和 (opens new window)
🌙 24.路径总和 III (opens new window)
🌙 八、其他
🌙 1.斐波那契数列 (opens new window)
function fibonacci(n) {
return help(n,1,1)
}
// 尾递归优化
function help(n,a,b) {
if (n <= 2) {
return b;
}
return help(n - 1, b, a+b);
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
真正实现尾递归优化,只是改写代码优化还不够,还需要编译器或解释器的支持才行
🌙 2.字符串加法
function strAdd(a, b) {
let i = a.toString().trim().length;
let j = b.toString().trim().length;
let ans = '';
let carry = 0;
while(i >=0 || j >=0) {
let sum;
let x=0, y=0;
if(i>=0) {
x = +a[i];
i--;
}
if(j>=0) {
y = +b[i];
j--;
}
sum = x + y + carry;
if(sum >= 10) {
carry = Math.floor(sum / 10);
sum = sum % 10;
} else {
carry = 0;
}
ans = sum + ans;
}
if(carry) {
ans = carry + ans;
}
return ans;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
🌙 3.字符串乘法
function strMul(a, b) {
a = a.toString().trim();
b = b.toString().trim();
let m = a.length;
let n = b.length;
let arr = new Array(m + n).fill(0);
// 从个位开始相乘
for(let i=m-1; i>=0; i--) {
for(let j=n-1; j>=0; j--) {
let mul = a[i] * b[j];
let p1 = i + j;
let p2 = i + j + 1;
let sum = mul + arr[p2];
arr[p2] = sum % 10;
arr[p1] += ~~(sum / 10);
}
}
// 去除首位多余的0
let i=0;
while(i<arr.length && arr[i]===0) {
i++;
}
return arr.slice(i).join('') || '0';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
🌙 4.最长回文子串 (opens new window)
/**
* @param {string} s
* @return {string}
*/
var longestPalindrome = function(s) {
if(s.length < 2) return s;
let ans = '', max = 0;
for(let i=0; i<s.length; i++) {
let str1 = palindrome(s, i, i);
let str2 = palindrom(s, i, i+1);
ans = max < str1.length ? (str1.length < str2.length ? str2 : str1) : ans;
max = Math.max(max, str1.length, str2.length);
}
}
function palindrome(s, l, r) {
while(l >= 0 && r<s.length && s[l]===s[r]) {
l--;
r++;
}
return s.slice(1+1, r);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
🌙 5.最长公共前缀 (opens new window)
/**
* @param {string[]} strs
* @return {string}
*/
var longestCommonPrefix = function(strs) {
if (strs.length === 0) return "";
let first = strs[0];
if (first === "") return "";
let minLen = Number.MAX_SAFE_INTEGER;
for (let i = 1; i < strs.length; i++) {
const len = twoStrLongestCommonPrefix(first, strs[i]);
minLen = Math.min(len, minLen);
}
return first.slice(0, minLen);
};
function twoStrLongestCommonPrefix (s, t) {
let i = 0, j = 0;
let cnt = 0;
while (i < s.length && j < t.length) {
console.log(s[i], t[j], cnt)
if (s[i] === t[j]) {
cnt++;
} else {
return cnt;
}
i++;
j++;
}
return cnt;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
🌙 6.无重复字符的最长子串 (opens new window)
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
let window = {};
let left = 0, right = 0;
let maxLen = 0, maxStr = '';
while (right < s.length) {
let c = s[right];
right++;
if (window[c]) window[c]++;
else window[c] = 1
while (window[c] > 1) {
let d = s[left];
left++;
window[d]--;
}
if (maxLen < right - left) {
maxLen = right - left;
}
}
return maxLen;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
🌙 7. 最小覆盖子串【滑动窗口】 (opens new window)
/**
* @param {string} s
* @param {string} t
* @return {string}
*/
var minWindow = function(s, t) {
let need = {}, window = {};
// 1.记录需要的字符数
for (let c of t) {
if (!need[c]) need[c] = 1;
else need[c]++;
}
// 双指针
let left = 0, right = 0;
// 记录需要的数量
let valid = 0, len = Object.keys(need).length;
// 记录结果
let minLen = s.length + 1, minStr = '';
// right指针先走
while (right < s.length) {
const d = s[right];
right++;
if (!window[d]) window[d] = 1;
else window[d]++;
// 2.记录达标的数量
if (need[d] && need[d] === window[d]) {
valid++;
}
console.log('left - right', left, right);
// 3.直到刚好满足需求
while (valid === len) {
// 更新结果
if (right - left < minLen) {
minLen = right - left;
minStr = s.slice(left, right);
}
console.lo
// 4.缩小范围
let c = s[left];
left++;
window[c]--;
if (need[c] && window[c] < need[c]) {
valid--;
}
}
}
return minStr;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49