排序算法
性质记忆
1、关于稳定性
不稳定: 快选堆希(快速排序、选择排序、堆排序、希尔排序)
稳 定: 插冒归计基(简单插入排序、冒泡排序、归并排序、计数排序、基数排序)
2、关于时间复杂度
平方阶 (O(n2)) 排序
各类简单排序:直接插入、直接选择和冒泡排序。
线性对数阶 (O(nlog2n)) 排序
快速排序、堆排序和归并排序;
O(n1+§)) 排序,§ 是介于 0 和 1 之间的常数
希尔排序
线性阶 (O(n)) 排序
基数排序,此外还有桶、箱排序。
3、关于移动次数和关键字顺序无关的排序
堆排序、归并排序、选择排序、基数排序
In-place:占用常数内存,不占用额外内存
Out-place:占用额外内存
冒泡排序
每次遍历,都将最大的元素移到最后。
相邻两个元素之间的比较。
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
public void bulbSort(int[] arr) {
// 当前个与后一个相比,所以外循环只需n-1次
for (int i = 0; i < arr.length-1; i++) {
// 内循环比较大小,因为当第i次循环完后,最后的i+1个已排完序,下一次可以不用参与
// 如:3 1 4 2
// 第i=0次循环完(4-1-0=3次):1 3 2 4,最后一个排完序,下一次可以不用参与
// 第i=1次循环完(4-1-1=2次):1 2 3 4,最后两个排完序,下一次可以不用参与
for (int j = 0; j < arr.length - i - 1; j++) {
// 相邻元素两两对比,如果第一个比第二个大,就交换他们两个
if (arr[j]>arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
选择排序
在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
- 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
- 重复第二步,直到所有元素均排序完毕。
public void selectionSort(int[] arr) {
// 当前个与后一个相比,所以外循环只需n-1次
for (int i = 0; i < arr.length - 1; i++) {
// 每次循环,i前面的都是已经排完序了的
// 初始将当前i位置的数认为是最小的
int minIndex = i;
// 从i后面的所有数中,寻找值最小的数
for (int j = i+1; j < arr.length; j++) {
// 判断是否比当前已知的最小的数还要小
if (arr[j]<arr[minIndex]) {
// 将最小数的索引保存
minIndex = j;
}
}
// 如果两者不相等,说明存在比它更小的数,需要交换
if (i!=minIndex){
// 将i位置的数与最小值的数交换
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
}
插入排序
依次将元素插入对应位置,不符合的元素后移。
在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
当前元素与它前面所有元素的对比。
- 从第一个元素开始,该元素可以认为已经被排序;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5。