常见排序算法之快速排序原理及递归非递归版本代码

10 篇文章 0 订阅
9 篇文章 1 订阅

快速排序原理

之前介绍过三种基础的排序算法以及它们之间的比较:冒泡排序选择排序直接插入排序,我们知道它们的时间复杂度都是O(n2),那是不是所有的算法的时间复杂度都是O(n2)呢?当然不是!下面我们介绍的快速排序之所以叫快速排序就是因为目前已有的排序算法中,快速排序的时间复杂度是最小的。快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法。
快速排序的基本思想:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分小,则可以分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

复杂度分析

  • 最优与平均时间复杂度O(nlogn)
  • 最坏时间复杂度O(n2)
  • 空间复杂度O(logn)
  • 快速排序是一种不稳定的排序方法

快速排序代码

1.递归版本

void MySort::QuickSort(std::vector<int>& nums)//快速排序
{
	int l = 0, r = nums.size()-1;
	QSort(nums, l, r);
}
void QSort(std::vector<int>& nums, int left, int right)
{
	if (left < right) //当left>=right时停止递归
	{
		int pos = AdjustPos(nums, left, right);//返回调整后基准数的位置
		QSort(nums, left, pos - 1);//递归调用对前半部分再次进行排序
		QSort(nums, pos + 1, right);//递归调用对后半部分再次进行排序
	}
}
int AdjustPos(std::vector<int>& nums, int left, int right)
{
	int tmp = nums[left];//基准数
	while (left < right)//left>=right时一趟比较结束
	{
		while (left < right&&nums[right] >= tmp)//寻找右边第一个小于基准的数的位置
		{
			right--;
		}
		if (left < right)
		{
			nums[left] = nums[right];//填坑法
			left++;
		}
		while (left < right&&nums[left] <= tmp)//寻找左边第一个大于基准的数的位置
		{
			left++;
		}
		if (left < right)
		{
			nums[right] = nums[left];//填坑法
			right--;
		}
	}
	nums[left] = tmp;//将基准数填入
	return left;//返回最终的基准位置
}

主要的代码就是AdjustPos这个函数,采用的是之前看过一个大佬的博客中提到的挖坑填数法。

2.非递归版本

由于每一次递归调用时都会调用大量的堆栈,因此我们可以用非递归版本实现快速排序。递归的算法主要是在划分子区间,如果要非递归实现快排,只要使用一个栈来保存区间就可以了。一般将递归程序改成非递归首先想到的就是使用栈,因为递归本身就是一个压栈的过程。

void MySort::QuickSortNotR(std::vector<int>& nums)//快速排序
{
	int l = 0, r = nums.size()-1;
	QSortNotR(nums, l, r);
}
void QSortNotR(std::vector<int>& nums, int left, int right)
{
	std::stack<int> s;
	s.push(left);
	s.push(right);//后入的right,所以要先拿right
	while (!s.empty())//栈为空时退出
	{
		int right = s.top();
		s.pop();
		int left = s.top();
		s.pop();

		int index = AdjustPos(nums, left, right);//这个函数还是之前递归版本的函数
		//如果返回的index小于或等于left,那么左半边数组为空的就不用再排序,否则栈永远不会为空
		if ((index - 1) > left)//左子序列
		{
			s.push(left);
			s.push(index - 1);
		}
		if ((index + 1) < right)//右子序列
		{
			s.push(index + 1);
			s.push(right);
		}
	}
}

测试及结果

测试

int main()
{
	srand((int)time(NULL));//随机种子
	vector<int> nums,nums1,nums2;
	print_data printTest;
	MySort sortTest;
	for (int i = 0; i < 10; i++)
	{
		nums.push_back(rand() % 100);//生成0-99之间的随机数
	}
	nums1 = nums;
	nums2 = nums;
	vector<int> numsk = nums;
	vector<int> numskNotR = nums;

	cout << "快速排序:" << endl;
	//改进冒泡排序
	printTest.printVector(numsk);
	sortTest.QuickSort(numsk);
	printTest.printVector(numsk);

	cout << "快速排序非递归:" << endl;
	//改进冒泡排序
	printTest.printVector(numskNotR);
	sortTest.QuickSortNotR(numskNotR);
	printTest.printVector(numskNotR);

	system("pause");
	return 0;
}

结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值