数据结构中的时间复杂度和空间复杂度基础

news2024/11/24 14:29:04

目录

数据结构

数据结构中的基本名词

数据

数据对象

数据元素

数据项

数据类型

数据对象、数据元素和数据项之间的关系

数据结构及分类

逻辑结构

物理结构

算法

算法的特点

算法设计上的要求

算法效率的衡量

时间复杂度

大O渐进表示法

最坏情况和平均情况

常见的时间复杂度计算

常数阶

线性阶

求和

倍数

对数阶

次方阶

等差数列(平方阶)

等比数列(次方阶)

空间复杂度

常见的空间复杂度计算

常数阶(仅常数个存储单元开辟)

线性阶(主要分析递归)


数据结构

数据结构中的基本名词

数据

描述客观事物的符号,是计算机中可以操作的对象,是能被计算机识别,并输入给计算机处理的符号集合,例如整型数据、字符数据、图片数据、音频数据

数据对象

性质相同的数据元素的集合,是数据的子集,因为是数据的子集,数据包括各种类型,而数据对象指的是具体的某一种类型,所以数据对象也可以简称为数据

数据元素

组成数据的、有一定意义的基本单位,在计算机中通常作为整体处理,也被称为记录

数据项

一个数据元素可以由若干个数据项组成,数据项是数据中不可分割的最小单位

数据类型

一组性质相同的值的集合及定义在此集合上的一些操作的总称,例如表格的整型是整数的集合

抽象数据类型:

一个数学模型以及定义在该模型上的一组操作,既包括以及定义并实现的数据类型,也包括自定义类型和对应的实现方法

数据对象、数据元素和数据项之间的关系

//定义数据,这个数据中包含两个数据对象

//第一个数据对象
struct Person
{
    int age;//数据项
    char name[20];//数据项
}

//第二个数据对象
struct lessons
{
    char lesson[20];//数据项
    int credits;//数据项
}

//使用两个数据对象分别创建两个数据元素
//第一组数据元素
struct Person Peter = {20, "Peter"};
struct Person Mark = {18, "Mark"};
//第二组数据元素
struct Lesson_P = {"English", 2};
struct Lesson_M = {"Mathematics", 2};

数据[struct Person struct lessons]

数据对象[struct Person struct lessons]

数据元素[PeterMark]

数据元素[Lesson_PLesson_M]

数据项

[整型:age]

数据项

[字符数组类型:name]

数据项

[字符数组类型:lesson]

数据项

[整型:credits]

数据结构及分类

结构:不同数据元素之间不是独立的,而是存在特定的关系,这些关系称为结构

数据结构:在计算机中存储、组织数据的方式,相互之间存在一种或多种特定关系的数据元素的集合。主要是在内存中管理数据,基本的管理方式有:增加数据元素、删除数据元素、查找数据元素和修改数据元素


数据结构可以分为:逻辑结构和物理结构

逻辑结构

数据对象中数据元素之间的相互关系

逻辑结构包括:

  1. 集合结构:集合结构中的数据元素除了同属于一个集合外,没有其他关系
  2. 线性结构:线性结构中的数据元素之间是一对一的关系
  3. 树形结构:树形结构中的数据元素之间存在一对多的层次关系
  4. 图形结构:图形结构的数据元素是多对多的关系
物理结构

指数据的逻辑结构在计算机中的实际存储形式

物理结构包括:

  1. 顺序存储结构:把数据元素存放在地址必然连续的存储单元(内存)里,其数据间的逻辑关系和物理关系一致
  2. 链式存储结构:把数据元素存放在任意的存储单元(内存)里,而这些存储单元可以连续也可以不连续

📌

逻辑结构是面向问题的,而物理结构就是面向计算机的,其基本的目标就是将数据及其逻辑关系存储到计算机的内存中

算法

算法:就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为

输出。简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果

算法的特点

  1. 输入输出特点:
    1. 对于输入:算法可以没有输入和多个输入
    2. 对于输出:算法至少有一个或多个输出
  2. 有穷性:算法在执行有限的步骤之后,自动结束而不会出现死循环,并且每个步骤在可接受的时间内完成
  3. 确定性:算法的每一步骤都具有确定的含义,不会出现二义性,即相同的输入只有唯一的输出结果
  4. 可行性:算法的每一步都必须是可行的,即每一步都能通过执行有限次数完成

