Java eight kinds of sorting algorithms practice

First, the eight kinds of sorting algorithm different amounts of test data

Second, the way to test the code

Third, the quick sort

Fourth, Hill sorting

Fifth, merge sort

Sixth, radix sort

Seven, insertion sort

Eight, selection sort

Nine, heap sort

Ten, bubble sort


First, the eight kinds of sorting algorithm different amounts of test data

100条随机数据
快速排序 不稳定 耗时: 1 ms
希尔排序 不稳定 耗时: 1 ms
归并排序 稳定 耗时: 0 ms
基数排序 稳定 耗时: 1 ms
插入排序 稳定 耗时: 0 ms
选择排序算法 不稳定 耗时: 1 ms
堆排序 不稳定 耗时: 1 ms
冒泡排序 稳定 耗时: 1 ms
冒泡排序Plus 稳定 耗时: 0 ms

1k条随机数据
快速排序 不稳定 耗时: 1 ms
希尔排序 不稳定 耗时: 2 ms
归并排序 稳定 耗时: 3 ms
基数排序 稳定 耗时: 8 ms
插入排序 稳定 耗时: 4 ms
选择排序算法 不稳定 耗时: 7 ms
堆排序 不稳定 耗时: 10 ms
冒泡排序 稳定 耗时: 11 ms
冒泡排序Plus 稳定 耗时: 9 ms

1w条随机数据
快速排序 不稳定 耗时: 7 ms
希尔排序 不稳定 耗时: 17 ms
归并排序 稳定 耗时: 9 ms
基数排序 稳定 耗时: 40 ms
插入排序 稳定 耗时: 23 ms
选择排序算法 不稳定 耗时: 93 ms
堆排序 不稳定 耗时: 183 ms
冒泡排序 稳定 耗时: 200 ms
冒泡排序Plus 稳定 耗时: 201 ms

10w条随机数据
快速排序 不稳定 耗时: 41 ms
希尔排序 不稳定 耗时: 27 ms
归并排序 稳定 耗时: 57 ms
基数排序 稳定 耗时: 366 ms
插入排序 稳定 耗时: 1901 ms
选择排序算法 不稳定 耗时: 5704 ms
堆排序 不稳定 耗时: 16578 ms
冒泡排序 稳定 耗时: 21210 ms
冒泡排序Plus 稳定 耗时: 20488 ms

20w条随机数据
快速排序 不稳定 耗时: 58 ms
希尔排序 不稳定 耗时: 65 ms
归并排序 稳定 耗时: 64 ms
基数排序 稳定 耗时: 3157 ms
插入排序 稳定 耗时: 4442 ms
选择排序算法 不稳定 耗时: 22142 ms
堆排序 不稳定 耗时: 67493 ms
冒泡排序 稳定 耗时: 81873 ms
冒泡排序Plus 稳定 耗时: 83514 ms

30w条随机数据   排除先前后面三种耗时长的算法
快速排序 不稳定 耗时: 57 ms
希尔排序 不稳定 耗时: 79 ms
归并排序 稳定 耗时: 83 ms
基数排序 稳定 耗时: 5000 ms
插入排序 稳定 耗时: 10154 ms
选择排序算法 不稳定 耗时: 50156 ms

40w条随机数据   排除先前后面一种耗时长的算法
快速排序 不稳定 耗时: 100 ms
希尔排序 不稳定 耗时: 162 ms
归并排序 稳定 耗时: 128 ms
基数排序 稳定 耗时: 7173 ms
插入排序 稳定 耗时: 17932 ms

50w条随机数据
快速排序 不稳定 耗时: 91 ms
希尔排序 不稳定 耗时: 136 ms
归并排序 稳定 耗时: 176 ms
基数排序 稳定 耗时: 10244 ms
插入排序 稳定 耗时: 29190 ms

60w条随机数据
快速排序 不稳定 耗时: 134 ms
希尔排序 不稳定 耗时: 183 ms
归并排序 稳定 耗时: 150 ms
基数排序 稳定 耗时: 13585 ms
插入排序 稳定 耗时: 43520 ms

70w条随机数据 排除先前后面一种耗时长的算法
快速排序 不稳定 耗时: 153 ms
希尔排序 不稳定 耗时: 177 ms
归并排序 稳定 耗时: 204 ms
基数排序 稳定 耗时: 17881 ms

80w条随机数据
快速排序 不稳定 耗时: 138 ms
希尔排序 不稳定 耗时: 222 ms
归并排序 稳定 耗时: 195 ms
基数排序 稳定 耗时: 22211 ms


