比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。
本文先介绍比较排序的八种算法:
一、交换排序——冒泡排序
时间复杂度:最坏情况下:O(n²);最好情况下:O(n);平均:O(n²) 稳定
import java
.util
.Arrays
;
public class BubbleSort {
public static void main(String
[] args
) {
int[] arr
= {2, 3, 23, 43, 19, 25, 87, 23, 41};
arr
= bubbleSort(arr
);
System
.out
.println(Arrays
.toString(arr
));
}
public static int[] bubbleSort(int[] arr
) {
for(int i
=0; i
<arr
.length
-1; ++i
){
for(int j
=0; j
<arr
.length
-1-i
; ++j
){
if(arr
[j
] > arr
[j
+1]){
int tmp
= arr
[j
];
arr
[j
] = arr
[j
+1];
arr
[j
+1] = tmp
;
}
}
}
return arr
;
}
}
二、交换排序——快速排序
import java
.util
.Arrays
;
public class QuickSort {
public static void main(String
[] args
) {
int[] arr
= {2, 3, 23, 43, 19, 25, 87, 23, 41};
arr
= quickSort(arr
, 0, arr
.length
-1);
System
.out
.println(Arrays
.toString(arr
));
}
public static int[] quickSort(int[] arr
, int low
, int high
){
int start
= low
;
int end
= high
;
int key
= arr
[low
];
while(end
> start
){
while (end
>start
&& arr
[end
]>=key
){
end
--;
}
if(arr
[end
] < key
){
int tmp
= arr
[end
];
arr
[end
] = arr
[start
];
arr
[start
] = tmp
;
}
while (end
>start
&& arr
[start
]<=key
){
start
++;
}
if(arr
[start
]>key
){
int tmp
= arr
[start
];
arr
[start
] = arr
[end
];
arr
[end
] = tmp
;
}
}
if (start
> low
) arr
= quickSort(arr
, low
, start
-1);
if (end
< high
) arr
= quickSort(arr
, end
+1, high
);
return arr
;
}
}
三、插入排序——简单插入排序
【数据规模越小或者越有序,简单插入算法越高效】 时间复杂度:最坏情况下:O(n²);最好情况下:O(n);平均:O(n²) 简单插入算法 稳定
import java
.util
.Arrays
;
public class InsertSort {
public static void main(String
[] args
) {
int[] arr
= {2, 3, 23, 43, 19, 25, 87, 23, 41};
System
.out
.println(Arrays
.toString(arr
));
}
public static int[] insertSort(int[] arr
){
for(int i
=1; i
<arr
.length
; i
++){
int insertVal
= arr
[i
];
int index
= i
- 1;
while (index
>=0 && insertVal
<arr
[index
]) {
arr
[index
+1] = arr
[index
];
index
--;
}
arr
[index
+1] = insertVal
;
}
return arr
;
}
}
四、插入排序——希尔排序
希尔排序的时间复杂度跟增量序列有关 不稳定
import java
.util
.Arrays
;
public class ShellSort {
public static void main(String
[] args
) {
int[] arr
= {21, 25, 49, 26, 16, 8};
shellSort(arr
);
System
.out
.println(Arrays
.toString(arr
));
}
public static void shellSort(int[] arr
) {
int dk
= arr
.length
/2;
while (dk
> 0) {
shellInsertSort(arr
, dk
);
dk
= dk
/2;
}
shellInsertSort(arr
, dk
);
}
public static void shellInsertSort(int[] arr
, int dk
) {
for (int i
=dk
; i
<arr
.length
; i
++){
int insertVal
= arr
[i
];
int index
= i
- dk
;
while(index
>= 0 && arr
[index
] > insertVal
) {
arr
[index
+ dk
] = arr
[index
];
index
-= dk
;
}
arr
[index
+dk
] = insertVal
;
}
}
}
五、选择排序——简单选择排序
import java
.text
.SimpleDateFormat
;
import java
.util
.Arrays
;
import java
.util
.Date
;
public class SelectSort {
public static void main(String
[] args
) {
int[] arr
= new int[80000];
for(int i
=0; i
<arr
.length
; ++i
){
arr
[i
] = (int)(Math
.random()*800000);
}
SimpleDateFormat sdf
= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date1
= new Date();
String time1
= sdf
.format(date1
);
System
.out
.println("排序前时间为:"+time1
);
selectSort(arr
);
System
.out
.println(Arrays
.toString(arr
));
Date date2
= new Date();
String time2
= sdf
.format(date2
);
System
.out
.println("排序前时间为:"+time2
);
}
public static void selectSort(int[] arr
) {
for(int i
=0; i
<arr
.length
-1; ++i
){
int minIdx
= i
;
for(int j
=i
; j
<arr
.length
; ++j
){
if(arr
[j
] < arr
[minIdx
]){
minIdx
= j
;
}
}
if(i
!= minIdx
) {
int temp
= arr
[i
];
arr
[i
] = arr
[minIdx
];
arr
[minIdx
] = temp
;
}
}
}
}
六、选择排序——堆排序
堆排序详细讲解参考这篇博客
因为堆排序无关乎初始序列是否已经排序已经排序的状态,始终有两部分过程,构建初始的大顶堆的过程时间复杂度为O(n),交换及重建大顶堆的过程中,需要交换n-1次,重建大顶堆的过程根据完全二叉树的性质,[log2(n-1),log2(n-2)…1]逐步递减,近似为nlogn。所以它最好和最坏的情况时间复杂度都是O(nlogn),空间复杂度O(1)。不稳定
import java
.util
.Arrays
;
public class HeapSort {
public static void main(String
[] args
) {
int[] arr
= {2, 3, 73, 12, 93, 23, 36, 21};;
heapSort(arr
);
System
.out
.println(Arrays
.toString(arr
));
}
public static void heapSort(int[] arr
) {
if(arr
==null
|| arr
.length
== 0) {
return;
}
int len
= arr
.length
;
buildMaxHeap(arr
, len
);
for (int i
=len
-1; i
>0; i
--) {
swap(arr
, 0, i
);
len
--;
heapify(arr
, 0, len
);
}
}
public static void buildMaxHeap(int[] arr
, int len
) {
for(int i
=len
/2-1; i
>=0; --i
) {
heapify(arr
, i
, len
);
}
}
public static void heapify(int[] arr
, int i
, int len
) {
int left
= 2 * i
+ 1;
int right
= 2 * i
+ 2;
int largestIdx
= i
;
if (left
< len
&& arr
[left
] > arr
[largestIdx
]) {
largestIdx
= left
;
}
if (right
< len
&& arr
[right
] > arr
[largestIdx
]) {
largestIdx
= right
;
}
if (largestIdx
!= i
) {
swap(arr
, i
, largestIdx
);
heapify(arr
, largestIdx
, len
);
}
}
public static void swap(int[] arr
, int i
, int j
) {
int temp
= arr
[i
];
arr
[i
] = arr
[j
];
arr
[j
] = temp
;
}
}
七、归并排序
归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(nlogn) 的时间复杂度。代价是需要额外的内存空间。
import java
.util
.Arrays
;
public class MergeSort {
public static void main(String
[] args
) {
int[] arr
= {2, 3, 23, 43, 19, 25, 87, 23, 41};
int[] temp
= new int[arr
.length
];
mergeSort(arr
, 0, arr
.length
-1, temp
);
System
.out
.println(Arrays
.toString(arr
));
}
public static void mergeSort(int[] arr
, int left
, int right
, int[] temp
) {
if (left
< right
) {
int mid
= (right
+ left
) / 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
];
i
++;
t
++;
} else {
temp
[t
] = arr
[j
];
j
++;
t
++;
}
}
while (i
<= mid
) {
temp
[t
] = arr
[i
];
i
++;
t
++;
}
while (j
<= right
) {
temp
[t
] = arr
[j
];
j
++;
t
++;
}
int s
= 0;
for(int w
=left
; w
<=right
; ++w
) {
arr
[w
] = temp
[s
];
s
++;
}
}
}