排序的稳定性:两个相等的数字在排序后相对位置不发生变化,这种算法就是稳定的。
排序方法 | 最好 | 平均 | 最坏 | 空间复杂度 | 稳定性 |
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
希尔排序 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(1) | 不稳定 |
快速排序 | O(n*log(n)) | O(n*log(n)) | O(n^2) | O(log(n))~O(n) | 不稳定 |
归并排序 | O(n*log(n)) | O(n*log(n)) | O(n*log(n)) | O(1) | 稳定 |
1.直接插入排序:认为第一个数已经有序,从第二个数开始,从后往前遍历已经有序的数组,直到找到比第二个数字小的数字,将第二个数字排在比它小的数字后;这时认为前两个数字已经有序,继续从第三个数字开始,重复前面的步骤;一直重复,直到排序完成。
static int[] insertionSort (int[] arr) {
if (arr.length==0) return arr;
int cur = 0;
for (int i=0; i<arr.length; i++) {
cur = arr[i+1];
int preIndex = i;
while (preIndex>=0&&cur<arr[preIndex]) {
arr[preIndex+1] = arr[preIndex];
preIndex--;
}
arr[preIndex+1] = cur;
}
return arr;
}
2.希尔排序:又称为缩小增量法,是插入排序的改良版。先把所有数进行分组,再对每一组进行插入排序;然后重复分组,排序,直到最终有序。
public static void shellSort(int[] arr) {
int gap = arr.length;
while (gap>1) {
insertSortGap(arr,gap);
gap = (gap/3)+1;
}
insertSortGap(arr,1);
}
private static void insertSortGap(int[] arr, int gap) {
for (int i = 0; i < arr.length; i++) {
int v = arr[i];
int j = i - gap;
for ( ; j >= 0 && arr[j] > v; j -= gap) {
arr[j+gap] = arr[j];
}
arr[j+gap] = v;
}
}
3.选择排序:在数组中找出最小的数排在最前面;已经排序好的最小数字除外,重复操作,直到排序完成。
static int[] selectionSort (int[] arr) {
if (arr.length==0) return arr;
int min = 0, index = 0;
for (int i=0; i<arr.length; i++) {
min = arr[i];
index = i;
for (int j=i; j<arr.length; j++) {
if (min>arr[j]) {
min = arr[j];
index = j;
}
}
int tmp = arr[i];
arr[i] = min;
arr[index] = tmp;
}
return arr;
}
4.堆排序:堆排序是基于堆这种数据结构产生的一种排序算法。堆有小根堆和大根堆两种,大根堆的根节点的值大于左右子节点的值,小根堆的根节点的值小于左右子节点的值。如下图所示,
以大根堆为例,堆排序的操作步骤是,堆顶元素是最大的元素,将堆顶元素放到队尾,然后将其余元素重新构造成为大根堆,然后重复前面的步骤,最终就得到了降序排列的一组元素。
5.冒泡排序:比较相邻两个数大小,让较小的数在前面;对每一对数字进行比较,这样最大的数就在最后了;重复上面操作,但是最后一个数不动;一直重复,直到排序完成。
static int[] bubbleSort (int[] arr) {
if (arr.length==0) return arr;
for (int i=0; i<arr.length; 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;
}
6.快速排序:选一个数作为基准,遍历数组,将比基准小的数放到基准左边,比基准大的数放到基准右边;采用分治思想,对基准左右两个区间进行选基准,遍历数组操作,直到区间长度为1,表示已经有序,或者区间长度为0,代表没有数据了。
public class QuickSort {
public void quickSort(int[] a,int l,int r){
if (l<r){
int temp=a[l];
while (l<r){
while (l<r && a[r]>temp){
r--;
}
if (l<r){
a[l++]=a[r];
}
while (l<r && a[l]<=temp){
l++;
}
if (l<r){
a[r--]=a[l];
}
}
a[l]=temp;
quickSort(a,l,temp-1);
quickSort(a,temp+1,r);
}
}
}
7.归并排序:归并排序是建立在归并操作上的一种有效的排序算法,采用分治思想。将已有序的子序列合并,得到完全有序的序列;即先让每个子序列有序,再让子序列段间有序。若将两个有序表合并成一个有序表,称为二路合并。
public static void mergeSort(int[] array) {
mergeSortInternal(array, 0, array.length);
}
private static void mergeSortInternal (int[] array, int low, int high) {
if (low-1 >= high) {
return;
}
int mid = (low+high) / 2;
mergeSortInternal(array, low, mid);
mergeSortInternal(array, mid, high);
merge(array, low, mid, high);
}