90w条随机数据
快速排序 不稳定 耗时: 174 ms
希尔排序 不稳定 耗时: 249 ms
归并排序 稳定 耗时: 238 ms
基数排序 稳定 耗时: 30343 ms

100w条随机数据
快速排序 不稳定 耗时: 172 ms
希尔排序 不稳定 耗时: 310 ms
归并排序 稳定 耗时: 259 ms
基数排序 稳定 耗时: 35890 ms

500w条随机数据  排除先前后面一种耗时长的算法
快速排序 不稳定 耗时: 955 ms
希尔排序 不稳定 耗时: 1980 ms
归并排序 稳定 耗时: 1260 ms

1000w条随机数据
快速排序 不稳定 耗时: 2201 ms
希尔排序 不稳定 耗时: 2864 ms
归并排序 稳定 耗时: 2337 ms

经过上面的数据测试得出,快速排序、希尔排序、归并排序三种算法计算性能优异

Second, the way to test the code

 /**
     * 获取一个打乱的数组
     *
     * @param arr
     */
    private static int[] getRandomArr(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            arr[i] = new Random().nextInt(arr.length);
        }
        return arr;
    }



   private static void testSort() {
        long s;

        int[] arr = new int[10000000];
        int[] a = getRandomArr(arr);
        int[] b = a.clone();
        int[] c = b.clone();
        int[] d = b.clone();
        int[] e = b.clone();
        int[] f = b.clone();
        int[] g = b.clone();
        int[] h = b.clone();
        int[] i = b.clone();


        s = Clock.systemDefaultZone().millis();
        QuickSort.sort(f);
        System.out.println("快速排序 不稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        ShellSort.sort(i);
        System.out.println("希尔排序 不稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        MergeSort.sort(e);
        System.out.println("归并排序 稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        RadixSort.sort(g);
        System.out.println("基数排序 稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        InsertSort.sort(d);
        System.out.println("插入排序 稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        SelectSort.sort(h);
        System.out.println("选择排序算法 不稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        HeapSort.sort(c);
        System.out.println("堆排序 不稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        BubbleSort.sort(a);
        System.out.println("冒泡排序 稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");

        s = Clock.systemDefaultZone().millis();
        BubbleSort.sortPlus(b);
        System.out.println("冒泡排序Plus 稳定 耗时: " + (Clock.systemDefaultZone().millis() - s) + " ms");
    }

Third, the quick sort

/**
     * 快速排序
     *
     * @param numbers 带排序数组
     */
    public static void sort(int[] numbers) {
        Test.printArr("快速排序 前:", numbers);
        if (numbers.length > 0) {   //查看数组是否为空
            quickSort(numbers, 0, numbers.length - 1);
        }
        Test.printArr("快速排序 后:", numbers);
    }

    /**
     * 查找出中轴(默认是最低位low)的在numbers数组排序后所在位置
     *
     * @param numbers 带查找数组
     * @param low     开始位置
     * @param high    结束位置
     * @return 中轴所在位置
     */
    public static int getMiddle(int[] numbers, int low, int high) {
        int temp = numbers[low]; //数组的第一个作为中轴
        while (low < high) {
            while (low < high && numbers[high] >= temp) {
                high--;
            }
            numbers[low] = numbers[high];//比中轴小的记录移到低端
            while (low < high && numbers[low] < temp) {
                low++;
            }
            numbers[high] = numbers[low]; //比中轴大的记录移到高端
        }
        numbers[low] = temp; //中轴记录到尾
        return low; // 返回中轴的位置
    }

    /**
     * @param numbers 带排序数组
     * @param low     开始位置
     * @param high    结束位置
     */
    public static void quickSort(int[] numbers, int low, int high) {
        if (low < high) {
            int middle = getMiddle(numbers, low, high); //将numbers数组进行一分为二
            quickSort(numbers, low, middle - 1);   //对低字段表进行递归排序
            quickSort(numbers, middle + 1, high); //对高字段表进行递归排序
        }

    }

Fourth, Hill sorting

/**
     * 希尔排序
     * <p>
     * 希尔排序的原理:根据需求,如果你想要结果从大到小排列,它会首先将数组进行分组,然后将较大值移到前面,较小值
     * 移到后面,最后将整个数组进行插入排序,这样比起一开始就用插入排序减少了数据交换和移动的次数,可以说希尔排序是加强
     * 版的插入排序
     * 拿数组5, 2, 8, 9, 1, 3,4来说,数组长度为7,当increment为3时,数组分为两个序列
     * 5,2,8和9,1,3,4,第一次排序,9和5比较,1和2比较,3和8比较,4和比其下标值小increment的数组值相比较
     * 此例子是按照从大到小排列,所以大的会排在前面,第一次排序后数组为9, 2, 8, 5, 1, 3,4
     * 第一次后increment的值变为3/2=1,此时对数组进行插入排序,
     * 实现数组从大到小排
     */
    public static void sort(int[] data) {
        Test.printArr("希尔排序 前:", data);
        int j = 0;
        int temp = 0;
        //每次将步长缩短为原来的一半
        for (int increment = data.length / 2; increment > 0; increment /= 2) {
            for (int i = increment; i < data.length; i++) {
                temp = data[i];
                for (j = i; j >= increment; j -= increment) {
                    if (temp > data[j - increment])//如想从小到大排只需修改这里
                    {
                        data[j] = data[j - increment];
                    } else {
                        break;
                    }
                }
                data[j] = temp;
            }
        }
        Test.printArr("希尔排序 后:", data);
    }

Fifth, merge sort

public static void sort(int[] numbers) {
        Test.printArr("归并排序 前:", numbers);
        mergeSort(numbers, 0, numbers.length - 1);
        Test.printArr("归并排序 后:", numbers);
    }

    /**
     * 归并排序
     * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。
     * 然后再把有序子序列合并为整体有序序列
     * 时间复杂度为O(nlogn)
     * 稳定排序方式
     *
     * @param nums 待排序数组
     * @return 输出有序数组
     */
    public static void mergeSort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(nums, low, mid);
            // 右边
            mergeSort(nums, mid + 1, high);
            // 左右归并
            merge(nums, low, mid, high);
        }
    }

    /**
     * 将数组中low到high位置的数进行排序
     *
     * @param nums 待排序数组
     * @param low  待排的开始位置
     * @param mid  待排中间位置
     * @param high 待排结束位置
     */
    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;

        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }

        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = nums[i++];
        }

        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = nums[j++];
        }

        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }

Sixth, radix sort

/**
     * 基数排序  本实现方式没有避免负数排序时报错问题
     */
    public static void sort(int[] numbers) {
        Test.printArr("基数排序 前:", numbers);
        int max = numbers[0];
        for (int i = 1; i < numbers.length; i++) {
            if (numbers[i] > max) {
                max = numbers[i];
            }
        }
        int time = 0;
        while (max > 0) {
            max /= 10;
            time++;
        }
        List<ArrayList<Integer>> queue = new ArrayList<ArrayList<Integer>>();
        for (int i = 0; i < 10; i++) {
            ArrayList<Integer> queue1 = new ArrayList<Integer>();
            queue.add(queue1);
        }
        for (int i = 0; i < time; i++) {
            for (int j = 0; j < numbers.length; j++) {
                int x = numbers[j] % (int) Math.pow(10, i + 1) / (int) Math.pow(10, i);
                ArrayList<Integer> queue2 = queue.get(x);
                queue2.add(numbers[j]);
                queue.set(x, queue2);
            }
            int count = 0;
            for (int k = 0; k < 10; k++) {
                while (queue.get(k).size() > 0) {
                    ArrayList<Integer> queue3 = queue.get(k);
                    numbers[count] = queue3.get(0);
                    queue3.remove(0);
                    count++;
                }
            }
        }
        Test.printArr("基数排序 后:", numbers);
    }

Seven, insertion sort

 /**
     * 插入排序
     * <p>
     * 从第一个元素开始,该元素可以认为已经被排序
     * 取出下一个元素,在已经排序的元素序列中从后向前扫描
     * 如果该元素(已排序)大于新元素,将该元素移到下一位置
     * 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
     * 将新元素插入到该位置中
     * 重复步骤2
     *
     * @param numbers 待排序数组
     */
    public static void sort(int[] numbers) {
        Test.printArr("插入排序 前:", numbers);
        int size = numbers.length;
        int temp = 0;
        int j = 0;

        for (int i = 0; i < size; i++) {
            temp = numbers[i];
            //假如temp比前面的值小,则将前面的值后移
            for (j = i; j > 0 && temp < numbers[j - 1]; j--) {
                numbers[j] = numbers[j - 1];
            }
            numbers[j] = temp;
        }
        Test.printArr("插入排序 后:", numbers);
    }

Eight, selection sort

 /**
     * 选择排序算法
     * 在未排序序列中找到最小元素,存放到排序序列的起始位置
     * 再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。
     * 以此类推,直到所有元素均排序完毕。
     *
     * @param numbers
     */
    public static void sort(int[] numbers) {
        Test.printArr("选择排序算法 前:", numbers);
        int size = numbers.length; //数组长度
        int temp = 0; //中间变量

        for (int i = 0; i < size; i++) {
            int k = i;   //待确定的位置
            //选择出应该在第i个位置的数
            for (int j = size - 1; j > i; j--) {
                if (numbers[j] < numbers[k]) {
                    k = j;
                }
            }
            //交换两个数
            temp = numbers[i];
            numbers[i] = numbers[k];
            numbers[k] = temp;
        }
        Test.printArr("选择排序算法 后:", numbers);
    }

Nine, heap sort

/**
     * 堆排序是一种树形选择排序,是对直接选择排序的有效改进。
     *   堆的定义下:具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1)
     * (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为
     * 最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
     *   思想:初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根
     * 节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有
     * 两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,
     * 二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排
     * 序的函数。
     */
    public static void sort(int[] a) {
        Test.printArr("堆排序 前:", a);
        int arrayLength = a.length;
        //循环建堆
        for (int i = 0; i < arrayLength - 1; i++) {
            //建堆
            buildMaxHeap(a, arrayLength - 1 - i);
            //交换堆顶和最后一个元素
            swap(a, 0, arrayLength - 1 - i);

        }
        Test.printArr("堆排序 后:", a);
    }

    //对data数组从0到lastIndex建大顶堆
    public static void buildMaxHeap(int[] data, int lastIndex) {
        //从lastIndex处节点(最后一个节点)的父节点开始
        for (int i = (lastIndex - 1) / 2; i >= 0; i--) {
            //k保存正在判断的节点
            int k = i;
            //如果当前k节点的子节点存在
            while (k * 2 + 1 <= lastIndex) {
                //k节点的左子节点的索引
                int biggerIndex = 2 * k + 1;
                //如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在
                if (biggerIndex < lastIndex) {
                    //若果右子节点的值较大
                    if (data[biggerIndex] < data[biggerIndex + 1]) {
                        //biggerIndex总是记录较大子节点的索引
                        biggerIndex++;
                    }
                }
                //如果k节点的值小于其较大的子节点的值
                if (data[k] < data[biggerIndex]) {
                    //交换他们
                    swap(data, k, biggerIndex);
                    //将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值
                    k = biggerIndex;
                } else {
                    break;
                }
            }
        }
    }

    //交换
    private static void swap(int[] data, int i, int j) {
        int tmp = data[i];
        data[i] = data[j];
        data[j] = tmp;
    }

Ten, bubble sort

 /**
     * 冒泡排序   稳定
     *
     * @param numbers 需要排序的数组
     */
    public static void sort(int[] numbers) {
        Test.printArr("冒泡排序 前:", numbers);
        for (int x = 0; x < numbers.length - 1; x++) {
            for (int y = 0; y < numbers.length - 1 - x; y++) {
                if (numbers[y] > numbers[y + 1]) {
                    int t = numbers[y];
                    numbers[y] = numbers[y + 1];
                    numbers[y + 1] = t;
                }
            }
        }
        Test.printArr("冒泡排序 后:", numbers);
    }

    /**
     * 优化冒泡排序
     * 加入一个布尔变量,如果内循环没有交换值,说明已经排序完成,提前终止
     * @param arr
     */
    public static void sortPlus(int[] arr){
        Test.printArr("冒泡排序Plus 前:", arr);
        if(arr != null && arr.length > 1){
            for(int i = 0; i < arr.length - 1; i++){
                // 初始化一个布尔值
                boolean flag = true;
                for(int j = 0; j < arr.length - i - 1 ; j++){
                    if(arr[j] > arr[j+1]){
                        // 调换
                        int temp;
                        temp = arr[j];
                        arr[j] = arr[j+1];
                        arr[j+1] = temp;
                        // 改变flag
                        flag = false;
                    }
                }
                if(flag){
                    break;
                }
            }
        }
        Test.printArr("冒泡排序Plus 后:", arr);
    }

 

Published 82 original articles · won praise 44 · views 70000 +

Guess you like

Origin blog.csdn.net/sinat_28864443/article/details/104678922