目录
排序算法
排序算法之间的比较:
排序算法 | 最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | 稳定 | O(1) |
选择排序 | O(n^2) | O(n^2) | 不稳定 | O(1) |
插入排序 | O(n^2) | O(n^2) | 稳定 | O(1) |
归并排序 | O(nlogn) | O(nlogn) | 稳定 | O(n) |
快速排序 | O(n^2) | O(nlogn) | 不稳定 | O(1) |
堆排序 | O(n^2) | O(n*logn) | 不稳定 | O(1) |
希尔排序 | O(n^2) | O(n^1.3) | 不稳定 | O(1) |
桶排序 | O(n) | O(n) | 稳定 | O(n) |
一、时间复杂度为O(n^2)的排序算法
1.1 冒泡排序(bubbleSort)
- 进行n-1轮排序,每轮排序数组中元素依次两两交换, 获取最大的元素;
- 时间复杂度 O(n^2), 空间复杂度O(1);
public class BubbleSort {
public int[] bubbleSort(int[] A) {
// write code here
if(A == null || A.length < 2) return A;
for(int i = 0; i < A.length - 1; i++){
for(int j = 0; j < A.length - i - 1; j++){
if(A[j] > A[j+1]){
swap(A, j, j+1);
}
}
}
return A;
}
public void swap(int[] A, int i1, int i2){
int tmp = A[i1];
A[i1] = A[i2];
A[i2] = tmp;
}
}
1.2 选择排序(selectionSort)
- 进行n-1轮交换, 每轮交换获取最小的元素
- 时间复杂度 O(n^2), 空间复杂度O(1);
public class SelectionSort {
public int[] selectionSort(int[] A) {
// write code here
if(A == null || A.length < 2) return A;
for(int i = 0; i < A.length - 1; i++){
for(int j = i + 1; j < A.length; j ++){
if(A[i] > A[j]){
swap(A, i, j);
}
}
}
return A;
}
public void swap(int[] A, int i1, int i2){
int tmp = A[i1];
A[i1] = A[i2];
A[i2] = tmp;
}
}
1.3 插入排序(insertionSort)
- 时间复杂度 O(n^2), 空间复杂度O(1);
- 当数组元素有序, 时间复杂度O(n)
public class InsertionSort {
public int[] insertionSort(int[] A, int n) {
// write code here
for(int i = 1; i < A.length; i++){
int key = A[i];
int pre = i - 1;
while(pre >= 0 && A[pre] > key){
A[pre+1] = A[pre--];
}
A[pre + 1] = key;
}
return A;
}
}
二、时间复杂度为O(log(n))的排序算法
2.1 归并排序(mergeSort)
- 时间复杂度O(log(n)), 空间复杂度O(n)
- 归并排序的时间复杂度与排序数组元素顺序无关;
public class MergeSort {
public int[] mergeSort(int[] A, int n) {
// write code here
if(A == null || A.length < 2) return A;
int[] tmp = new int[A.length];
divide(A, 0, A.length - 1, tmp);
return A;
}
// 数组合并
public void merge(int[] A, int left, int right, int mid, int[] tmp){
for(int i = left; i <= right; i++){
tmp[i] = A[i];
}
int l1 = left, l2 = mid+1, index = left;
for(int i = left; i <= right; i++){
if(l1 > mid){
A[index++] = tmp[l2++];
}else if(l2 > right){
A[index++] = tmp[l1++];
}else if(tmp[l1] < tmp[l2]){
A[index++] = tmp[l1++];
}else{
A[index++] = tmp[l2++];
}
}
}
public void divide(int[] A, int left, int right, int[] tmp){
if(left < right){
// 与 mid = (left + right)/2 相比, 可以防止left+right超出int范围
int mid = left + (right-left)/2;
divide(A, left, mid, tmp);
divide(A, mid+1, right, tmp);
merge(A, left, right, mid, tmp);
}
}
}
2.2 快速排序(quickSort)
最优情况: 元素分布均匀, 每次都能取到中间位置
- 时间复杂度O(log(n)), 空间复杂度O(logn);
最坏情况: 时间复杂度O(n^2), 空间复杂度O(n), 以下三种最坏情况
- 数组已经是正序(same order)排过序的。
- 数组已经是倒序排过序的。
- 所有的元素都相同(1、2的特殊情况)
public class QuickSort {
public int[] quickSort(int[] A, int n) {
// write code here
quick(A, 0, A.length - 1);
return A;
}
public void quick(int[] A, int left, int right){
if(left >= right) return ;
int key = A[left];
int head = left, tail = right+1;
while(true){
while(A[++head] <= key) if(head == right) break;
while(A[--tail] > key) if(tail == left) break;
if(head >= tail) break;
int tmp = A[head];
A[head] = A[tail];
A[tail] = tmp;
}
A[left] = A[tail];
A[tail] = key;
quick(A, left, tail-1);
quick(A, tail + 1,right);
}
}
2.3 堆排序
-
时间复杂度O(nlog(n)), 空间复杂度O(1)
-
最差情况, 数组元素已经是从小到大有序的, 时间复杂度O(n^2)
public class HeapSort {
public int[] heapSort(int[] A, int n) {
// 建堆
for(int i = A.length/2; i >= 0; i--){
heapAdjust(A, i, A.length);
}
for(int i = A.length - 1; i >= 0; i--){
int head = A[0];
A[0] = A[i];
A[i] = head;
heapAdjust(A, 0, i);
}
return A;
}
// 堆调整 O(log(n))
public void heapAdjust(int[] A, int parent, int len){
int head = A[parent];
while(parent*2+1 < len){
int childLeft = parent*2 + 1;
if(childLeft + 1 < len && A[childLeft + 1] > A[childLeft]){
childLeft++;
}
if(A[childLeft] < head) break;
A[parent] = A[childLeft];
parent = childLeft;
}
A[parent] = head;
}
}
2.4 希尔排序(shellSort)
-
Shell排序比起QuickSort,MergeSort,HeapSort慢很多。适合于数据量在5000以下并且速度并不是特别重要的场合。它对于数据量较小的数列重复排序是非常好的。
-
时间复杂度O(nlog(n)),空间复杂度O(1)
public class ShellSort {
public int[] shellSort(int[] A, int n) {
for(int i = n; i >= 1; i /= 2){
for(int j = i; j < n; j++){
int key = A[j];
int pre = j - i;
while(pre >= 0 && A[pre] > key){
A[pre + i] = A[pre];
pre -= i;
}
A[pre + i] = key;
}
}
return A;
}
}
三、 桶排序(时间复杂度O(n))
3.1 计数排序
- 计数排序适用于元素在一定有限范围内的数组;
- 时间复杂度O(n), 空间复杂度 O(m)
import java.util.*;
public class CountingSort {
public int[] countingSort(int[] A, int n) {
if(A == null || A.length < 2) return A;
int min = A[0], max = A[0];
for(int i = 0; i < A.length; i++){
min = Math.min(min, A[i]);
max = Math.max(max, A[i]);
}
int[] bucket = new int[max - min + 1];
// 入桶
for(int i = 0; i < A.length; i++){
bucket[A[i] - min]++;
}
// 出桶
int index = 0;
for(int i = 0; i < bucket.length; i++){
while(bucket[i]-- > 0){
A[index++] = i + min;
}
}
return A;
}
}
3.2 基数排序
- 时间复杂度O(n), 空间复杂度O(n)
// 保证元素均小于等于2000的基数排序
public class RadixSort {
public int[] radixSort(int[] A, int n) {
if(A == null || n < 2) return A;
int[][] bucket = new int[10][n]; // 创建10个桶,每个桶需要的长度都有可能为n
int[] count = new int[10]; // 记录每个对应桶中装入的元素个数
for(int i = 1; i <= 1000; i *= 10) {
for(int k = 0; k < n; k++) {
int num = A[k] / i % 10; // 获取位数上对应数字
bucket[num][count[num]++] = A[k];
}
// 从bucket中将数据倒回A中
int index = 0;
for(int k = 0; k < 10; k++) {
for(int j = 0; j < count[k]; j++) {
A[index++] = bucket[k][j];
}
count[k] = 0; // 重置为0
}
}
return A;
}
}