Java实现十个经典排序算法(带动态效果图)

代码模板

/**

* 希尔排序

* @param array

*/

public static void shellSort(int[] array){

    int len = array.length;

    int temp, gap = len / 2;

    while (gap > 0) {

        for (int i = gap; i < len; i++) {

            temp = array[i];

            int preIndex = i - gap;

            while (preIndex >= 0 && array[preIndex] > temp) {

                array[preIndex + gap] = array[preIndex];

                preIndex -= gap;

            }

            array[preIndex + gap] = temp;

        }

        gap /= 2;

    }

}

归并排序

归并排序是采用的分而治之的递归方式来完成数据排序的,主要是将已有序的两个子序列,合并成一个新的有序子序列。先将子序列分段有序,然后再将分段后的子序列合并成,最终完成数据的排序。

主要步骤:

将数据的长度从中间一分为二,分成两个子序列,执行递归操作,直到每个子序列就剩两个元素。

然后分别对这些拆好的子序列进行归并排序。

将排序好的子序列再两两合并,最终合并成一个完整的排序序列。

动图演示

归并排序

代码模板

/**

    * 归并排序

    * @param array    数组

    * @param left      0

    * @param right    array.length-1

    */

    public static void mergeSort(int[] array,int left,int right){


        if (right <= left){

            return;

        }

        // 一分为二

        int mid = (left + right)/2;

        // 对前半部分执行归并排序

        mergeSort(array, left, mid);

        // 对后半部分执行归并排序

        mergeSort(array, mid + 1, right);

        // 将分好的每个子序列,执行排序加合并操作

        merge(array, left, mid, right);

    }

    /**

    * 合并加排序

    * @param array

    * @param left

    * @param middle

    * @param right

    */

    public static void merge(int[] array,int left,int middle,int right){

    // 中间数组

    int[] temp = new int[right - left + 1];

    int i = left, j = middle + 1, k = 0;

    while (i <= middle && j <= right) {

        // 若前面数组的元素小,就将前面元素的数据放到中间数组中

        if(array[i] <= array[j]){

            temp[k++] = array[i++];

        }else {

            // 若后面数组的元素小,就将后面数组的元素放到中间数组中

            temp[k++] = array[j++];

        }

    }

    // 若经过上面的比较合并后,前半部分的数组还有数据,则直接插入中间数组后面

    while (i <= middle){

        temp[k++] = array[i++];

    }

    // 若经过上面的比较合并后,后半部分的数组还有数据,则直接插入中间数组后面

    while (j <= right){

        temp[k++] = array[j++];

    }

    // 将数据从中间数组中复制回原数组

    for (int p = 0; p < temp.length; p++) {

        array[left + p] = temp[p];

    }

}

归并排序总结

归并排序效率很高,时间复杂度能达到O(nlogn),但是依赖额外的内存空间,而且这种分而治之的思想很值得借鉴,很多场景都是通过简单的功能,组成了复杂的逻辑,所以只要找到可拆分的最小单元,就可以进行分而治之了。

快速排序

快速排序,和二分查找的思想很像,都是先将数据一份为二然后再逐个处理。快速排序也是最常见的排序算法的一种,面试被问到的概率还是比较大的。

主要步骤:

从数据中挑选出一个元素,称为 "基准"(pivot),一般选第一个元素或最后一个元素。

然后将数据中,所有比基准元素小的都放到基准元素左边,所有比基准元素大的都放到基准元素右边。

然后再将基准元素前面的数据集合和后面的数据集合重复执行前面两步骤。

动图演示

快速排序

代码模板

/**

* 快速排序

* @param array 数组

* @param begin 0

* @param end  array.length-1

*/

public static void quickSort(int[] array, int begin, int end) {

    if (end <= begin) return;

    int pivot = partition(array, begin, end);

    quickSort(array, begin, pivot - 1);

    quickSort(array, pivot + 1, end);

}

/**

* 分区

* @param array

* @param begin

* @param end

* @return

*/

public static int partition(int[] array, int begin, int end) {

    // pivot: 标杆位置,counter: 小于pivot的元素的个数

    int pivot = end, counter = begin;

    for (int i = begin; i < end; i++) {

        if (array[i] < array[pivot]) {

          // 替换,将小于标杆位置的数据放到开始位置算起小于标杆数据的第counter位

            int temp = array[counter];

            array[counter] = array[i];

            array[i] = temp;

            counter++;

        }

    }

    // 将标杆位置的数据移动到小于标杆位置数据的下一个位。

    int temp = array[pivot];

    array[pivot] = array[counter];

    array[counter] = temp;

    return counter;

}

快速排序总结

我找的快速排序的模板代码,是比较巧妙的,选择了最后一个元素作为了基准元素,然后小于基准元素的数量,就是基准元素应该在的位置。这样看起来是有点不好懂,但是看明白之后,就会觉得这个模板写的还是比较有意思的。

堆排序

堆排序其实是采用的堆这种数据结构来完成的排序,一般堆排序的方式都是采用的一种近似完全二叉树来实现的堆的方式完成排序,但是堆的实现方式其实不止有用二叉树的方式,其实还有斐波那契堆。

而根据排序的方向又分为大顶堆和小顶堆:

大顶堆:每个节点值都大于或等于子节点的值,在堆排序中用做升序排序。

小顶堆:每个节点值都小于或等于子节点的值,在堆排序中用做降序排序。

像Java中的PriorityQueue就是小顶堆。

主要步骤:

创建一个二叉堆,参数就是无序序列[0~n];

