本内容是笔者结合《数据结构(C语言版)》(严蔚敏)总结所得,记录学习过程,分享知识!
目录:
1. 希尔排序分析:基本有序!
- - 1.1 基本有序思路的说明
- - 1.2 有序化处理流程(图示)
2. 希尔排序实现
- - 2.1 间隔式调整模拟实现
- - 2.1.1 写法一:双for循环 + while循环 写法
- - 2.1.2 写法二:单层for循环 + while循环 写法
- - 2.2 希尔排序实现
- - 2.3 可执行完整程序代码及结果展示
3. 希尔排序总结
4. 结语及相关推荐(排序汇总)
1. 希尔排序分析:基本有序!
紧接上篇《算法训练营【day8】:: 排序篇(C语言版):插入排序:分析、实现与总结》;
前文曾提及:插入排序的优化方案是:使原序列尽可能有序程度高!
如何提升这种有序程度呢?
~ 在插入排序中我们提及了打麻将示例,此处我们再引用试试,麻将牌分为万、饼、条等牌种,每种都有 1~9 的点数牌且都有四张!持牌者通常会将同牌种的牌集中放置(且有序)如下图。此时在插入新摸到的手牌进行对应插入是不是比则乱拜访来的更方便!
那问题来了,对于一个序列而言,我们是否可以采用类似预有序化思路实现呢?
为了实现有序化程度高(或者说是基本有序)我们尝试以下思路:
~ 分块有序化尝试:
对于序列:{ 9,1,5 ,8、3、7,4,6,2}
若使用分块形式的预处理:{ 9,1,5 }、{8、3、7}、{4,6,2};
再有序化:结果:{1,5,9}、{3,7,8}、{2,4,6};
此时我们发现,若将其合并相比于原序列显然没有基本有序的明显提升!
1.1 基本有序思路的说明
注意:基本有序是指:实现小值无论顺序基本在前,大值同理基本在后,不大不小的又基本在中间部分!
例如
原序列:{ 9,1,5 ,8、3、7,4,6,2};
基本有序序列:{1,3,2,5,3,4,9,8,7};
基本有序处理后,在使用插入排序会有明显的效率提升!
实现基本有序的方式:希尔排序:
- 对原序列进行间隔分组(如上图中同种颜色为同组)【若分块式分组无法实现基本有序】
- 在分组内进行跳跃式组内排序;
- 最终在知道间隔减小到一,即为相邻情形实现排序!
(推荐看图理解)
1.2 有序化处理流程(图示)
2. 插入排序实现
实现流程:
- 间隔式调整实现基本有序;
- 调整间隔大小反复调整;
- 直到间距为:1,实现排序。
2.1 间隔式调整模拟实现
2.1.1 写法一:双for循环 + while循环 写法
看代码注释:
// 注意循环条件,实际上间隔式组内也是使用插入排序方式调整;
// 分组后的好处:减小了遍历范围,提高效率
for (int j = 0; j < gap; ++j) {
for (int i = j; i < size - gap; i += gap) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap]; // 存储等间隔处理的数
while (end >= 0) {
if (temp < array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
}
2.1.2 写法二:单层for循环 + while循环 写法
for (int i = 0; i < size - gap; i++) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap];
while (end >= 0) {
if (temp > array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
2.2 希尔排序实现
希尔排序的注意要点:
- 间隔:gap 的持续调整!
- 注意:gap 的最小值问题:最小值:1
写法一:双for循环 + while循环 写法
void ShellSort1(DataType* array, int size) {
int gap = size; // 希尔处理的间隔步数
while (gap > 1) {
gap = gap / 3 + 1; // 此处间隔调整:注意:+1 确保最小值为:1
for (int j = 0; j < gap; ++j) {
for (int i = j; i < size - gap; i += gap) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap]; // 存储等间隔处理的数
while (end >= 0) {
if (temp < array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
}
}
}
写法二:单层for循环 + while循环 写法
void ShellSort2(DataType* array, int size) {
int gap = size;
while (gap > 1) {
gap = gap / 3 + 1; // 此处间隔调整:注意:+1 确保最小值为:1
for (int i = 0; i < size - gap; i++) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap];
while (end >= 0) {
if (temp > array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
}
}
2.3 可执行完整程序代码及结果展示
#pragma
/* 本篇内容将实现:C 语言版顺序存储:希尔排序 */
#include<stdio.h>
typedef char DataType;
/* 打印函数 */
void PrintArray1(DataType* array, int size);
void PrintArray2(DataType* array, int size);
/* 顺序存储希尔排序: */
/* 参数说明:被操作数组;数组大小 */
/* 写法一:双for循环 + while循环 写法 */
void ShellSort1(DataType* array, int size);
/* 写法二:单层for循环 + while循环 写法 */
void ShellSort2(DataType* array, int size);
/* 打印函数 */
void PrintArray1(DataType* array, int size) {
printf("array:");
for (int i = 0; i < size; i++)
printf("%d ", array[i]);
printf("\n");
}
void PrintArray2(DataType* array, int size) {
printf("array:");
for (int i = 0; i < size; i++)
printf("%c ", array[i]);
printf("\n");
}
/* 写法一:双for循环 + while循环 写法 */
void ShellSort1(DataType* array, int size) {
int gap = size; // 希尔处理的间隔步数
while (gap > 1) {
gap = gap / 3 + 1;
for (int j = 0; j < gap; ++j) {
for (int i = j; i < size - gap; i += gap) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap]; // 存储等间隔处理的数
while (end >= 0) {
if (temp < array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
}
}
}
/* 写法二:单层for循环 + while循环 写法 */
void ShellSort2(DataType* array, int size) {
int gap = size;
while (gap > 1) {
gap = gap / 3 + 1;
for (int i = 0; i < size - gap; i++) {
// 单次希尔插入排序操作
int end = i;
DataType temp = array[end + gap];
while (end >= 0) {
if (temp > array[end]) {
array[end + gap] = array[end];
end -= gap;
}
else break;
}
array[end + gap] = temp;
}
}
}
void test_ShellSort() {
#if 1
printf("============================================================\n");
printf("整型测试用例:\n");
int a[] = { 5,3,6,44,8,2,2,64,85,16,48,46,846,4,6843,54,846,16,48,4,1,64,684,6,46 };
int size = sizeof(a) / sizeof(int);
PrintArray1(&a, size);
ShellSort1(&a, size);
PrintArray1(&a, size);
ShellSort2(&a, size);
PrintArray1(&a, size);
#else
printf("============================================================\n");
printf("字符型测试用例(注:需将 typedef int DataType; 改成:typedef char DataType;才可正常):\n");
char b[] = "qwertyuiopasdfghjklzxcvbnm";
int size = sizeof(b) / sizeof(char);
PrintArray2(b, size);
ShellSort1(b, size);
PrintArray2(b, size);
ShellSort2(b, size);
PrintArray2(b, size);
#endif
}
int main() {
test_ShellSort();
return 0;
}
测试结果展示:
3. 希尔排序总结
关于时间复杂度:O(N^1.3)
这求证方式为网上参考资料!
希尔排序特点:
排序时:
- gap(间隔)越大,大的数据更快到后面,小的数更快到前面,但是越不接近有序;
- gap(间隔)越小,越接近有序,当 gap == 1 时,就是直接插入排序。