排序分类
根据时间复杂度的不同,常见的排序算法可以分为三大类:
- 时间复杂度为O(n^2)的算法
冒泡排序、选择排序、插入排序、希尔排序等。 - 时间复杂读为O(nlogn)的算法
快速排序、归并排序、堆排序等。 - 时间复杂度为O(n)的排序算法
计数排序、桶排序、基数排序等。
以下将挑选三种较为经典的算法进行介绍。
冒泡排序
冒泡排序是一种基础的交换排序,是稳定排序类型。
package main.java;
import java.util.Arrays;
public class Sort {
public static void sort(int arrry[]) {
//记录最后一次交换的位置
int lastExchangeIndex = 0;
//无序数组的边界,每次比较只需要比较到这里为止
int sortBorder = arrry.length - 1;
for (int i = 0; i < arrry.length - 1; i++) {
//有序标记,每一轮的初始值都是True
boolean isSorted = true;
for (int j = 0; j < arrry.length - i - 1; j++) {
int tep = 0;
if (arrry[j] > arrry[j + 1]) {
tep = arrry[j];
arrry[j] = arrry[j + 1];
arrry[j + 1] = tep;
//因为有元素进行交换,所以不是有序的,标记为false
isSorted = false;
//跟新为最后一次交换元素的位置
lastExchangeIndex = j;
}
}
sortBorder = lastExchangeIndex;
if (isSorted) {
break;
}
}
}
public static void main(String[] args) {
int[] array = new int[]{5, 8, 6, 3, 2, 14, 1};
sort(array);
System.out.println(Arrays.toString(array));
}
}
快速排序
快速排序在每一轮挑选一个基准元素,并让其它比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。快速排序是不稳定的排序类型。
package main.java;
import java.util.Arrays;
public class QuickSort {
public static void quickSort(int[] array, int startIndex, int endIndex){
//递归结束条件,startIndex大于或等于endIndex时
if (startIndex>=endIndex){
return;
}
//等到基准元素位置
int pivotIndex = partition(array, startIndex, endIndex);
//根据基准元素,分成两部分进行递归排序
quickSort(array, startIndex, pivotIndex-1);
quickSort(array, pivotIndex+1, endIndex);
}
/**
* 分治 单边循环法
*/
private static int partition(int[] array, int startIndex, int endIndex){
// 取第一个位置(也可以随机位置的元素)做基准元素
int pivot = array[startIndex];
int mark = startIndex;
for(int i=startIndex+1;i<=endIndex; i++){
if(array[i]<pivot){
mark++;
int p = array[mark];
array[mark]=array[i];
array[i]=p;
}
}
array[startIndex] = array[mark];
array[mark] = pivot;
return mark;
}
public static void main(String[] args) {
int[] array = new int[]{1,3,8,7,9,6,14};
quickSort(array, 0, array.length-1);
System.out.println(Arrays.toString(array));
}
}
桶排序
桶排序是基于计数排序衍生出来的排序,借助桶来解决计数排序只能对整数进行排序的问题,是可以达到线性时间复杂度的稳定排序类型。
package main.java;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
public class BucketSort {
public static double[] bucketSort(double[] array) {
//1.得到数列的最大值和最小值,并求出差值d
double max = array[0];
double min = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
double d = max - min;
//2.初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);
for (int i = 0; i < bucketNum; i++) {
bucketList.add(new LinkedList<Double>());
}
//3.遍历原始数组,将每个元素放入桶中
for (int i = 0; i < array.length; i++) {
int num = (int) ((array[i] - min) * (bucketNum - 1) / d);
bucketList.get(num).add(array[i]);
}
//4.对每个桶内部进行排序
for (int i = 0; i < bucketList.size(); i++) {
//JDK底层采用归并排序
Collections.sort(bucketList.get(i));
}
//5.输出全部元素
double[] sortedArray = new double[array.length];
int index = 0;
for (LinkedList<Double> list : bucketList) {
for (double element : list) {
sortedArray[index] = element;
index++;
}
}
return sortedArray;
}
public static void main(String[] args) {
double[] array = new double[]{1.1, 5.5, 6.6, 7.6, 3.4, 9.9, 2.1, 14.4, 12.1};
double[] sortedArray = bucketSort(array);
System.out.println(Arrays.toString(sortedArray));
}
}
总结
通过对比这几种排序算法:
- 冒泡排序的平均时间复杂度为O(n^2),空间复杂度为O(1),稳定排序;
- 快速排序的平均时间复杂度为O(nlogn),空间复杂度O(logn),不稳定排序;
- 桶排序的平均时间复杂度为O(n),空间复杂度O(n),稳定排序;
通过对比这几类排序算法即可发现,桶排序的时间复杂度是最优的。但即使是桶排序的性能也并非稳定,如果元素分布极其不均衡,在极端情况下,第一个桶中一个元素,最后一个桶中1各元素,时间复杂度将退化为O(nlogn)。
由此可见,没有绝对好的算法,也没有绝对不好的算法,只不过是通过时间换空间或者空间换时间罢了,关键要看具体的使用场景。