【数据结构】通俗易懂的快速排序递归及非递归(Hoare版本、挖坑法、前后指针)C++完整代码

本文深入解析了快速排序算法,包括Hoare版本、挖坑法和前后指针版本的详细步骤及C++实现,探讨了基准值的选择策略,如三数取中法,以及递归和非递归的实现方式。

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

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。

其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

如何寻找基准值?

一般情况下会选择排序序列的最后一个元素作为基准值,但是如果待排序列的最后一个元素是最值或者接近于最值的情况下,会使得排序后的左右序列元素个数相差过大,降低了效率,所以我们一般采用三数取中法:找出序列的开始节点、结束节点以及序列的中的节点取中间值作为基准值。

将区间按照基准值划分为左右两半部分的常见方式有:

hoare版本 :

1、首先设定基准值

假设我们找到了基准值是最后一个值,此时我们设定其位置为 pivot ,同时设定两个指针 left 、right 分别指向待排序列的左右两端;

2、然后开始移动 left(++) 和 right(--)

当 left 所指向的数字比 pivot 所指向的数字大时,停止移动

当 right 所指向的数字比 pivot 所指向的数字小时,停止移动

当 left 和 right 都停止移动后,交换两个指针所指向的数字;

 3、继续移动,当 left 和 right 想遇时停止移动,这里我们假设 left 先移动,那么两者的可以相遇的情况一定是 左右都已经排好了,并且此时 left 和 right 所指向的数字一定比 pvoit 所指向的数字大!!!

此时只需要将,left 和 right 所指向的数组和 pvoit 所指向的数字进行交换即可;

4、此时以及成功将待排序列分为左右两部分 (以 6 为中心),之后只需要对左右序列进行以上同的工作就可以了;

用递归实现是非常简单的,主要是要完成一次排序后,不断对左右生于的子序列进行递归即可。

那么,非递归又怎么办?其实也很简单,利用栈(stack)的结构,来模拟进行递归就可以了,函数的递归调用本来也就是在函数调用栈中进行的~

代码(C++)如下所示:

template <class T> 
void Swap(T* a, T* b)
{
	int tmp;
	tmp = *a;
	*a = *b;
	*b = tmp;
}
int partition(int* data, int left, int right)
{	
	int pivot = right;
	while (left < right) {
		while (left < right && data[left] <= data[pivot])
			++left;
		while (left < right && data[right] >= data[pivot])
			--right;
		Swap(&data[left], &data[right]);
	}
	Swap(&data[left], &data[pivot]);
	return pivot;
}

//递归
void quickSortRecusive(int* data, int left, int right){
	if (data == NULL || left < 0 || right <= 0 || left > right)
		return;
	int pivot = partition(data,left,right);
	if (pivot > left)
		quickSortRecusive(data, left, pivot - 1);
	if (pivot < right)
		quickSortRecusive(data, pivot + 1, right);
	
}

//非递归实现
void quickSortNorecusive(int* data, int left, int right)
{
	if (data == NULL || left < 0 || right <= 0 || left > right)
		return;
	stack<int> s;
	int l, r;
	s.push(right);
	s.push(left);
	while (!s.empty()) {
		l = s.top();
		s.pop();
		r = s.top();
		s.pop();
		if (l < r) {
			int povit = partition(data, l, r);
			if (povit > l) {
				s.push(povit - 1);
				s.push(l);
			}
			if (r > povit) {
				s.push(r);
				s.push(povit + 1);
			}
		}
	}

}

挖坑法  :

挖坑法顾名思义,我们需要把选择的基准值的位置固定下来,就像于是挖了一个坑。

1、首先需要定一个指针 index 指向选定的基准值而且记住这个值 pivot,并且需要定义两个指针 left 和 right 分别指向 代拍序列的首段和尾端;

2、移动指针及判断,假设我们打算先移动 right,首先要判断 right 所指向的值 和 index 所指向的 pivot 相比较

 如果 right 的值小于 index 的值,那就把right 所指向的值,填到 left 中,并且将 index 指向right,left 向右移动;

3、然后比较 left 和 index 所指向的值:

如果 left 的值 大于 index 的值,就将 left 的值 填入 indx 中,nidex 指向 left,right 左移;

4、继续移动,当 left 和 right 相遇的时候,只需要将开始记录的 Pivot 的值,填入到当前 index 和 left right 共同指向的位置即可;

代码(C++)如下:

int partitionW(int* arr,int left,int right) {
	int pivot = arr[left];
	int index = left;
	while (left < right) {
		while (right != index) {
			if (arr[right] < pivot) {
				arr[index] = arr[right];
				index = right;
				++left;
			}
			else
				--right;
		}
		while (left != index) {
			if (arr[left] > pivot) {
				arr[index] = arr[left];
				index = left;
				--right;
			}
			else
				++left;
		}
	}
	arr[index] = pivot;
	return index;
}
//递归
void quickSortRecusiveW(int* data,int left,int right) {
	if (data == NULL || left < 0 || right <= 0 || left >right)
		return;
	int pos = partitionW(data, left, right);
	//左右
	quickSortRecusiveW(data, left, pos - 1);
	quickSortRecusiveW(data, pos + 1, right);
}
//非递归
void quickSortNorecusiveW(int* data, int left, int right) {
	if (data == NULL || left < 0 || right <= 0 || left > right)
		return;
	stack<int> s;
	int l, r;
	s.push(right);
	s.push(left);
	while (!s.empty()) {
		l = s.top();
		s.pop();
		r = s.top();
		s.pop();
		if (l < r) {
			int pos = partitionW(data, l, r);
			if (l < pos) {
				s.push(pos - 1);
				s.push(l);
			}
			if (r > pos) {
				s.push(r);
				s.push(pos + 1);
			}
		}
	}
}

前后指针版本:

1、首先我们选择基准值设为 piovt ,并且设定两个指针 left 和 right 分别指向待排序列的初始位置和结尾位置。

2、首先移动 right 指针,如果 right 所指向的值小于 piovt ,则停止移动。

然后移动 left 指针,如果 left 所指向的值大于 piovt,则停止移动;

3、交换 left 的值和 right 的值;

4、继续上面的动作,直到 left 和 right 指向同一个值,此时只需要将 pivot 的值和这个交换即可;

int partitionZ(int* arr, int left, int right) {
	int index = left;
	int pivot = arr[left];
	while (left < right) {
		while (left < right) {
			if (arr[right] < pivot)
				break;
			--right;
		}
		while (left < right) {
			if (arr[left] > pivot)
				break;
			++left;
		}
		if(left < right)
			swap(arr[left], arr[right]);
	}
	swap(arr[left], arr[index]);
	return left;
}
//递归
void quickSortRecusiveZ(int* arr, int left, int right) {
	if (arr == NULL || left < 0 || right <= 0 || left > right)
		return;
	int pos = partitionZ(arr, left, right);

	if(left < pos)
		quickSortRecusiveZ(arr, left, pos - 1);
	if(right > pos)
		quickSortRecusiveZ(arr, pos + 1, right);
}
//非递归
void quickSortNorecusiveZ(int* arr, int left, int right) {
	if (arr == NULL || left < 0 || right <= 0 || left > right)
		return;
	stack<int> s;
	s.push(right);
	s.push(left);
	int l, r;
	while (!s.empty()) {
		l = s.top();
		s.pop();
		r = s.top();
		s.pop();
		if(l < r){
			int pos = partitionZ(arr, l, r);
			if (l < pos) {
				s.push(pos - 1 );
				s.push(l);
			}
			if (r > pos) {
				s.push(r);
				s.push(l + 1);
			}
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_ClivenZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值