排序算法三:Shell插入排序
排序算法三:Shell插入排序
声明:引用请注明出处http://blog.csdn.net/lg1259156776/
引言
在我的博文《“主宰世界”的10种算法短评》中给出的首个算法就是高效的排序算法。本文将对排序算法做一个全面的梳理,从最简单的“冒泡”到高效的堆排序等。
上一篇博文《排序算法二:二分(折半)插入排序》讲述了直接插入排序,本文讲述第三种插入排序算法:Shell插入排序。实际上它是改进自插入排序和冒泡排序。
排序相关的的基本概念
- 排序:将一组杂乱无章的数据按一定的规律顺次排列起来。
- 数据表( data list): 它是待排序数据对象的有限集合。
- 排序码(key):通常数据对象有多个属性域,即多个数据成员组成,其中有一个属性域可用来区分对象,作为排序依据。该域即为排序码。每个数据表用哪个属性域作为排序码,要视具体的应用需要而定。
- 分类
- 内排序:指在排序期间数据对象全部存放在内存的排序;
- 外排序:指在排序期间全部对象个数太多,不能同时存放在内存,必须根据排序过程的要求,不断在内、外存之间移动的排序。
排序算法的分析
排序算法的稳定性
如果在对象序列中有两个对象r[i]和r[j] ,它们的排序码k[i]==k[j] 。如果排序前后,对象r[i]和r[j] 的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。
排序算法的评价
时间开销
- 排序的时间开销可用算法执行中的数据比较次数与数据移动次数来衡量。
- 算法运行时间代价的大略估算一般都按平均情况进行估算。对于那些受对象排序码序列初始排列及对象个数影响较大的,需要按最好情况和最坏情况进行估算
空间开销
算法执行时所需的附加存储。
Shell插入排序
基本思想
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt−l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。实际上当dt=1时,完全就是直接插入排序。但是经过多个分组的直接插入排序,最后一次完整的插入排序前数据表几乎已经是排好序了,因此shell sort充分利用了直接插入排序在数据表几乎已经有序的条件下工作高效的特点。
算法的C plus plus实现
根据算法的基本思想,shell插入排序的实现还是比较容易的,其C++实现如下:
#include <iostream>
#include <iomanip>
using namespace std;
void print(int ar[], int sz, int step)
{
for(int i = 0; i < sz; ++i) {
if(((i + 1) % step) != 0)
cout << setw(3) << ar[i];
else
cout << setw(3) << ar[i] << endl;
}
cout << endl;
}
void ShellSort(int a[], int sz)
{
int i, j;
int step, temp;
step = sz / 2 ;
while(step) {
print(a, sz, step);
cout << "==>" << endl;
for (i = step; i < sz; i++) {
temp = a[i];
j = i;
while (j >= step && a[j - step] > temp) {
a[j] = a[j - step];
j = j - step;
}
a[j] = temp;
}
print(a, sz, step);
cout << "current array" << endl;
print(a, sz, sz);
cout << "----------------" << endl;
step = step / 2.2;
}
}
int main(void)
{
int a[] = {13, 14, 94, 33, 82, 25, 59, 94, 65, 23, 45, 27, 73, 25, 39, 10};
const size_t sz = sizeof(a)/sizeof(a[0]);
cout << "Initial array" << endl;
print(a,sz,sz);
cout << "-------------" << endl;
ShellSort(a,sz);
cout << "Sorted array" << endl;
print(a,sz,sz);
return 0;
}
核心部分ShellSort中内层的while循环实现的是查找相应组内的插入位置,并提前进行位置的交换。而for循环控制的是从每个组内的第2个数开始重复在该组前面有序的数据表中实现直接插入排序。与直接插入排序的区别是shell插入排序一次for将所有组内的第i个数插入到各自前i-1个有序表中。
输出为:
Initial array
13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10
-------------
13 14 94 33 82 25 59 94
65 23 45 27 73 25 39 10
==>
13 14 45 27 73 25 39 10
65 23 94 33 82 25 59 94
current array
13 14 45 27 73 25 39 10 65 23 94 33 82 25 59 94
----------------
13 14 45
27 73 25
39 10 65
23 94 33
82 25 59
94
==>
13 10 25
23 14 33
27 25 45
39 73 59
82 94 65
94
current array
13 10 25 23 14 33 27 25 45 39 73 59 82 94 65 94
----------------
13
10
25
23
14
33
27
25
45
39
73
59
82
94
65
94
==>
10
13
14
23
25
25
27
33
39
45
59
65
73
82
94
94
current array
10 13 14 23 25 25 27 33 39 45 59 65 73 82 94 94
----------------
Sorted array
10 13 14 23 25 25 27 33 39 45 59 65 73 82 94 94
算法分析
- 增量序列的选择
Shell排序的执行时间依赖于增量序列。
好的增量序列的共同特征:- 最后一个增量必须为1;
- 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。
有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6nl.25之间。
- Shell排序的时间性能优于直接插入排序
希尔排序的时间性能优于直接插入排序的原因:- 当数据表初态基本有序时直接插入排序所需的比较和移动次数均较少。
- 当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
- 在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di−1作为距离排过序,使数据表较接近于有序状态,所以新的一趟排序过程也较快。
因此,希尔排序在效率上较直接插人排序有较大的改进。
- 稳定性
希尔排序是不稳定的。两个相同关键字在排序前后的相对次序是可能发生变化的。
这个方法对于大的数据集效率其实并不高,但是它是一个对小数量数据表(小于1000)进行排序的最快算法之一。另外,相对使用的内存较少。
2015-9-24 艺少
排序算法三:Shell插入排序的更多相关文章
- python实现排序算法三:插入排序
插入排序基本思想:假设一个无序数组A,则对于只有一个元素A[0]的子数组C来讲,其是有序的,然后将A[1]插入到C中,则就是将A[1]与A[0]进行比较,如果A[1]比A[0]小,则交换两者的顺序,这 ...
- Java常见排序算法之Shell排序
在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...
- Java常见排序算法之直接插入排序
在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...
- Java常见排序算法之折半插入排序
在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...
- 排序算法之直接插入排序Java实现
排序算法之直接插入排序 舞蹈演示排序: 冒泡排序: http://t.cn/hrf58M 希尔排序:http://t.cn/hrosvb 选择排序:http://t.cn/hros6e 插入排序: ...
- 七内部排序算法汇总(插入排序、Shell排序、冒泡排序、请选择类别、、高速分拣合并排序、堆排序)
写在前面: 排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的随意序列,又一次排列成一个按keyword有序的序列.因此排序掌握各种排序算法很重要. 对以下介绍的各个排序,我们假定全部排 ...
- 我的Java开发学习之旅------>Java经典排序算法之二分插入排序
一.折半插入排序(二分插入排序) 将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法.在处理A[i]时,A[0]--A[i-1]已经按关键码值排好序.所谓折半比较, ...
- IOS算法(三)之插入排序
直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其keyword大小插入到前面已经排好序的子序列中的适当位置,直到所有记录插入完毕为止. 设数组为a[0-n-1]. ...
- 结构-行为-样式-Js排序算法之 直接插入排序
最新因工作原因需要接触到算法,之前学习C++的时候有接触过算法,Javascript中实现算法其实也是大同小异.下面我讲下第一个实现的排序算法--直接插入排序.基本实现思路:假定一个数组中前n(n&g ...
随机推荐
- (1)打鸡儿教你Vue.js
当今世界不会Vue.js,前端必定路难走 一个JavaScript MVVM库 以数据驱动和组件化的思想构建的 Vue.js是数据驱动 HTML/CSS/JavaScript/ES6/HTTP协议/V ...
- git 常用命令使用,git bash通用命令
git 常用命令 1.强制推送(慎用,除非你认为其他冲突等可以丢弃 或者不是很重要) git push -- force 2.创建文件等小命令 touch a // 创建一个a文件 >> ...
- CF786E ALT
题意 有一棵 \(n\) 个点的树和 \(m\) 个人,第 \(i\) 个人从 \(u_i\) 走到 \(v_i\) 现在要发宠物,要求一个人要么他自己发到宠物,要么他走的路径上的都有宠物. 求最小代 ...
- 数据结构——KMP算法
算法介绍 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法).KMP算法的核心是利用 ...
- [转载]workbench分网---mapped face meshing面映射网格划分
原文地址:face meshing面映射网格划分">workbench分网---mapped face meshing面映射网格划分作者:一丝尘埃 face meshing面映射网格划 ...
- C#MD5方法
不同形式,一样结果 /// <summary> /// 获取大写的MD5签名结果 /// </summary> /// <param name="encypSt ...
- legend3---开发总结1
legend3---开发总结1 一.总结 一句话总结: 管它柳绿花红,我自潇洒人间 1.数据库字段命名:比如l_id? 见名知意,字段唯一,方便连表 2.所有方法在服务端都要加验证? 比如先查数据再更 ...
- golang gin解决跨域访问
package middleware import ( "github.com/gin-gonic/gin" "net/http") func Cors() g ...
- Microsoft Visual C++ Runtime library not enough space for thread data
Microsoft Visual C++ Runtime library not enough space for thread data 电脑最近一直在运行的时候,弹出提示框,如下: 解决办 ...
- Python 中的 -> 是什么意思
函数标注通常用于 类型提示:例如以下函数预期接受两个 int 参数并预期返回一个 int 值:```def sum_two_numbers(a: int, b: int) -> int:retu ...