24.C语言最全排序方法小结(不断更新)
希尔排序:
该方法的基本思想是:先将整个待排元素序列切割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
由于直接插入排序在元素基本有序的情况下(接近最好情况),效率是非常高的,因此希尔排序在时间效率上比前两种方法有较大提高。
以n=10的一个数组49, 38, 65, 97, 26, 13, 27, 49, 55, 4为例
第一次 gap = 10 / 2 = 5
49 38 65 97 26 13 27 49 55 4
1A 1B
2A 2B
3A 3B
4A 4B
5A 5B
1A,1B,2A,2B等为分组标记,数字同样的表示在同一组,大写字母表示是该组的第几个元素, 每次对同一组的数据进行直接插入排序。
即分成了五组(49, 13) (38, 27) (65, 49) (97, 55) (26, 4)这样每组排序后就变成了(13, 49) (27, 38) (49, 65) (55, 97) (4, 26),下同。
第二次 gap = 5 / 2 = 2
排序后
13 27 49 55 4 49 38 65 97 26
1A 1B 1C 1D 1E
2A 2B 2C 2D 2E
第三次 gap = 2 / 2 = 1
4 26 13 27 38 49 49 55 97 65
1A 1B 1C 1D 1E 1F 1G 1H 1I 1J
第四次 gap = 1 / 2 = 0 排序完毕得到数组:
4 13 26 27 38 49 49 55 65 97
以下给出严格依照定义来写的希尔排序:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 void show(int *p, int length) 5 { 6 for (int i = 0; i < length; i++) 7 { 8 printf("%4d", p[i]); 9 } 10 printf("\n"); 11 } 12 13 void shellsort(int *p, int length) 14 { 15 int d = length / 2;//增量 16 while (d >= 1) 17 { 18 //从后往前推 19 for (int i = d;d<length && i<length; i++) 20 { 21 int x = p[i];//备份当前数据 22 int j = i - d;//前面一个元素 23 24 //在数组范围内,找到插入的位置,如果大则把大的赋给后面,j一直是前一个位置 25 while (j >= 0 && p[j] > x) 26 { 27 p[j + d] = p[j]; 28 j = j - d; 29 } 30 //所以这里要加d 31 p[j + d] = x; 32 } 33 d /= 2;//增量变化 34 } 35 } 36 37 void main() 38 { 39 int a[10] = { 49 , 38 , 65 ,97 ,26 , 13, 27 , 49 , 55 , 4 }; 40 show(a, 10); 41 shellsort(a, 10); 42 show(a, 10); 43 system("pause"); 44 }
桶排序(基数排序)
桶排序(也称箱排序),据坊间演绎,其实现方式有很多。
在此我们仅仅阐述一下本文的实现思想,以便于更好的理解下面的内容,同时加深对桶排序的认识。
首先,说明一点,我们是使用数组模拟桶(最好应该是使用链表模拟)。
所谓数组模拟桶实现排序的过程到底是怎么进行的呢?呵呵!其实还真有点抽象。
实现步骤如下:
(1)定义映射函数
<1>求得欲排数据序列中的最大数据。
<2>通过遍历欲排数据对每个数据乘以10再与最大数据取余,求得每个数据对应桶的索引(或称关键字)。
(2)求得每个桶中盛放的数据个数(为了保证随后准确分配数据)
(3)求得每个桶盛放数据个数的右边界索引(所谓的桶逻辑控制)
(4)从右向左(确保稳定性)扫描欲排数据序列,依次分配到对应桶中
(5)对各桶中所盛数据进行收集
(6)利用插入排序再对各个桶中所盛数据进行排序
(7)直至排序结束,即为已排序数据序列
其实,整个过程再讲通俗一点,可以如下描述:
建立一个数组作为我们的所谓的桶(逻辑桶)
然后,申请开辟与欲排数据所占空间相同的内存,作为真正的盛放数据的桶(物理桶)
数组的索引默认为桶号
对数组的每一次赋值都有着不同的意义(请参考代码分析)
最后再用插入排序对各桶中所收集数据分别进行排序。即完成桶排序。
总而言之:先分类,后收集,再排序。
【2】示例代码及其分析过程
(1)代码如下:
1 #include<iostream> 2 #include<malloc.h> 3 using namespace std; 4 5 6 void PrintArr(int ar[],int n) 7 { 8 for(int i = 0; i < n; ++i) 9 cout<<ar[i]<<" "; 10 cout<<endl; 11 } 12 13 int MapToIndex(int x,int max) 14 { 15 return (10 * x) / max; 16 } 17 18 void insertion_sort(int arr[],int begin,int end) 19 { 20 for(int i = begin+1; i <= end; ++i) 21 { 22 int v = arr[i]; 23 int j = i; 24 while(j-1 >= begin && v<arr[j-1]) 25 { 26 arr[j--] = arr[j-1]; 27 } 28 arr[j] = v; 29 } 30 } 31 32 void bucket_sort(int ar[], int begin, int end) 33 { 34 35 const int radix = 11 ; //注意:为什么是11? 36 int count[radix], i, j; 37 int size = end-begin+1; 38 39 //计数值置空 40 for(i = 0; i < radix; ++i) 41 { 42 count[i] = 0; //置空 43 } 44 45 //end-begin+1 = 9 - 0 + 1 = 10 46 int *Temp = (int *) malloc((size) * sizeof(int)); //分配临时空间 47 48 //取得当前待排序数据中的最大数据 49 int max = 0; 50 for(i = begin; i < size; ++i) 51 { 52 if(ar[i] > max) 53 max = ar[i]; 54 } 55 56 //统计各桶需要装的元素的个数 57 for(i = begin; i < size; ++i) 58 { 59 count[MapToIndex(ar[i], max)]++; 60 } 61 62 //输出计数结果: 63 PrintArr(count, radix); 64 65 //求出桶的边界索引,count[i]为第i个桶的右边界索引+1 66 for(i = 1; i < radix; ++i) 67 { 68 count[i] = count[i] + count[i-1]; 69 } 70 71 //输出桶边界索引 72 PrintArr(count, radix); 73 74 //从右向左扫描,保证排序稳定性 75 for(i = end; i >= begin; --i) 76 { 77 j = MapToIndex(ar[i], max); 78 Temp[count[j]-1] = ar[i]; //放入对应的空间中,count[j]-1是第j个桶的右边界索引 79 --count[j]; //准备放置下一个元素的位置索引 80 } 81 82 for(int i = 0; i < size; ++i) 83 { 84 cout<<Temp[i]<<" "; 85 } 86 cout<<endl; 87 88 PrintArr(count, radix); 89 90 //从各个桶中收集数据 91 for(i = begin, j = 0; i < size; i++, j++) 92 { 93 ar[i] = Temp[j]; 94 } 95 96 PrintArr(ar, end+1); 97 98 //释放空间 99 free(Temp); 100 101 for(i = 0; i < size; i++) 102 { 103 int index1 = begin + count[i]; //得到第i个桶的左边界 104 int index2 = begin + count[i+1] - 1; //得到第i个桶的右边界 105 if(index1 < index2) 106 insertion_sort(ar, index1, index2); 107 } 108 } 109 110 void main() 111 { 112 int ar[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89}; 113 int len = sizeof(ar)/sizeof(int); 114 bucket_sort(ar, 0, len-1); 115 PrintArr(ar, len); 116 } 117 /* 118 4 3 0 0 0 1 1 0 0 0 1 119 4 7 7 7 7 8 9 9 9 9 10 120 5 6 3 8 12 14 9 47 54 89 121 0 4 7 7 7 7 8 9 9 9 9 122 5 6 3 8 12 14 9 47 54 89 123 3 5 6 8 9 12 14 47 54 89 124 */
(2)分析过程如下:
学过数据结构的估计都可以想象得到:桶排序中所谓的桶应该是用一个单链表实现
因为,我们一直觉得,计算机世界就是对现实世界的模拟。那么,既然是山寨,当然越逼真越好
但是,假如我们没有学习过单链表,脑子里面根本没有单链表的概念。而且,我们要实现桶排序。
好吧!我唯一可以借助的就是数组。
不过数组模拟桶实现桶排序的确不是很好理解,有几分抽象
上面这就是一个用数组模拟桶实现的桶排序示例代码,现在做一下具体分析,希望可以更深刻理解桶排序。
分析过程如下:
(1)待排序数据序列为:
(2)count数组本质上是逻辑的桶,为什么说是逻辑上的桶?因为,它控制着桶的所有数据逻辑
但是申请的内存Temp才是每个桶的真正储存空间,所以只能说是逻辑上的桶。
而当数据被分配到各个桶中又从桶(即就是从申请空间)被收集之后,就对申请空间进行释放(因为留着再没有必要)。
如何理解以上内容?请结合一下图示理解具体分析:
第一行:所建桶的索引(可以看到总共为11个桶)
为什么是11个桶?由我们在对待排数据求输入桶对应的索引时匹配函数(MapToIndex)决定。
由于最大数据输入匹配函数后所得桶对应索引为10。所以,在此必须如此设计。
第二行:所有桶置空
第三行:每个桶中待盛的数据个数
第四行:每个桶的右边界索引
如何理解右边界索引?
比如0号桶,第三行已经得出其待盛四个数据,那么将来数据就会存入Temp[0],Temp[1],Temp[2],Temp[3]
比如1号桶,第三行已经得出其待盛三个数据,那么将来数据就会存入Temp[4],Temp[5],Temp[6]
第五行:待排数据全部放入各个桶中后,桶的左边界索引(这个是为了下面插入排序使用)。
(3)对每一个桶(即就是申请空间)中所盛的数据再利用插入排序进行排序
(4)数组中的数据即为已排序序列
【3】桶排序分析
桶排序是稳定排序算法。
桶排序使用范围比较窄。