快速排序的递归形式和非递归形式

图解快速排序

(1)选取基准值,并设置两个指针:left,right
通常我们选取第一个元素为基准值(注意:这并不是一个好的办法)。所以4就是基准值。
left指向序列的最左端位置,right指向序列的最右端。
在这里插入图片描述

(2)从右至左开始寻找,移动right指针,直到找到比基准值小的元素。然后将该元素赋值给left指针所指的位置。
在这里插入图片描述
(3)然后从左至右开始寻找,移动left指针,直到找到比基准值大的元素。然后将该元素赋值给right指针所指的位置。
在这里插入图片描述
(4)不断重复步骤二、三。直到left指针与right指针重合。然后将基准值赋值给left指针所指的位置,此时已经完成第一次排序。基准值左边的数一定比基准值要小,基准值右边的数一定比基准值要大。
在这里插入图片描述
(5)以基准值2为界限,将其分成左右两个序列。分别对这左右两个序列进行上述步骤。

二、代码实现

实现快速排序有两种办法:递归形式和非递归形式
(1)递归形式:

#include <iostream>
#include <vector>

using namespace std;

int division(vector<int> &list, int left, int right) 
{
	// 以最左边的数(left)为基准
	int base = list[left];
	while (left < right)
	 {
		// 从序列右端开始,向左遍历,直到找到小于base的数
		while (left < right && list[right] >= base)
			right--;
		// 找到了比base小的元素,将这个元素放到最左边的位置
		list[left] = list[right];

		// 从序列左端开始,向右遍历,直到找到大于base的数
		while (left < right && list[left] <= base)
			left++;
		// 找到了比base大的元素,将这个元素放到最右边的位置
		list[right] = list[left];
	}

	// 最后将base放到left位置。此时,left位置的左侧数值应该都比left小;
	// 而left位置的右侧数值应该都比left大。
	list[left] = base;
	return left;
}

// 快速排序
void DealQuickSort(vector<int> &list, int left, int right)
 {
	// 左下标一定小于右下标,否则就越界了
	if (left < right) 
	{
		// 对数组进行分割,取出下次分割的基准标号
		int base = division(list, left, right);

		// 对“基准标号“左侧的一组数值进行递归的切割,以至于将这些数值完整的排序
		DealQuickSort(list, left, base - 1);

		// 对“基准标号“右侧的一组数值进行递归的切割,以至于将这些数值完整的排序
		DealQuickSort(list, base + 1, right);
	}
}

void QuickSort(vector<int>& list, int length)
{
	DealQuickSort(list, 0, length - 1);
}

int main()
 {
	int arr[] = { 6, 4, 8, 9, 2, 3, 1 };
	vector<int> test(arr, arr + sizeof(arr) / sizeof(arr[0]));
	cout << "排序前" << endl;
	for (int i = 0; i < test.size(); i++) 
	{
		cout << test[i] << " ";
	}
	cout << endl;
	vector<int> result = test;
	QuickSort(result, result.size());
	cout << "排序后" << endl;
	for (int i = 0; i < result.size(); i++) 
	{
		cout << result[i] << " ";
	}
	cout << endl;
	system("pause");
}

上面的代码实际上是一个升序排序,如果想要改为降序排序,那么只需要将步骤二改为寻找比基准值大的数,步骤三改为寻找比基准值小的数即可。

2)非递归形式

#include <iostream>
#include <vector>
#include<stack>

using namespace std;

int division(vector<int> &list, int left, int right) 
{
	// 以最左边的数(left)为基准
	int base = list[left];
	while (left < right) 
	{
		// 从序列右端开始,向左遍历,直到找到小于base的数
		while (left < right && list[right] >= base)
			right--;
		// 找到了比base小的元素,将这个元素放到最左边的位置
		list[left] = list[right];

		// 从序列左端开始,向右遍历,直到找到大于base的数
		while (left < right && list[left] <= base)
			left++;
		// 找到了比base大的元素,将这个元素放到最右边的位置
		list[right] = list[left];
	}

	// 最后将base放到left位置。此时,left位置的左侧数值应该都比left小;
	// 而left位置的右侧数值应该都比left大。
	list[left] = base;
	return left;
}

//仅仅改变了这个函数
void DealQuickSort(vector<int> &list, int left, int right)
 {
	
	stack<int> s;
	s.push(left);     //将值用栈保存下来
	s.push(right);

	while (!s.empty())
	{
		int end = s.top();
		s.pop();
		int start = s.top();
		s.pop();
		int base = division(list, start, end);

		if (start < base - 1)    
		{
			s.push(start);
			s.push(base-1);
		}

		if (end > base + 1)
		{
			s.push(base + 1);
			s.push(end);
		}
	}
}

void QuickSort(vector<int>& list, int length)
{
	DealQuickSort(list, 0, length - 1);
}

int main() 
{
	int arr[] = { 6, 4, 8, 9, 2, 3, 1 };
	vector<int> test(arr, arr + sizeof(arr) / sizeof(arr[0]));
	cout << "排序前" << endl;
	for (int i = 0; i < test.size(); i++) 
	{
		cout << test[i] << " ";
	}
	cout << endl;
	vector<int> result = test;
	QuickSort(result, result.size());
	cout << "排序后" << endl;
	for (int i = 0; i < result.size(); i++) 
	{
		cout << result[i] << " ";
	}
	cout << endl;
	system("pause");
}

三、性能分析

(1)时间复杂度

  1. 最坏的情况下:对一个有序的序列进行排序,比如说将一个降序序列排为升序序列。以第一个数为基准值将其分割为两个子序列,其中一个子序列一定为空,此时的时间复杂度最高,为O(N*N)
  2. 最好情况下:数据随机分布,以第一个数为基准值分割为两个子序列,此时两个子序列中的元素个数几乎相同,此时时间复杂度最低,为O(N*logN)
  3. 平均情况下:同样也为O(N*logN)

(2)空间复杂度
在每次分割的时候,需要一个空间存储基准值。而快排中需要进行logN次分割,所以占用空间也为logN

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值