Java程序员从笨鸟到菜鸟(二)五种基本排序算法实现

注:数组排序以从升序排序为例

1.冒泡排序

1.1 算法原理:从左向右比较相邻的元素,如果前面的数比后面的数大,则交换它们的位置,这样每次比较完,靠后的位置的数字都是比前面的数值更大,循环比较到倒数第二位。详细解释见冒泡排序

1.2 算法分析:

时间复杂度:若数组开始为正序,则比较次数Cmin= n-1,移动次数Mmin=0,最好的时间复杂度为O(n);若数组初始为反序的,则需要进行n-1趟排序,最坏的时间复杂度为O(n2),综合一下冒泡排序的时间复杂度为O(n2)。

算法的稳定性:冒泡排序的算法是比较两个相邻的元素,将较大值往后移,较小值往前移,移动只是发生在相邻元素之间,即使是两个相等的元素,也不会改变前后顺序,冒泡排序是一种稳定的排序算法

1.3 算法实现:

 /**
     * 冒泡排序
     * 实现方式:使用for循环遍历数组,将前后两个数组元素依次遍历比较,将较大值往后靠
     * @param arr 传入一个int类型的数组
     * */
    private static void bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println("冒泡排序后的数组为:");
        printArr(arr);
    }

 

2.快速排序

快速排序是对冒泡排序的一种改进

2.1 算法原理:假设数组下标第一个为i,最后一个为j选取数组最左边的元素为基准值,先从j开始向左遍历,逐个查找比基准值小的数值然后再与基准值交换位置,再从i开始向右遍历,找到第一个大于基准值的元素,然后与基准值交换位置,如此反复查找,当j<=i的时候,该趟查找结束,剩下的也这样递归查找,直到数组为升序。详细解释见快速排序

2.2 算法分析:

时间复杂度:最坏情况的时间复杂度和插入排序是一样的为O(n2),最好的情况的时间复杂度是O( nlogn),综上快速排序算法的平均时间复杂度为O( nlogn)。

算法稳定性:是种不稳定的排序方式

2.3 算法实现:

/**
     * 快速排序
     * 1.取中值:先定义数组两边的下标分别标为i,j,取最左边的值为基准值,然后从j开始向右遍历,找到第一个小于基准值的数值
     * 再与基准值交换位置,然后再从i开始向右遍历,寻找到第一个大于基准值的数值,交换位置,直到i >= j,第一次遍历结束,
     * 之后重复步骤
     * @param low 传入int类型的值
     * @param high 传入int类型的值
     * @param arr 传入一个int类型的数组
     * @return low 返回一个int类型的值
     * */
    private static int getMid(int low, int high, int[] arr) {
        int temp = arr[low];
        while (low < high) {
            if (arr[high] >= temp) {
                high--;
            }
            int temp1 = arr[low];
            arr[low] = arr[high];
            arr[high] = temp1;

            if (low < high && arr[low] <= temp) {
                low++;
            }
            int temp2 = arr[high];
            arr[high] = arr[low];
            arr[low] = temp2;
        }
        return low;
    }
    /**
     * 2.快速排序
     * 实现方式:将每次遍历得到的中值前后两个数组进行快速排序
     * @param low 传入一个int类型的值
     * @param high 传入一个int类型的值
     * @param arr 传入一个int类型的值
     * */
    private static void quickSort(int low, int high, int[] arr) {
        int mid = getMid(low, high, arr);
        if (low < high) {
            quickSort(low, mid - 1, arr);
            if (mid != arr.length - 1) {
                quickSort(mid + 1, high, arr);
            }
        }
    }

注:在取基准值进行排序时,对基准值右边即给后半部分进行排序的时候,需要加入基准值是否为数组最后一个元素,如果不加判断条件,在基准值进行+1操作之后,会出现越界错误,这是一个测试用例,可以用这例子对其它快速排序算法进行测试一下:

int[] arr = {12,45,1,45,89,78,19};

 

3.插入排序

包括直接插入排序、,二分插入排序(又称折半插入排序),链表插入排序,希尔排序(又称缩小增量排序),这里主要使用的是直接插入排序

 

3.1 算法原理:将一个数值插入到已经排好顺序的序列中,使插入后的数组仍然是个有序数组。详细解释见插入排序

 

3.2 算法分析:

时间复杂度:O(n2)

算法稳定性:是稳定的排序方法

3.3 算法实现

/**
     * 插入排序
     * 实现方式:使用for循环遍历,将数组依次划分成小区间,将小区间里面的元素进行排序
     * @param arr 传入一个int类型的数组
     * */
    private static void insertSort(int[] arr) {
        int currentElem = 0, j = 0;
        for (int i = 1; i < arr.length; i++) {
            currentElem = arr[i];
            for (j = i - 1; j >= 0 && arr[j] > currentElem; j--) {
                arr[j + 1] = arr[j];
            }
            arr[ j + 1] = currentElem;
        }
        System.out.println("插入排序后的数组为:");
        printArr(arr);
    }

4.选择排序

