【数据结构】——时间复杂度、空间复杂度(下)

简介: 【数据结构】——时间复杂度、空间复杂度(下)

✨四、时间复杂度

1.时间复杂度表示

因为在不同的硬件电路中,同一个程序编译花费的时间是不同的,因此,计算时间复杂度不能直接计算程序运行花费的时间,而算法中的基本操作的执行次数,就是算法的时间复杂度。时间复杂度用大写字母O加上( )表示,即O( );

2.大O的渐进表示法

推导大O阶方法:

  1. 用常数1取代运行时间中的所有加法常数。
  2. 在修改后的运行次数函数中,只保留最高阶项。
  3. 如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

请计算下列代码的时间复杂度:

//例1
void fun1(int n)
{
    int count = 0;
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
        ++count;
        }
    }
    
    for (int i = 0; i < 2 * n; i++)
    {
        ++count;
    }
    
    int M = 10;
    while (M--)
    {
        ++count;
    }
    printf("%d", count);
}

Func1执行的基本操作次数 :

F(N)=N2+2∗N+10F(N)=N^2+2*N+10 F(N)=N2+2N+10

  • N = 10 F(N) = 130
  • N = 100 F(N) = 10210
  • N = 1000 F(N) = 1002010

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。
因此这个程序的时间复杂度可以表示为:

O(N2)O(N^2) O(N2)


另外有些算法的时间复杂度存在最好、平均和最坏情况:

最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到

在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)


练习题

题目1:

//例1
void fun2(int n)
{
    int count = 0;
    for (int i = 0; i < 2 * n; i++)
    {
        ++count;
    }
    int M = 10;
    while (M--)
    {
        ++count;
    }
    printf("%d", count);
}

此时算法的执行次数是2N+10次,通过推导大O的渐进表示法 时间复杂度是O(N)


题目2

//例2
void fun3(int N,int M)
{
    int count = 0;
    for (int i = 0; i < N; i++)
    {
        ++count;
    }
    for (int i = 0; i < M; i++)
    {
        ++count;
    }
    printf("%d", count);
}

此时算法的执行次数是M+N次,有两个未知数,时间复杂度是O(M+N);
如果给了条件:

  1. M远大于N,时间复杂度是O(M);
  2. M,N大小差不多,时间复杂度是O(M)或O(N);

题目3:

//例3
void fun3(int N, int M)
{
    int count = 0;
    for (int i = 0; i < 100; i++)
    {
        ++count;
    }
    printf("%d", count);
}

执行次数为100次 为是常数,所以时间复杂度为O( 1 )


题目4

//例4
const char* strchr(const char* str, char character)
{
    while (*str != '\0')
    {
        if (*str == character)
            return str;

        ++str;
    }
    return NULL;
}

sttrchr是在一个字符串中查找一个字符,查找次数最好O(1),最坏O(2),根据大O的渐进表示法所以时间复杂度为O(N)


题目5

//例5 冒泡排序
void BUbbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = n; i > end; ++i)
        {
            if (a[i - 1] > a[i])
            {
                Swap(&a[i - 1], &a[i]);
                exchange = 1;
            }
        }
        if (exchange == 0)
            break;
    }
}

因为比较的数字是n-1,n-2,n-3…3,2,1反过来就是1,2,3,…,n-3,n-2,n-1规律就是一个等差数列根据等差数列求和公式(n^2+n)/2,

那么根据大O的渐进表示法就是O(N ^ 2)


题目6

//例6
//有序数组二分查找
int BinarySearch(int* a, int n, int x)
{
    assert(a);
    int begin = 0;
    int end = n;
    while (begin < end)
    {
        int mid = begin + ((end - begin) >> 1);
        if (a[mid] < x)
            begin = mid + 1;
        else if (a[mid] > x)
            end = mid;
        else
            return mid;
    }
    return -1;
}

在一个有序的数组中一直二分直到找到要找的那个数字,那么n/2/2/2…=1,那么就可以变换为2^x=n,那么x就是log以2为底,n的次方,时间复杂度就是

O(log2N)O(log2^N) O(log2N)


题目7

//例7
//求阶乘递归
long long Factorial(size_t N)
{
    return N < 2 ? N : Factorial(N - 1) * N;
}

这题N是不是要递归到1才停止递归,那么就是递归N次。

这个题目求N阶层,那么算法的执行次数就是N,时间复杂度就是O(N)

递归复杂度=递归次数*每次递归函数次数


题目8:

