《算法》阅读笔记-2.1初级排序算法

本文介绍了几种初级排序算法,包括选择排序、插入排序及其改进版本希尔排序。详细解释了每种算法的工作原理、核心代码实现及时间复杂度分析,并对比了它们的适用场景。

第二章 排序

2.1初级排序算法

2.1.1 辅助函数

public static voidexch(Comparable[] a, inti, intj){
    //用于交换a[i]和a[j]
    Comparable t = a[i];
    a[i] = a[j];
    a[j] = t;
}

public static boolean less(Comparable v, Comparable w){
    //如果v<w返回true,v>w返回false
    return v.compareTo(w) <0;
}

2.1.2 选择排序

算法描述:不断在剩余元素中选择最小的

1.    找到数组中最小的那个元素,将它和数组的第一个数交换(如果第一个元素就是最小的元素,就和自己交换)

2.    在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置

3.    如此往复,直到将整个数组排序

核心代码

public classSelection {
    public static void sort(Comparable[] a){
        //将数组a升序排序
        int N = a.length;
        for(inti = 0; i < N; i++){
            int min =i;
            for(intj = i; j < N; j++){
                if(less(a[j],a[min])){
                    min = j;
                }
                exch(a, i, min);
            }
        }
    }
}

时间复杂度:

对于长度为N的数组,需要大约N(N-1)/2次比较和N次交换,时间复杂度是N^2

2.1.3 插入排序

 算法描述:当前元素的左边元素都是有序的,但最终位置还不确定;将当前元素它前面的元素比较,插入到合适的位置。

     

 核心代码:

public classInsertion {
        public static void sort(Comparable[] a){
            //将数组a升序排序
            int N = a.length;
            for(inti = 0; i < N; i++){
                //将a[j]与它左边的a[j-1]a[j-2]a[j-2]...比较,插入到合适的位置
                for(intj = i; j > 0&&less(a[j],a[j-1]); j--){
                    exch(a, j, j-1);
                }
            }
        }
   }

时间复杂度:

    最坏的情况需要N(N-1)/2次比较,N(N-1)/2次交换;最好情况下需要N-1次比较,0次交换;平均情况下需要N(N-1)/4次比较,N(N-1)/4次交换

插入排序适合的情况:

    数组部分有序:逆序的数量 小于数组大小的某个倍数

    典型的部分有序数组:

    1. 数组中每个元素距离它的最终位置都不远;

    2. 一个有序的大数组接一个小数组

    3. 数组中只有几个元素的位置不正确

    插入排序适合部分有序数组(当逆序数量很少),小规模数组时

关于插入排序的命题:

    插入排序需要的交换操作 = 数组中逆序的数量

    逆序的数量 <= 需要的比较次数 <= 逆序的数量 + 数组的大小 – 1

(每次交换对应这一次比较,在i没有到达最左端时,每个i需要一次额外的比较)

2.1.4 选择排序 vs 插入排序

 算法比较:

      选择排序只访问右侧元素;插入排序只访问左侧元素

哪个更快:

      对于随机排序的无重复主键的数组,插入排序和选择排序的运行时间是平方级的,两者之比应该是一个较小的常数

      这个小常量没有准确描述,是因为不同的情况下结果不同

实验验证

 

2.1.5 希尔排序

       插入排序,在大规模乱序数组的情况下,效率很差,因为它只能交换相邻的元素

算法描述:

      h有序数组:数组中任意间隔为h的元素都是有序的,如下图所示:

      h从一个很大的数开始递减至1,比较和交换都以h为间隔(当h很大时可以将元素移动到很远的地方,未更小的h有序创造条件,当h=1时可以实现整个数组排序)

 核心代码:

public classShell {
    public static void sort(Comparable[] a) {
        //将数组a升序排序
        int N = a.length;
        int h =1;
        while (h< N /3) {
            h = 3 * h +1;
        }//1,4,13,40,121......
        while (h>=1){
            for (inti = h; i < N; i++) {
                //将a[i]插入到a[j-h],a[j-2h],a[j-3h]...
                for (intj = i; j >= h && less(a[j],a[j - h]); j -= h) {
                    exch(a, j, j -h);
                }
                h = h / 3;
            }
        }
    }
}

 

 时间复杂度:

      希尔排序权衡了子数组的规模和有序性;

      排序之初各个子数组都很短,排序之后子数组是部分有序的;这两种情况都是插入排序擅长的情况

算法比较:

      实验证明,希尔排序比插入和选择排序快得多,数组越大,优势越大

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值