冒泡排序

  今天,有个编程大概三年的过来面试,其中有道题目是冒泡排序,当然他也很快坐出来,并且答案也是对的,但是仔细一问,他自己也不怎么理解冒泡排序,问问怎么提高冒泡效率做法,他也答不上来,所以这篇文章就让我们好好的讲讲冒泡排序。

       如果已经了解了冒泡排序,那么可以直接进入到总结篇:https://www.cnblogs.com/gdouzz/p/10759399.html

 (一)什么是冒泡排序

   直接上例子把,既然是冒泡,那就有冒泡的过程(假设从小到大排列),请看接下来的例子:

        有一个数组    arr=[4,5,6,3,2,1] ,那接下来就来模拟冒泡的过程 

a[5] 1 1 1 1 6
a[4] 2 2 2 6 1
a[3] 3 3 6 2 2
a[2] 6 6 3 3 3
a[1] 5 5 5 5 5
a[0] 4 4 4 4 4

注意:我这个数据的方向,写成竖的形式,这样就很助于理解冒泡的过程(上面是第一次冒泡的最后结果)。

      下面是第一次冒泡的过程

      首先,我们取4,5比较 也就是arr[0]和arr[1],因为我们之前说了,这次是从小到大的排序,arr[0]和arr[1]不用交换位置。

a[5] 1
a[4] 2
a[3] 3
a[2] 6
a[1] 5
a[0] 4

     接下来,我们取a[1]和a[2]进行交换,还是因为arr[1]小于arr[2]的话呢,不用交换位置。结果如下:

a[5] 1  1
a[4] 2  2
a[3] 3  3
a[2] 6  6
a[1] 5  5
a[0] 4  4

    再接下来,我们取a[2]和a[3]进行交换,a[2]大于a[3]要进行位置交换。结果如下:

a[5] 1  1  1
a[4] 2  2  2
a[3] 3  3  6
a[2] 6  6  3
a[1] 5  5  5
a[0] 4  4  4

     再然后呢,我们取a[3]和a[4]进行交换,根据大小关系,需要交换位置,结果如下:

a[5] 1  1  1  1
a[4] 2  2  2  6
a[3] 3  3  6  2
a[2] 6  6  3  3
a[1] 5  5  5  5
a[0] 4  4  4  4

 再过来,我们取a[4]和a[5]进行交换,根据大小关系,需要交换位置,结果如下:

a[5] 1  1  1  1  6
a[4] 2  2  2  6  1
a[3] 3  3  6  2  2
a[2] 6  6  3  3  3
a[1] 5  5  5  5  5
a[0] 4  4  4  4  4

    我们这一次冒泡就结束了,通过这个过程,我们大概也知道了一些冒泡的规律

    1、一次冒泡之后,最后的那个数一定是在自己的正确位置了,至少有一个数回到了正确的位置上。

    2、接下来的第二次冒泡,可以少比较一次(比前面那一趟)。

    根据这两个规则,我们继续完成接下来的冒泡

     第二次冒泡

a[5] 6 6 6 6
a[4] 1 1 1 5
a[3] 2 2 5 1
a[2] 3 5 2 2
a[1] 5 3 3 3
a[0] 4 4 4 4

 第三次冒泡

a[5]  6 6 6
a[4]  5 5 5
a[3]  1 1 4
a[2]  2 4 1
a[1]  4 2 2
a[0]  3 3 3

 第四次冒泡

a[5]  6 6
a[4]  5 5
a[3]  4 4
a[2]  1 3
a[1]  3 1
a[0]  2 2

    第五次冒泡

