这是一个全新的专栏,内容主要是用C语言学习数据结构的初阶内容,一是复习,二是希望能带给大家一些帮助。


从今天开始,我们进入数据结构与算法的学习,在进入今天的内容之前,我们先了解一下什么是数据结构,什么是算法。

第一章:复杂度分析

  • 定义解释
  • 算法的复杂度
    • 时间复杂度
      • 冒泡排序
      • 二分查找
      • 递归算法
  • 小结

定义解释

数据结构:
数据结构是计算机存储、组织数据的方式,是存在一种或多种特定关系的数据元素的集合。

举个例子:比如我们在校学生的信息,要知道他是哪个班,哪个专业,哪个系,是大几的,这时候我们就可以用一个结构体把他们储存起来,然后再使用一些数据结构,描述他们的关系,例如数组,链表,树,图这样的数据结构,把学生信息按照一种特殊的方式存储起来,方便查找,节省空间和时间。

算法

算法是良好的计算过程,是通过一系列的计算步骤,解决你的问题,把输入数据变成我们想要的输出结果的一种方法。

既然是方法那就有好也有坏,我们怎么评估一个算法是好还是坏呢?这就要进入我们今天的重点,复杂度

算法的复杂度

一个算法在编写成可执行程序后,运行时会耗费时间资源和内存资源,我们衡量一个算法的好坏,就从时间和空间两个维度开始衡量,这也就是时间复杂度和空间复杂度。

时间复杂度是衡量算法运行快慢,空间复杂度是衡量算法运行所需要的额外空间。我们也知道现在计算机的存储容量已经很大了,我们在关注的时候,更加关注一个算法的时间复杂度,他的效率如何。
下面来介绍一下时间复杂度及其求法:

时间复杂度

在计算机科学中,算法的时间复杂度是一个函数(数学函数表达式),他定量的描述了算法的运行时间,但我们仔细的想一下,一个算法换做不同的电脑,不同的编译器,甚至是相同电脑的不同时间,运行时间都是不一样的所以我们对时间复杂度的计算并不是一个精确的值,而是一种比例,这种比例是通用的,是与算法中语句的执行次数成比例的,那么一个算法中的语句有很多,我们又该选取哪一条呢?在这里先不直接给大家,先举一个例子,大家通过例子来感受一下:

   #include<stdio.h>  int main()  {  int cnt = 0;  int cmt = 0;  for(int i=0;i<10;++i)  {  cmt++;for(int j=0;j<10;++j){cnt++;}}return 0;}

我们看这样一个算法,我们衡量算法的时间复杂度,要的是找一条基础性的语句,算法每执行一次,他都会执行的语句,这样的语句才有代表性,就例如上面嵌套循环的代码 里面有在外层循环的cmt++;,也有在内层循环的cnt++;,究竟那一条才是基础语句呢?我们可以分析一下 :
cmt++在外层循环,也就是他执行了10次,cnt++在内层循环,他执行了100次。在这里注意,我们衡量一个算法的时间复杂度,一定是要根据其执行次数最多的语句来进行的,后面也会提到,我们看的是算法执行时间最长的情况作为时间复杂度,这也很好理解,这样的话情况只会更好,不会更坏,所以在这个算法中,时间复杂度就是O(100),在后面的介绍中我们会对这样的复杂度进行简化。
按照这样的思路,我们提高一下难度再进行计算:

我们看这样一串代码,来计算他的时间复杂度,毫无疑问,基础语句是cnt++,因为只有cnt++才是每一次循环都执行的语句,第一个嵌套循环,100次,这里就不多说了,第二个for循环 cnt++执行了2*N次,第三个for循环cnt++执行了 N*N = N^2次,加起来就是 N^2+2*N+100次,时间复杂度就是

O(N^2+2*N+100)

这式子未免也太长了点,要是在多几十个循环,岂不是电脑屏幕都装不下了?不用慌,我们有大O渐进表示法,这个表示法只考虑最高阶,忽略低阶函数对复杂度的影响,可以想一下当N越大时 N^2就越大,而其他的项相当于最高阶项就越小,当N = 10000000时,有没有那个100,意义就不大了。所以我们就可以把上面的表达式直接写成O(N^2)就可以了,那如果函数表达式时一个常数,如O(100),O(109),我们就可以直接把他写成O(1)表示算法的复杂度会改变,是常数阶,也是最优的时间复杂度。
在这里我们把大O渐进表示法的三条规则给大家罗列出来,方便记忆:

