希尔排序又称缩小增量排序。
个人理解其思想是将一组待排序序列分成几个小组,每个小组采用直接插入排序算法。假设一个待排序列有n个元素,找到一个整数gap(gap<n)作为间隔,把序列分成gap个小组。假如有十个元素,我们选取gap=5,则序列分成5个子序列,{1,6},{2,7},{3,8},{4,9},{5,10},相隔gap(5)个数的元素归为同一个子序列。然后对着每个子序列进行直接插入排序,这是第一趟排序,然后gap = gap/2作为第二趟的gap=2,{1,3,5,7,9},{2,4,6, 8, 10},再对这两个子序列进行直接插入排序。继续gap= gap/2 ,作为新的gap(1),知道gap为1,排序变成直接插入排序,此时序列几乎是有序的。
gap的取值习惯取序列元素个数的一半,然后每次减半,直到gap=1,变成直接插入排序。
i 0 1 2 3 4 5 6 7
初始序列 21 25 49 25* 16 8 62 38
n = 8 gap = n/2 = 4;
gap是4,序列分成四组分别是{21,16},{25,8},{49,62},{25*,38},下面用不同颜色标注不同的序列。对这四组使用直接插入算法得到{16,21},{8,25},{49,62},{25*,38}
i 0 1 2 3 4 5 6 7
第一趟 16 8 49 25* 21 25 62 38
gap = gap/2 gap=2;
gap是2,序列分成2组,{16,49,21,62}, {8,25*,25,38}.分别使用直接插入排序。排序结果{16,21,49,62},{8,25*,25,38}
i 0 1 2 3 4 5 6 7
第二趟 16 8 21 25* 49 25 62 38
gap = gap/2 gap = 1;
gap是1,此时希尔排序沦为直接插入排序。
i 0 1 2 3 4 5 6 7
第三趟 8 16 21 25* 25 38 49 62
本质上还是直接插入排序,但它这种分隔排序确实比直接插入排序效率高,虽然最后是直接插入排序,但经过前几次的排序,此时大多数元素都是有序的。比较适合较大规模的序列(n<=1000).
1、Sort.h
#pragma once
typedef struct node{
int key;
}DataType;
typedef struct {
DataType *elem;
int maxSize;
int num;
}DataList;
class Sort
{
public:
Sort(void);
~Sort(void);
void shellSort(DataList &L, int d[], int m);
void shellInsertSort(DataList &L,int start, int gap);
};
2、Sort.pp
#include "StdAfx.h"
#include "Sort.h"
Sort::Sort(void)
{
}
Sort::~Sort(void)
{
}
void Sort::shellSort( DataList &L , int d[], int m)
{
int i, j;
for(i = m-1; i >= 0 ; i--)
{
for(j = 0; j < d[i]; j++)
{
shellInsertSort(L,j,d[i]);
}
}
}
void Sort::shellInsertSort( DataList &L,int start, int gap )
{
DataType temp;
int i,j;
for(i = start + gap; i < L.num; i = i + gap )
{
if (L.elem[i].key < L.elem[i-gap].key)
{
temp = L.elem[i];
for (j = i - gap; j >= 0 && L.elem[j].key > temp.key; j = j - gap)
{
L.elem[j+gap] = L.elem[j];
}
L.elem[j+gap] = temp;
}
}
}
分析:
最好情况:比较次数O(n^1.25),移动次数0次(我一直认为最好情况是指序列是有序的,此时不用移动元素。移动次数应该为0,但书上却说是O(n^1.25),个人感觉它指的是平均移动次数。)
最坏情况:比较次数n^1.5,移动次数O(n^2)没找到数据。
平均时间复杂度是O(n^1.5)
不稳定排序