文章目录
先提两个问题:
- 插入排序为什么比冒泡常用?
(两者的最优、平均、最差时间复杂度都相同,且都是原地排序——时间复杂度O(1),并且都是)
因为冒泡排序的赋值交换次数比插入排序多(插入排序每次交换只需要一次赋值)
- 归并的最差时间复杂度比快排低,但是为什么比快排应用更广泛
因为(没想好)
如何选择
- 折半查找:记录数较多(但不是海量,数据不分布均匀)
- 桶排序:数据海量,全量排序,数据分布均匀
- 快速排序:就平均时间而言,快排是所有排序算法中效果最好的
- 堆排序:数据多,求TopN的场景
- 基数排序:数据多,全量排序,但是数据的范围小,数据可以不均匀(比如高考分数排序)【计数排序也行,但是计数排序处理小数或负数需要扩大范围】
【再者,比如对100万个手机号码做排序,使用计数排序就不好,使用快排也不好。这时候使用基数排序或者桶排序更佳】
使用基数排序时,如果数字(或字符串)不等长怎么办?
在后面补0 - 直接插入:数据较少,或者数据基本有序
- 若n较小(如n≤50),可采用直接插入或直接选择排序
- 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序
快排、冒泡、插入、shell排序,会受到初始序列的影响。
综述
排序大的分类可以分为两种:内排序和外排序。
内排序是指所有的数据已经读入内存,在内存中进行排序的算法。排序过程中不需要对磁盘进行读写。同时,内排序也一般假定所有用到的辅助空间也可以直接存在于内存中。
外排序即内存中无法保存全部数据,需要进行磁盘访问,每次读入部分数据到内存进行排序。
时间复杂度和空间复杂度对比表(有必要记忆下)[1]
完整代码-顺序表版本
package list;
import java.util.Arrays;
/**
* <p>Descirption:分别用顺序表以及链表的方式实现常用的几种排序算法</p>
*
* @author 王海
* @version V1.0
* @package list
* @date 2018/4/7 16:25
* @since 1.0
*/
public class MultiSortArrayAndList {
private static final float[] FORSORT = {
10, -2, 3, -1111111111.9999999f, 55, 32.8f, 66, 7.8f, 10233.88f, 52222, 45.67f, 13, 654, 99999, 1000000.666f, 77.77f, 3214, 66, 736.2f, 5, 23, 65465, 54783, 9999.999f, 5664645, -33, -66.5566778899f, 2147483647.555f, 2147483647};
/**
* 冒泡排序,带标志位(属于交换排序)
*
* @param forSort 等待排序的序列
*/
private void bubbleSort(float[] forSort) {
String name = "冒泡";
long begin = System.nanoTime();
if (forSort.length <= 1) {
printHelper(name, forSort, begin);
return;
}
// 冒泡排序
for (int i = 0; i < forSort.length - 1; i++) {
// 假设下一次不需要再排序
boolean sorted = false;
for (int j = 0; j < forSort.length - i - 1; j++) {
// 这里-i是因为前几次的排序已经沉底了i个数
if (forSort[j] > forSort[j + 1]) {
float temp = forSort[j];
forSort[j] = forSort[j + 1];
forSort[j + 1] = temp;
// 排序过了
sorted = true;
}
}
if (!sorted) {
break;
}
}
printHelper(name, forSort, begin);
}
/**
* 快速排序(属于交换排序)
*
* @param forSort 等待排序的序列
* @param left 左
* @param right 右
*/
private float[] quickSort(float[] forSort, int left, int right) {
if (forSort.length <= 1) return;
int i = left;
int j = right;
// 递归的结束条件
if (left < right) {
float benchmark = forSort[left];
// 下面不能写if,因为并不是说左右指针各移动一次就能保证左右指针“碰头”;完成一次排序
while (i < j) {
// 右侧比较,小则往前放,大则右指针继续“向左前进”
while (i < j && forSort[j] > benchmark) {
--j;
}
// while循环退出,如果left还小于right,就该交换数据了
if (i < j) {
forSort[i++] = forSort<