一.朴素版
1.一层循环
快排的每一层循环就是选出一个key值将其放到正确的位置(也就是放到已经完全有序时的位置)。
例如以上有一组数据,我们选择6为key值,这次循环我们要将6放到正确的位置。用一个left指针和一个right指针,分别指向左右两端,left指针向右走,遇到大于key值的数就停下,然后right指针向左走,遇到小于key值的数就停下。之后两指针所对应的数交换位置,直到两指针相遇,交换left所指向的数和key值,一次循环停止。就得到key数左边的数都小于等于key,右边的数都大于等于key。
此时一次循环结束6左边的数都小于等于6,右边的数都大于等于6。(一般令最左边的数为key值)
2.递归
每次循环找到一个数,那么依次递归当left>=right,也就是这次递归里已经没有元素时就说明排序完成。
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
int begin = left, end = right;
int key = left;
while (right > left)
{
while (left<right&&a[right] >= a[key])
right--;
while (left<right&&a[left] <= a[key])
left++;
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[key]);
key = left;
QuickSort(a, begin,key-1);
QuickSort(a, key + 1, end);
}
二.改进(选取key值)
缺陷
如果改数据已经是升序或者逆序的话,我们每次选最左边为key值,那么每次递归只能减少一个数(无法做到理想情况下的二分)。如果有100w个数,就容易造成栈溢出,所以需要改进。
1.随机数取法
其实快排的缺陷主要是由key值的选取造成的,那么我们可以随机选key值,就可以避免最坏的情况。
2.三路取中
选随机数是可行的,但有人认为太过于不确定了,所以又出炉了一个新方法,取中间(max>a[mid]>min)的坐标。
3.前后指针法
简单来说就是一个cur指针从最左边走起,pre初始位置在cur+1位置,pre向后走,如果a[pre]<a[key],就交换a[++cur]和a[pre],直到pre走到最右边。
源代码
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int GetMid(int* a, int left, int right)
{
int mid = (left + right) / 2;
if (a[mid] > a[left])
{
if (a[mid] < a[right])
return mid;
else if (a[left] > a[right])
return left;
else
return right;
}
else
{
if (a[mid] > a[right])
return mid;
else if (a[left] < a[right])
return left;
else
return right;
}
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
//int begin = left, end = right;
//随机选key
//int randi =left+ rand() % (right - left);//让产生的随机数在left和right之间
//Swap(&a[randi], &a[left]);//为了避免扰乱后面的排序,我们将其调到最左边的位置
//三数取中
int mid = GetMid(a, left, right);
Swap(&a[mid], &a[left]);
/*int key = left;
while (right > left)
{
while (left<right&&a[right] >= a[key])
right--;
while (left<right&&a[left] <= a[key])
left++;
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[key]);
key = left;
QuickSort(a, begin,key-1);
QuickSort(a, key + 1, end);*/
//前后指针法
int cur = left, pre = cur + 1,key=left;
while (pre<=right)
{
if (a[pre] < a[key])
{
cur++;
Swap(&a[pre], &a[cur]);
}
pre++;
}
Swap(&a[cur], &a[key]);
key = cur;
QuickSort(a, left, key-1);
QuickSort(a, key + 1, right);
}
int main()
{
int a[20000];
srand((unsigned int)time(NULL));
for (int i = 0; i < 20000; i++)
{
a[i] = rand() % 1000;
}
QuickSort(a,0,19999);
int st = 0;
for (int i = 0; i < 19998; i++)
{
if (a[i] > a[i + 1])
{
printf("%d->%d,%d\n", a[i], a[i + 1], i);
st = 1;
break;
}
}
if (st)
{
printf("out of order");
}
else
{
printf("order");
}
}
三,小区间(减少递归)
我们知道快排是按二叉树形式排列的,而在理想情况下,最后一层递归就占总递归的二分之一。实际上当每个子树只剩下10个数时,我们就可以考虑不再递归改用插入排序,那么这样(在理想情况下)就可以去除最后三层递归,也就会减少87.5%的递归。
不了解插入排序的可以看看这篇博客插入排序
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int GetMid(int* a, int left, int right)
{
int mid = (left + right) / 2;
if (a[mid] > a[left])
{
if (a[mid] < a[right])
return mid;
else if (a[left] > a[right])
return left;
else
return right;
}
else
{
if (a[mid] > a[right])
return mid;
else if (a[left] < a[right])
return left;
else
return right;
}
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
//int begin = left, end = right;
//随机选key
//int randi =left+ rand() % (right - left);//让产生的随机数在left和right之间
//Swap(&a[randi], &a[left]);//为了避免扰乱后面的排序,我们将其调到最左边的位置
//三数取中
int mid = GetMid(a, left, right);
Swap(&a[mid], &a[left]);
/*int key = left;
while (right > left)
{
while (left<right&&a[right] >= a[key])
right--;
while (left<right&&a[left] <= a[key])
left++;
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[key]);
key = left;
QuickSort(a, begin,key-1);
QuickSort(a, key + 1, end);*/
//前后指针法
if (right - left + 1 > 10)
{
int cur = left, pre = cur + 1, key = left;
while (pre <= right)
{
if (a[pre] < a[key])
{
cur++;
Swap(&a[pre], &a[cur]);
}
pre++;
}
Swap(&a[cur], &a[key]);
key = cur;
QuickSort(a, left, key - 1);
QuickSort(a, key + 1, right);
}
else//插入排序
{
for (int i = 1; i < 10; i++)
{
int end = i - 1;
int tmp = a[i];
while (end >= 0)
{
if (a[end] > tmp)
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
}
int main()
{
int a[20000];
srand((unsigned int)time(NULL));
for (int i = 0; i < 20000; i++)
{
a[i] = rand() % 1000;
}
QuickSort(a,0,19999);
int st = 0;
for (int i = 0; i < 19998; i++)
{
if (a[i] > a[i + 1])
{
printf("%d->%d,%d\n", a[i], a[i + 1], i);
st = 1;
break;
}
}
if (st)
{
printf("out of order");
}
else
{
printf("order");
}
}
四.非递归
使用栈先进后出来模拟实现深度优先遍历。如果不了解栈可以看看这篇博客栈
#include"stack.h"
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void Swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int GetMid(int* a, int left, int right)
{
int mid = (left + right) / 2;
if (a[mid] > a[left])
{
if (a[mid] < a[right])
return mid;
else if (a[left] > a[right])
return left;
else
return right;
}
else
{
if (a[mid] > a[right])
return mid;
else if (a[left] < a[right])
return left;
else
return right;
}
}
int PartSort(int* a, int left, int right)
{
// 三数取中
int midi = GetMid(a, left, right);
if (midi != left)
Swap(&a[midi], &a[left]);
int keyi = left;
int prev = left;
int cur = left + 1;
while (cur <= right)
{
if (a[cur] < a[keyi] && ++prev != cur)
Swap(&a[cur], &a[prev]);
++cur;
}
Swap(&a[prev], &a[keyi]);
keyi = prev;
return keyi;
}
void QuickSort(int* a, int left, int right)
{
ST st;
STInit(&st);
STPush(&st, right);
STPush(&st, left);
while (!STEmpty(&st))
{
int begin = STTop(&st);
STPop(&st);
int end = STTop(&st);
STPop(&st);
int keyi = PartSort(a, begin, end);
// [begin,keyi-1] keyi [keyi+1, end]
if (keyi + 1 < end)
{
STPush(&st, end);
STPush(&st, keyi + 1);
}
if (begin < keyi - 1)
{
STPush(&st, keyi - 1);
STPush(&st, begin);
}
}
STDestroy(&st);
}
int main()
{
/*int a[11] = {3,3,8,7,6,5,4,3,2,1,0};
QuickSort(a, 0, 10);
for (int i = 0; i < 10; i++) printf("%d ", a[i]);*/
int a[200000];
srand((unsigned int)time(NULL));
for (int i = 0; i < 200000; i++)
{
a[i] = rand() % 1000;
}
QuickSort(a, 0, 199999);
int st = 0;
for (int i = 0; i < 199998; i++)
{
if (a[i] > a[i + 1])
{
printf("%d->%d,%d\n", a[i], a[i + 1], i);
st = 1;
break;
}
}
if (st)
{
printf("out of order");
}
else
{
printf("order");
}
}