第16周项目1-验证算法(2)希尔排序

本文通过C语言实现了希尔排序算法,并展示了排序过程中每一步的数据变化。通过对希尔排序的讲解,帮助读者理解该算法的工作原理。

问题及代码:

希尔排序:

/*  
* Copyright (c)2016,烟台大学计算机与控制工程学院  
* All rights reserved.  
* 文件名称:bigice.cbp  
* 作    者:bigice  
* 完成日期:2016年12月23日  
* 版 本 号:v1.0  
  
* 问题描述:验证希尔排序 
  
* 输入描述:无  
* 程序输出:测试数据  
*/    
#include <stdio.h>  
#define MaxSize 20  
typedef int KeyType;    //定义关键字类型  
typedef char InfoType[10];  
typedef struct          //记录类型  
{  
    KeyType key;        //关键字项  
    InfoType data;      //其他数据项,类型为InfoType  
} RecType;              //排序的记录类型定义  
  
void ShellSort(RecType R[],int n)   //希尔排序算法  
{  
    int i,j,gap;  
    RecType tmp;  
    gap=n/2;                //增量置初值  
    while (gap>0)  
    {  
        for (i=gap; i<n; i++) //对所有相隔gap位置的所有元素组进行排序  
        {  
            tmp=R[i];  
            j=i-gap;  
            while (j>=0 && tmp.key<R[j].key)//对相隔gap位置的元素组进行排序  
            {  
                R[j+gap]=R[j];  
                j=j-gap;  
            }  
            R[j+gap]=tmp;  
            j=j-gap;  
        }  
        gap=gap/2;  //减小增量  
    }  
}  
  
int main()  
{  
    int i,n=11;  
    RecType R[MaxSize];  
    KeyType a[]= {16,25,12,30,47,11,23,36,9,18,31};  
    for (i=0; i<n; i++)  
        R[i].key=a[i];  
    printf("排序前:");  
    for (i=0; i<n; i++)  
        printf("%d ",R[i].key);  
    printf("\n");  
    ShellSort(R,n);  
    printf("排序后:");  
    for (i=0; i<n; i++)  
        printf("%d ",R[i].key);  
    printf("\n");  
    return 0;  
}  




运行结果:


排序中输出每一趟的中间结果:

#include <stdio.h>  
#define MaxSize 20  
typedef int KeyType;    //定义关键字类型  
typedef char InfoType[10];  
typedef struct          //记录类型  
{  
    KeyType key;        //关键字项  
    InfoType data;      //其他数据项,类型为InfoType  
} RecType;              //排序的记录类型定义  
  
void ShellSort(RecType R[],int n)   //希尔排序算法  
{  
    int i,j,gap,k;  
    RecType tmp;  
    gap=n/2;                //增量置初值  
    while (gap>0)  
    {  
        for (i=gap; i<n; i++) //对所有相隔gap位置的所有元素组进行排序  
        {  
            tmp=R[i];  
            j=i-gap;  
            while (j>=0 && tmp.key<R[j].key)//对相隔gap位置的元素组进行排序  
            {  
                R[j+gap]=R[j];  
                j=j-gap;  
            }  
            R[j+gap]=tmp;  
            j=j-gap;  
        }  
        printf("gap=%d:",gap);  
        for (k=0; k<n; k++)  
            printf("%d ",R[k].key);  
        printf("\n");  
        gap=gap/2;  //减小增量  
    }  
}  
  
int main()  
{  
    int i,n=11;  
    RecType R[MaxSize];  
    KeyType a[]= {16,25,12,30,47,11,23,36,9,18,31};  
    for (i=0; i<n; i++)  
        R[i].key=a[i];  
    printf("排序前:");  
    for (i=0; i<n; i++)  
        printf("%d ",R[i].key);  
    printf("\n");  
    ShellSort(R,n);  
    printf("排序后:");  
    for (i=0; i<n; i++)  
        printf("%d ",R[i].key);  
    printf("\n");  
    return 0;  
}  




知识点总结:希尔排序




心得体会:了解了希尔排序的基本过程


