6.1 KiB
6.1 KiB
数据结构与算法
1. 链表
场景:操作历史记录(浏览器前进后退)、LRU 缓存淘汰、撤销重做功能、音乐播放列表。 解决:频繁插入/删除场景下数组性能差的问题,O(1) 插入删除。
反转链表
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?.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
合并有序链表
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 树遍历、文件目录结构、组织架构图、表达式解析、前端路由匹配。 解决:层级数据的存储与高效查找,遍历、搜索与路径问题。
遍历(前中后序)
// 前序:根-左-右
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;
};
求最大深度
const maxDepth = root => {
if (!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
};
路径和
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 广度优先搜索。 解决:先进后出/先进先出的数据操作顺序控制。
用栈实现队列
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; }
}
有效的括号
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) 时间复杂度的键值对存储和查找,避免线性遍历。
两数之和
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 [];
}
字母异位词分组
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. 排序算法
场景:商品价格/销量排序、排行榜、数据可视化预处理。 解决:无序数据有序化,理解分治思想(快排、归并)是算法基础。
快速排序
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)];
}
归并排序
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) 时间复杂度快速查找,比线性查找效率高很多。
基础二分查找
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;
}
旋转数组查找
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;
}