算法总结

raw/articles/2026/05/算法总结

一句话总结

数据结构的本质是数组和链表的变换;算法的本质是穷举,关键是"无遗漏、无冗余"。


核心思想

一、数据结构的本质

所有数据结构皆为数组(顺序存储)和链表(链式存储)的变换

1.1 存储方式只有两种

数据结构 底层实现 特点
队列、栈 数组或链表 数组实现需处理扩容缩容;链表实现无需扩容但多内存开销
邻接表(链表)或邻接矩阵(二维数组) 邻接矩阵判断连通性快但耗费空间;邻接表节省空间
哈希表 大数组 + 散列函数 拉链法需链表特性;线性探查法需数组特性
数组(堆)或链表(二叉搜索树等) 完全二叉树适合数组;普通树适合链表

1.2 基本操作只有两种

所有数据结构的基本操作 = 遍历 + 访问(增删查改)

遍历方式只有两种:

  • 线性:for/while 迭代
  • 非线性:递归

1.3 数组 vs 链表

特性 数组 链表
存储方式 连续存储 不连续,靠指针连接
随机访问 O(1) 不支持
扩容 需重新分配 + 复制 O(N) 无此问题
插入/删除中间 O(N) 需搬移 O(1) 已知前后驱时
存储空间 较节省 需额外指针空间

二、算法的本质

算法的本质 = 穷举

2.1 穷举的两个关键

关键 问题 原因
无遗漏 遗漏导致答案错误 对算法框架掌握不到位
无冗余 重复计算拖慢速度 没有充分利用信息

2.2 如何穷举?

难点在"如何穷举"的算法:回溯算法、动态规划

  • 回溯算法:"遍历"的思维,抽象成树结构,用代码遍历树的所有节点
  • 动态规划:"分解问题"的思维,把问题分解为子问题,利用数学归纳法找状态转移方程

2.3 如何聪明地穷举?

难点在"如何聪明地穷举"的算法:二分搜索、滑动窗口、并查集、贪心算法

  • 二分搜索:有序数组中用 O(logN) 时间搜索,而非 O(N) 暴力遍历
  • 滑动窗口:用快慢双指针在 O(N) 时间内找到子数组答案,而非 O(N²)
  • 并查集:用数组模拟树结构,将连通性操作优化到 O(1)
  • 贪心算法:利用贪心选择性质,不用完整穷举所有解

代码框架模板

一、数据结构遍历框架

1.1 数组遍历(线性迭代)

void traverse(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        // 迭代访问 arr[i]
    }
}

1.2 链表遍历(迭代 + 递归)

// 基本的单链表节点
class ListNode {
    int val;
    ListNode next;
}

// 迭代遍历
void traverse(ListNode head) {
    for (ListNode p = head; p != null; p = p.next) {
        // 迭代访问 p.val
    }
}

// 递归遍历
void traverse(ListNode head) {
    if (head == null) return;
    // 递归访问 head.val
    traverse(head.next);
}

1.3 二叉树遍历(递归框架)

// 基本的二叉树节点
class TreeNode {
    int val;
    TreeNode left, right;
}

// 二叉树递归遍历框架
void traverse(TreeNode root) {
    if (root == null) return;
    // 前序位置
    traverse(root.left);
    // 中序位置
    traverse(root.right);
    // 后序位置
}

1.4 N 叉树遍历

// 基本的 N 叉树节点
class TreeNode {
    int val;
    TreeNode[] children;
}

void traverse(TreeNode root) {
    if (root == null) return;
    for (TreeNode child : root.children) {
        traverse(child);
    }
}

1.5 图遍历(DFS/BFS)

// DFS 遍历(递归)
boolean[] visited;
void dfs(int node, List<Integer>[] graph) {
    if (visited[node]) return;
    visited[node] = true;
    for (int neighbor : graph[node]) {
        dfs(neighbor, graph);
    }
}

// BFS 遍历(队列)
void bfs(int start, List<Integer>[] graph) {
    boolean[] visited = new boolean[graph.length];
    Queue<Integer> q = new LinkedList<>();
    q.offer(start);
    visited[start] = true;
    
    while (!q.isEmpty()) {
        int cur = q.poll();
        for (int neighbor : graph[cur]) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                q.offer(neighbor);
            }
        }
    }
}

