java基础复习——冒泡排序以及改进

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shangming150/article/details/79279994

冒泡排序算法,基本是我们遇到的第一个排序算法,虽然效率并不高,但毕竟是引入门的算法,还是必须要了解的。
基本思想
在待排序的一组数据中,对还未进行排序的数据,按照从下到上的顺序遍历,依次比较相邻两个数据的大小,将较大(小)值向上冒泡,即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换;循环直到数组中的最后一组数据的比较。此时极大(小)值就出现在了最上边。
这里写图片描述

基本步骤:

 1、外循环是遍历每个元素,每次都放置好一个元素;   

 2、内循环是比较相邻的两个元素,把大的元素交换到后面;

 3、等到第一步中循环好了以后也就说明全部元素排序好了;

算法实现:

    /**
     * 冒泡排序
     * 小->大
     *
     * @param data 待排序的数组
     */
    public static void bubbleSort(int[] data) {
        int len = data.length;
        for (int i = 0; i < len - 1; i++) {
            for (int j = 0; j < len - i - 1; j++) {
                if (data[j] > data[j + 1]) {
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                }
            }
        }
    }

上面也说了,冒泡排序的效率并不高,它的时间复杂度达到了这里写图片描述,所以接下来,对这个冒泡排序算法进行一些改进。

改进一:增加位置标识
从冒泡排序的基本实现中可以看到,只有当两个数据的排序不符合我们的排序要求时,才执行交换;那么如果在某一个点位置开始之后的数据没有出现过交换,那么就说明这之后的数据已经符合我们的排序要求,不需要再执行冒泡的比较过程了。
基于这个思想,我们在冒泡算法基础上增加一个位置标识,记录最后一次执行交换的位置,下一次循环只执行到上一次的最后交换位置即可。
改进一实现

    /**
     * 冒泡排序改进
     * 设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。
     * 由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
     *
     * @param data 待排序的数组
     */
    public static void bubbleSortPos(int[] data) {
        int len = data.length;
        int i = len - 1; // 初始化最后一个遍历的位置
        while (i > 0) {
            int pos = 0; // 用于记录最后一次交换的位置
            for (int j = 0; j < i; j++) {   // 循环遍历只执行到上一次最后交换的位置
                if (data[j] > data[j + 1]) {
                    pos = j;
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                }
            }
            i = pos;
        }
    }

改进二:增加是否交换过标识
从改进一可以看到,如果有一趟排序循环中,没有出现过交换,那么i=pos=0,直接就退出了while循环,排序结束。由此可以得到,也可以设置一个boolean类型的标识位,如果某一次遍历排序没有出现过交换,证明数组已经符合我们的排序要求了,那么可以结束排序。
改进二实现

    /**
     * 冒泡排序改进
     * 设置一个标识位,如果这一趟冒泡过程中,没有出现过交换,这说明数组中的数据已经是有序的了,可以直接退出排序过程
     *
     * @param data 待排序的数组
     */
    public static void bubbleSortFlag(int[] data) {
        int len = data.length;
        for (int i = 0; i < len - 1; i++) {
            boolean flag = false;
            for (int j = 0; j < len - i - 1; j++) {
                if (data[j] > data[j + 1]) {
                    int temp = data[j];
                    data[j] = data[j + 1];
                    data[j + 1] = temp;
                    flag = true;
                }
            }
            if (!flag) {
                break;
            }
        }
    }

