C/C++编程笔记:快速排序的思路与优化改进(C 语言描述)

本文中的代码适用于对数组元素进行排序(以整型数据为例)。

自定义的方法

在排序方法之前,定义了三个不同的方法,以便于对数组元素进行比较和交换。为了适用于不同的数据类型,这里使用到了 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语言编程学习基地

有一些源码和资料分享,欢迎转行也学习编程的伙伴,和大家一起交流成长会比自己琢磨更快哦!

你可能感兴趣的:(C语言,数据结构,C语言,C语言算法,快速排序,学习编程,编程知识)