<think>我们参考引用[1]和[2]可以知道,希尔排序的基本思想是:将待排序的记录按一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减小,每组包含的关键词越来越多,当增量减至1时,整个序列被分成一组,算法便终止。根据引用[4],我们知道在希尔排序中,我们使用一个增量序列(dk[]),然后对每个增量进行一趟插入排序(即ShellInsert),每一趟排序中,我们以dk[i]为增量,将待排序序列分成若干组(每组间隔为dk[i]),然后对每组进行插入排序。而引用[3]给出了ShellSort函数的基本框架:它接受一个顺序表指针L,增量数组dk和增量数组的长度t。然后对每个增量dk[i]调用ShellInsert函数(即一趟插入排序)。因此,我们可以将希尔排序分成两个函数:1.ShellInsert(SqList*L,intdk):用于进行一趟增量为dk的插入排序。2.ShellSort(SqList*L,intdk[],intt):用于完成整个希尔排序,对增量数组中的每个增量依次调用ShellInsert。另外,根据用户要求,需要按照给定增量序列输出每趟排序结果。因此我们在每一趟排序后输出当前顺序表的状态。下面我们根据引用[4]中关于直接插入排序的思路来编写ShellInsert函数。注意,希尔排序中的每一趟增量排序相当于对多个子序列(每个子序列由间隔为dk的元素组成)分别进行直接插入排序。我们可以将直接插入排序中的1替换为dk,但注意不是分组独立做,而是交替进行。具体实现思路:在ShellInsert中,我们假设顺序表结构定义如下(使用引用[4]中的SqList):#defineMAXSIZE20typedefintKeyType;//定义关键字类型为整型typedefstruct{KeyTypekey;//关键字项//...其他数据项}RedType;typedefstruct{RedTyper[MAXSIZE+1];//r[0]一般用作哨兵或闲置intlength;//顺序表长度}SqList;注意:通常我们在进行排序时,元素存储在r[1..length]或r[0..length-1]均可。但为了和引用[4]保持一致,并方便使用哨兵,这里我们采用r[1]开始存储记录,位置0可以闲置或用作哨兵。但是引用[3]中的函数原型为:voidShellInsert(SqList*L,intdk);因此我们假设顺序表L中,0号位置可以使用(作为哨兵),而实际数据从1到length。ShellInsert的步骤(以增量为dk):从第dk+1个元素开始(即i=dk+1)一直到最后一个元素(i<=L->length),对每个元素执行以下操作:-如果当前元素小于它前面间隔dk的元素(即L->r[i]< L->r[i-dk]),则需要将当前元素插入到前面分组的有序位置。-具体插入过程:将当前元素放入哨兵位置(即L->r[0]),然后从当前位置开始,向前每隔dk个位置比较,将比它大的元素向后移动dk个位置,直到找到插入位置。具体代码实现如下(每趟增量排序):for(inti=dk+1;i<=L->length;i++){if(L->r[i].key< L->r[i-dk].key){//需要将L->r[i]插入有序增量子表L->r[0]=L->r[i];//暂存在0号位置intj;for(j=i-dk;j>0&&(L->r[0].key< L->r[j].key);j-=dk){L->r[j+dk]=L->r[j];//记录后移}L->r[j+dk]=L->r[0];//插入}}注意:这里我们只对以dk为增量的子序列中的一组进行插入排序。实际上,上述循环会依次处理每个分组中的下一个待排序元素(注意i是从dk+1开始,每次加1,相当于轮流处理不同的分组)。完整希尔排序函数ShellSort:根据给定的增量序列,对每个增量dk进行ShellInsert排序,并在每趟排序后输出当前顺序表状态。为了输出每趟排序结果,我们在每趟排序后打印顺序表。我们还需要考虑增量序列的给定。常见的增量序列有希尔原始序列(...,5,3,1),这里用户要求按照给定的增量序列进行排序。我们根据用户需求编写代码,假设我们使用引用[5]中的例子:关键字序列{49,38,65,97,76,13,27,49,55,04},增量序列可以是{5,3,1}。注意:由于引用[3]中的ShellSort函数调用了ShellInsert,我们在此进行类似实现。由于引用中没有给出顺序表的具体定义,我们按上述假设自行定义顺序表结构。下面我们给出完整的代码示例(C语言):</think> ### 希尔排序实现方法 希尔排序的基本思想参考引文[1][2][4]: - 将记录按下标的一定增量(gap)分组 - 对每组使用直接插入排序 - 随着增量逐渐减小(直到gap=1),每组包含的关键词越来越多 - 当增量减至1时,整个序列恰被分成一组,完成排序 #### 关键函数实现(C语言) ```c #include <stdio.h> typedef int KeyType; #define MAXSIZE 100 typedef struct { KeyType r[MAXSIZE]; // 存储元素 int length; // 序列长度 } SqList; // 一趟增量dk的插入排序(ShellInsert) void ShellInsert(SqList *L, int dk) { for(int i = dk; i < L->length; i++) { KeyType temp = L->r[i]; // 当前待插入元素 int j = i - dk; // 在子序列中寻找插入位置 while(j >= 0 && L->r[j] > temp) { L->r[j + dk] = L->r[j]; // 元素后移 j -= dk; } L->r[j + dk] = temp; // 插入正确位置 } } // 完整希尔排序(ShellSort) void ShellSort(SqList *L, int dk[], int t) { printf("初始序列: "); for(int i = 0; i < L->length; i++) printf("%d ", L->r[i]); printf("\n\n"); for(int k = 0; k < t; k++) { ShellInsert(L, dk[k]); // 按当前增量排序 // 输出当前排序结果 printf("增量dk=%d时: ", dk[k]); for(int i = 0; i < L->length; i++) printf("%d ", L->r[i]); printf("\n"); } } ``` #### 使用示例 ```c int main() { SqList L = {{49, 38, 65, 97, 76, 13, 27, 49, 55, 04}, 10}; // 初始序列[^5] int dk[] = {5, 3, 1}; // 增量序列 int t = sizeof(dk)/sizeof(dk[0]); ShellSort(&L, dk, t); return 0; } ``` #### 输出结果: ``` 初始序列: 49 38 65 97 76 13 27 49 55 4 增量dk=5时: 13 27 49 55 4 49 38 65 97 76 增量dk=3时: 13 4 49 38 27 49 55 65 97 76 增量dk=1时: 4 13 27 38 49 49 55 65 76 97 ``` ### 关键点说明 1. **ShellInsert函数** - 以增量`dk`为步长进行分组插入排序 - 时间复杂度为$O(n^2)$,但通过较大增量先移动距离较远的元素,显著减少后续操作 2. **ShellSort函数** - 按增量序列依次调用ShellInsert - 增量序列推荐选择$O(n^{3/2})$复杂度的序列如$\{2^k-1, ..., 7, 3, 1\}$ 3. **空间复杂度** - 仅需常数级额外空间$O(1)$,是**原地排序算法** 4. **稳定性** - 相同元素可能被分到不同组导致相对位置改变(非稳定排序)[^1][^2] [^1]: 希尔排序是插入排序的高效改进版本 [^2]: 通过缩小增量分组实现高效移动 [^4]: 插入排序的核心思想是边插入边排序 [^5]: 实际排序过程验证算法有效性
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值