四种常用排序方式(Java实现)

插入排序

排序原理:
1.把所有的元素分为两组,已经排序的和未排序的;
2.找到未排序的组中的第一个元素,向已经排序的组中进行插入;
3.倒叙遍历已经排序的元素,依次和待插入的元素进行比较,直到找到一个元素小于等于待插入元素,那么就把待插入元素放到这个位置,其他的元素向后移动一位;
原理如图:
在这里插入图片描述
第一次操作: 指针指向第二个元素, 将其与前一个元素进行比较
如果满足条件, 则进行交换, 同时指针前移一位, 重复上述步骤, 直到指针到达数组的
否则说明已经成有序列, 退出当前循环;
第二次操作: 指针指向第三个元素, 将其与前一个元素进行比较…
在这里插入图片描述

/**
 * @author 悠一木碧
 * 插入排序类
 */
public class Insert {
    /**
     * 升序排序
     * @param a
     */
    public static void sort(Comparable[] a) {
        if (null == a) {
            throw new NullPointerException("空指针数组!");
        }
        int len = a.length;
//        1. 从第二个元素开始进行排序
//        2. 用temp记录当前索引位置, 索引必须 > 0(当索引为0的时候不需要再往前比较了)
//        3.如果满足条件, 则进行交换, 同时将索引前移一位----temp--        
        for (int i = 1; i < a.length; i++) {
            int temp = i;
            while (temp > 0 && bigger(a[temp - 1], a[temp])) {
                exchange(a, temp - 1, temp);
                temp--;
            }
        }
    }

    /**
     * 判断a 是否大于 b
     * @param a
     * @param b
     * @return
     */
    private static boolean bigger(Comparable a, Comparable b) {
        return a.compareTo(b) > 0;
    }

    /**
     * 交换索引index1, index2位置的元素
     * @param a
     * @param index1
     * @param index2
     */
    private static void exchange(Comparable[] a, int index1, int index2) {
        Comparable comparable = a[index1];
        a[index1] = a[index2];
        a[index2] = comparable;
    }
}

按照大O推导法则,保留函数中的最高阶项那么最终插入排序的时间复杂度为O(N^2).

希尔排序

希尔排序是插入排序的一种,又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
之前的插入排序中, 我们一次次比较, 一次次进行元素交换
比如要将图片中的1放到3所在的位置, 我们需要进行两次比较, 两次交换
在这里插入图片描述

而希尔排序就是利用这一点进行优化, 直接进行比较并交换

排序原理:
1.选定一个增长量increment,按照增长量increment作为数据分组的依据,对数据进行分组;
2.对分好组的每一组数据完成插入排序;
3.减小增长量,最小减为1,重复第二步操作。
关于增长量increment的确定:
我们使用

        int len = a.length;
        int increment = 1;
        while (increment < len / 2) {
            increment = increment * 2 + 1;
        }

进行确定, 这样使得increment都是奇数, 且increment > len / 2, 即增长量的初始值在数组长度的一半以上

确定好了增长量increment的初始值后, 再一个需要确定的就是increment的变化方式了
增长量变化趋势: 大 ----> 小----------最终increment = 1 即可
写成代码可以是这样:

//      确保increment >= 1
        while (increment > 0) {
        	...
        	...
        	...
        	increment = increment / 2;
        }

现在进行深入分析希尔排序和插入排序的相同点, 不同点
在这里插入图片描述
在这里插入图片描述
代码实现:

/**
 * @author 悠一木碧
 * 希尔排序类
 */
public class Shell {
    /**
     * 对数组内元素进行排序(升序)
     * @param a
     */
    public static void sort(Comparable[] a) {
        if(null == a){
            throw new NullPointerException("空指针数组!");
        }
//        1.通过数组的长度确定增长量initialIncrement的初始值, 初始化值为1, 通过循环条件(<len / 2)进行确定
//        2.确定初始值后, 确定循环条件, 由于increment需要逐渐减小, 变换方法为increment /= 2
//        3.初始下标为0 + increment, 1+increment, 2+increment.....区间[increment, len-1]内
//        4.数与数之间的间隔为increment, 从当前的数开始, 与前一个数进行比较(假设前面的数成有序列)
//        5.如果不符合期待的大小关系, 则进行交换, 否则退出循环(说明已经成有序列了)
        int len = a.length;
        int increment = 1;
        while (increment < len / 2) {
            increment = increment * 2 + 1;
        }
        while (increment > 0) {
            for (int i = increment; i < len; i++) {
                int temp = i;
                while(temp >= increment && greater(a[temp - increment], a[temp])){
                    exchange(a, temp - increment, temp);
                    temp -= increment;
                }

            }
            increment /= 2;
        }
    }

    /**
     * 判断x是否大于y
     *
     * @param x
     * @param y
     * @return
     */
    private static boolean greater(Comparable x, Comparable y) {
        return x.compareTo(y) > 0;
    }

    /**
     * 交换a数组中, i,j索引处的值
     *
     * @param a
     * @param i
     * @param j
     */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;

    }


}

注:
在希尔排序中,增长量h并没有固定的规则,有很多论文研究了各种不同的递增序列,但都无法证明某个序列是最好的

归并排序(和快速排序一样, 应用了分组的思想, 采取递归的策略)

待补充:
代码实现:

/**
 * @author 悠一木碧
 * 归并排序
 */
