0
点赞
收藏
分享

微信扫一扫

Java实现的常用八种排序算法


 提到数据结构与算法,无法避免的一点就包含排序,熟练的掌握各种排序算法则是一个程序员必备的素质之一,除此之外,排序算法也是当下各大技术公司比较喜欢问的技术点,所以,就这一点青山整理了常见的8种排序算法,希望可以给诸位一点点的参考,有什么错误问题或者更好的解法,欢迎大家在评论区留言,小编一定不遗余力的学习与改正。

常见的8种排序算法性能对比

排序算法的分类

排序算法

最好时间

最坏时间

平均时间

辅助空间

稳定性

备注

交换排序

冒泡排序

O(n)

O(n^2)

O(n^2)

O(1)

稳定

n小时较好

快速排序

O(nlogn)

O(n^2)

O(nlogn)

O(logn)

不稳定

n大时较好

插入排序

直接插入排序

O(n)

O(n^2)

O(n^2)

O(1)

稳定

大部分元素已经有序

希尔排序

O(n)

O(nlogn)

O(n^k)(1<k<2)

O(1)

不稳定

K为所选分组

选择排序

选择排序

O(n^2)

O(n^2)

O(n^2)

O(1)

不稳定

n小时较好

堆排序

O(nlogn)

O(nlogn)

O(nlogn)

O(1)

不稳定

n大时较好

归并排序

O(nlogn)

O(nlogn)

O(nlogn)

O(n)

稳定

n大时较好

基数排序

O(d(n+k))

O(d(n+k))

O(d(n+k))

O(n+k)

稳定

元素大小差别大时较好

定义

在开始撸代码之前,对于以下几个概念还是要做一个简单的解释的哈,方便理解。

        时间复杂度:简单理解的话就是对一个数据规模为n的数据排序所需要的操作次数

        空间复杂度:简单理解就是执行排序算法时所需要的存储空间。其中O(1):空间复杂度不随n大小而改变时,是一个常量级别的空间复杂度;O(n):算法的空间复杂度与n成线性比例时的表示;O(logn):一个算法的空间复杂度与以2为底的对数成正比的时候的一种表示,2x = n,那么x就等于log2n;

        稳定性:所谓的稳定性就是排序前后,2个相等的元素的相对位置没有发生改变,那么这种排序算法就是稳定的,如: 排序前为A1,A2;排序后则还是A1,A2(A1=A2)。

算法

一、冒泡排序(交换排序)

所谓冒泡排序,通俗理解就是通过不断的将相邻的元素进行两两比较和交换,使得关键字较小的记录像水中气泡一样,不断上浮到最左侧;关键字较大的记录像石头一样不断下沉到最右侧,每一次就有一个最大的关键字下沉到最右侧,经过n趟相同操作,使得整个序列有序。代码如下: 

import java.util.Arrays;

/**
 * 常用排序算法之冒泡排序:
 * 算法描述:冒泡排序,不用比喻成泡泡上浮,个人觉得直接上定义,浅显易懂,就是对于一个拥有N个元素的无序序列
 *          进行至少N-1次的排序(最后一个元素,单个元素直接是有序的,故不需要再排),每次排序的过程都是相邻的两个元素进行比较,
 *          替换操作,因此,每一次至少会比较出一个最大值,
 *          而这个最大值就会落在序列的一头,然后对剩下的序列进行同样的操作,直到排序完成。
 *  最好时间复杂度:O(n^2),整个序列已经排好队,一次遍历搞定
 *  最坏的时间复杂度:O(n^2),
 *  平均的时间复杂度:O(n^2),
 *  空间复杂度:O(1);
 *
 *  稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *  在排序后仍然是A(1),A(2)
 *
 *  稳定性:稳定
 *
 *  //将ArrayList中元素转为int数组中元素
 *  //int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
 *
 *  备注:n较小时排序排序效率较好。
 */
public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //冒泡排序(实现算法就这么多代码)
    public static void 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 temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

二、快速排序(交换排序)

