ToB企服应用市场:ToB评测及商务社交产业平台
标题: 从插入排序到希尔排序 [打印本页]
作者: 徐锦洪 时间: 2025-2-19 23:56
标题: 从插入排序到希尔排序
从插入排序到希尔排序
插入排序
原理
插入排序是一种简朴直观的排序算法,其根本头脑是通过将每个元素逐个插入到已排序的部分中,渐渐构建一个有序序列。
操作步骤
初始化:将第 1 个元素视为已经有序的部分(初始时长度为 1)。
遍历数组:从第 2 个元素开始,依次将其视为待插入的“新”元素。
查找位置:在已排序部分中找到该元素的精确位置,并将其插入到适当的位置。
重复步骤:直到全部元素都处理完毕。
排序过程
默认[ 3 ]视为有序部分。
第 1 轮遍历排序 8,将其插入 3 前面,并将 [ 8 3 ] 视为有序部分。
第 2 轮遍历排序 5,将其插入 8 后面,并将 [ 8 5 3 ] 视为有序部分。
第 3 轮遍历排序 1,将其插入 3 后面,并将 [ 8 5 3 1 ] 视为有序部分。
第 4 轮遍历排序 2,将其插入 3 后面,并将 [ 8 5 3 2 1] 视为有序部分。
第 5 轮遍历排序 6,将其插入 8 后面,并将 [ 8 6 5 3 2 1 ] 视为有序部分。
第 6 轮遍历排序 9,将其插入 8 前面,并将 [ 9 8 6 5 3 2 1 ] 视为有序部分。
第 7 轮遍历排序 7,将其插入 8 后面,并将 [ 9 8 7 6 5 3 2 1 ] 视为有序部分。
代码实现
- /**
- * @ 简单插入排序
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ 要求从大到小排序数据
- * */
- void simple_insertion_sort(int *arr, int num)
- {
- int i, j, tmp;
- for (i=1; i<num; i++) {
- j = i - 1; /* 有序部分最后一个索引 */
- tmp = arr[i]; /* 当前待插入的元素 */
- /* 1. 若插入数据比有序部分最后一个元素还小,无需处理 */
- if (arr[j] >= tmp) {
- continue;
- }
- /* 2. 查找带插入数据位置,并后移数据腾出位置 */
- while (j >= 0) {
- if (arr[j] >= tmp)
- break;
- arr[j+1] = arr[j];
- j--;
- }
- /* 3. 插入到正确位置 */
- arr[j+1] = tmp;
- }
- }
复制代码 技能点
进阶代码
插入排序,可以理解为从序列中第 1 个元素开始,隔断固定 1 个元素,进行的序列排序。
假设程序猿想要在序列中从任意元素 (start_pos) 位置开始,隔断固定元素个数 (gap) 提取成新的序列进行插入排序,此时代码又该怎样进行通用筹划呢?
- /**
- * @ 插入排序
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ start_pos - 待处理元素起始位置 gap - 间隔
- * @ 要求从大到小排序数据
- * */
- void insertion_sort(int *arr, int num, int start_pos, int gap)
- {
- int i;
- int index; /* 用于有序部分数据遍历索引 */
- int insert_data; /* 用于记录待插入的数据 */
- for (i=start_pos+gap; i<num; i+=gap) {
- index = i - gap; /* 有序部分最后一个索引,从最后一个开始索引 */
- insert_data = arr[i]; /* 当前待插入的元素 */
- /* 1. 若插入数据比有序部分最后一个元素还小,无需处理 */
- if (arr[index] >= insert_data) {
- continue;
- }
- /* 2. 查找带插入数据位置,并后移数据腾出位置 */
- while (index >= 0) {
- if (arr[index] >= insert_data)
- break;
- arr[index + gap] = arr[index];
- index -= gap;
- }
- /* 3. 插入到正确位置 */
- arr[index + gap] = insert_data;
- }
- }
复制代码
运行效果
总结
插入排序作为基础排序算法之一,虽然在最坏环境下性能不佳,但在某些特定场景下仍然具有其独特的优势。通过优化如“提前制止内层循环”等方法,可以在一定程度上提升效率,使其更恰当于小规模数据或部分有序的数据。然而,对于大规模数据来说,仍然建议使用更高效的排序算法。
希尔排序
原理
希尔排序是一种改进的插入排序,它答应在远距离的元素之间进行比较和互换,从而减少排序的时间复杂度。
确定步长:选择一个初始步长 gap,通常取数组长度的一半。
分组排序:将数组分成若干个子序列,每个子序列中的元素隔断为当前的步长 gap。
渐渐缩小步长:对每一个子序列进行插入排序或其他排序算法(如互换排序),然后减小步长 gap,重复分组和排序的过程,直到步长为1,此时整个数组视为一个子序列,进行一次插入排序。
操作步骤
初始化步长:设置初始步长 gap = n / 2,其中 n 是数组的长度。
循环调解步长:当前步长减半:gap = gap / 2 直到步长为 0
分组排序:对于每个子序列(元素隔断为当前步长),进行插入排序或其他稳定排序算法。
排序过程
确定序列元素数目 n=8
第 1 次排序
将序列分为 n/2 = 4 组,每组固定隔断 gap = n/2 = 4 元素。
分组环境 [ 3 2 ] [ 8 6 ] [ 5 9 ] [ 1 7 ]。
对于每组进行插入排序得到 [ 3 2 ] [ 8 6 ] [ 9 5] [ 7 1]。
归并之后的序列为 [ 3 8 9 7 2 6 5 1]。
第 2 次排序
将序列分为 gap/2 = 2组,每组固定隔断 gap = gap/2 = 2元素。
分组环境 [ 3 9 2 5 ] [ 8 7 6 1 ]。
对于每组进行插入排序得到 [ 9 5 3 2 ] [ 8 7 6 1 ]。
归并之后的序列为 [ 9 8 5 7 3 6 2 1]。
第 3 次排序
代码实现
- /**
- * @ 希尔排序,插入排序的优化
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ 要求从大到小排序数据
- * */
- void shell_sort(int *arr, int num)
- {
- int i;
- int gap = num / 2; /* 控制间距, 默认从 num/2 开始 */
- while (gap > 0) { /* 控制间距 */
- for (i=0; i<gap; i++) { /* 控制组数 */
- /* 调用插入排序 */
- insertion_sort(arr, num, i, gap);
- }
- gap /= 2;
- }
- }
复制代码 分析
运行效果
缺点
希尔排序在大多数实现中并不是稳定的排序算法。这意味着,当两个相等的元素出现时,它们的相对顺序大概会被打乱。
在某些特定的数据分布下,希尔排序的时间复杂度大概退化到O(n²),这与普通的插入排序相当,无法发挥其高效的理论优势。
希尔排序的性能严峻依赖于步长序列的选择。如果步长选择不妥,大概导致算法效率低下或排序失败。
优化方法
改进步长序列的选择:使用更科学的步长序列,如Sedgewick提出的步长序列:1, 3, 7, 15, 31,…。这种步长序列可以在一定程度上减少排序过程中的逆序数,提高算法效率。
确保稳定性:在比较和互换元素时,除了比较巨细外,还应考虑元素的原始位置。例如,在比较两个相等的元素时,优先保存原位置较前的元素。这可以通过在键值对中包罗索引信息来实现。
优化排序过程中的细节:在内循环中,只管减少不须要的数据移动和比较操作。例如,当发现当前元素已经位于精确的位置时,可以提前制止内层循环。
联合其他优化技能:将希尔排序与其他排序算法的优点联合起来,如在小规模的数据子集上使用更高效的排序方法(如归并排序或快速排序)进行优化。
完备代码
- /**
- * @Filename : insertion_sort.c
- * @Revision : $Revision: 1.00 $
- * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
- * @Description : 从插入排序一步一步到希尔排序
- **/
- #include <stdio.h>
- #include <string.h>
- #define MAX_NUM 8
- staticint cnt = 0; /* 统计排序次数 */
- /**
- * @ 打印数组
- * @ arr - 待打印的数组 num - 数组元素个数
- * */
- void print_arr(int *arr, int num)
- {
- int i;
- for (i=0; i<num; i++)
- printf("%02d ", arr[i]);
- printf("\n");
- }
- /**
- * @ 插入排序
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ start_pos - 待处理元素起始位置 gap - 间隔
- * @ 要求从大到小排序数据
- * */
- void insertion_sort(int *arr, int num, int start_pos, int gap)
- {
- int i;
- int index; /* 用于有序部分数据遍历索引 */
- int insert_data; /* 用于记录待插入的数据 */
- for (i=start_pos+gap; i<num; i+=gap) {
- index = i - gap; /* 有序部分最后一个索引,从最后一个开始索引 */
- insert_data = arr[i]; /* 当前待插入的元素 */
- cnt++;
- /* 1. 若插入数据比有序部分最后一个元素还小,无需处理 */
- if (arr[index] >= insert_data) {
- printf("无需移位 第%02d次排序: ", cnt);
- print_arr(arr, num);
- continue;
- }
- /* 2. 查找带插入数据位置,并后移数据腾出位置 */
- while (index >= 0) {
- if (arr[index] >= insert_data)
- break;
- arr[index + gap] = arr[index];
- index -= gap;
- }
- /* 3. 插入到正确位置 */
- arr[index + gap] = insert_data;
- printf("需要移位 第%02d次排序: ", cnt);
- print_arr(arr, num);
- }
- }
- /**
- * @ 简单插入排序
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ 要求从大到小排序数据
- * */
- void simple_insertion_sort(int *arr, int num)
- {
- printf("排序前数组内容: ");
- print_arr(arr, num);
- cnt = 0; /* 清空统计次数 */
- insertion_sort(arr, num, 0, 1);
- printf("排序后数组内容: ");
- print_arr(arr, num);
- }
- /**
- * @ 希尔排序,插入排序的优化
- * @ arr - 待排序的数组 num - 数组元素个数
- * @ 要求从大到小排序数据
- * */
- void shell_sort(int *arr, int num)
- {
- int i;
- int gap = num / 2; /* 控制间距, 默认从 num/2 开始 */
- printf("排序前数组内容: ");
- print_arr(arr, num);
- cnt = 0; /* 清空统计次数 */
- while (gap > 0) { /* 控制间距 */
- for (i=0; i<gap; i++) { /* 控制组数 */
- /* 调用插入排序 */
- insertion_sort(arr, num, i, gap);
- }
- gap /= 2;
- }
- printf("排序后数组内容: ");
- print_arr(arr, num);
- }
- int main(void)
- {
- int buf1[] = {3, 8, 5, 1, 2, 6, 9, 7};
- int buf2[] = {3, 8, 5, 1, 2, 6, 9, 7};
- printf ("----------简单插入排序---------\n");
- simple_insertion_sort(buf1, MAX_NUM);
- printf ("---------希尔排序---------\n");
- shell_sort(buf2, MAX_NUM);
-
- return0;
- }
复制代码 运行效果
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) |
Powered by Discuz! X3.4 |