欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java算法——排序算法

程序员文章站 2024-02-23 08:01:22
...

排序的分类

  1. 内部排序
    将需要处理的所有数据都加载到内部存储器中进行排序。

  2. 外部排序
    数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。

    内部排序分类:
    插入排序(直接插入排序、希尔排序)
    选择排序(简单选择排序、堆排序)
    交换排序(冒泡排序、快速排序)
    归并排序
    基数排序

排序法 平均时间 最差情况 稳定度 额外空间 备注
冒泡 O(n2) O(n2) 稳定 O(1) n小时较好
交换 O(n2) O(n2) 不稳定 O(1) n小时较好
选择 O(n2) O(n2) 不稳定 O(1) n小时较好
插入 O(n2) O(n2) 稳定 O(1) 大部分已排序时较好
基数 O(logRB) O(logRB) 稳定 O(n) B是真数(0-9)R是基数(个十百)
shell O(nlogn) O(n^s)1<s<2 不稳定 O(1) s是所选分组
快排 O(nlogn) O(n2) 不稳定 O(nlogn) n大时较好
归并 O(nlogn) O(nlogn) 稳定 O(1) n大时较好
O(nlogn) O(nlogn) 不稳定 O(1) n大时较好

测试代码

	public static void main(String[] args) {
		//测试速度
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int)(Math.random()*8000000);
        }

        Date date1 = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1Str = simpleDateFormat.format(date1);
        System.out.println("排序前时间"+date1Str);
        //测试
        具体的算法

        Date date2 = new Date();
        String date2Str = simpleDateFormat.format(date2);
        System.out.println("排序后时间"+date2Str);
        //System.out.println(Arrays.toString(arr));
    }

冒泡排序(Bubble Sorting)

基本思想:通过对待排序序列从前往后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前往后移动。如果一趟下来没有进行交换,说明序列有序,可以设置flag标志判断元素是否进行过交换,从而减少不必要的比较。

例子:
原始数组:5,2,4,1,3
第一趟排序
(1)2,5,4,1,3
(2)2,4,5,1,3
(3)2,4,1,5,3
(4)2,4,1,3,5
第二趟排序
(1)2,4,1,3,5
(2)2,1,4,3,5
(3)2,1,3,4,5
第三趟排序
(1)1,2,3,4,5
(2)1,2,3,4,5
第四趟排序
(1)1,2,3,4,5
排序规则:
(1)一共进行数组的大小-1次大的循环
(2)每一趟排序的次数在逐渐的减小
(3)如果我们发现在某趟排序中,没有发生一次交换,可以提前结束冒泡排序。(优化)

代码实现

    private static void Bubble(int[] arr){
        int temp = 0;
        boolean flag = false;//标示变量
        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]) {
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if (flag == false) {
                //在一趟排序中,一次交换都没有发生
                break;
            } else {
                flag = false;
            }
        }
    }

分析:处理8万个数据的时间大概是9s左右。

选择排序(Select Sorting)

基本思想:第一次从arr[0]-arr[n-1]中选择最小值,与arr[0]交换,第二次从arr[1]-arr[n-1]中选择最小值,与arr[1]交换,以此类推,得到一个按排序码从小到大的有序序列。

例子:
原始数组:101,52,124,24
第一轮:24,52,124,101
第二轮:24,52,124,101
第二轮:24,52,101,124

排序规则:
(1)一共进行数组的大小-1次大的排序
(2)每一轮排序,又是一个循环,循环规则:先假定自己是最小数,与后面比较,若有更小的数,重新确定最小数,并得到下标,一轮结束后得到最小数及其下标,进行交换。

代码实现

  private static void seleceSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) {
                    min = arr[j];//找到最小值
                    minIndex = j;//找到最小值下标
                }
            }
            //将最小值放在arr[0],即完成交换
            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }

分析:处理8万个数据的时间大概是2-3s,比冒泡要快很多。

插入排序(Insert Sorting)

基本思想:把n个待排序的元素看为一个有序表和一个无序表,开始时有序表中只有一个元素,无序表里面有n-1个元素,排序过程中每次从无序表中取出一个元素,依次与有序表中的元素进行比较,插入适当的位置。

代码实现

    private static void insertSort(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            //定义待插入的数
            int insertVal = arr[i];
            int insertIndex = i - 1;

            //insertIndex>=0保证给insertVal找插入位置,不越界
            //insertVal<arr[insertIndex]待插入的数,还没有找到插入的位置
            //就需要将arr[insertIndex]后移
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            //当退出循环时,说明找到了插入的位置,insertIndex+1
            arr[insertIndex + 1] = insertVal;
        }
    }

分析:处理8万个数据的时间大概是1s,比冒泡、选择要快。当插入的数据较小的时候,后移的次数明显增多,对效率有影响。

希尔排序(Shell Sorting)

基本思想:把记录按下标的一定增量分组,对每组使用直接插入排序算法排序,随着增量减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分为一组,算法终止。

代码实现
(1)交换法

    /**
     * 交换法
     *
     * @param arr
     */
    public static void shellSort(int[] arr) {

        int temp = 0;
        for(int gap = arr.length/2;gap>0;gap/=2){
            for (int i = gap; i < arr.length; i++) {
                //遍历各组中所有的元素(共gap组,每组有个元素)
                for (int j = i - gap; j >= 0; j -= gap) {
                    //如果当前元素大于加上步长后的那个元素,说明交换
                    if (arr[j] > arr[j + gap]) {
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
        }
    }

(2)移位法

	public static void shellSort(int[] arr) {
        for(int gap = arr.length/2;gap>0;gap/=2){
            for (int i = gap; i < arr.length; i++) {
                int j=i;
                int temp = arr[j];
                if(arr[j]<arr[j-gap]){
                    while(j-gap>=0&&temp<arr[j-gap]){
                        arr[j] = arr[j-gap];
                        j-=gap;
                    }
                }
            }
        }
	}

分析:交换法时间为7s左右,移位法时间为2s左右。

快速排序(Quick Sorting)

基本思想:通过一趟排序,将要排序的数据分割成独立的两个部分,其中一部分的所有数据都比另外一部分的所有睡觉要小,递归得到有序序列。