JavaScript 算法与数据结构
前端工程师进阶之路:掌握这些算法,让你的代码更高效
目录
- 时间空间复杂度
- 数组与字符串
- 链表
- 栈与队列
- 树与二叉树
- 图
- 哈希表
- 排序算法
- 搜索算法
- 动态规划
- 回溯与递归
- 前端实战场景
- [LeetCode 经典题目](#LeetCode 经典题目)
- 算法思维总结
- 附录:常用代码模板
一、复杂度分析
1.1 时间复杂度
javascript
// O(1) - 常数时间
function getFirst(arr) {
return arr[0];
}
// O(log n) - 对数时间
function binarySearch(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
// O(n) - 线性时间
function findMax(arr) {
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
}
return max;
}
// O(n log n) - 线性对数时间(归并排序)
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) result.push(left[i++]);
else result.push(right[j++]);
}
return [...result, ...left.slice(i), ...right.slice(j)];
}
// O(n^2) - 平方时间(冒泡排序)
function bubbleSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
// O(2^n) - 指数时间(斐波那契递归)
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
1.2 空间复杂度
javascript
// O(1) 空间 - 原地操作
function reverseInPlace(arr) {
let left = 0, right = arr.length - 1;
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
return arr;
}
// O(n) 空间 - 需要额外数组
function copyArray(arr) {
return [...arr];
}
// O(n) 空间 - 递归调用栈
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
1.3 复杂度对比表
| 复杂度 | 名称 | 示例 |
|---|---|---|
| O(1) | 常数 | 数组取值、哈希表查找 |
| O(log n) | 对数 | 二分查找 |
| O(n) | 线性 | 遍历数组 |
| O(n log n) | 线性对数 | 归并排序、快速排序 |
| O(n^2) | 平方 | 冒泡排序、嵌套循环 |
| O(2^n) | 指数 | 子集生成、递归斐波那契 |
| O(n!) | 阶乘 | 全排列 |
二、数组与字符串
2.1 双指针技巧
javascript
// 对撞指针 - 两数之和 II(有序数组)
function twoSumSorted(numbers, target) {
let left = 0, right = numbers.length - 1;
while (left < right) {
const sum = numbers[left] + numbers[right];
if (sum === target) return [left + 1, right + 1];
if (sum < target) left++;
else right--;
}
return [];
}
// 快慢指针 - 删除有序数组中的重复项
function removeDuplicates(nums) {
if (nums.length === 0) return 0;
let slow = 1;
for (let fast = 1; fast < nums.length; fast++) {
if (nums[fast] !== nums[fast - 1]) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
// 滑动窗口 - 最长子串
function lengthOfLongestSubstring(s) {
const set = new Set();
let left = 0, maxLen = 0;
for (let right = 0; right < s.length; right++) {
while (set.has(s[right])) {
set.delete(s[left]);
left++;
}
set.add(s[right]);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
2.2 前缀和与差分数组
javascript
// 前缀和 - 区间求和
class PrefixSum {
constructor(arr) {
this.prefix = [0];
for (let i = 0; i < arr.length; i++) {
this.prefix[i + 1] = this.prefix[i] + arr[i];
}
}
rangeSum(left, right) {
return this.prefix[right + 1] - this.prefix[left];
}
}
// 差分数组 - 区间修改
class DiffArray {
constructor(arr) {
this.diff = new Array(arr.length).fill(0);
this.diff[0] = arr[0];
for (let i = 1; i < arr.length; i++) {
this.diff[i] = arr[i] - arr[i - 1];
}
}
increment(left, right, val) {
this.diff[left] += val;
if (right + 1 < this.diff.length) {
this.diff[right + 1] -= val;
}
}
result() {
const res = [this.diff[0]];
for (let i = 1; i < this.diff.length; i++) {
res[i] = res[i - 1] + this.diff[i];
}
return res;
}
}
2.3 字符串经典算法
javascript
// KMP 字符串匹配
function kmpSearch(text, pattern) {
if (!pattern.length) return 0;
const lps = buildLPS(pattern);
let i = 0, j = 0;
while (i < text.length) {
if (text[i] === pattern[j]) {
i++;
j++;
if (j === pattern.length) return i - j;
} else {
if (j > 0) j = lps[j - 1];
else i++;
}
}
return -1;
}
function buildLPS(pattern) {
const lps = new Array(pattern.length).fill(0);
let len = 0, i = 1;
while (i < pattern.length) {
if (pattern[i] === pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len > 0) len = lps[len - 1];
else { lps[i] = 0; i++; }
}
}
return lps;
}
// Manacher 最长回文子串
function longestPalindrome(s) {
const t = '#' + s.split('').join('#') + '#';
const n = t.length;
const p = new Array(n).fill(0);
let center = 0, rightBound = 0;
let maxCenter = 0, maxLen = 0;
for (let i = 0; i < n; i++) {
if (i < rightBound) {
p[i] = Math.min(rightBound - i, p[2 * center - i]);
}
while (i - p[i] - 1 >= 0 && i + p[i] + 1 < n && t[i - p[i] - 1] === t[i + p[i] + 1]) {
p[i]++;
}
if (i + p[i] > rightBound) {
center = i;
rightBound = i + p[i];
}
if (p[i] > maxLen) {
maxLen = p[i];
maxCenter = i;
}
}
const start = (maxCenter - maxLen) / 2;
return s.substring(start, start + maxLen);
}
三、链表
3.1 链表实现
javascript
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
this.size = 0;
}
append(val) {
const node = new ListNode(val);
if (!this.head) {
this.head = node;
} else {
let curr = this.head;
while (curr.next) curr = curr.next;
curr.next = node;
}
this.size++;
}
prepend(val) {
this.head = new ListNode(val, this.head);
this.size++;
}
delete(val) {
if (!this.head) return;
if (this.head.val === val) {
this.head = this.head.next;
this.size--;
return;
}
let curr = this.head;
while (curr.next) {
if (curr.next.val === val) {
curr.next = curr.next.next;
this.size--;
return;
}
curr = curr.next;
}
}
toArray() {
const result = [];
let curr = this.head;
while (curr) {
result.push(curr.val);
curr = curr.next;
}
return result;
}
}
3.2 链表经典操作
javascript
// 反转链表
function reverseList(head) {
let prev = null, curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
// 检测环(快慢指针)
function hasCycle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
// 找环的入口
function detectCycle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
slow = head;
while (slow !== fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
return null;
}
// 合并两个有序链表
function mergeTwoLists(l1, l2) {
const dummy = new ListNode();
let curr = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
curr.next = l1 || l2;
return dummy.next;
}
// 合并 K 个有序链表
function mergeKLists(lists) {
if (!lists.length) return null;
while (lists.length > 1) {
const merged = [];
for (let i = 0; i < lists.length; i += 2) {
const l1 = lists[i];
const l2 = i + 1 < lists.length ? lists[i + 1] : null;
merged.push(mergeTwoLists(l1, l2));
}
lists = merged;
}
return lists[0];
}
// 链表中点
function findMiddle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
// 判断回文链表
function isPalindrome(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
let prev = null, curr = slow;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
let left = head, right = prev;
while (right) {
if (left.val !== right.val) return false;
left = left.next;
right = right.next;
}
return true;
}
四、栈与队列
4.1 栈的实现
javascript
class Stack {
constructor() {
this.items = [];
}
push(val) { this.items.push(val); }
pop() { return this.items.pop(); }
peek() { return this.items[this.items.length - 1]; }
isEmpty() { return this.items.length === 0; }
size() { return this.items.length; }
}
4.2 队列与双端队列
javascript
class Queue {
constructor() { this.items = []; }
enqueue(val) { this.items.push(val); }
dequeue() { return this.items.shift(); }
front() { return this.items[0]; }
isEmpty() { return this.items.length === 0; }
}
class Deque {
constructor() { this.items = []; }
pushFront(val) { this.items.unshift(val); }
pushBack(val) { this.items.push(val); }
popFront() { return this.items.shift(); }
popBack() { return this.items.pop(); }
front() { return this.items[0]; }
back() { return this.items[this.items.length - 1]; }
}
4.3 栈的经典应用
javascript
// 有效的括号
function isValidParentheses(s) {
const stack = [];
const map = { ')': '(', ']': '[', '}': '{' };
for (const char of s) {
if (char === '(' || char === '[' || char === '{') {
stack.push(char);
} else {
if (stack.pop() !== map[char]) return false;
}
}
return stack.length === 0;
}
// 每日温度(单调栈)
function dailyTemperatures(temperatures) {
const n = temperatures.length;
const result = new Array(n).fill(0);
const stack = [];
for (let i = 0; i < n; i++) {
while (stack.length && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const idx = stack.pop();
result[idx] = i - idx;
}
stack.push(i);
}
return result;
}
// 柱状图最大矩形(单调栈)
function largestRectangleArea(heights) {
const stack = [];
let maxArea = 0;
const arr = [...heights, 0];
for (let i = 0; i < arr.length; i++) {
while (stack.length && arr[i] < arr[stack[stack.length - 1]]) {
const h = arr[stack.pop()];
const w = stack.length ? i - stack[stack.length - 1] - 1 : i;
maxArea = Math.max(maxArea, h * w);
}
stack.push(i);
}
return maxArea;
}
// 最小栈
class MinStack {
constructor() {
this.stack = [];
this.minStack = [];
}
push(val) {
this.stack.push(val);
this.minStack.push(
this.minStack.length ? Math.min(val, this.minStack[this.minStack.length - 1]) : val
);
}
pop() { this.stack.pop(); this.minStack.pop(); }
top() { return this.stack[this.stack.length - 1]; }
getMin() { return this.minStack[this.minStack.length - 1]; }
}
// 表达式求值
function evalRPN(tokens) {
const stack = [];
for (const token of tokens) {
if (['+', '-', '*', '/'].includes(token)) {
const b = stack.pop();
const a = stack.pop();
switch (token) {
case '+': stack.push(a + b); break;
case '-': stack.push(a - b); break;
case '*': stack.push(a * b); break;
case '/': stack.push(Math.trunc(a / b)); break;
}
} else {
stack.push(Number(token));
}
}
return stack[0];
}
4.4 用栈实现队列
javascript
class QueueByStacks {
constructor() {
this.inStack = [];
this.outStack = [];
}
push(val) { this.inStack.push(val); }
pop() {
this._transfer();
return this.outStack.pop();
}
peek() {
this._transfer();
return this.outStack[this.outStack.length - 1];
}
_transfer() {
if (!this.outStack.length) {
while (this.inStack.length) {
this.outStack.push(this.inStack.pop());
}
}
}
}
五、树与二叉树
5.1 二叉树基础
javascript
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function createTree(arr) {
if (!arr.length) return null;
const root = new TreeNode(arr[0]);
const queue = [root];
let i = 1;
while (queue.length && i < arr.length) {
const node = queue.shift();
if (i < arr.length && arr[i] !== null) {
node.left = new TreeNode(arr[i]);
queue.push(node.left);
}
i++;
if (i < arr.length && arr[i] !== null) {
node.right = new TreeNode(arr[i]);
queue.push(node.right);
}
i++;
}
return root;
}
5.2 遍历算法
javascript
// 前序遍历(递归)
function preorderTraversal(root) {
if (!root) return [];
return [root.val, ...preorderTraversal(root.left), ...preorderTraversal(root.right)];
}
// 前序遍历(迭代)
function preorderIterative(root) {
const result = [];
const stack = [root];
while (stack.length) {
const node = stack.pop();
if (!node) continue;
result.push(node.val);
stack.push(node.right);
stack.push(node.left);
}
return result;
}
// 中序遍历(递归)
function inorderTraversal(root) {
if (!root) return [];
return [...inorderTraversal(root.left), root.val, ...inorderTraversal(root.right)];
}
// 中序遍历(迭代)
function inorderIterative(root) {
const result = [];
const stack = [];
let curr = root;
while (curr || stack.length) {
while (curr) { stack.push(curr); curr = curr.left; }
curr = stack.pop();
result.push(curr.val);
curr = curr.right;
}
return result;
}
// 后序遍历(迭代)
function postorderIterative(root) {
const result = [];
const stack = [root];
while (stack.length) {
const node = stack.pop();
if (!node) continue;
result.push(node.val);
stack.push(node.left);
stack.push(node.right);
}
return result.reverse();
}
// 层序遍历
function levelOrder(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length) {
const level = [];
const size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
level.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(level);
}
return result;
}
5.3 二叉搜索树
javascript
class BST {
constructor() { this.root = null; }
insert(val) { this.root = this._insert(this.root, val); }
_insert(node, val) {
if (!node) return new TreeNode(val);
if (val < node.val) node.left = this._insert(node.left, val);
else if (val > node.val) node.right = this._insert(node.right, val);
return node;
}
search(val) {
let curr = this.root;
while (curr) {
if (val === curr.val) return true;
curr = val < curr.val ? curr.left : curr.right;
}
return false;
}
delete(val) { this.root = this._delete(this.root, val); }
_delete(node, val) {
if (!node) return null;
if (val < node.val) node.left = this._delete(node.left, val);
else if (val > node.val) node.right = this._delete(node.right, val);
else {
if (!node.left) return node.right;
if (!node.right) return node.left;
let minNode = node.right;
while (minNode.left) minNode = minNode.left;
node.val = minNode.val;
node.right = this._delete(node.right, minNode.val);
}
return node;
}
}
5.4 树的常用操作
javascript
function maxDepth(root) {
if (!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
function invertTree(root) {
if (!root) return null;
[root.left, root.right] = [root.right, root.left];
invertTree(root.left);
invertTree(root.right);
return root;
}
function isSymmetric(root) {
if (!root) return true;
function isMirror(left, right) {
if (!left && !right) return true;
if (!left || !right) return false;
return left.val === right.val
&& isMirror(left.left, right.right)
&& isMirror(left.right, right.left);
}
return isMirror(root.left, root.right);
}
function lowestCommonAncestor(root, p, q) {
if (!root || root === p || root === q) return root;
const left = lowestCommonAncestor(root.left, p, q);
const right = lowestCommonAncestor(root.right, p, q);
if (left && right) return root;
return left || right;
}
function hasPathSum(root, targetSum) {
if (!root) return false;
if (!root.left && !root.right) return root.val === targetSum;
return hasPathSum(root.left, targetSum - root.val)
|| hasPathSum(root.right, targetSum - root.val);
}
function rightSideView(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length) {
const size = queue.length;
for (let i = 0; i < size; i++) {
const node = queue.shift();
if (i === size - 1) result.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return result;
}
六、图
6.1 图的实现
javascript
class Graph {
constructor() { this.adjacencyList = new Map(); }
addVertex(vertex) {
if (!this.adjacencyList.has(vertex)) {
this.adjacencyList.set(vertex, []);
}
}
addEdge(v1, v2, weight = 1) {
this.adjacencyList.get(v1).push({ node: v2, weight });
this.adjacencyList.get(v2).push({ node: v1, weight });
}
addDirectedEdge(v1, v2, weight = 1) {
this.adjacencyList.get(v1).push({ node: v2, weight });
}
}
6.2 图的遍历
javascript
function bfs(graph, start) {
const visited = new Set([start]);
const result = [];
const queue = [start];
while (queue.length) {
const vertex = queue.shift();
result.push(vertex);
for (const { node } of graph.adjacencyList.get(vertex) || []) {
if (!visited.has(node)) {
visited.add(node);
queue.push(node);
}
}
}
return result;
}
function dfs(graph, start) {
const visited = new Set();
const result = [];
function dfsHelper(vertex) {
visited.add(vertex);
result.push(vertex);
for (const { node } of graph.adjacencyList.get(vertex) || []) {
if (!visited.has(node)) dfsHelper(node);
}
}
dfsHelper(start);
return result;
}
6.3 最短路径
javascript
function dijkstra(graph, start) {
const distances = new Map();
const visited = new Set();
const pq = [];
for (const vertex of graph.adjacencyList.keys()) {
distances.set(vertex, Infinity);
}
distances.set(start, 0);
pq.push({ node: start, dist: 0 });
while (pq.length) {
pq.sort((a, b) => a.dist - b.dist);
const { node: current } = pq.shift();
if (visited.has(current)) continue;
visited.add(current);
for (const { node, weight } of graph.adjacencyList.get(current) || []) {
const newDist = distances.get(current) + weight;
if (newDist < distances.get(node)) {
distances.set(node, newDist);
pq.push({ node, dist: newDist });
}
}
}
return distances;
}
6.4 拓扑排序
javascript
function topologicalSort(graph) {
const inDegree = new Map();
const result = [];
for (const vertex of graph.adjacencyList.keys()) {
inDegree.set(vertex, 0);
}
for (const [, neighbors] of graph.adjacencyList) {
for (const { node } of neighbors) {
inDegree.set(node, (inDegree.get(node) || 0) + 1);
}
}
const queue = [];
for (const [vertex, degree] of inDegree) {
if (degree === 0) queue.push(vertex);
}
while (queue.length) {
const vertex = queue.shift();
result.push(vertex);
for (const { node } of graph.adjacencyList.get(vertex) || []) {
inDegree.set(node, inDegree.get(node) - 1);
if (inDegree.get(node) === 0) queue.push(node);
}
}
return result.length === graph.adjacencyList.size ? result : null;
}
6.5 并查集
javascript
class UnionFind {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.rank = new Array(n).fill(0);
this.count = n;
}
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]);
}
return this.parent[x];
}
union(x, y) {
const rootX = this.find(x);
const rootY = this.find(y);
if (rootX === rootY) return;
if (this.rank[rootX] < this.rank[rootY]) {
this.parent[rootX] = rootY;
} else if (this.rank[rootX] > this.rank[rootY]) {
this.parent[rootY] = rootX;
} else {
this.parent[rootY] = rootX;
this.rank[rootX]++;
}
this.count--;
}
connected(x, y) { return this.find(x) === this.find(y); }
}
七、哈希表
7.1 哈希表实现
javascript
class HashTable {
constructor(size = 53) { this.keyMap = new Array(size); }
_hash(key) {
let total = 0;
const PRIME = 31;
for (let i = 0; i < Math.min(key.length, 100); i++) {
total = (total * PRIME + (key.charCodeAt(i) - 96)) % this.keyMap.length;
}
return total;
}
set(key, value) {
const index = this._hash(key);
if (!this.keyMap[index]) this.keyMap[index] = [];
for (const pair of this.keyMap[index]) {
if (pair[0] === key) { pair[1] = value; return; }
}
this.keyMap[index].push([key, value]);
}
get(key) {
const bucket = this.keyMap[this._hash(key)];
if (bucket) {
for (const [k, v] of bucket) { if (k === key) return v; }
}
return undefined;
}
}
7.2 哈希表经典应用
javascript
function twoSum(nums, target) {
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];
map.set(nums[i], i);
}
return [];
}
function groupAnagrams(strs) {
const map = new Map();
for (const str of strs) {
const sorted = str.split('').sort().join('');
if (!map.has(sorted)) map.set(sorted, []);
map.get(sorted).push(str);
}
return [...map.values()];
}
function longestConsecutive(nums) {
const set = new Set(nums);
let maxLen = 0;
for (const num of set) {
if (!set.has(num - 1)) {
let curr = num, len = 1;
while (set.has(curr + 1)) { curr++; len++; }
maxLen = Math.max(maxLen, len);
}
}
return maxLen;
}
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.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
}
}
八、排序算法
8.1 冒泡排序
javascript
function bubbleSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
let swapped = false;
for (let j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
swapped = true;
}
}
if (!swapped) break;
}
return arr;
}
8.2 选择排序
javascript
function selectionSort(arr) {
const n = arr.length;
for (let i = 0; i < n - 1; i++) {
let minIdx = i;
for (let j = i + 1; j < n; j++) {
if (arr[j] < arr[minIdx]) minIdx = j;
}
if (minIdx !== i) [arr[i], arr[minIdx]] = [arr[minIdx], arr[i]];
}
return arr;
}
8.3 插入排序
javascript
function insertionSort(arr) {
for (let i = 1; i < arr.length; i++) {
const key = arr[i];
let j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
return arr;
}
8.4 归并排序
javascript
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) result.push(left[i++]);
else result.push(right[j++]);
}
return [...result, ...left.slice(i), ...right.slice(j)];
}
8.5 快速排序
javascript
function quickSort(arr, left = 0, right = arr.length - 1) {
if (left < right) {
const pivotIndex = partition(arr, left, right);
quickSort(arr, left, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, right);
}
return arr;
}
function partition(arr, left, right) {
const pivot = arr[right];
let i = left - 1;
for (let j = left; j < right; j++) {
if (arr[j] < pivot) {
i++;
[arr[i], arr[j]] = [arr[j], arr[i]];
}
}
[arr[i + 1], arr[right]] = [arr[right], arr[i + 1]];
return i + 1;
}
8.6 堆排序
javascript
function heapSort(arr) {
const n = arr.length;
for (let i = Math.floor(n / 2) - 1; i >= 0; i--) heapify(arr, n, i);
for (let i = n - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
heapify(arr, i, 0);
}
return arr;
}
function heapify(arr, n, i) {
let largest = i;
const left = 2 * i + 1;
const right = 2 * i + 2;
if (left < n && arr[left] > arr[largest]) largest = left;
if (right < n && arr[right] > arr[largest]) largest = right;
if (largest !== i) {
[arr[i], arr[largest]] = [arr[largest], arr[i]];
heapify(arr, n, largest);
}
}
8.7 排序算法对比
| 算法 | 时间复杂度(平均) | 空间复杂度 | 稳定性 |
|---|---|---|---|
| 冒泡排序 | O(n^2) | O(1) | 稳定 |
| 选择排序 | O(n^2) | O(1) | 不稳定 |
| 插入排序 | O(n^2) | O(1) | 稳定 |
| 归并排序 | O(n log n) | O(n) | 稳定 |
| 快速排序 | O(n log n) | O(log n) | 不稳定 |
| 堆排序 | O(n log n) | O(1) | 不稳定 |
九、搜索算法
9.1 二分查找
javascript
function binarySearch(arr, target) {
let left = 0, right = arr.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
// 搜索旋转排序数组
function searchRotated(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) return mid;
if (nums[left] <= nums[mid]) {
if (target >= nums[left] && target < nums[mid]) right = mid - 1;
else left = mid + 1;
} else {
if (target > nums[mid] && target <= nums[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
9.2 DFS 与 BFS
javascript
// 岛屿数量
function numIslands(grid) {
if (!grid.length) return 0;
let count = 0;
const rows = grid.length, cols = grid[0].length;
function dfs(r, c) {
if (r < 0 || r >= rows || c < 0 || c >= cols || grid[r][c] !== '1') return;
grid[r][c] = '0';
dfs(r + 1, c); dfs(r - 1, c); dfs(r, c + 1); dfs(r, c - 1);
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === '1') { count++; dfs(r, c); }
}
}
return count;
}
// BFS 最短路径(单词接龙)
function ladderLength(beginWord, endWord, wordList) {
const wordSet = new Set(wordList);
if (!wordSet.has(endWord)) return 0;
const queue = [[beginWord, 1]];
const visited = new Set([beginWord]);
while (queue.length) {
const [word, level] = queue.shift();
for (let i = 0; i < word.length; i++) {
for (let c = 97; c <= 122; c++) {
const newWord = word.slice(0, i) + String.fromCharCode(c) + word.slice(i + 1);
if (newWord === endWord) return level + 1;
if (wordSet.has(newWord) && !visited.has(newWord)) {
visited.add(newWord);
queue.push([newWord, level + 1]);
}
}
}
}
return 0;
}
十、动态规划
10.1 斐波那契与爬楼梯
javascript
function climbStairs(n) {
if (n <= 2) return n;
let a = 1, b = 2;
for (let i = 3; i <= n; i++) [a, b] = [b, a + b];
return b;
}
function rob(nums) {
if (!nums.length) return 0;
if (nums.length === 1) return nums[0];
let prev2 = 0, prev1 = 0;
for (const num of nums) {
[prev2, prev1] = [prev1, Math.max(prev1, prev2 + num)];
}
return prev1;
}
function rob2(nums) {
if (nums.length === 1) return nums[0];
return Math.max(rob(nums.slice(0, -1)), rob(nums.slice(1)));
}
10.2 背包问题
javascript
function knapsack01(weights, values, capacity) {
const n = weights.length;
const dp = new Array(capacity + 1).fill(0);
for (let i = 0; i < n; i++) {
for (let w = capacity; w >= weights[i]; w--) {
dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
}
}
return dp[capacity];
}
function coinChange(coins, amount) {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (const coin of coins) {
for (let i = coin; i <= amount; i++) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
}
10.3 子序列问题
javascript
function lengthOfLIS(nums) {
const dp = new Array(nums.length).fill(1);
for (let i = 1; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
return Math.max(...dp);
}
function longestCommonSubsequence(text1, text2) {
const m = text1.length, n = text2.length;
const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; 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[m][n];
}
function maxSubArray(nums) {
let maxSum = nums[0], 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;
}
十一、回溯与递归
11.1 全排列与组合
javascript
function permute(nums) {
const result = [];
function backtrack(path, used) {
if (path.length === nums.length) {
result.push([...path]);
return;
}
for (let i = 0; i < nums.length; i++) {
if (used.has(i)) continue;
used.add(i);
path.push(nums[i]);
backtrack(path, used);
path.pop();
used.delete(i);
}
}
backtrack([], new Set());
return result;
}
function subsets(nums) {
const result = [];
function backtrack(start, path) {
result.push([...path]);
for (let i = start; i < nums.length; i++) {
path.push(nums[i]);
backtrack(i + 1, path);
path.pop();
}
}
backtrack(0, []);
return result;
}
11.2 经典回溯问题
javascript
function solveNQueens(n) {
const result = [];
const board = Array.from({ length: n }, () => new Array(n).fill('.'));
function isValid(row, col) {
for (let i = 0; i < row; i++) {
if (board[i][col] === 'Q') return false;
if (col - (row - i) >= 0 && board[i][col - (row - i)] === 'Q') return false;
if (col + (row - i) < n && board[i][col + (row - i)] === 'Q') return false;
}
return true;
}
function backtrack(row) {
if (row === n) { result.push(board.map(r => r.join(''))); return; }
for (let col = 0; col < n; col++) {
if (!isValid(row, col)) continue;
board[row][col] = 'Q';
backtrack(row + 1);
board[row][col] = '.';
}
}
backtrack(0);
return result;
}
function generateParenthesis(n) {
const result = [];
function backtrack(str, left, right) {
if (str.length === 2 * n) { result.push(str); return; }
if (left < n) backtrack(str + '(', left + 1, right);
if (right < left) backtrack(str + ')', left, right + 1);
}
backtrack('', 0, 0);
return result;
}
function exist(board, word) {
const rows = board.length, cols = board[0].length;
function dfs(r, c, index) {
if (index === word.length) return true;
if (r < 0 || r >= rows || c < 0 || c >= cols || board[r][c] !== word[index]) return false;
const temp = board[r][c];
board[r][c] = '#';
const found = dfs(r + 1, c, index + 1) || dfs(r - 1, c, index + 1)
|| dfs(r, c + 1, index + 1) || dfs(r, c - 1, index + 1);
board[r][c] = temp;
return found;
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (dfs(r, c, 0)) return true;
}
}
return false;
}
十二、前端实战场景
12.1 防抖与节流
javascript
function debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
function throttle(fn, delay) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn.apply(this, args);
}
};
}
12.2 深拷贝
javascript
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
const clone = Array.isArray(obj) ? [] : {};
hash.set(obj, clone);
for (const key of Reflect.ownKeys(obj)) {
clone[key] = deepClone(obj[key], hash);
}
return clone;
}
12.3 发布订阅模式
javascript
class EventBus {
constructor() { this.events = new Map(); }
on(event, callback) {
if (!this.events.has(event)) this.events.set(event, []);
this.events.get(event).push(callback);
}
off(event, callback) {
if (!this.events.has(event)) return;
this.events.set(event, this.events.get(event).filter(cb => cb !== callback));
}
emit(event, ...args) {
if (!this.events.has(event)) return;
for (const cb of this.events.get(event)) cb(...args);
}
once(event, callback) {
const wrapper = (...args) => { callback(...args); this.off(event, wrapper); };
this.on(event, wrapper);
}
}
12.4 Promise 并发控制
javascript
async function asyncPool(limit, tasks) {
const results = [];
const executing = new Set();
for (const task of tasks) {
const p = Promise.resolve().then(() => task());
results.push(p);
executing.add(p);
p.finally(() => executing.delete(p));
if (executing.size >= limit) await Promise.race(executing);
}
return Promise.all(results);
}
12.5 数组扁平化与去重
javascript
function flatten(arr, depth = Infinity) {
return arr.reduce((acc, val) =>
Array.isArray(val) && depth > 0
? acc.concat(flatten(val, depth - 1))
: acc.concat(val)
, []);
}
function uniqueBy(arr, key) {
const seen = new Set();
return arr.filter(item => {
const value = typeof key === 'function' ? key(item) : item[key];
if (seen.has(value)) return false;
seen.add(value);
return true;
});
}
12.6 数据结构转换
javascript
function arrayToTree(arr) {
if (!arr.length) return null;
const root = { val: arr[0], left: null, right: null };
const queue = [root];
let i = 1;
while (queue.length && i < arr.length) {
const node = queue.shift();
if (i < arr.length && arr[i] !== null) {
node.left = { val: arr[i], left: null, right: null };
queue.push(node.left);
}
i++;
if (i < arr.length && arr[i] !== null) {
node.right = { val: arr[i], left: null, right: null };
queue.push(node.right);
}
i++;
}
return root;
}
function buildCommentTree(comments) {
const map = new Map();
const result = [];
comments.forEach(c => map.set(c.id, { ...c, children: [] }));
comments.forEach(c => {
const node = map.get(c.id);
if (c.parentId) {
const parent = map.get(c.parentId);
if (parent) parent.children.push(node);
} else {
result.push(node);
}
});
return result;
}
12.7 函数组合与柯里化
javascript
function pipe(...fns) {
return initial => fns.reduce((acc, fn) => fn(acc), initial);
}
function compose(...fns) {
return initial => fns.reduceRight((acc, fn) => fn(acc), initial);
}
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) return fn.apply(this, args);
return (...args2) => curried.apply(this, args.concat(args2));
};
}
十三、LeetCode 经典题目
13.1 简单难度
javascript
function twoSum(nums, target) {
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];
map.set(nums[i], i);
}
return [];
}
function isPalindrome(x) {
if (x < 0) return false;
const str = x.toString();
return str === str.split('').reverse().join('');
}
function mergeTwoLists(l1, l2) {
const dummy = new ListNode();
let curr = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) { curr.next = l1; l1 = l1.next; }
else { curr.next = l2; l2 = l2.next; }
curr = curr.next;
}
curr.next = l1 || l2;
return dummy.next;
}
function maxSubArray(nums) {
let maxSum = nums[0], 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;
}
function climbStairs(n) {
if (n <= 2) return n;
let a = 1, b = 2;
for (let i = 3; i <= n; i++) [a, b] = [b, a + b];
return b;
}
13.2 中等难度
javascript
function threeSum(nums) {
const result = [];
nums.sort((a, b) => a - b);
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1, right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
result.push([nums[i], nums[left], nums[right]]);
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++; right--;
} else if (sum < 0) { left++; }
else { right--; }
}
}
return result;
}
function lengthOfLongestSubstring(s) {
const set = new Set();
let left = 0, maxLen = 0;
for (let right = 0; right < s.length; right++) {
while (set.has(s[right])) { set.delete(s[left]); left++; }
set.add(s[right]);
maxLen = Math.max(maxLen, right - left + 1);
}
return maxLen;
}
function generateParenthesis(n) {
const result = [];
function backtrack(str, left, right) {
if (str.length === 2 * n) { result.push(str); return; }
if (left < n) backtrack(str + '(', left + 1, right);
if (right < left) backtrack(str + ')', left, right + 1);
}
backtrack('', 0, 0);
return result;
}
function search(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (nums[mid] === target) return mid;
if (nums[left] <= nums[mid]) {
if (target >= nums[left] && target < nums[mid]) right = mid - 1;
else left = mid + 1;
} else {
if (target > nums[mid] && target <= nums[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
13.3 困难难度
javascript
function mergeKLists(lists) {
if (!lists.length) return null;
while (lists.length > 1) {
const merged = [];
for (let i = 0; i < lists.length; i += 2) {
merged.push(mergeTwoLists(lists[i], lists[i + 1] || null));
}
lists = merged;
}
return lists[0];
}
function trap(height) {
let left = 0, right = height.length - 1;
let leftMax = 0, rightMax = 0, water = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) leftMax = height[left];
else water += leftMax - height[left];
left++;
} else {
if (height[right] >= rightMax) rightMax = height[right];
else water += rightMax - height[right];
right--;
}
}
return water;
}
function minDistance(word1, word2) {
const m = word1.length, n = word2.length;
const dp = Array.from({ length: m + 1 }, (_, i) =>
Array.from({ length: n + 1 }, (_, j) => {
if (i === 0) return j;
if (j === 0) return i;
return 0;
})
);
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (word1[i - 1] === word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
}
}
return dp[m][n];
}
function longestValidParentheses(s) {
const stack = [-1];
let maxLen = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === '(') stack.push(i);
else {
stack.pop();
if (!stack.length) stack.push(i);
else maxLen = Math.max(maxLen, i - stack[stack.length - 1]);
}
}
return maxLen;
}
十四、算法思维总结
14.1 五大算法思想
-
分治:将问题分解为子问题,分别求解后合并
- 归并排序、快速排序、合并有序链表
-
动态规划:保存子问题结果,避免重复计算
- 斐波那契、背包问题、最长公共子序列
-
贪心:每步选择局部最优解
- 活动选择、哈夫曼编码
-
回溯:枚举所有可能路径,剪枝优化
- 全排列、N皇后、括号生成
-
分支限界:BFS + 剪枝,寻找最优解
- 最短路径、数独
14.2 常见模式速查
| 模式 | 适用场景 | 关键技巧 |
|---|---|---|
| 双指针 | 有序数组、链表 | 对撞指针、快慢指针 |
| 滑动窗口 | 子串、子数组 | 扩张 + 收缩 |
| 哈希表 | 快速查找、计数 | 空间换时间 |
| 递归/回溯 | 组合、排列 | 选与不选、状态恢复 |
| 动态规划 | 最优解、计数 | 状态定义、转移方程 |
| 单调栈 | 下一个更大元素 | 递减栈 |
| 优先队列 | TOP-K、流式数据 | 最小堆/最大堆 |
| 位运算 | 状态压缩 | 与、或、异或 |
14.3 复杂度速查
| 数据结构 | 操作 | 时间复杂度 |
|---|---|---|
| 数组 | 访问 | O(1) |
| 数组 | 搜索 | O(n) |
| 数组 | 插入/删除 | O(n) |
| 链表 | 访问 | O(n) |
| 链表 | 插入/删除 | O(1) |
| 栈/队列 | 增删 | O(1) |
| 哈希表 | 增删改查 | O(1) |
| 二叉搜索树 | 增删改查 | O(log n) |
| 堆 | 堆顶/插入 | O(1) / O(log n) |
| 图(BFS/DFS) | 遍历 | O(V + E) |
14.4 面试高频 Top 20
- 两数之和 🔥🔥🔥🔥🔥
- 三数之和 🔥🔥🔥🔥🔥
- 滑动窗口最大值 🔥🔥🔥🔥
- 合并两个有序链表 🔥🔥🔥🔥
- 反转链表 🔥🔥🔥🔥🔥
- 环形链表 🔥🔥🔥🔥
- 全排列 🔥🔥🔥🔥
- 子集 🔥🔥🔥🔥
- 括号生成 🔥🔥🔥🔥
- 最长递增子序列 🔥🔥🔥🔥🔥
- 岛屿数量 🔥🔥🔥🔥
- 接雨水 🔥🔥🔥🔥🔥
- 最小栈 🔥🔥🔥🔥
- 二叉树遍历 🔥🔥🔥🔥
- 二叉树最近公共祖先 🔥🔥🔥🔥
- 编辑距离 🔥🔥🔥🔥🔥
- 爬楼梯 🔥🔥🔥🔥🔥
- 最大子序和 🔥🔥🔥🔥🔥
- 打家劫舍 🔥🔥🔥🔥
- 背包问题 🔥🔥🔥🔥🔥
14.5 刷题路线建议
入门阶段(1-2 周)
├── 数组、字符串基本操作
├── 链表基础(反转、合并、检测环)
├── 栈与队列基础应用
└── 二叉树遍历(前/中/后/层序)
进阶阶段(2-4 周)
├── 双指针、滑动窗口
├── 二分查找及变体
├── 哈希表高频题
└── DFS/BFS 网格搜索
高级阶段(4-6 周)
├── 动态规划(背包、子序列、路径)
├── 回溯(排列、组合、N皇后)
├── 贪心算法
└── 图论(最短路径、拓扑排序)
冲刺阶段(1-2 周)
├── LeetCode Hot 100
├── 剑指 Offer 全刷
└── 模拟面试限时训练
附录:常用代码模板
快速排序模板
javascript
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = arr.filter(x => x < pivot);
const middle = arr.filter(x => x === pivot);
const right = arr.filter(x => x > pivot);
return [...quickSort(left), ...middle, ...quickSort(right)];
}
归并排序模板
javascript
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
while (left.length && right.length) {
result.push(left[0] <= right[0] ? left.shift() : right.shift());
}
return [...result, ...left, ...right];
}
二分搜索模板
javascript
function binarySearch(arr, target) {
let left = 0, right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
DFS 模板
javascript
function dfs(node, state) {
if (!node) return;
process(node);
dfs(node.left, newState);
dfs(node.right, newState);
}
BFS 模板
javascript
function bfs(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length) {
const node = queue.shift();
result.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
return result;
}
回溯模板
javascript
function backtrack(path, choices) {
if (meetCondition(path)) {
result.push([...path]);
return;
}
for (const choice of choices) {
path.push(choice);
backtrack(path, nextChoices);
path.pop();
}
}
滑动窗口模板
javascript
function slidingWindow(s) {
const window = new Map();
let left = 0, right = 0;
while (right < s.length) {
window.set(s[right], (window.get(s[right]) || 0) + 1);
while (windowNeedsShrink(window)) {
window.set(s[left], window.get(s[left]) - 1);
if (window.get(s[left]) === 0) window.delete(s[left]);
left++;
}
right++;
}
}
并查集模板
javascript
class UnionFind {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
this.rank = new Array(n).fill(0);
}
find(x) {
if (this.parent[x] !== x) this.parent[x] = this.find(this.parent[x]);
return this.parent[x];
}
union(x, y) {
const px = this.find(x), py = this.find(y);
if (px === py) return;
if (this.rank[px] < this.rank[py]) this.parent[px] = py;
else if (this.rank[px] > this.rank[py]) this.parent[py] = px;
else { this.parent[py] = px; this.rank[px]++; }
}
connected(x, y) { return this.find(x) === this.find(y); }
}
Trie 模板
javascript
class TrieNode {
constructor() {
this.children = {};
this.isEnd = false;
}
}
class Trie {
constructor() { this.root = new TrieNode(); }
insert(word) {
let node = this.root;
for (const ch of word) {
if (!node.children[ch]) node.children[ch] = new TrieNode();
node = node.children[ch];
}
node.isEnd = true;
}
search(word) {
const node = this._find(word);
return node !== null && node.isEnd;
}
startsWith(prefix) { return this._find(prefix) !== null; }
_find(str) {
let node = this.root;
for (const ch of str) {
if (!node.children[ch]) return null;
node = node.children[ch];
}
return node;
}
}
💪 总结 :本文涵盖前端 JavaScript 算法与数据结构的 核心知识点 ,从基础到实战,包含 50+ 经典题目,帮助你提升编码能力,攻克面试难题!