十大排序算法与Java实现

本文参考资源:

https://github.com/iTimeTraveler/SortAlgorithms

十大经典排序算法 - 冰狼爱魔 - 博客园

十大经典排序算法总结(Java语言实现)_Java_wang的博客-CSDN博客

排序算法概述

十大排序算法与Java实现

可以看到时间复杂度低的算法空间复杂度通常较高,甚至最后三种算法已经没有原地算法了,这就是常说的“用空间换时间”,越往后越不接近人的自然思想,可能就会更难理解,毕竟人通常是不会在脑子里排序的时候为这个数组开辟出一块新区域用空间换时间的。

是否稳定的意思是指,一轮排序之后,是否会破坏尚未比较元素之间的相对顺序,若排序前数组本身有序性已经较为良好,使用稳定算法进行排序效率会高些。而非稳定的算法有的时候应用在数据有序性不太好的情况下偶尔有奇效。

插入排序

插入排序把数组分为已排序的部分和未排序的部分,这种算法通常采用原地策略,将每个元素与之前的元素交换,直到找到合适的位置。

十大排序算法与Java实现

Java代码:

public void insertionSort2(int[] arr){
    for(int i=0; i<arr.length-1; i++ ) {
        for( int j=i+1; j>0; j-- ) {
            if( arr[j-1] <= arr[j] )
                break;
            int temp = arr[j];      //交换操作
            arr[j] = arr[j-1];
            arr[j-1] = temp;
        }
    }
    System.out.println(Arrays.toString(arr));
}

选择排序

选择排序也是很接近人的自然思想的一种排序方法,也分为已排序的部分和未排序的部分,直接在未排序的序列中找到最小值,附加到已排序的序列末尾(和未排序序列的第一个元素交换)

十大排序算法与Java实现

Java代码:

public void insertionSort2(int[] arr){
    for(int i=0; i<arr.length-1; i++ ) {
        for( int j=i+1; j>0; j-- ) {
            if( arr[j-1] <= arr[j] )
                break;
            int temp = arr[j];      //交换操作
            arr[j] = arr[j-1];
            arr[j-1] = temp;
        }
    }
    System.out.println(Arrays.toString(arr));
}

冒泡排序

冒泡排序的思想也很容易理解,每次遍历比较相邻的两个元素,将它们的顺序排好,这样一直遍历直到所有元素被正确排序。

十大排序算法与Java实现

/**
 * 冒泡排序
 *
 * ①. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
 * ②. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
 * ③. 针对所有的元素重复以上的步骤,除了最后一个。
 * ④. 持续每次对越来越少的元素重复上面的步骤①~③,直到没有任何一对数字需要比较。
 * @param arr  待排序数组
 */
public static void bubbleSort(int[] arr){
    for (int i = arr.length - 1; i > 0; i--) {      //外层循环移动游标
        for(int j = 0; j < i; j++){    //内层循环遍历游标及之后(或之前)的元素
            if(arr[j] > arr[j+1]){
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                System.out.println("Sorting: " + Arrays.toString(arr));
            }
        }
    }
}

归并排序

十大排序算法与Java实现

这个的思想就是分治啦,先把数组拆分成一个一个元素的子序列,然后一直在合并两个子序列(双指针法),直到合并回一个完整的数组。

/**
* 归并排序,递归方法
*
* @param array
* @return
*/
public static int[] MergeSort(int[] array) {
    if (array.length < 2) return array;
    int mid = array.length / 2;
    int[] left = Arrays.copyOfRange(array, 0, mid);
    int[] right = Arrays.copyOfRange(array, mid, array.length);
    return merge(MergeSort(left), MergeSort(right));
}
/**
* 归并排序——将两段排序好的数组结合成一个排序数组
*
* @param left
* @param right
* @return
*/
public static int[] merge(int[] left, int[] right) {
    int[] result = new int[left.length + right.length];
    for (int index = 0, i = 0, j = 0; index < result.length; index++) {
        if (i >= left.length)
            result[index] = right[j++];
        else if (j >= right.length)
            result[index] = left[i++];
        else if (left[i] > right[j])
            result[index] = right[j++];
        else
            result[index] = left[i++];
    }
    return result;
}

堆排序

这个之前已经写过啦,见带有优先级的完全二叉树-堆

快速排序

快速排序的思想也是分治,我们从数组中选择一个元素,我们把这个元素称之为中轴元素(基准)吧,然后把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,显然,此时中轴元素所处的位置的是有序的。也就是说,我们无需再移动中轴元素的位置。

从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不包含中轴元素),接着我们通过递归的方式,让中轴元素左边的数组和右边的数组也重复同样的操作,直到数组的大小为1,此时每个元素都处于有序的位置。

十大排序算法与Java实现

public class QuickSort {
    public static int[] quickSort(int[] arr, int left, int right) {
        if (left < right) {
            //获取中轴元素所处的位置
            int mid = partition(arr, left, right);
            //进行分割
            arr = quickSort(arr, left, mid - 1);
            arr = quickSort(arr, mid + 1, right);
        }
        return arr;
    }