1.用常数1取代算法中所有的加法常数
2.在函数中只保留最高阶项
3.如果最高阶存在而且系数不为1,把系数改为1

在进一步了解算法的时间复杂度之前,我想问大家一个问题,假如我们有一个查找函数,我们是取其最优情况,一次就找到的情况为时间复杂度,还是取其最差的情况,还是取其平均情况呢?答案是:取其最差情况,这样就只有更好,没有更差了。

下面我们看几种常见算法的时间复杂度计算:

冒泡排序


如果有不熟悉冒泡排序的,没关系,在这里我们回忆一下:

像这样,第一趟把第一大的数据放到末尾,第二趟不再对末尾进行比较,从末尾-1开始比较(因为末尾已经是最大的了,不需要比较),把第二大的放在末尾-1的位置…一直到不需要交换,那排序就完成了,像这样最大的数依次向后放的排序就叫冒泡排序。
那么他的时间复杂度应该如何计算呢 ,基础语句肯定就是if判断,判断了多少次,就是函数执行了多少次。第一趟执行N-1次,第二趟执行 N-2次 第三趟执行 N-3次,一直执行到1次为止(只判断一次且不交换),这样看起来是一个公差为1的等差数列,那也就是 N*(N-1)/2,我们再用大O渐进表示法进行简化,那就是O(N^2)。

二分查找

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 mid = begin + ((end-begin)>>1); ,他就是我们的基础语句,那只需要搞明白他会执行多少次就可以了,我们先回忆一下二分查找的原理:

假设有一串有序(从小到大排列)的整数,让我们查找其中的某一个数,由于是有序的,我们把要查找的数字(key)和区间中点数字进行比较,如果key大于区间中点的值,我们就抛弃左半区间,再取中点,重复步骤一直到key
== a[mid]为止,这就是二分查找,假设我们要查找的数字是区间最左端的数据,画图如下:

一直到区间只有一个元素,也就是第一个元素的时候,我们查找就算完成了。
取这种情况还有一个作用:我们在前面的学习提到过,算法的时间复杂度,求的是最差的形况,那么对于二分查找的情况来说,最好的情况是一开始要找的数据就在中点,这样一次就出来了,最坏的情况就是我们已经分到不能再分了,只剩下一个元素了,也就是图示的这种情况。

所以我们要针对这种情况求时间复杂度,基础语句是int mid = begin + ((end-begin)>>1); ,假设要查找的数组中有N个数字,那基础语句执行的步骤可以表示为:N/2/2…/2 = 1,N除以2的次数也就是我们要求的时间复杂度,那怎么表示这个次数呢?我们可以把所有的/2都乘过去 N = 12222… 假设算法执行了M次,那也就是 N = 2M,紧接着对两边取对数 M = log2N,也算是一种逆向思维,从1个元素开始还原整个数组,这样,我们就把二分查找的时间复杂度求出来了。

递归算法

递归的思想就是大事化小,小事化了,再把过程往回反馈的过程,我们先来看一个简单的求阶乘的递归函数:

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

我们知道,递归函数都是一层一层的调用,所以递归的复杂度计算公式事:单次执行循环的次数*递归的层数,我们画图来求一下(假设求4的阶乘):

大概就是这样,我们先找到算法中的基础语句,肯定就是那个 return 了,在每个方法中都执行1次,递归的层数是N次,那这样 N*1 ,这个递归算法的复杂度就是O(N)。

下面再看看经典的斐波那契数列

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

这个就留给大家来练练手,给点提示,加起来是一个等比数列,简化是O(2N)。
留个图好理解:

小结

对于常见算法的时间复杂度分析就到此结束,相信大家对于时间复杂度也有了一定了了解,想要深入了解的可以把自己了解的算法都算一遍,加深自己的印象。

