快速排序算法(三种分区方法要熟练!)
快排确实厉害!!!
总的思想是分治递归,取定一个值作为标签,比该值小的去左边,比该值大的去右边。
单向扫描分区法:

去左边的操作:只将sp++即可。
去右边的操作:具体是将sp指向的值与bigger指向的值交换。
考虑边界:当扫描指针sp与bigger相等时,再执行一次循环后,sp刚好在bigger的右边一格。

/*
* 快速排序算法
*/
void swp(int arr[], int n, int m)
{
int temp = arr[n];
arr[n] = arr[m];
arr[m] = temp;
}
int Part(int arr[], int p, int r)
{
int pivot = arr[p]; // 定中间数
int sp = p + 1; // 扫描指针
int bigger = r; // 右侧指针
while (sp <= bigger)
{
if (arr[sp] > pivot) {
swp(arr, sp, bigger);
bigger--;
}
else
sp++;
}
swp(arr, p, bigger);
return bigger;
}
void quickSort(int arr[], int p, int r)
{
int q = 0;
if (p < r) {
// 分区:小于的数移动到左边,大于的数移动到右边
q = Part(arr, p, r);
// 将排序问题分治
quickSort(arr, p, q - 1);
quickSort(arr, q + 1, r);
}
}
int main()
{
int ar[2] = {1, -1};
quickSort(ar, 0, 1);
// 打印
for (int i = 0; i <= 1; i++)
cout << ar[i] << endl;
return 0;
}双向扫描分区法:
与单向扫描分区类似,但left指针一直往右移,直到大于中间值时停止;right指针一直往左移,直到小于中间值时停止。然后left值与right值交换。之后left继续一直左移,right一直右移。重复执行。
小心:left一直左移(或right一直右移)导致的数组越界问题。
/*
* 快速排序算法
*/
void swp(int arr[], int n, int m)
{
int temp = arr[n];
arr[n] = arr[m];
arr[m] = temp;
}
int Part(int arr[], int p, int r)
{
int pivot = arr[p]; // 定中间数
int left = p + 1; // 左指针
int right = r; // 右指针
// 双向扫描核心
while (left <= right)
{
while (left <= right && arr[left] <= pivot) left++;
while (left <= right && arr[right] > pivot) right--;
if (left < right)
swp(arr, left, right);
}
swp(arr, p, right);
return right;
}
void quickSort(int arr[], int p, int r)
{
int q = 0;
if (p < r) {
// 分区:小于等于的数移动到左边,大于的数移动到右边
q = Part(arr, p, r);
// 将排序问题分治
quickSort(arr, p, q - 1);
quickSort(arr, q + 1, r);
}
}
int main()
{
int ar[8] = {2, 3, 3, 3, 45, 8, 4, 6};
quickSort(ar, 0, 7);
// 打印
for (int i = 0; i <= 7; i++)
cout << ar[i] << " ";
return 0;
}三指针分区法:

三指针分区法对于相等元素较多的数组能提升一定的效率。
Next_Less_Pos指针始终指向数组的相等区第一个元素;Next_Scan_Pos指针始终指向要扫描区域的第一个元素;Next_Bigger_Pos指针的右区域所有元素总大于主元。

/*
* 快速排序算法
*/
void swp(int arr[], int n, int m)
{
int temp = arr[n];
arr[n] = arr[m];
arr[m] = temp;
}
void Part(int arr[], int p, int &e, int &bigger)
{
int pivot = arr[p]; // 定中间数
int s = e;
// 三指针分区核心
while (s <= bigger) {
if (arr[s] < pivot) {
swp(arr, s, e);
s++; e++;
}
else if (arr[s] > pivot) {
swp(arr, s, bigger);
bigger--;
}
else s++;
}
swp(arr, e - 1, p);
}
void quickSort(int arr[], int p, int r)
{
int left = p + 1, right = r;
if (p < r) {
// 分区:小于的数移动到左边,大于的数移动到右边,等于的数在中间
Part(arr, p, left, right);
// 将排序问题分治
if (left > p) quickSort(arr, p, left - 1);
if (right < r) quickSort(arr, right + 1, r);
}
}
int main()
{
int ar[20];
srand((unsigned)time(nullptr));
for (int i = 0; i <= 19; i++)
ar[i] = rand() % (10 - 1 + 1) + 1;
quickSort(ar, 0, 19);
// 打印
for (int i = 0; i <= 19; i++)
cout << ar[i] << " ";
return 0;
}小心边界:必须是相等区域的前一个小于元素,和相等区域的后一个大于元素(即 left - 1 和 right + 1),这时能避免无限循环。
但在避免无限循环的同时,又得保证边界下标的合理性,故我们此时加 if 语句判断。