说明:以下排序如无特别说明,都是从小到大升序排序
1. 冒泡排序
核心思想:每个元素与其相邻元素比较,如果前者大于后者则交换,每次循环结束后会将最大值放到最后,像小水泡从底下冒到上面成大水泡一样,如此循环,较大元素会逐渐冒泡到后面,直到最小的元素在最前面,完成从小到大排序。
public static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length; i++) {// 注意由于是与下一个元素比较,故这里必须是 j < arr.length-i-1for (int j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;}}}
}
时间复杂度 O(n^2);空间复杂度O(1);不稳定排序;原地排序
优化:
public static void bubbleSort(int[] arr) {for (int i = 0; i < arr.length; i++) {// 增加一个是否需要继续的标志boolean flag = false;for (int j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;flag = true;}}// 比较一轮后发现所有元素都无需交换,即认为本来是有序的if (!flag) {break;}}
}
2. 选择排序
核心思想:对长度为 n 的数组,循环 n-1 次,每次循环将当前元素与后面的元素比较找出最小元素并交换。
常规思路一:比较时交换
public static void selectSort1(int[] arr) {for (int i = 0; i < arr.length - 1; i++) {int minIndex;for (int j = i + 1; j < arr.length; j++) {// 如果比当前元素小,就交换if (arr[j] < arr[i]) {minIndex = j;int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}}
}
优化思路二:比较完成后再交换
public static void selectSort(int[] arr) {// 每次循环找出最小元素索引,如果其与当前元素索引不同,则将其与当前元素索引交换for (int i = 0; i < arr.length - 1; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}if (minIndex != i) {int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}
}
时间复杂度 O(n^2);空间复杂度O(1);不稳定排序;原地排序
3. 插入排序
核心思想:从第二个元素开始将每个元素与其左边的元素对比,如果当前元素比其左边的元素小,就将其左边的元素往后移动,直到左边无比当前元素更大的元素,将当前元素插入到最左边,如此循环,较小的元素都会插入到合适的位置,最后完成排序。
public static void insertSort(int[] arr) {for (int i = 1; i < arr.length; i++) {// 当前要插入的值int toInsertValue= arr[i];// 初始时和当前元素的前一个元素比较int toInsertIndex= i - 1;while (toInsertIndex>= 0 && toInsertValue< arr[toInsertIndex]) {arr[toInsertIndex+ 1] = arr[toInsertIndex];insertIndex--;}// 这里必须是 toInsertIndex + 1arr[toInsertIndex+ 1] = toInsertValue;}
}
时间复杂度 O(n^2)。
4. 希尔排序
核心思想:将长度为 n 的数组按 gap=n/2^i(i=1,2,3…, gap > 0) 的间隔依次进行插入排序。
public static void shellSort(int[] arr) {for (int gap = arr.length / 2; gap > 0; gap /= 2) {// 插入排序,从第 gap 个元素开始往前比较for (int i = gap; i < arr.length; i++) {int toInsertValue = arr[i];int toInsertIndex = i - gap;while (toInsertIndex >= 0 && toInsertValue < arr[toInsertIndex]) {arr[toInsertIndex + gap] = arr[toInsertIndex];toInsertIndex -= gap;}arr[toInsertIndex + gap] = toInsertValue;}}
}
时间复杂度 O(n^(2/3))。
不难看出,希尔排序的实现是在插入排序的外面套了一层分组循环,当 gap=1时,希尔排序即退变为插入排序。将希尔排序的插入排序部分提炼成一个单独的根据步长为 gap 排序的方法,则为:
public static void insertSort(int[] arr, int gap) {for (int i = gap; i < arr.length; i++) {int toInsertValue = arr[i];int toInsertIndex = i - gap;while (toInsertIndex >= 0 && toInsertValue < arr[toInsertIndex]) {arr[toInsertIndex + gap] = arr[toInsertIndex];toInsertIndex -= gap;}arr[toInsertIndex + gap] = toInsertValue;}
}
希尔排序调用上述插入排序方法即可:
public static void shellSort(int[] arr) {for (int gap = arr.length / 2; gap > 0; gap /= 2) {insertSort(arr, gap);}
}
则简单的插入排序可表示为 gap = 1 的情形:
public static void insertSort(int[] arr) {insertSort(arr, 1);
}
参考
[1] CSDN博客:十大基础算法
[2] 菜鸟教程-数据结构与算法