所谓快速排序,其实就是冒泡排序的升级版,二者都属于交换排序的范畴,不同的是,快排采用了分治思想,选出一个参照数之后,从头部和尾部分别开始与参照数比较,小于参照数的放在左侧,大于参照数的放在右侧,这样就类似二分的将一个序列分成了更小的两个序列,然后依次对参照数左侧和右侧的序列再进行递归快排,直到所有记录都有序为止。代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之快速排序:
 *   算法描述:快速排序,所谓快速排序,其实就是冒泡排序的升级版,二者都属于交换排序的范畴,
 *            不同的是,快速排序采用了二分思想,选出一个参照数之后,从头部和尾部开始分别与标准数
 *            这样,每遍历一次就会将相对参照数小和大的数,分别分散到了参照数的两侧,后面递归左侧和
 *            右侧,分别进行如上排序,直到全部有序。
 *    最好时间复杂度:O(nlog(n));
 *    最坏的时间复杂度:O(n^2),
 *    平均的时间复杂度:O(nlog(n)),
 *    空间复杂度:O(log(n));
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:不稳定
 *    备注:n大时比较好
 */
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    //快速排序
    public static void quickSort(int[] arr,int start,int end){
        if(start < end){
            int min = arr[start];
            int low = start;
            int high = end;
            while(low < high){
                while (low < high && min<=arr[high]){
                    high--;
                }
                //当不满足的high座标对应的数,肯定比min小,故将其赋值到low位置(min左侧)
                arr[low] = arr[high];
                while(low < high && min >= arr[low]){
                    low++;
                }
                arr[high] = arr[low];
            }
            arr[low] = min;
            //排序min左侧的序列
            quickSort(arr,start,low);
            //排序min右侧的序列
            quickSort(arr,low+1,end);
        }
    }
}

三、直接插入排序(插入排序)

所谓直接插入排序,就是给定一组记录,初始假设第一个记录自成一个有序序列,其余的序列为无序序列,接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的有序序列中,直到最后一个记录插入到有序序列中为止。代码如下:

/**
 * 常用排序算法之插入排序:
 *   算法描述:插入排序,把待排序的记录,按照其关键字的大小,逐个插入到一个已经排好序的有序序列中,
 *             直到所有的记录插完为止,得到一个全新的有序数列。
 *    最好时间复杂度:O(n);
 *    最坏的时间复杂度:O(n^2),
 *    平均的时间复杂度:O(n^2),
 *    空间复杂度:O(1);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:稳定
 *    备注:大部分元素已经有序时效果最好
 * @author qingshan
 * @time 2020/9/19 - 15:42
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //插入排序
    public static void insertSort(int[] arr) {
        //假设第一个记录是有序的,从第二个记录开始插入
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] < arr[i-1]){
                int temp = arr[i];
                int j;
                //当前位置向前比较,找到合适的位置插入
                for(j = i-1;j>=0&&temp<arr[j];j--){
                    arr[j+1]=arr[j];
                }
                arr[j+1]=temp;
            }
        }

    }

}

四、希尔排序(插入排序)

所谓希尔排序,就是插入排序的升级版,也叫缩小增量排序,先将待排序的元素分成多个子序列,使得每个子序列的元素个数相对较少,然后对每个子序列进行直接插入排序,这样使得每个子序列都有序,最后再将所有的元素进行一次插入排序。代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之希尔排序:
 *   算法描述:希尔排序,其实也是插入排序的升级版,也叫缩小增量排序,先将待排序的元素分成多个子序列
 *            这每个子序列排序起来速度会快很多,每个子序列都进行插入排序,这样使得每个子序列都有序
 *            最后再将所有的元素进行一次插入排序
 *    最好时间复杂度:O(n);
 *    最坏的时间复杂度:O(n^s,s=1.3),所以约等于O(nlog(n));
 *    平均的时间复杂度:O(nlog(n)),
 *    空间复杂度:O(1);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:不稳定
 *    备注:s为所选的分组
 * @author qingshan
 * @time 2020/9/19 - 15:42
 */
public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //插入排序
    public static void shellSort(int[] arr) {
        //遍历所有的步长
        for (int d = arr.length/2; d > 0; d/=2) {
            //每个步长的插入排序;
            for(int i = d;i<arr.length;i++){
                for(int j = i-d;j>=0;j-=d){
                    if(arr[j]>arr[j+d]){
                        int temp = arr[j];
                        arr[j] = arr[j+d];
                        arr[j+d] = temp;
                    }
                }
            }

        }

    }

}

五、简单选择排序(选择排序)

所谓简单选择排序,事先将数组分为已排序和未排序区域,每次从未排序的元素中通过交换将其放到已排序的去区域尾部。