改进三:双向冒泡
冒泡排序时,每轮次循环都找到了此次未排序序列中的一个极值,既然一个极值可以向上冒,那么它相对的极值应该可以向下沉才对。带着这个思想,我们对算法进行这样的改进:“一个来回”的遍历排序,找到两个极值,一个极大值,一个极小值,然后将遍历的范围一次缩减两个单位大小,这样就实现了双向的“冒泡”算法。
改进三实现

 /**
     * 冒泡排序改进
     * 双向冒泡排序:正向遍历一遍,冒泡出一个最大值,然后再反向遍历, 冒泡出一个最小值,循环操作,直到最小值位置=最大值位置
     *
     * @param data 待排序的数组
     */
    public static void bubbleSortDouble(int[] data) {
        int high = data.length - 1; // 记录上一次的极大值位置
        int low = 0; // 记录上一次的极小值位置
        int i;
        int temp;
        while (low < high) {
            for (i = low; i < high; i++) { // 正向遍历,找出极大值
                if (data[i] > data[i + 1]) {
                    temp = data[i];
                    data[i] = data[i + 1];
                    data[i + 1] = temp;
                }
            }
            high--; // 最高位置下移一位
            for (i = high; i > low; i--) { // 方向遍历,找出极小值
                if (data[i] < data[i - 1]) {
                    temp = data[i];
                    data[i] = data[i - 1];
                    data[i - 1] = temp;
                }
            }
            low++; // 最低位置上移一位
        }
    }

最后看下各冒泡排序的结果:
数组大小:100
测试代码:

public void sort() {
        int[] data = new int[100];
        Random rand = new Random();
        for (int i = 0; i < data.length; i++) {
            data[i] = rand.nextInt(100);
        }
        System.out.println("排序之前:" + Arrays.toString(data));

        int[] temp1 = data;
        Sort.bubbleSort(temp1);
        System.out.println("bubbleSort冒泡:" + Arrays.toString(temp1));

        temp1 = data;
        Sort.bubbleSortPos(temp1);
        System.out.println("bubbleSortPos冒泡:" + Arrays.toString(temp1));

        temp1 = data;
        Sort.bubbleSortFlag(temp1);
        System.out.println("bubbleSortFlag冒泡:" + Arrays.toString(temp1));

        Sort.bubbleSortDouble(data);
        System.out.println("bubbleSortDouble冒泡:" + Arrays.toString(data));

);

    }

冒泡排序结果:

这里写图片描述

冒泡排序时间:
数组大小:10W

测试代码:

public void sort() {
        int[] data = new int[100000];
        Random rand = new Random();
        for (int i = 0; i < data.length; i++) {
            data[i] = rand.nextInt(100000);
        }
//        System.out.println("排序之前:" + Arrays.toString(data));

        int[] temp1 = data;
        long time0 = System.currentTimeMillis();
        Sort.bubbleSort(temp1);
        long time1 = System.currentTimeMillis();
        long bubbletime = time1 - time0;
//        System.out.println("bubbleSort冒泡:" + Arrays.toString(temp1));

        temp1 = data;
        time0 = System.currentTimeMillis();
        Sort.bubbleSortPos(temp1);
        time1 = System.currentTimeMillis();
        long bubblePostime = time1 - time0;
//        System.out.println("bubbleSortPos冒泡:" + Arrays.toString(temp1));

        temp1 = data;
        time0 = System.currentTimeMillis();
        Sort.bubbleSortFlag(temp1);
        time1 = System.currentTimeMillis();
        long bubbleFlagtime = time1 - time0;
//        System.out.println("bubbleSortFlag冒泡:" + Arrays.toString(temp1));

        time0 = System.currentTimeMillis();
        Sort.bubbleSortDouble(data);
        time1 = System.currentTimeMillis();
        long bubbleDoubletime = time1 - time0;
//        System.out.println("bubbleSortDouble冒泡:" + Arrays.toString(data));

        System.out.println("冒泡时间:bubble:" + bubbletime + ",bubblePos:" + bubblePostime + ", bubbleFlag:" + bubbleFlagtime + ",bubbleDouble:" + bubbleDoubletime + ",");

    }

结果1:
这里写图片描述

结果2:
这里写图片描述

将Double版本的bubble移到前面执行,减少因java内存问题而导致的时间差:
结果3:
这里写图片描述

结果4:
这里写图片描述

从上面的执行时间对比结果中可以看到,效率最高的是增加标识Flag以及记录交换位置Pos的两个改进算法,Double双向版本的次之,但也比原始的冒泡排序提高了近10倍。

猜你喜欢

转载自blog.csdn.net/shangming150/article/details/79279994