八种常用排序算法总结

八种常用排序算法分类

时间复杂度、空间复杂度及稳定性分析


不同应用场景下的排序算法选择

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]+" ");
        }
    }
}
 
 

运行效果:


猜你喜欢

转载自blog.csdn.net/u012248802/article/details/79816176