zoukankan      html  css  js  c++  java
  • 冒泡排序

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

           如果已经了解了冒泡排序,那么可以直接进入到总结篇: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),这个会比很多算法都好,但是还是比不上插入排序。据说,在图像处理这方面,有一些算法是用冒泡排序的。

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

  • 相关阅读:
    [leetcode]Palindrome Partitioning II
    [wikioi]传纸条
    [leetcode]Palindrome Partitioning
    [leetcode]Convert Sorted List to Binary Search Tree
    [topcoder]ActivateGame
    [topcoder]NinePuzzle
    [topcoder]BestRoads
    [topcoder]IncreasingSubsequences
    [leetcode]Surrounded Regions
    CF 432B :Football Kit
  • 原文地址:https://www.cnblogs.com/gdouzz/p/10720451.html
Copyright © 2011-2022 走看看