初级排序算法

此博客用于个人学习,来源于算法的书籍,对知识点进行一个整理。

1. 规则:

我们关注的主要对象是重新排列数组元素的算法,其中每一个元素都有一个主键。排序算法的目标就是将所有元素的主键按照某种方式排序(通常是按照大小或者是字母顺序)。我们这里默认索引较大的主键大于等于索引较小的主键。在 Java 中,元素通常是对象,对主键的抽象描述则是通过一种内置的机制(Comparable 接口)来完成的。

2. 排序算法的模板:

我们会将排序代码放在类的 sort 方法中,该类还包含辅助函数 less 和 exchange ,还有一些其他辅助函数。大多数情况下,我们的排序代码只会通过两个方法操作数据:less 方法对元素进行比较,exchange 方法将元素调换位置,首先我们给出模板:

/**
 * 基本框架,以后的排序函数是以此来修改sort函数,其他函数逻辑相同
 */
public class baseSort {

    public static void sort(Comparable[] a){

    }

    private static boolean less(Comparable v,Comparable w){
        return v.compareTo(w) < 0;
    }

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

    private static void show(Comparable[] a){
        //在单行中打印数组
        for (int i = 0;i < a.length;i++){
            System.out.println(a[i]+" ");
        }
        System.out.println();
    }

    /**
     * 测试数组元素是否有序
     * @param a
     * @return
     */
    public static boolean isSorted(Comparable[] a){
        for (int i = 1;i < a.length;i++){
            if (less(a[i],a[i-1])){
                return false;
            }
        }
        return true;
    }
}

首先观察 less 函数,对于 v < w,v = w,v > w 三种情况,Java 的习惯是在 v.compareTo(w) 被调用的时候分别返回一个负整数,零和一个正整数(一般是-1、0、1)。如果 v 和 w 无法比较或者是两者之一是 null,v.compareTo(w) 将会抛出一个异常,比单纯返回 v > w 更加严谨。后面基本上是对 sort 函数进行填充和改进,其他函数的逻辑基本上不会改变。

3. 选择排序:

一种最简单的排序算法思路是这样的:首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。再次,在剩下的元素中,找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到整个数组排序完成。这种方法叫做选择排序,因为它在不断地选择剩余元素之中的最小者。

/**
 * 选择排序
 */
public class Selection {

    /**
     * 通过两层for循环,每一次找出i后面元素的最小值,并且与i调换顺序,直至顺序正确,默认升序排序
     * @param a
     */
    public static void sort(Comparable[] a){
        for (int i = 0;i < a.length;i++){
            int min = i;
            //将a[i]与a[i+1 ... N]中最小元素交换
            for (int j = i+1;j < a.length;j++){
                if (less(a[j],a[min])){
                    min = j;
                }
            }
            exchange(a,i,min);
        }
    }

    private static boolean less(Comparable v,Comparable w){
        return v.compareTo(w) < 0;
    }

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

    private static void show(Comparable[] a){
        //在单行中打印数组
        for (int i = 0;i < a.length;i++){
            System.out.println(a[i]+" ");
        }
        System.out.println();
    }

    /**
     * 测试数组元素是否有序
     * @param a
     * @return
     */
    public static boolean isSorted(Comparable[] a){
        for (int i = 1;i < a.length;i++){
            if (less(a[i],a[i-1])){
                return false;
            }
        }
        return true;
    }
}

与其他的排序相比,选择排序的数据移动是最少的,每次交换都会改变两个数组元素的值,因此选择排序用了 N 次交换——交换次数和数组的长度都是线性关系。这个时候注意到选择排序的 sort 方法逻辑,与冒泡排序是否有些类似?我们来探究一下选择排序和冒泡排序的区别。

4. 选择排序和冒泡排序:

  • 原理:
    选择排序是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

    冒泡排序的基本思想是将数组中的每个相邻元素进行两两比较,按照小元素在前(或大元素在前)的原则确定是否进行交换。这样每一轮执行之后,最大(或最小)的元素就会被交换到了最后一位。 完成一轮之后,我们可以再从头进行第二轮的比较,直到倒数第二位(因为最后一位已经是被排序好的了)时结束。这样一轮之后,第二大(或第二小)的元素就会被交换到了倒数第二位。同样的过程会依次进行,直到所有元素都被排列成预期的顺序为止。这个过程是不是很像是水中的起泡一个个冒起来的过程.。

  • 区别:

    主要在于交换方式:冒泡法每次比较和移动相邻的两项 ;选择排序每次交换当前项和第n项 。

