- 快速排序
一次partion的过程.png
2.png
int Partion(vector<int> &vec, int lo, int hi)
{
int i = lo;
int j = hi;
int targetNum = vec[lo];
while(true)
{
while(vec[i] <= targetNum && i < hi) // 找到大于 targetNum
i++;
while(vec[j] >= targetNum && j > lo) // 找到小于 targetNum
j--;
if (i < j)
swap(vec[i], vec[j]);
else
break;
}
swap(vec[j], vec[lo]);
return j;
}
void QuickSort(vector<int> &vec, int lo, int hi)
{
if (lo < hi)
{
int j = Partion(vec, lo, hi);
QuickSort(vec, lo, j-1);
QuickSort(vec, j+1, hi);
}
}
-
优化
三点中值
快速排序的效率与切分有很大关系,在选择枢轴时,我们可以采用取整 个序列的头,尾,中央3个位置的元素,再以其中值最为枢轴,这种做法被称为median-of-three partitioning。加入插入排序
在快速排序块要完成的时候,这时候序列已经大部分已经处于有序的状态,这时候我们再采用插入排序是会有更高的效率。STL中的sort即是采用此方法。
应用1:数组中出现次数超过一半的数字
数组中有1个数字出现的次数超过数组长度的一半,那么假设把这个数组排序,那么排序之后的位于数组中间的数字一定就是那个出现次数超过数组长度一半的数字。(中位数)
受快速排序partion的启发,我们先选随机选择一个数执行一次partion。
如果它的下标等于n/2,那么它就是中位数。
如果它的下标小于n/2,那么中位数一定在右半边,继续在它的右半边查找。
如果它的下标大于n/2,那么中位数一定在左半边,继续在它的左半边查找。
图示.png
/* 以下假定存在出现次数超过一半的数字 */
int MoreThanHalfNum(vector<int> vec, int middle)
{
int j = Partion(vec, 0, vec.size()-1);
while(j != middle)
{
if (j > middle)
j = partion(vec, 0, j-1);
else
j = partion(vec, j+1, vec.size()-1);
}
return vec[j];
}
应用2:第k大的数
受上题的启发,我们发现Partion之后,就可以确定第k大的数(j)。
/* 从0开始排名 */
int Knums(vector<int> vec, int k)
{
if (k < 0 || k >= vec.size())
throw ("参数超出范围");
int j = partion(vec, 0, vec.size()-1);
if (k == j)
return vec[j];
while(j != k)
{
if (j > k)
j = partion(vec, 0, j-1);
else
j = partion(vec, j+1, vec.size()-1);
}
return vec[j];
}
应用3:最小的k个数
同样受上题的启发,经过一次Partion之后,枢轴前面的数比它小,枢轴后面的数比它大。