数据与结构--时间复杂度和空间复杂度

一.什么是时间复杂度

    当我第一次看"时间复杂度"这一概念的时候,首先所想到的是代码运行的时间越短,则该代码运行效率越高,但后来仔细一想,如果同样一段代码在不同的电脑上运行,有的电脑配置高,有的电脑配置低,运行的时间自然也是不相同的,这无疑是一种不公平的比较方法。

      首先,为什么引进时间复杂度这一概念呢?

引入时间复杂度这一概念的主要原因是为了帮助我们评估和比较不同算法的性能。在解决同一个问题时,可能存在多种不同的算法,它们在执行效率上可能有很大的差异。时间复杂度提供了一种标准化的方法来量化算法的执行时间,从而使我们能够更好地理解算法的性能特征。

     时间复杂度是一种函数,定量地描述了该算法运行的时间。既然是一种函数,就涉及到自变量与因变量。因变量代表是时间复杂的规模,自变量是时间复杂度的执行时间。这里的执行时间并不是秒,分钟这类的具体时间  ,它表示的是一种“执行次数”。要想计算时间复杂度首先得找到该算法中的循环,算法中循环执行的次数就是算法的时间复杂度 。

     算法的时间复杂度的具体表示为:用大写的 O 来体现算法时间复杂度如O(f(n)),称之为 大 O 记数法。

  二.时间复杂度的表示

    1.大O的渐进表示法

// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N ; ++ i)
{
 for (int j = 0; j < N ; ++ j)
 {
 ++count;
 }
}
 
for (int k = 0; k < 2 * N ; ++ k)
{
 ++count;
}
int M = 10;
while (M--)
{
 ++count;
}
printf("%d\n", count);
}

首先,我们有三个循环结构:

  1. 两个嵌套的 for 循环,每个循环都运行 N 次。
  2. 一个 for 循环,运行 2 * N 次。
  3. 一个 while 循环,运行 10 次。

因此,我们可以计算 ++count 语句的总执行次数:

  1. 在嵌套的 for 循环中,++count 语句会执行 N * N 次。
  2. 在第二个 for 循环中,执行了 2 * N 次 ++count
  3. while 循环中,执行了 10 次 ++count

实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要 大概执行次数,那么这 里我们使用大 O 的渐进表示法。
    大 O 符号( Big O notation ):是用于描述函数渐进行为的数学符号。
    推导大 O 阶方法:
1 、用常数 1 取代运行时间中的所有加法常数。
2 、在修改后的运行次数函数中,只保留最高阶项。
3 、如果最高阶项存在且不是 1 ,则去除与这个项目相乘的常数。得到的结果就是大 O 阶。
    所以使用大 O 的渐进表示法以后, Func1的时间复杂度为O(N*N),即大O的N方

2.时间复杂度的分类

通过上面我们会发现大 O 的渐进表示法 去掉了那些对结果影响不大的项 ,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数 ( 上界 )
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数 ( 下界 )
例如:在一个长度为 N 数组中搜索一个数据 x
最好情况: 1 次找到
最坏情况: N 次找到
平均情况: N/2 次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为 O(N)
我们以以下代码作为示例
// 计算Func2的时间复杂度?
void Func2(int N)
{
 int count = 0;
 for (int k = 0; k < 2 * N ; ++ k)
 {
 ++count;
 }
 int M = 10;
 while (M--)
 {
 ++count;
 }
 printf("%d\n", count);
}

Func2:

这个函数包含一个 for 循环和一个 while 循环,它们的迭代次数分别为 2 * N 和 10。因此,时间复杂度为 O(N)。

// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
 int count = 0;
 for (int k = 0; k < M; ++ k)
 {
 ++count;
 }
 for (int k = 0; k < N ; ++ k)
 {
 ++count;
 }
 printf("%d\n", count);
}

Func3:

这个函数包含两个 for 循环,它们的迭代次数分别为 M 和 N。因此,时间复杂度为 O(M + N)。

// 计算Func4的时间复杂度?
void Func4(int N)
{
 int count = 0;
 for (int k = 0; k < 100; ++ k)
 {
 ++count;
 }
 printf("%d\n", count);
}

Func4:

这个函数只包含一个固定次数的 for 循环,其迭代次数是一个常数 100,不受输入参数 N 的影响。因此,时间复杂度为 O(1)。

三.计算冒泡排序的时间复杂度

#include <stdio.h>

// 冒泡排序函数
void bubbleSort(int arr[], int n) {
    int i, j;
    for (i = 0; i < n-1; i++) {
        // 每一轮把最大的元素移到最后
        for (j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // 交换 arr[j] 和 arr[j+1]
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

int main() {
    int arr[] = {3, 1, 5, 6, 7,2,4,8};
    int n = sizeof(arr) / sizeof(arr[0]);
    
    printf("Original array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    bubbleSort(arr, n);
    
    printf("Sorted array: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

冒泡排序是一种简单直观的排序算法,它重复地遍历要排序的列表,比较相邻的元素并交换它们,如果顺序不正确就交换。重复这个过程直到不需要交换,即列表已经排序完成。现在我们来分析冒泡排序的时间复杂度。

假设列表长度为 N。

冒泡排序的基本思想是每次从头开始比较相邻的两个元素,如果逆序就交换它们,直到将最大的元素移到列表的末尾。重复这个过程,每次都将当前未排序部分的最大元素交换到未排序部分的最后。所以,冒泡排序的核心是一次遍历会将一个最大(或最小)的元素放到正确的位置上。

最坏情况下,需要进行 N-1 轮比较。在每一轮比较中,需要比较的次数都会减少,因为每一轮都会有一个最大(或最小)的元素被放到了正确的位置上。因此,在第一轮中需要比较 N-1 次,第二轮中需要比较 N-2 次,以此类推,直到最后一轮只需要比较 1 次。

所以,总的比较次数可以表示为:

下面是一个示意图,说明了冒泡排序的过程:

四.什么是空间复杂度

    空间复杂度也是一个数学表达式,是对一个算法在运行过程中 临时占用存储空间大小的量度
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使用O渐进表示法
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。
我们举例帮助理解一下
void BubblesSort(int[] array) {

        boolean flag=true;//此处创建了一个标志性变量flag

        for(int i=0;i<array.length-1;i++) {//创建临时变量i
            
            for(int j=0;j<array.length-1-i;j++) {//创建临时变量j
                if(array[j]>array[j+1]) {
                    flag=false;
                    swap(array[j],array[j+1]);
                }
            }

            if(flag==true)
                break;
        }
    }

可以看到,上面的代码创建了flag,i,j三个临时变量,用大O表示空间复杂度是O( 1 )

需要说明的是,此处的要进行排序的数组array并能计算在空间复杂度里,因为它不是BubbleSort方法临时创建的变量

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值