[数据结构]希尔排序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数据结构]希尔排序相关的知识,希望对你有一定的参考价值。

一、问题描述

内部排序是一件具有重大意义的问题,许多项目的实现中都需要用到排序。

我们知道,排序的算法有许多种,每种排序算法的时间复杂度和空间复杂度不尽相同。在解决实际问题时,往往需要根据实际需要选择排序算法。

本实验重点介绍希尔排序的算法实现及其原理,简要说明与其相关的直接排序算法,并讨论希尔排序中步长的选择对排序速度的影响。

二、数据结构——顺序结构

本实验重点在算法实现上,数据结构的思想被弱化了。在排序过程中,由于维护序关系的需要,要有交换的操作,这就破坏了ADT的物理位置的相邻反映逻辑的依次的性质,可以说这里的顺序结构只是一个二次结构。因此,本实验不对此作过多说明。

三、算法的设计和实现

(1)直接排序(直接插入排序)

a)简述

以不降序排序为例,直接排序在每一轮排序中,找到出现的第一个破坏不降序性质的元素,将其值保存至a[0]处,然后向前扫,直到找到小于或等于a[0]的元素a[i],然后将a[0]插入到a[i]之后,后面的元素依次后移一位。重复上述过程,由循环不变式可知,最后得到的序列是保持不降序性质的。

可以看出,直接排序是一种基于插入的排序算法。算法的时间复杂度为O(n^2)。值得注意的是,直接排序只涉及到两个相邻数的交换,所以是稳定的。

b)例子:空白处的表示不改变,当前循环将不会访问到。

a[0] 49 38 65 97 76 13 27
38 38 49          
76 38 49 65 76 97    
13 13 38 49 65 76 97  
27 13 27 38 49 65 76 97

(1)希尔排序

a)简述

希尔排序是直接排序的一个升级版。以步长将待排序的序列划分成几个部分,将每个部分进行排序,使得整个序列粗有序,从而减少序列的移动次数,减小时间复杂度。

b)算法描述

首先枚举步长gap,以步长将待排序的序列分为gap个子序列,对每个子序列进行直接排序。步长从粗到细,最后一次步长为1。步长的选取直接关系到希尔排序的时间效率,一般来说,为了尽量避免重复计算,选取的步长要互质;至于更系统的讨论将在下面给出。

c)例子

步长 49 38 65 97 76 13 27 49 55 4
5 13 27 49 55 4 49 38 65 97 76
3 13 4 49 38 27 49 55 65 97 76
1 4 13 27 38 49 49 55 65 76 97

d)可以看出,希尔排序不仅基于插入,还有大跨度交换的操作,所以希尔排序是不稳定的。

四、预期结果和实验中的问题

1、预期结果

程序能够正确地将一个序列按照不递减的顺序排序。下图为一个例子。

技术分享

2、实验中的问题及思考

(1)问题:希尔排序的步长选择对算法效率的影响。

(2)解答:这是一个公开问题,似乎还没有一个特别完美的解释。维基百科上有相关的说明。https://en.wikipedia.org/wiki/Shellsort

附:c++源代码:

技术分享
 1 /*
 2 项目:shell sort
 3 作者:张译尹
 4 */
 5 #include <iostream>
 6 #include <cstdio>
 7 #include <cstring>
 8 
 9 using namespace std;
10 #define MaxN 120
11 
12 int gap[200]; //步长 2^k+1 第一项改为1 
13 int n;
14 
15 template <class T> class My_list
16 {
17 private:
18     T Elem[MaxN]; //待排序的元素 
19     int Len; //元素个数 
20 public:
21     void Init()
22     {
23         memset(Elem, 0, sizeof(Elem));
24         Len = 0;
25     }
26     void Insert_back(T x)
27     {
28         Elem[++Len] = x;
29     }
30     void Print()
31     {
32         int i;
33         for(i = 1; i < Len; i++)
34             printf("%d ", Elem[i]);
35         printf("%d\n", Elem[i]);
36     }
37     int GetLen()
38     {
39         return Len;
40     }
41     void ShellSort()
42     {
43         int i, j, k;
44         for(k = gap[0]; k >= 1; k--)
45         {
46             for(i = gap[k]; i <= Len; i++)
47             {
48                 Elem[0] = Elem[i];
49                 
50                 //把比当前元素大的都往后移动 
51                 for(j = i - gap[k]; j >= 0 && Elem[0] < Elem[j]; j -= gap[k])
52                     Elem[j + gap[k]] = Elem[j];
53                 Elem[j + gap[k]] = Elem[0];
54             }
55         }
56     }
57 };
58 
59 void Read(My_list <int> &L)
60 {
61     int i, x;
62     L.Init();
63     printf("请输入需要排序的数的个数。\n");
64     scanf("%d", &n);
65     printf("请输入需要排序的数列。\n"); 
66     for(i = 1; i <= n; i++)
67     {
68         scanf("%d", &x);
69         L.Insert_back(x); //把x插入到最后 
70     }
71 }
72 
73 void Pre_gap(My_list <int> &L)
74 {
75     int i, tmp = 2, n = L.GetLen();
76     for(i = 2; ; i++)
77     {
78         tmp = ((tmp - 1) << 1) + 1;
79         gap[i] = tmp;
80         if(tmp >= n / 2)
81             break;
82     }
83     gap[1] = 1;
84     gap[0] = i;
85 }
86 
87 int main()
88 {
89     int GapNum;
90     My_list <int> L;
91     Read(L);
92     Pre_gap(L);
93     L.ShellSort();
94     printf("升序排序后的数列:\n");
95     L.Print();
96     return 0;
97 }
View Code

 

以上是关于[数据结构]希尔排序的主要内容,如果未能解决你的问题,请参考以下文章

堆排/快排/希尔/归并排序,谁更快?

《算法》笔记 3 - 选择排序插入排序希尔排序

经典排序算法——希尔排序

希尔排序的介绍和希尔排序基本思想以及代码实现

(王道408考研数据结构)第八章排序-第二节:直接插入排序和希尔排序

希尔排序总结