文章目录
-
-
- 前端数据结构及算法篇
-
- 1. 如何实现一个高效的 LRU 缓存(Least Recently Used)?
- 2. 如何判断一个链表是否有环?
- 3. 如何实现二叉树的层序遍历?
- 4. 如何实现一个高效的深拷贝函数?
- 5. 如何实现一个二叉搜索树(BST)的插入操作?
- 6. 如何实现快速排序算法?
- 7. 如何检测数组中的重复元素?
- 8. 如何实现一个最小堆?
- 9. 如何实现一个 Trie(前缀树)?
- 10. 如何合并两个有序链表?
- 11. 如何实现一个拓扑排序?
- 12. 如何实现一个二分查找?
- 13. 如何反转一个链表?
- 14. 如何实现一个滑动窗口最大值算法?
- 15. 如何判断一个字符串是否是回文?
- 16. 如何实现一个并查集(Union-Find)?
- 17. 如何实现一个字符串的最长公共前缀?
- 18. 如何实现一个堆排序?
- 19. 如何实现一个图的深度优先搜索(DFS)?
- 20. 如何实现一个图的广度优先搜索(BFS)?
- 21. 如何实现一个字符串的最长不重复子串?
- 22. 如何实现一个数组的全排列?
- 23. 如何实现一个接雨水问题?
- 24. 如何实现一个岛屿数量计算?
- 25. 如何实现一个 LRU 缓存的链表版本?
- 26. 如何实现一个最短路径算法(Dijkstra)?
- 27. 如何实现一个字符串匹配算法(KMP)?
- 28. 如何实现一个数组的旋转?
- 29. 如何实现一个二叉树的最近公共祖先(LCA)?
- 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. 如何实现一个数组的第 k 大元素?
- 58. 如何实现一个字符串的通配符匹配?
- 59. 如何实现一个二叉树的锯齿形层序遍历?
- 60. 如何实现一个数组的四数之和?
- 61. 如何实现一个环形链表的入口节点?
- 62. 如何实现一个字符串的重复子串检测?
- 63. 如何实现一个数组的和为目标值的子数组?
- 64. 如何实现一个二叉树的路径总和 II?
- 65. 如何实现一个数组的跳跃游戏 II?
- 66. 如何实现一个字符串的最长回文子串?
- 67. 如何实现一个数组的螺旋矩阵?
- 68. 如何实现一个二叉树的验证?
- 69. 如何实现一个数组的缺失数字?
- 70. 如何实现一个字符串的单词梯?
- 71. 如何实现一个数组的和为零的子数组?
- 72. 如何实现一个二叉树的边界?
- 73. 如何实现一个数组的区间和查询?
- 74. 如何实现一个字符串的子序列计数?
- 75. 如何实现一个二叉树的翻转路径?
- 76. 如何实现一个数组的和为奇偶的目标子数组?
- 77. 如何实现一个字符串的回文分割?
- 78. 如何实现一个二叉树的路径总和 III?
- 79. 如何实现一个数组的旋转图像?
- 80. 如何实现一个字符串的正则表达式匹配(带缓存)?
- 81. 如何实现一个数组的和为目标值的连续子数组?
- 82. 如何实现一个二叉树的构造(前序 + 中序)?
- 83. 如何实现一个数组的单调栈?
- 84. 如何实现一个字符串的最长有效括号?
- 85. 如何实现一个数组的和为目标值的四数之和 II?
- 86. 如何实现一个二叉树的路径总和 IV?
- 87. 如何实现一个数组的和为目标值的子数组长度?
- 88. 如何实现一个字符串的子串异位词?
- 89. 如何实现一个二叉树的平衡检查?
- 90. 如何实现一个数组的和为目标值的最大子数组?
- 91. 如何实现一个字符串的子树匹配?
- 92. 如何实现一个数组的和为目标值的子数组最小长度?
- 93. 如何实现一个二叉树的层平均值?
- 94. 如何实现一个字符串的最小窗口子序列?
- 95. 如何实现一个数组的和为目标值的子数组最大长度?
- 96. 如何实现一个二叉树的路径总和最大值?
- 97. 如何实现一个数组的和为目标值的子数组计数(带负数)?
- 98. 如何实现一个字符串的子串匹配计数?
- 99. 如何实现一个二叉树的深度优先搜索(非递归)?
- 100. 如何实现一个数组的和为目标值的子数组最大和?
- 101. 如何实现一个二叉树的直径?
- 102. 如何实现一个字符串的回文子串计数?
- 103. 如何实现一个数组的和为目标值的子数组(返回所有子数组)?
- 104. 如何实现一个二叉树的先序遍历(Morris 遍历)?
- 105. 如何实现一个字符串的最长公共子序列?
- 106. 如何实现一个数组的最大矩形面积?
- 107. 如何实现一个二叉树的边界路径?
- 108. 如何实现一个字符串的最小回文分割?
- 109. 如何实现一个数组的和为目标值的子数组(带索引)?
- 110. 如何实现一个二叉树的 Morris 中序遍历?
- 111. 如何实现一个字符串的最长递增子序列?
- 112. 如何实现一个数组的最大子矩阵和?
- 113. 如何实现一个二叉树的路径总和(任意起点和终点)?
- 114. 如何实现一个字符串的括号生成?
- 115. 如何实现一个数组的和为目标值的子数组(最小和)?
- 116. 如何实现一个二叉树的 Morris 后序遍历?
- 117. 如何实现一个字符串的最长重复子串?
- 118. 如何实现一个数组的和为目标值的子数组(最大和)?
- 119. 如何实现一个二叉树的深度?
- 120. 如何实现一个字符串的最小窗口排序?
- 121. 如何实现一个数组的和为目标值的子数组(返回路径)?
- 122. 如何实现一个二叉树的层序遍历(倒序)?
- 123. 如何实现一个字符串的子串最大乘积?
- 124. 如何实现一个数组的和为目标值的子数组(最短路径)?
- 125. 如何实现一个二叉树的路径总和(返回所有路径)?
- 126. 如何实现一个字符串的最长子串(至少 k 个重复字符)?
- 127. 如何实现一个数组的和为目标值的子数组(最大长度)?
- 128. 如何实现一个二叉树的最近公共祖先(带父指针)?
- 129. 如何实现一个字符串的子串匹配(Rabin-Karp 算法)?
- 130. 如何实现一个数组的和为目标值的子数组(负数支持)?
- 131. 如何实现一个二叉树的路径总和(最大路径和)?
- 132. 如何实现一个字符串的最长回文子序列?
- 133. 如何实现一个数组的和为目标值的子数组(返回最长路径)?
- 134. 如何实现一个二叉树的层序遍历(带层号)?
- 135. 如何实现一个字符串的子串匹配(Boyer-Moore 算法)?
- 136. 如何实现一个数组的和为目标值的子数组(返回最小路径)?
- 137. 如何实现一个二叉树的路径总和(带负数)?
- 138. 如何实现一个字符串的最长无重复字符子串(返回子串)?
- 139. 如何实现一个数组的和为目标值的子数组(返回所有路径)?
- 140. 如何实现一个二叉树的层序遍历(带父节点)?
- 141. 如何实现一个字符串的最长公共子串?
- 142. 如何实现一个数组的和为目标值的子数组(返回最大路径)?
- 143. 如何实现一个二叉树的路径总和(带路径长度)?
- 144. 如何实现一个字符串的最长子串(k 个不同字符)?
- 145. 如何实现一个数组的和为目标值的子数组(返回最小和路径)?
- 146. 如何实现一个二叉树的路径总和(返回最大路径)?
- 147. 如何实现一个字符串的最长子串(无重复字符,返回子串)?
- 148. 如何实现一个数组的和为目标值的子数组(返回最大和路径)?
- 149. 如何实现一个二叉树的路径总和(返回最小路径)?
- 150. 如何实现一个字符串的最长子串(至少 k 个字符出现)?
- 151. 如何实现一个二叉树的路径总和(返回所有路径长度)?
- 152. 如何实现一个字符串的最长子串(固定 k 个不同字符)?
- 153. 如何实现一个数组的和为目标值的子数组(返回所有路径长度)?
- 154. 如何实现一个二叉树的路径总和(返回最短路径长度)?
- 155. 如何实现一个字符串的最长子串(至少 k 次出现字符)?
- 156. 如何实现一个数组的和为目标值的子数组(返回最短路径长度)?
- 157. 如何实现一个二叉树的路径总和(返回最大路径长度)?
- 158. 如何实现一个字符串的最长子串(返回所有无重复子串)?
- 159. 如何实现一个数组的和为目标值的子数组(返回最大路径长度)?
- 160. 如何实现一个二叉树的路径总和(返回所有路径和长度)?
- 161. 如何实现一个字符串的最长子串(返回所有 k 个不同字符子串)?
- 162. 如何实现一个数组的和为目标值的子数组(返回所有路径和长度)?
- 163. 如何实现一个二叉树的路径总和(返回最小路径和长度)?
- 164. 如何实现一个字符串的最长子串(返回所有至少 k 次出现字符子串)?
- 165. 如何实现一个数组的和为目标值的子数组(返回最小路径和长度)?
- 166. 如何实现一个二叉树的路径总和(返回最大路径和长度)?
- 167. 如何实现一个字符串的最长子串(返回所有固定 k 个不同字符子串)?
- 168. 如何实现一个数组的和为目标值的子数组(返回最大路径和长度)?
- 169. 如何实现一个二叉树的路径总和(返回所有路径和路径长度)?
- 170. 如何实现一个字符串的最长子串(返回所有至少 k 次出现字符子串和长度)?
- 171. 如何实现一个数组的和为目标值的子数组(返回所有路径和路径长度)?
- 172. 如何实现一个二叉树的路径总和(返回最小路径和路径长度)?
- 173. 如何实现一个字符串的最长子串(返回所有固定 k 个不同字符子串和长度)?
- 174. 如何实现一个数组的和为目标值的子数组(返回最小路径和路径长度)?
- 175. 如何实现一个二叉树的路径总和(返回最大路径和路径长度及路径)?
- 176. 如何实现一个字符串的最长子串(返回所有无重复字符子串和长度)?
- 177. 如何实现一个数组的和为目标值的子数组(返回最大路径和路径长度及路径)?
- 178. 如何实现一个二叉树的路径总和(返回所有路径、路径和及路径长度)?
- 179. 如何实现一个字符串的最长子串(返回所有至少 k 次出现字符子串、子串和长度)?
- 180. 如何实现一个数组的和为目标值的子数组(返回所有路径、路径和及路径长度)?
-
前端数据结构及算法篇
1. 如何实现一个高效的 LRU 缓存(Least Recently Used)?
答案解析
LRU 缓存需要快速访问最近使用的数据,并淘汰最久未使用的项。可以用 Map
(保持插入顺序)结合键值对实现:
- 时间复杂度:获取 O(1),插入/删除 O(1)。
- 空间复杂度:O(n),n 为缓存容量。
- 步骤:
- 使用
Map
存储键值对。 - 获取时将键移到末尾(表示最近使用)。
- 插入时若超出容量,删除第一个元素(最久未使用)。
- 使用
代码示例:
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);
}
}
应用场景:
- 浏览器缓存管理。
- 前端数据请求缓存(如 API 结果)。
2. 如何判断一个链表是否有环?
答案解析
使用快慢指针(Floyd 判圈法):
- 原理:快指针每次走 2 步,慢指针走 1 步,若有环两者必相遇。
- 时间复杂度:O(n)。
- 空间复杂度:O(1)。
代码示例:
class ListNode {
constructor(val) {
this.val = val;
this.next = null;
}
}
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;
}
应用场景:
- 检测循环依赖(如模块加载)。
- 浏览器事件循环调试。
3. 如何实现二叉树的层序遍历?
答案解析
层序遍历使用队列,按层从左到右访问节点:
- 时间复杂度:O(n)。
- 空间复杂度:O(w),w 为最大层宽。
代码示例:
class TreeNode {
constructor(val) {
this.val = val;
this.left = this.right = null;
}
}
function levelOrder(root) {
if (!root) return [];
const queue = [root], result = [];
while (queue.length) {
const levelSize = queue.length;
const level = [];
for (let i = 0; i < levelSize; 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;
}
应用场景:
- DOM 树层级遍历。
- 组件树渲染优化。
4. 如何实现一个高效的深拷贝函数?
答案解析
深拷贝需处理循环引用、特殊对象(如 Date、RegExp):
- 方法:递归 + WeakMap 记录已拷贝对象。
- 时间复杂度:O(n),n 为对象属性数。
代码示例:
function deepClone(obj, map = new WeakMap()) {
if (obj == null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
const clone = Array.isArray(obj) ? [] : {
};
map.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) clone[key] = deepClone(obj[key], map);
}
return clone;
}
应用场景:
- Redux 状态拷贝。
- 复杂对象的数据隔离。
5. 如何实现一个二叉搜索树(BST)的插入操作?
答案解析
BST 插入需保持左子树 < 根 < 右子树的性质:
- 时间复杂度:O(h),h 为树高(最坏 O(n))。
- 空间复杂度:O(h)(递归栈)。
代码示例:
class BSTNode {
constructor(val) {
this.val = val;
this.left = this.right = null;
}
}
class BST {
constructor() {
this.root = null;
}
insert(val) {
if (!this.root) {
this.root = new BSTNode(val);
return;
}
let current = this.root;
while (true) {
if (val < current.val) {
if (!current.left) {
current.left = new BSTNode(val);
break;
}
current = current.left;
} else {
if (!current.right) {
current.right = new BSTNode(val);
break;
}
current = current.right;
}
}
}
}
应用场景:
- 前端搜索建议排序。
- 数据高效查找。
6. 如何实现快速排序算法?
答案解析
快速排序通过分区递归实现:
- 步骤:选择基准(pivot),分区后递归排序。
- 时间复杂度:平均 O(n log n),最坏 O(n²)。
- 空间复杂度:O(log n)(递归栈)。
代码示例:
function quickSort(arr, left = 0, right = arr.length - 1) {
if (left >= right) return;
const pivot = partition(arr, left, right);
quickSort(arr, left, pivot - 1);
quickSort(arr, pivot + 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;
}
应用场景:
- 大数据表格排序。
- 前端列表渲染优化。
7. 如何检测数组中的重复元素?
答案解析
使用 Set 检测重复:
- 时间复杂度:O(n)。
- 空间复杂度:O(n)。
代码示例:
function hasDuplicates(arr) {
return arr.length !== new Set(arr).size;
}
应用场景:
- 用户输入校验(如唯一 ID)。
- 数据去重处理。
8. 如何实现一个最小堆?
答案解析
最小堆是一种完全二叉树,父节点小于子节点:
- 操作:插入时上浮,删除时下沉。
- 时间复杂度:插入/删除 O(log n),构建 O(n)。
代码示例:
class MinHeap {
constructor() {
this.heap = [];
}
insert(val) {
this.heap.push(val);
this.siftUp(this.heap.length - 1);
}
siftUp(index) {
let parent = Math.floor((index - 1) / 2);
while (index > 0 && this.heap[parent] > this.heap[index]) {
[this.heap[parent], this.heap[index]] = [this.heap[index], this.heap[parent]];
index = parent;
parent = Math.floor((index - 1) / 2);
}
}
extractMin() {
if (!this.heap.length) return null;
const min = this.heap[0];
this.heap[0] = this.heap.pop();
this.siftDown(0);
return min;
}
siftDown(index) {
const len = this.heap.length;
while (true) {
let min = index;
const left = 2 * index + 1, right = 2 * index + 2;
if (left < len && this.heap[left] < this.heap[min]) min = left;
if (right < len && this.heap[right] < this.heap[min]) min = right;
if (min === index) break;
[this.heap[index], this.heap[min]] = [this.heap[min], this.heap[index]];
index = min;
}
}
}
应用场景:
- 任务调度优先级队列。
- 前端事件队列优化。
9. 如何实现一个 Trie(前缀树)?
答案解析
Trie 用于高效存储和查找字符串集合:
- 时间复杂度:插入/查找 O(m),m 为字符串长度。
- 空间复杂度:O(ALPHABET_SIZE * m * n),n 为字符串数。
代码示例:
class TrieNode {
constructor() {
this.children = new Map();
this.isEnd = false;
}
}
class Trie {
constructor() {
this.root = new TrieNode();
}
insert(word) {
let node = this.root;
for (let char of word) {
if (!node.children.has(char)) node.children.set(char, new TrieNode());
node = node.children.get(char);
}
node.isEnd = true;
}
search(word) {
let node = this.root;
for (let char of word) {
if (!node.children.has(char)) return false;
node = node.children.get(char);
}
return node.isEnd;
}
startsWith(prefix) {
let node = this.root;
for (let char of prefix) {
if (!node.children.has(char)) return false;
node = node.children.get(char);
}
return true;
}
}
应用场景:
- 搜索框自动补全。
- 前端路由匹配。
10. 如何合并两个有序链表?
答案解析
通过比较节点值合并:
- 时间复杂度:O(n + m),n、m 为链表长度。
- 空间复杂度:O(1)(迭代)。
代码示例:
function mergeTwoLists(l1, l2) {
const dummy = new ListNode(0);
let current = dummy;
while (l1 && l2) {
if (l1.val < l2.val) {
current.next = l1;
l1 = l1.next;
} else {
current.next = l2;
l2 = l2.next;
}
current = current.next;
}
current.next = l1 || l2;
return dummy.next;
}
应用场景:
- 合并排序后的数据流。
- 前端列表合并渲染。
11. 如何实现一个拓扑排序?
答案解析
拓扑排序用于有向无环图(DAG),可用 DFS 或入度法:
- 方法:入度法,使用队列处理入度为 0 的节点。
- 时间复杂度:O(V + E)。
代码示例:
function topologicalSort(graph) {
const indegree = new Map(), queue = [], result = [];
for (let node in graph) {
indegree.set(node, 0);
}
for (let node in graph) {
for (let neighbor of graph[node]) {
indegree.set(neighbor, (indegree.get(neighbor) || 0) + 1);
}
}
for (let [node, degree] of indegree) {
if (degree === 0) queue.push(node);
}
while (queue.length) {
const node = queue.shift();
result.push(node);
for (let neighbor of graph[node]) {
indegree.set(neighbor, indegree.get(neighbor) - 1);
if (indegree.get(neighbor) === 0) queue.push(neighbor);
}
}
return result.length === Object.keys(graph).length ? result : [];
}
应用场景:
- 模块依赖解析。
- 任务调度顺序。
12. 如何实现一个二分查找?
答案解析
二分查找适用于有序数组:
- 时间复杂度:O(log n)。
- 空间复杂度:O(1)。
代码示例:
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;
}
应用场景:
- 前端搜索优化。
- 数据分页查找。
13. 如何反转一个链表?
答案解析
通过迭代或递归反转指针:
- 时间复杂度:O(n)。
- 空间复杂度:O(1)(迭代)。
代码示例:
function reverseList(head) {
let prev = null, current = head;
while (current) {
const next = current.next;
current.next = prev;
prev = current;
current = next;
}
return prev;
}
应用场景:
- 前端组件顺序调整。
- 数据流反向处理。
14. 如何实现一个滑动窗口最大值算法?
答案解析
使用双端队列维护窗口内最大值:
- 时间复杂度:O(n)。
- 空间复杂度:O(k),k 为窗口大小。
代码示例:
function maxSlidingWindow(nums, k) {
const deque = [], result = [];
for (let i = 0; i < nums.length; i++) {
while (deque.length && deque[0] <= i - k) deque.shift();
while (deque.length && nums[deque[deque.length - 1]] <= nums[i]) deque.pop();
deque.push(i);
if (i >= k - 1) result.push(nums[deque[0]]);
}
return result;
}
应用场景:
- 实时数据流分析。
- 图表最大值展示。
15. 如何判断一个字符串是否是回文?
答案解析
双指针从两端向中间比较:
- 时间复杂度:O(n)。
- 空间复杂度:O(1)。
代码示例:
function isPalindrome(s) {
s = s.toLowerCase().replace(/[^a-z0-9]/g, '');
let left = 0, right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) return false;
left++;
right--;
}
return true;
}
应用场景:
- 输入校验(如用户名)。
- 前端表单验证。
16. 如何实现一个并查集(Union-Find)?
答案解析
并查集用于处理动态连通性:
- 优化:路径压缩 + 按秩合并。
- 时间复杂度:近似 O(1)(均摊)。
代码示例:
class UnionFind {
constructor(n) {
this.parent = Array(n).fill().map((_, i) => i);
this.rank = 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]++;
}
}
}
应用场景:
- 社交网络分组。
- 前端组件连通性分析。
17. 如何实现一个字符串的最长公共前缀?
答案解析
逐个比较字符:
- 时间复杂度:O(n * m),m 为最短字符串长度。
- 空间复杂度:O(1)。
代码示例:
function longestCommonPrefix(strs) {
if (!strs.length) 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;
}
应用场景:
- 前端搜索建议分组。
- 文件路径匹配。
18. 如何实现一个堆排序?
答案解析
堆排序基于最大堆:
- 步骤:构建堆,逐步提取最大值。
- 时间复杂度:O(n log n)。
代码示例:
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, left = 2 * i + 1, 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);
}
}
应用场景:
- 前端大数据排序。
- 优先级任务处理。
19. 如何实现一个图的深度优先搜索(DFS)?
答案解析
DFS 使用递归或栈遍历图:
- 时间复杂度:O(V + E)。
- 空间复杂度:O(V)。
代码示例:
function dfs(graph, start) {
const visited = new Set(), result = [];
function traverse(node) {
visited.add(node);
result.push(node);
for (let neighbor of graph[node]) {
if (!visited.has(neighbor)) traverse(neighbor);
}
}
traverse(start);
return result;
}
应用场景:
- DOM 树深度遍历。
- 依赖关系解析。
20. 如何实现一个图的广度优先搜索(BFS)?
答案解析
BFS 使用队列按层遍历:
- 时间复杂度:O(V + E)。
- 空间复杂度:O(V)。
代码示例:
function bfs(graph, start) {
const visited = new Set(), queue = [start], result = [];
visited.add(start);
while (queue.length) {
const node = queue.shift();
result.push(node);
for (let neighbor of graph[node]) {
if (!visited.has(neighbor)) {
visited.add(neighbor);
queue.push(neighbor);
}
}
}
return result;
}
应用场景:
- 最短路径计算(如路由)。
- 前端层级遍历。
21. 如何实现一个字符串的最长不重复子串?
答案解析
使用滑动窗口 + 哈希表:
- 时间复杂度:O(n)。
- 空间复杂度:O(min(m, n)),m 为字符集大小。
代码示例:
function lengthOfLongestSubstring(s) {
const map = new Map();
let max = 0, left = 0;
for (let right = 0; right < s.length; right++) {
if (map.has(s[right])) left = Math.max(left, map.get(s[right]) + 1);
map.set(s[right], right);
max = Math.max(max, right - left + 1);
}
return max;
}
应用场景:
- 输入框唯一性校验。
- 数据流分析。
22. 如何实现一个数组的全排列?
答案解析
使用回溯算法:
- 时间复杂度:O(n!)。
- 空间复杂度:O(n)。
代码示例:
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[i]) continue;
used[i] = true;
path.push(nums[i]);
backtrack(path, used);
path.pop();
used[i] = false;
}
}
backtrack([], Array(nums.length).fill(false));
return result;
}
应用场景:
- 前端组合生成(如菜单排列)。
- 数据测试用例生成。
23. 如何实现一个接雨水问题?
答案解析
使用双指针或单调栈计算每列接水量:
- 双指针法:左右指针计算水高。
- 时间复杂度:O(n)。
代码示例:
function trap(height) {
let left = 0, right = height.length - 1, leftMax = 0, rightMax = 0, water = 0;
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (leftMax < rightMax) water += leftMax - height[left++];
else water += rightMax - height[right--];
}
return water;
}
应用场景:
- 可视化数据处理。
- 前端布局计算。
24. 如何实现一个岛屿数量计算?
答案解析
使用 DFS 标记连通区域:
- 时间复杂度:O(m * n)。
- 空间复杂度:O(m * n)(递归栈)。
代码示例:
function numIslands(grid) {
let count = 0;
function dfs(i, j) {
if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] === '0') return;
grid[i][j] = '0';
dfs(i - 1, j);
dfs(i + 1, j);
dfs(i, j - 1);
dfs(i, j + 1);
}
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] === '1') {
count++;
dfs(i, j);
}
}
}
return count;
}
应用场景:
- 前端图像分割。
- 组件连通性分析。
25. 如何实现一个 LRU 缓存的链表版本?
答案解析
使用双向链表 + 哈希表:
- 时间复杂度:O(1)。
- 空间复杂度:O(n)。
代码示例:
class DLinkedNode {
constructor(key, value) {
this.key = key;
this.value = value;
this.prev = this.next = null;
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.map = new Map();
this.head = new DLinkedNode(0, 0);
this.tail = new DLinkedNode(0, 0);
this.head.next = this.tail;
this.tail.prev = this.head;
}
get(key) {
if (!this.map.has(key)) return -1;
const node = this.map.get(key);
this.moveToHead(node);
return node.value;
}
put(key, value) {
if (this.map.has(key)) {
const node = this.map.get(key);
node.value = value;
this.moveToHead(node);
} else {
const node = new DLinkedNode(key, value);
this.map.set(key, node);
this.addToHead(node);
if (this.map.size > this.capacity) {
const removed = this.removeTail();
this.map.delete(removed.key);
}
}
}
moveToHead(node) {
this.removeNode(node);
this.addToHead(node);
}
addToHead(node) {
node.prev = this.head;
node.next = this.head.next;
this.head.next.prev = node;
this.head.next = node;
}
removeNode(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
removeTail() {
const node = this.tail.prev;
this.removeNode(node);
return node;
}
}
应用场景:
- 高性能缓存设计。
- 前端资源管理。
26. 如何实现一个最短路径算法(Dijkstra)?
答案解析
Dijkstra 使用优先队列计算单源最短路径:
- 时间复杂度:O((V + E) log V)。
- 空间复杂度:O(V)。
代码示例:
function dijkstra(graph, start) {
const distances = new Map(), pq = new MinHeap(), visited = new Set();
for (let node in graph) distances.set(node, Infinity);
distances.set(start, 0);
pq.insert([0, start]);
while (pq.heap.length) {
const [dist, node] = pq.extractMin();
if (visited.has(node)) continue;
visited.add(node);
for (let [neighbor, weight] of graph[node]) {
const newDist = dist + weight;
if (newDist < distances.get(neighbor)) {
distances.set(neighbor, newDist);
pq.insert([newDist, neighbor]);
}
}
}
return distances;
}
应用场景:
- 前端路由规划。
- 网络请求优化。
27. 如何实现一个字符串匹配算法(KMP)?
答案解析
KMP 通过前缀表避免回溯:
- 时间复杂度:O(n + m)。
- 空间复杂度:O(m)。
代码示例:
function kmpSearch(text, pattern) {
const next = getNext(pattern);
let i = 0, j = 0;
while (i < text.length) {
if (j === -1 || text[i] === pattern[j]) {
i++;
j++;
if (j === pattern.length) return i - j;
} else j = next[j];
}
return -1;
}
function getNext(pattern) {
const next = [-1];
let i = 0, j = -1;
while (i < pattern.length) {
if (j === -1 || pattern[i] === pattern[j]) {
i++;
j++;
next[i] = j;
} else j = next[j];
}
return next;
}
应用场景:
- 前端搜索高亮。
- 文本匹配优化。
28. 如何实现一个数组的旋转?
答案解析
使用三次反转法:
- 时间复杂度:O(n)。
- 空间复杂度:O(1)。
代码示例:
function rotate(arr, k) {
k %= arr.length;
reverse(arr, 0, arr.length - 1);
reverse(arr, 0, k - 1);
reverse(arr, k, arr.length - 1);
return arr;
}
function reverse(arr, start, end) {
while (start < end) {
[arr[start], arr[end]] = [arr[end], arr[start]];
start++;
end--;
}
}
应用场景:
- 前端轮播图切换。
- 数据循环展示。
29. 如何实现一个二叉树的最近公共祖先(LCA)?
答案解析
递归查找路径交点:
- 时间复杂度:O(h)。
- 空间复杂度:O(h)。
代码示例:
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;
}
应用场景:
- DOM 树节点关系分析。
- 组件层级查找。
30. 如何实现一个数组的子集生成?
答案解析
使用回溯生成所有子集:
- 时间复杂度:O(2^n)。
- 空间复杂度:O(n)。
代码示例:
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;
}
应用场景:
- 前端过滤选项组合。
- 数据分析子集生成。
31. 如何实现一个最大子数组和?
答案解析
使用 Kadane 算法:
- 时间复杂度:O(n)。
- 空间复杂度:O(1)。
代码示例:
function maxSubArray(nums) {
let max = nums[0], current = nums[0];
for (let i = 1; i < nums.length; i++) {
current = Math.max(nums[i], current + nums[i]);
max = Math.max(max, current);
}
return max;
}
应用场景:
- 数据趋势分析。
- 前端图表计算。
32. 如何实现一个字符串的正则表达式匹配?
答案解析
使用动态规划处理通配符:
- 时间复杂度:O(m * n)。
- 空间复杂度:O(m * n)。
代码示例:
function isMatch(s, p) {
const m = s.length, n = p.length;
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(false));
dp[0][0] = true;
for (let j = 1; j <= n; j++) {
if (p[j - 1] === '*') dp[0][j] = dp[0][j - 2];
}
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (p[j - 1] === '.' || p[j - 1] === s[i - 1]) dp[i][j] = dp[i - 1][j - 1];
else if (p[j - 1] === '*') {
dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (s[i - 1] === p[j - 2] || p[j - 2] === '.'));
}
}
}
return dp[m][n];
}
应用场景:
- 前端表单验证。
- 搜索过滤逻辑。
33. 如何实现一个循环队列?
答案解析
使用数组模拟循环:
- 时间复杂度:入队/出队 O(1)。
- 空间复杂度:O(n)。
代码示例:
class CircularQueue {
constructor(capacity) {
this.queue = new Array(capacity);
this.capacity = capacity;
this.front = 0;
this.rear = 0;
this.size = 0;
}
enqueue(value) {
if (this.isFull()) return false;
this.queue[this.rear] = value;
this.rear = (this.rear + 1) % this.capacity;
this.size++;
return true;
}
dequeue() {
if (this.isEmpty()) return null;
const value = this.queue[this.front];
this.front = (this.front + 1) % this.capacity;
this.size--;
return value;
}
isEmpty() {
return this.size === 0;
}
isFull() {
return this.size === this.capacity;
}
}
应用场景:
- 前端任务缓冲。
- 数据流循环处理。
34. 如何实现一个二叉树的中序遍历?
答案解析
中序遍历(左-根-右)使用递归或栈:
- 时间复杂度:O(n)。
- 空间复杂度:O(h)。
代码示例:
function inorderTraversal(root) {
const result = [], stack = [];
let current = root;
while (current || stack.length) {
while (current) {
stack.push(current);
current = current.left;
}
current = stack.pop();
result.push(current.val);
current = current.right;
}
return result;
}
应用场景:
- BST 有序输出。
- 前端树形组件渲染。
35. 如何实现一个字符串的最小窗口子串?
答案解析
使用滑动窗口 + 计数器:
- 时间复杂度:O(n)。
- 空间复杂度:O(k)。
代码示例:
function minWindow(s, t) {
const map = new Map();
for (let char of t) map.set(char, (map.get(char) || 0) + 1);
let left = 0, minLen = Infinity, minStart = 0, count = t.length;
for (let right = 0; right < s.length; right++) {
if (map.has(s[right])) {
map.set(s[right], map.get(s[right]) - 1);
if (map.get(s[right]) >= 0) count--;
}
while (count === 0) {
if (right - left + 1 < minLen) {
minLen = right - left + 1;
minStart = left;
}
if (map.has(s[left])) {
map.set(s[left], map.get(s[left]) + 1);
if (map.get(s[left]) > 0) count++;
}
left++;
}
}
return minLen === Infinity ? '' : s.slice(minStart, minStart + minLen);
}
应用场景