void QuickSort(int *arr, int begin, int end)
{
assert(arr);
int div = PartSort1(arr, begin, end);
if (div - 1>begin)
PartSort1(arr, begin, div - 1);
if (div + 1<end)
PartSort1(arr, div + 1, end);
}
快速排序是一种划分交换的方法,它采用分治法进行排序。
快速排序的基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列,直到所有元素都排在相应的位置(即序列中只有一个元素或者没有元素的时候)。
方法一:左右指针法
一个指针从头开始,一个指针从尾开始,选取尾的数作为key值。两个指针分别开始走,头指针遇到比key值大的就停下来,尾指针遇到比key值小的就停下来,头指针停下来的数和尾指针停下来的数进行交换。交换之后头尾指针分别继续走,直至头指针大于尾指针则本次循环结束。此时,头指针指向的数一定大于key值,头指针指向的数与key指向的数交换。
(在进行比较的时候一定要至少一个加上等号否则如果相等头指针和尾指针都不走了,就会陷入死循环)
如上图,为第一次排序的过程,第一次排序之后,key所指向的5左侧所有的数均比5小,右侧所有的数均比5大。
int PartSort1(int* arr, int begin, int end)
{
int key = arr[end];
int left = begin;
int right = end;
while (left < right)
{
while (left < right && arr[left] < key)
{
++left;
}
while (left < right && arr[right] >= key)
{
--right;
}
swap(arr[left], arr[right]);
}
swap(arr[left], arr[end]);
return begin;
}
方法二:挖坑法
挖坑法是先找一个值,将其保存起来,这样这个值的位置就形成一个坑,可以随意覆盖掉了。
由于坑在left的位置,所以我们从right开始找比key小的值。将这个值放入key刚才所在的坑里,此时形成一个新的坑。
此时坑在right的位置,从left找一个比key大的值放入坑内。
right不断减,最终与left相遇,则把key放入坑内。此时第一趟排序结束,key值左侧的值均比key小,右侧的值均比key大。 再对子序列重复上述操作,直到划分的子序列中元素的个数只有一个或者没有的时候才停止。
int PartSort2(int* arr, int begin, int end)
{
int key = arr[begin];
int left = begin;
int right = end;
while (left < right)
{
while (left < right&&arr[right] >= key)
{
--right;
}
arr[left] = arr[right];
while (left < right&&arr[left] <= key)
{
++left;
}
arr[right] = arr[left];
}
arr[left] = key;
return left;
}
方法三:前后指针法
一个prev指针和一个cur指针。prev指向cur的前一个。
cur向后移动,如果arr[cur]比key值小就停下来,++prev再判断如果prev!=cur就交换,交换之后cur就继续走。如果大于key就直接++cur。
等到cur遍历完整个数组,++prev,然后将arr[prev]和arr[end]交换
int PartSort3(int* arr, int begin, int end)
{
int key = arr[end];
int cur = begin;
int prev = begin - 1;
while (cur < end)
{
if(arr[cur] < key)
{
++prev;
if (prev != cur)
{
swap(arr[cur], arr[prev]);
}
}
++cur;
}
++prev;
swap(arr[prev], arr[end]);
return prev;
}
小区间优化法
思想:假如划分的区间小于13,那么我们认为不需要用快排序来实现,因为递归也是需要付出代价的,这样反而效率低,因此我们选择直接插入排序来解决。
三数取中法
由于快速排序可能在划分的时候它的end-begin可能是最大值或者最小值也可能是接近于最大值或最小值,而在这种情况下快速排序它的时间复杂度就可能接近于O(N2)为了避免这种情因此我们都选择三个数里面最中间的那个数。
int GetMiddle(int*arr, int left, int right)
{
int mid = left + ((right - left) >> 1);
if (arr[left] < arr[right])
{
if (arr[right] < arr[mid])
return right;
else
{
if (arr[left] < arr[mid])
return mid;
else
return left;
}
}
else//left>right
{
if (arr[right] > arr[mid])
return right;
else
{
if (arr[left] > arr[mid])
return mid;
else
return left;
}
}
}
快速排序非递归
借助栈,通过压栈出栈操作,实现快速排序的非递归。
void QuickSortNR(int *arr, int begin, int end)
{
assert(arr);
stack<int> s;
s.push(begin);
s.push(end);
while (!s.empty())
{
int left = s.top();
s.pop();
int right = s.top();
s.pop;
int div = PartSort3(arr, left, right);
if (div - 1 > left)
{
s.push(div - 1);
s.push(left);
}
if (div + 1 < right)
{
s.push(right);
s.push(div + 1);
}
}
}