插入排序、希尔排序、选择排序、归并排序。
插入排序等时间复杂度为O(N^2),任何通用的排序算法的时间复杂度都是O(N Log N)。
一、插入排序
import java.util.Arrays;
public class App {
public static void main(String[] args) {
int[] testArray = {25, 221, 1, 33, 89, 76, 5, 9};
insertionSort(testArray);
}
// 升序排序
public static void insertionSort(int[] array) {
int j;
// 从位置1开始,因为默认 0 上的元素被认为是一个排序好的列表
for (int i = 1; i < array.length; i++) {
// 一次比较开始,本次要比较的数是tmp
int tmp = array[i];
// 从当前位置往前比较,一边比较一边往后挪一格位置给自己,直到找到一个比自己小的元素为止
for (j = i; j > 0; j--) {
// 停止条件
if (array[j - 1] <= tmp) {
break;
}
array[j] = array[j - 1]; // 往后腾出一格
}
// 一次比较结束,结束的条件是array[j - 1] <= tmp,所以把值赋值给array[j]
array[j] = tmp;
// 打印一次结果
printArray(array);
}
}
public static void printArray(int[] array) {
System.out.println(Arrays.toString(array));
}
}
二、希尔排序
希尔排序是对直接插入排序的改进。因为当向一个已经有序的列表中插入一个元素的效率是比较高的,所以希尔排序的基本思想是:
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
25, 221, 1, 33, 89, 76, 5, 9
25--------------89
221-------------76
1--------------5
33-------------9
一次ShellInsetSort的结果: [25, 76, 1, 9, 89, 221, 5, 33] //gap=4
25 1 89 5
76 9 221 33
二次ShellInsetSort的结果: [1, 9, 5, 33, 25, 76, 89, 221] //gap=2
二次ShellInsetSort的结果: [1, 5, 9, 25, 33, 76, 89, 221] // gap=1
希尔排序中的分组是通过Gap这个概念来完成,Gap1 = n/2,以及类推。从上图可以看出:
(1)ShellInsetSort的次数的收敛条件是 gap / 2 > 0。
(2)上面的举例的侧重点是分组
,但是大家不要被这个例子迷惑了,因为分组排序
只是逻辑上的,在代码实现中,我们是从index_gap开始,一个一个元素进行向前的比较插入排序。
(3)每个ShellInsetSort中,每个分组的第一个元素是默认被排序好了的,我们需要从分组内的第二个元素开始插入排序,所以在一次ShellInsetSort中,下标[gap, length]都是需要向前比较插入的,只是相邻的两个元素往往在不同的分组。
public static void shellSort(int[] array) {
int gap;
for (gap = array.length / 2; gap > 0; gap /= 2) {
shellInsertSort(array, gap);
System.out.println("------ gap = " + gap);
printArray(array);
}
}
private static void shellInsertSort(int[] array, int gap) {
int j;
// 左边界是gap不是0
for (int i = gap; i < array.length; i++) {
// 插入值
int value = array[i];
// 插入位置向左遍历,左边界是gap不是0
for (j = i; j >= gap; j -= gap) {
// 如果前一个元素比插入值小,则找到了插入位置j
if (array[j - gap] < value) {
break;
}
// 腾出一格
array[j] = array[j - gap];
}
// 赋值
array[j] = value;
}
}
三、选择排序
public static void selectionSort(int[] array) {
int j;
int indexMin;
for (int i = 0; i < array.length - 1; i++) {
// 最小元素的索引
indexMin = i;
for (j = i + 1; j < array.length; j++) {
if (array[j] < array[indexMin]) {
indexMin = j;
}
}
exchange(array, i, indexMin);
}
}
四、归并排序
public static void mergeSort(int[] array) {
int[] tmpArray = new int[array.length];
mergeSort(array, tmpArray, 0, array.length - 1);
}
private static void mergeSort(int[] array, int[] tmpArray, int leftPos, int rightEnd) {
// 递归的收敛条件
if (leftPos >= rightEnd) {
return;
}
int center = (leftPos + rightEnd) / 2;
mergeSort(array, tmpArray, leftPos, center);
mergeSort(array, tmpArray, center + 1, rightEnd);
merge(array, tmpArray, leftPos, center + 1, rightEnd);
}
/**
* 对数组中的两节进行归并
*/
private static void merge(int[] array, int[] tmpArray, int leftPos, int rightPos, int rightEnd) {
int tmpPos = leftPos;
int leftEnd = rightPos - 1;
int leftStart = leftPos;
// 比较归并
while (leftPos <= leftEnd && rightPos <= rightEnd) {
if (array[leftPos] <= array[rightPos]) {
tmpArray[tmpPos++] = array[leftPos++];
} else {
tmpArray[tmpPos++] = array[rightPos++];
}
}
// 检查和拷贝左段的遗留元素
while (leftPos <= leftEnd) {
tmpArray[tmpPos++] = array[leftPos++];
}
// 检查和拷贝右段的遗留元素,左右只可能有一段有遗留
while (rightPos <= rightEnd) {
tmpArray[tmpPos++] = array[rightPos++];
}
// 将tmpArray中的元素复制回array
for (int i = leftStart; i <= rightEnd; i++) {
array[i] = tmpArray[i];
}
}