算法练习——冒泡排序的两次进化

冒泡排序是一个很简单的排序算法,也是每一个学计算机的同学一开始就能掌握的算法。冒泡排序虽然简单,但是却很简陋,是效率很低的排序算法。如果我们对其稍加改造,它还是能够变得灵活一些的。这就是这篇文章的主题,冒泡排序的改进方式。
先来看一下我们熟悉的冒泡排序(这里使用的C++编译器是Xcode,并不是兼容所有版本的,所以大家将其当作伪代码好了,,,):

// 原始的 bubbleSort
int  bubbleSort_origin( int  a[], int  n)
{
    
int  compareNum= 0 ;    // 元素比较次数
    
int  step= 0 ;      // 趟数
    
int  *b= new   int [n];
    
// 将数组元素拷贝到 b 数组里,不改动 a 数组
    
if (a!= NULL )
    {
        
for ( int  i= 0 ;i         {
            b[i]=a[i];
        }
    }
    
else
    {
        
return  - 1 ;
    }
    
for ( int  i= 0 ;i 1 ;i++)
    {
        
for  ( int  j= 0 ;j 1 ; j++)
        {
            compareNum++;
            
if (b[j]>b[j+ 1 ])
            {
                
int  temp=b[j+ 1 ];
                b[j+
1 ]=b[j];
                b[j]=temp;
            }
        }
        step++;
    }
    
int  i= 0 ;
    
for (i= 0 ;i 1 ;i++)
    {
        
cout < "," ;
    }
    
cout < endl ;
    
cout << " 元素比较的次数: " < endl ;
    
return  step;
}

OK,我们说一下第一个问题,数据结构课一定也讲过,就是如果数组本身就已经排好了,大家都站好队了,或者才两趟就已经把队伍排好了,可是这种原始的冒泡排序还要傻傻地
继续重复着没有意义的比较,数据量多了,还是挺坑的。
插播最近我很爱的一个广告:最近我在玩一款三国策略游戏,,,,,,,,,如果你是脑残,就别来了,来了也是坑,哈哈哈哈。有时候这个广告我要看好几遍。
那么第一次进化就是为冒泡排序添加一个哨兵,它是一个bool变量,用来标记是否已经排好序了,如果排好序了,就不排下一趟了呗:
// 带哨兵的冒泡排序
int  bubbleSort_soilder( int  a[], int  n)
{
    
int  compareNum= 0 ;    // 元素比较次数
    
int  step= 0 ;      // 趟数
    
int  *b= new   int [n];
    
// 将数组元素拷贝到 b 数组里,不改动 a 数组
    
if (a!= NULL )
    {
        
for ( int  i= 0 ;i         {
            b[i]=a[i];
        }
    }
    
else
    {
        
return  - 1 ;
    }
    
bool  flag= true // 这就是哨兵哥
    
for ( int  i= 0 ;i 1 &&flag;i++)
    {
        flag=
false ;
        
for  ( int  j= 0 ;j 1 ; j++)
        {
            compareNum++;
            
if (b[j]>b[j+ 1 ])
            {
                
int  temp=b[j+ 1 ];
                b[j+
1 ]=b[j];
                b[j]=temp;
                flag=
true ;
            }
        }
        step++;
    }
    
int  i= 0 ;
    
for (i= 0 ;i 1 ;i++)
    {
        
cout < "," ;
    }
    
cout < endl ;
    
cout << " 元素比较的次数: " < endl ;
    
return  step;
}

只要加一个bool flag,每一趟开始时,都判断一下flag,如果flag为true,说明上一趟还交换过元素的,队伍还不一定排好呢,如果为false,则说明上一趟都没交换过值,队伍已经排好了,已经OK了。
至于第二次进化,则在于,有哨兵的冒泡排序虽然解决了坑的不停检查已经排好队的情况,但是如果队伍是:1,1,1,1,1,1,1,1,1,1,1,1,1,100,9,10,25,33,1000,1000,1000,1000,1000    ,前后两端都已经排好了,但是中间的排列却还是要从头比较,一直比较到尾部,10个郭靖排在队头,10个姚明排在队尾,中间一拨人打乱了顺序,关前后两头啥事啊:

// 自动调节比较范围的冒泡排序
int  bubbleSort_final( int  a[], int  n)
{
    
int  compareNum= 0 ;    // 元素比较次数
    
int  step= 0 ;      // 趟数
    
int  *b= new   int [n];
    
// 将数组元素拷贝到 b 数组里,不改动 a 数组
    
if (a!= NULL )
    {
        
for ( int  i= 0 ;i         {
            b[i]=a[i];
        }
    }
    
else
    {
        
return  - 1 ;
    }
    
    
int  headindex= 0 ;     // 比较范围的头部
    
int  tailindex=n- 1 ;   // 比较范围的尾部
    
bool  flag= false ;     // 判断是否是某一趟的第一次交换元素
    
for ( int  i= 0 ;i 1 ;i++)
    {
        
if (headindex>=tailindex)     // 比较范围首尾会师
        {
            
break ;
        }
        
int  tailindex_temp= 0 ;
        
for  ( int  j=headindex;j         {
            compareNum++;
            
if (b[j]>b[j+ 1 ])
            {
                
int  temp=b[j+ 1 ];
                b[j+
1 ]=b[j];
                b[j]=temp;
                
if (flag== false )
                {
                    headindex=j-
1 ?j> 0 :j;
                    flag=
true ;
                }
                tailindex_temp=j+
1 ;
            }
        }
        tailindex=tailindex_temp;
        flag=
true ;
        step++;
    }
    
int  i= 0 ;
    
for (i= 0 ;i 1 ;i++)
    {
        
cout < "," ;
    }
    
cout < endl ;
    
cout << " 元素比较的次数: " < endl ;
    
return  step;
}

好了,这就是冒泡排序的两度进化,显然,一般情况下,哨兵的冒泡排序和调整范围的冒泡排序都比原始冒泡排序趟数少,比较次数则显然是调整范围的冒泡排序要少的多。那这种进化的冒泡排序降复杂度吗?sorry,不降,叔叔我们不降,还是O(n^2)........
还有,虽然命名为bubbleSort_final,但也许你还有更好的方法,所以这远不是final,,,下次遇到复杂一些的问题,一定画图辅助。。。。这是第一篇算法练习题,希望能和大家一同交流进步:)











你可能感兴趣的:(算法练习题)