我觉得之前写的快排在代码的理解上,还得研究一下,有点麻烦吧,我看了左神的视频后觉得他的代码还行:
我贴了上来:
vector<int> partition(vector<int> &arr, int lo, int hi)
{
int more = hi;
int less = lo - 1;
while (lo<more)
{
if (arr[lo] < arr[hi])
{
swap(arr[++less], arr[lo++]);
}
else if (arr[lo] > arr[hi])
{
swap(arr[lo], arr[--more]);
}
else lo++;
}
swap(arr[lo], arr[hi]);
//这个less 和 more+1 是为了防止对重复元素进行排序
return vector<int>{less, more+1};
}
void quickSort(vector<int> &arr, int lo, int hi)
{
if (lo < hi) {
swap(arr[hi], arr[lo + rand() % (hi - lo + 1)]);
vector<int> p = partition(arr, lo, hi);
quickSort(arr, lo, p[0]);
quickSort(arr, p[1], hi);
}
}
void QuickSort(vector<int> &arr)
{
if (arr.size() < 2) return;
quickSort(arr, 0, arr.size() - 1);
}
原答案:
快速排序算法是一种分治策略的典型应用;它可以在o(1)时间内,由子问题的解直接得到原问题的解,但为了将原问题划分为两个子问题,却需要o(n)的时间。
(此处借用邓俊辉老师的数据结构书中的图)
考察任一向量区间S[lo,hi),对于任何lo<=mi<hi,以元素S[mi]为界,都可分割出前,后两个子向量S[lo,mi)和S(mi,hi)。若S[lo,mi)中的元素均不大于S[mi],且S(mi,hi)中的元素均不小于S[mi],则元素S[mi]称作向量S的一个基点;于是这前后两个子向量前后独立,采用分治策略,递归的利用轴点的特性,完成原向量的排序。
于是算法就可以这样递归下去:
template <typename T>
void quickSort(int lo, int hi)
{
if (hi - lo < 2) return;
int mi = partition(lo, hi - 1);//在[lo,hi-1]内构造轴点
quickSort(lo, mi);
quickSort(mi + 1, hi);
}
于是找轴点是一个比较关键的事情;
因为并非每个向量都必然包含轴点。所以这里partition,就是为了构造出轴点;
template <typename T>
int partition(int lo, int hi, vector<int> &send)
{
swap(send[lo], send[lo + rand() % (hi - lo + 1)]); //保持选的第一个元素是随机的
T pivot = send[lo];
while (lo < hi)
{
while ((lo < hi) && (pivot <= send[hi]))
hi--;
send[lo] = send[hi]; //将小于轴点的放在轴点的左边
while ((lo < hi) && (send[lo] <= pivot))
lo++;
send[hi] = send[lo]; //将大于轴点的放在轴点的右边
}
send[lo] = pivot;
return lo; //返回轴点
}
(借用邓俊辉的数据结构的书)
所以上述查找轴点的算法的时间复杂度是与查找的区间长度成正比关系,即O( hi - lo );
复杂度:
可以看出,由于partition()算法在划分出的子任务在规模上不仅不能保证接近,而且还可能相差悬殊。如果这是一个全序的向量或者是全都一样的向量,则每次划分的子任务规模就为0和n - 1;则递推关系如下:
T(n) =T (0) + T(n - 1) + O (n)
T(n) =T ( n - 2 ) + 2 O( n ) = ......=O(n^2)
于是最坏的情况是O(n^2);
但是在最佳情况下,是从数组的中点开始分:T(n)=2*T(n/2)+n;
其时间复杂度为O(nlogn)
如果几乎所有元素都是一样的情况下,则整个算法会退化成线性递归,递归深度为O( n ),导致总体运行时间为O(n^2)
于是可以对partition()稍微进行改进
template <typename T>
int partition(int lo, int hi, vector<int> &send)
{
swap(send[lo], send[lo + rand() % (hi - lo + 1)]); //保持选的第一个元素是随机的
T pivot = send[lo];
while (lo < hi)
{
while (lo < hi)
if(pivot < send[hi])
hi--;
else { send[lo++] = send[hi]; break; } //将小于轴点的放在轴点的左边
while (lo < hi)
if (send[lo] <= pivot)
lo++;
else { send[hi--] = send[lo]; break; } //将大于轴点的放在轴点的右边
}
send[lo] = pivot;
return lo; //返回轴点
}