【数据结构与算法】空间复杂度从入门到面试:不仅会算,还要会解释

👨‍💻 关于作者:会编程的土豆

"不是因为看见希望才坚持,而是坚持了才看见希望。"

你好,我是会编程的土豆,一名热爱后端技术的Java学习者。

📚 正在更新中的专栏:

💕作者简介:后端学习者

很多人学算法时都会有一个真实问题:时间复杂度还能勉强分析,但空间复杂度一问就容易卡住,尤其一遇到递归、容器就开始不确定。其实空间复杂度并不难,它的核心只有一句话:程序运行过程中,额外占用了多少随输入规模 n 增长的内存

这篇文章不讲空话,直接结合代码,把空间复杂度一层一层讲清楚。


一、最基础:什么是空间复杂度

先看一个最简单的例子:

cpp 复制代码
int sum(int n) {
    int s = 0;
    for (int i = 1; i <= n; i++) {
        s += i;
    }
    return s;
}

这段代码:

  • 只用了 si 两个变量

  • 不管 n 多大,变量数量不变

所以空间复杂度是:

复制代码
O(1)

二、空间"跟着 n 变"的情况

再看一个例子:

cpp 复制代码
vector<int> buildArray(int n) {
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = i;
    }
    return a;
}

这里关键点:

  • vector<int> a(n) 开辟了 n 个空间

  • n 越大,占用空间越大

所以:

复制代码
空间复杂度 = O(n)

三、二维情况:更高维空间

复制代码
vector<vector<int>> matrix(int n) {
    vector<vector<int>> a(n, vector<int>(n));
    return a;
}

这里开了一个 n × n 的二维数组:

复制代码
空间复杂度 = O(n²)

四、最容易忽略的重点:递归栈

来看一个经典递归:

复制代码
int dfs(int n) {
    if (n == 0) return 0;
    return dfs(n - 1) + 1;
}

这个函数会:

复制代码
dfs(n) → dfs(n-1) → dfs(n-2) → ... → dfs(0)

一共调用 n 层,每一层都会占用栈空间。

所以:

复制代码
空间复杂度 = O(n)

再看一个更典型的例子(二叉树)

复制代码
int maxDepth(TreeNode* root) {
    if (!root) return 0;
    return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}

这里递归深度取决于树的高度:

  • 平衡树:高度 ≈ log n → O(log n)

  • 极端链式树:高度 = n → O(n)


五、辅助数据结构的空间

比如 BFS:

cpp 复制代码
vector<vector<int>> levelOrder(TreeNode* root) {
    vector<vector<int>> res;
    if (!root) return res;

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        int n = q.size();
        vector<int> level;

        for (int i = 0; i < n; i++) {
            TreeNode* node = q.front();
            q.pop();
            level.push_back(node->val);

            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }

        res.push_back(level);
    }
    return res;
}

这里用到了:

  • 队列 queue

  • 结果数组 res

最坏情况下:

复制代码
空间复杂度 = O(n)

六、经典案例:堆排序的空间复杂度

很多人会误判这个题。

代码(递归版 heapify)

cpp 复制代码
void heapify(vector<int>& arr, int i, int heapSize) {
    int largest = i;
    int left = 2*i + 1;
    int right = 2*i + 2;

    if (left < heapSize && arr[left] > arr[largest])
        largest = left;
    if (right < heapSize && arr[right] > arr[largest])
        largest = right;

    if (largest != i) {
        swap(arr[i], arr[largest]);
        heapify(arr, largest, heapSize);
    }
}

分析:

  • 没有开新数组 → O(1)

  • 但是有递归 → 深度 ≈ log n

所以:

复制代码
空间复杂度 = O(log n)

优化:改成迭代写法

cpp 复制代码
void heapify(vector<int>& arr, int i, int heapSize) {
    while (true) {
        int largest = i;
        int left = 2*i + 1;
        int right = 2*i + 2;

        if (left < heapSize && arr[left] > arr[largest])
            largest = left;
        if (right < heapSize && arr[right] > arr[largest])
            largest = right;

        if (largest == i) break;

        swap(arr[i], arr[largest]);
        i = largest;
    }
}

此时:

复制代码
空间复杂度 = O(1)

七、常见误区总结

1. 把输入算进去

复制代码
vector<int> nums(n);

这个不算空间复杂度。


2. 忽略递归

复制代码
dfs(n)

必须考虑调用栈。


3. 忽略容器

复制代码
queue / stack / map

这些通常都是 O(n)。


八、通用判断模板(面试直接用)

判断空间复杂度可以按这个顺序:

  1. 有没有 new / vector / 数组 → 看是否 O(n)

  2. 有没有递归 → 看深度(n / log n)

  3. 有没有辅助结构(队列、栈、哈希表)

  4. 取最大的一项


九、一句话总结

空间复杂度本质就是:

复制代码
额外空间 + 递归调用栈

谁增长最快,就看谁。


相关推荐
Dillon Dong2 小时前
【风电控制】TI TMS320F28379D 双CPU架构解析与任务分布设计
嵌入式硬件·算法·变流器·风电控制
NiceCloud喜云7 小时前
Opus 4.8 的 Effort Control 怎么选:Low 到 Max 五档策略
android·java·大数据·前端·c++·python·spring
小羊在睡觉7 小时前
力扣84. 柱状图中最大的矩形
后端·算法·leetcode·golang·go
cjhbachelor7 小时前
c++继承
c++
3DVisionary7 小时前
蓝光三维扫描:医疗制造的精度焦虑怎么解
人工智能·算法·制造·蓝光三维扫描·医疗制造·三维检测·义齿检测
jiayong237 小时前
面试中遇到不熟悉问题的应对策略深度解析
面试·职场和发展
好评笔记7 小时前
机器学习面试八股——常用损失函数
人工智能·深度学习·算法·机器学习·校招
weixin_468466857 小时前
全局与局部注意力机制新手实战指南
人工智能·python·深度学习·算法·自然语言处理·transformer·注意力机制
肩上风骋8 小时前
C++14特性
开发语言·c++·c++14特性
_日拱一卒8 小时前
LeetCode:994腐烂的橘子
java·数据结构·算法·leetcode·深度优先