一. 时间复杂度和空间复杂度:
1.时间复杂度:
一个算法执行所需要进行的计算工作量,通常用“大O表示法表示”,例如O(1),O(n),O(logn),O(nlogn),O(n^2), O(2^n),O(2!)等。算法的时间复杂度反映了程序执行时间随输入规模增长而增长的量级,也反映了算法的优劣程度。
计算时间复杂度的方法:
-
找到最基本的执行语句。(可能存在多条)
-
计算该语句执行次数的数量级。(有多条最基本的执行语句时,平行相加即可)
-
用“大O法”和“渐进时间复杂度”的概念来表示时间复杂度。
2.空间复杂度:
一个算法执行所需要的内存空间,是对一个算法在运行过程中临时占用的存储空间大小的量度。
一个算法在计算机上占用的内存包括:程序代码所占用的空间,输入输出数据所占用的空间,辅助变量所占用的空间这三个方面。
计算空间复杂度的方法:
- 忽略常数,用O(1)表示
- 递归算法的空间复杂度=递归深度N*每次递归所要的辅助空间
- 对于单线程来说,递归有运行时堆栈,求的是递归最深的那一次压栈所耗费的空间的个数,因为递归最深的那一次所耗费的空间足以容纳它所有递归过程。
二. 选择排序、冒泡排序、插入排序:
1.选择排序:
在长度为n的序列中,每次遍历n-i次序列,(i=0,1,2,... ,n)将序列后(n-i)个元素中最大/小的元素放到第i个位置上。
2.冒泡排序:
在长度为n的序列中,每次遍历整个序列,比较前后两个元素,如果后面的这个元素比前面的这个元素小,交换两个元素,这样多次遍历后,知道序列有序。
3.插入排序:
在长度为n的序列中,将序列第 i 个元素有序地插入到前(i-1)个已经有序的元素中,直到序列有序。
排序方法 | 平均时间复杂度 | 辅助空间 |
选择排序 | O(n^2) | O(1) |
冒泡排序 | O(n^2) | O(1) |
插入排序 | O(n^2) | O(1) |
三. 二分法的使用和复杂度的分析:
二分法的使用前提是序列必须有序。
二分法通常用于二分查找。所谓二分,其实就是每次与序列中的中值比较,如果比中值小,则去中值的左边序列中查找,如果比中值大,则去中值的右边序列中查找,如果与中值相等,则查找成功。就这样按照上面的步骤循环查找,直到找到或者序列长度为1为止。
二分法的时间复杂度为O(logn)
四. 一道时间复杂度很低的利用异或(^)运算解决的问题:
整数型数组中,每个元素均出现两次,除了一个元素例外,如何找出这个元素?能否设计一个线性时间的算法,且不需要额外的存储空间?
使用异或运算符(^)可以O(n)的时间复杂度解决这个问题。
异或运算符的特点是:数a两次异或同一个数b(a=a^b^b)仍然为原值a。对于任何数x,都有x^x=0,x^0=x。
其实O(n)的算法不容易一下子想到,先说说常规的解决思路,有如下两种:
1、对元素的出现次数进行统计,可进行n*n循环,判断元素是否只出现了一次。这样时间复杂度为O(n^2), 不需要额外空间。
2、先对元素进行排序,然后进行相邻两元素的对比,如a1和a2对比,a3和a4对比,如果不同,则前一个元素(a1、a3)就是所要查找的元素。这样的时间复杂度还是比O(n)高。
这两种解法的时间复杂度都比O(n)更高。但是,如果你运用了异或运算符的特点,那么这个问题就很容易解决了,算法复杂度为O(n),且不需要额外空间,像这样:
int singleNumber(int A[], int n) {
int result = 0;
for (int i = 0; i<n; i++)
{
result ^=A[i];
}
return result;
}
五. 常见时间复杂度的比较:
算法 | 算法时间复杂度 |
执行时间与输入无关 | O(1)————常量时间 |
线性查找 | O(n)————线性时间 |
二分查找、欧几里得GCD | O(logn)——--对数时间 |
归并排序、堆排序、快速排序(平均时间) | O(nlogn)——n倍对数时间 |
选择排序、插入排序、冒泡排序、交换排序、快速排序(最差时间) | O(n^2)———平方时间 |
递归算法(汉诺塔、斐波那契数) | O(2^n)———指数时间 |
六.用master公式估算递归函数的时间复杂度:
master公式:也叫主定理。它提供了一种通过渐近符号表示递推关系式的方法。应用Master定理可以很简便的求解递归方程。
T (n) = a*T(n/b) + O (N^d)
其中T(n)是样本量为N时的时间复杂度,n/b是划分成子问题的规模,子问题发生了a次,将规模为n的问题分解为a个子问题,后面O(N^d)是除去调用子过程之外的时间复杂度。
①当d<log(b,a)时,时间复杂度为O(n^(logb a))
②当d=log(b,a)时,时间复杂度为O((n^d)*logn)
③当d>log(b,a)时,时间复杂度为O(n^d)
七.对数器的使用:
对数器的作用
对数器可以验证算法是否正确, 在比赛或者笔试的时候,如果需要大量的测试用例,而且不知道是所写的算法能够满足要求就可以使用。
对数器的使用
1. 有一个你想测试的算法a
2.实现一个绝对正确但复杂度高的算法b (必须是正确的,不论复杂度高低)
3.实现一个随机样本产生器 (样本的长度尽量小一点,出现错误之后方便查看)
4. 实现比对算法a和b的方法
5. 多次(100000+)比对a和b来验证a是否正确
6. 如果有样本出错,则打印出来分析
7. 当对此对比测试都正确时,可以基本判断算法a正确