经典排序算法(希尔、归并、快排)---基于Java实现

1.希尔排序

1.1 排序思想

     通俗来讲,希尔排序就是一种分组的插入排序,通过一个偏移量h将待排序序列分组进行插入排序,偏移量逐渐减小直至为1,当h为1时,排序已经完成。

     插入排序就是偏移量从头到尾都为1的希尔排序。

1.2 代码实现

      希尔排序的时间复杂度主要与h的取值有关,我自己这边主要是将h=2*h+1(h>length/2)

Step1:先确定h的值

Step2:进行while循环,循环结束的条件是h=1

Step2.1:找到排序元素将其插入前面排序好的序列中(主要是序列的index值注意体会)

Step2.2 h/2

public class Shell {

    public static void sort(Comparable[] a) {
        //1.确定h的值
        int length = a.length;
        int h = 1;
        while (h < length / 2) {
            h = 2 * h + 1;
        }
        /***
         * 其实这里的逻辑与插入排序一致
         * 都是先找到排序的元素,然后将该元素插入到前面排序好的序列中
         * 不一样的点:
         * 插入排序的h是固定为1;
         * 但是希尔排序的h会慢慢减少到1
         */
        //2.进行排序
        while (h >= 1) {
            //2.1找到排序的元素
            for (int i = h; i < length; i++) {
                //将当前的元素插入前面排序好的序列中
                for(int j=i;j>=h;j-=h){
                    if(greater(a[j-h],a[j])){
                        exchange(a,j-h,j);
                    }else{
                        break;
                    }
                }
            }
            //3.将h的值减少
            h=h/2;
        }
    }

    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }

    /***
     * 比较大小
     * @param comparable
     * @param comparable1
     * @return
     */
    private static boolean greater(Comparable comparable, Comparable comparable1) {
        //大于0表示comparable>comparable1
        return comparable.compareTo(comparable1)>0;
    }
}

1.3 时间复杂度分析

   希尔排序的时间复杂度主要是与h的取值有关。

2.快速排序

2.1 排序思想

   个人觉得快速排序是一个有哨兵的排序,这个哨兵就是待排序的第一个元素,通过一定的操作(从后往前找比哨兵小的进行交换,从前往后找比哨兵大的进行交换)将数列分为比哨兵小的部分和比哨兵大的部分,对两部分进行分而治之,这两部分的操作和之前的一模一样。

     典型的一种分治算法。

2.2 代码实现

通过两个指针left、right完成分组的操作 

public class Quick {
    /***
     * 总方法
     * 进行排序
     * @param a
     */
    public static void sort(Comparable[] a){
        int low=0;
        int high=a.length-1;
        //调用分的方法
        sort1(a,low,high);

    }

    /***
     * 在low和high区间内进行排序
     * @param a
     * @param low
     * @param high
     */
    public static void sort1(Comparable[] a,int low,int high){
        //先做安全性校验
        if(low>high){
            return ;
        }
        //分成两个数组进行排序
        int partition=partition(a,low,high);  //返回的那个哨兵的索引值
        sort1(a,low,partition-1);
        sort1(a,partition+1,high);
    }

    private static int partition(Comparable[] a,int low,int high){
        //确定分界值
        Comparable key=a[low];
        //定义两个指针
        int left=low;
        int right=high+1;
        //切分
        while(true){
            //从右向左移动right找到比key小的值
            while(less(key,a[--right])){
                if(right==low){
                    break;
                }
            }
            //从左向右移动left找到比key大的值
            while(less(a[++left],key)){
                if(left==high){
                    break;
                }
            }
            //判断left>=right,如果是就结束扫描,如果不是就交换上面两个的值
            if(left>=right){
                break;
            }else{
                exchange(a,left,right);
            }
        }
        //交换分界值将第一个和right的值进行交换
        exchange(a,low,right);
       //返回right的值
        return right;
    }

    /***
     * 交换
     * @param a
     * @param i
     * @param j
     */
    private static void exchange(Comparable[] a, int i, int j) {
        Comparable temp;
        temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }

    /***
     * 比较大小
     * @param comparable
     * @param comparable1
     * @return
     */
    private static boolean less(Comparable comparable, Comparable comparable1) {
        //小于0表示comparable<comparable1
        return comparable.compareTo(comparable1)<0;
    }
}

 2.3 时间复杂度分析

最好的情况:O(nlogn)  每次分组都很平均,可以形成一棵树

最坏的情况:O(n^2)  逆序,每次分组都得扫描全部

3 归并排序

3.1 排序思想

典型的分治算法,进行分组排序然后合并,每次都平均分组进行排序,再将排序好的数列进行合并,需要一个辅助数组进行辅助典型的用空间换时间的算法

3.2 代码实现

主要的代码实现是合并的部分merge()方法,主要是通过三个指针和一个辅助数组,这部分一定要手动模拟合并的过程。

public class Merge {
    //定义一个辅助数组
    private static Comparable[] assist;

    /***
     * 总方法
     * 进行排序
     * @param a
     */
    public static void sort(Comparable[] a){
        //初始化辅助数组
        assist=new Comparable[a.length];
        int low=0;
        int high=a.length-1;
        //调用分的方法
        sort1(a,low,high);
    }

    /***
     * 在low和high区间内进行排序
     * @param a
     * @param low
     * @param high
     */
    public static void sort1(Comparable[] a,int low,int high){
        //先做安全性校验
        if(low>high){
            return ;
        }
        //分成两个数组进行排序
        int mid=low+(high-low)/2;
        sort1(a,low,mid);
        sort1(a,mid+1,high);
        //合并两个数组
        merge(a,low,mid,high);
    }

    /***
     * 将[low,mid]和[mid,high+1]
     * 两个数组进行排序合并
     * 主要是利用指针进行合并
     * @param a
     * @param low
     * @param mid
     * @param high
     */
    public static void merge(Comparable[] a,int low,int mid,int high){
        //定义三个指针
        int i=low;
        int p1=low;
        int p2=mid+1;
        //进行遍历比较指针大小
        while(p1<=mid && p2<=high){
            /***
             * 如果p1的值比p2小
             * 那就把p1的值放在辅助数组中
             * 反之
             * 把p2的值放在辅助数组中
             */
            if(less(a[p1],a[p2])){
                assist[i++]=a[p1++];
            }else{
                assist[i++]=a[p2++];
            }
        }
        //检查数组的值是否遍历完全
        while(p1<=mid){
            assist[i++]=a[p1++];
        }
        while(p2<=high){
            assist[i++]=a[p2++];
        }
        //将辅助数组的值拷贝到原数组中
        for(int index=low;index<=high;index++){
            a[index]=assist[index];
        }
    }


    /***
     * 比较大小
     * @param comparable
     * @param comparable1
     * @return
     */
    private static boolean less(Comparable comparable, Comparable comparable1) {
        //小于0表示comparable<comparable1
        return comparable.compareTo(comparable1)<0;
    }
}

3.3 时间复杂度分析

这里建议用一个8个数列的排序手动排序一下;

首先看拆分了多少次:其实拆分过程就是一棵树,树的高度就是拆分了多少次:log2n

第k层有2^k个数组,每个数组的长度是2^(log2n-k),那每个数组需要比较多少次就是数组长度,那每一层需要比较的次数为:2^(log2n)

那k层共比较:log2n*2^(log2n)=nlog2n

时间复杂度为O(nlogn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值