您的位置:首页 > 其它

改进排序:希尔排序、堆排序

2012-12-27 16:44 274 查看

1,希尔排序。

希尔排序是对插入排序的改进。它的原理是将元素分为设定步长的多个序列,对每个序列进行插入排序;然后改变步长,直到元素序列不可分,整个序列排序完毕。

比如:data[10]={0,5,7,1,6,3,9,2,4,8};那么如何来排序呢?

首先设定步长为3,那么第一次步长为3,此时数组被分为:

{0,5,7}

{1,6,3}

{9,2,4}

{8}

接着对列进行排序,得到以下序列:

{0,2,3}

{1,5,4}

{9,6,7}

{8}

即:{0,2,3,1,5,4,9,6,7,8}

然后改变步长,分开的数组长度为3*3,原来数组的长度为10<3*3,故此时对整个数组进行插入排序。

仔细观察,此时数组几乎已经快有序了,只需要简单的进行比较即可,并不需要大量的移动元素。

得到:{0,1,2,3,4,5,6,7,8,9}

相比快速排序而要提高了效率,其时间复杂度为O(n log2 n),但是是不稳定的,需要选择好步长。

下面给出代码

void shellsort(int *data, int length)
{
for (int gap = length / 3; gap > 0; gap /= 3)//gap是指步长。第一次每个数组元素是3,步长为3;第二次每个数组元素为3*3,步长为1
{
for (int i = gap; i < length; ++i)//插入排序,由于是对数组的列进行操作,所以把数组的第一行当成是初始有序列表。
{
int temp = data[i];
int j = 0;
for( j = i -gap; j >= 0 && data[j] > temp; j -= gap)//对列的元素进行排序
{
data[j+gap] = data[j];//移动数据
}
data[j+gap] = temp;//插入最小数据
}
}
}


可以看出,实际上希尔排序是把数组当成二维数组再进行排序,然后对列进行排序。直到二维数组变换成一维数组,此时数组基本有序,再进行简单的插入排序,提高了效率。

2,堆排序

这里我们研究的是二叉堆。二叉堆是一种数据结构,是完全二叉树。

这里我们设定堆的遍历顺序为层级遍历。

堆分为大顶堆和小顶堆,顾名思义,指父母节点比子节点的值大称为大顶堆,否则是小顶堆。

那么堆排序,实际上就简化为如何建立大顶堆或者小顶堆的过程。这里我们按递增排列,故建立大顶堆。

如何建立大顶堆呢?由完全二叉树的定义可知,对于父母节点i,那么左孩子为2i,右孩子为2i+1,其关系是data[i]>=data[2i]>=data[2i+1].

那么看一下推导过程:data[10]={0,5,7,1,6,3,9,2,4,8};

1,初始化大顶堆。

如果根节点位置为0,那么左子树,右子树均为0,故从尾部开始找到根节点位置。

由2n<=length-1 得到根节点位置是4,左节点位置为8,右节点为9,而且由于其子节点为叶子节点,并不需要调整。

即根节点为6,其子节点为4,8,右节点比根节点大,所以需要交换,得到{0,5,7,1,8,3,9,2,4,6}

继续建立大顶堆,根节点位置减1,为3,此时经过交换得{0,5,7,9,8,3,1,2,4,6}

继续得到{0,5,8,9,7,3,1,2,4,6}...最终得到{9,8,7,5,6,3,1,2,4,0}

2,调整大顶堆,按照大顶堆的建立方法得到递减数组。

即让所有根节点都放在数组最前面或者最后面。如果把根节点放在数组最后面则成为递增数组。

{9,8,7,5,6,3,1,2,4,0}-->{8,7,6,5,0,3,1,2,4,9}-->{7,6,4,5,0,3,1,2,8,9}...{0,1,2,3,4,5,6,7,8,9}

下面来看代码:

//建立大顶堆
void CreateHeap(int *data,int root,int size)
{
int max=root;//假定当前根节点为最大值
int lChild=2*root;//左节点
int rChild=lChild+1; //右节点

if(root<=(length-1)/2)//由完全二叉树的性质可以得知,根节点不能大于长度的一半
{
if(lChild<=size&& data[lChild]>data[max])//左节点大于根节点
max=lChild;
if(rChild<=size&& data[rChild]>data[max])//右节点大于根节点
max=rChild;
if(root!=max)//最大节点发生变化,把子节点交换到根节点
{
int temp=data[root];
data[root]=data[max];
data[max]=temp;

CreateHeap(data,max,size);//避免子树不是大顶堆,故需要进行重新调整
}
}
}
void heapSort(int *data,int length)
{
//首先初始化堆
for(int i = (length-1) / 2; i >= 0; i--)//因为数组下标是从0起,所以length-1
CreateHeap(data,i,length-1);

//然后对堆的子树进行调节,使其成为递增数组
for(int i=length-1;i>=0;i--)
{
int root=data[0];
data[0]=data[i];
data[i]=root;
CreateHeap(data,0,i-1);//此时长度变为i-1.
}

}


  

上面是用递归的方式,完成了堆的排序,有时候在栈空间极为宝贵的情况下,我们不需要递归的形式。

相关算法见:http://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F

由上看出,实际上堆排序,是对选择排序的改进,用了二叉树的形式使得效率大大提升。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