public class Merge {
//     辅助数组
    private static Comparable[] assitArray;
    /**
     * 对数组内的元素进行排序
     * @param array
     */
    public static void sort(Comparable[] array){
        if(null == array){
            throw new NullPointerException("空指针数组!");
        }
        int len = array.length;
        assitArray = new Comparable[len];
        sort(array, 0, len-1);
    }

    /**
     * 对区间[staIndex, endIndex]中的数组元素进行排序
     * 递归至 staIndex == endIndex停止
     * 在staIndex = endIndex - 1的时候开始调用merge进行归并
     * @param array
     * @param staIndex
     * @param endIndex
     */
    private static void sort(Comparable[] array, int staIndex, int endIndex){
        if(staIndex < endIndex){
            int mid = (staIndex + endIndex) / 2;
            sort(array, staIndex, mid);
            sort(array, mid + 1, endIndex);
            merge(array, staIndex, mid, endIndex);
        }
    }

    /**
     * [left, mid], [mid + 1, right]两个子组进行有序组合
     * 1. 定义辅助数组的初始指针, 定义原数组的两个区间的初始指针
     * 2. 依次移动两个区间的指针, 比较他们指向元素的大小, 按大小放入至辅助数组中
     * 3. 将排好序的辅助数组中的元素复制到原数组中
     * 两个区间的指针必定会有一个提前走完, 剩下没走完的指针, 将其剩余的元素放到辅助数组中即可
     * @param left
     * @param mid
     * @param right
     */
    private static void merge(Comparable[] array, int left, int mid, int right){
        int i = left;
        int index1 = left;
        int index2 = mid + 1;
        while(index1 <= mid && right >= index2){
            if(bigger(array[index1], array[index2])){
                assitArray[i++] = array[index2++];
            } else{
                assitArray[i++] = array[index1++];
            }
        }
        while(index1 <= mid){
            assitArray[i++] = array[index1++];
        }
        while(index2 <= right){
            assitArray[i++] = array[index2++];
        }
        if (right - left + 1 >= 0)
            System.arraycopy(assitArray, left, array, left, right + 1 - left);
    }

    /**
     * 判断a > b
     * @param a
     * @param b
     * @return
     */
    private static boolean bigger(Comparable a, Comparable b){
        return a.compareTo(b) > 0;
    }

    /**
     * 元素a[index1] 和 元素a[index2]进行交换
     * @param a
     * @param index1
     * @param index2
     */
    private static void exchange(Comparable[] a, int index1, int index2){
        Comparable comparable = a[index1];
        a[index1] = a[index2];
        a[index2] = comparable;
    }
}

快速排序(递归, 双指针)

待补充:
代码实现:

/**
 * @author 悠一木碧
 * 快速排序
 */
public class Quick {
    public static void sort(Comparable[] a) {
        if(null == a){
            throw new NullPointerException("传入数组参数为空!");
        }
        int len = a.length;
        if(0 != len){
            sort(a, 0, len - 1);
        }
    }

    /**
     * 对区间[staIndex, endIndex]中的数组元素进行排序
     * 将其拆分为[lowerLimit, demarcationIndex-1], [demarcationIndex+1, upperLimit]两个区间排序
     * 递归至 staIndex == endIndex停止--------分成只有一个元素的时候停止
     * @param array
     * @param lowerLimit
     * @param upperLimit
     */
    private static void sort(Comparable[] array, int lowerLimit,  int upperLimit){
        if(lowerLimit >= upperLimit){
            return;
        }
        int demarcationIndex = grouping(array, lowerLimit, upperLimit);
        sort(array, lowerLimit, demarcationIndex - 1);
        sort(array, demarcationIndex + 1, upperLimit);
    }

    /**
     * 对索引在区间[lowerLimit, upperLimit]内的数组元素进行切分, 并返回切分后分界元素的索引
     * 这里是将左边第一个元素作为分界元素, 所以先让右边的指针先动
     * @param array
     * @param lowerLimit
     * @param upperLimit
     * @return 返回的是变换后的索引
     */
    private static int grouping(Comparable[] array, int lowerLimit, int upperLimit){
//      1. 定义两个指针, 分别指向待切区间的最小索引 和 最大索引的下一个位置
        int left = lowerLimit;
        int right = upperLimit + 1;
        while (true) {
//          2. 先移动右指针, 自右向左扫描, 直到找到比分界元素小的元素停止
//          3. 然后移动左指针, 自坐向右扫描, 直到找到比分界元素大的元素停止

            while(less(array[lowerLimit], array[--right])){
                if(left == right){
                    break;
                }
            }
//          5. 两个指针重合的时候, 交换分界元素和指针当前指向的元素并返回索引
            if(left == right){
                exchange(array, lowerLimit, right);
                return right;
            }
            while(less(array[++left], array[lowerLimit])){
                if(left == right){
                    break;
                }
            }
//          4. 交换左右指针指向的元素
            if(left == right){
                exchange(array, lowerLimit, right);
                return right;
            } else{
                exchange(array, left, right);
            }

        }
    }
    /**
     * 判断x是否小于y
     * @param x
     * @param y
     * @return
     */
    private static boolean less(Comparable x, Comparable y) {
        return x.compareTo(y) < 0;
    }

    /**
     * 交换a数组中, i,j索引处的值
     * @param a
     * @param i
     * @param j
     */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp = a[i];
        a[i] = a[j];
        a[j] = temp;

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值