a[5]  6
a[4]  5
a[3]  4
a[2]  3
a[1]  2
a[0]  1 

     上面就是整个的冒泡过程,经过这个冒泡过程之后,大家的代码也在心里面了描绘出来了。

    public static int[] BubbleSort(int[] arr)
        {
            //第一层循环,总共六个元素,只需要冒泡五次。
            for (var i = 0; i < arr.Length - 1; i++)
            {
                //每次冒泡完了之后,都会有一个元素的位置被确定,不用再进行交换
                for (var j = 0; j < arr.Length - 1 - i; j++)
                {
                    if (arr[j] > arr[j + 1])
                    {
                        var temp = arr[j];
                        arr[j] = arr[j + 1];
                        arr[j + 1] = temp;
                    }
                }
            }
            return arr;
        }

    (二)冒泡排序的改进

    改进思路:减少交换的次数。我们知道,如果数据很早的到达了有序状态,那么剩下的交换次数可以跳过,下面就是对这个的改进。

         举个极端的例子:arr=[1,2,3,4,5]  再下面这个算法中,就可以减少多次的循环,还有很多类似的例子,大家可以好好品尝一下这段代码。

   public static int[] BubbleSort(int[] arr)
        {
            for (var i = 0; i < arr.Length - 1; i++)
            {
                bool swapFlag = false;
                for (var j = 0; j < arr.Length - 1 - i; j++)
                {
                        if (arr[j] > arr[j + 1])
                        {
                            var temp = arr[j];
                            arr[j] = arr[j + 1];
                            arr[j + 1] = temp;
                            swapFlag = true;
                        }
                }
                if (!swapFlag)
                {
                    break;
                }
            }
            return arr;
        }

  相关的代码在:https://github.com/gdoujkzz/Data-Structure-For-CSharp.git

   (三)冒泡排序的复杂度分析

   //冒泡排序       
   //交换过程     
   //交换,就是位置调换过来的意思。
   //冒泡排序的交换过程:n次的交换代码。
   //冒泡排序的比较过程:也是一样n次的比较过程。
   //下面来一个最坏情况  5,4,3,2,1 按照从小到大排序
   //按照从小到大排序
   //1
   //2
   //3
   //4
   //5  
   //第一趟排序,过程如下
   //5比4大(比较一次),需要交换位置(交换位置一次)
   //5比3大(比较一次),需要交换位置(交换位置一次)
   //5比2大(比较一次),需要交换位置(交换位置一次)
   //5比1大(比较一次),需要交换位置(交换位置一次)
   //第一趟排序结束。 可以看出交换次数,和比较次数的规律了。
   //最坏情况下,比较次数和交换次数都是一样,五个数字的话呢,就是4+3+2+1 等差数列  n(n-1)/2 。这里是要比较10次,交换10次,最差情况下//最好情况下,1,2,3,4,5 从小到大排序。
   //交换次数为零次,比较次数为o(n)。 
//我们取平均的交换次数为n(n-1)/4 比较次数为(n+n(n-1)/2)/2,可以知道平均交换次数是没有比较次数那么多的,取比较次数作为时间复杂度(把常量省略)得到o(n²)

   总结:冒泡排序的时间复杂度为o(n²),空间复杂度(除去本身之外,所需的额外空间)为o(1)。

 (四)冒泡排序的优缺点

  冒泡排序,作为我们教科书(我当时大学的学的是严老师的数据结构第二版),里面的排序入门算法,就是冒泡排序,我当时虽然知道冒泡排序,需要时间复杂度为O(n²),但是,感觉这个算法特别吊(当时也没有理解透彻),时至今日,才重新温习,发现了冒泡排序是真的没必要作为教科书的入门排序算法,就是因为它效率低,交换次数太多对CPU也不友好,我觉得教科书里面应该要选择简单插入排序作为入门算法(简单入门排序算法稍后会介绍,也会有一篇文章专门对冒泡排序,简单选择排序,简单插入排序做一个比较)。冒泡排序的唯一优点,可能在当数据是有序的情况下,所需要的时间复杂度为o(n),这个会比很多算法都好,但是还是比不上插入排序。据说,在图像处理这方面,有一些算法是用冒泡排序的。

  总得来说,冒泡排序名字倒是很好听,但是说真的效率不高,不推荐使用,即使作为一个初学者,写排序算法,也应该先想到用插入排序。以上是个人想法,欢迎大家留言交流。

posted @ 2019-04-17 09:25  GDOUJKZZ  阅读(399)  评论(2编辑  收藏  举报