1、我们知道,在冒泡排序算法中,每一次冒泡的过程就是一次求最值的过程,也就是说一次冒泡只能确定一个数的位置。所以冒泡排序需要冒泡n-1次(n为待排序数据的个数),现在我们可以这样想:如果一次冒泡的过程中,同时确定首尾两个最值,这样是否可以实现呢!
2、如前所述的排序方法实际上是基于冒泡排序算法的,也就是一种双向的冒泡排序算法,也称鸡尾酒排序算法。
3、下面以一组数{1, 5, 0, 2, 4, 3}为例,通过与普通冒泡算法比较说明该算法:
普通冒泡算法:
(1)大值右移(注:通过交换相邻的元素实现移动):{1, 0, 2, 4, 3, 5},此时数据5的位置确定了;
(2)现在对{1, 0, 2, 4, 3}进行排序,大值右移:{1, 0, 2, 3, 4},此时数据4的位置确定了;
(3)现在对{1, 0, 2, 3}进行排序,,大值右移:{1, 0, 2, 3},此时数据3的位置确定了;
(4)现在对{1, 0, 2,}进行排序,,大值右移:{1, 0, 2},此时数据2的位置确定了;
(5)现在对{1, 0}进行排序,,大值右移:{0, 1},此时数据1的位置确定了;
经过如上5步(即5次冒泡)后,这组数就实现了升序。
下面是双向冒泡算法的实现:
(1)大值向右移(注:同样通过交换相邻的元素实现移动):{1, 0, 2, 4, 3, 5},此时数据5的位置确定了;
(2)现在对{1, 0, 2, 4, 3}反向排序,小值左移:{0, 1, 2, 3, 4},现在我们看到实际上这组数已经排好序了。
注:上述两步是在一次内循环中完成的,也就是在一次冒泡过程中先将最大值交换到最右边,再将最小值交换到最左边,一次确定两个数的位置。
4、下面是实现代码:
#define swap(x, y) (x = (x) + (y), y = (x) - (y), x = (x) - (y))
|
void CocktailSort1( int * a, int n)
{
for ( int i = 0; i < n - 1; i++)
{
for ( int j = i; j < n - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
}
}
for ( int j = n - 1 - i - 1; j > i; j--)
{
if (a[j] < a[j - 1])
{
swap(a[j], a[j - 1]);
}
}
}
}
|
5、就上例来说,显然双向冒泡排序与普通冒泡排序法相比,减少了交换次数;当然该算法也可以有普通冒泡算法类似的优化:
void CocktailSort1( int * a, int n)
{
bool bFlag = true ;
for ( int i = 0; i < n - 1; i++)
{
bFlag = true ;
for ( int j = i; j < n - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
bFlag = false ;
}
}
if (bFlag)
{
break ;
}
bFlag = true ;
for ( int j = n - 1 - i - 1; j > i; j--)
{
if (a[j] < a[j - 1])
{
swap(a[j], a[j - 1]);
bFlag = false ;
}
}
if (bFlag)
{
break ;
}
}
}
|
6、二次优化(通过记录最后交换位置,减少循环次数):
void CocktailSort1( int * a, int n)
{
bool bFlag = true ;
int nLeft = -1;
int nRight = n - 1;
int nIndex;
for ( int i = 0; i < n - 1; i++)
{
bFlag = true ;
nIndex = nRight;
for ( int j = nLeft + 1; j < nIndex; j++)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
bFlag = false ;
nRight = j;
}
}
if (bFlag)
{
break ;
}
bFlag = true ;
nIndex = nLeft;
for ( int j = nRight - 1; j > nIndex; j--)
{
if (a[j] < a[j - 1])
{
swap(a[j], a[j - 1]);
bFlag = false ;
nLeft = j;
}
}
if (bFlag)
{
break ;
}
}
}
|