蛮力算法: 选择排序 冒泡排序(详解)

蛮力法:

蛮力法(brute force)是一种简单直接地解决问题的方法,常常直接基于问题的描述和所涉及的概念定义。
虽然巧妙而高效的算法很少来自于蛮力法,但它还是具有重要地位。因为它能解决几乎任何问题,如果要解决的问题的规模不大,或者对性能没有很高的要求,那么花大工夫涉及复杂的算法显然是不值得的。
下面就来介绍一下2大蛮力排序算法,然后是它们的分析。

框架介绍:

在介绍算法之前,有必要介绍一些以后会用到的方法。
使用前2个方法,我们就可以生成随机数组,然后方便地判断数列是否被正确排序了,以此验证排序算法的正确性
第3个方法从标准输入中读取数据(通过重定向),进行大规模排序,以此比较不同算法的性能

    /**
     * 生成一个长度为0~600的数组,每个元素的值为0~99999的整数。
     * 
     * @return
     */
    public static Integer[] randomArray() {
        Integer[] r = new Integer[(int) (600 * Math.random())];
        for (int i = 0; i < r.length; i++)
            r[i] = (int) (99999 * Math.random());
        return r;
    }

    /**
     * 返回一个数组是否是有序的。
     * @param r
     * @return
     */
    public static boolean isSorted(Integer[] r) {
        for (int i = 1; i < r.length; i++)
            if (r[i].compareTo(r[i - 1]) < 0)
                return false;
        return true;
    }

    /**
     * 从标准输入中读取1000000个整数的数组。
     * @return
     */
    public static Integer[] arrayIn(){
        Scanner in = new Scanner(System.in);
        Integer[] r = new Integer[1000000];
        for(int i=0;i<1000000;i++)
            r[i] = in.nextInt();
        return r;
    }

选择排序:

选择排序开始的时候,我们扫描整个列表,找到它的最小元素,然后和第一个元素交换(如果第一个元素就是最小元素,那么它就和自己交换。)。再次,在剩下的元素中找到最小元素,将它和数组的第二个元素交换位置,以此往复,直到整个数组有序。这种算法叫做选择排序,因为它每次都选择剩余元素之中最小的元素放在正确位置

    public static void selection(Integer[] r) {
        int N = r.length;
        for (int i = 0; i < N - 1; i++) {
            int min = i;//已知最小元素的索引
            for (int j = i + 1; j < N; j++)
                if (r[min] > r[j])//如果找到更小的元素,更新索引
                    min = j;
            int temp = r[i];//交换位置
            r[i] = r[min];
            r[min] = temp;
        }
    }

算法分析:
对于长度为N的数组,选择排序需要大约(N^2-N)次比较和N次交换。
选择排序是一种容易理解和实现的简单排序算法:
1. 它的运行时间和输入无关: 也就是说,一个有序的数组和一个随机排序的数组所用的排序时间是相同的!对于任何输入来说,选择排序都是一个O(N^2)的算法。
2. 数据移动是线性的: 键的交换次数仅为O(N), 准确的是说n-1此,这个特点使得选择排序优于很多其他算法。

冒泡排序:

另一个非常直观的方法是冒泡排序,它比较表中的相邻元素,如果它们是逆序的话,就交换它们的位置。重复多次之后,最大的元素就“沉到”列表最后一个位置。第二遍操作将第二大的元素“沉下去”。第n-1遍之后,该列表就排好序了。

    public static void bubble(Integer[] r) {
        int N = r.length;
        for (int i = 0; i < N - 1; i++) //第i遍,每次沉下一个最大的元素
            for (int j = 0; j < N - 1 - i; j++) //扫描数组
                if (r[j] > r[j + 1]) {//如果逆序就交换
                    int temp = r[j];
                    r[j] = r[j + 1];
                    r[j + 1] = temp;
                }
    }

算法分析:
对于大小为N的数组,冒泡排序的比较次数和选择排序一样大约为(N^2)/2, 但它的交换次数取决于特定的输入。最坏情况下就是遇到降序数组,这时交换次数和比较次数一样大约是(N^2)/2。根据冒泡排序的特点,我们可以提出下面的改良方案。

改良方案:
如果对列表比较一遍之后没有交换元素的位置,那么这个表已经有序,我们就可以停止这个算法了。
虽然对于某些输入,这个版本的冒泡排序可以比较快,但是最坏情况平均情况下,它依然是个时间复杂度为O(N^2)的算法。
但不管怎么样,冒泡排序除了名字好听之外,也就并没有什么优点了。它不是一个好的选择。

总结:

main函数代码如下,循环生成随机整数数组,进行排序,然后验证。

    public static void main(String[] args) {
        while (true) {
            Integer[] r = randomArray();
            //selection(r);
            //bubble(r);
            for (int x : r)
                System.out.print(x + " ");
            System.out.println("\n" + isSorted(r) + "\t" + r.length);
            if(!isSorted(r))    break;
        }
    }

对于随机排序的无重复主键的数组,它们的运行时间是平方级别的O(N^2)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章