一、简介
双调排序(Bitonic Sort)属于排序网络(Sorting Network)的一种,它是一种可以并行计算的排序算法。
要理解双调排序,首先需要理解双调序列,双调序列定义如下:
如果序列<a0, a1, …, an-1>满足以下两个条件之一,则称之为双调序列:™
存在一个0≤k≤n-1,使得<a0, a1, …, ak-1>为升序序列,<ak, ak+1, …, an-1>为降序序列;或存在一个标号的循环移位,使得条件1)满足。
如果n为偶数,且<a0, a1, …, an/2-1>为升序序列,<an/2, an/2+1, …, an-1>为降序序列,则以下两个序列都是双调序列
S1=<min(a0, an/2), min(a1, an/2+1), …, min(an/2-1, an-1)>
S2=<max(a0, an/2), max(a1, an/2+1), …, max(an/2-1, an-1)>
双调排序主要思想:
1、首先不断的二分,直到每组只剩下一个元素,然后开始归并。
2、双调排序归并时以不大于n的最大2的幂次方2^k为界限,把2^k~n的元素分别与0~(n-2^k)的元素比较,然后再分别在0~2^k和2^k~n这两段上应用比较网络。
3、双调排序难以理解的地方就在于这个归并的过程,假设我们要把长度为n的序列a升序排列,由于二分时是把前n/2的序列降序排列后n/2的序列升序排列的,而n-2^k < n/2,所以前n-2^k 和后n-2^k个元素都大于中间的元素,当前n-2^k个元素和后n-2^k个元素比较时,会把序列中最大的n-2^k个元素放到最后n-2^k个位置上,也就是说比较后,2^k~n的元素都比0~2^k的元素大,这样在分别对这两段用同样的方法归并,最终得到完整的升序序列。
以6个元素的序列为例,当前3个元素已经降序排好,后3个元素已经升序排好(递归到底时是从1个元素开始返回的,所以对6个元素归并时前后3个元素已经排好序),这个以4(不大于6的最大2的幂次方)为界限,分别让第1个和第5个、第2个和第6个元素比较,使得这6个元素中最大的两个处于第5、6个位置上,然后再分别对前4个和后2个元素按此方法归并,最终递归完成排序。
二、算法实现
//双调归并过程
template <typename T>
void BitonicMerge(T* pFirst,T* pLast,bool bDirection)
{
T* pTemp = pFirst;
if(pFirst < pLast)
{
int nLength = pLast - pFirst + 1; //计算数组长度
int nMid = 1;
while(nMid < nLength) //计算小于数组长度的2的最大幂次方值k
nMid = nMid << 1;
nMid = nMid >> 1;
for(int i=0;i< nLength - nMid;i++) //对元素0~n-k和元素k~n进行比较,根据升序降序标志bDirection对元素进行交换
{
if((*(pTemp+i) > *(pTemp+i+nMid)) == bDirection)
{
Swap(*(pTemp+i),*(pTemp+i+nMid));
}
}
BitonicMerge(pFirst,pFirst+nMid-1,bDirection); //递归对数组前后两部分元素进行双调递归
BitonicMerge(pFirst+nMid,pLast,bDirection);
}
}
//Bitonic排序(双调排序):属于排序网络(Sorting Network)的一种,它是一种可以并行计算的排序算法。
//首先,双调序列是指序列要么先单调递增然后再单调递减,要么先单调递减然后又单调递增。
//通过对要排序的数组构造双调序列,然后递归进行双调归并即可完成对数组的排序
template <typename T>
bool BitonicSort(T* pFirst,T* pLast,bool bDirection,Compare pFun)
{
bool bIsReverse = false;
T* pTemp = NULL;
if((pFirst == NULL) || (pLast == NULL))
{
cout<<"Input parameters error!"<<endl;
return false;
}
if(pFirst > pLast)
{
bIsReverse = true;
pTemp = pFirst;
pFirst = pLast;
pLast = pTemp;
}
if(pFirst < pLast)
{
int nNum = pLast - pFirst + 1; //计算数组长度
int nMid = nNum / 2; //计算中间元素索引值
BitonicSort(pFirst,pFirst+nMid-1,!bDirection); //对数组前半部分递归进行分裂
BitonicSort(pFirst+nMid,pLast,bDirection); //对数组后半部分递归进行分裂
BitonicMerge(pFirst,pLast,bDirection); //双调归并过程
}
if(bIsReverse)
{
while(pFirst < pLast)
{
Swap(*pFirst,*pLast);
++pFirst;
--pLast;
}
}
return true;
}
其他 排序算法及数据结构的具体实现详见GitHub:https://github.com/daiyl0320/IntroductionToAlgorithms。