数据结构与算法-时间复杂度

 这篇文章会讲到时间频度,时间复杂度,一些排序算法的平均时间复杂度与最坏时间复杂度

目录

1.度量一个程序(算法)的两种方法

1)事后统计法

2)事前估算法

2.时间频度

1.基本介绍

算法1:

算法2:

3.算法的时间复杂度引入

1.举例说明:忽略常数项

2.举例说明:忽略低次项

3.举例说明:忽略系数

4.算法的时间复杂度介绍

1.时间复杂度

2.常见的时间复杂度

分类:

顺序:

3.具体介绍常见的时间复杂度

1.常数阶O(1)

2.对数阶O(log2n)

3.线性阶O(n)

4.线性对数阶O(Nlogn)

5.平方阶O(n^2)

6.立方阶O(n^3)、k次方阶O(n^k)

5.算法的平均时间复杂度和最坏时间复杂度

1.介绍:

2.我们以排序算法为例:

6.算法的空间复杂度简介

7.提炼一下


1.度量一个程序(算法)的两种方法

1)事后统计法

这种方法就是运行一段程序,运行后看花了多少时间。然后再运行另外一段程序,看程序运行花了多少时间,把这时间进行比较。

但是事后统计法有两个问题:

一是要评测设计的算法的运行性能,就需要实际运行该程序,每次要比较就要亲自运行一遍未免太麻烦了,更别说如果这个程序运行完就要10分钟,一直等着那太痛苦了;

二是所得的时间的统计量依赖于计算机的硬件、软件等环境因素,比如你的计算机硬件环境比较好,运行起来嘎嘎快,那同样的程序运行的时间都不一样,更别说不同的程序了,不好比较的,这样就需要在同一台计算机相同的状态下运行才能比较哪个算法速度更快

2)事前估算法

通过分析某个算法的时间复杂度来判断哪个算法更优。

2.时间频度

1.基本介绍

一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中语句的执行次数称为语句频度或者时间频度,记为T(n)。

举例:

计算1-100所有数字之和,可以设计两种算法:

算法1:

int total=0;

int end=100;

for(int i=1;i<=end;i++){

    total+=i;

}

那么上面这个算法时间频度就是T(n)=n+1;

解释:

因为虽然循环中执行total+=i是执行了1到end也就是100次,但是在for循环的条件中还有个i<=end,然后i=101的时候是判断了,然后不满足条件所以退出,多了一次,所以它就是101次。

算法2:

int total=0;
int end=100;
total=(1+end)*end/2;

那么上面这个算法的时间频度就是T(n)=1

3.算法的时间复杂度引入

我们看到上面的时间频度n+1中带有常数1,次数不是100而是101,太细致了吧。对于常数项,我们还可以看一看,如果没有像1这样的常数项,和带常数项的相比会有多大差别呢?

1.举例说明:忽略常数项

这里可以通过一个表格来看一下:

从n为1到n为300,可以发现这个常数项的影响变小了很多。再看看图:

有常数项的和没有常数项的曲线是不是到n比较大的时候就很接近了!要是n到了1000,那差距是不是看着更小了。

可以得出结论:

1.2n+20和2n随着n变大,执行曲线无限接近,20可以忽略

2.3n+10和3n随着n变大,执行曲线无限接近,10可以忽略

那么对于时间频度而言,常数项可以忽略

2.举例说明:忽略低次项

下面的T(2n^2+3n+10)应该是T(n)= 2n^2+3n+10

可以看到前面两项随着n增大,数值看着比较接近了。后面两项也是,不再是一开始的相差几十倍了。再看图:

有低次项的和没有低次项的曲线是不是到n比较大的时候就很接近了!都要重合了。

可以得出结论:

1.2n^2+3n+10和2n^2随着n变大,执行曲线无限接近,3n+10可以忽略

2.n^2+5n+20和n^2随着n变大,执行曲线无限接近,5n+20可以忽略

那么对于时间频度而言,低次项可以忽略

3.举例说明:忽略系数

下面的T(3n^2+2n)应该是T(n)= 3n^2+2n

在三次方参与的情况下,n=100时这两个二次方的都是与1000000相差很多,在图上看着是很接近的。

可以得出结论:

1.随着n变大,5n^2+7n和3n^2+2n,执行曲线无限接近,在这种情况下作为系数的5和3可以忽略,反正都是二次方的。

2.n^3+5n和6n^3+4n随着n变大,执行曲线分离,但是次方是不能忽略的。二次方和三次方差异巨大啊。不过这里呢n^3+5n和6n^3+4n求导发现大概是6,1000000与6000000。我觉得吧忽略系数这种还是对于一次方二次方的比较适用,对于三次方可能就感觉差距还是挺大的,不太好忽略。

4.算法的时间复杂度介绍

