01 数据结构与算法——复杂度分析

一、时间复杂度

1、推算方法

  • 统计完整的操作数量 T ( n ) = 2 n 2 + 7 n + 3 T(n) = 2n^2+7n+3 T(n)=2n2+7n+3
  • 忽略常数项和非常数项系数 T ( n ) = n 2 + n T(n) = n^2+n T(n)=n2+n
  • 保留最高阶项 T ( n ) = n 2 T(n) = n^2 T(n)=n2
  • 时间复杂度为 O ( n 2 ) O(n^2) O(n2)
void algorithm(int n) 
{
    int a = 1;  // +1
    a = a + n;  // +1 
    for (int i = 0; i < 5 * n + 1; i++) 
    {
        cout << 0 << endl;	// +n
    }
    for (int i = 0; i < 2 * n; i++) 
    {
        for (int j = 0; j < n + 1; j++) 
        {
            cout << 0 << endl;	// +n*n
        }
    }
}

2、复杂度比较

在这里插入图片描述
在这里插入图片描述

3、常见类型

  • 常数阶 O ( 1 ) O(1) O(1)
    操作数量与输入数据大小n无关,不随n的变化而变化
// 1+1+100000
int constant(int n) 
{
    int count = 0;
    int size = 100000;
    for (int i = 0; i < size; i++)
        count++;
    return count;
}
  • 线性阶 O ( n ) O(n) O(n)
    操作数量相对于输入数据大小n以线性级别增长
// 1+n
int linear(int n) 
{
    int count = 0;
    for (int i = 0; i < n; i++)
        count++;
    return count;
}
  • 平方阶 O ( n 2 ) O(n^2) O(n2)
    操作数量相对于输入数据大小n以平方级别增长
// 1+n*n
int quadratic(int n) 
{
    int count = 0;
    for (int i = 0; i < n; i++) 
    {
        for (int j = 0; j < n; j++) 
        {
            count++;
        }
    }
    return count;
}
  • 指数阶 O ( 2 n ) O(2^n) O(2n)
    在每次循环中一分为二
// 指数阶的循环实现
int exponential(int n) 
{
    int count = 0, base = 1;
    for (int i = 0; i < n; i++) 
    {
        for (int j = 0; j < base; j++) 
        {
            count++;
        }
        base *= 2;	// 一分为二
    }
    return count;
}
// 指数阶的递归实现
int expRecur(int n) 
{
    if (n == 1)
        return 1;
    return expRecur(n - 1) + expRecur(n - 1) + 1;	// 一分为二
}
  • 对数阶 O ( l o g ( n ) ) O(log(n)) O(log(n))
    在每次循环中缩减一半
// 对数阶的循环实现
int logarithmic(int n) 
{
    int count = 0;
    while (n > 1) 
    {
        n = n / 2;	// 缩减一半
        count++;
    }
    return count;
}
  • 线性对数阶 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
    嵌套循环,两层循环的时间复杂度分别为 O ( l o g ( n ) ) O(log(n)) O(log(n)) O ( n ) O(n) O(n)
int linearLogRecur(int n) 
{
    if (n <= 1)
        return 1;
    int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);	// O(log(n))
    
    for (int i = 0; i < n; i++) 
    {
        count++;	// O(n)
    }
    return count;
}
  • 阶乘阶 O ( n ! ) O(n!) O(n!)
    给定n个互不重复的元素,求其所有可能的排列方案
int factorialRecur(int n) 
{
    if (n == 0)
        return 1;
    int count = 0;
    // 从 1 个分出 n 个递归
    for (int i = 0; i < n; i++) 
    {
        count += factorialRecur(n - 1);
    }
    return count;
}

二、空间复杂度

1、运行空间分类

  • 输入空间:用于存储算法的输入数据。
  • 暂存空间:用于存储算法在运行过程中的变量、对象、函数上下文等数据。
    a.暂存数据:用于保存算法运行过程中的各种常量、变量、对象等。
    b.栈帧空间:用于保存调用函数的上下文数据。
    c.指令空间:用于保存编译后的程序指令,在实际统计中忽略不计。
  • 输出空间:用于存储算法的输出数据。
    在这里插入图片描述

2、推算方法

  • 通常只关注最差空间复杂度
  • 以最差输入数据为准
  • 以算法运行中的峰值内存为准

3、常见类型

  • 常数阶 O ( 1 ) O(1) O(1)
    常见于元素数量与输入数据大小n无关的常量、变量、对象
void constant(int n) 
{
    // 常量、变量、对象占用 O(1) 空间
    const int a = 0;
    int b = 0;
    vector<int> nums(10000);
    ListNode node(0);
    // 循环中的变量占用 O(1) 空间
    for (int i = 0; i < n; i++) 
    {
        int c = 0;
    }
    // 循环中的函数占用 O(1) 空间
    for (int i = 0; i < n; i++) 
    {
        func();
    }
}
  • 线性阶 O ( n ) O(n) O(n)
    常见于元素数量与n成正比的数组、链表、栈、队列等
void linear(int n) 
{
    // 长度为 n 的数组占用 O(n) 空间
    vector<int> nums(n);
    // 长度为 n 的列表占用 O(n) 空间
    vector<ListNode> nodes;
    for (int i = 0; i < n; i++) 
    {
        nodes.push_back(ListNode(i));
    }
    // 长度为 n 的哈希表占用 O(n) 空间
    unordered_map<int, string> map;
    for (int i = 0; i < n; i++) 
    {
        map[i] = to_string(i);
    }
}
  • 平方阶 O ( n 2 ) O(n^2) O(n2)
    常见于矩阵和图,元素数量与n成平方关系
/* 平方阶 */
void quadratic(int n) 
{
    // 二维列表占用 O(n^2) 空间
    vector<vector<int>> numMatrix;
    for (int i = 0; i < n; i++) 
    {
        vector<int> tmp;
        for (int j = 0; j < n; j++) 
        {
            tmp.push_back(0);
        }
        numMatrix.push_back(tmp);
    }
}
// 平方阶的递归实现 
int quadraticRecur(int n) 
{
    if (n <= 0)
        return 0;
    vector<int> nums(n);
    return quadraticRecur(n - 1);
}
  • 指数阶 O ( 2 n ) O(2^n) O(2n)
    常见于二叉树
// 指数阶,建立满二叉树
TreeNode *buildTree(int n) 
{
    if (n == 0)
        return nullptr;
    TreeNode *root = new TreeNode(0);
    root->left = buildTree(n - 1);
    root->right = buildTree(n - 1);
    return root;
}
  • 对数阶 O ( l o g ( n ) ) O(log(n)) O(log(n))
    常见于分治算法
    例如归并排序,输入长度为n的数组,每轮递归将数组从中点处划分为两半,形成高度为 l o g ( n ) log(n) log(n) 的递归树,使用 O ( l o g ( n ) ) O(log(n)) O(log(n))栈帧空间

参考https://www.hello-algo.com

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值