改进排序:希尔排序、堆排序
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
由上看出,实际上堆排序,是对选择排序的改进,用了二叉树的形式使得效率大大提升。
相关文章推荐
- 排序(希尔排序,堆排序,归并排序,快速排序)
- 插入排序、冒泡排序、选择排序、希尔排序、快速排序、归并排序、堆排序和LST基数排序的C++代码实现
- 快速排序、堆排序、希尔排序实现
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 插入排序、冒泡排序、选择排序、希尔排序、高速排序、归并排序、堆排序和LST基数排序——C++实现
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析(好戏在后面,有图有真相)
- oc中的排序 快速排序,冒泡排序,直接插入排序和折半插入排序,希尔排序,堆排序,直接选择排序
- 排序(冒泡排序,插入排序,希尔排序,选择排序,堆排序)
- 排序总结:插入(简单和改进)、希尔、选择、冒泡、快速、堆排序、归并排序
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析(好戏在后面,有图有真相)
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)
- 七大内部排序算法总结(插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、归并排序、堆排序)
- 数据结构-排序算法详解(插入排序,希尔排序,堆排序,归并排序,快速排序,桶式排序)
- 10种算法原理(冒泡排序,选择排序,快速排序,堆排序,希尔排序,桶排序等)
- 排序基础之归并排序、快排、堆排序、希尔排序思路讲解与Java代码实现
- 三种改进型排序算法-快速排序,堆排序,希尔排序
- 常见排序算法的实现(归并排序、快速排序、堆排序、选择排序、插入排序、希尔排序)
- 排序算法合集(插入排序,折半插入排序,希尔排序,冒泡排序,快速排序,简单选择排序,堆排序,归并排序)
- java实现七种排序 (插入排序, 希尔排序, 插入排序, 快速排序, 简单选择排序, 堆排序, 归并排序)
- java版排序算法简介及冒泡排序以及优化,选择排序,直接插入排序,希尔排序,堆排序,快速排序及其优化前言 2 分类 2 稳定性 3 时间复杂度 4 Java实现版本 5 1、冒泡排序 6 2、选择排序