把堆顶元素和堆尾元素互换;

调整后的堆顶元素,可能不是最大或最小的值,所以还需要调整此时堆顶元素的到正确的位置,这个调整位置的过程,主要是和二叉树的子元素的值对比后找到正确的位置。

重复步骤2、步骤3,直至整个序列的元素都在二叉堆的正确位置上了。

动图演示

堆排序

模板代码

/**

* 堆排序

* @param array

*/

public static int[] heapSort(int[] array){

    int size = array.length;

    // 先将数据放入堆中

    for (int i = (int) Math.floor(size / 2); i >= 0; i--) {

        heapTopMove(array, i, size);

    }

    // 堆顶位置调整

    for(int i = size - 1; i > 0; i--) {

        swapNum(array, 0, i);

        size--;

        heapTopMove(array, 0,size);

    }

    return array;

}

/**

* 堆顶位置维护

* @param array

* @param i

* @param size

*/

public static void heapTopMove(int[] array,int i,int size){

    int left = 2 * i + 1;

    int right = 2 * i + 2;

    int largest = i;

    if (left < size && array[left] > array[largest]) {

        largest = left;

    }

    if (right < size && array[right] > array[largest]) {

        largest = right;

    }

    if (largest != i) {

        swapNum(array, i, largest);

        heapTopMove(array, largest, size);

    }

}

/**

* 比较交换

* @param array

* @param left

* @param right

*/

public static void swapNum(int[] array,int left,int right){

    int temp = array[left];

    array[left] = array[right];

    array[right] = temp;

}

堆排序总结

堆排序的时间复杂度也是O(nlogn),这个也是有一定的概率在面试中被考察到,其实如果真实在面试中遇到后,可以在实现上不用自己去维护一个堆,而是用Java中的PriorityQueue来实现,可以将无序数据集合放入到PriorityQueue中,然后再依次取出堆顶数据,取出堆顶数据时要从堆中移除取出的这个元素,这样每次取出的就都是现有数据中最小的元素了。

计数排序

计数排序是一种线性时间复杂度的排序算法,它主要的逻辑时将数据转化为键存储在额外的数组空间里。计数排序有一定的局限性,它要求输入的数据,必须是有确定范围的整数序列。

主要步骤:

找出待排序的数组中最大和最小的元素;

统计数组中每个值为i的元素出现的次数,存入数组C的第i项;

对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);

反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。

动图演示

计数排序

代码模板

/**

* 计数排序

* @param array

*/

public static void countSort(int[] array){

    int bucketLen = getMaxValue(array) + 1;

    int[] bucket = new int[bucketLen];

    // 统计每个值出现的次数

    for (int value : array) {

        bucket[value]++;

    }

    // 反向填充数组

    int sortedIndex = 0;

    for (int j = 0; j < bucketLen; j++) {

        while (bucket[j] > 0) {

            array[sortedIndex++] = j;

            bucket[j]--;

        }

    }

}

/**

* 获取最大值

* @param arr

* @return

*/

private static int getMaxValue(int[] arr) {

    int maxValue = arr[0];

    for (int value : arr) {

        if (maxValue < value) {

            maxValue = value;

        }

    }

    return maxValue;

}

桶排序

桶排序算是计数排序的一个加强版,它利用特定函数的映射关系,将属于一定范围内的数据,放到一个桶里,然后对每个桶中的数据进行排序,最后再将排序好的数据拼接起来。

主要步骤:

设置一个合适长度的数组作为空桶;

遍历数据,将数据都放到指定的桶中,分布的越均匀越好;

对每个非空的桶里的数据进行排序;

将每个桶中排序好的数据拼接在一起。

动图演示

桶排序

代码模板

/**

  * 桶排序

  * @param arr

  * @param bucketSize

  * @return

  */

private static int[] bucketSort(int[] arr, int bucketSize){

    if (arr.length == 0) {

        return arr;

    }

    int minValue = arr[0];

    int maxValue = arr[0];

    // 计算出最大值和最小值

    for (int value : arr) {

        if (value < minValue) {

            minValue = value;

        } else if (value > maxValue) {

            maxValue = value;

        }

    }

    // 根据桶的长度以及数据的最大值和最小值,计算出桶的数量

    int bucketCount = (int) Math.floor((maxValue - minValue) / bucketSize) + 1;

    int[][] buckets = new int[bucketCount][0];

    // 利用映射函数将数据分配到各个桶中

    for (int i = 0; i < arr.length; i++) {

        int index = (int) Math.floor((arr[i] - minValue) / bucketSize);

        // 将数据填充到指定的桶中

        buckets[index] = appendBucket(buckets[index], arr[i]);

    }

    int arrIndex = 0;

    for (int[] bucket : buckets) {

        if (bucket.length <= 0) {

            continue;

        }

        // 对每个桶进行排序,这里使用了插入排序

        InsertSort.insertSort(bucket);

        for (int value : bucket) {

            arr[arrIndex++] = value;

        }

    }

    return arr;

}

/**

  * 扩容,并追加数据

  *

  * @param array

  * @param value

  */

private static int[] appendBucket(int[] array, int value) {

    array = Arrays.copyOf(array, array.length + 1);

    array[array.length - 1] = value;

    return array;

}

USB Microphone https://www.soft-voice.com/

Wooden Speakers  https://www.zeshuiplatform.com/

亚马逊测评 www.yisuping.cn

深圳网站建设www.sz886.com

你可能感兴趣的:(Java实现十个经典排序算法(带动态效果图))