冒泡排序算法---代码图形化详解以及优化

很长时间没有写博客了,这段时间迎接了我的下一代宝贝,是人生中很幸福的一件事。今天我给大家介绍一下冒泡排序算法吧。不论哪种语言,在介绍到数组和循环的时候都会说到冒泡排序,并不是它的名字好听而是冒泡排序算法是思路最简单,最容易理解的,那么我们还是通过这个冒泡排序来开启我们算法的排序之路吧。

1 最简单的排序实现

冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的值,如果反序则交换位置,直到没有反序的交换记录为止。冒泡排序的细节在实现上有很多种变换,我们先来看看最简单的实现方法

    /**
     * 交换排序
     */
    public void exChangeSort(int[] arr) {
        //定义两个下标i,j
        int i, j;
        for (i = 0; i < arr.length; i++) {
            for (j = i ; j < arr.length; j++) {
                //判断i下标的值和j下标的值大小如果i>j交换位置
                if (arr[i] > arr[j]) {
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        for (int k = 0; k < arr.length; k++) {
            System.out.println(arr[k]);
        }
    }

测试方法

	@Test
    public void test() {
        //定义数组
        int[] arr = {9, 1, 5, 8, 3, 7, 4, 2, 6};
        exChangeSort(arr);
    }

打印结果

2019-04-04 10:32:00.272  INFO 15036 --- [           main] com.example.demo.DemoApplicationTests    : Started DemoApplicationTests in 1.612 seconds (JVM running for 2.243)
1
2
3
4
5
6
7
8
9
2019-04-04 10:32:00.423  INFO 15036 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

我们来用一组图解释上面排序的整个运行过程

冒泡排序算法---代码图形化详解以及优化_第1张图片
我们可以看上图分析一下简单的交叉排序是怎么样的一个运行过程,他的思路就是让每一个关键字参数值都和后面的每一个值进行比较,如果反序则交换顺序

  • 当i = 0 的时候 9和1 进行比较,反序调换顺序为1,9之后用1和5去比较不需要调换在用1和8去比较也不需要交换,以后同理
  • 当i = 1 的时候用最初会用9和5进行比较反序调换,在用5和后面的每一位进行比较,反序交换,不反序不动,最后得出结论为129857436
  • 下面当i = 2、i=3、i=4…都是一样的道理就不在一一介绍。

上面这个简单易懂的代码是最容易写出的排序代码,但是他是有缺陷的,观察后发现,在排序好1和2的位置后,对其余关键字的排序没有任何作用,反观3还是跑到了数组末尾,也就是说这个数组排序算法的效率是非常低的,那么我们下面看下正常的冒泡排序算法吧

2 冒泡排序算法

我们来看看正宗的冒泡排序算法有没有什么需要改进的地方

public void BubbleSort(int[] arr) {
        int i, j;
        for (i = 0; i < arr.length; i++) {
        	//这里的j arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        for (int k = 0; k < arr.length; k++) {
            System.out.println(arr[k]);
        }
    }
	@Test
    public void test() {
        //定义数组
        int[] arr = {9, 1, 5, 8, 3, 7, 4, 2, 6};
        BubbleSort(arr);
    }

运算结果

2019-04-04 12:06:45.593  INFO 8600 --- [           main] com.example.demo.DemoApplicationTests    : Starting DemoApplicationTests on DESKTOP-K7I687J with PID 8600 (started by lqf in D:\test\demo)
2019-04-04 12:06:45.594  INFO 8600 --- [           main] com.example.demo.DemoApplicationTests    : No active profile set, falling back to default profiles: default
2019-04-04 12:06:46.669  INFO 8600 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-04-04 12:06:46.867  INFO 8600 --- [           main] com.example.demo.DemoApplicationTests    : Started DemoApplicationTests in 1.452 seconds (JVM running for 2.037)
1
2
3
4
5
6
7
8
9
2019-04-04 12:06:46.985  INFO 8600 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'

还是用一组图来解释上面整个的运算过程
冒泡排序算法---代码图形化详解以及优化_第2张图片
通过上图我们可以观察出这个排序的整个运算循环比较过程,他符合冒泡排序的思想两两比较相邻记录的值,如果反序则交换位置,直到没有反序的交换记录为止。所以这是一个正确的冒泡排序,我们通过上图也可以看出它是递减循环的由于我们上面的循环条件进行了-i操作,性能上对于方法一进行了显著的提高,但是我们仔细观察上图会发现当下标i = 5的时候最后的循环结果已经是一个排序好的数组了,那么下标i没有结束还需要继续循环这不就浪费了资源消耗了性能么?

3 优化后的冒泡排序

	/**
     * 优化后的排序算法
     */
    public void perfectBubbleSort(int[] arr) {
        int i, j;
        boolean flag = true;
        for (i = 0; i < arr.length && flag; i++) {
            flag = false;
            for (j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = true;
                }
            }
        }
        for (int k = 0; k < arr.length; k++) {
            System.out.println(arr[k]);
        }
    }
	 @Test
    public void test() {
        //定义数组
        int[] arr = {2, 1, 3, 4, 5, 6, 7, 8, 9};
        perfectBubbleSort(arr);
    }

运算结果和上面是一样的这里就不展示了
还是通过一组图片来描述这个优化后的运算过程上面的数组我已经改变了这样更能好的展示出优化效果
冒泡排序算法---代码图形化详解以及优化_第3张图片
我们现在按着代码和图片来分析一下上面的运行过程
首先我们在循环的最外层设置了一个变量flag = true,所以第一层循环的条件&&flag是可以正常进入的,随后我们更换的变量flag = flase,继续去执行内层循环,这个时候如果内层循环如果有位置上的替换那么我们还会将flag设置为true以便后续循环替换,反之由于冒泡排序的思想是相邻的两个变量值两两进行比较如果我们循环判断了一圈发现变量值没有任何可替换的操作那么现在说明了我们整个数组是有序的,也就是flag值为false不会改变成true的状态,当下次循环到外层循环的时候条件判断&& flag就不成立,这样就可以实现因为有序的情况下进行无意义的循环判断操作。

冒泡排序的时间复杂度

分析一下它的时间复杂度,当最好的情况也就是要排序的数组本身就是有序的,那么根据优化后的程序运行的话可以推断出就是 n-1此的比较,没有数据交换,时间复杂度为O(n)。当最坏的情况,即待排序表是逆序的情况,需要进行 n-1趟排序。每趟排序要进行 n-i 次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:那么冒泡排序的时间复杂度为O(n²)。

你可能感兴趣的:(日积月累,java,java面试,程序人生,数据结构与算法)