常见的排序算法有8种:
表格:
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
直接选择排序 | O(n2) | O(n2) | O(n2) | O(1) | 不稳定 |
直接插入排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
希尔排序 | O(nlog2n) | O(n2) | O(n) | O(1) | 不稳定 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
快速排序 | O(nlog2n) | O(n2) | O(nlog2n) | O(nlog2n) | 不稳定 |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 |
基数排序 | O(d(n+r)) | O(d(n+r)) | O(d(n+r)) | O(n+r) | 稳定 |
图片为转载,如侵删:
冒泡排序和选择排序用的不是很多,因为时间复杂度高,但是因为常数项少,小规模排序执行时间不一定比其他排序长。
插入排序 虽然时间复杂度高,但是因为常数项比较小,通常用在大致排序完成后最后的调整阶段。比如Java中Array.sort()的底层实现便用了二分插入排序(长度小于32的Tim sort算法)。
快速排序和归并排序是最常用的。堆排序的思想比较重要,涉及到优先队列的问题。
基数排序 能够解决一些特定的问题。
下面排序都会用到的辅助代码:
//交换位置
public static void swap(int[] arr, int i, int j) {
/* 不能用于浮点数和交换自身
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
*/
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
过程:
1. 从0位置开始,比较相邻两个数的大小,如果后面的数小于前面,则交换位置。
2. 遍历一遍下来,最后一个数为整个数组中的最大值。
3. 把最后一个数排除,继续比较剩下的数组。
4. 总共比较次数为N*N,时间复杂度为O(n²)。
//Bubble Sort
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
过程:
//Selection Sort
public static void selectionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
过程:
//Insertion Sort
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
swap(arr, j, j + 1);
}
}
}
过程:
1. 过程类似于插入排序,算是插入排序的一种优化。
2. 首先,需要确定一个步长k,根据步长,把数组分为N/k部分,每一部分单独排序。
3. 把步长缩短,继续排序,直到步长为1。
4. 通过步长,减少了数组需要移动的次数,从而降低了复杂度。
5. 所以复杂度的高低完全取决于步长的好坏,是一种特别不稳定的算法,也是一种实现简单分析困难的算法。
//维基上找的,没自己写
# Sort an array a[0...n-1].
gaps = [701, 301, 132, 57, 23, 10, 4, 1]
# Start with the largest gap and work down to a gap of 1
foreach (gap in gaps)
{
# Do a gapped insertion sort for this gap size.
# The first gap elements a[0..gap-1] are already in gapped order
# keep adding one more element until the entire array is gap sorted
for (i = gap; i < n; i += 1)
{
# add a[i] to the elements that have been gap sorted
# save a[i] in temp and make a hole at position i
temp = a[i]
# shift earlier gap-sorted elements up until the correct location for a[i] is found
for (j = i; j >= gap and a[j - gap] > temp; j -= gap)
{
a[j] = a[j - gap]
}
# put temp (the original a[i]) in its correct location
a[j] = temp
}
}
需要重点掌握:
过程:
public static void quickSort(int[] arr) {
if ( arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length-1);
}
public static void quickSort(int[] arr, int left, int right) {
if ( left >= right ) {
return;
}
//随机产生partition值,防止出现最坏情况
swap(arr, right, (int) (Math.random() * ( right - left + 1) )+ left );
//返回的数组p为partition的左右边界
int[] p = partition(arr, left, right);
quickSort(arr, left, p[0]-1);
quickSort(arr, p[1]+1, right);
}
public static int[] partition(int[] arr, int left, int right) {
int less = left - 1, more = right;
while ( left < more ) {
if ( arr[left] < arr[right] ) {
swap(arr, ++less, left++);
} else if ( arr[left] > arr[right] ) {
swap(arr, --more, left);
} else {
left++;
}
}
swap(arr, more, right);
return new int[] {less+1,more};
}
需要重点掌握:
过程:
public static void mergeSort(int[] arr) {
if ( arr == null || arr.length < 2 ) {
return;
}
mergeSort(arr, 0, arr.length - 1 ) ;
}
public static void mergeSort(int[] arr, int l, int r) {
if (l == r) {
return;
}
int mid = l + ((r - l) >> 1);
mergeSort(arr, l, mid);
mergeSort(arr, mid + 1, r);
merge(arr, l, mid, r);
}
public static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1];
int i = 0;
int p1 = l;
int p2 = m + 1;
while (p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
while (p1 <= m) {
help[i++] = arr[p1++];
}
while (p2 <= r) {
help[i++] = arr[p2++];
}
for (i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
重点:
过程:
public static void HeapSort(int[] arr) {
if ( arr == null || arr.length < 2 ) {
return;
}
for ( int i = 1; i < arr.length; i++ ) {
insertHeap(arr, i);
}
int r = arr.length - 1;
while(r > 0 ) {
swap(arr, 0, r);
heapify(arr, 0, r--);
}
}
public static void insertHeap(int[] arr, int i) {
while (arr[i] > arr[(i - 1) / 2]) {
swap(arr, i, (i - 1) / 2);
i = (i - 1) / 2;
}
}
public static void heapify(int[] arr, int index, int size) {
int left = index * 2 + 1;
while ( left < size ) {
int largest = left+1 < size && arr[left] < arr[left + 1] ? left+1 : left;
if ( arr[index] > arr[largest] ) {
return;
}
swap(arr, index, largest);
index = largest;
left = index * 2 + 1;
}
}
适用范围:
过程:
其他:
代码:
// only for 0~200 value
public static void bucketSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
int i = 0;
for (int j = 0; j < bucket.length; j++) {
while (bucket[j]-- > 0) {
arr[i++] = j;
}
}
}
过程:
代码:
// only for no-negative value
public static void radixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
public static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
public static void radixSort(int[] arr, int begin, int end, int digit) {
final int radix = 10;
int i = 0, j = 0;
int[] count = new int[radix];
int[] bucket = new int[end - begin + 1];
for (int d = 1; d <= digit; d++) {
for (i = 0; i < radix; i++) {
count[i] = 0;
}
for (i = begin; i <= end; i++) {
j = getDigit(arr[i], d);
count[j]++;
}
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
for (i = end; i >= begin; i--) {
j = getDigit(arr[i], d);
bucket[count[j] - 1] = arr[i];
count[j]--;
}
for (i = begin, j = 0; i <= end; i++, j++) {
arr[i] = bucket[j];
}
}
}
public static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}