//例8
//求斐波那契数列
long long Fibonacci(size_t N)
{
    return N < 2 ? N : Fibonacci(N - 1) + Fibonacci(N - 2);
}

大O的渐进表示法时间复杂度是O(2^N)

画图分析:

【数据结构】——时间复杂度、空间复杂度_时间复杂度_02

非递归算法 O(n)=n

//非递归写法
int fabio_for(int n)
{
    int f1=1;
    int f2=1;
    int f3=1;
    
    for(int i=2;i<n;i++)
    {
    f3=f1+f2;
    f1=f2;
    f2=f3;
    }
    return f3;
}

⌚五.空间复杂度

1.空间复杂度表示

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,即在函数中有几个临时变量

2.空间复杂度经典题目

题目1

void BUbbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = n; i > end; ++i)
        {
            if (a[i - 1] > a[i])
            {
                Swap(&a[i - 1], &a[i]);
                exchange = 1;
            }
        }
        if (exchange == 0)
            break;
    }
}

使用了5个( 常数 )个额外空间,所以空间复杂度为 O(1)


题目2


// 计算Fibonacci的空间复杂度?
long long* Fibonacci(size_t n)
{
    if (n == 0)
        return NULL;
    //动态开辟了n+1个空间
    long long* fibArray =(long long*)malloc((n + 1) * sizeof(long long)); fibArray[0] = 0;
    
    fibArray[1] = 1; for (int i = 2; i <= n; ++i)
    {
        fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
    }

    return fibArray;
}

这里动态开辟了n+1个空间,所以空间复杂度是O(N)。


题目3:

//求阶乘递归
long long Factorial(size_t N)
{
    return N < 2 ? N : Factorial(N - 1) * N;
}

里递归每次调用一次函数,就会开辟一次栈帧,每个栈帧使用了常数个空间,所以空间复杂度为O(N)


目录
相关文章
|
6月前
|
算法 搜索推荐 程序员
数据结构中时间复杂度的介绍
冒泡排序是通过重复遍历数组,比较并交换相邻元素来排序数组的。因为它包含两层嵌套循环,每层循环的最大迭代次数近似于n,所以时间复杂度是O(n²)。 通过上述分析,我们可以看到不同代码结构对算法性能有着显著的影响。在设计数据结构和算法时,理解并计算时间复杂度是非常重要的,它帮助程序员选择或优化算法,以处理更大的数据集或提高程序的运行速度。
45 2
|
2月前
|
机器学习/深度学习 存储 缓存
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
文章主要介绍了排序算法的分类、时间复杂度的概念和计算方法,以及常见的时间复杂度级别,并简单提及了空间复杂度。
38 1
数据结构与算法学习十:排序算法介绍、时间频度、时间复杂度、常用时间复杂度介绍
|
6月前
|
存储 算法 C语言
数据结构中的空间复杂度
优化空间复杂度对于提升程序性能和资源利用率至关重要,特别是在资源受限的环境(如嵌入式系统和移动设备)中。高效的数据结构和算法设计可以显著提升程序的执行效率和可扩展性。 综上所述,理解和优化空间复杂度是设计高效数据结构和算法的关键。通过分析常见数据结构的空间复杂度,并结合实际代码示例,我们可以更好地理解这一重要概念,并在实际编程中应用这些知识。希望本文能帮助你更好地掌握空间复杂度及其在数据结构中的应用。
47 2
|
2月前
|
机器学习/深度学习 存储 算法
【初阶数据结构】算法效率大揭秘 | 时间与空间复杂度的深度剖析
【初阶数据结构】算法效率大揭秘 | 时间与空间复杂度的深度剖析
|
2月前
|
算法
[数据结构] -- 时间复杂度和空间复杂度
[数据结构] -- 时间复杂度和空间复杂度
16 0
|
4月前
|
存储 算法
【数据结构】——时间复杂度与空间复杂度
【数据结构】——时间复杂度与空间复杂度
|
6月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-2
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
6月前
|
算法 C++
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题-1
【数据结构与算法】:关于时间复杂度与空间复杂度的计算(C/C++篇)——含Leetcode刷题
|
6月前
|
机器学习/深度学习 算法
数据结构入门 时间 空间复杂度解析
数据结构入门 时间 空间复杂度解析
36 0
|
6月前
|
存储 算法 C语言
数据结构和算法——堆排序(选择排序、思路图解、代码、时间复杂度、堆排序及代码)
数据结构和算法——堆排序(选择排序、思路图解、代码、时间复杂度、堆排序及代码)
39 0