步骤:1,进行数组长度-1轮比较

           2,每轮比较找到未排序区域的最小值下标

           3,如果最小值下标为非排序区域第一个元素,进行交换后,此时,未排序区域第一个元素则变成了已排序区域的最后一个元素。

           4,进行下一轮比较的时候,找到未排序区域的最小值,然后放到已排序区域的最末尾处,直到整个序列有序。

代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之选择排序:
 *   算法描述:选择排序,将序列分为已排序和未排序,每次从未排序的序列中找到最小的元素
 *    最好时间复杂度:O(n^2);
 *    最坏的时间复杂度:O(n^2),
 *    平均的时间复杂度:O(n^2),
 *    空间复杂度:O(1);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:不稳定
 *    备注:n小时较好
 * @author qingshan
 * @time 2020/9/19 - 15:42
 */
public class SelectSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        selectSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //选择排序
    public static void selectSort(int[] arr) {
        //遍历所有的数
        for (int i = 0; i < arr.length; i++) {
            //初始化最小元素的下标
            int minIndex = i;
            for (int j = i+1; j < arr.length; j++) {
                if(arr[minIndex]>arr[j]){
                    //记录最小值下标
                    minIndex = j;
                }
            }
            if(minIndex!=i){
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }

    }

}

Java实现的常用八种排序算法_时间复杂度

六、堆排序(选择排序)

堆:一种特殊的树形结构,其每个节点都有一个值,通常提到的堆都是指一颗完全二叉树,根节点的值大于(或者小于)两个子节点的值,同时,根节点的两个子树也分别十一个堆。

所谓堆排序,其实就是一种树形选择排序,堆排序中有大顶堆和小顶堆两种,其中大顶堆(升序ASC适用),每个父节点的值都比子节点要大,这样每次排序后都从根节点取出最大值放到队列尾部,将剩下的部分再次构建成大顶堆,再取出,直到所有元素都有序。小顶堆:原理类似,只不过每次找出的是最小数,适合降序(DESC)排列,代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之堆排序:
 *   算法描述:堆排序,堆排序,主要分为小顶堆和大顶堆,所谓的大顶堆(升序(ASC)适用)就是每个父节点都比子节点要大,这样
 *             每次排序后都取出最大的值放到了队列尾部,直到所有的元素有序。
 *             小顶堆:原理与大顶堆类似,只不过每次找出的是最小的值,所以适应于降序(DESC)排列
 *    最好时间复杂度:O(nlog(n));
 *    最坏的时间复杂度:O(nlog(n)),
 *    平均的时间复杂度:O(nlog(n)),
 *    空间复杂度:O(1);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:不稳定
 *    备注:n大的时候较好
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    //使用大顶堆进行堆排序
    public static void heapSort(int[] arr) {
        int start = (arr.length-2)/2;
        for (int i = start;i>=0; i--) {
            maxHeap(arr,arr.length,i);
        }
        for (int i = arr.length-1;i>=0; i--) {
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            maxHeap(arr,i,0);
        }
    }
    /**
     * 构造一个大顶堆
     * @param arr
     * @param size 要构造成大顶堆的元素有多少个
     * @param index 从哪个位置开始构造大顶堆,一般为最后一个节点的父节点开始
     */
    public static void maxHeap(int[] arr,int size,int index){
        int leftNode = 2*index+1;
        int rightNode = 2*index+2;
        int max = index;
        if(leftNode<size&&arr[leftNode]>arr[index]){
            max = leftNode;
        }
        if(rightNode<size&&arr[rightNode]>arr[index]){
            max = rightNode;
        }
        if(max!=index){
            int temp = arr[index];
            arr[index] = arr[max];
            arr[max] = temp;
            //处理一下由于交换位置导致的破坏原来的堆结构,
            maxHeap(arr,size,max);
        }
    }
}

七、归并排序

所谓归并排序,就是利用递归和分治的思想将数据划分为越来越小的半子表,然后再对半子表进行排序,最后再用递归的方法将排好序的半子表合并成越来越大的有序列表。代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之归并排序:
 *   算法描述:归并排序利用递归于分治思想将数据划分成为越来越小的半子表,再对半子表排序
 *              最后再用递归方法将排好序的半子表合并为越来越大的有序列表。
 *    最好时间复杂度:O(nlog(n));
 *    最坏的时间复杂度:O(nlog(n)),
 *    平均的时间复杂度:O(nlog(n)),
 *    空间复杂度:O(n);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:稳定
 *    备注:n大的时候较好
 */
