一,冒泡排序

  无论你学习哪种编程语言,在学到循环和数组时,通常都会介绍一种排序算法来作为例子,而这个算法一般就是冒泡排序。并不是它的名称很好听,而是说这个算法的思路最简单,最容易理解。因此哪怕大家可能都已经学过冒泡排序了,我们还是从这个算法开始我们的排序之旅。

  冒泡排序效率不高,几乎是最慢的,但是最容易理解的,但不是最优排序算法

  * 比较原理图(这里是降序排序):

  

1,C#语言实现

    int[] l1={7,9,12,1,32,5,7};
        
        for(int i=0;i<l1.Length-1;i++)
        {
            for(int j=0;j<l1.Length-i-1;j++)
            {
                if(l1[j]>l1[j+1])
                {
                    int temp=l1[j];
                    l1[j]=l1[j+1];
                    l1[j+1]=temp;
                }
            }
        }

  冒泡排序是否还可以再优化呢?答案是肯定的,试想一下,我们待排序的序列是{1,2,3,4,5,6}或者{2,1,3,4,5,6},也就是说,除了第一和第二的关键字需要交换外,别的都已经是正常的顺序。当2和1交换之后,此时序列已经有序了,就不需要循环再进行下去。

   ---优化版:

    int[] l1={7,9,12,1,32,5,7};  
        //int[] l1={1,2,3,4,5,6,7};
        bool flag=true;
        int count=0;   //计数器,计算循环次数
        for(int i=0;i<l1.Length && flag;i++) //当flag为false时,退出循环,因为数组已经有序
        {
            flag=false;        //当下边循环中彻底没有数据交换时,证明数组已经有序,就不需要排序了。注意,flag一定要加在这里,确保数组彻底没有数据交换了。
            for(int j=0;j<l1.Length-i-1;j++)  //这里之所以减i,是因为,后边比较的数据不用比较了,可以看总结1中的数字梯形图。
            {
                count++;
                if(l1[j]<l1[j+1])    //这里如果是小于号,就是降序。如果是大于号,就是升序排序
                {

        //C#中交换值方法。需要一个临时变量temp
                    int temp=l1[j]; 
                    l1[j]=l1[j+1];
                    l1[j+1]=temp;
                    flag=true;  //当有数据交换时,证明数组还需要排序。
                }

////注意,flag的值,改变不能放在else这里,因为,假如数组后边值的都不用交换了,这里就会改变flag的值为false,从而就会退出循环,但前边的值有可能还需要交换。例如12,32,9,7,7,5,1这个数组,降序排序时,后边的条件都不成立,即前边的数都不小于后边的数,那么flag就会被设置为false,但这时候,12和32还需要交换,排序就会出问题。flag放在外边的好处,就是,只有当所有的数据不再交换时,flag才不会设置为true,就会跳出循环。

//             else   
//             {
//                flag=false;  //当没有数据交换时,证明数组已经有序,就不需要排序了
//             }
            }            
        }

2,Python语言实现

l1=[22,1,13,5,7,9,2]

---嵌套循环实现(注意边界问题,索引界限问题):

for i in range(len(l1)):
    for j in range(len(l1)-i-1):   //列表长度是7,这里第一次循环,range值是6,j从0开始循环,最大是5,当j等于5时,j+1等于6,即l1的最后一个值。数学归纳法,先用一个值试一下。最好是试开始和结尾的值,最有效果。
        if(l1[j]>l1[j+1]):                
            #temp=l1[j]
            #l1[j]=l1[j+1]
            #l1[j+1]=temp
            #Python中交换值方法如下
            l1[j],l1[j+1]=l1[j+1],l1[j]

  #这里不管数组的顺序是乱的,还是已经排好序的,都要进行比较21次。时间复杂度为1+2+3+...+(n-1)=n(n-1)/2 (这里是根据高斯求和推理出的公式),因此时间复杂度为O(n^2),尽管上边的值为n^2-n/2,取n^2,所以时间复杂度为O(n^2)。

   ---优化版:

  >>> c=0   #计数器,计算循环次数
  >>> l1=[1,2,3,4,5,6]     #待排序列表

  >>> #l1=[22, 1, 13, 5, 7, 9, 2]
  >>> for i in range(len(l1)):

    flag=False;  //这里同上边C#实现一样,flag不能放在下边,具体原因看上边。
    for j in range(len(l1)-i-1):
            c+=1
            if(l1[j]>l1[j+1]):    //这里如果是小于号,就是降序。如果是大于号,就是升序排序
                  l1[j],l1[j+1]=l1[j+1],l1[j]  #Python中交换值方法如下
                flag=True   //当有数据交换时,证明数组还需要排序。
              #else:
                #flag=False  
        if(flag):       //这里判断,是否跳出大循环,如果为true,证明有数据交换,排序还没排好,继续循环。如果为false,证明没有数据交换了,数组已经有序,就不需要排序了,可跳出循环。
              continue
        else:
              print(c)
                  break     

 

@总结(主要学习到了:1,数学归纳法验证算法的正确性---即用代入数据法验证算法。2,flag的使用---看C#优化版总结,注意flag值的改变位置和判断位置。):

1,先自己在本子上,画下图,总结下比较原理,先第一个数和后边的依次比较,如果是升序,那最后一个数比较下来就还是最大的。例如:

7,9,22,18,8,1,99,2      //先7和9比较,9大,两者位置不交换,9和22比较,22大,两者位置不变,22和18比较,22大,两者交换位置(7,9,18,22,8,1,99,2),然后22和8比较,同上交换位置,然后22和1比较,同上交换位置,然后22和99比较,99大,两者位置不变,然后99和2比较,同上交换位置,这时候,就选出了最大数99。所以,数组的最后一位就不用再比较了,比较前6位就可以了。  //这里99退出比较

7,9,18,8,1,22,2    //这里22退出比较

7,9,8,1,18,2   //这里18退出比较

7,8,1,9,2   //这里9退出比较

7,1,2,8   //这里8退出比较

1,2,7   //这里7退出比较

1,2 // 这里2提出比较   ,结束循环。  结果:1,2,7,8,9,18,22,99

2,转换为代码,先用你最熟悉的语言实现,C#是我的马,我就先用它实现,很容易,用嵌套循环实现,然后Python比葫芦画瓢实现了。

3,推荐,用数学归纳法进行验证。代入开始和末尾的值,对代码进行验证,是最好的验证方法。

4,审查代码,实现功能以后,回过头来,再审查一下代码,考虑一下多种排序情况,看还能否再优化下代码。

 

posted on 2014-05-06 18:24  学到老死  阅读(349)  评论(0)    收藏  举报