内部排序:
插入类排序
直接插入排序:将其一串数字看为两个表,前面为有序,后面为无序,我们只需要将无序的数字插入到有序的表中即可。
当数字为逆序的时候,最坏比较次数为(n-1)n/2
而当数字有序的时候,最少的比较次数为O(n)
算法是稳定的
可用于线性表和链表
时间复杂度:O( n^(2) )
空间复杂度:O(1)
折半插入排序:
与折半插入最大的区别就是减少了比较的次数,由于前面的表是有序的,所以可以利用折半查找减少比较次数。
算法是稳定的
时间复杂度:O( n^(2) )
空间复杂度:O(1)
希尔排序:(依次减少增量序列)
将数字进行分组,组内进行比较和排序
时间复杂度:O( n^(2) )
空间复杂度:O(1)
算法是不稳定的
适合于基本有序的顺序表
选择类排序
简单选择排序:
每次都选择最小的一个与最前面的进行交换(每次交换后都能确定一个数字的位置)
时间复杂度:O( n^(2) ) 与初试次序无关
空间复杂度:O(1)
比较次数始终是n(n-1)/2
堆排序:
首先要先建立大根堆(小跟堆),特点:根>=左,右(跟<=左,右)
大根堆的建立依赖于完全二叉树,首先我们要先找到分支结点
根据完全二叉树的性质,我们知道根结点如果i,则它的左孩子结点为2i,右孩子结点为2i+1
根结点始终是小于n/2下取整的
建立大根堆:
首先找到最下层的分支结点进行检查,看是否符合大根堆的特性,先比较左孩子和右孩子找到其中最大的,再和分支结点进行比较(注意这里的比较次数是2),如果分支结点大于最大的孩子结点,不交换,否则,进行交换,使其达到大根堆的特性。
当让分支结点和孩子结点进行交换的时候,可能会导致下一层的分支结点不符合大根堆的特性,这个是要就要让小元素下坠
最后我们得到了大根堆,得到大根堆之后,我们要进行选择最大的元素就非常容易,即第一个元素,我们选择第一个元素(最大的)放到最后,在让待排序的最后一个放到堆顶,在进行调整大根堆,重复上面得步骤,我们最终得到了正确的排序
算法是不稳定的
空间复杂度为O(1)
时间复杂度为O( nlog2(n) )
时间复杂度的分析主要取决于建立大根堆和排序
建立大根堆为O(n)
建立大根堆每一个结点下坠,最多要比较2次(孩子之间进行比较一次,父与子在比较一次)
建堆过程关键字数的对比不会超过4n
而排序则需要进行n-1趟,每趟为O( log2(n) )
所以排序需要O( nlog2(n) )
而时间复杂度也就是是O( nlog2(n) ),与初试次序
交换类排序
冒泡排序:
就像命名是一致的,泡泡会向上走
从最后一个出发,邻序的每两个进行比较,知道比较到第一个为止
每一趟都可以确定一个元素的位置
交换次数是3n(n-1)/2
是因为temp = a a = b b = temp
时间复杂度:O( n^(2) )
空间复杂度:O(1)
快速排序:选择一个基准
分别设置两个指针,一个指向头(基准),一个指向尾,(在这里基准要提前设置变量保存)
检查尾指针是否大于基本如果大于基准,则指针向前移动,如果不大于基准,这将此指针的数字赋给头指针(覆盖掉基准),头指针向前移动进行检查,如果不大于基准就一直向前直到头指针等于尾指针,如果大于基本则将头指针的数字赋给尾指针,尾指针在接着检查,知道尾指针等于头指针的时候,结束确定了基准的位置。
使用了分治的思想
时间复杂度:O( nlog2(n) )
空间复杂度:O(n)
归并排序:
二路归并
将两个或以上的有序数组进行排序
2路归并每次选出一个元素要比较1次
而k路归并每选出一个元素要比较n次
首先把一串数字中的每一个元素都看做是一个无序表在表内进行排序得到有序表,之后再将以上形成的两个有序表组成的一个无序表在表内进行排序得到有序表,知道最后合并成为一个有序表。
此算法也是利用了递归的方法
在此算法中利用了辅助数组
空间复杂度:O(n)
时间复杂度:O( nlog2(n) )
可以将此看为一个倒立的二叉树
树高为h 排序趟数就为h-1
而n<=2^(h-1) h-1=log2(n) 的上取整
每趟比较次数不会超过n次
基数排序
按照关键权重进行的
稳定性:必须稳定
时间复杂度:和初始次序无关,是O(d(n+r)),进行d趟排序,一趟需要O(n),一趟收集需要O®
空间复杂度:O®,辅助空间r个队列
外部排序
由于文件太大,内存无法全部读入
这是就要用到K路归并,依次的将文件读入进行排序在写回,由于要读写磁盘快,所以外部排序的总时间是很长的。
把一个k路归并的过程看做是一个k叉树,树高为h,则需要进行h-1趟的排序,如果有r个初试归并段,则k^(h-1)>=r,即h-1=log(k,r)向上取整=归并趟数。
由上述的表达我们可以知道了只要能够增大k,或者减小r就可以减少趟数。但是在增大k的同时选出一个关键字的比较次数又会增大。
减少关键字比较次数引出了败者树
败者树每次都是记录是败的结点来自那个归并段
当我们取出最小的之后,取走的结点的那个归并段会再次进行比较
假设有k个归并段,第一次选出关键的比较次数依旧是k-1次,但是对于下一次的比较次数就是树的高度,即2^(h-1)>=k
减少初试归并段引出了置换选择排序
将不同数目的归并段进行归并又引入了最佳归并树
最佳归并树利用了哈夫曼树的思想
利用严格的k叉树,使得读写磁盘的次数最少
如果不能严格构成k叉树我们就需要补充虚段使之成为严格k叉树
(初试归并段的数量-1)%(k-1)=0,则不需要补充
(初试归并段的数量-1)%(k-1)=u,则需要补充k-1-u个虚段