算法设计上的要求

  1. 正确性:正确性:算法的正确性是指算法至少应该具有输入、输出和加工处理无歧义性,能正确反映问题的需求,能够得到问题的正确答案
  2. 可读性:算法设计的另一目的是为了便于阅读、理解和交流
  3. 健壮性:当输入数据不合法时,算法也能做出相关处理,而不是产生异常或莫名其妙的结果
  4. 时间效率高和存储量低

算法效率的衡量

衡量一个程序的好坏,一般从时间和空间两个维度进行衡量,即时间复杂度和空间复杂度

在程序中,时间复杂度主要衡量一个算法的运行快慢,空间复杂度主要衡量一个算法运行所需要的额外空间

时间复杂度

在计算机科学中,算法的时间复杂度是一个数学函数,它定量描述了该算法的运行时间。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度

在计算时间复杂度时,通常不需要计算出精确的执行次数,只需要计算大概执行的次数,故在计算时采用大O的渐进表示法

大O渐进表示法

大O符号(Big O notation):是用于描述数学函数渐进行为的数学符号

推导大O阶方法:

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

例如,对于下面的C语言代码,试计算出下面代码的时间复杂度

// 请计算一下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);
}

不计算创建变量和打印语句,直观准确来计算时,大概率得出一个公式,即

f(n)={​{n}^{2}}+n+10

但是,时间复杂度计算的是一个估计值,也就是说,我们不需要完全准确地将代码执行次数计算出来,对于上面的表达式,由于N^{_{2}}的增长速度要大于n,当n→+∞时,n对函数整体计算出来的值影响并不大,故取N^{_{2}},而不是取n,另外在计算时间复杂度时,加法常数一般是忽略不计的

所以,上面代码在计算时间复杂度时,只需要取出N^{_{2}}即可,即O(N^{_{2}})

最坏情况和平均情况

最坏情况:任意输入规模的最大运行次数(上界)

平均情况:任意输入规模的期望运行次数

最好情况:任意输入规模的最小运行次数(下界)

例如:在一个长度为N数组中搜索一个数据x

最好情况:1次找到

最坏情况:N次找到

平均情况:\frac{N}{2}次找到

📌

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

常见的时间复杂度计算

常数阶

void Func4(int N)
{
    int count = 0;
    for (int k = 0; k < 100; ++ k)
    {
        ++count;
    }
    printf("%d\n", count);
}

在上面的代码中,代码的运行次数,不计算创建变量和打印语句,共运行了100次,在计算平均复杂度时,因为100为常数次,故时间复杂度为O(1)

📌

计算时间复杂度时,不论常数是多少都记作O(1),不能是其他数字

线性阶

求和
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);
}

上面的代码中,第一个for循环和第二个for循环分开进行,故最后时间复杂度为O(M+N)

📌

注意时间不可以共享,只能累积

long long Fac(size_t N)
{
    if(0 == N)
        return 1;
    
    return Fac(N-1)*N;
}

上述代码是个函数递归,由于计算时间复杂度时计算的是代码执行的次数,故此处时间复杂度即为递归次数,取决于N,故此代码时间复杂度为O(N)

📌

注意,这里实际代码执行的次数为N+1次,例如N = 5时,有

Fac(5)Fac(4)Fac(3)Fac(2)Fac(1)Fac(0),递归结束后再返回,故调用了6次

但是在计算时间复杂度时忽略常数,故时间复杂度为O(N)

倍数
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);
}

上面的代码中,在计算时间复杂度时,可能开始计算时答案为O(2N+M),但是注意,在计算时间复杂度时,不需要最高次项的系数和常数,尽管这里的M是变量,但是变量中已经给了常量10,所以变量M算作常量,可以去掉,故本题时间复杂度为O(N)

对数阶

