webDevInterviewQuestions/algorithm/03-data-structure.md
2026-01-19 09:50:11 +08:00

379 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 数据结构与算法
## 1. 链表
> **场景**操作历史记录浏览器前进后退、LRU 缓存淘汰、撤销重做功能、音乐播放列表。
> **解决**:频繁插入/删除场景下数组性能差的问题O(1) 插入删除。
### 反转链表
```js
function reverseList(head) {
let prev = null, curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
```
### 环形链表判断
```js
function hasCycle(head) {
let slow = head, fast = head;
while (fast?.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
```
### 合并有序链表
```js
function mergeTwoLists(l1, l2) {
const dummy = { next: null };
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;
}
```
---
## 2. 二叉树
> **场景**DOM 树遍历、文件目录结构、组织架构图、表达式解析、前端路由匹配。
> **解决**:层级数据的存储与高效查找,遍历、搜索与路径问题。
### 遍历(前中后序)
```js
// 前序:根-左-右
const preorder = (root, res = []) => {
if (!root) return res;
res.push(root.val);
preorder(root.left, res);
preorder(root.right, res);
return res;
};
// 中序:左-根-右
const inorder = (root, res = []) => {
if (!root) return res;
inorder(root.left, res);
res.push(root.val);
inorder(root.right, res);
return res;
};
// 后序:左-右-根
const postorder = (root, res = []) => {
if (!root) return res;
postorder(root.left, res);
postorder(root.right, res);
res.push(root.val);
return res;
};
```
### 求最大深度
```js
const maxDepth = root => {
if (!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
};
```
### 路径和
```js
function hasPathSum(root, targetSum) {
if (!root) return false;
if (!root.left && !root.right) return targetSum === root.val;
return hasPathSum(root.left, targetSum - root.val)
|| hasPathSum(root.right, targetSum - root.val);
}
```
---
## 3. 栈与队列
> **场景**栈用于括号匹配、撤销操作、函数调用栈队列用于任务调度、消息队列、BFS 广度优先搜索。
> **解决**:先进后出/先进先出的数据操作顺序控制。
### 用栈实现队列
```js
class MyQueue {
constructor() {
this.inStack = [];
this.outStack = [];
}
push(x) { this.inStack.push(x); }
pop() {
if (!this.outStack.length) {
while (this.inStack.length) this.outStack.push(this.inStack.pop());
}
return this.outStack.pop();
}
peek() {
if (!this.outStack.length) {
while (this.inStack.length) this.outStack.push(this.inStack.pop());
}
return this.outStack[this.outStack.length - 1];
}
empty() { return !this.inStack.length && !this.outStack.length; }
}
```
### 有效的括号
```js
function isValid(s) {
const map = { ')': '(', ']': '[', '}': '{' };
const stack = [];
for (const c of s) {
if (map[c]) {
if (stack.pop() !== map[c]) return false;
} else {
stack.push(c);
}
}
return !stack.length;
}
```
---
## 4. 哈希表
> **场景**快速查找用户ID查询、统计词频、数据去重、分组操作。
> **解决**O(1) 时间复杂度的键值对存储和查找,避免线性遍历。
### 两数之和
```js
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const diff = target - nums[i];
if (map.has(diff)) return [map.get(diff), i];
map.set(nums[i], i);
}
return [];
}
```
### 字母异位词分组
```js
function groupAnagrams(strs) {
const map = new Map();
for (const s of strs) {
const key = [...s].sort().join('');
map.set(key, (map.get(key) || []).concat(s));
}
return [...map.values()];
}
```
---
## 5. 排序算法
> **场景**:商品价格/销量排序、排行榜、数据可视化预处理。
> **解决**:无序数据有序化,理解分治思想(快排、归并)是算法基础。
### 快速排序
```js
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)];
}
```
### 归并排序
```js
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) {
result.push(left[i] < right[j] ? left[i++] : right[j++]);
}
return result.concat(left.slice(i), right.slice(j));
}
```
---
## 6. 二分查找
> **场景**有序列表快速定位分页跳转、版本号查找、IP 地址归属地查询、猜数字游戏。
> **解决**:在有序数据中 O(log n) 时间复杂度快速查找,比线性查找效率高很多。
### 基础二分查找
```js
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;
arr[mid] < target ? (left = mid + 1) : (right = mid - 1);
}
return -1;
}
```
### 旋转数组查找
```js
function searchRotated(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 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;
}
```
---
## 7. 斐波那契数列
> **场景**:算法入门经典题、理解递归与动态规划思想、缓存优化演示、爬楼梯问题变种。
> **解决**:求第 n 位斐波那契数F(0)=0, F(1)=1, F(n)=F(n-1)+F(n-2))。
### 方法一:普通递归(不推荐)
```js
// 时间复杂度 O(2^n),存在大量重复计算
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
```
### 方法二:记忆化递归
```js
// 时间复杂度 O(n),空间复杂度 O(n)
function fib(n, memo = {}) {
if (n <= 1) return n;
if (memo[n]) return memo[n];
return memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
}
```
### 方法三:动态规划
```js
// 时间复杂度 O(n),空间复杂度 O(n)
function fib(n) {
if (n <= 1) return n;
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
```
### 方法四:空间优化(推荐)
```js
// 时间复杂度 O(n),空间复杂度 O(1)
function fib(n) {
if (n <= 1) return n;
let prev = 0, curr = 1;
for (let i = 2; i <= n; i++) {
[prev, curr] = [curr, prev + curr];
}
return curr;
}
```
### 方法五:矩阵快速幂(大数优化)
```js
// 时间复杂度 O(log n),适合求极大位数
function fib(n) {
if (n <= 1) return n;
const multiply = (a, b) => [
[a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1]],
[a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1]]
];
const power = (m, p) => {
let result = [[1, 0], [0, 1]]; // 单位矩阵
while (p > 0) {
if (p & 1) result = multiply(result, m);
m = multiply(m, m);
p >>= 1;
}
return result;
};
const matrix = [[1, 1], [1, 0]];
return power(matrix, n - 1)[0][0];
}
```
| 方法 | 时间复杂度 | 空间复杂度 | 适用场景 |
|------|-----------|-----------|----------|
| 普通递归 | O(2^n) | O(n) | 仅理解思想 |
| 记忆化递归 | O(n) | O(n) | 面试常考 |
| 动态规划 | O(n) | O(n) | 面试常考 |
| 空间优化 | O(n) | O(1) | **推荐** |
| 矩阵快速幂 | O(log n) | O(1) | 大数/竞赛 |