前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >排序----快速排序

排序----快速排序

作者头像
SuperHeroes
发布2018-05-30 17:29:03
7370
发布2018-05-30 17:29:03
举报
文章被收录于专栏:云霄雨霁云霄雨霁

上一篇:归并排序

  • 将长度为N的无重复数组排序,快速排序平均需要~2*NlgN次比较(以及1/6的交换)。
  • 快速排序最多需要N^2/2次比较,但随机打乱数组能预防这种情况。

归并排序和希尔排序一般都比快速排序慢,其原因就在它们还在内循环中移动数据;快速排序的另一个速度优势在于它的比较次数很少。

快速排序的特点:

  • 原地排序(只需要一个很小的辅助栈)
  • 将长度为N的数组排序所系时间和NlgN成正比。
  • 快排的内循环比大多数排序算法都要短小,这意味着无论在理论上还是实际中都要更快。
  • 归并排序和希尔排序一般都比快排慢,其原因就是它们还在内循环中移动数据。
  • 主要缺点是非常脆弱,实现时要非常小心才能避免低劣的性能。

快速排序的切分:

切分满足下面三个条件:

  • 对于某个j,aj已经排定
  • alo到aj-1中的所有元素都不大于aj
  • aj+1到ahi中的所有元素都不小于aj
代码语言:javascript
复制
private static int partition(Comparable[] a,int lo,int hi){
		int i=lo,j=hi+1;
		Comparable v = a[lo];    //子数组第一个元素作为切分元素
		while(true){    
			while(less(a[++i],v))	if(i==hi)	break;    //从左到右找到第一个大于切分元素的元素
			while(less(v,a[--j]))	if(j==lo)	break;    //从右至左找到第一个小于切分元素的元素
			if(i>=j)break; 
			exch(a,i,j);    //交换找到的两个元素
		}
		exch(a,lo,j);    //最后将切分元素交换到正确的位置
		return j;
}

快速排序特点:

快速排序的实现需要注意几个细节:

  • 原地切分。避免使用辅助数组,减小数组复制之间的开销。
  • 别越界。如果切分元素是数组中最大或最小的元素,要特别小心别让扫描指针跑出数组边界。
  • 保持随机性。
  • 处理切分元素值有重复的情况。糟糕的处理可能会使算法运行时间变为平方级别。
  • 终止递归。
代码语言:javascript
复制
public static void sort(Comparable[] a){
	StdRandom.shuffle(a);
	sort(a,0,a.length-1);
}
private static void sort(Comparable[] a,int lo,int hi){
	if(hi<=lo)return ;
	int j = partition(a,lo,hi);    //切分
	sort(a,lo,j-1);    //递归左半部分排序
	sort(a,j+1,hi);    //递归右半部分排序
}

算法改进

  • 切换到插入排序
  • 三取样切分
  • 熵最优排序

含有大量重复元素的数组,快速排序还有巨大的改进空间。一个经典的算法是Dijkstra的“三向切分的快速排序”。它从左到右遍历数组,设有三个指针lt,i,gt。使alo...lt-1中的元素都小于v,agt+1...hi中的元素都大于v,alt...i-1元素都等于v,ai...gt元素未定。

  1. ai小于v, 将alt和ai交换,将lt和i加一;
  2. ai大于v, 将agt和ai交换,将gt减一;
  3. ai等于v, 将i加一。

对于包含大量相同元素的数组,它将排序时间线性对数级别降到了线性级别。

代码语言:javascript
复制
private static void sort(Comparable[] a,int lo,int hi){
	if(hi<=lo)return;
	int lt=lo,i=lo+1,gt=hi;
	Comparable v = a[lo];
	while(i<=gt){
		int cmp = a[i].compareTo(v);
		if(cmp<0)	exch(a,lt++,i++);    //小于,放左边
		else if(cmp>0)	exch(a,i,gt--);    //大于,放右边
		else i++;    //等于,放中间
	}
    //只递归左右小于和大于V的部分,中间等于V的部分不需要递归
	sort(a,lo,lt-1);
	sort(a,gt+1,hi);
}

算法改进:

  • 哨兵:可以通过设置哨兵来去掉while中的边界检查。由于切分元素本身就是一个哨兵,左侧边界检查是多余的;可以将数组中的最大元素放置在alength-1中来去掉右部检查。注意:在处理内部数组中,右子数组最左侧元素可以成为左子数组的哨兵。
  • 非递归的快速排序:可以使用一个循环来将弹出栈的切分并将结果子数组重新压栈来实现非递归快排。注意:先将较大子数组压栈可以保证栈中最多只会有lgN个元素。
  • 快速三向切分:可以讲相等的元素放在数组两边而不是中间实现快速三向切分。

下一篇:堆排序

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.11.30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 算法改进
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档