声明:以下都是学的尚硅谷网课所记的笔记。可能会有一些错误,发现了会修改。
冒泡排序
介绍: 对待排序序列从前往后(从下标较小的元素开始),依次比较相邻元素的值,如果逆序则交换,使得值较大的元素逐渐从前往后移,就像水泡一样逐渐往上冒。
规则:
- 一共需要进行数组的大小减1次循环;
- 每一趟需要排序的次数逐渐减小;
- 优化: 如果发现某趟排序中,没有发生一次交换,则可以提前结束冒泡排序。
//只列出关键代码
int temp = 0;
boolean flag = false;
for(int i = 0; i < arr.length-1; i++){
for(int j = 0; j < arr.length-1-i; j++){
if(arr[j] > arr[i]){
flag == true;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
if(flag == false){ //一次也没有交换过,则可以提前结束
break;
} else{
flag = false; //重置标识为了进行下次判断
}
}
平均时间复杂度为O(n2),没优化前,80000个随机数据排序大概20s左右。
选择排序
介绍:从待排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。 例如从数据中选出最小的值arr[0],然后选出次最小的放在arr[1],依次排序直到完成排序。
规则:
- 共有数组大小减1轮排序。
- 先假定当前这个为最小数,然后和后面每个进行比较,有更小的则交换。
public static void selectSort(int[] arr){
for(int i = 0; i < arr.length-1; i++){
int minIndex = i;
int min = arr[i];
for(int j = i + 1; j < arr.length; j++){
if(min > [j]){
min = arr[j];
minIndex = j;
}
}
if(minIndex != i){
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
平均时间复杂度为O(n2),80000个随机数据排序用了大概3s左右。比冒泡快,因为冒泡排序需要交换很多次,费时。
插入排序
介绍:对待排序的元素以插入的方式找到该元素的适当位置,达到排序的目的。 用一个有序表和一个无序表,开始有序表中只有一个元素,无序表中有n-1个元素,每次从无序表中取第一个元素,将其值依次与有序表的值进行比较,将其插入到有序表中适当位置,完成排序。
代码参考这篇文章 插入排序–直接插入排序
平均时间复杂度为O(n2),80000个随机数据排序大概5s左右。
希尔排序
介绍: 也是一种插入排序,是一种改进版本,也称为缩小增量排序。把记录按下标的一定增量分组,对每组使用直接插入排序算法,随着增量逐渐减小,每组包含的数据越来越多,当增量减少至1时,整个数据集被分成一组,算法便终止。
原理参考这篇文章 希尔排序
步骤:
- 先把数据分为gap = length / 2组,gap为增量,然后对这n组数据分别进行插入排序;
- 再将数据分为gap1 = gap / 2组,再进行直接插入排序,直到最后只剩一组,进行最后一次插入排序。
public static void shellSort(int[] arr){
int temp = 0;
for(int gap = arr.length/2; gap>0; gap/=2){//gap为步长
for(int i=gap; i<arr.length; i++){
for(int j=i-gap; j>=0; j-=gap){
if(arr[j]>arr[j+gap]){
//插入时使用交换法
temp = arr[j];
arr[j] = arr[j+gap];
arr[j+gap] = temp;
}
}
}
}
}
平均时间复杂度为O(nlogn),80000个随机数据排序大概15s左右。可以在交换时用移位法优化。
改进版本:
public static void shellSort2(int[] arr){
//增量gap,并逐步缩小增量
for(int gap = arr.length/2; gap > 0; gap/=2){
//从第gap个元素,逐个对其所在组进行插入排序
for(int i = gap; i < arr.length; i++){
int j = i;
int temp = arr[j];
if(arr[j] < arr[j-gap]){
while(j-gap >=0 && temp < arr[j-gap]){
//移动
arr[j] = arr[j-gap];
j -= gap;
}
arr[j] = temp;
}
}
}
}
80000个随机数据花了1s左右。
快速排序
**介绍:**对冒泡排序的一种改进,通过一趟排序将要排序的数据分割成两部分,其中一部分所有数据比另一部分的所有数据都要小,然后再对这两部分分别进行快排,整个排序过程可以递归。平均时间复杂度为O(nlogn),80000个数据需要1s不到,优于希尔排序。
具体参考博文 快速排序
归并排序
介绍: 是一种稳定排序。利用归并思想实现的排序方法,采用经典的分治策略(将问题分成一些小问题然后递归求解,而治的阶段则将分的阶段得到的各答案“修补”在一起,分而治之)。归并排序需要额外的空间,以空间换时间,提高了排序效率。平均时间复杂度为O(nlogn),80000个数据需要1s不到,8000000个数据3s左右,跟快排差不多。
基数排序
介绍: 分配式排序,又称“桶子法”,通过数值的各个位的值,从低位开始将待排序的数按照这一位的值放到相应的编号为0~9的桶中,达到排序的目的。是一种稳定性排序,效率高,是桶排序的扩展,也是以空间换时间。
具体参考 基数排序详解以及java实现
思想: 将所有待比较数值统一为同样位数长度,数位较短的前面补0。然后从最低位开始依次进行一次排序。这样从最低位开始一直到最高位排序完,就完成了排序。
例:arr ={53, 3, 542, 748, 14, 214} (第一轮排序比较个位) -> {542, 53, 3, 14, 214, 748} (第二轮排序比较十位) -> {3, 14, 214, 542, 748, 53} (第三轮排序比较百位) -> {3, 14, 53, 214, 542, 748}.
注:进行多少轮排序取决于最高位数是多少位,上例最高位为千位,即3位,所以进行了3轮排序。如果有负数的数组,则不能用基数排序来做。
平均时间复杂度为O(n×k)。80000个数据1s不到,8000000个数据1s左右,比快排、归并快,且稳定。当运行80000000个数据时,占用内存太大,导致出现异常(OutOfMemoryError),缺点就是数据越多越占用空间。
堆排序
介绍:
- 堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,平均时间复杂度为O(nlogn),是一种不稳定的排序。
- 堆是具有以下性质的完全二叉树:每个节点值都大于或等于其左右子节点的值,称为大顶堆。其中左右子节点的值的大小关系没有要求。
- 每个节点值都小于或者等于其子节点值,称为小顶堆。一般升序采用大顶堆,降序采用小顶堆。
特点: arr[i] >= arr[2i+1], arr[i] >= arr[2i+2] ,i从0开始。80000个随机数据排序用了1s不到,8000000个数据大概2s左右。
注: 堆排序实际上没有创建树,而是用数组下标来表示的。
思想:
- 将待排序的序列构造成一个大顶堆;
- 整个序列的最大值就是堆顶的根节点;
- 将其与末尾交换,此时末尾就为最大值;
- 然后将剩余n-1个元素重新构造成一个大顶堆,这样就可以得到次大值,如此反复,就可以完成排序。
具体参考 Java实现堆排序和图解
总结:常用排序算法对比
名词解释:
- 稳定性:a原本在b前面,且a=b,排序后a仍然在b前面,就称该算法具有稳定性。
- 排序方式分为内排序和外排序。内排序:所有操作在内存中完成。外排序:由于数据过大,把数据放在磁盘中,通过磁盘和内存的数据传输才能进行排序。
- 时间复杂度:一个算法所耗费的时间。
- 空间复杂度:运行一个程序所需要的内存大小。
- n:数据规模。
- k:桶的个数。
- In-place:不占额外内存。
- Out-place:占额外内存。
---------------------------- 个人学习笔记----------------------------