概述
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
我们这里说说八大排序就是内部排序。
排序算法稳定性:
通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。
稳定性的好处:排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如果排序算法稳定,可以避免多余的比较;
稳定的排序算法:直接插入排序、冒泡排序、归并排序、基数排序
不稳定的排序算法:希尔排序、简单选择排序、堆排序、快速排序
1.插入排序—直接插入排序(Straight Insertion Sort)
算法描述:对于给定的一个数组,初始时假设第一个记录自成一个有序序列,其余记录为无序序列。接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直至最后一个记录插入到有序序列中为止。
稳定性分析:如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的。
算法实现1:容易理解些
public static void insertionSort(int[] a) {
int tmp;
for (int i = 1; i < a.length; i++) {
for (int j = i; j > 0; j--) {
if (a[j] < a[j - 1]) {
tmp = a[j - 1];
a[j - 1] = a[j];
a[j] = tmp;
}
}
}
System.out.println("insertionSort" + Arrays.toString(a));
}
算法实现2: 算法实现1的优化
public static void insertionSort2(int[] a) {
for (int i = 1; i < a.length; i++) {
int temp = a[i];
int j = i - 1;
for (; j >= 0 && a[j] > temp; j--) {
// 将大于temp的值整体后移一个单位
a[j + 1] = a[j];
}
a[j + 1] = temp;
}
System.out.println("insertionSort2" + Arrays.toString(a));
}
2.插入排序—希尔排序(Shell`s Sort)
算法描述:希尔排序也成为“缩小增量排序”,其基本原理是,现将待排序的数组元素分成多个子序列(例如第1个元素 和 第5个元素构成一个子序列),使得每个子序列的元素个数相对较少,然后对各个子序列分别进行直接插入排序,待整个待排序列“基本有序”后,最后在对所有元素进行一次直接插入排序。因此,我们要采用跳跃分割的策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。希尔排序是对直接插入排序算法的优化和升级。
所谓的基本有序,就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间,例如{2,1,3,6,4,7,5,8,9,}就可以称为基本有序了。但像{1,5,9,3,7,8,2,4,6}这样,9在第三位,2在倒数第三位就谈不上基本有序。
稳定性分析:希尔排序的关键并不是随便分组后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式移动,使得排序的效率提高。需要注意的是,增量序列的最后一个增量值必须等于1才行。
另外,由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。
经过第二趟排序后,数组基本有序。当增量为1时,其实做的就是 直接插入排序。
算法实现1:容易理解些
public static void shellSort(int[] a) {
int tmp;
for (int increment = a.length / 2; increment > 0; increment /= 2) {
for (int i = increment; i < a.length; i++) {
for (int j = i; j >= increment; j -= increment) {
if (a[j] < a[j - increment]) {
tmp = a[j - increment];
a[j - increment] = a[j];
a[j] = tmp;
}
}
}
}
System.out.println("shellSort2" + Arrays.toString(a));
}
算法实现2:算法实现1的优化
public static void shellSort2(int[] a) {
for (int increment = a.length / 2; increment > 0; increment /= 2) {
for (int i = increment; i < a.length; i++) {
int temp = a[i];
int j = i - increment;
for (; j >= 0 && a[j] > temp; j -= increment) {
// 将大于temp的值整体后移increment单位
a[j + increment] = a[j];
}
a[j + increment] = temp;
}
}
System.out.println("insertionSort2" + Arrays.toString(a));
}
3.选择排序—简单选择排序(Simple Selection Sort)
算法描述:在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
稳定性分析:举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
算法实现:
public static void selectSort(int[] array) {
int position = 0;
for (int i = 0; i < array.length; i++) {
int j = i + 1;
position = i;
int temp = array[i];
for (; j < array.length; j++) {
if (array[j] < temp) {
temp = array[j];
position = j;
}
}
array[position] = array[i];
array[i] = temp;
}
System.out.println("selectSort" + Arrays.toString(array));
}
4.选择排序—堆排序(Heap Sort)
算法描述:
稳定性分析:
算法实现:
5.交换排序—冒泡排序(Bubble Sort)
算法描述:
稳定性分析:
算法实现:
6.交换排序—快速排序(Quick Sort)
算法描述:
稳定性分析:
算法实现:
7.归并排序(Merge Sort)
算法描述:
稳定性分析:
算法实现:
8.桶排序/基数排序(Radix Sort)
算法描述:
稳定性分析:
算法实现: