数据结构与算法之--高级排序:shell排序和快速排序
高级排序比简单排序要快的多,简单排序的时间复杂度是O(N^2),希尔(shell)排序大约是O(N*(logN)^2),而快速排序是O(N*logN)。
说明:下面以int数组的从小到大排序为例。
希尔(shell)排序
希尔排序是基于插入排序的,首先回顾一下插入排序,假设插入是从左向右执行的,待插入元素的左边是有序的,且假如待插入元素比左边的都小,就需要挪动左边的所有元素,如下图所示:
==>
图1和图2:插入右边的temp柱需要outer标记位左边的五个柱子都向右挪动
如图3所示,相比插入排序,希尔排序是这样做的:对固定间隔的元素做插入排序,然后减小间隔,重复做插入排序,直到间隔减小为1。
==> 
图3和图4: outer位置和inner-h位置的柱子做插入排序
数据量大的图形看这个过程更容易形象地把握算法特点,如图5和6,总元素数量等于100:

图5和图6:间隔分别为40和13执行完插入排序后的效果
相比简单插入排序,大间隔地做插入排序有两个好处:
一、大间隔直接导致需要挪动的数据稀少,且数据挪动的效率高,图5中一次挪动可以跨越40个位置;
二、经过前一步大间隔的插入排序后,整个数组从整体上粗略地看已经有了明显的顺序,后一步小间隔的插入排序时,一部分操作是不需要挪动数据的,再次减少了挪动数据的次数。
间隔的序列:间隔的常用序列,通过递归表示:h=3*h+1。(1,4,13,40,121 ...)
希尔排序的效率:“没有理论上分析希尔排序的效率的结论,各种基于实验的评估,估计它的时间级从O(N^(3/2))到O(N^(7/6))”--[1]。
参考代码:
/**
* Shell排序可以说是插入排序的升级版,相比较插入排序,
* Shell排序首先使用大间隔、少元素的插入排序,使得每次移动的数据少,但是移动的跨度大;
* 然后再使用小间隔,略多数量元素的插入排序,经过上一步插入排序,很多数据已经距离排序位置不远了,
* 这样第二次插入排序时,需要移动的数据的数量就会减少,
* 间隔最终减小到1.
*/
public static int[] shellSort(int[] input) {
int length = input.length;
int inner, outer;
int temp; int h = 1;
while (h <= length / 3) {
h = h * 3 + 1;
} while (h > 0) {
for (outer = h; outer < length; outer++) {
temp = input[outer];
inner = outer; while (inner > h - 1 && input[inner - h] >= temp) {
input[inner] = input[inner - h];
inner -= h;
}
input[inner] = temp;
}
h = (h - 1) / 3;
} return input;
}
快速排序
快速排序算法的策略是这样的:首先把数组用某个值分为两个子数组,且称这个值为分组值,一个子数组中的元素均小于分组值,另一子数组则均大于等于分组值,这里的子组内并不排序;然后,再分别对两个子组进行再分组,重复递归这个过程,直到最后每两个元素作为一组进行再分组,整个数组就排好序了。
分组过程具体如下:同时从左往右和从右往左扫描数组,记扫描标记位为LP和RP。在LP一边,若发现元素小于分组值则跳过(即向右移动一位检查下一个元素),否则等待RP的扫描;RP若发现元素大于等于分组值跳过,直到找到小于分组值的元素,然后LP和RP位置的元素交换,重复这个过程,直到LP和RP相遇。
如图7,8所示,以11号元素为分组值,LP停在0号位置,RP跳过10号,停在图7中的9号位置(粉色柱),然后0号和9号交换,后续重复这个过程。
=> 
图7和图8:以11号柱作为分组值,0号和9号柱子将交换