public class MergeSort {

    public static void main(String[] args) {
        int[] arr = {3,2,6,1,8,4};
        System.out.println(Arrays.toString(arr));
        mergeSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    public static void mergeSort(int[] arr,int low,int high){
        if(low<high){
            int middle = low+(high-low)/2;
            mergeSort(arr,low,middle);
            mergeSort(arr,middle+1,high);
            merge(arr,low,high,middle);
        }
    }    //归并排序,将一个数组以middle为分界线,看成两个数组,然后进行递归归并操作
    public static void merge(int[] arr,int low,int high,int middle) {
        //先创建一个临时数组用来存储归并的元素
        int[] temp = new int[high-low+1];
        //记录第一个数组的遍历
        int i = low;
        //记录第二个数组的遍历
        int j = middle+1;
        //用于记录上面那个临时数组记录的下标(temp)
        int index = 0;
        while(i<=middle&&j<=high){
            if(arr[i]<=arr[j]){
                temp[index] = arr[i];
                i++;
            }else{
                temp[index] = arr[j];
                j++;
            }
            index++;
        }
        //处理其中一个数组的多余的数据
        while(i<=middle){
            temp[index] = arr[i];
            i++;
            index++;
        }
        while(j<=high){
            temp[index] = arr[j];
            j++;
            index++;
        }
        //把临时数组中的数据放回源数组
        for(int k = 0;k<temp.length;k++){
            arr[k+low]=temp[k];
        }
    }

}

八、基数排序

所谓基数排序,就是通过最大值的位数,然后按照每一位的数,放到对应的0-9的数组中,然后按照大小顺序从数组中取数,直到所有位数排完,就是有序了,利用了空间换时间。代码如下:

import java.util.Arrays;

/**
 * 常用排序算法之基数排序:
 *   算法描述:就是通过最大值的位数,然后按照每一位的数,放到0-9的数组中,然后按照顺序从数组中
 *              取数,直到所有位数排完,就是有序了。利用空间换时间
 *              第二种解决方法:就是在第一层数组中,使用队列来存取临时取出来的数据,根据队列先进先出
 *              的性质取数,直到有序。
 *    最好时间复杂度:O(d(n+j));
 *    最坏的时间复杂度:O(d(n+j)),
 *    平均的时间复杂度:O(d(n+j)),
 *    空间复杂度:O(n+j);
 *
 *    稳定性:所谓稳定性就是在一个序列中多个相同的元素,排序前和排序后的相对次序相同,如A(1),A(2)
 *    在排序后仍然是A(1),A(2)
 *
 *    稳定性:不稳定
 *    备注:n个元素中大小差距比较大的效果好
 * @author qingshan
 * @time 2020/9/19 - 15:42
 */
public class RadixSort {

    public static void main(String[] args) {
        int[] arr = {32,2,67,109,821,423};
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }
    public static void radixSort(int[] arr){
        //存数组中最大的数字
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i]>max){
                max = arr[i];
            }
        }
        //判断最大数字是几位数
        int maxLength = (max+"").length();
        //用于临时存储数据的数组
        int[][] temp = new int[10][arr.length];
        //用于记录在temp中相应的数组中存放的数字的数量
        int[] counts = new int[10];
        //根据最大长度的数决定要比较的次数
        for (int i = 0,n=1; i < maxLength; i++,n*=10) {
            for (int j = 0; j < arr.length; j++) {
                //计算余数
                int ys = arr[j] / n % 10;
                //把当前遍历的数据放入指定的数组中
                temp[ys][counts[ys]] = arr[j];
                //记录添加到每个数组中的数量
                counts[ys]++;
            }
            //记录取得元素需要放的位置
            int index = 0;
            //把数字取出来
            for(int k = 0;k<counts.length;k++){
                //记录数量的数组中当前余数记录的数量不为0;
                if(counts[k]!=0){
                    for(int l=0;l<counts[k];l++){
                        //取出元素
                        arr[index] = temp[k][l];
                        index++;
                    }
                    //把数量置为0
                    counts[k] = 0;
                }
            }
        }

    }

}



举报

相关推荐

0 条评论