按照自己的理解,分区算法partition的核心在于:索引的变换和交换元素,也就是说按照索引将不同的元素交换到不同的区间。快排中的partition只划分了2个区间:小等于以及大于。在这里,我们划分3个区间:小于、等于和大于,若给定数组arr{1,2,3,4,0,2},最后一个2设为主元nPivot,则经过分区后的数组也许为arr{1,0,2,2,4,3}(先不管相同区间的元素顺序)。
划分3个区间的思路(以上面的数组为例):设2个索引值,分别指向0和第2个2,记为nBeforeEqual和nBeforeLarge,同时用i遍历整个数组。
1.若arr[i] > nPivot,啥都不做。
2.若arr[i] == nPivot,交换arr[nBeforeLarge+1]和arr[i],若数字序列为1,0,4,2,此时i指向2,nBeforeLarge指向0,则交换4和2即可,变成1,0,2,4。
3.若arr[i] < nPivot, 先交换arr[nBeforeLarge+1]和arr[i],再交换arr[nBeforeLarge+1]和arr[nBeforeEqual+1],也就是说先把小元素交换到等于这个区间,再交换到小于这个区间,若数字序列为1,2,4,0,此时i指向0,nBeforeLarge指向2,nBeforeEqual指向1,先交换0和4,变成1,2,0,4,再交换2和0,变成1,0,2,4。
最后,再交换arr[nBeforeEqual+1]和nPivot,把主元交换到等于区间,完成分区。
代码如下:
<span style="font-size:24px;">template<typename T>
CNumPair partition(T* pArr, int nLeft, int nRight)
{
int nPivot = pArr[nRight];
int nBeforeEqual = nLeft-1;
int nBeforeLarge = nLeft-1;
for (int i = nLeft; i <= nRight; ++i)
{
if (pArr[i] < nPivot)
{
++nBeforeLarge;
swap(pArr+nBeforeLarge, pArr+i);
++nBeforeEqual;
swap(pArr+nBeforeLarge, pArr+nBeforeEqual);
}
else if (pArr[i] == nPivot)
{
++nBeforeLarge;
swap(pArr+nBeforeLarge, pArr+i);
}
}
swap(pArr+nBeforeLarge+1, pArr+nRight);
return CNumPair(nBeforeEqual+1, nBeforeLarge+1);
}</span>
同样,如果需要划分4个,5个区间,只需要多设几个索引值,每遍历一个元素,逐渐交换,一直换到对应的区间。
OVER