方法 | 平均时间 | 最坏时间 | 额外空间 | 稳定性 |
---|
冒泡排序 | O(n2) | O(n2) | O(1) | 稳定 | 选择排序 | O(n2) | O(n2) | O(1) | 不稳定 | 插入排序 | O(n2) | O(n2) | O(1) | 稳定 | 基数排序 | O(d(n+r)) | O(d(n+r)) | O(d+r) | 稳定 | 希尔排序 | O(nlogn) | O(n2) | O(1) | 不稳定 | 快速排序 | O(nlogn) | O(n2) | O(nlogn) | 不稳定 | 归并排序 | O(nlogn) | O(nlogn) | O(1) | 稳定 | 堆排序 | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
1、插入排序
基本思想——把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
public static void insertSort(int[] arr) {
if (null == arr || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
int insertVal = arr[i];
int insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
arr[insertIndex + 1] = insertVal;
}
}
2、希尔排序
基本原理——把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
public static void shellSortSwap(int[] arr) {
int temp = 0;
for (int step = arr.length / 2; step > 0; step /= 2) {
for (int i = step; i < arr.length; i++) {
for (int j = i - step; j >= 0; j -= step) {
if (arr[j] > arr[j + step]) {
temp = arr[j];
arr[j] = arr[j + step];
arr[j + step] = temp;
}
}
}
}
}
优化
public static void shellSortMove(int[] arr) {
for (int step = arr.length / 2; step > 0; step /= 2) {
System.out.println("步长为" + step);
for (int i = step; i < arr.length; i++) {
int insertVal = arr[i];
int insertIndex = i - step;
while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
arr[insertIndex + step] = arr[insertIndex];
insertIndex -= step;
}
arr[insertIndex + step] = insertVal;
System.out.println(Arrays.toString(arr));
}
}
}
3、选择排序
基本思想——从数组中找到最小的,和第一个交换,然后从剩下的元素中,找到最小的和第二个交换,以此类推完成排序
public static void selectSort(int[] arr) {
if (null == arr || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int min = arr[i];
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < min) {
min = arr[j];
minIndex = j;
}
}
if (minIndex != i) {
arr[minIndex] = arr[i];
arr[i] = min;
}
}
}
优化,每次选择最小值的同时,也可以将最大值选择并交换,减少选择次数
4、堆排序
基本思想——构建初始堆,将待排序列构成一个大顶堆(或者小顶堆),升序大顶堆,降序小顶堆;将堆顶元素与堆尾元素交换,并断开(从待排序列中移除)堆尾元素。重新构建堆。重复元素交换和构建堆,直到待排序列中只剩下一个元素(堆顶元素)。
public static void heapSort(int[] arr) {
for (int i = arr.length / 2 - 1; i >= 0; i--) {
adjustHeap(arr, i, arr.length);
}
int temp = 0;
for (int i = arr.length - 1; i > 0; i--) {
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
adjustHeap(arr, 0, i);
}
}
private static void adjustHeap(int arr[], int i, int length) {
int temp = arr[i];
for (int k = i * 2 + 1; k < length; k = k * 2 + 1) {
if (k + 1 < length && arr[k] < arr[k + 1]) {
k++;
}
if (arr[k] > temp) {
arr[i] = arr[k];
i = k;
} else {
break;
}
}
arr[i] = temp;
}
5、冒泡排序
基本思想——通过对待排序序列从前向后,依次比较相邻的元素的值,所发现逆序则交换,使较大的元素从前向后移动,完成排序
public static void bubbleSort(int[] arr) {
if (null == arr || arr.length < 2) {
return;
}
int temp;
int count = 0;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
count++;
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("比较了" + count + "次");
}
优化:如果一趟比较下来,没有发生元素交换说明序列已经有序,应该结束循环
public static void bubbleSort1(int[] arr) {
if (null == arr || arr.length < 2) {
return;
}
int temp;
int count = 0;
boolean flag = false;
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
count++;
if (arr[j] > arr[j + 1]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag) {
flag = false;
} else {
break;
}
}
System.out.println("比较了" + count + "次");
}
6、快速排序
基本思想——是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
public static void quickSort(int[] arr, int left, int right) {
int l = left;
int r = right;
int pivot = arr[(left + right) / 2];
int temp = 0;
while (l < r) {
while (arr[l] < pivot) {
l++;
}
while (arr[r] > pivot) {
r--;
}
if (l >= r) {
break;
}
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
if (arr[l] == pivot) {
r--;
}
if (arr[r] == pivot) {
l++;
}
}
if (l == r) {
l++;
r--;
}
if (left < r) {
quickSort(arr, left, r);
}
if (l < right) {
quickSort(arr, l, right);
}
}
7、归并排序
基本思想——归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
public static void mergeSort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr, left, mid, right, temp);
}
}
public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right) {
if (arr[i] < arr[j]) {
temp[t] = arr[i];
t++;
i++;
} else {
temp[t] = arr[j];
t++;
j++;
}
}
while (i <= mid) {
temp[t] = arr[i];
i++;
t++;
}
while (j <= right) {
temp[t] = arr[j];
j++;
t++;
}
t = 0;
int tempLeft = left;
while (tempLeft <= right) {
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
8、基数排序(桶排序)
基本思想——将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
public static void radixSort(int[] arr) {
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
int maxLength = (max + "").length();
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
for (int j = 0; j < arr.length; j++) {
int digitOfElement = arr[j] / n % 10;
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}
int index = 0;
for (int k = 0; k < 10; k++) {
if (bucketElementCounts[k] > 0) {
for (int l = 0; l < bucketElementCounts[k]; l++) {
arr[index++] = bucket[k][l];
}
}
bucketElementCounts[k] = 0;
}
}
}
|