内部排序系列 之选择排序与堆(heap)排序
2011-12-16 00:33
232 查看
这是内部排序系列的第三弹啦~~也许也是这个月的最后一次更新了 -- 要期末考啦 TAT..复习太忙了 . 太多想做的事情 , 却被或这或那的理由打断着 , 也许这就是现实的无奈呀~~.. 抒情什么的到此打住了 . 马上开始这次关于选择排序和堆排序的探讨吧~.
还是由简到难的顺序,先让我们对相对简单的选择排序进行下剖析.
选择排序(selectsort)
让一个刚接触编程语言的人来做排序问题 , 我想很多人最开始的思路都会和选择排序的思路相似 -- 第一次比较后选出其中最大的元素放在第一位 ,之后再选出次大的元素进行放置
, 以此循环 , 最终所有的元素都将是有序的 .
先给出过程的操作图示 :
这里总结下选排的基本思想 : 每次循环之后(假设是第n次),在待排序的(sz-n)个元素中选择最大(最小)的元素放置到第n号位置,待到(sz-2)次循环后,最后一个元素也是有序的了,排序任务完成 . 确实是比较简单的想法
, 但是简单的不加改善的思维过程必将导致程序的时间复杂度不如人意 . 下面先给出选择排序的实现代码 :
明显地 , 外循环第N次运行时需要处理(SZ-N)次的比较操作 经过简单的数学计算,可以得出选排的时间复杂度是O(N²) , 选择排序也是属于简单排序的~. 下面开始介绍与选排思想相近的另外一种高级排序---堆排序.
堆排序(HEAPSORT)
堆排序可以说并没有自己的排序操作,因为它完全是对堆这种已经存在的特殊的数据结构加以利用,依赖堆的各种特性,来对数据进行处理.通过不断的选出堆中剩余最大的元素来进行排序. 我们可以从很轻易的从它的代码中发现这一点.
不难发现,堆排序的主要操作都是通过调用heap成员函数来实现的.一个是构造函数,进行建堆操作.另外一个则是Removemax函数,通过不断的取出堆中的最大值,完成对堆中元素的排序操作.
想要深入了解堆排序的实现过程,就必须对堆这一数据结构有一定的了解.这里不方便展开来讨论(堆的内容可以另开一篇来讲了),就简单的给出堆相关的几个操作的作用.首先是构造函数,Maxheap这个函数通过一些操作可以将给定的一组数据建成堆.补充下堆的两条性质 : first,它是一棵完全二叉树.also ,它是局部有序的,父子节点间有某种联系. 建成堆之后,排序操作就很容易了.
循环调用的Removmax函数可以不断的将剩余数据中的最大值移出来并保证移除后的结构还是保持堆的性质不变 .
堆排序之所以迅速,完全是由堆的性质决定的. 一棵完全二叉树可以由数组来存储, 并且通过下标计算很容易对有父子关系的元素进行定位. 堆的高效使得堆排序的效率也很高. 它的时间复杂度为O(nlogn) .可以让大多数情况接受.
想要真正掌握堆排序,关键是掌握堆这种数据结构. 堆排序不过是利用了堆结构的一些操作而已.后面贴出堆的实现代码,比较长,有兴趣的可以看下.
还是由简到难的顺序,先让我们对相对简单的选择排序进行下剖析.
选择排序(selectsort)
让一个刚接触编程语言的人来做排序问题 , 我想很多人最开始的思路都会和选择排序的思路相似 -- 第一次比较后选出其中最大的元素放在第一位 ,之后再选出次大的元素进行放置
, 以此循环 , 最终所有的元素都将是有序的 .
先给出过程的操作图示 :
这里总结下选排的基本思想 : 每次循环之后(假设是第n次),在待排序的(sz-n)个元素中选择最大(最小)的元素放置到第n号位置,待到(sz-2)次循环后,最后一个元素也是有序的了,排序任务完成 . 确实是比较简单的想法
, 但是简单的不加改善的思维过程必将导致程序的时间复杂度不如人意 . 下面先给出选择排序的实现代码 :
void selsort(int A[], int sz) { for(int i=0; i<sz; i++) //总共进行(sz)次循环,确认所有元素位置 { int lowindex=i; //存储本次循环最小值下标 for(int j=sz-1;j>i;j--) if(A[j]<A[lowindex]) lowindex=j; swap(A,i,lowindex); //交换操作 } }
明显地 , 外循环第N次运行时需要处理(SZ-N)次的比较操作 经过简单的数学计算,可以得出选排的时间复杂度是O(N²) , 选择排序也是属于简单排序的~. 下面开始介绍与选排思想相近的另外一种高级排序---堆排序.
堆排序(HEAPSORT)
堆排序可以说并没有自己的排序操作,因为它完全是对堆这种已经存在的特殊的数据结构加以利用,依赖堆的各种特性,来对数据进行处理.通过不断的选出堆中剩余最大的元素来进行排序. 我们可以从很轻易的从它的代码中发现这一点.
int MaxHeapSort(int argc, char *argv[]) { int a[MAX],b[MAX]; int n; while(cin>>n,n) { for(int i=0;i<n;i++) cin>>a[i]; Maxheap<int,Compare>heap(a,n,MAX); for(int i=0;i<n;i++){ heap.Removemax(m); b[i]=m; } } }
不难发现,堆排序的主要操作都是通过调用heap成员函数来实现的.一个是构造函数,进行建堆操作.另外一个则是Removemax函数,通过不断的取出堆中的最大值,完成对堆中元素的排序操作.
想要深入了解堆排序的实现过程,就必须对堆这一数据结构有一定的了解.这里不方便展开来讨论(堆的内容可以另开一篇来讲了),就简单的给出堆相关的几个操作的作用.首先是构造函数,Maxheap这个函数通过一些操作可以将给定的一组数据建成堆.补充下堆的两条性质 : first,它是一棵完全二叉树.also ,它是局部有序的,父子节点间有某种联系. 建成堆之后,排序操作就很容易了.
循环调用的Removmax函数可以不断的将剩余数据中的最大值移出来并保证移除后的结构还是保持堆的性质不变 .
堆排序之所以迅速,完全是由堆的性质决定的. 一棵完全二叉树可以由数组来存储, 并且通过下标计算很容易对有父子关系的元素进行定位. 堆的高效使得堆排序的效率也很高. 它的时间复杂度为O(nlogn) .可以让大多数情况接受.
想要真正掌握堆排序,关键是掌握堆这种数据结构. 堆排序不过是利用了堆结构的一些操作而已.后面贴出堆的实现代码,比较长,有兴趣的可以看下.
//Maxheap.h template <class Elem,class Comp>class Maxheap { private: Elem*Heap; int size; int n,count; void Siftdown(int); public: Maxheap(Elem* h, int num,int max) { Heap = h; n = num;count=0; size = max; Buildheap(); } int heapsize()const { return n; } bool isLeaf(int pos) { return (pos>=n/2)&&(pos<n);} int leftChild(int pos) { return 2*pos+1; } int rightChild(int pos) { return 2*pos+2; } int parent(int pos) { return (pos-1)/2; } bool Insert(const Elem&); bool Removemax(Elem&); bool Remove(int ,Elem&); void Buildheap() { for(int i=n/2-1;i>=0;i--)Siftdown(i);} }; template <class Elem,class Comp> void Maxheap<Elem,Comp>::Siftdown(int pos) { while(!isLeaf(pos)) { int j=leftChild(pos);int rc=rightChild(pos); if((rc<n)&&(Comp::lt(Heap[j],Heap[rc]))) j=rc; if(!Comp::lt(Heap[pos],Heap[j]))return ; {swap(Heap,pos,j);count++;} pos=j; } } template <class Elem,class Comp> bool Maxheap<Elem,Comp>::Insert(const Elem&e) { if(n>=size)return false; int curr=n++; Heap[curr]=e; while((curr!=0)&&(Comp::gt(Heap[curr],Heap[parent(curr)]))) { swap(Heap,curr,parent(curr)); count++; curr=parent(curr); } return true; } template <class Elem,class Comp> bool Maxheap<Elem,Comp>::Removemax(Elem&e) { if(n==0)return false; swap(Heap,0,--n); count++; if(n!=0)Siftdown(0); e = Heap ; return true; } template <class Elem,class Comp> bool Maxheap<Elem,Comp>::Remove(int pos,Elem&e) { if(pos<0||pos>=n)return false; e = Heap[pos]; swap(Heap,pos,--n); while((pos!=0)&&(Comp::gt(Heap[pos],Heap[parent(pos)]))) { swap(Heap,pos,parent(pos)); pos=parent(pos); } Siftdown(pos); return true; } class Compare { public: static bool lt(int x, int y){return x < y ;} static bool eq(int x, int y){return x == y;} static bool gt(int x, int y){return x > y ;} };
相关文章推荐
- 算法系列(二)冒泡排序、选择排序、插入排序和希尔排序(Java实现)
- 白话经典算法系列之四 直接选择排序及交换二个数据的正确实现
- C 语言经典题目系列解决方案(8)-选择排序
- 堆积排序(HeapSort) - 改良的选择排序
- 2015年大二上-数据结构-内部排序-(5)-直接选择排序
- 白话经典算法系列之四 直接选择排序及交换二个数据的正确实现
- 算法熟记-排序系列-选择排序
- 排序算法系列---快速选择排序(C++)
- 简单的选择排序(内部排序)
- PHP排序算法系列:直接选择排序
- 算法系列-直接选择排序
- 八大排序算法之四选择排序—堆排序(Heap Sort)
- 白话经典算法系列之四 直接选择排序及交换二个数据的正确实现
- 新手上路系列1:冒泡排序与选择排序的C语言程序实现
- 白话经典算法系列之四 直接选择排序及交换二个数据的正确实现
- 经典系列――选择排序
- 内部排序系列 之 分配排序与基数排序
- 排序系列之——冒泡排序、插入排序、选择排序
- 算法系列(三)排序算法上篇--冒泡排序插入排序和选择排序
- 排序算法系列之选择排序 (2)