本文中的代码适用于对数组元素进行排序(以整型数据为例)。
自定义的方法
在排序方法之前,定义了三个不同的方法,以便于对数组元素进行比较和交换。为了适用于不同的数据类型,这里使用到了 void 指针以及指针类型的转换,也就是 C 语言中范型的概念。如果需要对不同元素类型的数组进行排序,只修改 LessArr() 方法和 Less() 方法即可。
// 比较整型数组中两个元素的大小
int LessArr(void *arr, int i, int j, int size)
{
return *(int*) (arr + i * size) < *(int*) (arr + j * size);
}
// 比较两个整型元素的大小
int Less(void *x, void *y)
{
return *(int*) x < *(int*) y;
}
// 交换数组中元素的方法
void Exch(void *arr, int i, int j, int size)
{
void * temp = malloc(size);
memcpy(temp, arr + i * size, size);
memcpy(arr + i * size, arr + j * size, size);
memcpy(arr + j * size, temp, size);
free(temp);
}
未改进的快速排序
基本快速排序
基本快速排序的切分方法中,针对于切分的标志元素(这里是数组的首元素),通过自身与数组中其他元素不断交换位置来达到切分目的。
// 基本快速排序的切分方法
int Partation_Basic(void *arr, int lo, int hi, int size)
{
int v = lo;
--lo;
++hi;
while (1)
{
while (LessArr(arr, v, --hi, size)) if (hi == v) break;
if (lo >= hi) break;
Exch(arr, hi, v, size);
v = hi;
while (!LessArr(arr, v, ++lo, size)) if (lo == v) break;
if (lo >= hi) break;
Exch(arr, lo, v, size);
v = lo;
}
return v;
}
// 基本快速排序的双参方法
void QuickSort_Basic_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Basic(arr, lo, hi, size);
QuickSort_Basic_Part(arr, lo, k - 1, size);
QuickSort_Basic_Part(arr, k + 1, hi, size);
}
// 基本快速排序的单参方法
void QuickSort_Basic(void *arr, int len, int size)
{
QuickSort_Basic_Part(arr, 0, len - 1, size);
}
后交换快速排序
后交换快速排序同样是使用了很多次交换方法,不过对于基本快速排序来说,交换次数明显减少。因为切分的标志元素总是在大循环结束之后进行的,也就是说在前面的交换里,没有将自身加进来,这样就达成了使用更少的交换次数得到同样效果的目的。
// 后交换快速排序的切分方法
int Partation_Smart(void *arr, int lo, int hi, int size)
{
int i = lo, j = hi + 1;
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
while (1)
{
while (!Less(v, arr + (++i) * size)) if (i == hi) break;
while (!Less(arr + (--j) * size, v)) if (j == lo) break;
if (i >= j) break;
Exch(arr, i, j, size);
}
Exch(arr, lo, j, size);
free(v);
return j;
}
// 后交换快速排序的双参方法
void QuickSort_Smart_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Smart(arr, lo, hi, size);
QuickSort_Smart_Part(arr, lo, k - 1, size);
QuickSort_Smart_Part(arr, k + 1, hi, size);
}
// 后交换快速排序的单参方法
void QuickSort_Smart(void *arr, int len, int size)
{
QuickSort_Smart_Part(arr, 0, len - 1, size);
}
非交换快速排序
非交换快速排序中并没有使用到前面的交换方法,它是通过事先保存切分标志元素的内容,重复进行前后赋值来实现的。
// 非交换快速排序的切分方法
int Partation_Hole(void *arr, int lo, int hi, int size)
{
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
++hi;
while (1)
{
while (!Less(arr + (--hi) * size, v)) if (lo >= hi) break;
if (lo >= hi) break;
memcpy(arr + lo * size, arr + hi * size, size);
while (!Less(v, arr + (++lo) * size)) if (lo >= hi) break;
if (lo >= hi) break;
memcpy(arr + hi * size, arr + lo * size, size);
}
memcpy(arr + lo * size, v, size);
free(v);
return lo;
}
// 非交换快速排序的双参方法
void QuickSort_Hole_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Hole(arr, lo, hi, size);
QuickSort_Hole_Part(arr, lo, k - 1, size);
QuickSort_Hole_Part(arr, k + 1, hi, size);
}
// 非交换快速排序的单参方法
void QuickSort_Hole(void *arr, int len, int size)
{
QuickSort_Hole_Part(arr, 0, len - 1, size);
}
乱序处理
为了确保数组中数据的随机性,我们可以对数组进行乱序处理。或者随机在数组中选取一个元素作为枢轴(pivot)来对数组进行划分。
三取样切分快速排序
三取样切分也就是三位取中的方法,一般是将数组的首、尾以及中间元素的中位数作为切分元素。为了达到更好的切分效果,也可以选择随机在数组中寻找三个元素,用它们的中位数元素作为枢轴(pivot)进行数组元素的切分。这里我选择了将首、尾以及中三个元素进行比较,将中位元素置于首位,再调用之前完成的未改进方法。
// 三取样切分优化(对数组内容进行操作,置中位元素于首位)
void MedianOf_Three(void *arr, int lo, int hi, int size)
{
int mid = lo + (hi - lo) / 2;
if (LessArr(arr, lo, mid, size)) Exch(arr, mid, lo, size);
if (LessArr(arr, hi, mid, size)) Exch(arr, mid, lo, size);
if (LessArr(arr, hi, lo, size)) Exch(arr, lo, hi, size);
}
// 三取样切分改进
int Partation_Three(void *arr, int lo, int hi, int size)
{
MedianOf_Three(arr, lo, hi, size);
return Partation_Smart(arr, lo, hi, size);
}
// 三取样切分优化的双参方法
void QuickSort_Three_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Three(arr, lo, hi, size);
QuickSort_Three_Part(arr, lo, k - 1, size);
QuickSort_Three_Part(arr, k + 1, hi, size);
}
// 三取样切分优化的单参方法
void QuickSort_Three(void *arr, int len, int size)
{
QuickSort_Three_Part(arr, 0, len - 1, size);
}
三向切分快速排序(熵最优的排序)
在实际应用中可能会出现数据重复次数很多的情况,这就具有很大的改进潜力,我们可以将当前实现的线性对数级的性能提高到线性级别。一个简单的思路就是将数组切分为三个部分。
// 三向切分快速排序的双参方法
void QuickSort_ThreeWay_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int lt = lo, i = lo + 1, gt = hi;
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
while (i <= gt)
{
if (Less(arr + i * size, v)) Exch(arr, lt++, i++, size);
else if (Less(v, arr + i * size)) Exch(arr, gt--, i, size);
else ++i;
}
QuickSort_ThreeWay_Part(arr, lo, lt - 1, size);
QuickSort_ThreeWay_Part(arr, gt + 1, hi, size);
}
// 三向切分快速排序的单参方法
void QuickSort_ThreeWay(void *arr, int len, int size)
{
QuickSort_ThreeWay_Part(arr, 0, len - 1, size);
}
排序方法的调用
int main(int argc, char *argv[])
{
int arr[] = {3,1,2,6,9,4,-1,-1,-2,11,23,12,18};
int len;
GET_ARRAY_LEN(arr, len);
// QuickSort_Basic(arr, len, sizeof(int));
// QuickSort_Smart(arr, len, sizeof(int));
// QuickSort_Hole(arr, len, sizeof(int));
// QuickSort_Three(arr, len, sizeof(int));
// QuickSort_ThreeWay(arr, len, sizeof(int));
for (int i = 0; i < len; ++i) printf("%d ", arr[i]);
printf("\n");
}
完整代码
#include
#include
#include
#define GET_ARRAY_LEN(arr,len) { len = (sizeof(arr) / sizeof(arr[0])); }
// 比较整型数组中两个元素的大小
int LessArr(void *arr, int i, int j, int size)
{
return *(int*) (arr + i * size) < *(int*) (arr + j * size);
}
// 比较两个整型元素的大小
int Less(void *x, void *y)
{
return *(int*) x < *(int*) y;
}
// 交换数组中元素的方法
void Exch(void *arr, int i, int j, int size)
{
void * temp = malloc(size);
memcpy(temp, arr + i * size, size);
memcpy(arr + i * size, arr + j * size, size);
memcpy(arr + j * size, temp, size);
free(temp);
}
/* ----------------------------------------------- */
// 基本快速排序的切分方法
int Partation_Basic(void *arr, int lo, int hi, int size)
{
int v = lo;
--lo;
++hi;
while (1)
{
while (LessArr(arr, v, --hi, size)) if (hi == v) break;
if (lo >= hi) break;
Exch(arr, hi, v, size);
v = hi;
while (!LessArr(arr, v, ++lo, size)) if (lo == v) break;
if (lo >= hi) break;
Exch(arr, lo, v, size);
v = lo;
}
return v;
}
// 基本快速排序的双参方法
void QuickSort_Basic_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Basic(arr, lo, hi, size);
QuickSort_Basic_Part(arr, lo, k - 1, size);
QuickSort_Basic_Part(arr, k + 1, hi, size);
}
// 基本快速排序的单参方法
void QuickSort_Basic(void *arr, int len, int size)
{
QuickSort_Basic_Part(arr, 0, len - 1, size);
}
/* ----------------------------------------------- */
// 后交换快速排序的切分方法
int Partation_Smart(void *arr, int lo, int hi, int size)
{
int i = lo, j = hi + 1;
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
while (1)
{
while (!Less(v, arr + (++i) * size)) if (i == hi) break;
while (!Less(arr + (--j) * size, v)) if (j == lo) break;
if (i >= j) break;
Exch(arr, i, j, size);
}
Exch(arr, lo, j, size);
free(v);
return j;
}
// 后交换快速排序的双参方法
void QuickSort_Smart_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Smart(arr, lo, hi, size);
QuickSort_Smart_Part(arr, lo, k - 1, size);
QuickSort_Smart_Part(arr, k + 1, hi, size);
}
// 后交换快速排序的单参方法
void QuickSort_Smart(void *arr, int len, int size)
{
QuickSort_Smart_Part(arr, 0, len - 1, size);
}
/* ----------------------------------------------- */
// 非交换快速排序的切分方法
int Partation_Hole(void *arr, int lo, int hi, int size)
{
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
++hi;
while (1)
{
while (!Less(arr + (--hi) * size, v)) if (lo >= hi) break;
if (lo >= hi) break;
memcpy(arr + lo * size, arr + hi * size, size);
while (!Less(v, arr + (++lo) * size)) if (lo >= hi) break;
if (lo >= hi) break;
memcpy(arr + hi * size, arr + lo * size, size);
}
memcpy(arr + lo * size, v, size);
free(v);
return lo;
}
// 非交换快速排序的双参方法
void QuickSort_Hole_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Hole(arr, lo, hi, size);
QuickSort_Hole_Part(arr, lo, k - 1, size);
QuickSort_Hole_Part(arr, k + 1, hi, size);
}
// 非交换快速排序的单参方法
void QuickSort_Hole(void *arr, int len, int size)
{
QuickSort_Hole_Part(arr, 0, len - 1, size);
}
/* ----------------------------------------------- */
// 三取样切分优化(对数组内容进行操作,置中位元素于首位)
void MedianOf_Three(void *arr, int lo, int hi, int size)
{
int mid = lo + (hi - lo) / 2;
if (LessArr(arr, lo, mid, size)) Exch(arr, mid, lo, size);
if (LessArr(arr, hi, mid, size)) Exch(arr, mid, lo, size);
if (LessArr(arr, hi, lo, size)) Exch(arr, lo, hi, size);
}
// 三取样切分改进
int Partation_Three(void *arr, int lo, int hi, int size)
{
MedianOf_Three(arr, lo, hi, size);
return Partation_Smart(arr, lo, hi, size);
}
// 三取样切分优化的双参方法
void QuickSort_Three_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int k = Partation_Three(arr, lo, hi, size);
QuickSort_Three_Part(arr, lo, k - 1, size);
QuickSort_Three_Part(arr, k + 1, hi, size);
}
// 三取样切分优化的单参方法
void QuickSort_Three(void *arr, int len, int size)
{
QuickSort_Three_Part(arr, 0, len - 1, size);
}
/* ----------------------------------------------- */
// 三向切分快速排序的双参方法
void QuickSort_ThreeWay_Part(void *arr, int lo, int hi, int size)
{
if (lo >= hi) return;
int lt = lo, i = lo + 1, gt = hi;
void * v = malloc(size);
memcpy(v, arr + lo * size, size);
while (i <= gt)
{
if (Less(arr + i * size, v)) Exch(arr, lt++, i++, size);
else if (Less(v, arr + i * size)) Exch(arr, gt--, i, size);
else ++i;
}
QuickSort_ThreeWay_Part(arr, lo, lt - 1, size);
QuickSort_ThreeWay_Part(arr, gt + 1, hi, size);
}
// 三向切分快速排序的单参方法
void QuickSort_ThreeWay(void *arr, int len, int size)
{
QuickSort_ThreeWay_Part(arr, 0, len - 1, size);
}
/* ----------------------------------------------- */
int main(int argc, char *argv[])
{
int arr[] = {3,1,2,6,9,4,-1,-1,-2,11,23,12,18};
int len;
GET_ARRAY_LEN(arr, len);
// QuickSort_Basic(arr, len, sizeof(int));
// QuickSort_Smart(arr, len, sizeof(int));
// QuickSort_Hole(arr, len, sizeof(int));
// QuickSort_Three(arr, len, sizeof(int));
// QuickSort_ThreeWay(arr, len, sizeof(int));
for (int i = 0; i < len; ++i) printf("%d ", arr[i]);
printf("\n");
}
本次文章的分享就到这里了,希望对大家有帮助!
自学C/C++编程难度很大,不妨和一些志同道合的小伙伴一起学习成长!
C语言C++编程学习交流圈子,【点击进入】微信公众号:C语言编程学习基地
有一些源码和资料分享,欢迎转行也学习编程的伙伴,和大家一起交流成长会比自己琢磨更快哦!