赞
踩
- 为什么插入排序比冒泡排序更受欢迎?
- 如何用快排思想在O(n)内查找第K大元素?
- 如何根据年龄给100万用户数据排序?(线性排序)
- 如何实现一个通用的、高性能的排序函数?(排序优化)
我最近在系统整理一些 Java 后台方面的面试题和参考答案,有找工作需求的童鞋,欢迎关注我的 Github 仓库,如果觉得不错可以点个 star 关注 :
是否原地排序 | 是否稳定 | 最好、最坏、平均时间复杂度 | 空间复杂度 | 是否基于比较 | |
---|---|---|---|---|---|
冒泡排序 | 是 | 是 | O(n)、 O(n^2)、 O(n^2) | O(1) | 是 |
插入排序 | 是 | 是 | O(n)、 O(n^2)、 O(n^2) | O(1) | 是 |
选择排序 | 是 | 否 | O(n^2)、 O(n2)、O(n2) | O(1) | 是 |
希尔排序 | 是 | 否 | O(n)、O(n2)、O(n1.3) | O(1) | 是 |
快速排序 | 是 | 否 | O(nlogn)、O(n^2)、O(nlogn) | O(logn)~O(n) | 是 |
归并排序 | 否 | 是 | O(nlogn)、O(nlogn)、O(nlogn) | O(n) | 是 |
计数排序 | 否 | 是 | O(n+k)、O(n+k)、O(n+k),k 是数据范围 | O(n+k) | 否 |
桶排序 | 否 | 是 | O(n)、O(n)、O(n) | O(N+M),N表示待排数据个数,M表示桶个数 | 否 |
基数排序 | 否 | 是 | O(nk)、O(nk)、O(n*k),k 是维度 | O(n+k) | 否 |
堆排序 | 是 | 否 | O(nlogn)、O(nlogn)、O(nlogn) | O(1) | 是 |
1. 为什么插入排序比冒泡排序更受欢迎?
从代码实现上来看,冒泡排序的数据交换要比插入排序的数据移动要复杂,冒泡排序需要 3 个赋值操作,而插入排序只需要 1 个。
// 冒泡排序中数据的交换操作:
if (a[j] > a[j+1]) { // 交换
int tmp = a[j];
a[j] = a[j+1];
a[j+1] = tmp;
flag = true;
}
// 插入排序中数据的移动操作:
if (a[j] > value) {
a[j+1] = a[j]; // 数据移动
} else {
break;
}
2. 如何用快排思想在O(n)内查找第K大元素?
比如,4, 2, 5, 12, 3 这样一组数据,第 3 大元素就是 4。
我们选择数组区间 A[0…n-1] 的最后一个元素 A[n-1] 作为 pivot,对数组 A[0…n-1] 原地分区,这样数组就分成了三部分,A[0…p-1]、A[p]、A[p+1…n-1]。
如果 p+1=K,那 A[p] 就是要求解的元素;如果 K>p+1, 说明第 K 大元素出现在 A[p+1…n-1] 区间,我们再按照上面的思路递归地在 A[p+1…n-1] 这个区间内查找。同理,如果 K<p+1,那我们就在 A[0…p-1] 区间查找。
我们再来看,为什么上述解决思路的时间复杂度是 O(n)?
第一次分区查找,我们需要对大小为 n 的数组执行分区操作,需要遍历 n 个元素。第二次分区查找,我们只需要对大小为 n/2 的数组执行分区操作,需要遍历 n/2 个元素。依次类推,分区遍历元素的个数分别为、n/2、n/4、n/8、n/16.……直到区间缩小为 1。
如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于 2n-1。所以,上述解决思路的时间复杂度就为 O(n)。
3. 如何根据年龄给100万用户数据排序?(线性排序)
假设年龄的范围最小 1 岁,最大不超过 120 岁。可以遍历这 100 万用户,根据年龄将其划分到这 120 个桶里,然后依次顺序遍历这 120 个桶中的元素。这样就得到了按照年龄排序的 100 万用户数据。
4. 如何实现一个通用的、高性能的排序函数?(排序优化)
1.数据量不大时,可以采取用时间换空间的思路
2.数据量大时,优化快排分区点的选择
3.防止堆栈溢出,可以选择在堆上手动模拟调用栈解决
4.在排序区间中,当元素个数小于某个常数是,可以考虑使用O(n^2)级别的插入排序
5.用哨兵简化代码,每次排序都减少一次判断,尽可能把性能优化到极致
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。