    //每次都假设第一个元素是中轴元素,但分成两个序列块之后要把中轴元素交换到中间
    private static int partition(int[] arr, int left, int right) {
        //选取中轴元素
        int pivot = arr[left];
        int i = left + 1;
        int j = right;
        while (true) {
            // 向右找到第一个小于等于 pivot 的元素位置
            while (i <= j && arr[i] <= pivot) i++;
            // 向左找到第一个大于等于 pivot 的元素位置
            while(i <= j && arr[j] >= pivot ) j--;
            if(i >= j)
                break;
            //交换两个元素的位置,使得左边的元素不大于pivot,右边的不小于pivot
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        arr[left] = arr[j];
        // 使中轴元素处于有序的位置
        arr[j] = pivot;
        return j;
    }
}

快速排序的最坏情况还是蛮恐怖的,如果每一次选取的中轴元素都是当前序列最大/最小的元素。

说一下为什么快速排序是原地算法,空间复杂度还是O(logN),是因为这个空间复杂度计算了递归调用栈空间的使用,栈的深度为logN

希尔排序

希尔排序是插入排序的改进算法,它按按一定的间隔为逻辑分组进行插入排序,这个间隔会慢慢缩小。

先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。

希尔排序难得在时间复杂度为O(nlogn)的算法之中还维持着原地排序O(1)的时间复杂度,其思想值得学习。

十大排序算法与Java实现

public class ShellSort {
    public static int[] shellSort(int arr[]) {
        if (arr == null || arr.length < 2) return arr;
        int n = arr.length;
        // 对每组间隔为 h的分组进行排序,刚开始 h = n / 2;
        for (int h = n / 2; h > 0; h /= 2) {
        //对各个局部分组进行插入排序
            for (int i = h; i < n; i++) {
            // 将arr[i] 插入到所在分组的正确位置上
            insertI(arr, h, i);
        }
     }
     return arr;
    }

    /**
     * 将arr[i]插入到所在分组的正确位置上
     * arr[i]] 所在的分组为 ... arr[i-2*h],arr[i-h], arr[i+h] ...
     */
    private static void insertI(int[] arr, int h, int i) {
        int temp = arr[i];
        int k;
        for (k = i - h; k > 0 && temp < arr[k]; k -= h) {
            arr[k + h] = arr[k];
        }
        arr[k + h] = temp;
    }
}

计数排序

计数排序就很像bitmap了(见海量数据算法-BitMap介绍和实现),如果回想bitmap,它的排序效率是极高的,但元素不能重复,如果要解决元素不能重复的问题,就可以将bit(表示是否存在)转为int(表示出现了多少次)。

这种算法的问题就很明显了,首先得知道元素取值的范围,如果范围太大还得用int存储空间复杂度极高。所以它适用于取值范围不大的数组排序。然后元素只能是整数,否则无法和数组的下标关联起来。

桶排序

桶排序也得知道元素的取值范围,它是把所有元素按取值范围划分为若干个区间(桶),桶的个数通常就等于数组的大小,按(array[i] - min) * (bucketNum-1) / d将元素划分到桶中(即按取值范围均分)。然后在每个桶内按归并排序或快速排序算法排序,由于桶之间本来就是有序的,再把它们按桶的顺序连接起来即可。

基数排序

基数排序的排序思路是这样的:先以个位数的大小来对数据进行排序,接着以十位数的大小来多数进行排序,接着以百位数的大小……

排到最后,就是一组有序的元素了。不过,它在以某位数进行排序的时候,是用“桶”来排序的。

由于某位数(个位/十位….,不是一整个数)的大小范围为0-9,所以我们需要10个桶,然后把具有相同数值的数放进同一个桶里,之后再把桶里的数按照0号桶到9号桶的顺序取出来,这样一趟下来,按照某位数的排序就完成了

十大排序算法与Java实现

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e5%8d%81%e5%a4%a7%e6%8e%92%e5%ba%8f%e7%ae%97%e6%b3%95%e4%b8%8ejava%e5%ae%9e%e7%8e%b0/

(0)
彭晨涛彭晨涛管理者
上一篇 2020年3月10日
下一篇 2020年3月11日

相关推荐

  • leetcode84-柱状图中最大的矩形

    原题 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 以上是柱状图的示例,其中每个柱子的宽…

    2020年1月24日
    0100
  • leetcode198-打家劫舍

    原题 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会…

    算法 2020年5月29日
    060
  • leetcode402-移掉K位数字

    原题 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。 注意: num 的长度小于 10002 且 ≥ k。 num 不会包含任何前导零。 …

    算法 2020年1月29日
    090
  • leetcode16-最接近的三数之和

    原题 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存…

    算法 2020年5月4日
    080
  • leetcode695-岛屿的最大面积

    原题 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围…

    算法 2020年3月15日
    01230
  • leetcode26-删除排序数组中的重复项

    原题 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空…

    算法 2019年11月23日
    0190
  • leetcode112-路径总和

    原题 给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。 示例:  给定如下二叉树,…

    算法 2020年1月12日
    0150
  • leetcode215-数组中的第K个最大元素

    原题 在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 示例1: 输入: [3,2,1,5,6,4] 和…

    算法 2020年2月10日
    0140
  • leetcode443-压缩字符串

    原题 给定一组字符,使用原地算法将其压缩。 压缩后的长度必须始终小于或等于原数组长度。 数组的每个元素应该是长度为1 的字符(不是 int 整数类型)。 在完成原地修改输入数组后,…

    算法 2020年5月28日
    070
  • 剑指offer44-数字序列中某一位的数字

    原题(来源Leetcode) 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位…

    算法 2020年6月15日
    02900

发表回复

登录后才能评论