图9:100个元素的数组经过两次分组后的效果
分组值的选择,可以想见,理想的分组值应该是待分组元素的中值,这样分组后子组在数量少几乎是一半对一半,不过找中值无疑增加了算法的工作量。图7中采用了更简单的方式,直接选数组最右边的元素为分组值,分组结束后,再把这个值交换到LP和RP相遇的位置,假如初始数组是从大到小排序的,这种情况下,选择最右边的元素作为分组值,其区分度就很差了。更好用的方法是所谓的取首尾中三项数据的中值或者平均值。
通过对算法过程的描述可知,其时间复杂度应该为:O(N*logN),比简单排序和希尔排序都要快。
参考程序如下:
/**
* Time Complexity: O(N*logN)
*/
public static void quickSort(int[] input, final int left, final int right) {
if (left < right) {
int partition = partition(input, left, right);
quickSort(input, left, partition - 1);
quickSort(input, partition, right);
}
} public static int median(int i1, int i2, int i3) {
int[] arrTmp = {i1, i2, i3};
int min = arrTmp[0];
int max = arrTmp[0];
for (int i = 1; i < arrTmp.length; i++) {
min = min < arrTmp[i] ? min : arrTmp[i];
max = max > arrTmp[i] ? max : arrTmp[i];
}
return (i1 + i2 + i3) - min - max;
} public static int partition(int[] input, final int left, final int right) {
final int mid = (left + right) / 2;
int pivotValue = median(input[left], input[mid], input[right]); int leftPtr = left;
int rightPtr = right;
while (leftPtr < rightPtr) {
if (input[leftPtr] < pivotValue) {
leftPtr++;
continue;
}
if (input[rightPtr] > pivotValue) {
rightPtr--;
continue;
}
int temp = input[leftPtr];
input[leftPtr] = input[rightPtr];
input[rightPtr] = temp;
leftPtr++;
rightPtr--;
} // 找出大于pivotalValue的第一个值的位置。
if (input[rightPtr] > pivotValue) {
return rightPtr;
} else {
return rightPtr + 1;
}
}
参考文献
【1】Java数据结构与算法 Rober Lafore 2nd
说明: 文中的图片来自文献【1】附带的Java applet演示小程序。
数据结构与算法之--高级排序:shell排序和快速排序的更多相关文章
- 数据结构和算法 – 11.高级排序算法(上)
对现实中的排序问题,算法有七把利剑可以助你马道成功. 首先排序分为四种: 交换排序: 包括冒泡排序,快速排序. 选择排序: 包括直接选择排序,堆排序. 插入排序 ...
- 为什么我要放弃javaScript数据结构与算法(第十章)—— 排序和搜索算法
本章将会学习最常见的排序和搜索算法,如冒泡排序.选择排序.插入排序.归并排序.快速排序和堆排序,以及顺序排序和二叉搜索算法. 第十章 排序和搜索算法 排序算法 我们会从一个最慢的开始,接着是一些性能好 ...
- Java数据结构和算法总结-冒泡排序、选择排序、插入排序算法分析
前言:排序在算法中的地位自然不必多说,在许多工作中都用到了排序,就像学生成绩统计名次.商城商品销量排名.新闻的搜索热度排名等等.也正因为排序的应用范围如此之广,引起了许多人深入研究它的兴趣,直至今天, ...
- 数据结构-排序-shell排序
shell排序 首先,希尔排序适用于待排序列关键有序. 接下来一步步图解SHELL排序 我为了方便理解内部操作.我先把代码输出整理下. #include<iostream> #includ ...
- 数据结构与算法——认识O(NlogN)的排序(2)
输入整型数组和排序标识,对其元素按照升序或降序进行排序 (一组测试用例可能会有多组数据) 接口说明 原型: void sortIntegerArray(Integer[] pIntegerArray, ...
- 数据结构与算法——认识O(NlogN)的排序(1)
归并排序 1) 整体就是一个简单递归,左边排好序.右边排好序.让其整体有序 2) 让其整体有序的过程里用了外排序方法 3) 利用master公式来求解时间复杂度 4) 归并排序的实质 时间复杂度0(N ...
- 数据结构与算法——常用高级数据结构及其Java实现
前文 数据结构与算法--常用数据结构及其Java实现 总结了基本的数据结构,类似的,本文准备总结一下一些常见的高级的数据结构及其常见算法和对应的Java实现以及应用场景,务求理论与实践一步到位. 跳跃 ...
- php面试题之二——数据结构和算法(高级部分)
二.数据结构和算法 1.使对象可以像数组一样进行foreach循环,要求属性必须是私有.(Iterator模式的PHP5实现,写一类实现Iterator接口)(腾讯) <?php class T ...
- 数据结构和算法 – 11.高级排序算法(下)
三.选择类排序 3.1.简单选择排序 http://www.cnblogs.com/tangge/p/5338734.html#XuanZe 3.2 堆排序 要知道堆排序,首先要了解一下二叉树的模型. ...
随机推荐
- [转载]php中sleep,flush,ob_flush函数介绍
<?phpecho str_pad(" ",1024);//当上面这句没有的时候浏览器没有任何输出 直到sleep函数设定的时间结束 才会输出//原因如下面截图for ($i ...
- Django Model 数据表
Django Model 定义语法 版本:1.7主要来源:https://docs.djangoproject.com/en/1.7/topics/db/models/ 简单用法 from djang ...
- ACE_DEBUG介绍及日志输出
转载于:http://blog.csdn.net/zzjxiaozi/article/details/6642925 ACE_DEBUG 常规的一些输出消息 ACE_ERROR 会提供程序出错 ...
- 阐述ArrayList、Vector、LinkedList的存储性能和特性?
ArrayList 和Vector他们底层的实现都是一样的,都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内 ...
- ZooKeeper屏障和队列的指南(七)
引言 在这个指南中,使用展示了使用ZooKeeper实现的屏障和生产-消费队列.我们分别称这些类为Barrier和Queue.这些例子假定你至少有一个运行的ZooKeeper服务. 两个原语都使用下面 ...
- [BZOJ1087][SCOI2005]互不侵犯King解题报告|状压DP
在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 好像若干月前非常Naive地去写过DFS... ...
- Quick-Cocos2dx-Community_3.6.3_Release 中 tolua++ 使用方法
参考文章1 http://www.aichengxu.com/view/45851 参考文章2 http://blog.csdn.net/pawleft/article/details/5212744 ...
- Desert King(POJ2728+最优比率生成树+二分)
题目链接:http://poj.org/problem?id=2728 题目: 题意:求一颗生成树,使得费用与距离的比值最小,其中距离等于两点之间的平面欧拉距离,费用为z坐标之差. 思路: 由上图我们 ...
- 大聊Python----多线程
Python里的多线程是假的多线程,不管有多少核,同一时间只能在一个核中进行操作!利用Python的多线程,只是利用CPU上下文切换的优势,看上去像是并发,其实只是个单线程,所以说他是假的单线程. 那 ...
- python2 处理urllib/urllib2错误并打印源码
import urllib2 import urllib url = 'http://localhost/12.php' data = {} data['cmd']='whoami' data = u ...