算法学习 《算法》2.1 直接插入排序的三种实现方式

工作原理:

每次指定一个待排序的元素,插入到前面已经排序号的序列中去,直到插完所有元素

所以,每次只需要比较指定元素和其左侧的元素大小,交换一定次数即可达到左侧的有序

本文提供三种实现方式:简单实现减少交换次数的优化加上哨兵位后的算法。并且在最后面测试这三中算法的效率。

 


简单版代码实现:

 

public class SortMain {

    public static void main(String[] args) {

        Integer[] testArray={1,4,5,2,3,6,3};
        selectedSort(testArray);
        for (int i:testArray){
            System.out.print(i+" ");
        }
        System.out.println(isSorted(testArray));
    }

    public static boolean isSorted(Comparable[] a){
        //测试是否有序,注意防止数组下标越界
      for (int i=1;i0&&less(a[j],a[j-1]);j--){
                exch(a,j,j-1);
            }
        }
    }
}

算法分析:

直接插入排序对于实际应用中常见的某些类型的非随机数组很有效,适合部份有序的小规模数组


算法优化:

前面的算法,需要在每次比较大小后交换位置。我们可以优化为在每次比较后,较大元素右移,在内循环退出后把a[i]赋值到他应该去的位置,而不总是交换两个元素,可以使访问数组的次数减半

 

减少交换次数优化后代码:

 

public static void insertSortYouHua(Comparable[] a){
    //将a升序排列
    int N=a.length;
    for (int i=1;i0&&less(temp,a[j]);j--){
            a[j+1]=a[j];
        }
        a[j+1]=temp;
    }
}

算法中加入哨兵位:

看书才知道原来有个东西叫哨兵位(原谅我是个菜鸡),是一个最大或者最小值,一般处于开头或者末尾,来避免数组下标越界的问题。有哨兵的算法和上面的算法的最大区别就在于for循环中条件的判断,测试循环条件会减少一半的时间。但是需要首先遍历一遍数组来找到哨兵(最大或者最小的值)

public static void insertSortShaoBing(Comparable[] a) {
       /*
    使用哨兵位的直接插入排序
     */
    //将a升序排列
    int N = a.length;
    //为了避免数组本来就排列好,先进行判断
    if (!isSorted(a)) {
        //从后往前遍历,找出最小的值,放到a[0]当哨兵位
        for (int i = N - 1; i > 0; i--) {
            if (less(a[i], a[i - 1])) {
                exch(a, i, i - 1);
            }
        }
    }
    for (int i = 2; i < N; i++) {
        //内循环中直接从a[1]和a[2]的比较开始
        //如果前面的值比后面的值小,就把前面的值复制到后面的位置上去
        Comparable temp = a[i];//保存第i位的值
        int j;
        //设置变量记录第i位前一位,把a[i]与前面的进行比较
        //若比a[i]大,则后移
        //退出循环时,把a[i]的值赋值给空的位置(退出循环时又"-"了一个)
        for (j = i - 1; less(temp, a[j]); j--) {
            a[j + 1] = a[j];
        }
        a[j + 1] = temp;
    }
}

最后进行算法的比较(三个算法处理T个长度为N的数组):

1.T=100,N=1000

 

public class SortCompare {
    public static long time(String alg, Double[] a){
        long startTime = System.currentTimeMillis();    //获取开始时间
        if (alg.equals("Insertion")) SortMain.insertSort(a);
        if (alg.equals("Insertion2")) SortMain.insertSortYouHua(a);
        if (alg.equals("Insertion3")) SortMain.insertSortShaoBing(a);
        long endTime = System.currentTimeMillis();    //获取结束时间
        return endTime-startTime;
    }

    public static long timeRandomInput(String alg,int N,int T){
        //使用算法alg将T个长度为N的数组排序
        long total=0;
        Double[] a=new Double[N];
        Random random=new Random();
        for (int t=0;t

运行多次不同结果:


2.T=1000,N=1000

运行多次不同结果:


3.T=10000,N=1000

运行多次不同结果:

 

 

总结:

简易版的直接插入算法虽然好写,但是在处理大数据时效率很低。

减少交换次数的优化对速度有很明显的提升。

加上哨兵后,速度没有变快,可能是因为要先遍历一遍数组找出最小值,这也是个大工程。以后要是要提升速度可以不写哨兵,自己注意数组下标(也可能是我哨兵用错了,欢迎指正!!!)。

我的电脑是真的有点慢........

 

最后给个github上大佬自己写的算法书课后习题的链接:

https://github.com/jimmysuncpt/Algorithms/blob/master/src/com/jimmysun/algorithms/chapter2_1/Ex24.java

 

 

 

 

 

你可能感兴趣的:(算法学习)