标签导航:

javascript 面试备忘单 - 第 2 部分

常见 leetcode 模式

// two pointers - in-place array modification
const modifyarray = (arr) => {
    let writepointer = 0;
    for (let readpointer = 0; readpointer < arr.length; readpointer++) {
        if (/* condition */) {
            [arr[writepointer], arr[readpointer]] = [arr[readpointer], arr[writepointer]];
            writepointer++;
        }
    }
    return writepointer; // often returns new length or modified position
};

// fast and slow pointers (floyd's cycle detection)
const 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;
};

// sliding window - fixed size
const fixedslidingwindow = (arr, k) => {
    let sum = 0;
    // initialize first window
    for (let i = 0; i < k; i++) {
        sum += arr[i];
    }

    let maxsum = sum;
    // slide window
    for (let i = k; i < arr.length; i++) {
        sum = sum - arr[i - k] + arr[i];
        maxsum = math.max(maxsum, sum);
    }
    return maxsum;
};

// sliding window - variable size
const varslidingwindow = (arr, target) => {
    let start = 0, sum = 0, minlen = infinity;

    for (let end = 0; end < arr.length; end++) {
        sum += arr[end];
        while (sum >= target) {
            minlen = math.min(minlen, end - start + 1);
            sum -= arr[start];
            start++;
        }
    }

    return minlen === infinity ? 0 : minlen;
};

// bfs - level order traversal
const levelorder = (root) => {
    if (!root) return [];
    const result = [];
    const queue = [root];

    while (queue.length) {
        const levelsize = queue.length;
        const currentlevel = [];

        for (let i = 0; i < levelsize; i++) {
            const node = queue.shift();
            currentlevel.push(node.val);

            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        result.push(currentlevel);
    }
    return result;
};

// dfs - recursive template
const dfs = (root) => {
    const result = [];

    const traverse = (node) => {
        if (!node) return;

        // pre-order
        result.push(node.val);

        traverse(node.left);
        // in-order would be here
        traverse(node.right);
        // post-order would be here
    };

    traverse(root);
    return result;
};

// backtracking template
const backtrack = (nums) => {
    const result = [];

    const bt = (path, choices) => {
        if (/* ending condition */) {
            result.push([...path]);
            return;
        }

        for (let i = 0; i < choices.length; i++) {
            // make choice
            path.push(choices[i]);
            // recurse
            bt(path, /* remaining choices */);
            // undo choice
            path.pop();
        }
    };

    bt([], nums);
    return result;
};

// dynamic programming - bottom up template
const dpbottomup = (n) => {
    const dp = new array(n + 1).fill(0);
    dp[0] = 1; // base case

    for (let i = 1; i <= n; i++) {
        for (let j = 0; j < i; j++) {
            dp[i] += dp[j] * /* some calculation */;
        }
    }

    return dp[n];
};

// dynamic programming - top down template
const dptopdown = (n) => {
    const memo = new map();

    const dp = (n) => {
        if (n <= 1) return 1;
        if (memo.has(n)) return memo.get(n);

        let result = 0;
        for (let i = 0; i < n; i++) {
            result += dp(i) * /* some calculation */;
        }

        memo.set(n, result);
        return result;
    };

    return dp(n);
};

// monotonic stack template
const monotonicstack = (arr) => {
    const stack = []; // [index, value]
    const result = new array(arr.length).fill(-1);

    for (let i = 0; i < arr.length; i++) {
        while (stack.length && stack[stack.length - 1][1] > arr[i]) {
            const [previndex, _] = stack.pop();
            result[previndex] = i - previndex;
        }
        stack.push([i, arr[i]]);
    }
    return result;
};

// prefix sum
const prefixsum = (arr) => {
    const prefix = [0];
    for (let i = 0; i < arr.length; i++) {
        prefix.push(prefix[prefix.length - 1] + arr[i]);
    }
    // sum of range [i, j] = prefix[j + 1] - prefix[i]
    return prefix;
};

// binary search variations
const binarysearchleftmost = (arr, target) => {
    let left = 0, right = arr.length;
    while (left < right) {
        const mid = math.floor((left + right) / 2);
        if (arr[mid] < target) left = mid + 1;
        else right = mid;
    }
    return left;
};

const binarysearchrightmost = (arr, target) => {
    let left = 0, right = arr.length;
    while (left < right) {
        const mid = math.floor((left + right) / 2);
        if (arr[mid] <= target) left = mid + 1;
        else right = mid;
    }
    return left - 1;
};

// trie operations
class trienode {
    constructor() {
        this.children = new map();
        this.isendofword = false;
    }
}

class trie {
    constructor() {
        this.root = new trienode();
    }

    insert(word) {
        let node = this.root;
        for (const char of word) {
            if (!node.children.has(char)) {
                node.children.set(char, new trienode());
            }
            node = node.children.get(char);
        }
        node.isendofword = true;
    }

    search(word) {
        let node = this.root;
        for (const char of word) {
            if (!node.children.has(char)) return false;
            node = node.children.get(char);
        }
        return node.isendofword;
    }

    startswith(prefix) {
        let node = this.root;
        for (const char of prefix) {
            if (!node.children.has(char)) return false;
            node = node.children.get(char);
        }
        return true;
    }
}

// union find (disjoint set)
class unionfind {
    constructor(n) {
        this.parent = array.from({length: n}, (_, i) => i);
        this.rank = new array(n).fill(0);
    }

    find(x) {
        if (this.parent[x] !== x) {
            this.parent[x] = this.find(this.parent[x]); // path compression
        }
        return this.parent[x];
    }

    union(x, y) {
        let rootx = this.find(x);
        let rooty = this.find(y);

        if (rootx !== rooty) {
            if (this.rank[rootx] < this.rank[rooty]) {
                [rootx, rooty] = [rooty, rootx];
            }
            this.parent[rooty] = rootx;
            if (this.rank[rootx] === this.rank[rooty]) {
                this.rank[rootx]++;
            }
        }
    }
}

常见的时间/空间复杂度模式

// O(1) - Constant
Array.push(), Array.pop(), Map.set(), Map.get()

// O(log n) - Logarithmic
Binary Search, Balanced BST operations

// O(n) - Linear
Array traversal, Linear Search

// O(n log n) - Linearithmic
Efficient sorting (Array.sort())

// O(n²) - Quadratic
Nested loops, Simple sorting algorithms

// O(2ⁿ) - Exponential
Recursive solutions without memoization