第十六周项目1-(5)直接选择排序(6)堆排序

本文通过一组测试数据展示了直接选择排序和堆排序两种经典排序算法的实现过程及运行结果,帮助读者理解这两种算法的工作原理。

问题及代码:

/* 
* Copyright (c)2016,烟台大学计算机与控制工程学院 
* All rights reserved. 
* 文件名称:ccc.cpp 
* 作    者:陈梦雪
* 完成日期:2016年12月16日 
* 版 本 号:v1.0 
* 问题描述:用序列{57,40,38,11,13,34,48,75,6,19,9,7}作为测试数据,验证直接选择排序、堆排序。 
* 输入描述:无 
* 程序输出:测试数据 
*/  

代码:

直接选择排序:

#include <stdio.h>  
#define MaxSize 20  
typedef int KeyType;    //定义关键字类型  
typedef char InfoType[10];  
typedef struct          //记录类型  
{  
    KeyType key;        //关键字项  
    InfoType data;      //其他数据项,类型为InfoType  
} RecType;              //排序的记录类型定义  
void SelectSort(RecType R[],int n)  
{  
    int i,j,k,l;  
    RecType temp;  
    for (i=0; i<n-1; i++)           //做第i趟排序  
    {  
        k=i;  
        for (j=i+1; j<n; j++)   //在当前无序区R[i..n-1]中选key最小的R[k]  
            if (R[j].key<R[k].key)  
                k=j;            //k记下目前找到的最小关键字所在的位置  
        if (k!=i)               //交换R[i]和R[k]  
        {  
            temp=R[i];  
            R[i]=R[k];  
            R[k]=temp;  
        }  
        printf("i=%d: ",i);  
        for (l=0; l<n; l++)  
            printf("%d ",R[l].key);  
        printf("\n");  
    }  
}  
int main()  
{  
    int i,n=10;  
    RecType R[MaxSize];  
    KeyType a[]= {57,40,38,11,13,34,48,75,6,19,9,7};  
    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");  
    SelectSort(R,n);  
    printf("排序后:");  
    for (i=0; i<n; i++)  
        printf("%d ",R[i].key);  
    printf("\n");  
    return 0;  
}  
运行结果:


2)堆排序:

#include <stdio.h>
#define MaxSize 20
typedef int KeyType;    //定义关键字类型
typedef char InfoType[10];
typedef struct          //记录类型
{
    KeyType key;        //关键字项
    InfoType data;      //其他数据项,类型为InfoType
} RecType;              //排序的记录类型定义


//调整堆
void sift(RecType R[],int low,int high)
{
    int i=low,j=2*i;                        //R[j]是R[i]的左孩子
    RecType temp=R[i];
    while (j<=high)
    {
        if (j<high && R[j].key<R[j+1].key)  //若右孩子较大,把j指向右孩子
            j++;                                //变为2i+1
        if (temp.key<R[j].key)
        {
            R[i]=R[j];                          //将R[j]调整到双亲结点位置上
            i=j;                                //修改i和j值,以便继续向下筛选
            j=2*i;
        }
        else break;                             //筛选结束
    }
    R[i]=temp;                                  //被筛选结点的值放入最终位置
}


//堆排序
void HeapSort(RecType R[],int n)
{
    int i;
    RecType temp;
    for (i=n/2; i>=1; i--) //循环建立初始堆
        sift(R,i,n);
    for (i=n; i>=2; i--) //进行n-1次循环,完成推排序
    {
        temp=R[1];       //将第一个元素同当前区间内R[1]对换
        R[1]=R[i];
        R[i]=temp;
        sift(R,1,i-1);   //筛选R[1]结点,得到i-1个结点的堆
    }
}


