有关时间复杂度,空间复杂度通俗的理解
2018-01-26 22:58
288 查看
有关时间复杂度,空间复杂度通俗的理解
今天,在复习C语言知识时,碰到了一个非常有趣的题目,题目如下:一个数组中除了两个数字之外,其余数字均出现了两次,如{1, 2, 3, 4, 5, 3, 2, 1}。查找出这两个只出现一次的数字,要求如下:时间复杂度为O(n),控件复杂度位O(n)。
当笔者看到这道题目的时候,就被“时间复杂度为O(n),控件复杂度位O(n)”所困惑,究竟题目中的这两个要求是什么意思?
首先,笔者百度了一下,百度上显示如下:
在计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。
what‘s that?一点都看不懂;
简单说O(n²)表示当n很大的时候,复杂度约等于Cn²,C是某个常数,简单说就是当n足够大的时候,n的线性增长,复杂度将沿平方增长。(如果还不懂继续往下看哦)
要想真正搞懂时间复杂度,空间复杂度其实不难,在笔者学习C中有关排序内容的时候就曾遇到过,只是当时没怎么注意,于是笔者又打开了尘封千年的笔记本,以下便是八大排序所对应的复杂度:
相信接触过排序的人都知道,各个排序是怎么实现的,举个最简单的例子——冒泡排序,在冒泡排序的代码中有这么一段:
// 这里是对长度为10的数组进行排序 for(i = 0; i < len-1; i ++) { for(j = 0; j < len - i - 1; j ++) { if(a[j] > a[j + 1]) { tmp = a[j]; a[j] = a[j + 1]; a[j + 1] = tmp; } } }
很明显,冒泡排序是通过连层嵌套的for语句实现的,对应上面表格中的时间复杂度(平均时间)是O(n^2);所以,根据上面的定义,我们把时间(len/数组长度)扩大至无限大,第一个for循环是一个简单的线性增长所以是O(n),同理,第二个for循环也是一个简单的线性增长,也是O(n),由于两个是嵌套的所以总的时间复杂度是O(n^2);
是不是,有了那么一点懂了,下面一些时间复杂度的算法:
时间复杂度是一个函数,它定量描述了该算法的运行时间。常见的时间复杂度有以下几种。 1,log(2)n,n,n log(2)n ,n的平方,n的三次方,2的n次方,n! 1指的是常数。即,无论算法的输入n是多大,都不会影响到算法的运行时间。这种是最优的算法。 而n!(阶乘)是非常差的算法。当n变大时,算法所需的时间是不可接受的。 用通俗的话来描述,我们假设n=1所需的时间为1秒。当时间复杂度是1,那么当n = 10时,需要10秒; 当时间复杂度是n的平方时,n = 10,需要100s; 当 n = 10000时 O(1)的算法需要1秒执行完毕。 O(n)的算法需要10,000秒 ≈ 2.7小时 执行完毕。 O(n2)的算法需要100,000,000秒 ≈ 3.17年 执行完毕。 O(n!)的算法需要XXXXXXXX(系统的计算器已经算不出来了)。 对于程序A,for(int i=0;i<1000;i++),当输入任意的n时循环次数均为1000,复杂度为O(1); 对于程序B,for(int i=0;i<n;i++),当输入任意的n时循环次数为n,复杂度为O(n)。
那么,相类似的,对于控件复杂度,我们可以理解为是空间复杂度,是对一个算法在运行过程中临时占用存储空间大小的量度。举个例子:
直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。 而一般的递归算法就要有O(n)的空间复杂度了,因为每次递归都要存储返回信息
以上是从百度百科截下来的原话,像堆排序,冒泡排序等空间复杂度都为1,而归并,基数则是n;简单来说一下原因,归并和基数排序中需要malloc动态分配内存空间,而冒泡,堆排序都是通过和自身元素换位置来排序的,最多交换变量时借助一个tmp变量;那么假如这四种排序同时对n(假设n非常大)个数据排序,归并和基数排序所maolloc的空间是大于n的,而堆和冒泡排序则是1或者是2;和n相比几乎可以忽略不计。
所以我们说堆,冒泡排序空间复杂度是O(1),而归并,基数则是O(n)。
类似,空间复杂度算法和时间复杂度也是一样的,主要看程序运行时开辟多少空间辅助程序运行;
嗯,到了这里的话,我们大致有了解题思路了,条件限制是不能有多层嵌套,和递归函数的出现,而辅助内存则是越少越好,不能有大量数据复制。
#include <stdio.h> int main() { int a[] = {1, 2, 0, 2, 1, 9, 3, 9, 0, -1}; int number1 = 0, number2 = 0; int i = 1, tmp = a[0], index = 0; for (; i < sizeof(a)/sizeof(*a); ++i) { //得出 3 ^ -1 tmp ^= a[i]; } number1 = tmp; number2 = tmp; // number1 = 0; // number2 = 0; // number1 = tmp; number2 = tmp; 时,number累积异或的结果为出现在另一组的那个数 // number1 = 0; number2 = 0; 时,number累积异或的结果为出现在本组的那个数 while (tmp) { //找出tmp从右数第一个为1的位置 if (1== (tmp & 1)) { break; } ++index; tmp >>= 1; } for (i = 0; i < sizeof(a)/sizeof(a[0]); ++i) { //以index位为1将数组分成两部分,每部分只含有一个只出现一次的数字 if (1 == ((a[i] >> index) & 1)) { //得到一个 number1 ^= a[i]; } else { //得到另外一个 number2 ^= a[i]; } } printf("%d %dn", number1, number2); return 0; }
相关文章推荐
- 帮你理解时间复杂度和空间复杂度
- 理解:时间复杂度和空间复杂度
- 时间复杂度和空间复杂度的理解
- 数据结构中时间复杂度和空间复杂度的理解
- 怎样理解时间复杂度和空间复杂度
- 有关算法时间复杂度和空间复杂度的浅析
- 时间复杂度和空间复杂度分析
- 数据结构&算法(二)_算法基础之前传(递归、时间复杂度、空间复杂度、二分查找)
- 算法的时间复杂度和空间复杂度-总结
- 各种排序算法的稳定性以及时间和空间复杂度分析
- 什么是时间复杂度和空间复杂度
- 常用排序算法的时间空间复杂度
- 各种排序算法比较(2):时间复杂度,空间复杂度
- java 快速排序 时间复杂度 空间复杂度 稳定性
- 常见排序算法及对应的时间复杂度和空间复杂度
- 实现一个数组中奇数和偶数分开,奇数在前部分,偶数在后部分 时间复杂度为O(n),空间复杂度为O(1)
- 时间复杂度和空间复杂度
- 对于一颗完全二叉树,要求给所有节点加上一个pNext指针,指向同一层的相邻节点;如果当前节点已经是该层的最后一个节点,则将pNext指针指向NULL;给出程序实现,并分析时间复杂度和空间复杂度。
- 仔细分析了下这7行,貌似时间复杂度,空间复杂度都不大,为嘛就是执行效率这么低?
- JAVA数据结构和算法:第一章(时间复杂度和空间复杂度)