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

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

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

你好,我是会编程的土豆,一名热爱后端技术的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. 取最大的一项


九、一句话总结

空间复杂度本质就是:

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

谁增长最快,就看谁。


相关推荐
luj_17682 小时前
残熵算法实时化三大瓶颈突破
c语言·开发语言·网络·经验分享·算法
mCell3 小时前
你以为短链接只是 Hash + 301/302?
后端·算法·架构
怕浪猫3 小时前
现在的"AI 原生应用"热潮,恰恰证明了大多数产品需求本身就是个草台班子
面试
远离UE44 小时前
UE5 compute shader 原子加
开发语言·c++·ue5
C+-C资深大佬4 小时前
C++ 显式类型转换详解:static_cast、dynamic_cast、const_cast、reinterpret_cast
开发语言·c++
码少女4 小时前
数据结构——哈希表的基础
数据结构·散列表
KaMeidebaby5 小时前
卡梅德生物技术快报|抗体亲和力成熟工业化调控新机制:差异性浆细胞增殖工艺优化思路
java·开发语言·人工智能·算法·机器学习·架构·spark
luj_17685 小时前
心形曲线轨迹控制三大关键技术
c语言·开发语言·c++·经验分享·算法
取地址符5 小时前
C++学习笔记(基于learn-cxx)(1)
c++·经验分享·笔记·学习
tmlx3I0815 小时前
高光谱拼接算法(六)RANSAC 误匹配剔除
人工智能·算法·机器学习