数据结构与算法 (1)复杂度---(时间复杂度)相关推荐

  1. 【数据结构与算法】复杂度分析

    一.什么是复杂度分析? 1.数据结构和算法解决是"如何让计算机更快时间.更省空间的解决问题". 2.因此需从执行时间和占用空间两个维度来评估数据结构和算法的性能. 3.分别用时间复 ...

  2. 数据结构与算法分析c++第四版_数据结构与算法 - 时空复杂度分析

    这周主要总结了时间复杂度的学习,跟小伙伴们分享下,欢迎指正. 一.为何需要分析算法复杂度 挺多同学本科都学习过数据结构和算法这门课,但是有没有想过这门课到底是解决什么问题?科学家设计这些数据结构和算法 ...

  3. day001:数据结构和算法-时间频度和时间复杂度

    1.初识数据结构和算法 简单认识中:ArrayList的查询快点,插入慢.LinkedList查询慢,插入快.但是结论是将数据的大小为基础前提,相对的. -1^n 计算问题 编程问题:求(-1)^0 ...

  4. 关于 数据结构 与算法的复杂度

    程序=数据结构+算法(瑞士 尼克劳斯·威茨 提出) 数据结构:逻辑结构 和 物理结构 逻辑结构:集合结构.线性结构.树形结构.图形结构 物理结构:数据存储 数据元素的存储结构:顺序存储结构(地址连续的 ...

  5. 数据结构与算法之美--1.时间复杂度分析

    时间复杂度分析 为什么要进行时间复杂度的分析 大O复杂度表示法 如何分析一段代码的时间复杂度? 几种常见时间复杂度实例分析. 空间复杂度 最好.最坏时间复杂度 平均时间复杂度 均摊时间复杂度 为什么要 ...

  6. python【数据结构与算法】一种时间复杂度和空间复杂度的计算方法

    文章目录 1 算法的时间复杂度定义 2 推导大O阶方法 2.1 常数阶 2.2 线性阶 2.3 对数阶 2.4 平方阶 2.5 立方阶 3 常见的时间复杂度排序 4 算法空间复杂度 5 常用算法的时间 ...

  7. 数据结构与算法的分析 —— 平均时间复杂度 vs 分摊时间复杂度

    平均时间复杂度:假定各种输入实例的出现符合某种概率分布(如均匀独立随机分布)之后,进而估计出的加权时间复杂度均值. 分摊时间复杂度,纵观连续的足够多次操作,并将其间总体所需的运行时间分摊至各次操作.与 ...

  8. 数据结构与算法_01_复杂度分析

    文章目录 记号 记号

  9. 【数据结构与算法】时间复杂度和空间复杂度

    文章目录 前言 数据结构与算法 如何学好数据结构和算法 算法效率 算法的复杂度 时间复杂度 大O的渐进表示法 空间复杂度 常见复杂度对比 OJ练习题 消失的数字 轮转数组 结语 前言 大家好哦,< ...

最新文章

  1. 爬虫之selenium标签页的切换
  2. Android监听后台状态,退出即杀死并显示退出提示框
  3. method-dispatch/
  4. 中国科技大学校长朱清时:我的两次“错误”选择
  5. 面试被问 | 防止 Java 代码被反编译的方法有几种?
  6. Java-jdbc连接数据库
  7. IntelliJ IDEA tomcat配置
  8. 【Java】浅析Java位移运算符<<、>>、>>>
  9. python并发编程之semaphore(信号量)_Python 并发编程系列之多线程
  10. HTML5的优点与缺点
  11. 推荐一款超好用的vue的markdown 编辑器
  12. 基于Android平台的会议室管理系统详细设计说明书
  13. 戴尔服务器修改主板IP,Dell R410, R710设置iDRAC(远控卡)的ip地址
  14. pd.concat实现数据合并
  15. 物业智能化管理西方行政学说
  16. 如何解决 MacBook 电池耗电问题
  17. bio linux 创建_Linux IO请求处理流程-bio和request
  18. 大数据毕设 航空公司客户价值分析
  19. 20_TodoList案例
  20. java.text.ParseException: Unparseable date: 2018-09-12

热门文章

  1. 制作efi启动u盘linux,手动制作BIOS和EFI多启动U盘
  2. 抓包工具anyproxy使用总结
  3. 苹果5壁纸_iOS14.2 beta4描述文件下载-苹果iOS14.2测试版Beta4描述文件官方更新文件下载 v1.0...
  4. 生成二维码跳转页面(转)
  5. 机器学习面试题之——简单介绍最小二乘
  6. FPGA Vivado XDC 约束文件编写方式语法笔记
  7. ABB机器人备份的那些事儿
  8. 简易的组装Json工具类
  9. 电机 输送机 机械手 提升机 发酵罐 减速机 破碎机
  10. 微课-面向对象程序设计(5课82分钟)