1.时间复杂度

  1. 一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示(就是前面时间频度的那个T(n))。若有某个辅助函数f(n)使得当n趋于无穷大时T(n)/f(n)的极限值为不等于0的常数,就称f(n)是T(n)的同量级函数,记为T(n)=O(f(n)),O(f(n))为算法的渐进时间复杂度,简称为时间复杂度。比如T(n)=n+1,这时有个辅助函数f(n)=n,那么T(n)/f(n)就是(n+1)/n,在n无穷大时这个结果就是1.我们就可以吧T(n)=O(f(n))=O(n),这个O(n)就是前面算法的时间复杂度。
  2. T(n)不同但是时间复杂度可能相同,比如T(n)=n^2+7n+6与T(n)=3n^2+2n+2,这两个T(n)不一样,但是有个f(n)为n^2,当n无穷大时就趋于一个常数项,所以两个T(n)时间复杂度相同,都是O(n^2)。
  3. 计算时间复杂度的方法:
    • 用常数1代替运算时间中的所有加分常数。比如T(n)=3n^2+7n+6变成T(n)=3n^2+7n+1
    • 修改后的运算次数函数中只保留最高阶项,比如T(n)=3n^2+7n+1变成T(n)=3n^2
    • 去除最高阶项的系数,比如T(n)=3n^2变成T(n)=n^2,推出来时间复杂度O(n^2)

2.常见的时间复杂度

分类:

  1. 常数阶O(1)
  2. 对数阶O(log2n)
  3. 线性阶O(n)
  4. 线性对数阶O(nlog2n)
  5. 平方阶O(n^2)
  6. 立方阶O(n^3)
  7. K次方阶O(n^k)
  8. 指数阶O(2^n)

顺序:

以上算法的时间复杂度从小到大依次为:

O(1)< O(log2n)< O(n)< O(nlog2n)< O(n^2)< O(n^3)< O(n^k)< O(2^n)

随着问题规模n不断增大,上面算法的时间复杂度不断增大。算法的时间复杂度越大,算法的执行效率越低

从图中我们可以看到,指数阶的算法在n=10的时候就飙上去了,我们应该尽量避免使用指数阶的算法。其实还有个阶乘阶O(n!),是时间复杂度最大的,比O(2^n)还大。

3.具体介绍常见的时间复杂度

1.常数阶O(1)

无论代码执行多少行,只要没有循环之类的复杂结构,代码的时间复杂度就是O(1)

举例:

int i=1;

int j=2;

++i;

j++;

int m=i+1;

如果i=100000,j=200000,程序执行消耗的时间也不会变,它不会因为变量的增长使消耗的时间变长,就算几千行几万行,都可以用O(1)来表示它的时间复杂度。

2.对数阶O(log2n)

对数阶不一定是以2为底的,也可以log3n,log4n,log10n等等。

在数学中,如果N=a^x(a>0,a≠1),也就是a的x次方等于N,那么x就是以a为底N的对数,x=logaN。a就是对数的底数,x就是以a为底N的对数。

下面的例子是以2为底的对数阶。

举例:

int i=1;

while(i<n){

   i=i*2;

}

解释:在while循环中,每次将i乘以2,成倍变化,那么总有一个时候不满足条件i<n,也就是i=n或者i>n的时候循环结束。假设循环x次之后,i就大于2了,那么循环就退出了,也就是2的x次方等于n,x=log2n,循环log2n次之后代码就结束了,所以这段代码时间复杂度为O(log2n)。假设n为1024,那么那么x就是10,那么循环10次后代码就结束了。

如果while循环里面i=i*3,那么时间复杂度就是O(log3n)。

那么就记住,这样的循环里面是i=i*k,循环条件是i<n,代码的时间复杂度就是O(logkn)

3.线性阶O(n)

比如单纯的for循环,要循环n次的。

举例:

for(int i=1;i<=n;++i){

   j=i;

   j++;

}

上面代码中for循环里的代码会执行n遍,时间频度是T(n+1),它消耗的时间也随n变化而变化,时间复杂度为O(n)。如果n为10,那么时间复杂度就是O(10)

4.线性对数阶O(Nlogn)

其实将时间复杂度为O(logn)的代码循环N遍的时间复杂度就是O(Nlogn),因为O(N*f(n))=O(N*logn)。

举例:

