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

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

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

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

📚 正在更新中的专栏:

💕作者简介:后端学习者

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

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


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

先看一个最简单的例子:

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

这段代码:

  • 只用了 s 和 i 两个变量

  • 不管 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. 取最大的一项


九、一句话总结

空间复杂度本质就是:

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

谁增长最快,就看谁。


相关推荐
不要打扰75625 分钟前
《房地产经纪操作实务》重点和难点是什么?用哪个题库好
经验分享·职场和发展
YangYang9YangYan37 分钟前
2027 届秋招求职|金融科技背景,反欺诈岗位面试案例题考点拆解
科技·面试·金融
All for pursuit.44 分钟前
【动态规划-6】96.不同的二叉搜索树
数据结构·c++·算法·leetcode·动态规划
积跬步以至千里1 小时前
Agent 工程师学习线路 — 核心知识点总结
面试
rou1 小时前
Attention(注意力机制)
算法
信奥卷王1 小时前
[GESP202609 六级] 数组划分
数据结构·算法
大鹏的NLP博客2 小时前
C++ 内存安全的未来:从 Safety Profiles 到 Safe C++
c++·内存安全
牧羊人.3332 小时前
动手学深度学习 07|Resnet网络与迁移学习
网络·人工智能·深度学习·神经网络·算法·cnn·迁移学习
程序员zgh2 小时前
C++ erase() 函数用法
c语言·开发语言·c++·学习
All for pursuit.3 小时前
【并查集-2】399.除法求值
数据结构·c++·算法·leetcode