二、算法框架

2.1 回溯算法框架(遍历思维)

class Solution {
    List<List<Integer>> res = new LinkedList<>();
    LinkedList<Integer> track = new LinkedList<>();
    boolean[] used;

    List<List<Integer>> permute(int[] nums) {
        used = new boolean[nums.length];
        backtrack(nums);
        return res;
    }

    void backtrack(int[] nums) {
        // 到达叶子节点,收集结果
        if (track.size() == nums.length) {
            res.add(new LinkedList(track));
            return;
        }
        
        for (int i = 0; i < nums.length; i++) {
            if (used[i]) continue;
            
            // 做选择
            track.add(nums[i]);
            used[i] = true;
            
            // 进入下一层递归
            backtrack(nums);
            
            // 撤销选择
            track.removeLast();
            used[i] = false;
        }
    }
}

核心:回溯 = 多叉树遍历,把问题抽象成树结构,遍历所有叶子节点。

2.2 动态规划框架(分解问题思维)

// 定义:dp(coins, amount) 表示凑出 amount 所需的最少硬币数
int dp(int[] coins, int amount) {
    // base case
    if (amount == 0) return 0;
    if (amount < 0) return -1;
    
    int res = Integer.MAX_VALUE;
    for (int coin : coins) {
        int subProblem = dp(coins, amount - coin);
        if (subProblem == -1) continue;
        res = Math.min(res, subProblem + 1);
    }
    
    return res == Integer.MAX_VALUE ? -1 : res;
}

// 加备忘录优化(自顶向下)
int dp(int[] coins, int amount, Map<Integer, Integer> memo) {
    if (amount == 0) return 0;
    if (amount < 0) return -1;
    if (memo.containsKey(amount)) return memo.get(amount);
    
    int res = Integer.MAX_VALUE;
    for (int coin : coins) {
        int subProblem = dp(coins, amount - coin, memo);
        if (subProblem == -1) continue;
        res = Math.min(res, subProblem + 1);
    }
    
    res = res == Integer.MAX_VALUE ? -1 : res;
    memo.put(amount, res);
    return res;
}

核心:动态规划 = 分解问题,明确函数定义,用数学归纳法找状态转移方程。

2.3 BFS 算法框架(层序遍历)

// 层序遍历二叉树(也是 BFS 基础框架)
void levelTraverse(TreeNode root) {
    if (root == null) return;
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    
    int depth = 0;
    while (!q.isEmpty()) {
        int sz = q.size();  // 关键:必须在循环前保存队列长度
        for (int i = 0; i < sz; i++) {
            TreeNode cur = q.poll();
            // 访问 cur 节点
            
            if (cur.left != null) q.offer(cur.left);
            if (cur.right != null) q.offer(cur.right);
        }
        depth++;
    }
}

核心:BFS = 层序遍历,用队列按层级扩散,适合求最短路径。

2.4 滑动窗口框架

void slidingWindow(String s) {
    Map<Character, Integer> window = new HashMap<>();
    int left = 0, right = 0;
    
    while (right < s.length()) {
        char c = s.charAt(right);
        right++;
        // 增大窗口:更新窗口数据
        window.put(c, window.getOrDefault(c, 0) + 1);
        
        while (windowNeedsShrink()) {  // 窗口需要收缩的条件
            char d = s.charAt(left);
            left++;
            // 缩小窗口:更新窗口数据
            window.put(d, window.get(d) - 1);
        }
    }
}

2.5 二分搜索框架

int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        }
    }
    return -1;
}

总结

  1. 数据结构:数组 + 链表 → 所有数据结构都是它们的变换
  2. 算法:穷举 → 如何无遗漏?如何无冗余?
  3. 框架思维:所有算法都有固定框架,套用框架 + 填充细节
  4. 两种思维模式
    • 遍历思维(回溯算法):遍历树的所有节点
    • 分解问题思维(动态规划):把问题分解为子问题

相关概念

  • 数组 - 顺序存储的基础
  • 链表 - 链式存储的基础
  • 二叉树 - 大多数高级数据结构的基础
  • DFS - 深度优先搜索,回溯算法的基础
  • BFS - 广度优先搜索,层序遍历的延伸
  • 并查集 - 聪明地穷举的典型例子
  • 穷举 - 算法的本质

Interactive Graph