关闭

算法学习 - 快速排序相关

标签: 快速排序算法
441人阅读 评论(0) 收藏 举报
分类:


1. 划分

快速排序中最重要的步骤就是划分,即选择一个元素(很多地方称为枢纽元素),将序列划分成两部分,比枢纽元素大的放在枢纽元素的右边,比枢纽元素小的放在枢纽元素的左边。

划分函数的思想:

1. 选择一个枢纽元素(以选择序列的第一个元素为例),暂存到临时变量中

2. 从序列的右边向左查找,找到一个比枢纽元素小的值。若找到,则将这个值填入到枢纽元素的位置

3. 从序列的左边向右查找,找到一个比枢纽元素大的值。若找到,则将这个值填入到step 2中那个比枢纽元素小的元素的位置。

4. 重复step2, step3,直到向右查找的“指针”(索引)和向左查找的“指针”(索引)碰到一起。表明一次划分就完成了。

按照上述思想实现代码如下:

int partition(int *arr, int l, int r)
{
	int i = l, j = r;
	int key = arr[l];									//枢纽元素
	while (i < j)
	{
		while (i < j && arr[j] >= key) j--;				//从右往左找到一个比枢纽小的
		if (i < j)
		{
			arr[i] = arr[j];
			i++;
		}
		while (i < j && arr[i] < key) i++;				//从右往左找到一个比枢纽大的
		if (i < j)
		{
			arr[j] = arr[i];
			j--;
		}
	}
	arr[i] = key;
	return i;
}


2. 递归快排

递归的方式是最常见的方式,因为递归的写法代码简短。

算法思想:对元素进行划分,划分一次之后分别对两个子序列进行划分。直到子序列的元素个数变成1为止。

void QuickSort(int *arr, int l, int r)
{
	if (l < r)
	{
		int par = partition(arr, l, r);
		QuickSort(arr, l, par - 1);
		QuickSort(arr, par + 1, r);
	}
}


3. 使用栈作为辅助空间的快排

递归的过程其实就可以理解为一个栈,因为递归是依靠函数调用压栈的方式实现的。所以我们可以手动将中间过程保留,来进行快排。

算法思想:

1. 对序列进行一次划分,划分后的两个子序列头尾都压入栈中。

2. 在栈非空的情况下,从栈中取出两个元素(子序列的头尾位置)。对子序列进行划分。

3. 同样,对子序列进行划分的时候也将划分后的序列头尾压入栈中。

4. 重复step2 step3,知道栈为空为止。

void QuickSort2(int *arr, int l, int r)
{
	stack<int> tmp;								//临时栈
	if (l < r)									//
	{
		int par = partition(arr, l, r);			//

		if (l < par - 1)						//
		{
			tmp.push(l);						//
			tmp.push(par - 1);
		}
		if (par + 1 < r)
		{
			tmp.push(par + 1);
			tmp.push(r);
		}

		while (!tmp.empty())
		{
			r = tmp.top(); tmp.pop();
			l = tmp.top(); tmp.pop();

			par = partition(arr, l, r);

			if (l < par - 1)
			{
				tmp.push(l); tmp.push(par - 1);
			}

			if (par + 1 < r)
			{
				tmp.push(par + 1); tmp.push(r);
			}
		}
	}
}


4. 使用队列作为辅助空间的快排

这个实现其实和用栈作为辅助空间的思想是相同的,无非就是将存储中间结果的辅助空间换成了队列。

void QuickSort3(int *arr, int l, int r)
{
	queue<int> tmp;								//临时队列
	if (l < r)									
	{
		int mid = partition(arr, l, r);			//一次划分

		if (l < mid - 1)						//划分中间结果入队列
		{
			tmp.push(l);						
			tmp.push(mid - 1);
		}
		if (mid + 1 < r)
		{
			tmp.push(mid + 1);
			tmp.push(r);
		}

		while (!tmp.empty())
		{
			l = tmp.front(); 
			tmp.pop();
			r = tmp.front();
			tmp.pop();

			mid = partition(arr, l, r);

			if (l < mid - 1)
			{
				tmp.push(l); tmp.push(mid - 1);
			}

			if (mid + 1 < r)
			{
				tmp.push(mid + 1);
				tmp.push(r);
			}
		}
	}
}


5. 快排什么时候适合使用,什么时候不适合使用?

快排是基于划分的,最好的情况就是每次划分恰好都是在序列的中间,这样算法的复杂度就是O(n*lgn)。所以说枢纽元素的选择会直接影像到排序的效率。

随机序列快排:枢纽元素的选择也将是随机的,这时候的划分不会导致出现最差的结果。

已序序列快排:假设采用枢纽元素总是选择第一个元素的方式,很明显,每次划分都只能排序一个元素,这时候排序的效率就是最差的(也就是严重的不平衡树)。此时效率为O(n^2)。

