排序分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。我们这里说说八大排序就是内部排序。
分别从算法思想、排序示例、代码实现、算法分析,包括复杂度、稳定性等进行分析,其中算法稳定性是指两个相同的元素,排序前后位置是否发生变化。
一、插入排序(Insertion Sort)
1、直接插入排序
(1)思想
每趟将一个元素,按其关键字值的大小插入到它前面已经排序的子序列中,依次重复,直到插入全部元素
(2)排序示例
第i趟,将元素ai插入到子序列{a0,a1...ai-1}的适当位置,使插入后的子序列仍然是排序的。
(3)代码实现
/**
* 插入排序算法
*
* @author zzj
*
*/
public class InsertSort {
public static void main(String[] args) {
int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
System.out.println("排序前:");
System.out.println(Arrays.toString(table));
insertSort(table);
System.out.println("排序后:");
System.out.println(Arrays.toString(table));
}
public static void insertSort(int[] table) {
for (int i = 1; i < table.length; i++) {// i为趟数
int temp = table[i], j;
for (j = i - 1; j >= 0 && temp < table[j]; j--) {
table[j + 1] = table[j];
}
table[j + 1] = temp;
}
}
}
运行结果:
排序前:
[3, 5, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]
(4)算法分析
1)时间复杂度
最好是O(n),已排好序;
最坏是(n*2),随机排列和反序排列
2)空间复杂度
0(1)
3)稳定性
稳定
2、二分法插入排序
(1)思想
直接插入排序的每一趟,将一个元素插入到它前面的排序子序列中,其中采用顺序查找算法寻找插入点。此时,子序列是有序的,正好符合二分法查找的要求,因此,用二分法查找代替直接插入排序中的顺序查找。
二分法没有排序,只有查找。所以当找到要插入的位置时。移动必须从最后一个记录开始,向后移动一位。
(2)排序示例
(3)代码实现
/**
* 二分插入排序
*
* @author zzj
*
*/
public class BinarySort {
public static void main(String[] args) {
int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
System.out.println("排序前:");
System.out.println(Arrays.toString(table));
binarySort(table);
System.out.println("排序后:");
System.out.println(Arrays.toString(table));
}
public static void binarySort(int[] table) {
for (int i = 1; i < table.length; i++) {// i为趟数
int temp = table[i];
int low = 0, high = i;
while (low <= high) {
int mid = (low + high) / 2;
if (table[mid] < temp) {
low = mid + 1;
} else {
high = mid - 1;
}
}
for (int j = i - 1; j > high; j--) {
table[j + 1] = table[j];
}
table[high + 1] = temp;
}
}
}
运行结果
排序前:
[5, 3, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]
二、交换排序算法
1、冒泡排序(Bubble Sort)
(1)思想
比较相邻两个元素大小,如果反序,则交换。若按升序排序,每趟将数据序列中的最大元素交换到最后位置,就像气泡从水里冒出一样。
(2)排序示例
(3)代码实现
注意:结束条件: 如果一趟扫描没有数据交换,则排序完成
/**
* 冒泡法
*
* @author zzj
*
*/
public class BubbleSort {
public static void main(String[] args) {
int[] table = { 3, 4, 1, 9, 0, 6, 2, 6, 2 };
System.out.println("排序前:\n" + Arrays.toString(table));
int[] table_new = bubbleSort(table);
System.out.println("排序后:\n" + Arrays.toString(table_new));// 返回的是字符串表示
}
public static int[] bubbleSort(int[] table) {
boolean exchange = true;// 是否交换的标记
for (int i = 1; i < table.length && exchange; i++) {// i为趟数
for (int j = 0; j < table.length - i; j++) {// j为每趟需要比较的次数
exchange = false;// 假设元素都没有交换
if (table[j] > table[j + 1]) {
int temp = table[j];
table[j] = table[j + 1];
table[j + 1] = temp;
exchange = true;
}
}
}
return table;
}
}
运行结果
排序前:
[5, 3, 1, 7, 9, 0, 3, 8, 2, 6]
排序后:
[0, 1, 2, 3, 3, 5, 6, 7, 8, 9]
(4)算法分析
1)时间复杂度
最好是O(n),排好序,一趟 比较n次
最坏是(n*2),随机排列和反序排列
2)空间复杂度
0(1),交换两个元素
3)稳定性
稳定
2、快速排序(QuickSort)
(1)思想
在数据序列中选择一个元素作为基准值,每趟从数据序列的两端开始交替进行,将小于基准值的元素交换到序列前端,将大于基准值的元素交换到序列后端,介于两者之间的位置则成为基准值的最终位置。同时,序列被划分为两个字序列,再分别对两个子序列进行快速排序,直到子序列长度为1,完成排序。
(2)排序示例
(3)代码实现
/**
* 快速排序算法
* @author zzj
*
*/
public class QuickSort {
public static void main(String[] args) {
int[] table = { 3, 5, 1, 8, 0, 3, 5, 2, 7 };
System.out.println("排序前:");
System.out.println(Arrays.toString(table));
quickSort(table, 0, table.length - 1);
System.out.println("排序后:");
System.out.println(Arrays.toString(table));
}
public static void quickSort(int[] table, int begin, int end) {
if (begin < end) {//因为是递归调用,所有只有开头位置小于结束位置才执行
//1、取第一个值为基准值,则空出第一个元素位置i
int i = begin, j = end;
int base = table[i];
while (i != j) {
//2、将j位置元素与基准值比较,若大,则不移动,循环依次j--
while (i < j && base <= table[j])
j--;
if (i < j) {
table[i] = table[j];
i++;
}
//3、将i位置元素与基准值比较,若小,则不移动,循环依次i++
while (i < j && table[i] <= base)
i++;
if (i < j) {
table[j] = table[i];
j--;
}
}
table[i] = base;
quickSort(table, begin, j - 1);//递归调用前一个子序列
quickSort(table, i + 1, end);//递归调用后一个子序列
}
}
}
运行结果
排序前:
[3, 5, 1, 8, 0, 3, 5, 2, 7]
排序后:
[0, 1, 2, 3, 3, 5, 5, 7, 8]
(4)算法分析
1)时间复杂度
最好情况:O(n*logn),分成长度相近的两个子序列
最坏情况:O(n2)分成长度差异很大的两个子序列
2)空间复杂度
递归函数。使用栈保存参数,栈占用的空间与递归调用的次数有关:O(logn)
3)稳定性
不稳定
三、选择排序
1、直接选择排序(SelectSort)
(1)思想
(2)排序示例
(3)代码实现
/**
* 直接选择排序
*
* @author zzj
*
*/
public class SelectSort {
public static void main(String[] args) {
int[] table = { 3, 5, 1, 7, 9, 0, 3, 8, 2, 6 };
System.out.println("排序前:");
System.out.println(Arrays.toString(table));
selectSort(table);
System.out.println("排序后:");
System.out.println(Arrays.toString(table));
}
public static void selectSort(int[] table) {
for (int i = 0; i < table.length; i++) {
int min = i;
for (int j = i + 1; j < table.length; j++) {
if (table[min] > table[j]) {
min = j;
}
}
if (min != i) {
int temp = table[min];
table[min] = table[i];
table[i] = temp;
}
}
}
}
运行结果
(4)算法分析
1)时间复杂度
2)空间复杂度
3)稳定性
四、归并排序(MergeSort)
(1)思想
将两个排序的子序列合并,形成一个排序数据序列,又称两路归并排序。
(2)排序示例
(3)代码实现
/**
* 归并排序算法
* @author zzj
*
*/
public class MergeSort {
public static void main(String args[]) {
int[] table = { 52, 5, 3, 76, 63, 36, 15, 78, 24, 32, 43 };
System.out.println("排序前:");
System.out.println(Arrays.toString(table));
mergeSort(table);
System.out.println("排序后:");
System.out.println(Arrays.toString(table));
}
public static void mergeSort(int[] X) {
int[] Y = new int[X.length];
int n = 1;// 子序列长度
while (n < X.length) {
mergepass(X, Y, n);// 一趟归并,将X数组中各子序列归并到Y中
n *= 2;
if (n < X.length) {
mergepass(Y, X, n);// 一趟归并,将Y数组中各子序列归并到X中
n *= 2;
}
}
}
// 一趟归并
public static void mergepass(int[] X, int[] Y, int n) {
for (int i = 0; i < X.length; i += 2 * n) {
merge(X, Y, i, i + n, n);
}
}
// 一次归并
public static void merge(int[] X, int[] Y, int m, int r, int n) {
int i = m, j = r, k = m;
while (i < r && j < r + n && j < X.length) {
if (X[i] < X[j]) {
Y[k++] = X[i++]; // 较小值复制到Y中
} else {
Y[k++] = X[j++];
}
}
while (i < r && i < X.length) { // 将前一个子序列剩余元素复制到Y中
Y[k++] = X[i++];
}
while (j < r + n && j < X.length) {// 将后一个子序列剩余元素复制到Y中
Y[k++] = X[j++];
}
}
}
运行结果
排序前:
[52, 5, 3, 76, 63, 36, 15, 78, 24, 32, 43]
排序后:
[3, 5, 15, 24, 32, 36, 43, 52, 63, 76, 78]
(4)算法分析
1)时间复杂度
n个元素归并排序,每趟比较n-1次,数据移动n-1次,进行logn趟,时间复杂度为O(n*logn)
2)空间复杂度
O(n)
3)稳定性
稳定
五、算法对比