4.1 算法原理:每一次从数组中选取最小值,存放在数组的起始位置。详细解释见选择排序

4.2 算法分析:

时间复杂度:最好的时间复杂度是O(n2),最坏的时间复杂度是O(n2),平均时间复杂度是O(n2)

算法稳定性:是种不稳定的排序方式。

4.3 算法实现:

/**
     * 选择排序
     * 实现方式:使用for循环遍历,将数组元素依次比较,将较小值的小标标为min,每次遍历排序找出最小的值
     * @param arr 传入一个int类型的数组
     * */
    private static void selectSort(int[] arr) {
        int min = 0;
        for (int i = 0; i < arr.length; i++) {
            min = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[min]) {
                    min = j;
                }
            }
            if (min != i) {
                int temp = arr[min];
                arr[min] = arr[i];
                arr[i] = temp;
            }
        }
        System.out.println("选择排序后的数组为:");
        printArr(arr);
    }

5、计数排序算法

5.1 算法原理:计数排序是一种非常快捷的稳定性强的排序方法,非基于比较的排序算法,时间复杂度O(n+k),其中n为要排序的数的个数,k为要排序的数的组大值。 计数排序对一定量的整数排序时候的速度非常快,一般快于其他排序算法。但计数排序局限性比较大,只限于对整数进行排序。 计数排序是消耗空间发杂度来获取快捷的排序方法,其空间发展度为O(K)同理K为要排序的最大值

 

5.2 算法分析:

时间复杂度:O(n + k) k 为数组中最大值-最小值

算法稳定性:稳定

 

5.3 算法实现:

/**
     * 计数排序:
     * 实现方式:
     * 1、对输入的数字进行频率统计
     * 2、将频率转换为开始索引
     * 准备三个数组:
     * 1、要排序的数列大小
     * 2、要记录比某个数小的元素的个数,大小为 K, 构造一个 k + 1 大小的数组
     * */
    private static void countSort(int[] arr) {
       int max = arr[0], min = arr[0];
        for (int i: arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }
        // 最大最小元素之间范围【min, max】的长度
        int r = max - min + 1;
        // 1、计算频率
        int[] count = new int[r + 1]; // 数组每一个下标位置的值,代表了对应整数出现的次数
        for (int i: arr) {
            // 使用加 1 后的索引,有重复值就自增
            count[i - min + 1]++;
        }
        // 2、频率 -> 元素的开始索引,后面的元素等于前面的元素之和
        for (int i = 0; i < count.length; i++) {
            count[i + 1] += count[i];
        }
        // 3、元素按照开始索引分类,用到一个和待排序数组一样大临时数组存放数据
        int[] aux = new int[arr.length];
        for (int i: arr) {
            // 填充一个数据后,自增,以便相同的数据可以填到下一个空位
            aux[count[i - min]++] = i;
        }

        System.out.println("计数排序后的数组为:");
        printArr(aux);
    }
局限:
1、数列最大值和最小值差距过大
2、数列元素不是整数

6、桶排序

6.1、算法原理:

1、创建桶 , 桶个数:等于原始数列的元素个数,除了最后一个桶只包含最大值,其他桶的区间按比例确定 区间跨度:(最大值 - 最小值)/(桶的数量 - 1)

2、遍历原始数组,将元素对号入座放入各个桶中

3、每个桶内部的元素分别排序

4、遍历所有的桶,输出所有元素

 

6.2 算法分析:

时间复杂度:假设有 n 个元素,分成 m 个桶(分桶 m = n)平均每个桶的元素个数为 n / m

O(n + m + n(logn-logm))

空间复杂度:O(m + n)

 

6.3、代码实现

/**
     * 桶排序
     * @param arr 数组
     * */
    public static double[] bucketSort(double[] arr) {
        // 1、取最大值和最小值,并输出区间 d
        double max = arr[0];
        double min = arr[0];
        for (double i: arr) {
            if (i > max) {
                max = i;
            }
            if (i < min) {
                min = i;
            }
        }
        double d = max - min;

        // 2、初始化桶,每个桶里面有一个 LinkedList 链表
        int bucketNum = arr.length;
        ArrayList<LinkedList<Double>> bucketList = new ArrayList<>(bucketNum);
        for (int i = 0; i < bucketNum; i++) {
            bucketList.add(new LinkedList<Double>());
        }

        // 3、遍历原始数组,将每个元素放入桶中
        for (int i = 0; i < arr.length; i++) {
            int num = (int)((arr[i] - min) * (bucketNum - 1) / d);
            bucketList.get(num).add(arr[i]);
        }

        // 4、对每个桶内进行排序
        for (int i = 0; i < bucketList.size(); i++) {
            Collections.sort(bucketList.get(i));
        }

        // 5、输出全部元素
        double[] sortedArr = new double[arr.length];
        int index = 0;
        for (LinkedList<Double> list: bucketList) {
            for (double element: list) {
                sortedArr[index] = element;
                index++;
            }
        }
        return sortedArr;
    }

版权声明:欢迎转载, 转载请保留原文链接。https://mp.csdn.net/postedit/79351742

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值