经典排序算法----希尔排序算法(非稳定)
来源:互联网 发布:淘宝自定义付款方式 编辑:程序博客网 时间:2024/06/07 23:28
希尔排序(非稳定)
希尔排序的实质就是分组插入排序,该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。(希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。)
该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法(冒泡排序算法和直接插入排序算法)有较大提高。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
- 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
- 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
以n=10的一个数组49, 38, 65, 97, 26, 13, 27, 49, 55, 4为例
第一次: gap = 10/2=5
1A,1B,2A,2B等为分组标记,数字相同的表示在同一组,大写字母表示是该组的第几个元素, 每次对同一组的数据进行直接插入排序。即分成了五组(49, 13) (38, 27) (65, 49) (97, 55) (26, 4)这样每组排序后就变成了(13, 49) (27, 38) (49, 65) (55, 97) (4, 26),下同。
第一次排序完之后数组:13,27,49,55,4,49,38,68,97,26
第二次: gap = 5/2=2
分成了两组(13,49,4,38,97)和(27,55,49,65,26)每次对同一组数据进行直接插入排序,每组就变成了(4,13,38,49,97)和(26,27,49,55,65)。
第二次排序之后的数组:4,26,13,27,38,49,49,55,97,65
第三次:gap = 2/2=1
这次只有一组,进行直接插入排序。
第三次得到数组:4,13,26,27,38,49,49,55,65,97
第四次 gap 1/2 = 0 结束。
下面给出严格按照定义来写的希尔排序算法:
#include<stdio.h>#include<stdlib.h>#include<time.h>void Show(int *arr, int n){for (int i = 0; i < n; i++)printf("%-5d", arr[i]);printf("\n");}void ShellSort(int *arr, int n){int i, j, gap;for (gap = n / 2; gap > 0; gap /= 2)//步长{for (i = 0; i < gap; i++)//直接插入排序{for (j = i + gap; j < n; j+=gap)if (arr[j] < arr[j - gap]){int temp = arr[j];int k = j - gap;while (k >= 0 && arr[k] > temp){arr[k + gap] = arr[k];k -= gap;}arr[k + gap] = temp;}}}}//直接插入排序不就是希尔排序的特殊情况吗?int main(){time_t time1;srand((unsigned int)time(&time1));int arr[100] = { 0 };for (int i = 0; i < 100; i++)arr[i] = rand() % 100;printf("排序之前数组中的元素为:\n");Show(arr, 100);ShellSort(arr, 100);printf("排序之后数组中的元素为:\n");Show(arr, 100);system("pause");return 0;}
运行结果:
排序之前数组中的元素为:80 67 70 6 97 14 84 94 25 96 37 59 62 58 52 414 30 15 53 92 59 59 33 55 77 7 13 33 95 82 5518 23 31 75 26 54 21 38 12 69 3 85 17 39 49 825 45 76 23 96 54 79 87 23 94 54 6 96 32 71 8955 22 47 94 87 22 34 49 77 43 18 54 38 38 13 4335 78 93 96 8 68 91 76 33 59 95 54 85 51 99 6468 92 22 14排序之后数组中的元素为:3 4 6 6 7 8 8 12 13 13 14 14 14 15 17 1818 21 22 22 22 23 23 23 25 25 26 30 31 32 33 3333 34 35 37 38 38 38 39 43 43 45 47 49 49 51 5253 54 54 54 54 54 55 55 55 58 59 59 59 59 62 6467 68 68 69 70 71 75 76 76 77 77 78 79 80 82 8485 85 87 87 89 91 92 92 93 94 94 94 95 95 96 9696 96 97 99请按任意键继续. . .
很明显,上面的shellsort1代码虽然对直观的理解希尔排序有帮助,但代码量太大了,不够简洁清晰。因此进行下改进和优化,以第二次排序为例,原来是每次从1A到1E,从2A到2E,可以改成从1B开始,先和1A比较,然后取2B与2A比较,再取1C与前面自己组内的数据比较…….。这种每次从数组第gap个元素开始,每个元素与自己组内的数据进行直接插入排序显然也是正确的。
#include<stdio.h>#include<stdlib.h>#include<time.h>void Show(int *arr, int n){for (int i = 0; i < n; i++)printf("%-5d", arr[i]);printf("\n");}void ShellSort(int a[], int n){int j, gap;for (gap = n / 2; gap > 0; gap /= 2)for (j = gap; j < n; j++)//从数组第gap个元素开始if (a[j] < a[j - gap])//每个元素与自己组内的数据进行直接插入排序{int temp = a[j];int k = j - gap;while (k >= 0 && a[k] > temp){a[k + gap] = a[k];k -= gap;}a[k + gap] = temp;}}//直接插入排序不就是希尔排序的特殊情况吗?int main(){time_t time1;srand((unsigned int)time(&time1));int arr[100] = { 0 };for (int i = 0; i < 100; i++)arr[i] = rand() % 100;printf("排序之前数组中的元素为:\n");Show(arr, 100);ShellSort(arr, 100);printf("排序之后数组中的元素为:\n");Show(arr, 100);system("pause");return 0;}
运算结果:
排序之前数组中的元素为:11 59 90 41 37 43 32 81 91 27 44 64 17 64 42 7029 93 51 63 56 52 17 72 29 39 96 7 29 84 84 5481 9 79 35 33 4 94 47 15 66 9 8 64 62 82 1581 40 78 82 92 67 74 62 51 84 79 13 5 40 33 8632 83 13 68 15 42 65 12 29 90 56 45 46 38 54 3441 3 61 39 5 26 89 52 19 22 21 63 96 90 30 9091 21 87 4排序之后数组中的元素为:3 4 4 5 5 7 8 9 9 11 12 13 13 15 15 1517 17 19 21 21 22 26 27 29 29 29 29 30 32 32 3333 34 35 37 38 39 39 40 40 41 41 42 42 43 44 4546 47 51 51 52 52 54 54 56 56 59 61 62 62 63 6364 64 64 65 66 67 68 70 72 74 78 79 79 81 81 8182 82 83 84 84 84 86 87 89 90 90 90 90 91 91 9293 94 96 96请按任意键继续. . .
再将直接插入排序部分用 白话经典算法系列之二 直接插入排序的三种实现 中直接插入排序的第三种方法来改写下:
#include<stdio.h>#include<stdlib.h>#include<time.h>void Show(int *arr, int n){for (int i = 0; i < n; i++)printf("%-5d", arr[i]);printf("\n");}void Swap(int *a, int *b){int temp = *a;*a = *b;*b = temp;}void ShellSort(int a[], int n){int i, j, gap;for (gap = n / 2; gap > 0; gap /= 2)for (i = gap; i < n; i++)for (j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)Swap(&a[j], &a[j + gap]);}//直接插入排序不就是希尔排序的特殊情况吗?int main(){time_t time1;srand((unsigned int)time(&time1));int arr[100] = { 0 };for (int i = 0; i < 100; i++)arr[i] = rand() % 100;printf("排序之前数组中的元素为:\n");Show(arr, 100);ShellSort(arr, 100);printf("排序之后数组中的元素为:\n");Show(arr, 100);system("pause");return 0;}运行结果:
排序之前数组中的元素为:37 48 37 10 83 55 17 42 50 28 31 87 32 47 68 5418 34 85 47 44 15 72 10 12 63 33 84 64 79 16 9854 17 6 81 29 23 52 7 33 17 59 41 82 24 86 2989 70 32 77 36 5 94 69 19 40 8 98 10 75 39 7976 38 34 57 13 48 39 21 17 60 33 33 81 45 19 599 58 52 0 50 67 96 53 76 39 66 74 76 90 24 688 98 54 33排序之后数组中的元素为:0 5 6 7 8 8 9 10 10 10 12 13 15 16 17 1717 17 18 19 19 21 23 24 24 28 29 29 31 32 32 3333 33 33 33 34 34 36 37 37 38 39 39 39 40 41 4244 45 47 47 48 48 50 50 52 52 53 54 54 54 55 5758 59 59 60 63 64 66 67 68 68 69 70 72 74 75 7676 76 77 79 79 81 81 82 83 84 85 86 87 89 90 9496 98 98 98请按任意键继续. . .
附注:上面希尔排序的步长选择都是从n/2开始,每次再减半,直到最后为1。其实也可以有另外的更高效的步长选择,如果读者有兴趣了解,请参阅维基百科上对希尔排序步长的说明:
http://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F
引自<http://blog.csdn.net/morewindows/article/details/6668714>
- 经典排序算法----希尔排序算法(非稳定)
- 数据结构与算法之六(希尔排序,非稳定排序)
- 经典排序算法----归并排序(稳定)
- 经典算法--希尔排序
- 经典排序算法----冒泡排序算法及其优化(稳定)
- 【经典排序算法】插入排序、希尔排序
- 经典算法之希尔排序
- 图示经典算法--希尔排序
- 经典算法学习:排序之希尔排序(壳排序)
- 排序算法(稳定)
- 经典排序算法 - 希尔排序Shell sort
- 经典算法排序——希尔排序
- 经典排序算法 - 希尔排序Shell sort
- 经典排序算法 - 希尔排序Shell sort
- 经典排序算法之希尔排序
- 经典排序算法之希尔排序
- 经典排序算法之一:希尔排序
- 经典排序算法之:希尔排序
- HDU1848 Fibonacci again and again(SG函数的应用)
- Python高级特性——学习笔记
- android 更加复杂的小鱼游
- jsp基础知识图解
- Hadoop常用的命令(三)
- 经典排序算法----希尔排序算法(非稳定)
- C++ public protected private成员访问权限解读
- http和https的区别
- HDMI基本知识
- NSFileManager
- 服务器
- Java split()方法使用注意问题
- const修饰的函数
- 剑指offer之面试题14:调整数组顺序使奇数位于偶数前面