算法与数据结构(二):排序
趁着需要面试百度的机会,复习了常用的排序方法。
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操作
- public static void sift(int[] input, int s, int length) {
- int left = 2 * s + 1;
- int min = left;
- if (s > length / 2 - 1) return; //判断向下递归的退出条件
- //寻找根、左子树根、右子树根三者间的最小值
- if (left + 1 < length && input[left] > input[left + 1])
- min = left + 1;
- if (input[s] <= input[min]) {
- return;
- } else {
- //交换,破坏了子树堆的特性,递归的调整
- int tmp = input[s];
- input[s] = input[min];
- input[min] = tmp;
- sift(input, min, length);
- }
- }
主过程:
- public static void heapsort(int[] input) {
- //建堆
- for (int i = input.length / 2 - 1; i >= 0; i--) {
- sift(input, i, input.length);
- }
- //输出堆头,调整堆头
- for (int i = input.length - 1; i >= 0; i--) {
- int tmp = input[0];
- input[0] = input[i];
- input[i] = tmp;
- sift(input, 0, i);
- }
- }
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次