十大排序算法
冒泡排序
冒泡排序也叫做起泡排序。
执行流程
- ① 从头开始比较每一对相邻元素,如果第1个比第2个大,就交换它们的位置。执行完一轮后,最未尾那个元素就是最大的元素。
- ② 忽略①中曾经找到的最大元素,重复执行步骤①,直到全部元素有序。
这是一个标准的冒泡排序。
/**
* 冒泡排序-无优化
*/
public class BubbleSort1<T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
for (int end = array.length - 1; end > 0; end--) {
for (int begin = 1; begin <= end; begin++) {
if (cmp(begin, begin - 1) < 0) {
swap(begin, begin - 1);
}
}
}
}
}
冒泡排序优化一
此优化不一定有用,只有在有序的情况下时间复杂度更低
这个优化并不会提高冒泡排序的平均性能,只是针对极端条件进行处理。
如果序列已经完全有序,可以提前终止冒泡排序。
/**
* 冒泡排序-优化1
* 如果序列已经完全有序,可以提前终止冒泡排序
*/
public class BubbleSort2<T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
for (int end = array.length - 1; end > 0; end--) {
boolean sorted = true;
for (int begin = 1; begin <= end; begin++) {
if (cmp(begin, begin - 1) < 0) {
swap(begin, begin - 1);
sorted = false;
}
}
if (sorted) break;
}
}
}
冒泡排序优化二
这个优化是在优化一的基础上,增大极端条件的范围,从完全有序变为尾部局部有序。
如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数。
/**
* 冒泡排序-优化2
* 如果序列尾部已经局部有序,可以记录最后1次交换的位置,减少比较次数
*/
public class BubbleSort3<T extends Comparable<T>> extends Sort<T>{
@Override
protected void sort() {
for (int end = array.length - 1; end > 0; end--) {
// 设为1是因为:如果这轮遍历一次交换都没进行
// 则说明序列后面已经有序, 则 end = sortedIndex = 1
// end--后等于0,会直接跳出循环
int sortedIndex = 1; //为完全有序做准备
for (int begin = 1; begin <= end; begin++) {
if (cmp(begin, begin - 1) < 0) {
swap(begin, begin - 1);
// 记录最后1次交换的位置,减少比较次数
sortedIndex = begin;
}
}
end = sortedIndex;
}
}
}
复杂度与稳定性
复杂度
最好时间复杂度:完全有序,只需要经过一趟扫描
稳定性
冒泡排序属于 In-place
冒泡排序属于稳定的排序算法
稍有不慎,稳定的排序算法也能被写成不稳定的排序算法,比如下面的冒泡排序代码是不稳定的。
选择排序
执行流程:
- ① 从序列中找出最大的那个元素,然后与最未尾的元素交换位置
执行完一轮后,最未尾的那个元素就是最大的元素。 - ② 忽略①中曾经找到的最大元素,重复执行步骤①。
/**
* 选择排序
*/
public class SelectionSort<T extends Comparable<T>> extends Sort<T> {
@Override
protected void sort() {
for (int end = array.length - 1; end > 0; end--) {
int max = 0;
for (int begin = 1; begin <= end; begin++) {
if (cmp(max, begin) < 0) {
max = begin;
}
}
swap(max, end);
}
}
}
选择排序的交换次数要远远少于冒泡排序,平均性能优于冒泡排序。
复杂度与稳定性
堆排序
执行流程:
- 对序列进行原地建堆(heapify)O(N)
- 重复执行以下操作(依次取出堆中最大值,并删除),直到堆的元素数量为 1 ,N-I次 O(N)
- 交换堆顶元素与堆尾元素(把最大值放到最后面)O(1)
- 堆的元素数量减 1(不管最后已经放到最后的最大值)O(1)
- 对 0 位置进行 1 次 siftDown (建堆)操作O(log(N))
时间复杂度:O(N+Nlog(N)) --> O(NlogN)