int main()
{
    int i,n=10;
    RecType R[MaxSize];
   KeyType a[]= {57,40,38,11,13,34,48,75,6,19,9,7};
 for (i=1; i<=n; i++)
        R[i].key=a[i];
    printf("排序前:");
    for (i=1; i<=n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    HeapSort(R,n);
    printf("排序后:");
    for (i=1; i<=n; i++)
        printf("%d ",R[i].key);
    printf("\n");
    return 0;
}

运行结果:



<think>嗯,用户想让我详细分析各种排序算法的原理和各趟排序过程。首先,我需要确定用户的基础水平,可能是学生或者刚学数据结构的人。他们需要清晰的步骤和例子,可能还不太明白这些排序之间的区别和应用场景。 首先,我得回忆每种排序的基本原理。比如直接插入排序是逐个插入到已排序序列,折半插入用二分法找位置,希尔排序是分组插入。冒泡和快速都属于交换排序,但快排用了分治。选择排序每次选最小,堆排序用堆结构。归并是分治合并,基数按位分配,计数则是统计频率。 用户可能需要每个排序的例子,所以得准备具体的数字序列,并一步步展示每趟的变化。比如用同一个例子,比如[8, 3, 5, 1, 2],但基数排序可能需要更多位数,比如三位数。或者调整例子以适应不同排序的需求。 需要注意各趟排序的细节,比如希尔排序的增量选择堆排序的建堆和调整,归并的分割和合并过程。还要确保例子中的步骤准确,避免错误,比如快速排序的分区操作是否正确。 另外,用户可能希望对比这些排序的优缺点,虽然问题中没有明确要求,但详细分析各趟过程其实也间接展示了时间复杂度和稳定性。比如直接插入排序在部分有序时效率高,希尔排序通过分组提高效率,快速排序的平均性能好,但最坏情况差。 需要检查LaTeX格式是否符合要求,行内公式用$...$,独立公式用$$...$$,并正确换行。例如,在堆排序中,可能需要用公式表示堆的结构,或者归并排序的分治步骤。 可能遇到的难点是确保每个步骤描述清晰,比如基数排序的每一趟分配和收集过程,计数排序的计数数组和累加过程。需要用具体的数字示例,让用户更容易理解。 还要注意术语的中文表达是否正确,比如“折半”而不是“二分”,“希尔”排序的音译是否正确。同时,保持结构清晰,分点列出每个排序的原理和步骤,使用标题和分段,帮助用户逐步理解。 最后,确保回答真实可靠,没有遗漏关键步骤或错误,比如在快速排序中基准元素的选择和分区的正确性,归并排序的合并操作是否正确处理剩余元素等。可能需要多次检查每个例子中的数字变化是否正确,避免误导用户。</think>以下是对常见排序算法的原理分析及示例演示(以序列[8, 3, 5, 1, 2]为例): --- ### 一、直接插入排序 **原理**:将未排序元素逐个插入已排序序列的正确位置 **过程演示**: ``` 初始序列:[8|3,5,1,2] # | 左边为已排序区 第1趟: [3,8|5,1,2] # 插入3 第2趟: [3,5,8|1,2] # 插入5 第3趟: [1,3,5,8|2] # 插入1 第4趟: [1,2,3,5,8] # 插入2 ``` --- ### 二、折半插入排序 **原理**:在直接插入排序基础上,用二分查找确定插入位置 **过程演示**: ``` 插入5时:在[3,8]中使用二分法找到插入位置1 插入1时:在[3,5,8]中找到位置0 其他步骤与直接插入相同 ``` --- ### 三、希尔排序 **原理**:按增量分组进行插入排序(示例增量序列5/2=2) **过程演示**: ``` 增量2分组:[8,5,2]和[3,1] 第1趟组内排序:[2,5,8]和[1,3] → 整体[2,1,5,3,8] 增量1分组:全体元素 第2趟完成排序:[1,2,3,5,8] ``` --- ### 四、冒泡排序 **原理**:相邻元素两两比较,将最大元素"冒泡"到末尾 **过程演示**: ``` 第1趟:[3,5,1,2,8] # 8就位 第2趟:[3,1,2,5,8] # 5就位 第3趟:[1,2,3,5,8] # 3就位 第4趟无交换,排序完成 ``` --- ### 五、快速排序 **原理**:选取基准元素进行分区(示例基准选第一个元素) **过程演示**: ``` 初始基准8 → 分区[3,5,1,2]和[] 左子区基准3 → 分区[1,2]和[5] 继续递归排序,最终得到[1,2,3,5,8] ``` --- ### 六、简单选择排序 **原理**:每次选择最小元素放到已排序序列末尾 **过程演示**: ``` 第1趟选1 → [1|3,5,8,2] 第2趟选2 → [1,2|5,8,3] 第3趟选3 → [1,2,3|8,5] 第4趟选5 → [1,2,3,5,8] ``` --- ### 七、堆排序 **原理**:建立大顶堆,交换堆顶与末尾元素后调整堆 **过程演示**: ``` 建堆:[8,3,5,1,2] → 初始堆结构 第1次交换:8↔2 → 调整堆[5,3,2,1,8] 第2次交换:51 → 调整堆[3,1,2,5,8] ... 最终得到[1,2,3,5,8] ``` --- ### 八、归并排序 **原理**:分治法将序列拆分为子序列,合并时排序 **过程演示**: ``` 拆分至单元素:[8][3][5][1][2] 逐层合并: [3,8] ← 合并[8]和[3] [1,5] ← 合并[5]和[1] [1,2,5] ← 合并[1,5]和[2] 最终合并[3,8]和[1,2,5] → [1,2,3,5,8] ``` --- ### 九、基数排序 **原理**:按位分配收集(示例使用三位数[802, 123, 555, 001, 022]) **过程演示**: ``` 个位分配:0→[802,022], 1→[001], 3→[123], 5→[555] 收集结果:[802,022,001,123,555] 十位分配:0→[802,001], 2→[022,123], 5→[555] 收集结果:[001,802,022,123,555] 百位分配:0→[001], 1→[123], 5→[555], 8→[802] 最终结果:[001,022,123,555,802] ``` --- ### 十、计数排序 **原理**:统计元素出现频率(适用于小范围整数) **过程演示**: ``` 统计数组(假设元素范围0-8): 索引:0 1 2 3 4 5 6 7 8 频次:0 1 1 1 0 1 0 0 1 累加数组得到位置信息:[0,0,1,2,3,3,4,4,4] 反向填充得到有序序列 ``` --- ### 对比总结 | 排序算法 | 时间复杂度 | 稳定性 | 适用场景 | |---------|----------|-------|--------| | 直接插入 | $O(n^2)$ | 稳定 | 小规模/基本有序 | | 希尔排序 | $O(n^{1.3})$ | 不稳定 | 中等规模 | | 快速排序 | $O(n\log n)$ | 不稳定 | 通用排序 | | 堆排序 | $O(n\log n)$ | 不稳定 | 大数据量 | | 归并排序 | $O(n\log n)$ | 稳定 | 外部排序 | | 基数排序 | $O(d(n+k))$ | 稳定 | 多关键字排序 | 需要具体分析某类排序的详细执行过程时,可提供具体示例进行逐步推演。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值