趁着需要面试百度的机会,复习了常用的排序方法。
1、快速排序
平均时间复杂度:O(nlogn),最差情况时间复杂度:O(n^2),即序列以有序的情况
空间复杂度:O(1),不需要开辟额外的空间
实现细节:递归的分而治之,
在每一个递归中,将最后一个数作为比较数mid,pa指示小于的部分,pb指示大于等于mid的部分。
当pa=pb时,比较结束。将序列分割为小于mid、等于mid和大于mid三段,分别递归
2、计数排序
时间复杂度:O(n)
空间复杂度:O(码本)
实现细节:在知道元素取值范围的情况下使用
3、选择排序
时间复杂度:O(n^2)
空间复杂度:O(1)
不稳定:序列(7) 2 5 9 3 4 [7] 1第一轮遍历就将(7)放到了最后
实现细节:每一轮循环遍历后面n-i个元素,找出最小值与第i个元素交换
4、冒泡排序
时间复杂度:O(n^2)
空间复杂度:O(1)
实现细节:每一轮循环从i开始,两两比较x和x+1。
优化:若某一轮循环没有发生交换,则表明自元素i后的序列有序,对于局部有序序列可以有O(n)的性能
5、归并排序
时间复杂度:O(nlogn)
空间复杂度:O(n)
实现细节:首先递归的将序列等距离划分,向上返回时归并段两两比较,写入辅助数组,然后复制到对应的归并段位置,保证向上返回的归并段段内有序。
6、堆排序
一种树形选择排序,是对直接选择排序的改进。
时间复杂度:O(nlogn),其中O(n)为建堆的时间复杂度,O(nlogn)为每次调整堆顶元素的时间复杂度
空间复杂度:O(1)
应用场景:适合大数据排序场合。TOPK
实现细节:
堆排序包括两个基本步骤:建立最小(大)堆和排序。其中的核心操作HeapAdjust(Heap H, int s, int m)。
HeapAdjust输入参数为序列中的元素s和序列长度m。
初始条件:s元素左右子树皆为堆
递归的:比较s与左右子树根节点的大小,若s是最小值,则不发生交换,s保持了堆的特性,退出;若发生交换,则左/右子树的堆特性无法保持,递归的HeapAdjust发生交换的子树。
退出:原来以s为根节点的子树保持了堆的特性
建堆:从最后一个根节点length/2开始调整[length/2….0]这些根节点的堆特性。
有序输出:循环的将头结点与最后一个结点交换,然后调用HeapAdjust保持自根节点的堆特性。
堆排序源码:
HeapAdjust操作
主过程:
7、寻找最大的K个数
方法一:利用堆排序的特性
时间复杂度:O(nlogK)
首先建立一个K元素的最小堆,即根节点小于左右子树节点;
接着遍历数组元素,若x>root,则root<-x,调整堆性质O(logK)
整个过程为O(n*logK)
方法二:线性期望时间O(n)
采用randomized-select方法,从数组中随机选取一个数x,将数组划分为小于x的Sa和大于x的Sb的两个部分,i为x的坐标
若i=K,则x就是第K个数,同时可以输出前k个数
若i > K,则randomized-select(Sa,K)
若i < K,则randomized-select(Sb, K-i)
但是在最坏情况下,具有O(n^2)的时间复杂度,也就是每次划分极不均匀,选到的随机数不是最大,就是最小,每次划分为O(n),共要划分n次