算法-从0开始-20260408

7 阅读5分钟

算法

一、排序算法

1. 快速排序

问题:快速排序

答案核心回答:快速排序使用分治策略,选择基准元素分区递归排序。

代码示例

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)];
}

// in-place 版本
function quickSortInPlace(arr, low = 0, high = arr.length - 1) {
    if (low < high) {
        const pi = partition(arr, low, high);
        quickSortInPlace(arr, low, pi - 1);
        quickSortInPlace(arr, pi + 1, high);
    }
    return arr;
}

function partition(arr, low, high) {
    const pivot = arr[high];
    let i = low - 1;
    
    for (let j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
    }
    [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
    return i + 1;
}

// 时间复杂度:平均 O(n log n),最坏 O(n²)
// 空间复杂度:O(log n)

2. 归并排序

问题:归并排序

答案核心回答:归并排序使用分治策略,将数组二分递归排序后合并。

代码示例

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) {
        if (left[i] <= right[j]) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    
    return result.concat(left.slice(i)).concat(right.slice(j));
}

// 时间复杂度:O(n log n) 稳定
// 空间复杂度:O(n)

3. 冒泡排序

问题:冒泡排序

答案核心回答:冒泡排序通过相邻元素比较交换,将最大元素冒泡到末尾。

代码示例

function bubbleSort(arr) {
    const n = arr.length;
    for (let i = 0; i < n - 1; i++) {
        let swapped = false;
        for (let j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
                swapped = true;
            }
        }
        if (!swapped) break; // 优化:提前结束
    }
    return arr;
}

// 时间复杂度:O(n²)
// 空间复杂度:O(1)

二、查找算法

4. 二分查找

问题:二分查找

答案核心回答:二分查找通过折半查找目标值,要求数组有序。

代码示例