int BinarySearch(int* a, int n, int x)
{
    assert(a);
 
    int begin = 0;
    int end = n-1;
    while (begin <= end)
    {
        int mid = begin + ((end-begin)>>1);
        if (a[mid] < x)
            begin = mid+1;
        else if (a[mid] > x)
            end = mid-1;
        else
            return mid;
    }
 
    return -1;
}

上面的代码是二分查找算法,二分查找的原理是在有序的数组中查找数据,找不到就进行折半操作

//对于数组
int nums[10] = { 1,5,9,10,15,20,21,30,35,45 };

在进行第一次查找过程中,数组元素个数为10,第二次查找数组元素个数为5,第三次查找数组元素个数为2,即元素个数n与代码执行次数x成对数关系,每一次少一半,也就是除以2,即多少次折半对应多少个元素,即 {​{2}^{x}}= n,以上面的数组为例,当前10个元素,则有等式{​{2}^{x}} = 10,故x≈3,最坏情况下三次才能找到那么x对应的就是代码执行次数,即时间复杂度,故二分查找的时间复杂度为O(\log_{2}{N})(注意此处的N为元素个数)

次方阶

等差数列(平方阶)
void BubbleSort(int* a, int n)
{
    assert(a);
    for (size_t end = n; end > 0; --end)
    {
        int exchange = 0;
        for (size_t i = 1; i < end; ++i)
        {
            if (a[i-1] > a[i])
            {
                Swap(&a[i-1], &a[i]);
                exchange = 1;
            }
        }
 
        if (exchange == 0)
            break;
    }
}

上面的代码中为冒泡排序算法,,而冒泡排序每一趟都会将满足条件的数值放到合适的位置,如果有10个元素,那么就要进行9次冒泡才可以将该数值放置在对应的位置

📌

例如上面图中每一趟冒泡排序都将需要的数值放置在数组的最后一个元素的位置,共需要9趟冒泡排序

而冒泡排序的原理是不满足条件进行两两交换,每一趟排序中,如果只有两个元素,则只需要交换1次,三个元素只需要交换两次,那么n个元素就只需要进行n-1次,故外层循环实际上执行了n-1次,而内层循环随着外层循环执行次数的增长时逐渐减少,也就是两两交换的次数就逐渐减少

📌

例如上面图中每一趟冒泡排序中两两交换的次数都在随着趟数的增加而逐渐减少

所以,在计算时间复杂度时,可以看到10个元素时,第一趟冒泡排序执行了9次,第二趟冒泡排序执行了8次,以此类推,最后一趟冒泡排序执行了1次,故总共执行次数为9+8+7+6+5+4+3+2+1 =\frac{(9+1)\ast 9}{2} = 45(其中9和1即首项和尾项,*9中的9为冒泡排序的趟数)。

故当有n个元素进行冒泡排序时,第一趟冒泡排序需要执行n-1次,第二趟执行n-2次,以此类推,最后一趟还是1次,总共执行次,因为最高此时为\frac{(n-1+1)×(n-1)}{2}=\frac{n(n-1)}{2},并且需要去掉系数故最后时间复杂度为O({​{N}^{2}})

等比数列(次方阶)
long long Fib(size_t N)
{
    if(N < 3)
        return 1;
    
    return Fib(N-1) + Fib(N-2);
}

推荐使用二叉树进行分析,此处暂不做分析,给出答案为时间复杂度为O({​{2}^{N}})

空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度,在计算时也使用大O渐进表示法

📌

函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定,例如变量的个数以及动态内存开辟的空间

常见的空间复杂度计算

常数阶(仅常数个存储单元开辟)

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

上面代码中,由于创建了变量,但是数量是常数个,故空间复杂度为O(1)

long long* Fibonacci(size_t n)
{
    if(n==0)
         return NULL;
    
    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;
}

上面代码中,使用了malloc函数开辟了n+1个空间,故空间复杂度为O(N)

线性阶(主要分析递归)

long long Fac(size_t N)
{
    if(N == 0)
        return 1;
    
    return Fac(N-1)*N;
}

上面代码中,因为每次函数调用需要开辟函数栈帧,从而消耗了空间,而开辟的空间有N的个数决定,实际开辟了N+1个空间,故空间复杂度为O(N)