for(int m=1;m<n;m++){

   i=1;

   while(i<n){

   {

      i=i*2;

   }

}

可以看到外面的循环条件m<n,里面的循环时间复杂度logn(其实是log2n简写为logn),那么总的时间复杂度就是O(nlogn)。

5.平方阶O(n^2)

双层for循环,外面循环n次,里面循环n次就达到了n^2次,时间复杂度就是O(n*n)=O(n^2)。

举例:

for(int i=1;i<=n;i++){

   for(int j=1;j<=n;j++){

   {

System.out.println(“i=”+i+“,j=”+j);

   }

}

上面代码中输出i的值和j的值,i循环n次,j循环n次,那么内层循环中代码就执行了n^2次。比如n=10,输出的就有100行。

如果把其中一层循环的n改成m,那么时间复杂度就是O(m*n),比如m=8,n=10,i<=m,j<=n,那么内层循环中的输出语句就执行8*10=80次。

6.立方阶O(n^3)、k次方阶O(n^k)

参考上面的O(n^2)理解,O(n^3)相当于三层循环,k次方阶相当于k层循环。

5.算法的平均时间复杂度和最坏时间复杂度

1.介绍:

平均时间复杂度指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。

  • 最坏时间复杂度是最坏情况下的时间复杂度。一般讨论的时间复杂度都是最坏情况下的时间复杂度。因为最坏时间复杂度是算法在任何输入实例上运行时间的界限,保证运行时间不会比最坏情况下更长。拿最坏时间复杂度当保底,再差也不会比保底差。
  • 平均时间复杂度和最坏时间复杂度是否一致呢?得看具体算法。

2.我们以排序算法为例:

排序算法

平均时间

最坏情况

稳定度

额外空间

备注

冒泡

O(n^2)

O(n^2)

稳定

O(1)

n小用

交换

O(n^2)

O(n^2)

不稳定

O(1)

n小用

选择

O(n^2)

O(n^2)

不稳定

O(1)

n小用

插入

O(n^2)

O(n^2)

稳定

O(1)

大部分已排序用

基数

O(logRB)

O(logRB)

稳定

O(n)

B为真数(0-9)

R为基数(个十百)

希尔

O(nlogn)

O(n^s)

1<s<2

不稳定

O(1)

s为所选分组

快速

O(nlogn)

O(n^2)

不稳定

O(nlogn)

n大用

归并

O(nlogn)

O(nlogn)

稳定

O(1)

n大用

O(nlogn)

O(nlogn)

不稳定

O(1)

n大用

6.算法的空间复杂度简介

算法的空间复杂度定义为该算法所耗费的存储空间,也是关于问题规模n的函数。它是对一个算法在运行过程中临时占用的存储空间大小的度量。有的算法需要占用的临时工作单元数与解决问题的规模n有关,随n增大而增大,当n较大时占用的存储单元就多,比如快速排序归并排序算法。

注意,我们在做算法分析时,主要讨论的是时间复杂度,从用户角度看,更看重程序执行的速度,要是程序执行很慢用户肯定不乐意了,用户体验就不好了。一些缓存产品比如redis和memcache还有算法比如基数排序,就是用空间换时间。而且一些程序的算法优化就是用空间换时间,设置二级缓存三级缓存,会占用额外空间但是时间上加载时就会更快。

7.提炼一下

①时间频度如T(n),算法时间复杂度如O(n)。

②时间频度常常忽略常数项,低次项,系数。

③常见算法时间复杂度顺序O(1)< O(log2n)< O(n)< O(nlog2n)< O(n^2)< O(n^3)< O(n^k)< O(2^n)

④一般讨论的时间复杂度都是最坏情况下的时间复杂度


以上就是算法时间复杂度的内容。后面会写八大排序算法,看我能不能找到什么好理解又好记的办法吧^_^加油加油

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。
一.单选题(共10题,5) 1线性表采用链式存储结构时,其地址( )。A、必须是连续的B、部分地址必须是连续的C、一定是不连续的D、连续与否均可以正确答案: D 2带头结点的单链表head为空的判断条件是( )。A、head=NULLB、head->next=NULLC、head->next=headD、head!=NULL正确答案: B 3将两个各有n个元素的有序表归并成一个有序表,其最少的比较次数是( )。A、nB、2n-1C、 2nD、n-1正确答案: A 4在一个单链表中,已知q所指结点是p所指结点的前趋结点,若在q和p之间插入s结点,则执行( )。A、 s->next=p->next;p->next=sB、p->next=s->next;s->next=pC、q->next=s;s->next=pD、p->next=s;s->next=q正确答案: C 5向一个有 127 个元素的顺序表中插入一个新元素并保持原来顺序不变,平均要移动( )个元素。A、64B、63C、63.5D、7正确答案: C 插入 n/2 删除 n-1/2 查询 n+1/2 6若某线性表中最常用的操作是在最后一个元素之后插入一个元素和删除第一个元素,则采用( )存储方式最节省运算时间。A、单链表B、仅有头指针的单循环链表C、双链表D、仅有尾指针的单循环链表正确答案: D 7若长度为n的线性表采用顺序存储结构,在其第个i位置插入一个新元素算法时间复杂度为( )。A、 O(log2n)B、O(1)C、O(n)D、O(n2) 正确答案: C 8线性表的静态链表存储结构与顺序存储结构相比优点是( )。A、所有的操作算法简单B、便于插入和删除C、便于利用零散的存储空间D、便于随机存取正确答案: B 9在一个单链表中, 若删除 p 所指结点的后续结点, 则执行( )A、s->next=p;p->next=s;B、s->next=p->next;p=s;C、s->next=p->next;p->next=s;D、p->next=s;s->next=p;正确答案: B 10一个线性表第一个元素的存储地址是 100, 每个元素的长度为 2,则第 5 个元素的地址是( )A、110B、108C、100D、120

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值