原文地址:我的博客:# LeetCode 经典算法题目整理
1. 两数之和
function twoSum(nums, target) {
if (nums.length <= 1) return [];
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
} else {
map.set(nums[i], i);
}
}
return [];
}
2. 最大子序和
function maxSubArray(nums) {
let prev = 0;
let max = nums[0];
nums.forEach((value) => {
prev = Math.max(prev + value, value);
max = Math.max(prev, max);
});
return max;
}
3. 反转链表
function reverseList(head) {
let prev = null;
let current = head;
while (current) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
4. 比较版本号
function compareVersion(version1, version2) {
const arr1 = version1.split(".");
const arr2 = version2.split(".");
let maxLength = Math.max(arr1.length, arr2.length);
for (let i = 0; i < maxLength; i++) {
const num1 = Number(arr1[i] || 0);
const num2 = Number(arr2[i] || 0);
if (num1 > num2) {
return 1;
}
if (num1 < num2) {
return -1;
}
}
return 0;
}
5. 合并两个有序数组
function mergeSortArray(nums1, m, nums2, n) {
let i = m - 1;
let j = n - 1;
let k = m + n - 1;
while (i >= 0 || j >= 0) {
if (i < 0) {
nums1[k--] = nums2[j--];
} else if (j < 0) {
nums1[k--] = nums1[i--];
} else if (nums1[i] < nums2[j]) {
nums1[k--] = nums2[j--];
} else {
nums1[k--] = nums1[i--];
}
}
}
6. 无重复字符的最长子串
function lengthOfLongestSubstring(s) {
let max = 0;
let startIndex = 0;
let map = new Map();
for (let i = 0; i < s.length; i++) {
if (map.has(s[i])) {
startIndex = Math.max(map.get(s[i]) + 1, startIndex);
}
max = Math.max(i - startIndex + 1, max);
map.set(s[i], i);
}
return max;
}
7. 有效的括号
function validParenthesis(s) {
const map = {
"}": "{",
"]": "[",
")": "(",
};
const stack = [];
for (let i = 0; i < s.length; i++) {
if (stack.length && stack[stack.length - 1] === map[s[i]]) {
stack.pop();
} else {
stack.push(s[i]);
}
}
return !stack.length;
}
8. LRU 缓存机制
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return -1;
}
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.size >= this.capacity) {
this.cache.delete(this.cache.keys().next().value);
}
if (this.cache.has(key)) {
this.cache.delete(key);
}
this.cache.set(key, value);
}
}
const lru = new LRUCache(2);
lru.put(1, 1);
lru.put(2, 2);
console.log(lru.get(1));
lru.put(3, 3);
console.log(lru.get(1));
lru.put(4, 4);
console.log(lru.get(2));
console.log(lru.get(3));
console.log(lru.get(4));
9. 买卖股票的最佳时机
function maxProfit(prices) {
let minPrice = prices[0];
let maxProfit = 0;
for (let i = 1; i < prices.length; i++) {
minPrice = Math.min(prices[i], minPrice);
maxProfit = Math.max(maxProfit, prices[i] - minPrice);
}
return maxProfit;
}
10. 最长回文子串
function longestPalindrome(s) {
let l = 0;
let r = 0;
for (let i = 0; i < s.length; i++) {
helper(i, i);
helper(i, i + 1);
}
function helper(m, n) {
while (m >= 0 && n < s.length && s[m] === s[n]) {
m--;
n++;
}
if (n - m > r - l) {
r = n;
l = m;
}
}
return s.slice(l + 1, r);
}
11. 爬楼梯
1. 正常递归求解,但是时间空间复杂度很差
function climbStairs(n) {
if (n <= 2) return n;
return climbStairs(n - 1) + climbStairs(n - 2);
}
2. 利用数组缓存值
function climbStairs(n) {
let result = [1, 2];
for (let i = 2; i < n; i++) {
result[i] = result[i - 1] + result[i - 2];
}
return result.pop();
}
3. 动态规划求解
function climbStairs(n) {
if (n <= 2) return n;
let a = 1;
let b = 2;
let sum = 0;
for (let i = 2; i < n; i++) {
sum = a + b;
a = b;
b = sum;
}
return sum;
}
12. 三数之和
function threeSum(nums) {
if (!nums || nums.length <= 2) return nums;
nums.sort((a, b) => a - b);
let result = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] > 0) break;
if (i > 0 && nums[i] === nums[i - 1]) continue;
let L = i + 1;
let R = nums.length - 1;
while (L < R) {
const sum = nums[i] + nums[L] + nums[R];
if (sum === 0) {
result.push([nums[i], nums[L], nums[R]]);
while (L < R && nums[R] === nums[R - 1]) {
R--;
}
while (L < R && nums[L] === nums[L + 1]) {
L++;
}
L++;
R--;
} else if (sum > 0) {
R--;
} else {
L++;
}
}
}
return result;
}
13. 环形链表
function hasCycle(head) {
let slow = head;
let fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
14. 二叉树的层序遍历
function levelOrder(root) {
if (!root) return [];
let result = [];
let queue = [root];
while (queue.length) {
let levelNodes = [];
let levelNodeSize = queue.length;
for (let i = 0; i < levelNodeSize; i++) {
const node = queue.shift();
levelNodes.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(levelNodes);
}
return result;
}
15. 路径总和
1. 通过递归
function hasPathSum(root, targetSum) {
if (!root) return false;
if (!root.left && !root.right && root.val === targetSum) return true;
const nextTargetSum = targetSum - root.val;
return (
hasPathSum(root.left, nextTargetSum) ||
hasPathSum(root.right, nextTargetSum)
);
}
2. 通过 BFS
function hasPathSum(root, targetSum) {
if (!root) return false;
let queue = [[root, root.val]];
while (queue.length) {
const [node, currentSum] = queue.shift();
if (!node.left && !node.right && currentSum === targetSum) return true;
if (node.left) queue.push([node.left, node.left.val + currentSum]);
if (node.right) queue.push([node.right, node.right.val + currentSum]);
}
return false;
}
16. 全排列
function permute(nums) {
const result = [];
const visited = new Set();
function dfs(paths) {
if (paths.length === nums.length) {
result.push([...paths]);
return;
}
for (let i = 0; i < nums.length; i++) {
if (visited.has(nums[i])) continue;
visited.add(nums[i]);
paths.push(nums[i]);
dfs(paths);
visited.delete(nums[i]);
paths.pop();
}
}
dfs([]);
return result;
}
17. 搜索二维矩阵 II
function searchMatrix(matrix, target) {
let m = matrix.length;
let n = matrix[0].length;
let i = 0;
let j = n - 1;
while (i < m && j >= 0) {
if (matrix[i][j] === target) {
return true;
}
else if (matrix[i][j] < target) {
i++;
}
else {
j--;
}
}
return false;
}
18. 旋转图像
1. 先水平翻转,在沿对角线翻转
function rotate(matrix) {
const n = matrix.length;
for (let i = 0; i < Math.floor(n / 2); i++) {
for (let j = 0; j < n; j++) {
[matrix[i][j], matrix[n - i - 1][j]] = [
matrix[n - i - 1][j],
matrix[i][j],
];
}
}
for (let i = 0; i < n; i++) {
for (let j = 0; j < i; j++) {
[matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
}
}
return matrix;
}
2. 原地旋转
function rotate(matrix) {
const n = matrix.length;
for (let i = 0; i < Math.floor(n / 2); ++i) {
for (let j = 0; j < Math.floor((n + 1) / 2); ++j) {
const temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
return matrix;
}
19.螺旋矩阵
function spiralOrder(matrix) {
if (matrix.length == 0) return [];
let result = [];
let left = 0;
let bottom = matrix.length - 1;
let top = 0;
let right = matrix[0].length - 1;
while (left <= right && top <= bottom) {
for (let i = left; i <= right; i++) result.push(matrix[top][i]);
top++;
for (let i = top; i <= bottom; i++) result.push(matrix[i][right]);
right--;
if (left > right || top > bottom) break;
for (let i = right; i >= left; i--) result.push(matrix[bottom][i]);
bottom--;
for (let i = bottom; i >= top; i--) result.push(matrix[i][left]);
left++;
}
return result;
}
20. 颜色转化
const HexToRgb = (hex) => {
const hexRegExp = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
if (!hexRegExp.test(hex)) return null;
let hexChar = hex.slice(1);
if (hexChar.length === 3) {
hexChar = hexChar
.split("")
.map((char) => char + char)
.join("");
}
const r = parseInt(hexChar.slice(0, 2), 16);
const g = parseInt(hexChar.slice(2, 4), 16);
const b = parseInt(hexChar.slice(4, 6), 16);
return `rgb(${r},${g},${b})`;
};
console.log(HexToRgb("#fff"));
console.log(HexToRgb("#eeeeee"));
console.log(HexToRgb("#ff5733"));
const rgbToHex = (r, g, b) => {
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
throw Error("Invalid RGB values");
}
const toHex = (num) => {
return num.toString(16).padStart(2, "0");
};
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
};
console.log(rgbToHex(0, 0, 0));
21. 二叉树的右视图
function rightSideView(root) {
let result = [];
const queue = [root];
while (queue.length && root) {
const size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
if (i === size - 1) {
result.push(node.val);
}
}
}
return result;
}
function rightSideView(root) {
const result = [];
function dfs(node, deepPath) {
if (!node) return null;
if (deepPath === result.length) {
result.push(node.val);
}
dfs(node.right, deepPath + 1);
dfs(node.left, deepPath + 1);
}
dfs(root, 0);
22. 二叉树的最近公共祖先
function lowestCommonAncestor(root, p, q) {
if (!root || p === root || q === root) return root;
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left && right) return root;
return left || right;
}
23. 二叉树最大宽度
function widthOfBinaryTree(root) {
if (!root) return 0;
let maxWidth = 0;
let queue = [[root, 0]];
while (queue.length) {
const size = queue.length;
let startIndex = queue[0][1];
let endIndex = startIndex;
for (let i = 0; i < size; i++) {
const [node, index] = queue.shift();
endIndex = index;
if (node.left) queue.push([node.left, 2 * index]);
if (node.right) queue.push([node.right, 2 * index + 1]);
}
const currentWidth = endIndex - startIndex + 1;
maxWidth = Math.max(currentWidth, maxWidth);
}
return maxWidth;
}
24. 翻转二叉树
function invertTree(root) {
if (!root) return null;
const temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
function invertTree(root) {
if (!root) return null;
let queue = [root];
while (queue.length) {
const node = queue.shift();
const temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return root;
}
25. 合并两个有序链表
function mergeTwoLists(list1, list2) {
if (!list1) return list2;
if (!list2) return list1;
if (list1.val < list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
function mergeTwoLists2(list1, list2) {
let link = { val: -1, next: null };
let current = link;
while (list1 && list2) {
if (list1.val < list2.val) {
current.next = list1;
list1 = list1.next;
} else {
current.next = list2;
list2 = list2.next;
}
current = current.next;
}
current.next = list1 ?? list2;
return link.next;
}
26. 合并 K 个升序链表
function mergeKLists(lists) {
if (!lists.length) return null;
const mergeTowList = (list1, list2) => {
if (!list1) return list2;
if (!list2) return list1;
if (list1.val < list2.val) {
list1.next = mergeTowList(list1.next, list2);
return list1;
} else {
list2.next = mergeTowList(list1, list2.next);
return list2;
}
};
const merge = (lists, left, right) => {
if (left === right) return lists[left];
const mid = Math.floor((left + right) / 2);
const l1 = merge(lists, left, mid);
const l2 = merge(lists, mid + 1, right);
return mergeTowList(l1, l2);
};
return merge(lists, 0, lists.length - 1);
}
26. 和为 K 的子数组
function subarraySum(nums, k) {
let count = 0;
let prefixSums = new Map();
let prefixSum = 0;
prefixSums.set(0, 1);
for (const num of nums) {
prefixSum += num;
if (prefixSums.has(prefixSum - k)) {
count += prefixSums.get(prefixSum - k);
}
prefixSums.set(prefixSum, (prefixSums.get(prefixSum) || 0) + 1);
}
return count;
}
27. 缺失的第一个正数
var firstMissingPositive = function (nums) {
if (nums.indexOf(1) === -1) return 1;
for (let i = 0; i < nums.length; i++) {
if (nums[i] <= 0 || nums[i] > nums.length) {
nums[i] = 1;
}
}
for (let i = 0; i < nums.length; i++) {
const index = Math.abs(nums[i]) - 1;
nums[index] = -Math.abs(nums[index]);
}
for (let i = 0; i < nums.length; i++) {
if (nums[i] > 0) {
return i + 1;
}
}
return nums.length + 1;
};
function firstMissingPositive(nums) {
const n = nums.length;
if (!nums.includes(1)) return 1;
for (let i = 0; i < nums.length; i++) {
while (nums[i] > 0 && nums[i] <= n && nums[i] !== nums[nums[i] - 1]) {
const temp = nums[i];
nums[i] = nums[temp - 1];
nums[temp - 1] = temp;
}
}
for (let i = 0; i < nums.length; i++) {
if (nums[i] !== i + 1) {
return i + 1;
}
}
return nums.length + 1;
}
28. 删除有序数组中的重复项
function removeDuplicates(nums) {
let slow = 0;
let fast = 1;
while (fast <= nums.length - 1) {
if (nums[slow] !== nums[fast]) {
nums[slow + 1] = nums[fast];
slow++;
}
fast++;
}
return nums.slice(0, slow + 1);
}
29. Z 字形变换
function convert(s, numRows) {
if (numRows <= 1) return s;
let row = 0;
let down = true;
let result = new Array(numRows).fill("");
for (let i = 0; i < s.length; i++) {
result[row] += s[i];
if (down) {
row++;
} else {
row--;
}
if (row === 0) {
down = true;
}
if (row === numRows - 1) {
down = false;
}
}
return result.join("");
}
30. 最长公共前缀
function longestCommonPrefix(strs) {
if (!strs.length || !strs) return "";
let prefix = strs[0];
for (let i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) !== 0) {
prefix = prefix.slice(0, -1);
if (!prefix) return "";
}
}
return prefix;
}
31. 删除字符串中的所有相邻重复项
function removeDuplicates(s) {
let stack = [];
for (let i = 0; i < s.length; i++) {
if (s[i] === stack[stack.length - 1]) {
stack.pop();
} else {
stack.push(s[i]);
}
}
return stack.join("");
}
console.log(removeDuplicates("abbaca"));
32. 删除字符串中的所有相邻重复项 II
function removeDuplicates(s, k) {
const stack = [];
const countStack = [];
let i = 0;
while (i < s.length) {
if (stack[stack.length - 1] === s[i]) {
stack.push(s[i]);
countStack[countStack.length - 1] = countStack[countStack.length - 1] + 1;
if (countStack[countStack.length - 1] === k) {
for (let j = 0; j < k; j++) {
stack.pop();
}
countStack.pop();
}
} else {
stack.push(s[i]);
countStack.push(1);
}
i++;
}
return stack.join("");
}
33. 盛最多水的容器
function maxArea(height) {
let left = 0;
let right = height.length - 1;
let currentMaxArea = 0;
while (left < right) {
currentMaxArea = Math.max(
currentMaxArea,
(right - left) * Math.min(height[left], height[right])
);
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return currentMaxArea;
}
34. 最大子序和
function maxSubArray(nums) {
let maxSum = nums[0];
let currentSum = nums[0];
for (let i = 1; i < nums.length; i++) {
currentSum = Math.max(nums[i], currentSum + nums[i]);
maxSum = Math.max(maxSum, currentSum);
}
return maxSum;
}
35. 接雨水
function trap(height) {
if (!height || height.length < 3) return 0;
let left = 0;
let right = height.length - 1;
let leftMax = height[left];
let rightMax = height[right];
let waterTrapped = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
waterTrapped += leftMax - height[left];
}
left++;
} else {
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
waterTrapped += rightMax - height[right];
}
right--;
}
}
return waterTrapped;
}
36. N 皇后
function solveNQueens(n) {
const result = [];
const board = Array.from({ length: n }, () => Array(n).fill("."));
const cols = new Set();
const diag1 = new Set();
const diag2 = new Set();
function backtrack(row) {
if (row === n) {
result.push(board.map((row) => row.join("")));
return;
}
for (let col = 0; col < n; col++) {
if (cols.has(col) || diag1.has(row - col) || diag2.has(row + col)) {
continue;
}
board[row][col] = "Q";
cols.add(col);
diag1.add(row - col);
diag2.add(row + col);
backtrack(row + 1);
board[row][col] = ".";
cols.delete(col);
diag1.delete(row - col);
diag2.delete(row + col);
}
}
backtrack(0);
return result;
}
37. 二叉树的最大深度
function maxDepth(root) {
if (!root) return 0;
const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
38. 最长连续序列
function longestConsecutive(nums) {
if (nums.length === 0) return 0;
const numSet = new Set(nums);
let longest = 0;
for (let num of numSet) {
if (!numSet.has(num - 1)) {
let currentNum = num;
let currentLength = 1;
while (numSet.has(currentNum + 1)) {
currentNum++;
currentLength++;
}
longest = Math.max(longest, currentLength);
}
}
return longest;
}
39. 只出现一次的数字
function singleNumber(nums) {
let result = 0;
for (let num of nums) {
result ^= num;
}
return result;
}
40. 复原 IP 地址
function restoreIpAddresses(s) {
const result = [];
const isValid = (str) => {
if (str.length > 1 && str[0] === "0") return false;
const num = Number(str);
return num >= 0 && num <= 255;
};
const backtrack = (start, path) => {
if (path.length === 4 && start === s.length) {
result.push(path.join("."));
return;
}
if (path.length === 4) return;
for (let len = 1; len <= 3; len++) {
const segment = s.substring(start, start + len);
if (start + len > s.length) break;
if (isValid(segment)) {
path.push(segment);
backtrack(start + len, path);
path.pop();
}
}
};
if (s.length < 4 || s.length > 12) return result;
backtrack(0, []);
return result;
}
41. 二叉树的直径
function diameterOfBinaryTree(root) {
let maxDiameter = 0;
function depth(node) {
if (!node) return 0;
const leftDepth = depth(node.left);
const rightDepth = depth(node.right);
maxDiameter = Math.max(maxDiameter, leftDepth + rightDepth);
return Math.max(leftDepth, rightDepth) + 1;
}
depth(root);
return maxDiameter;
}