long long Fib(size_t N)
{
    if(N < 3)
        return 1;
    
    return Fib(N-1) + Fib(N-2);
}

推荐使用二叉树进行分析,此处暂不做分析,空间复杂度为O(N)

📌

注意时间需要累积,但是空间可以共享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1431659.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Simulink系列】——动态系统仿真 之 离散系统线性离散系统

一、离散系统定义 离散系统是指系统的输入与输出仅在离散的时间上取值&#xff0c;而且离散的时间具有相同的时间间隔。满足下列条件&#xff1a; ①系统&#xff08;的输入输出&#xff09;每隔固定时间间隔才更新一次。固定时间间隔称为采样时间。 ②系统的输出依赖当前的…

【Spring】Spring 启示录

一、OCP 开闭原则 核⼼&#xff1a;在扩展系统功能时不需要修改原先写好的代码&#xff0c;就是符合OCP原则的&#xff0c;反之修改了原先写好的代码&#xff0c;则违背了OCP原则的 若在扩展系统功能时修改原先稳定运⾏程序&#xff0c;原先的所有程序都需要进⾏重新测试&…

景区导览系统|智能导览|景区电子导览|智慧景区导览|AI智能导览

景区/园区导览系统是必不可少的服务内容&#xff0c;可提供提供指引导航&#xff0c;讲解景点、VR游览、预约购票等服务。随着元宇宙、VR、AR等数字科技的不断发展&#xff0c;导览系统的形式也从传统的纸质地图、指示牌等形式&#xff0c;发展为如今的VR/AR智慧导览。 作为国…

【OpenCV人脸检测】写了个智能锁屏小工具!人离开电脑自动锁屏

文章目录 1. 写在前面2. 设计思路3. 人脸检测4. 程序实现 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋…

MacOS Mojavev10.14.6

MacOS Mojave v10.14.6系统安装包是一款专为Mac用户设计的操作系统软件包。Mojave是苹果公司为Mac设备开发的一个操作系统版本&#xff0c;它提供了许多新功能和改进&#xff0c;旨在提高Mac用户的使用体验和工作效率。 安装MacOS Mojave v10.14.6系统后&#xff0c;用户可以享…

从0到1入门C++编程——07 基于多态的职工管理系统

文章目录 一、创建管理类及菜单功能实现二、创建职工抽象类三、退出管理程序四、增加职工信息五、读写文件操作六、显示职工信息七、删除职工信息八、修改职工信息九、查找职工信息十、对职工信息排序十一、清空文件内容十二、工程文件构成 本案例中利用C实现一个基于多态的职工…

flask_django_python五金电商网络营销的可视化分析研究

前面部分完成了系统需求分析&#xff0c;了解到新闻数据业务方面的需求&#xff0c;系统主要分为用户管理、五金信息管理、在线留言、系统管理等功能。销的可视化研究&#xff0c;并对这些数据进行处理&#xff0c; 然后对这些数据进行可视化分析和统计。 Python 爬虫技术目前来…

职言圈:华为4年员工,小金库存到100万,但却在事业上升期决定回老家县城考公务员。

“华为4年员工&#xff0c;小金库存到100万&#xff0c;但却在事业上升期决定回老家县城考公务员。只因不想现在赚的钱将来都花在了医院&#xff01;” 互联网大厂员工的薪资高&#xff0c;这是人尽皆知的事了&#xff0c;特别是像华为、百度、绿厂、阿里、鹅厂等这些知名公司…

Leetcode的AC指南 —— 栈与队列 :1047.删除字符串中的所有相邻重复项

摘要&#xff1a; **Leetcode的AC指南 —— 栈与队列 &#xff1a;1047.删除字符串中的所有相邻重复项 **。题目介绍&#xff1a;给出由小写字母组成的字符串 S&#xff0c;重复项删除操作会选择两个相邻且相同的字母&#xff0c;并删除它们。 在 S 上反复执行重复项删除操作&a…

CG-FS 风速传感器 485型三杯式风速计 气象环境监测仪器

