快速排序

本文深入解析了快速排序算法,包括其基本思想、递归与非递归实现方式,并探讨了多种分区策略如左右指针法、挖坑法及前后指针法。此外,还介绍了算法优化技巧,如三数取中法和小区间优化,以提高排序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快速排序(Quick Sort)是对冒泡排序的一种改进,基本思想是选取一个数作为关键字,经过一趟排序,将整段序列分为两个部分,其中一部分的值都小于关键字,另一部分都大关键字。然后继续对这两部分继续进行排序,从而使整个序列达到有序。
递归实现:

void QuickSort(int* arr, int left, int right)
{
	int mid = 0;
	if(left >= right)//表示完成一组的排序
		return;
	mid = PartSort1(arr, left, right);关键字的位置
	QuickSort(arr, left, mid-1);
	QuickSort(arr, mid+1, right);
}

PartSort()函数是进行一次快排的算法。
对于PartSort的函数有很多,这里展示三种。

左右指针法

1、选取一个关键字(key),一般取整组记录的第一个数/最后一个,这里采用选取数组的第一个数为key。
2、设置两个变量left = 0;right = N - 1;
3、从left一直向后走,直到找到一个大于key的值,right从后至前,直至找到一个小于key的值,然后交换这两个数。
4、重复第三步,一直往后找,直到left和right相遇,这时将key放置left的位置即可。

在这里插入图片描述
根据上面的思想,可以写出下面的代码

int PartSort1(int* arr, int left, int right)
{
	int key;
	int start = left;
	key = arr[left];
	while(left < right)
	{
		while(left < right && arr[right] >= key)
		{
			right--;
		}
		while(left < right && arr[left] <= key)
		{
			left++;
		}
		
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[left], &arr[start]);
	return left;
}

挖坑法

1、选取一个关键字key,一般取整组记录的第一个数/最后一个,这里采用选取数组第一个元素key,也是初始的坑位。
2、设置两个变量left = 0;right = N - 1;
3、right一直向前走,直到找到一个小于key的值,然后将该数放入坑中,坑位变成了arr[right]。
4、left一直向前走,直到找到一个小于key的值,然后将该数放入坑中,坑位变成了arr[left]。
5、重复3和4的步骤,直到left和right相遇,然后将key放入最后一个坑位。

在这里插入图片描述
代码如下

int PartSort2(int* arr, int left, int right)
{
	int key = arr[left];
	while(left < right)
	{
		while(left < right && arr[left] >= key)
		{
			right--;
		}
		arr[left] = arr[right];
		while(left < right && arr[left] <= key)
		{
			left++;
		}
		arr[right] = arr[left];	
	}
	arr[left] = key;
	return left;
}

前后指针法

1、定义变量prev指向序列的开头,定义变量cur指向prev的后一个位置。
2、当arr[cur] < key时,cur和prev同时往后走,如果arr[cur]>key,cur往后走,pre留在大于key的数值前一个位置。
3、当arr[cur]再次 < key时,交换array[cur]和array[pre]。
简单来说就是,在没找到大于key值前,pre永远紧跟cur,遇到大的两者之间机会拉开差距,中间差的肯定是连续的大于key的值,当再次遇到小于key的值时,交换两个下标对应的值就好了

在这里插入图片描述
代码如下

int PartSort3(int* arr, int left, int right)
{
	int key = arr[left];
	int prev = left;
	int cur = left+1;
	while(cur <= right)
	{
		if(arr[cur] < key && (++prev) != cur)
		{
			Swap(&arr[prev], &arr[cur]);
		}
		++cur;
	}
	Swap(&arr[left], &arr[prev]);
	return prev;
}