总的来说,两种排序比较的次数是相同的 ,但交换的次数,选择排序是更少的 。

但通常,选择排序更快一点 ,冒泡排序是每一次都可能要交换 ,而选择排序是在比较时记下a[i]的位置 最后来交换,所以他们的交换过程是不一样的,而查找的过程是一样的

5. 插入排序:

我们打牌的时候,拿到的牌经常是需要进行排序操作的,一般是一张一张的来,将每一张牌插入到其他有序的牌中的适当位置。基于这种思想,就有了插入算法——为了给要插入的元素腾出空间,我们需要将其余所有元素在插入之前都向右移动一位。我们来看看与选择排序有什么异同点:

  • 与选择排序一样,当前索引左边的所有元素都是有序的,但他们的最终位置还不确定,为了给最小元素腾出空间,它们可能会被移动。但是当索引到达数组的右侧时,数组排序就完成了。
  • 与选择排序不同的是,插入排序所需的时间取决于输入中元素的初始顺序。例如,对于一个很大且其中的元素已经有序(或接近有序)的数组进行排序将会比对随机顺序的数组或者是逆序数组进行排序要快得多。

于是,它的实现为:

/**
 * 插入排序
 */
public class Insertion {

    public static void sort(Comparable[] a){
        for (int i = 0;i < a.length;i++){
            //将a[j]插入到a[j-1 ... 0]中
            for (int j = i;j > 0 && less(a[j],a[j-1]);j--){
                exchange(a,j,j-1);
            }
        }
    }

    private static boolean less(Comparable v,Comparable w){
        return v.compareTo(w) < 0;
    }

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

    private static void show(Comparable[] a){
        //在单行中打印数组
        for (int i = 0;i < a.length;i++){
            System.out.println(a[i]+" ");
        }
        System.out.println();
    }

    /**
     * 测试数组元素是否有序
     * @param a
     * @return
     */
    public static boolean isSorted(Comparable[] a){
        for (int i = 1;i < a.length;i++){
            if (less(a[i],a[i-1])){
                return false;
            }
        }
        return true;
    }
}

此时 sort 方法的逻辑是——在 i 向后遍历的时候,构建一个循环向前遍历,将 a[i] (初始状态 j = i)以及前面的所有元素相邻两位元素两两进行比较,如果大小不符合规则,相邻元素之间调换位置。

6. 希尔排序:

希尔排序是一种基于插入排序的快速的排序算法,对于大规模乱序数组插入排序很慢,因为它之后交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另外一端。希尔排序为了加快速度简单地改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

希尔排序的思想是使数组中任意间隔为 h 的元素都是有序的,这样的数组被称为 h 有序数组。在进行排序的时候,如果 h 很大,我们就能将元素移动到很远的地方,为实现更好的 h 有序创造方便。我们这里使用的是 N/3 开始递减到 1 的递增序列。

/**
 * 希尔排序
 */
public class Shell {

    public static void sort(Comparable[] a){
        int h = 1;
        while (h < a.length/3){
            h = h*3 + 1;
        }
        while (h >= 1){
            for (int i = 0;i < a.length;i ++){
                //将a[j]插入到a[j-h],a[j-2h],a[j-3h]...中去
                for (int j = i;j > 0 && less(a[j],a[j-h]);j -= h){
                    exchange(a,j,j-h);
                }
            }
            h /= 3;
        }
    }

    private static boolean less(Comparable v,Comparable w){
        return v.compareTo(w) < 0;
    }

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

    private static void show(Comparable[] a){
        //在单行中打印数组
        for (int i = 0;i < a.length;i++){
            System.out.println(a[i]+" ");
        }
        System.out.println();
    }

    /**
     * 测试数组元素是否有序
     * @param a
     * @return
     */
    public static boolean isSorted(Comparable[] a){
        for (int i = 1;i < a.length;i++){
            if (less(a[i],a[i-1])){
                return false;
            }
        }
        return true;
    }
}

sort 方法的大体逻辑与插入排序相同,但在外面加入一层 while 循环,循环变量 h 每次除以三,与前面对应。

希尔排序是一种在实际应用中都十分常见的算法,因为对于中等大小的数组它的运行时间是可以接受的,它的代码量很小,而且不需要使用额外的内存空间。更加高效的算法确实也有,但他们可能只会比希尔排序快两倍(甚至可能还不到),而且更加复杂。如果你需要解决一个排序问题而又没有系统排序函数可以用,可以先用希尔排序,然后再考虑是否值得将它替换成更加复杂的排序算法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值