产品概述 本产品主要采用优质聚合物碳纤维为原材料&#xff0c;具有良好的防腐、防侵蚀等特点&#xff0c;能够保证仪器长期使用不起锈&#xff0c;同时配合内部顺滑的轴承系统&#xff0c;确保了信息采集的准确性。外型小巧轻便&#xff0c;便于携带和组装&#xff0c;三杯设…

计划赶不上变化!项目团队如何高效管理变更请求?

现实中&#xff0c;即使是计划得再好的项目&#xff0c;有时也需要变更。作为项目经理&#xff0c;管理变更请求是重要的工作之一&#xff0c;以便团队能继续无缝工作&#xff0c;实现项目目标。 变更请求的重要性 变更请求是指修改系统、产品或项目计划的建议&#xff0c;通常…

计算机网络_1.6.1 常见的三种计算机网络体系结构

1.6.1 常见的三种计算机网络体系结构 1、OSI&#xff08;七层协议&#xff09;标准失败的原因2、TCP/IP参考模型3、三种网络体系结构对比 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 1、OSI&#xff08;七层协议&#xff09;标准失败的原因 &#xff08;1&#xf…

利用ReentrantLock解决死锁----以哲学家问题为例

问题描述 哲学家问题&#xff1a;其中多个哲学家在共享有限资源&#xff08;筷子&#xff09;的情况下进行工作&#xff08;思考和吃饭&#xff09;&#xff0c;这可能导致死锁。 每位哲学家需要两根筷子才能吃饭&#xff0c;而每根筷子只能被一位哲学家使用&#xff0c;这就…

Web项目利用OSS进行图像存储服务

一、OSS介绍 在Web项目中&#xff0c;一些常见的功能&#xff0c;比如展示图片&#xff0c;修改头像等&#xff0c;都需要进行图片的上传操作&#xff0c;但是如果是存储在Web服务器中&#xff0c;在读取图片的时候会占用比较多的资源&#xff0c;影响服务器的性能。 常…

学习c语言,动态内存管理

malloc和calloc区别在于初始化

迅为RK3588开发板windows与开发板互传使用U盘进行拷贝

1 将 U 盘(U 盘的格式必须为 FAT32 格式&#xff0c;大小在 32G 以下)插到开发板的 usb 接口&#xff0c;串口打印信息如下所示&#xff0c;U 盘的设备节点是/dev/sdb4。U 盘的设备节点不是固定的&#xff0c;根据实际情况来查看设备节点。 2 输入以下命令挂载 U 盘&#xff0c…

2024年教资认定全流程详细图解✅

教资认定3月开始&#xff0c;大家别只顾着快乐过年&#xff0c;可以做好准备了&#xff01; ▶️材料准备&#xff1a; 有效期内的身份证原件学历证学位证普通话等级证书电子版个人照片&#xff08;个人近期白底免冠无头饰正面1寸证照。&#xff09;打印版填写完毕的个人承诺书…

感激沃尔玛跨境智星:轻松注册大量沃尔玛买家号的利器

最近&#xff0c;我急需大量沃尔玛买家号&#xff0c;但又担心账号之间可能存在关联问题。幸运的是&#xff0c;在我尝试使用沃尔玛跨境智星后&#xff0c;这一问题得到了解决。 首先&#xff0c;我要感谢这款软件提供的详细操作指南。通过访问沃尔玛官网并按照您的指引&#x…

【蓝桥杯冲冲冲】[NOIP2001 普及组] 装箱问题

蓝桥杯备赛 | 洛谷做题打卡day26 文章目录 蓝桥杯备赛 | 洛谷做题打卡day26题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示思路 题解代码我的一些话 [NOIP2001 普及组] 装箱问题 题目描述 有一个箱子容量为 V V V&#xff0c;同时有 n n n 个物品&#xff0c;每…

c++虚函数、静态绑定与动态绑定

首先说明&#xff0c;所谓绑定&#xff0c;就是指函数的调用 接下来&#xff0c;我们直接看一段代码来说明问题 class Base { public:Base(int data10):m_a(data){}void show(){cout<<"Base::show()"<<endl;}void show(int){cout<<"Base::sh…