function binarySearch(arr, target) {
    let left = 0;
    let right = arr.length - 1;
    
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        
        if (arr[mid] === target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    
    return -1; // 未找到
}

// 递归版本
function binarySearchRecursive(arr, target, left = 0, right = arr.length - 1) {
    if (left > right) return -1;
    
    const mid = Math.floor((left + right) / 2);
    
    if (arr[mid] === target) {
        return mid;
    } else if (arr[mid] < target) {
        return binarySearchRecursive(arr, target, mid + 1, right);
    } else {
        return binarySearchRecursive(arr, target, left, mid - 1);
    }
}

// 时间复杂度:O(log n)
// 空间复杂度:O(1)

三、数据结构

5. 栈与队列

问题:栈与队列

答案核心回答:栈是 LIFO,队列是 FIFO。

代码示例

// 栈
class Stack {
    constructor() {
        this.items = [];
    }
    
    push(item) {
        this.items.push(item);
    }
    
    pop() {
        return this.items.pop();
    }
    
    peek() {
        return this.items[this.items.length - 1];
    }
    
    isEmpty() {
        return this.items.length === 0;
    }
}

// 队列
class Queue {
    constructor() {
        this.items = [];
    }
    
    enqueue(item) {
        this.items.push(item);
    }
    
    dequeue() {
        return this.items.shift();
    }
    
    front() {
        return this.items[0];
    }
    
    isEmpty() {
        return this.items.length === 0;
    }
}

// 循环队列
class CircularQueue {
    constructor(k) {
        this.k = k;
        this.queue = new Array(k);
        this.head = -1;
        this.tail = -1;
    }
    
    enqueue(value) {
        if (this.isFull()) return false;
        this.tail = (this.tail + 1) % this.k;
        this.queue[this.tail] = value;
        if (this.head === -1) this.head = this.tail;
        return true;
    }
    
    dequeue() {
        if (this.isEmpty()) return false;
        if (this.head === this.tail) {
            this.head = -1;
            this.tail = -1;
        } else {
            this.head = (this.head + 1) % this.k;
        }
        return true;
    }
}

6. 链表

问题:链表

答案核心回答:链表是线性数据结构,通过指针连接节点。

代码示例

class ListNode {
    constructor(val) {
        this.val = val;
        this.next = null;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
        this.size = 0;
    }
    
    append(val) {
        const node = new ListNode(val);
        if (!this.head) {
            this.head = node;
        } else {
            let curr = this.head;
            while (curr.next) {
                curr = curr.next;
            }
            curr.next = node;
        }
        this.size++;
    }
    
    remove(index) {
        if (index < 0 || index >= this.size) return;
        
        if (index === 0) {
            this.head = this.head.next;
        } else {
            let prev = this.head;
            for (let i = 0; i < index - 1; i++) {
                prev = prev.next;
            }
            prev.next = prev.next.next;
        }
        this.size--;
    }
    
    reverse() {
        let prev = null;
        let curr = this.head;
        
        while (curr) {
            const next = curr.next;
            curr.next = prev;
            prev = curr;
            curr = next;
        }
        
        this.head = prev;
    }
}

7. 哈希表

问题:哈希表

答案核心回答:哈希表通过哈希函数将键映射到值,实现 O(1) 查找。

代码示例

class HashMap {
    constructor(size = 100) {
        this.size = size;
        this.buckets = new Array(size).fill(null).map(() => []);
    }
    
    _hash(key) {
        let hash = 0;
        for (const char of key) {
            hash = (hash * 31 + char.charCodeAt(0)) % this.size;
        }
        return hash;
    }
    
    set(key, value) {
        const index = this._hash(key);
        const bucket = this.buckets[index];
        
        for (const [k, v] of bucket) {
            if (k === key) {
                bucket[bucket.indexOf([k, v])] = [key, value];
                return;
            }
        }
        
        bucket.push([key, value]);
    }
    
    get(key) {
        const index = this._hash(key);
        const bucket = this.buckets[index];
        
        for (const [k, v] of bucket) {
            if (k === key) return v;
        }
        
        return undefined;
    }
    
    delete(key) {
        const index = this._hash(key);
        const bucket = this.buckets[index];
        
        for (const [k, v] of bucket) {
            if (k === key) {
                bucket.splice(bucket.indexOf([k, v]), 1);
                return true;
            }
        }
        
        return false;
    }
}

四、算法思想

8. 动态规划

问题:动态规划

答案核心回答:动态规划通过保存子问题结果,避免重复计算。

代码示例

// 斐波那契数列
function fibonacci(n) {
    if (n <= 1) return n;
    
    const dp = new Array(n + 1);
    dp[0] = 0;
    dp[1] = 1;
    
    for (let i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    
    return dp[n];
}

// 空间优化
function fibonacciOptimized(n) {
    if (n <= 1) return n;
    let prev = 0, curr = 1;
    for (let i = 2; i <= n; i++) {
        [prev, curr] = [curr, prev + curr];
    }
    return curr;
}

// 爬楼梯
function climbStairs(n) {
    if (n <= 2) return n;
    let prev = 1, curr = 2;
    for (let i = 3; i <= n; i++) {
        [prev, curr] = [curr, prev + curr];
    }
    return curr;
}

// 最长公共子序列
function lcs(text1, text2) {
    const m = text1.length, n = text2.length;
    const dp = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
    
    for (let i = 1; i <= m; i++) {
        for (let j = 1; j <= n; j++) {
            if (text1[i - 1] === text2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    
    return dp[m][n];
}

9. 贪心算法

问题:贪心算法

答案核心回答:贪心算法每步选择局部最优解,期望得到全局最优。

代码示例

// 找零钱问题
function coinChange(coins, amount) {
    coins.sort((a, b) => b - a);
    let count = 0;
    let remaining = amount;
    
    for (const coin of coins) {
        while (remaining >= coin) {
            remaining -= coin;
            count++;
        }
    }
    
    return remaining === 0 ? count : -1;
}

// 活动选择问题
function activitySelection(activities) {
    // 按结束时间排序
    activities.sort((a, b) => a[1] - b[1]);
    
    const selected = [activities[0]];
    let lastEnd = activities[0][1];
    
    for (let i = 1; i < activities.length; i++) {
        if (activities[i][0] >= lastEnd) {
            selected.push(activities[i]);
            lastEnd = activities[i][1];
        }
    }
    
    return selected;
}

10. 回溯算法

问题:回溯算法

答案核心回答:回溯通过尝试分步解决问题,不可行时退回上一步。

代码示例

// 全排列
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;
            
            path.push(nums[i]);
            used[i] = true;
            backtrack(path, used);
            path.pop();
            used[i] = false;
        }
    }
    
    backtrack([], new Array(nums.length).fill(false));
    return result;
}

// 子集
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;
}

// N 皇后
function solveNQueens(n) {
    const result = [];
    const board = new Array(n).fill('.'.repeat(n));
    
    function isValid(row, col, board) {
        for (let i = 0; i < row; i++) {
            if (board[i][col] === 'Q') return false;
        }
        for (let i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board[i][j] === 'Q') return false;
        }
        for (let i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board[i][j] === 'Q') return false;
        }
        return true;
    }
    
    function backtrack(row, board) {
        if (row === n) {
            result.push(board.map(r => r.join('')));
            return;
        }
        
        for (let col = 0; col < n; col++) {
            if (!isValid(row, col, board)) continue;
            
            board[row][col] = 'Q';
            backtrack(row + 1, board);
            board[row][col] = '.';
        }
    }
    
    backtrack(0, board);
    return result;
}