快速排序法及其优化版本(详解)

版权声明:转用请注明出处 https://blog.csdn.net/weixin_39411321/article/details/89212212

基本思想:

快速排序算法的基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

可能从字面感觉不到他的好处,我们通过例子来学习他的精妙之处;
例如:假设我们要对数组{50,10,90,30,70,40,80,60,20}进行排序
我们先来看代码:

快速排序法的普通版本

int Partition(SeqList*L, int low, int high)
{
	int pivotkey;
	pivotkey = L->data[low];
	while (low<high)
	{
		while (low<high&&L->data[high]>pivotkey)
		{
			high--;
		}
		swap(pivotkey, L->data[high]);
		while (low < high&&L->data[low] < pivotkey)
		{
			low++;
		}
		swap(pivotkey, L->data[low]);
	}
	return low;
}
void Qsort(SeqList*L,int low,int high)
{
	int pivot;
	if (low < high)
	{
		pivot = Partition(L, low, high);
		Qsort(L, low, pivot - 1);
		Qsort(L, pivot + 1, high);
	}
}
void Quicksort(SeqList *L)
{
	Qsort(L, 1, L->cursize);
}

在这里插入图片描述

//
int Partition(SeqList*L, int low, int high)
{
	int pivotkey;
	pivotkey = L->data[low];  	//用子表的第一个记录作为枢轴记录
	while (low<high)		//从表的两端交替向中间扫描
	{
		while (low<high&&L->data[high]>pivotkey)
		{
			high--;
		}
		swap(pivotkey, L->data[high]);	//将比枢轴记录小的交换到低端
		while (low < high&&L->data[low] < pivotkey)
		{
			low++;
		}
		swap(pivotkey, L->data[low]);	//将比枢轴记录大的交换到高端
	}
	return low;	//返回枢轴所在的位置
}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

快速排序法的优化版本

1.优化选取枢轴

在这里插入图片描述在这里插入图片描述

	int m = low + (high - low) / 2;//计算数组中间元素下标
	if (L->data[low] > L->data[high])
		swap(low, high);
	if (L->data[m] > L->data[high])
		swap(m, high);
	if (L->data[m] > L->data[low])
		swap(m, low);
	//此时L->data[low]已经为整个序列左中右三个关键字的中间值了

在这里插入图片描述

2.优化不必要的交换

观察上边的图我们发现,50这个关键字,他的变换位置是1–》9–》3–》6–》5;其最终目标位置是5; 当中的其他交换是不需要的;因此我们对Partition函数进行优化;

//快速排序法的优化算法
int Partition(SeqList*L, int low, int high)
{
	int pivotkey;
	int m = low + (high - low) / 2;//计算数组中间元素下标
	if (L->data[low] > L->data[high])
		swap(low, high);
	if (L->data[m] > L->data[high])
		swap(m, high);
	if (L->data[m] > L->data[low])
		swap(m, low);
	//此时L->data[low]已经为整个序列左中右三个关键字的中间值了
	pivotkey = L->data[low];//用子表的第一个记录做枢轴记录
	L->data[0] = pivotkey; //将枢轴关键字备份到L->data[0]中
	while (low<high)
	{
		while (low<high&&L->data[high]>pivotkey)
		{
			high--;
		}
		//swap(pivotkey, L->data[high]);
		L->data[low] = L->data[high]; //采用替换而不是交换的方式进行操作
		while (low < high&&L->data[low] < pivotkey)
		{
			low++;
		}
		//swap(pivotkey, L->data[low]);
		L->data[high] = L->data[low]; //采用替换而不是交换的方式进行操作
	}
	L->data[low] = L->data[0];
	return low;
}

在这里插入图片描述
我们事实上将pivotkey备份到L->data[0]中,而以前swap的时候我们现在只进行替换工作,最终当low与high会和,及就是找到了枢轴位置时,再将L->data[0]赋给L->data[low],在这个当中少了很多的交换函数,性能也提升了一些。

3.优化小数组时的排序方案

在这里插入图片描述

//快速排序法的优化算法
#define MAX_LENGTH_INSERT_SORT 7 
void Qsort(SeqList*L, int low, int high)//对顺序表L中的子序列L->[low-high]做快速排序
{
	int pivot;
	if ((high - low) > MAX_LENGTH_INSERT_SORT)
	{
		//当high-low大于MAX_LENGTH_INSERT_SORT时用快速排序法
		pivot = Partition(L, low, high); //将数组一分为二并且算出枢轴值pivot
		Qsort(L, low, pivot - 1);	//对低子表递归排序
		Qsort(L, pivot + 1, high);	//对高子表递归排序
	}
	else	//当high-low小于MAX_LENGTH_INSERT_SORT时用直接插入排序
		InsertSort(L);
}

在这里插入图片描述

4.优化递归操作

在这里插入图片描述

//快速排序法尾递归的优化算法
#define MAX_LENGTH_INSERT_SORT 7 
void Qsort(SeqList*L, int low, int high)//对顺序表L中的子序列L->[low-high]做快速排序
{
	int pivot;
	if ((high - low) > MAX_LENGTH_INSERT_SORT)
	{
		//当high-low大于MAX_LENGTH_INSERT_SORT时用快速排序法
		while (low < high)
		{
			pivot = Partition(L, low, high);//将数组一分为二并且算出枢轴值pivot
			Qsort(L, low, pivot - 1);	//对低子表递归排序
			low = pivot + 1;	//尾递归
		}
	}
	else//当high-low小于MAX_LENGTH_INSERT_SORT时用直接插入排序
		InsertSort(L);
}

因为第一次递归以后,变量low就没有用处了,所以可以将pivot+1赋值给low,再循环后,来一次Partition(L,low,high),他的效果等同于QSort(L,pivot+1,high);结果是相同的 ,但是因为采用的是迭代而不是递归可以缩减堆栈深度,从而整体提高了性能。

猜你喜欢

转载自blog.csdn.net/weixin_39411321/article/details/89212212