那么对于这种已序的序列什么排序比较适合呢?插入排序!!对于插入排序,若序列是已序的表明序列不需要作很多的移动,此时效率为O(n)。


这也就可以联系到STL中sort函数的实现:

STL中的sort(),在数据量大时,采用quicksort,分段递归排序;一旦分段后的数量小于某个门限值,改用Insertion sort,避免quicksort深度递归带来的过大的额外负担,如果递归层次过深,还会改用heapsort。


1
0
查看评论

啊哈算法-----快速排序

上一节的冒泡排序可以说是我们学习第一个真正的排序算法,并且解决了桶排序浪费空间的问题,但在算法的执行效率上却牺牲了很多,它的时间复杂度达到了O(N2)。假如我们的计算机每秒钟可以运行10亿次,那么对1亿个数进行排序,桶排序则只需要0.1秒,而冒泡排序则需要1千万秒,达到115天之久,是不是很吓人。那...
  • binyao02123202
  • binyao02123202
  • 2014-02-27 14:59
  • 4249

《算法(第四版)》排序-----快速排序

参考文章:   http://ahalei.blog.51cto.com/4767671/1365285 1.概念 快速排序,听这个名字就能想到它排序速度快,它是一种原地排序(只需要一个很小的辅助栈,注意不是数组),且将长度为N的数组排序所需的时间和NlgN成正比 缺点是:非...
  • kwang0131
  • kwang0131
  • 2016-04-07 14:33
  • 2743

快速排序中的分割算法实现

这里介绍快速排序使用到的两种分割算法。 对于快速排序而言,先选定一个枢轴元素,以枢轴元素为基准比枢轴元素小的元素放在枢轴元素的左边,比枢轴元素大的元素放在枢轴元素的右边。这就是一次分割过程。 1,先介绍第一种分割算法 该算法是《算法导论》中描述的PARTITION过程。这个分割的思想应该有很多应用,...
  • hapjin
  • hapjin
  • 2015-11-11 19:38
  • 1364

【算法理解】—— 快速排序(三向切分)

针对于“快速排序”算法的一个介绍,并对快速排序的优化版——“快速排序(三向切分)”做一个介绍。
  • a8336675
  • a8336675
  • 2016-07-04 09:49
  • 1434

一些算法书籍

看了这个人的我的算法学习之路:http://lucida.me/blog/on-learning-algorithms/ 以及刘未鹏的几个帖子:http://mindhacks.cn/topics/algorithms/ 还有这个帖子:http://book.douban.com/review/...
  • u010087886
  • u010087886
  • 2015-12-08 20:20
  • 406

选择和排序的Sherwood算法

将选择和排序的确定算法修改为Sherwood算法很简单,但是当算法较复杂,例如它是一个缺乏文档资料的软件包的一部分时,就很难对其进行修改。注意,只有当该算法平均时间性能较优,但最坏性能较差时,才有修改的价值。一般方法是: 1 将被解的实例变换到一个随机实例。// 预处理 2 用确定算法解此随机实例,...
  • u010786109
  • u010786109
  • 2014-11-25 16:15
  • 636

快速排序改进算法

#include using namespace std; const int M = 20; void quickSort(int *data,const int left,const int right); int partition(int *data,const int low,const...
  • Andrewseu
  • Andrewseu
  • 2014-09-13 23:35
  • 938

算法学习系列(贪心算法)—机器人攀登问题

问题描述: 要举办一场机器人攀登接力赛,规定攀登总高度为m米,每队参赛机器人个数为n(2≤n≤10),每个机器人只能攀登1次,至少攀登1米,至多攀登k(1≤k≤20)米,并且只能在整米处接力。某队有n个机器人,在平时训练时进行了性能测试,得出每个机器人连续攀登1米、2米、…、k米的时间,机器人攀登...
  • qq_34825926
  • qq_34825926
  • 2017-03-23 10:37
  • 269

排序算法系列:快速排序算法

本文就来说说交换排序的最后一拍:快速排序算法。本文就其原理、过程及实现几个方面讲解一下快速排序算法。
  • u013761665
  • u013761665
  • 2016-03-01 15:40
  • 22538

舍伍德算法改进快速排序

由于快速排序具有不稳定性,最好的时间复杂度为o(nlogn),而最坏可达到o(n^2),为了降低最坏情况出现的概率,可以用舍伍德算法对其进行改进~ #include #include #include #include #include #define N 1000 using namespa...
  • smallacmer
  • smallacmer
  • 2013-05-18 13:23
  • 3272
    个人资料
    • 访问:227741次
    • 积分:3541
    • 等级:
    • 排名:第11106名
    • 原创:108篇
    • 转载:10篇
    • 译文:0篇
    • 评论:106条
    博客专栏
    最新评论