Javascript数据结构常见面试题目(全)

以下是一个 前端 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. 常见的数据结构有哪些?

常见的数据结构包括:

  1. 数组(Array)
    • 特点:连续内存存储,支持随机访问。
    • 优点:访问速度快。
    • 缺点:插入、删除操作需要移动元素。
  2. 链表(Linked List)
    • 特点:通过指针连接节点。
    • 优点:插入、删除操作快。
    • 缺点:随机访问性能差。
  3. 栈(Stack)
    • 特点:后进先出(LIFO)。
    • 应用:括号匹配、递归调用。
  4. 队列(Queue)
    • 特点:先进先出(FIFO)。
    • 应用:任务调度、广度优先搜索。
  5. 哈希表(Hash Table)
    • 特点:通过哈希函数实现快速访问。
    • 优点:查找速度快。
    • 缺点:可能存在冲突,需要处理。
  6. 树(Tree)
    • 包括二叉树、二叉搜索树、平衡树、堆等。
    • 应用:快速查找、排序。
  7. 图(Graph)
    • 特点:由顶点和边构成。
    • 应用:最短路径、网络流。

7. 平衡树的基本实现,与红黑树的区别是什么?

平衡树的基本实现:

平衡树(如 AVL 树)是二叉搜索树的一种扩展,保证每个节点的左右子树高度差不超过 1。

实现要点:

  1. 插入或删除节点后,通过旋转操作调整树的平衡。
  2. 四种旋转:
    • LL(左子树左旋转)
    • RR(右子树右旋转)
    • LR(左子树右旋转)
    • RL(右子树左旋转)
红黑树的基本特性:
  1. 节点颜色为红色或黑色。
  2. 根节点为黑色。
  3. 每个红色节点的子节点必须是黑色(无连续两个红色节点)。
  4. 从根节点到每个叶子节点的路径包含相同数量的黑色节点。
红黑树与 AVL 树的区别:
特性 红黑树 AVL 树
平衡性 较弱 严格
插入删除效率 快(调整操作较少) 慢(调整操作较多)
查询效率 较低(近似平衡) 较高(严格平衡)
应用场景 常用于动态插入删除的场景 常用于查找较多的场景
总结:
  • AVL 树更加平衡,适合频繁查询的场景。
  • 红黑树调整代价低,适合插入和删除频繁的场景。

希望这些内容对您有所帮助!

---
相关推荐
愚愚是个大笨蛋16 分钟前
自定义VUE指定,实现鼠标悬停显示提示面板,离开元素或面板后面板消失
前端·javascript·vue.js
CSNMD18 分钟前
VueRouter之HelloWorld
前端·javascript·vue.js
️○-1 小时前
React之从0开始(3)
前端·javascript·react.js
计算机萍萍学姐1 小时前
ArrayList和LinkedList的区别是什么?
java·数据结构·算法
练习两年半的工程师1 小时前
使用React Redux实现异步请求
前端·javascript·react.js
screct_demo1 小时前
详细讲一下React中的路由React Router
前端·javascript·react.js
【D'accumulation】1 小时前
深入聊聊typescript、ES6和JavaScript的关系与前瞻技术发展
java·开发语言·前端·javascript·typescript·es6
亿点鸭梨1 小时前
vue使用el-select下拉框自定义复选框
前端·javascript·vue.js
T.S622 小时前
JavaScript高级程序设计基础(十一)
前端·javascript·ecmascript
程序员南飞2 小时前
算法-excel表头位置转成对应数字 26进制转10进制
java·javascript·算法