《数据结构原理与经典问题求解》——Shaker排序

Shaker排序又叫鸡尾酒排序或双向冒泡排序,它是冒泡排序的一种轻微改进。与冒泡排序相同,Shaker排序也是一种稳定排序算法。不同的是,普通的冒泡排序算法仅是从低到高比较序列里的每个元素,或者说普通的冒泡排序算法只能每次从前向后按一个次序进行遍历,而Shaker排序方法每次遍历却包括两个方向,先从前向后再从后向前,在从前往后遍历后能记录最后发生交换的两个元素位置,从后往前遍历时就从这个位置开始。这种双向交替比较不仅可以使小的浮上水面,同时也会使大的沉倒水底,因而较普通的冒泡算法在效率上有所改进。

在Shaker排序法中,具有n个待排序元素的数据表将分n/2个阶段来完成排序操作。每个阶段的Shaker排序都包括一个从左向右的遍历和一个从右向左的遍历。在遍历过程中会将两个相邻元素进行比较,如果它们的顺序是非正常的就将它们做交换。

假设有待排序数组[20, 37, 50, 35, 10, 1],可知整个排序过程共分3个阶段。首先进行第一阶段的从左向右遍历,整数对(20,37)被比较,不进行交换操作。接下来比较整数对(37,50),它们也不进行交换。然后比较整数对(50, 35),由于它们处于非正常顺序,因此产生交换,得到新数组[20, 37, 35, 50, 10, 1]。当50和10被比较时,通过一个交换得到新数组[20, 37, 35, 10, 50, 1]。最后比较整数对(50,1),在一个交换之后,第一阶段的自左向右遍历结束,并得到新数组[20, 37, 35, 10, 1, 50]。现在进行第一阶段的自右向左遍历。首先比较整数对(10,1),并做一次交换,得到新数组[20, 37, 35, 1, 10, 50]。然后比较整数对(35,1),进行一次交换后得到新数组[20, 37, 1, 35, 10, 50]。如此继续下去最终在第一阶段的自右向左遍历结束后得到新数组[1, 20, 37, 35, 10, 50]。第一阶段完毕后,最大和最小的元素已经分别得到,排除掉它们得到数组[20, 37, 35, 10],在这个数组上进行第二阶段操作。在完成自左向右遍历后,得到[20, 35, 10, 37]。接下来再进行自右向左遍历得到[10, 20, 35, 37]。同样排除掉两头的元素,因此第三阶段的操作将在[20, 35]上进行。

此处示意图略。

实现Shaker排序算法的源代码如下:

//Shaker排序
 
#ifndef SHAKERSORT_H
#define SHAKERSORT_H
 
#include <vector>
using namespace std;
 
class ShakerSort
{
private:
       int len;
       vector<int> list;
public:
       ShakerSort(vector<int> _list, int _len);
       void shaker_sort();
       void swap(int,int);
       void out();
};
 
#endif
 
#include "ShakerSort.h"
#include <iostream>
using namespace std;
 
ShakerSort::ShakerSort(vector<int> _list, int _len)
{
       for (int i=0; i<_len; i++) list.push_back(_list[i]);
       this->len = _len;
}
 
//7 Shaker排序------------------------------------------------------------
void ShakerSort::shaker_sort()
{
       int i, left = 0, right = len - 1,shift = 0;
       while(left < right)
       {
              for(i = left; i < right; i++)                   // 向右进行气泡排序
              {
                     if(list[i] > list[i+1])
                     {
                            swap(i, i+1);
                            shift = i;                                    // 记录最后一次所在的状态
                     }
              }
              right = shift;
 
              for(i = right; i > left; i--)                           // 向左进行气泡排序
              {
                     if(list[i] < list[i-1])
                     {
                            swap(i ,i-1);
                            shift = i;
                     }
              }
              left = shift;
       }
}
 
void ShakerSort::swap(int i, int j)                       // 交换两个元素的位置
{
       int temp = list[i];
       list[i] = list[j];
       list[j] = temp;
}
 
void ShakerSort::out()
{
       for (int i=0; i<len; i++)
       {
              cout<< list[i] << " ";
              if ((i+1)%18 == 0) cout<<endl;
       }
       cout <<endl;
}

为了分析Shaker排序算法的效能,首先看看元素对之间的比较次数。来做一个分步分析。易知,在第一阶段,元素的比较次数为(n-1)+(n-2);而在第二阶段,元素的比较次数为(n-3)+(n-4),依此类推,最终得到所有的阶段比较次数的和为(n-1)+(n-2)+(n-3)+…+i= n(n-1)/2。通过理论证明可以得到,Shaker排序算法的复杂度是O(n2)。需要说明的是,在最坏的情况下和平均的情况下算法的复杂度都是O(n2)。但是,如果数据表在进行排序之间已经处于正常顺序或非常接近正常顺序,那么算法的复杂度将接近O(n)。

 

文章出自左飞老师的笔记:http://student.csdn.net/space.php?uid=113322&do=blog&id=7009

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值