八种常用排序算法分类
![](https://i-blog.csdnimg.cn/blog_migrate/731e7d52a0608937619d1e923a3bfc74.png)
时间复杂度、空间复杂度及稳定性分析
![](https://i-blog.csdnimg.cn/blog_migrate/d80567e4914258130902d0ae51992317.png)
不同应用场景下的排序算法选择
1、数据规模较小
- 待排序列基本序的情况下,可以选择直接插入排序;
- 对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
2、数据规模不是很大
- 完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间;
- 序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
3、数据规模很大
- 对稳定性有求,则可考虑归并排序;
- 对稳定性没要求,宜用堆排序
4、序列初始基本有序(正序),宜用直接插入,冒泡
算法的Java实现
import java.util.ArrayList;
/**
* 八大排序算法
*/
public class SortAlgorithms {
//选择排序
public static void selectionSort(int[] arr, int len){
for (int i = 0; i < len; i++) {
int minIndex = i;//寻找[i, len)区间里的最小值下标
for (int j = i+1; j < len; j++) {
if (arr[j] < arr[minIndex]){
minIndex = j;
}
}
//将i位置的元素和最小值交换
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
//插入排序:可以提前终止内层循环
public static void insertionSort(int[] arr, int len){
//第一个元素不需要进行插入操作
for (int i = 1; i < len; i++) {
//寻找arr[i]合适的插入位置
int insertNum = arr[i];
int j;
for (j = i; j > 0 && arr[j-1] > insertNum; j--) {
//如果当前元素比前一个元素小,则交换
arr[j] = arr[j-1];
}
arr[j] = insertNum;
}
}
//冒泡排序
public static void bubbleSort(int[] arr, int len){
boolean swapped;
do {
swapped = false;
for (int i = 1; i < len; i++) {
if (arr[i-1] > arr[i]){
//交换
int temp = arr[i];
arr[i] = arr[i-1];
arr[i-1] = temp;
swapped = true;
}
}
len--;
}while (swapped);
}
//希尔排序
public static void shellSort(int[] arr, int len){
int h = len/2;
while (h>0){
//h-sort the array
for (int i = h; i < len; i++) {
int insertNum = arr[i];
int j;
for (j = i; j >=h && insertNum < arr[j-h]; j-=h) {
arr[j] = arr[j-h];
}
arr[j] = insertNum;
}
h /= 2;
}
}
//基于递归的归并排序
public static void mergeSort(int[] arr, int low, int high){
int mid = low + (high - low)/2;
if (mid < high){
mergeSort(arr, low, mid);//左边
mergeSort(arr,mid+1, high);//右边
//左右合并
merge(arr, low, mid, high);
//if (arr[mid] > arr[mid+1]){//这一步是一个优化操作,左右都是排好序的,只有当左边的最后一个元素比右边第一个大时才归并
// merge(arr, low, mid, high);
//}
}
}
//归并函数
public static void merge(int[] arr, int low, int mid, int high){
int mergeLen = high-low+1;
int[] temp = new int[mergeLen];
int left = low;
int right = mid+1;
int i = 0;
//把较小的数据放到新数组
while (left<=mid && right<=high){
if (arr[left]<arr[right]){
temp[i++] = arr[left++];
}else {
temp[i++] = arr[right++];
}
}
//把左数组中剩余的元素复制到排序数组
while (left<=mid){
temp[i++] = arr[left++];
}
//把右数组中剩余的元素复制到排序数组
while (right<=high){
temp[i++] = arr[right++];
}
//用新数组覆盖原数组
for (int j = 0; j < mergeLen; j++) {
arr[j + low] = temp[j];
}
}
//基于迭代的归并排序
public static void mergeSortBU(int[] arr, int len){
for (int sz = 1; sz <= len; sz += sz) {
//对arr[i...i+sz-1]和arr[i+sz...i+2*sz-1]进行归并
for (int i = 0; i+sz < len; i += sz+sz) {
merge(arr,i,i+sz-1, Math.min(i+sz+sz-1, len-1));
}
}
}
//快速排序
public static void quickSort(int[] arr, int low, int high){
if (low < high){
int left = low;
int right = high;
//通过随机选取数组中的元素作为基准元素,下面这一块是快排优化的一种方法
//int random = low + (int)(Math.random()*(high-low+1));
//int tmp = arr[low];
//arr[low] = arr[random];
//arr[random] = tmp;
int temp = arr[low];
while (left != right){
while (left<right && arr[right]>temp){
right--;
}
if (left<right){
arr[left] = arr[right];
left++;
}
while (left<right && arr[left]<temp){
left++;
}
if (left<right){
arr[right] = arr[left];
right--;
}
}
arr[left] = temp;
quickSort(arr, low,left-1);
quickSort(arr,left+1, high);
}
}
//堆排序
//使用数组存储二叉堆,根节点的索引号为0,从上往下,从左往右索引号递增1
//parent(i) = (i-1)/2
//left child(i) = i*2+1
//right child(i) = i*2+2
public static void heapSort(int[] arr, int len){
//循环建立最大堆,每次将最大堆的最大元素和数组的最后一个元素交换,再对无序的元素重新建堆
for (int i = len-1; i >0; i--) {//只剩最后一个元素时就完成排序了
buildMaxHeap(arr,i);
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
}
}
//建立最大堆
public static void buildMaxHeap(int[] arr, int last){
//从最后一个非叶子开始遍历,将不满足大顶堆的元素和它的左右孩子的最大值交换
for (int i = (last-1)/2; i >= 0; i--) {
int k = i;//记录当前正在判断的节点
//判断是否有左孩子,有左孩子的情况下再判断是否有右孩子,然后再判断是否需要交换
while (2*k+1 <= last){
int bigIndex = 2*k+1;//左孩子的索引
if (bigIndex < last){//说明存在右孩子
if (arr[bigIndex] < arr[bigIndex+1]){//比较左右孩子的大小
bigIndex++;
}
}
if (arr[k] < arr[bigIndex]){//父节点比其左右孩子的最大值小
int temp = arr[k];
arr[k] = arr[bigIndex];
arr[bigIndex] = temp;
k = bigIndex;
}else {
break;
}
}
}
}
//基数排序
public static void radixSort(int[] arr, int len) {
//先算出最大数的位数;
int max = arr[0];
for (int i = 1; i < len; i++) {
max = Math.max(max, arr[i]);
}
int maxDigit = 0;
while (max != 0) {
max /= 10;
maxDigit++;
}
int mod = 10, div = 1;
ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
for (int i = 0; i < 10; i++)
bucketList.add(new ArrayList<>());
for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
for (int j = 0; j < len; j++) {
int num = (arr[j] % mod) / div;
bucketList.get(num).add(arr[j]);
}
int index = 0;
for (int j = 0; j < bucketList.size(); j++) {
for (int k = 0; k < bucketList.get(j).size(); k++)
arr[index++] = bucketList.get(j).get(k);
bucketList.get(j).clear();
}
}
}
//生成有n个元素的随机数组,每一个元素的随机范围为[rangeL, rangeR]
public static int[] generateRandomArray(int n, int rangeL, int rangeR){
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = rangeL +(int)(Math.random()*(rangeR-rangeL+1));
}
return arr;
}
//拷贝数组
public static int[] copyIntArray(int[] arr, int len){
int[] newArr = new int[len];
for (int i = 0; i < len; i++) {
newArr[i] = arr[i];
}
return newArr;
}
public static void main(String[] args) {
int n = 30;
int[] arr1 = generateRandomArray(n,0, n);
int[] arr2 = copyIntArray(arr1, n);
int[] arr3 = copyIntArray(arr1, n);
int[] arr4 = copyIntArray(arr1, n);
int[] arr5 = copyIntArray(arr1, n);
int[] arr6 = copyIntArray(arr1, n);
int[] arr7 = copyIntArray(arr1, n);
int[] arr8 = copyIntArray(arr1, n);
int[] arr9 = copyIntArray(arr1, n);
System.out.println("-----------排序前-------------");
for (int i = 0; i < n; i++) {
System.out.print(arr7[i]+" ");
}
System.out.println("\n\n*******排序时间********");
long startTime1 = System.currentTimeMillis();
selectionSort(arr1, n);
long endTime1 = System.currentTimeMillis();
System.out.println("选择排序:\t" + (endTime1-startTime1)/1000.0 + "s");
long startTime2 = System.currentTimeMillis();
insertionSort(arr2, n);
long endTime2 = System.currentTimeMillis();
System.out.println("插入排序:\t" + (endTime2-startTime2)/1000.0 + "s");
long startTime3 = System.currentTimeMillis();
bubbleSort(arr3, n);
long endTime3 = System.currentTimeMillis();
System.out.println("冒泡排序:\t" + (endTime3-startTime3)/1000.0 + "s");
long startTime4 = System.currentTimeMillis();
shellSort(arr4, n);
long endTime4 = System.currentTimeMillis();
System.out.println("希尔排序:\t" + (endTime4-startTime4)/1000.0 + "s");
long startTime5 = System.currentTimeMillis();
mergeSort(arr5,0,n-1);
long endTime5 = System.currentTimeMillis();
System.out.println("自顶向下归并排序:\t" + (endTime5-startTime5)/1000.0 + "s");
long startTime6 = System.currentTimeMillis();
mergeSortBU(arr6, n);
long endTime6 = System.currentTimeMillis();
System.out.println("自底向上归并排序:\t" + (endTime6-startTime6)/1000.0 + "s");
long startTime7 = System.currentTimeMillis();
quickSort(arr7,0,n-1);
long endTime7 = System.currentTimeMillis();
System.out.println("快速排序:\t" + (endTime7-startTime7)/1000.0 + "s");
long startTime8 = System.currentTimeMillis();
heapSort(arr8, n);
long endTime8 = System.currentTimeMillis();
System.out.println("堆排序:\t" + (endTime8-startTime8)/1000.0 + "s");
long startTime9 = System.currentTimeMillis();
radixSort(arr9, n);
long endTime9 = System.currentTimeMillis();
System.out.println("基数排序:\t" + (endTime9-startTime9)/1000.0 + "s");
System.out.println("\n-----------排序后-------------");
for (int i = 0; i < n; i++) {
System.out.print(arr3[i]+" ");
}
}
}
运行效果:
![](https://i-blog.csdnimg.cn/blog_migrate/e78a021ce5c9890d3f3479479a3172c3.png)