快排的优化
首先快排的思想是找一个关键字,然后以关键字为中介线,一遍都小于它,另一边都大于它,然后对两段区间继续划分,那么关键字的选取就很关键。
1、三数取中法
上面的代码思想都是直接拿序列的第一个值作为关键字,如果最后这个值刚好是整段序列最大或者最小的值,那么这次划分就是没意义的。
所以当序列是正序或者逆序时,每次选到的关键字都是没有起到划分的作用。快排的效率会退化。
所以可以每次在选枢轴时,在序列的第一,中间,最后三个值里面选一个中间值出来作为枢轴,保证每次划分接近均等
2、小区间优化
由于是递归程序,每一次递归都要开辟栈帧,当递归到序列里的值不是很多时,我们可以采用直接插入排序来完成,从而避免这些栈帧的消耗。

整个代码如下:

//三数取中
int GetMid(int* arr, int left, int right)
{
	int mid = left + ((right-left)>>1);
	if(arr[left] <= arr[right])
	{
		if(arr[mid] < arr[left])
		{
			return left;
		}
		else if(arr[mid] > arr[right])
		{
			return right;
		}
		else
		{
			return mid;
		}
	}
	else
	{
		if(arr[mid] < arr[right])
		{
			return right;
		}
		else if(arr[mid] > arr[left])
		{
			return left;
		}
		else
		{
			return mid;
		}
	}

}
//左右指针法
int PartSort1(int* arr, int left, int right)
{
	
	int key;
	int start = left;
	int mid = GetMid(arr, left, right);
	Swap(&arr[mid], &arr[left]);
	key = arr[left];
	while(left < right)
	{
		while(left < right && arr[right] >= key)
		{
			right--;
		}
		while(left < right && arr[left] <= key)
		{
			left++;
		}
		Swap(&arr[left], &arr[right]);
	}
	Swap(&arr[left], &arr[start]);
	return left;
}

//挖坑法
int PartSort2(int* arr, int left, int right)
{
	int mid = GetMid(arr, left, right);
	Swap(&arr[left], &arr[mid]);
	int key = arr[left];
	
	while(left < right)
	{
		while(left < right && arr[left] >= key)
		{
			right--;
		}
		arr[left] = arr[right];
		while(left < right && arr[left] <= key)
		{
			left++;
		}
		arr[right] = arr[left];	
	}

	arr[left] = key;
	return left;
}

//前后指针法
int PartSort3(int* arr, int left, int right)
{
	int mid = GetMid(arr, left, right);
	Swap(&arr[left], &arr[mid]);
	int key = arr[left];
	int prev = left;
	int cur = left+1;
	while(cur <= right)
	{
		if(arr[cur] < key && (++prev) != cur)
		{
			Swap(&arr[prev], &arr[cur]);
		}
		++cur;
	}
	Swap(&arr[left], &arr[prev]);
	return prev;
}
void QuickSort(int* arr, int left, int right)
{
	int mid = 0;
	if(left >= right)
		return;
	//小区间优化
	if((right - left) <= 5)
	{
		InsertSort(arr, right-left+1);
	}
	mid = PartSort3(arr, left, right);
	QuickSort(arr, left, mid-1);
	QuickSort(arr, mid+1, right);
}

非递归实现
递归的算法主要是在划分子区间,如果要非递归实现快排,只要使用一个栈来保存区间就可以了。
一般将递归程序改成非递归首先想到的就是使用栈,因为递归本身就是一个压栈的过程。

void QuickSortNoR(int* arr, int left, int right)
{
	
	int mid = 0;
	int begin,end;
	Stack s;
	StackInit(&s, 3);
	StackPush(&s, right);
	StackPush(&s,left);
	while(StackEmpty(&s) != 0)
	{
		begin = StackTop(&s);//取出左边区间
		StackPop(&s);
		end = StackTop(&s);//起初右边区间
		StackPop(&s);
		mid = PartSort1(arr, begin, end);
		if((mid+1) < right)
		{
			StackPush(&s, right);
			StackPush(&s,mid+1);
		}
		if((mid-1) > left)
		{
			StackPush(&s, mid-1);
			StackPush(&s, left);
		}
	}
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值