希尔排序

希尔排序一种是很常见的排序算法,该算法在1959年由Donald Shell公布。

希尔排序的奥妙

  • 1、希尔排序的特殊之处是将待排序的数列看做是一个二维的矩阵,对序列的各列进行排序,我们将所有矩阵的w列的各自排序总称为w-sorting;同时我们将各列都排序完成的矩阵叫做w-ordered
  • 2、递减增量;希尔排序的过程中不断重排矩阵,使其宽度w更窄,重排后都要进行w-sorting操作;直到矩阵的宽度为1
  • 3、步长序列;在整个排序过程中使用到的宽度构成的逆序列叫做步长序列;步长序列要求首项为1,其余项递增;如:h = {w1 = 1, w2, w3, ..., wk}

总结来说就是:假设初始矩阵的宽度为w;在得到该矩阵后对其每一列进行排序操作,排序完成后将矩阵的宽度w按照一定的序列缩小;缩小后又进行列排序;如此往返,直到宽度w=1,此时完成列排序后就得到了一个有序序列。

ps: 步长序列有很多种的选择,每一种选择都对应着一种不同样的算法,它们有不同的性能,因此与其说希尔排序是一种算法,不如说它是一种排序框架。

实例

假设有待排序序列:99,20,15,30,19,07,10,33,42,82,55,02,78,66,77

第一次取矩阵的宽度为 8,则可转化为如下:

99,20,15,30,19,07,10,33
42,82,55,02,78,66,77

经过列排序后为:

42,20,15,02,19,07,10,33
99,82,55,30,78,66,77

第二次取矩阵宽度为4,则可将已经进行列排序的上述矩阵转换成:

42,20,15,02
19,07,10,33
99,82,55,30
78,66,77

再次对矩阵列排序:

19,07,10,02
42,20,15,30
99,82,55,33
78,66,77

第三次取矩阵的宽度为3:

19,07,10
02,42,20
15,30,99
82,55,33
78,66,77

列排序后:

02,07,10
15,30,20
19,42,33
78,55,77
82,66,99

第4次取矩阵宽度为2:

02,07
10,15
30,20
19,42
33,78
55,77
82,66
99

列排序后得到:

02,07
10,15
19,20
30,42
33,66
55,77
82,78
99

最后w缩小到1:

02
07
10
15
19
20
30
42
33
66
55
77
82
78
99

再次进行列排序:

02
07
10
15
19
20
30
33
42
55
66
77
78
82
99
为何如此大费周章

既然最后将矩阵压缩到宽度为1的时候还要进行一次列排序,那么为何不直接进行排序,而要如此大费周章?

在回答这个问题之前,我们先看一个关于邮资的问题:在某国家寄一封普通的信需要50分,寄一个明信片需要35分;该国家有且仅有两种面额的邮票,4分邮票和13分邮票。请问能否恰好找到一种组合使得邮资分别与之对应呢?

相信经过一番的计算之后能够找到恰好组成50分的算法(6个4分邮票和2个13分邮票),但是并不能找到组成35分的邮资算法。

不难理解,所有能够组成的邮资都满足表达式:4m + 13n ;m,n ∈ N,这样的式子也叫做线性组合。

推而广之,使用g,h分别代表两种邮票的面额,则可组成的邮资:

f = mg + nh; m,n ∈ N(又叫做 g和h的线性组合)

尽管如此,始终还是有些邮资无法组合出来,我们将这些邮资放入一个集合:

N(g, h) = {不能有g,h线性组合成的邮资}

关注其中的最大者,将其设为x,设x(g, h) = max(N(g, h))

根据数论可以得到:x(g, h) = (g-1)*(h-1) - 1 = gh - (g + h)

不妨将上述的两种邮票面额带入其中,得到 x(4, 13) = 4 x 13 - (4 + 13) = 35;恰好35分为不能组合成的邮资的最大值。也就是说大于35分的邮资都能够恰好由这两种面额的邮票组成。

邮资问题与希尔排序的关系

