以下是一个 前端 JavaScript 数据结构常见题目框架,可以帮助你快速组织思路并解决问题:
框架内容
1. 数组相关
- 查找与排序:
- 寻找数组的最大/最小值。
- 快速排序、归并排序、冒泡排序。
- 操作:
- 移除重复项:
new Set()
或双指针法。 - 滑动窗口法:求最大/最小子数组和。
- 二分查找:查找有序数组中目标值。
- 移除重复项:
2. 字符串相关
- 操作:
- 翻转字符串:双指针或
split().reverse().join()
。 - 判断回文字符串:头尾双指针。
- 翻转字符串:双指针或
- 模式匹配:
- 最长回文子串。
- 字符串的子序列匹配问题。
- 编码和解码:
- 字符串压缩与解压(如编码重复字符)。
3. 链表相关
- 基本操作:
- 翻转链表(递归和迭代)。
- 寻找中间节点(快慢指针)。
- 高级操作:
- 合并两个有序链表。
- 删除链表的倒数第 K 个节点。
- 复杂实现:
- LRU 缓存。
- 检测链表中的环。
4. 栈与队列
- 栈:
- 括号匹配:栈的经典应用。
- 单调栈:解决 "下一个更大元素"。
- 队列:
- 滑动窗口中的最大值(双端队列)。
- 用栈实现队列。
5. 哈希表相关
- 哈希操作:
- 字符串中第一个不重复的字符。
- 查找数组中和为目标值的两数。
- 结合其他结构:
- 前缀和问题(如连续子数组和为 K)。
- 判断数组的交集或差集。
6. 树和图相关
- 树:
- 二叉树的遍历(前序、中序、后序、层序)。
- 二叉树的最近公共祖先。
- 二叉树的序列化与反序列化。
- 图:
- 图的深度优先搜索(DFS)与广度优先搜索(BFS)。
- 最短路径问题(Dijkstra 或 Floyd)。
- 拓扑排序。
7. 动态规划
- 基础问题:
- 斐波那契数列。
- 最大子数组和问题。
- 路径相关:
- 不同路径问题(二维 DP)。
- 爬楼梯问题。
- 子序列:
- 最长上升子序列。
- 最长公共子序列。
8. 面试高频考点
- 双指针法:
- 判断有序数组中是否存在目标和。
- 三数之和问题。
- 滑动窗口:
- 最小覆盖子串。
- 长度为 K 的最大子数组和。
- 位运算:
- 判断奇偶性:
n & 1
。 - 找出只出现一次的数字:
异或
。
- 判断奇偶性:
9. 项目场景应用
- 文件解析:
- JSON 格式化或深拷贝。
- 模拟文件系统操作。
- 前端缓存:
- 使用 LRU 缓存实现最近访问管理。
- 使用 Map 和 WeakMap 管理缓存。
- 算法优化:
- 大数据查重(如使用布隆过滤器)。
- Web Worker 处理复杂计算。
实现模板
以下是常见代码组织的模板框架:
javascript
// 工具函数
const utils = {
swap: (arr, i, j) => { [arr[i], arr[j]] = [arr[j], arr[i]]; },
deepClone: (obj) => JSON.parse(JSON.stringify(obj)),
};
// 示例解题函数
function solveExample(input) {
// Step 1: 数据预处理
let processed = preprocess(input);
// Step 2: 核心逻辑
let result = coreLogic(processed);
// Step 3: 数据后处理
return postProcess(result);
}
// 示例核心逻辑
function coreLogic(data) {
// 核心算法
// 比如排序、搜索、遍历等
return data;
}
// 示例:测试用例
const testCases = [
{ input: [1, 2, 3], expected: 6 },
{ input: [4, 5, 6], expected: 15 },
];
testCases.forEach(({ input, expected }, idx) => {
const result = solveExample(input);
console.log(`Test Case #${idx + 1}:`, result === expected ? "Passed" : "Failed");
});
以下是每个问题的 JavaScript 实现:
1. 下一个更大元素 (循环数组)
javascript
function nextGreaterElements(nums) {
let n = nums.length;
let result = Array(n).fill(-1);
let stack = [];
for (let i = 0; i < 2 * n; i++) {
let num = nums[i % n];
while (stack.length && nums[stack[stack.length - 1]] < num) {
result[stack.pop()] = num;
}
if (i < n) stack.push(i);
}
return result;
}
2. 逆波兰表达式求值
javascript
function evalRPN(tokens) {
let stack = [];
for (let token of tokens) {
if (!isNaN(token)) {
stack.push(Number(token));
} else {
let b = stack.pop();
let 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;
}
}
}
return stack[0];
}
3. 二叉树的最大路径和
javascript
function maxPathSum(root) {
let maxSum = -Infinity;
function dfs(node) {
if (!node) return 0;
let left = Math.max(0, dfs(node.left));
let right = Math.max(0, dfs(node.right));
maxSum = Math.max(maxSum, left + right + node.val);
return Math.max(left, right) + node.val;
}
dfs(root);
return maxSum;
}
4. 从前序与中序遍历序列构造二叉树
javascript
function buildTree(preorder, inorder) {
if (!preorder.length || !inorder.length) return null;
let rootVal = preorder[0];
let root = { val: rootVal, left: null, right: null };
let mid = inorder.indexOf(rootVal);
root.left = buildTree(preorder.slice(1, mid + 1), inorder.slice(0, mid));
root.right = buildTree(preorder.slice(mid + 1), inorder.slice(mid + 1));
return root;
}
5. 从中序与后序遍历序列构造二叉树
javascript
function buildTreeFromInPost(inorder, postorder) {
if (!inorder.length || !postorder.length) return null;
let rootVal = postorder.pop();
let root = { val: rootVal, left: null, right: null };
let mid = inorder.indexOf(rootVal);
root.right = buildTreeFromInPost(inorder.slice(mid + 1), postorder);
root.left = buildTreeFromInPost(inorder.slice(0, mid), postorder);
return root;
}
6. 栈的链式存储
javascript
class StackNode {
constructor(value) {
this.value = value;
this.next = null;
}
}
class LinkedStack {
constructor() {
this.top = null;
}
push(value) {
let newNode = new StackNode(value);
newNode.next = this.top;
this.top = newNode;
}
pop() {
if (!this.top) return null;
let value = this.top.value;
this.top = this.top.next;
return value;
}
peek() {
return this.top ? this.top.value : null;
}
isEmpty() {
return this.top === null;
}
}
7. 下一个更大元素
javascript
function nextGreaterElement(nums1, nums2) {
let map = new Map();
let stack = [];
for (let num of nums2) {
while (stack.length && stack[stack.length - 1] < num) {
map.set(stack.pop(), num);
}
stack.push(num);
}
return nums1.map(num => map.get(num) || -1);
}
8. 括号匹配
javascript
function isValid(s) {
let stack = [];
let map = { ')': '(', '}': '{', ']': '[' };
for (let char of s) {
if (char in map) {
if (stack.pop() !== map[char]) return false;
} else {
stack.push(char);
}
}
return stack.length === 0;
}
9. 二叉树的最近公共祖先
javascript
function lowestCommonAncestor(root, p, q) {
if (!root || root === p || root === q) return root;
let left = lowestCommonAncestor(root.left, p, q);
let right = lowestCommonAncestor(root.right, p, q);
if (left && right) return root;
return left || right;
}
这些代码覆盖了您所列的所有问题,且在实践中非常实用。希望对您有所帮助!
以下是每个问题的 JavaScript 实现:
1. 最小栈
javascript
class MinStack {
constructor() {
this.stack = [];
this.minStack = [];
}
push(val) {
this.stack.push(val);
if (!this.minStack.length || val <= this.getMin()) {
this.minStack.push(val);
}
}
pop() {
if (this.stack.pop() === this.getMin()) {
this.minStack.pop();
}
}
top() {
return this.stack[this.stack.length - 1];
}
getMin() {
return this.minStack[this.minStack.length - 1];
}
}
2. 用栈实现队列
javascript
class MyQueue {
constructor() {
this.stackIn = [];
this.stackOut = [];
}
push(x) {
this.stackIn.push(x);
}
pop() {
this._move();
return this.stackOut.pop();
}
peek() {
this._move();
return this.stackOut[this.stackOut.length - 1];
}
empty() {
return !this.stackIn.length && !this.stackOut.length;
}
_move() {
if (!this.stackOut.length) {
while (this.stackIn.length) {
this.stackOut.push(this.stackIn.pop());
}
}
}
}
3. 二叉树的右视图
javascript
function rightSideView(root) {
if (!root) return [];
let result = [];
let queue = [root];
while (queue.length) {
let levelSize = queue.length;
for (let i = 0; i < levelSize; i++) {
let node = queue.shift();
if (i === levelSize - 1) result.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return result;
}
4. 表达式求值
javascript
function evaluateExpression(expression) {
let stack = [];
let num = 0;
let sign = 1;
let result = 0;
for (let char of expression) {
if (char === ' ') continue;
if (/\d/.test(char)) {
num = num * 10 + Number(char);
} else if (char === '+') {
result += sign * num;
num = 0;
sign = 1;
} else if (char === '-') {
result += sign * num;
num = 0;
sign = -1;
} else if (char === '(') {
stack.push(result);
stack.push(sign);
result = 0;
sign = 1;
} else if (char === ')') {
result += sign * num;
num = 0;
result *= stack.pop();
result += stack.pop();
}
}
return result + sign * num;
}
5. 验证二叉搜索树
javascript
function isValidBST(root, min = null, max = null) {
if (!root) return true;
if ((min !== null && root.val <= min) || (max !== null && root.val >= max)) {
return false;
}
return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max);
}
6. 用队列实现栈
javascript
class MyStack {
constructor() {
this.queue = [];
}
push(x) {
this.queue.push(x);
let size = this.queue.length;
while (size > 1) {
this.queue.push(this.queue.shift());
size--;
}
}
pop() {
return this.queue.shift();
}
top() {
return this.queue[0];
}
empty() {
return !this.queue.length;
}
}
7. 二叉树的序列化与反序列化
javascript
class Codec {
serialize(root) {
if (!root) return 'null';
return `${root.val},${this.serialize(root.left)},${this.serialize(root.right)}`;
}
deserialize(data) {
let nodes = data.split(',');
function buildTree() {
let val = nodes.shift();
if (val === 'null') return null;
let node = { val: Number(val), left: null, right: null };
node.left = buildTree();
node.right = buildTree();
return node;
}
return buildTree();
}
}
8. 二叉树的最大深度
javascript
function maxDepth(root) {
if (!root) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
9. 二叉树的最小深度
javascript
function minDepth(root) {
if (!root) return 0;
if (!root.left) return 1 + minDepth(root.right);
if (!root.right) return 1 + minDepth(root.left);
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}
10. 路径总和
javascript
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);
}
这些实现涵盖了问题的核心逻辑,旨在高效地解决每个场景的问题。希望这些代码对您有帮助!
以下是每个问题的 JavaScript 解决方案:
1. 对称二叉树
javascript
function isSymmetric(root) {
function isMirror(t1, t2) {
if (!t1 && !t2) return true;
if (!t1 || !t2) return false;
return t1.val === t2.val && isMirror(t1.left, t2.right) && isMirror(t1.right, t2.left);
}
return isMirror(root, root);
}
2. 二叉树的中序遍历
javascript
function inorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
traverse(node.left);
result.push(node.val);
traverse(node.right);
}
traverse(root);
return result;
}
3. 二叉树的层序遍历
javascript
function levelOrder(root) {
if (!root) return [];
let result = [];
let queue = [root];
while (queue.length) {
let level = [];
let size = queue.length;
for (let i = 0; i < size; i++) {
let 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;
}
4. 二叉树的直径
javascript
function diameterOfBinaryTree(root) {
let diameter = 0;
function depth(node) {
if (!node) return 0;
let left = depth(node.left);
let right = depth(node.right);
diameter = Math.max(diameter, left + right);
return 1 + Math.max(left, right);
}
depth(root);
return diameter;
}
5. 二叉树的前序遍历
javascript
function preorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
result.push(node.val);
traverse(node.left);
traverse(node.right);
}
traverse(root);
return result;
}
6. 二叉树的后序遍历
javascript
function postorderTraversal(root) {
let result = [];
function traverse(node) {
if (!node) return;
traverse(node.left);
traverse(node.right);
result.push(node.val);
}
traverse(root);
return result;
}
7. 翻转二叉树
javascript
function invertTree(root) {
if (!root) return null;
[root.left, root.right] = [invertTree(root.right), invertTree(root.left)];
return root;
}
8. 二叉树的所有路径
javascript
function binaryTreePaths(root) {
let paths = [];
function constructPaths(node, path) {
if (node) {
path += node.val;
if (!node.left && !node.right) {
paths.push(path);
} else {
path += '->';
constructPaths(node.left, path);
constructPaths(node.right, path);
}
}
}
constructPaths(root, '');
return paths;
}
9. 二又树的中序遍历(非递归)
javascript
function inorderTraversalIterative(root) {
let result = [];
let 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;
}
10. 二分图检测
javascript
function isBipartite(graph) {
let colors = Array(graph.length).fill(0);
function dfs(node, color) {
if (colors[node] !== 0) return colors[node] === color;
colors[node] = color;
for (let neighbor of graph[node]) {
if (!dfs(neighbor, -color)) return false;
}
return true;
}
for (let i = 0; i < graph.length; i++) {
if (colors[i] === 0 && !dfs(i, 1)) return false;
}
return true;
}
11. 拓扑排序
javascript
function topologicalSort(numCourses, prerequisites) {
let graph = Array.from({ length: numCourses }, () => []);
let inDegree = Array(numCourses).fill(0);
let queue = [];
let result = [];
for (let [course, prereq] of prerequisites) {
graph[prereq].push(course);
inDegree[course]++;
}
for (let i = 0; i < numCourses; i++) {
if (inDegree[i] === 0) queue.push(i);
}
while (queue.length) {
let course = queue.shift();
result.push(course);
for (let neighbor of graph[course]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return result.length === numCourses ? result : [];
}
这些实现涵盖了您列出的每个问题。代码力求简洁清晰,并使用了适当的数据结构和算法来提高效率。希望对您有所帮助!
以下是用 JavaScript 实现的解决方案:
1. 单源最短路径 (Dijkstra 算法)
javascript
function dijkstra(graph, start) {
let distances = Array(graph.length).fill(Infinity);
distances[start] = 0;
let visited = new Set();
while (visited.size < graph.length) {
let u = -1;
for (let i = 0; i < graph.length; i++) {
if (!visited.has(i) && (u === -1 || distances[i] < distances[u])) {
u = i;
}
}
visited.add(u);
for (let [v, weight] of graph[u]) {
distances[v] = Math.min(distances[v], distances[u] + weight);
}
}
return distances;
}
2. 关键连接 (桥检测)
javascript
function criticalConnections(n, connections) {
let graph = Array.from({ length: n }, () => []);
let discovery = Array(n).fill(-1);
let low = Array(n).fill(-1);
let bridges = [];
let time = 0;
for (let [u, v] of connections) {
graph[u].push(v);
graph[v].push(u);
}
function dfs(node, parent) {
discovery[node] = low[node] = time++;
for (let neighbor of graph[node]) {
if (neighbor === parent) continue;
if (discovery[neighbor] === -1) {
dfs(neighbor, node);
low[node] = Math.min(low[node], low[neighbor]);
if (low[neighbor] > discovery[node]) {
bridges.push([node, neighbor]);
}
} else {
low[node] = Math.min(low[node], discovery[neighbor]);
}
}
}
for (let i = 0; i < n; i++) {
if (discovery[i] === -1) dfs(i, -1);
}
return bridges;
}
3. 判断负权回路 (Bellman-Ford 算法)
javascript
function hasNegativeWeightCycle(n, edges) {
let distances = Array(n).fill(Infinity);
distances[0] = 0;
for (let i = 0; i < n - 1; i++) {
for (let [u, v, weight] of edges) {
if (distances[u] + weight < distances[v]) {
distances[v] = distances[u] + weight;
}
}
}
for (let [u, v, weight] of edges) {
if (distances[u] + weight < distances[v]) {
return true;
}
}
return false;
}
4. 判断图中是否存在环
javascript
function hasCycle(graph) {
let visited = new Set();
let stack = new Set();
function dfs(node) {
if (stack.has(node)) return true;
if (visited.has(node)) return false;
visited.add(node);
stack.add(node);
for (let neighbor of graph[node]) {
if (dfs(neighbor)) return true;
}
stack.delete(node);
return false;
}
for (let node = 0; node < graph.length; node++) {
if (dfs(node)) return true;
}
return false;
}
5. 图的深度优先搜索
javascript
function dfs(graph, start) {
let visited = new Set();
let result = [];
function explore(node) {
if (visited.has(node)) return;
visited.add(node);
result.push(node);
for (let neighbor of graph[node]) {
explore(neighbor);
}
}
explore(start);
return result;
}
6. 克隆图
javascript
function cloneGraph(node) {
if (!node) return null;
let map = new Map();
function clone(node) {
if (map.has(node.val)) return map.get(node.val);
let newNode = { val: node.val, neighbors: [] };
map.set(node.val, newNode);
for (let neighbor of node.neighbors) {
newNode.neighbors.push(clone(neighbor));
}
return newNode;
}
return clone(node);
}
7. 课程表 (拓扑排序)
javascript
function canFinish(numCourses, prerequisites) {
let graph = Array.from({ length: numCourses }, () => []);
let inDegree = Array(numCourses).fill(0);
for (let [course, prereq] of prerequisites) {
graph[prereq].push(course);
inDegree[course]++;
}
let queue = [];
for (let i = 0; i < numCourses; i++) {
if (inDegree[i] === 0) queue.push(i);
}
let count = 0;
while (queue.length) {
let course = queue.shift();
count++;
for (let neighbor of graph[course]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return count === numCourses;
}
8. 最短路径 (Floyd-Warshall 算法)
javascript
function floydWarshall(graph) {
let dist = graph.map(row => [...row]);
let n = graph.length;
for (let k = 0; k < n; k++) {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
return dist;
}
9. 图的广度优先搜索
javascript
function bfs(graph, start) {
let visited = new Set();
let queue = [start];
let result = [];
while (queue.length) {
let node = queue.shift();
if (visited.has(node)) continue;
visited.add(node);
result.push(node);
for (let neighbor of graph[node]) {
if (!visited.has(neighbor)) {
queue.push(neighbor);
}
}
}
return result;
}
这些实现涵盖了您所列出的所有问题。每种算法选择了高效且直观的方式,适合实际问题的解决。希望对您有所帮助!
1. 最小生成树 (Kruskal 算法)
javascript
function kruskal(n, edges) {
edges.sort((a, b) => a[2] - b[2]);
let parent = Array.from({ length: n }, (_, i) => i);
function find(x) {
if (parent[x] !== x) parent[x] = find(parent[x]);
return parent[x];
}
function union(x, y) {
parent[find(x)] = find(y);
}
let mst = [];
for (let [u, v, weight] of edges) {
if (find(u) !== find(v)) {
union(u, v);
mst.push([u, v, weight]);
}
}
return mst;
}
2. 网络延迟时间 (Dijkstra 算法)
javascript
function networkDelayTime(times, n, k) {
let graph = Array.from({ length: n + 1 }, () => []);
for (let [u, v, w] of times) {
graph[u].push([v, w]);
}
let distances = Array(n + 1).fill(Infinity);
distances[k] = 0;
let pq = [[k, 0]];
while (pq.length) {
pq.sort((a, b) => b[1] - a[1]);
let [node, time] = pq.pop();
for (let [neighbor, weight] of graph[node]) {
let newTime = time + weight;
if (newTime < distances[neighbor]) {
distances[neighbor] = newTime;
pq.push([neighbor, newTime]);
}
}
}
let maxTime = Math.max(...distances.slice(1));
return maxTime === Infinity ? -1 : maxTime;
}
3. 最大异或对
javascript
function findMaximumXOR(nums) {
let maxXOR = 0;
let mask = 0;
for (let i = 31; i >= 0; i--) {
mask |= (1 << i);
let prefixes = new Set(nums.map(num => num & mask));
let candidate = maxXOR | (1 << i);
for (let prefix of prefixes) {
if (prefixes.has(prefix ^ candidate)) {
maxXOR = candidate;
break;
}
}
}
return maxXOR;
}
4. 可能的二分法
javascript
function possibleBipartition(n, dislikes) {
let graph = Array.from({ length: n + 1 }, () => []);
for (let [u, v] of dislikes) {
graph[u].push(v);
graph[v].push(u);
}
let colors = Array(n + 1).fill(0);
function dfs(node, color) {
if (colors[node] !== 0) return colors[node] === color;
colors[node] = color;
for (let neighbor of graph[node]) {
if (!dfs(neighbor, -color)) return false;
}
return true;
}
for (let i = 1; i <= n; i++) {
if (colors[i] === 0 && !dfs(i, 1)) return false;
}
return true;
}
5. 课程表
javascript
function canFinish(numCourses, prerequisites) {
let graph = Array.from({ length: numCourses }, () => []);
let inDegree = Array(numCourses).fill(0);
for (let [course, prereq] of prerequisites) {
graph[prereq].push(course);
inDegree[course]++;
}
let queue = [];
for (let i = 0; i < numCourses; i++) {
if (inDegree[i] === 0) queue.push(i);
}
let count = 0;
while (queue.length) {
let course = queue.shift();
count++;
for (let neighbor of graph[course]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return count === numCourses;
}
6. 字符串集合维护
javascript
class StringSet {
constructor() {
this.set = new Set();
}
add(str) {
this.set.add(str);
}
remove(str) {
this.set.delete(str);
}
contains(str) {
return this.set.has(str);
}
}
7. 字符串相似性判断
javascript
function isSimilar(str1, str2) {
if (str1.length !== str2.length) return false;
let diff = 0;
for (let i = 0; i < str1.length; i++) {
if (str1[i] !== str2[i]) diff++;
if (diff > 2) return false;
}
return diff === 2 || diff === 0;
}
8. 集合操作
javascript
class CustomSet {
constructor() {
this.items = new Set();
}
add(item) {
this.items.add(item);
}
remove(item) {
this.items.delete(item);
}
has(item) {
return this.items.has(item);
}
union(otherSet) {
return new Set([...this.items, ...otherSet.items]);
}
intersection(otherSet) {
return new Set([...this.items].filter(item => otherSet.items.has(item)));
}
difference(otherSet) {
return new Set([...this.items].filter(item => !otherSet.items.has(item)));
}
}
9. 账户合并
javascript
function accountsMerge(accounts) {
let emailToName = {};
let graph = {};
for (let account of accounts) {
let name = account[0];
for (let email of account.slice(1)) {
emailToName[email] = name;
if (!graph[email]) graph[email] = [];
graph[account[1]].push(email);
graph[email].push(account[1]);
}
}
let visited = new Set();
let result = [];
function dfs(email, emails) {
if (visited.has(email)) return;
visited.add(email);
emails.push(email);
for (let neighbor of graph[email]) {
dfs(neighbor, emails);
}
}
for (let email in graph) {
if (!visited.has(email)) {
let emails = [];
dfs(email, emails);
result.push([emailToName[email], ...emails.sort()]);
}
}
return result;
}
10. 动物王国中的食物链
实现涉及并查集:
javascript
class UnionFind {
constructor(n) {
this.parent = Array.from({ length: n }, (_, i) => i);
}
find(x) {
if (this.parent[x] !== x) {
this.parent[x] = this.find(this.parent[x]);
}
return this.parent[x];
}
union(x, y) {
this.parent[this.find(x)] = this.find(y);
}
isConnected(x, y) {
return this.find(x) === this.find(y);
}
}
11. 删除链表中重复的元素
javascript
function deleteDuplicates(head) {
let current = head;
while (current && current.next) {
if (current.val === current.next.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
12. 复制带有随机指针的链表
javascript
function copyRandomList(head) {
if (!head) return null;
let map = new Map();
let current = head;
while (current) {
map.set(current, { val: current.val, next: null, random: null });
current = current.next;
}
current = head;
while (current) {
if (current.next) map.get(current).next = map.get(current.next);
if (current.random) map.get(current).random = map.get(current.random);
current = current.next;
}
return map.get(head);
}
以下是用 JavaScript 实现的解决方案:
1. 分割链表
javascript
function partition(head, x) {
let before = new ListNode(0);
let after = new ListNode(0);
let beforeHead = before, afterHead = after;
while (head) {
if (head.val < x) {
before.next = head;
before = before.next;
} else {
after.next = head;
after = after.next;
}
head = head.next;
}
after.next = null;
before.next = afterHead.next;
return beforeHead.next;
}
2. 找到链表中的倒数第 k 个节点
javascript
function findKthFromEnd(head, k) {
let slow = head, fast = head;
while (k > 0 && fast) {
fast = fast.next;
k--;
}
while (fast) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
3. 旋转链表
javascript
function rotateRight(head, k) {
if (!head || !head.next || k === 0) return head;
let length = 1, tail = head;
while (tail.next) {
tail = tail.next;
length++;
}
k = k % length;
if (k === 0) return head;
tail.next = head; // Make it a circular list
let stepsToNewHead = length - k;
while (stepsToNewHead--) {
tail = tail.next;
}
let newHead = tail.next;
tail.next = null;
return newHead;
}
4. LRU 缓存
javascript
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) return -1;
let 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);
}
}
5. 反转链表的部分节点
javascript
function reverseBetween(head, left, right) {
if (!head || left === right) return head;
let dummy = new ListNode(0);
dummy.next = head;
let prev = dummy;
for (let i = 1; i < left; i++) {
prev = prev.next;
}
let curr = prev.next;
for (let i = 0; i < right - left; i++) {
let temp = curr.next;
curr.next = temp.next;
temp.next = prev.next;
prev.next = temp;
}
return dummy.next;
}
6. 判断链表中是否有环
javascript
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;
}
7. 合并两个有序链表
javascript
function mergeTwoLists(l1, l2) {
let 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;
}
8. 排序链表
javascript
function sortList(head) {
if (!head || !head.next) return head;
let mid = getMid(head);
let left = sortList(head);
let right = sortList(mid);
return mergeTwoLists(left, right);
function getMid(head) {
let slow = head, fast = head, prev = null;
while (fast && fast.next) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
if (prev) prev.next = null;
return slow;
}
}
以下是您提到的问题的详细解决方案和解答:
---
### 1. **反转链表**
```javascript
function reverseList(head) {
let prev = null, current = head;
while (current) {
let nextNode = current.next;
current.next = prev;
prev = current;
current = nextNode;
}
return prev;
}
2. 寻找链表的中间节点
javascript
function findMiddle(head) {
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
3. 删除链表中的节点
题目描述: 给定指向某个节点的指针 node
(不是头节点),从链表中删除该节点。
javascript
function deleteNode(node) {
node.val = node.next.val;
node.next = node.next.next;
}
4. 实现两个链表的相加
两个链表表示非负整数,其数字按照逆序存储(个位在头部)。
javascript
function addTwoNumbers(l1, l2) {
let dummy = new ListNode(0);
let current = dummy, carry = 0;
while (l1 || l2 || carry) {
let sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + carry;
carry = Math.floor(sum / 10);
current.next = new ListNode(sum % 10);
current = current.next;
if (l1) l1 = l1.next;
if (l2) l2 = l2.next;
}
return dummy.next;
}
5. 下一个更大元素 (给定一个正整数)
题目描述: 找到给定数字的下一个更大的排列形式,如果不存在返回 -1。
javascript
function nextGreaterElement(n) {
let digits = n.toString().split('').map(Number);
let i = digits.length - 2;
while (i >= 0 && digits[i] >= digits[i + 1]) {
i--;
}
if (i < 0) return -1;
let j = digits.length - 1;
while (digits[j] <= digits[i]) {
j--;
}
[digits[i], digits[j]] = [digits[j], digits[i]];
let left = i + 1, right = digits.length - 1;
while (left < right) {
[digits[left], digits[right]] = [digits[right], digits[left]];
left++;
right--;
}
let result = parseInt(digits.join(''));
return result > 2 ** 31 - 1 ? -1 : result;
}
6. 常见的数据结构有哪些?
常见的数据结构包括:
- 数组(Array) :
- 特点:连续内存存储,支持随机访问。
- 优点:访问速度快。
- 缺点:插入、删除操作需要移动元素。
- 链表(Linked List) :
- 特点:通过指针连接节点。
- 优点:插入、删除操作快。
- 缺点:随机访问性能差。
- 栈(Stack) :
- 特点:后进先出(LIFO)。
- 应用:括号匹配、递归调用。
- 队列(Queue) :
- 特点:先进先出(FIFO)。
- 应用:任务调度、广度优先搜索。
- 哈希表(Hash Table) :
- 特点:通过哈希函数实现快速访问。
- 优点:查找速度快。
- 缺点:可能存在冲突,需要处理。
- 树(Tree) :
- 包括二叉树、二叉搜索树、平衡树、堆等。
- 应用:快速查找、排序。
- 图(Graph) :
- 特点:由顶点和边构成。
- 应用:最短路径、网络流。
7. 平衡树的基本实现,与红黑树的区别是什么?
平衡树的基本实现:
平衡树(如 AVL 树)是二叉搜索树的一种扩展,保证每个节点的左右子树高度差不超过 1。
实现要点:
- 插入或删除节点后,通过旋转操作调整树的平衡。
- 四种旋转:
- LL(左子树左旋转)
- RR(右子树右旋转)
- LR(左子树右旋转)
- RL(右子树左旋转)
红黑树的基本特性:
- 节点颜色为红色或黑色。
- 根节点为黑色。
- 每个红色节点的子节点必须是黑色(无连续两个红色节点)。
- 从根节点到每个叶子节点的路径包含相同数量的黑色节点。
红黑树与 AVL 树的区别:
特性 | 红黑树 | AVL 树 |
---|---|---|
平衡性 | 较弱 | 严格 |
插入删除效率 | 快(调整操作较少) | 慢(调整操作较多) |
查询效率 | 较低(近似平衡) | 较高(严格平衡) |
应用场景 | 常用于动态插入删除的场景 | 常用于查找较多的场景 |
总结:
- AVL 树更加平衡,适合频繁查询的场景。
- 红黑树调整代价低,适合插入和删除频繁的场景。
希望这些内容对您有所帮助!
---