性能优化主要应该着眼于I/O和内存管理,I/O系统调用通常发生在毫秒级,CPU调用在亚微秒级。
一、散列
1、散列函数一般需要快速工作,需要满足以下条件:
(1)最多含有一个除法运算(一般是最后的取摸运算)
(2)生成广泛的散列键
(3)不依赖于将促使产生聚集的数据属性
通用的散列函数:HashPJW,ElfHash
2、解决冲突的方法:
(1)线性再散列,简单的按顺序遍历散列表,寻找下一个位置(缺点:负载因子大于0.5时性能太差)
(2)非线性再散列,计算出一个新的散列值(缺点:负载因子大于0.5时性能太差)
(3)外部拉链法,散列表中的每个槽视为相同散列值的数据项链表的头部散列表将发生冲突的项添加到该链表中。(缺点:需要更多的存储空间,每次探寻需要多些时间因为需要间接使用指针)
影响散列表性能的最大概念:负载因子,插入表中元素个数/可用槽总数,负载因子越大性能越差。
当负载因子大于0.5时,再散列将不是可行的方案,性能太差。
建议:
(1)创建大小合理的散列表,不过在负载因子0.2以下的散列表,扩展他们将不会提供更好的性能。
(2)确保每个散列表的槽数是一个素数。
(3)在具有代表性的数据上测试散列表并度量结果。
(4)预先考虑冲突,在可能的地方预先使用拉链法。
二、排序
1、冒泡排序,N的平方
连续的扫描待排序的记录,每次比较相邻两个中较大的一个值,使其更接近顶部。
对正序排列很快,逆序和乱序很慢,优点:实现简单。
2、插入排序,N的平方
每次在一个有序的列表中找到需要插入元素的准确位置后插入。
3、希尔排序,
插入排序的变体,将序列依次拆成n/2,n/4 ,... ,1组数据分别进行插入排序。
4、快速排序,NlogN
快速排序是对冒泡排序的一种改进。
快速排序采用的思想是分治思想。
快速排序是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。
举例说明一下吧,这个可能不是太好理解。假设要排序的序列为
2 2 4 9 3 6 7 1 5 首先用2当作基准,使用i j两个指针分别从两边进行扫描,把比2小的元素和比2大的元素分开。首先比较2和5,5比2大,j左移
2 2 4 9 3 6 7 1 5 比较2和1,1小于2,所以把1放在2的位置
2 1 4 9 3 6 7 1 5 比较2和4,4大于2,因此将4移动到后面
2 1 4 9 3 6 7 4 5 比较2和7,2和6,2和3,2和9,全部大于2,满足条件,因此不变
经过第一轮的快速排序,元素变为下面的样子
[1] 2 [4 9 3 6 7 5]
之后,在把2左边的元素进行快排,由于只有一个元素,因此快排结束。右边进行快排,递归进行,最终生成最后的结果。
5、堆排序
若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。
由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
堆排序方法对记录数较少的文件并不值得提倡,但对n较大的文件还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上。
堆排序在最坏的情况下,其时间复杂度也为O(nlogn)。相对于快速排序来说,这是堆排序的最大优点。此外,堆排序仅需一个记录大小的供交换用的辅助存储空间。