我们先引入h-sorting 和 h-ordered的概念:

  • h-ordered,在一个序列中,任何距离为h的元素都保持前小后大的次序,我们就称它为h-orderedS[i] ≤ S[i + h] ; 0 ≤ i < n - h
  • h-sorting,我们使用希尔排序的方式,将序列从逻辑上转换成有h列的二维矩阵,然后再进行列排序,这个过程就叫h-sorting

定理K

假设矩阵经过一次 g-sorting,然后进行一次 h-sorting;那么从g-ordered到h-ordered后,该矩阵是否还有着g-ordered的特性呢?

答案是:具有g-ordered的特性。[Knuth,ACP Vol.3 p.90]

证明过程大家可以百度了解。

在回到线性组合,假设一个序列,同时满足g-orderedh-ordered,那么不难推出它也满足(g+h)-ordered;同理合理推而广之这个序列也将是满足(mg+nh)-ordered。简而言之:能够表示为线性组合即为顺序的。

对一个同时满足g-orderedh-ordered的序列(g和h互质);考察其第i个元素,那么与其不能构成线性组合的最大元素步长差为:(g-1)(h-1);在大与此步长之后的元素与i都是顺序的;也就是说能与i产生逆序的元素在i到其后(g-1)(h-1)之间;随着希尔排序的进行,这个范围也在缩小,能够产生的逆序对也就逐渐变小。由此我们不难联想到输入敏感的插入排序

内部排序算法的实现(列排序)

列排序时,必须采用输入敏感的算法,以保证有序性可以持续改善,且总体的成本足够低廉

对于输入敏感,我们很容易想到插入排序(insertion sort),当插入排序的输入序列有序时,它的时间复杂度就接近线性(O(n))。因此,插入排序可以是希尔排序的内部列排序的实现。

步长序列的选择

较早且常见的一种步长序列是希尔序列:

H = {1,2,4,8,...,2^k,...}

该序列并不是一个较好的序列,采用该序列进行希尔排序,最坏的情况下将需要O(n²)的运行时间。

如:

11 4 14 3 10 0 15 1 9 6 8 7 13 2 12 5

上述序列在完成2-sorting后,必然为如下结果:

8 0 9 1 10 2 11 3 12 4 13 5 14 6 15 7

不难看出其中的逆序对为算数级数量级(1+2+3+...+7 + 8);因此对于1-sorting仍需要:1+2...+2^(n-1)时间。

因此在使用希尔排序时,选择一个好的步长序列是至关重要的。

C/C++实现代码
void shellSort(int * nums, int count) {
    int w, hs[] = {8,4,2,1};  // hs is the step sequence.
    for (int i = 0; i < 4; i++) {
        w = hs[i];
        for (int j = 0; j < w; j++) {
            // insertion sort
            int row = 1, searchRow = 0, value = 0;
            while (j + row * w < count) {
                value = nums[j + row * w];
                searchRow = row - 1;
                while (searchRow >= 0 && nums[j + searchRow * w] > value) {
                    nums[j + (searchRow + 1) * w] = nums[j + searchRow * w];
                    searchRow--;
                }
                nums[j + (searchRow + 1) * w] = value;
                row++;
            }
        }
    }
}

注:本文总结自《数据结构(C++语言版)》第三版,清华大学出版社,邓俊辉编著

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 希尔排序是对插入排序的一种改进,也叫递减增量排序,算法过程中通过对增量值的递减调整,形成每一个增量值对应的一个或多...
    zhipingChen阅读 2,854评论 0 10
  • 由来 希尔排序是根据插入排序来实现的。 希尔排序根据插入排序的以下两点性质而提出的改进方法: 1.插入排序在对...
    MacXin阅读 145评论 0 0
  • 详细代码请参考Algorithm。参考代码比文字好理解。 希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳...
    一个人在路上走下去阅读 2,827评论 2 8
  • Demo_github 希尔排序 希尔排序(Shell Sort)又称为缩小增量排序,它是一种插入排序。它是直接插...
    SkyMing一C阅读 896评论 0 1
  • 前些日子赵先生上了一下魔兽,兴奋的告诉我:“呀,魔兽十一周年了,我一进去就得了一个成就!~”然后他沉默了一下,说:...
    筱之妖妖阅读 191评论 0 0