快速排序也是一种基于分治法的排序方法,简单的说,就是每次从序列中找出一个关键字,然后利用Partition的过程,将这个关键字放置到它在最终排好序的序列中的最终位置,并且保证在该位置左边的元素都小于它,右边的元素都大于等于它。然后递归的使用快速排序对左右两个子序列继续排序,直到子序列大小为1或者为空。
快速排序的速度取决于每次选出的那个关键字(pivot),简单的做法是取第一个元素,麻烦点的做法是取中间位置的元素。
代码如下:
void QuickSort(int* pData,int length)
{
DoQuickSort(pData,0,length-1);
}
void DoQuickSort(int* pData,int low,int high)
{
if(low >= high)
return;
int pivotIndex = FindPivotIndex(pData,low,high);
if(pivotIndex != low)
{
int temp = pData[low];
pData[low] = pData[pivotIndex];
pData[pivotIndex] = temp;
}
int k = QuickSortPartition(pData,low,high);
DoQuickSort(pData,low,k-1);
DoQuickSort(pData,k+1,high);
}
int FindPivotIndex(int* pData,int low,int high)
{
int middle = (low+high) / 2;
if((pData[low] >= pData[middle] && pData[low] <= pData[high]) ||(pData[low] >= pData[high] && pData[low] <= pData[middle]))
{
return low;
}
else if((pData[middle] >= pData[low] && pData[middle] <= pData[high]) ||(pData[middle] >= pData[high] && pData[middle] <= pData[low]))
{
return middle;
}
else
{
return high;
}
}
int QuickSortPartition(int* pData,int low,int high)
{
int pivotValue = pData[low];
while(low<high)
{
while(low<high && pData[high] >= pivotValue)
--high;
pData[low] = pData[high];
while(low<high && pData[low] <= pivotValue)
++low;
pData[high] = pData[low];
}
if(low != high)
{
throw logic_error("low is not equal to high");
}
else
{
pData[low] = pivotValue;
}
return low;
}
主要的算法就是Partition方法里面,Partition的方法有很多,上面的方法是,先将low元素保存在pivot临时变量中,方便在最后插入到合适的位置,然后开始以low和high为下标,从两边向中间来和pivot值比较。比较的思路是,low的左边的所有元素都是小于或者等于pivot的元素,high右边的元素都是大于等于pivot的值,low和high之间的元素是还没有比较过的部分,而low或者high中,总是有一个位置是一个空位,即可以用来放置pivot的候选位置,而另一个所指的就是当前需要和pivot比较的位置。low和high逐渐的向中间靠拢,直到最后low和high指向同一个位置而退出循环,那个位置就是pivot的最后位置。
下面是另外一个Partition的方式:
int QuickSortPartition2(int* start,int low,int high)
{
int pivot = start[low];
int i;
int lastSmall = low;
int temp;
for(i=low+1;i<=high;++i)
{
if(start[i] < pivot)
{
++lastSmall;
temp = start[i];
start[i] = start[lastSmall];
start[lastSmall] = temp;
}
}
temp = start[lastSmall];
start[lastSmall] = start[low];
start[low] = temp;
return lastSmall;
}
这种方式的循环不变式,是lastSmall指向最后一个小于pivot的元素,它的左边直到low都是小于pivot的元素。i指向第一个没有比较过的元素,它的右边直到high都是没有比较过的,而i的左边到lastSmall的右边这中间的元素就是大于等于pivot的元素。当发现i元素小于pivot,那就lastSmall增加1从而指向第一个大于等于pivot的元素,然后交换i和lastSmall元素,然后递增i,从而恢复循环不变式。当循环结束后,互换lastSmall和low位置的元素,就可以了。起始时,lastSmall指向low也就是pivot,表示小于pivot的序列式空,i指向low+1,代表从low+1到high位置的所有元素都是未比较的,i和lastSmall之间没有元素,因此大于等于pivot的序列也是空的,因此满足循环不变式。