冒泡排序

来源:互联网 发布:mac里面xp升级win7 编辑:程序博客网 时间:2024/05/22 15:54

冒泡排序的思路很简单——从头至尾遍历数组元素,若前一项大于(或小于)后一项,则交换相邻两项。单次遍历整个数组可将某一个元素排列到正确位置,因此需要遍历元素数量n次。在代码中体现也就是内外两层循环,内层循环负责遍历中两两元素的交换操作,外层负责遍历次数控制。

首先看版本1:

/** * 冒泡排序效率最低写法,没有任何优化步骤 * @param arrayToSort */public static void sortAlpha(int[] arrayToSort) {    for (int i = 0; i < arrayToSort.length; i++) {        for (int j = 0; j < (arrayToSort.length - 1); j++) {            int tmp;            if (arrayToSort[j] > arrayToSort[j + 1]) {                tmp = arrayToSort[j];                arrayToSort[j] = arrayToSort[j+1];                arrayToSort[j+1] = tmp;            }         }    }}

很明显的两层循环,这里按从小到大排序,因此弱发现后一元素比当前元素大,则交换两个元素的位置,直到比较到最后一个元素为止。因为是jj+1比较,为避免数组越界,内层for循环需以arrayToSort.length - 1做边界。

这个版本中没有做任何优化,因为遍历n次后,数组中后n个元素实际已经是排好序的了,因此不需要再进行比较了。所以内层循环还可以进一步优化,得到版本2:

/** * 冒泡排序优化版本1,减少内层循环遍历个数,已经排序好的元素无需遍历。 * @param arrayToSort */public static void sortBeta(int[] arrayToSort) {    for (int i = 0; i < arrayToSort.length; i++) {        for (int j = 0; j < (arrayToSort.length - 1 - i); j ++) {            int tmp;            if (arrayToSort[j] > arrayToSort[j + 1]) {                tmp = arrayToSort[j];                arrayToSort[j] = arrayToSort[j+1];                arrayToSort[j+1] = tmp;            }        }    }}

由于遍历n次后,后n项已经排好序了,因此内层循环上界再减去遍历次数即可,即arrayToSort.length - 1 - i这样每次内层遍历便可减少n次比较操作,提升了效率。

虽然这时内层循环已经优化操作次数,但如果给一个部分有序的数列,如

{1, 2, 3, 4, 8, 7, 6, 5}


遍历前四次后,数组实际已经排好序了,这时就不需要再进行比较操作了,因此除了前四次排序,仅在需要一次遍历就可检验出数组有序,也就是公共遍历5次数组就可完成排序了。在当前的逻辑下,会导致出现3次无意义的遍历。
解决这个问题的方案也很简单,若数组已经有序,则不会出现交换操作。因此仅需设定一个标志变量,当有交换时置成需要排序(检验)的状态,当所有元素已经有序,检验过程没有交换操作,那么再下次遍历数组直接退出即可,便是版本3:

/** * 冒泡排序优化版2,减少内部排序遍历个数,并且添加了数据是否有序的检验, * 若数组已经有序,无需再进行遍历,直接退出。 * @param arrayToSort */public static void sortGamma(int[] arrayToSort) {    boolean needSort = true;    for (int i = 0; i < arrayToSort.length; i++) {        if (!needSort) {            break;        }        for (int j = 0; j < (arrayToSort.length - 1 - i); j ++) {            needSort = false;            int tmp;            if (arrayToSort[j] > arrayToSort[j + 1]) {                tmp = arrayToSort[j];                arrayToSort[j] = arrayToSort[j+1];                arrayToSort[j+1] = tmp;                needSort = true;            }        }    }}

在每次进入内部循环时重置标志变量,若进行了交换操作则相应改变,进入内层循环前进行判断,若不需要再遍历,直接退出。

接下来写一些测试代码,来验证三种方案的效率。生成两个数组:一个完全逆序,长度为9999;另一个完全正序,长度同样为9999(不用再排序了),然后分别使用三种排序,统计排序时间,以下是一种方法的调用:

public static void main(String[] args) {    final int len = 9999;    int test[] = new int[len];    for (int i = 0; i < test.length; i++) {        test[i] = len - 1 - i;    }    int testNoNeedSort[] = new int[len];    for (int i = 0; i < testNoNeedSort.length; i ++) {        testNoNeedSort[i] = i;    }    long startTimeA = System.currentTimeMillis();    BubbleSort.sortGamma(test);    long endTimeA = System.currentTimeMillis();    System.out.println("Time use A:" + (endTimeA - startTimeA));    long startTimeB = System.currentTimeMillis();    BubbleSort.sortGamma(testNoNeedSort);    long endTimeB = System.currentTimeMillis();    System.out.println("Time use A:" + (endTimeB - startTimeB));}

运行三种排序,统计到的时间:

版本 逆序(最差情况)ms 正序(最好情况)ms 版本1 107 66 版本2 62 42 版本3 60 1

最好情况下由于仅需比较,不需要交换,因此三种版本使用时间均比最差情况短,但由于第三种增加了交换情况的判断,仅需遍历一遍就可完成。
最差情况下由于版本2,版本3对内层排序进行了操作数优化,因此显著优于完全没有优化的版本1。

0 0
原创粉丝点击