当前位置:文档之家› 10.9 堆排序

10.9 堆排序

内排序

Content 快速排序5合并排序6

堆排序7

PART SEVEN

堆排序

算法思想

?借助堆数据结构,不断输出当前堆顶元素

?每次堆顶离开当前堆后,对剩余元素重新调整成堆,直到堆中只剩下一个元素

?元素的输出序列可转换成元素的有序序列

?最大堆的概念、逻辑与存储结构

?堆的操作:AdjustDown

?堆的调整:如何利用AdjustDown操作将一个序列调整成最大堆

堆的存储结构:顺序存储

堆的逻辑结构:完全二叉树

最大堆的特点:堆顶元素是整个堆的最大元素

算法过程

准备:待排序序列构建成最大堆D[0], D[1],…, D[n?1];

第1趟:交换堆顶元素D[0]与堆底元素D[n?1];调整D[0], D[1],…, D[n?2]为最大堆;第2趟:交换堆顶元素D[0]与堆底元素D[n?2];调整D[0], D[1],…, D[n?3]为最大堆;…

第i趟:交换堆顶元素D[0]与堆底元素D[n?i];调整D[0], D[1],…, D[n?i?1]为最大堆;…

第n?1趟:交换堆顶元素D[0]与堆底元素D[1];排序完成。

堆排序算法示例

将初始序列构造成最大堆;

0 1 2 3 4 5 6

48366872124802

AjustDown

72486836124802

第i 趟时,将D[0]与D[n-i]交换,D[0]向下调整,使得剩余的前n-i 个元素还是堆;直到堆中只剩一个记录结束724868361248020 1 2 3 4 5 602486836124872684848361202720248483612687248364802

1268

721236480248687248361202486872交换

向下调整

交换

向下调整

交换

向下调整第一趟第二趟第三趟

0 1 2 3 4 5 648361202486872

02361248486872

交换36021248486872

向下调整120236484868

72

交换12023648486872

向下调整

02123648486872

交换堆中只剩一个元素,排序算法停止第四趟第五趟第六趟

算法分析

?堆排序主要有两步, 即构造初始堆和排序.

?构造初始最大堆的时间复杂度O(n)

?AdjustDown() 函数的执行时间不超过O(log

n)

2

?在排序部分, 除最后一趟的堆顶元素无需再调整外, 其余n-1 个元素均要向下调整一次, 花费时间O(log

n), 故堆排序的时间复杂度为O(nlog2n)

2

?最好、最坏与平均情况:O(nlog

n)

2

?一趟堆排序结束后, 可以确定一个元素的最终位置

?该排序是不稳定的排序方法.

END

堆 排 序 算 法

堆排序——C#实现 一算法描述 堆排序(Heap Sort)是利用一种被称作二叉堆的数据结构进行排序的排序算法。 二叉堆在内部维护一个数组,可被看成一棵近似的完全二叉树,树上每个节点对应数组中的一个元素。除最底层外,该树是满的。 二叉堆中,有两个与所维护数组相关的属性。Length表示数组的元素个数,而HeapSize则表示二叉堆中所维护的数组中的元素的个数(并不是数组中的所有元素都一定是二叉堆的有效元素)。因此,根据上述定义有: 0 = HeapSize = Length。 二叉堆可分为最大堆和最小堆两种类型。在最大堆中,二叉树上所有的节点都不大于其父节点,即 A[Parent(i)] = A[i]。最小堆正好相反:A[Parent(i)] = A[i]。 为维护一个二叉堆是最大(小)堆,我们调用一个叫做MaxHeapify (MinHeapify)的过程。以MaxHeapify,在调用MaxHeapify时,先假定根节点为Left(i)和Right(i)的二叉树都是最大堆,如果A[i]小于其子节点中元素,则交换A[i]和其子节点中的较大的元素。但这样一来,以被交换的子节点为根元素的二叉堆有可能又不满足最大堆性质,此时则递归调用MaxHeapify方法,直到所有的子级二叉堆都满足最大堆性质。如下图所示: 因为在调用MaxHeapify(MinHeapify)方法使根节点为A[i]的

二叉堆满足最大(小)堆性质时我们有其左右子堆均已满足最大(小)堆性质这个假设,所以如果我们在将一个待排序的数组构造成最大(小)堆时,需要自底向上地调用 MaxHeapify(MinHeapify)方法。 在利用最大堆进行排序时,我们先将待排序数组构造成一个最大堆,此时A[0](根节点)则为数组中的最大元素,将A[0]与A[n - 1]交换,则把A[0]放到了最终正确的排序位置。然后通过将HeapSize 减去1,将(交换后的)最后一个元素从堆中去掉。然后通过MaxHeapify方法将余下的堆改造成最大堆,然后重复以上的交换。重复这一动作,直到堆中元素只有2个。则最终得到的数组为按照升序排列的数组。 二算法实现 1 注意到在C#中数组的起始下标为0,因此,计算一个给定下标的节点的父节点和左右子节点时应该特别小心。 private static int Parrent(int i) return (i - 1) - 2; private static int Left(int i) return 2 * i + 1; private static int Right(int i) return 2 * i + 2; 2 算法的核心部分是MaxHeapify(MinHeapify)方法,根据算法描述中的说明,一下代码分别实现了对整数数组的最大堆化和最小堆化方法,以及一个泛型版本。

数据结构 堆排序

佛山科学技术学院 实验报告 课程名称数据结构 实验项目实现典型的排序算法 专业班级 09计算机(1)班姓名梁志恒学号________2009314138________ 指导教师黄营成绩____________ 日期________ _______ 题目:请编程实现堆排序算法。 #include #define maxsize 100 typedef struct { int key[maxsize]; int length; }SqList; //堆排序大根堆 void HeapAdjust(SqList *L,int s,int m) { int j; L->key[0]=L->key[s]; for(j=2*s;j<=m;j=2*j) { if(jkey[j]>L->key[j+1]) j++; if(!(L->key[0]>L->key[j])) break; L->key[s]=L->key[j]; s=j; } L->key[s]=L->key[0]; } void HeapSort(SqList *L) { //对顺序表key进行堆排序 int i; for(i=L->length/2;i>0;i--) HeapAdjust(L,i,L->length); for(i=L->length;i>1;i--)

{ L->key[0]=L->key[1]; L->key[1]=L->key[i]; L->key[i]=L->key[0]; HeapAdjust(L,1,i-1); } } void main() { SqList L; int i,s=1; printf("元素的个数length="); scanf("%d",&(L.length)); for(i=1;i<=L.length;i++) { scanf("%d",&(L.key[i])); } HeapSort(&L,s,L.length); printf("排序后:\n"); for(i=1;i<=L.length;i++) printf("%d ",L.key[i]); printf("\n"); } 1.请为所建立的堆选择适合的数据结构。 链式存储结构 typedef struct BiTNode { int data; struct BiTNode *lchild,* rchild; }BiTNode , *BiTree; 顺序存储结构 #define maxsize 100 typedef struct { int key[maxsize]; int length; }SqList;

堆排序算法的基本思想及算法实现示例

堆排序算法的基本思想及算法实现示例 堆排序 1、堆排序定义 n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质): (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤ ) 若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。 【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示。 2、大根堆和小根堆 根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆。 根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。 注意: ①堆中任一子树亦是堆。 ②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。 3、堆排序特点 堆排序(HeapSort)是一树形选择排序。 堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系【参见二叉树的顺序存储结构】,在当前无序区中选择关键字最大(或最小)的记录。 4、堆排序与直接插入排序的区别 直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。 堆排序可通过树形结构保存部分比较结果,可减少比较次数。5、堆排序 堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。 (1)用大根堆排序的基本思想

减治法堆排序

减治法堆排序 减治法实验报告 1、堆排序:编写程序实现对序列{2,3,4,1,5,7,6,8,10,9}的排序。 C++代码: #include "iostream.h" void SiftHeap(int r[ ], int k, int n) {int i,j,t; i=k; j=2*i; //置i为要筛的结点,j为i的左孩子 while (j<=n) //筛选还没有进行到叶子 { if (jr[j]) //根结点已经大于左右孩子中的较大者 break; else { t=r[i]; //将根结点与结点j交换 r[i]=r[j]; r[j]=t; i=j; j=2*i; //被筛结点位于原来结点j的位置 } } } void HeapSort(int r[],int n) { int i,t; for(i=n/2;i>0;--i)

SiftHeap(r,i,n); for(i=n;i>1;--i) { t=r[1]; r[1]=r[i]; 第1页 r[i]=t; SiftHeap(r,1,i-1); } } void main() { int r[]={10,2,3,4,1,5,7,6,8,10,9}; for(int i=1;i<11;i++) cout<

堆排序算法分析(C语言版)

堆排序 堆排序是利用堆的性质进行的一种选择排序,下面先讨论一下堆。 1.堆 堆实际上是一棵完全二叉树,其任何一非叶节点满足性质: Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2] 即任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。 堆分为大顶堆和小顶堆,满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,满足Key[i]<= key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。 2.堆排序的思想 利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。 其基本思想为(大顶堆): 1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区; 2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。 操作过程如下: 1)初始化堆:将R[1..n]构造为堆; 2)将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。 因此对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。 下面举例说明: 给定一个整形数组a[]={16,7,3,20,17,8},对其进行堆排序。 首先根据该数组元素构建一个完全二叉树,得到

堆排序

算法与数据结构程设计报告 一.设计题目: 堆排序的算法 二.运行环境: 1、硬件:计算机 2、软件:Microsoft Visual C++6.0 三.目的和意义: 目的:创建一个大堆,按从大到小顺序输出堆元素,实现堆排序。 意义:利用堆排序,即使在最坏情况下的时间复杂度也是O(nlog2n),相对于快速排序来说,时间复杂度小,这是堆排序的最大优点,可用于对若干元素进行排序,加快排序速度。 四.算法设计的基本思想: 堆排序算法设计基本思想: 堆排序利用了大根堆堆顶记录的关键字最大这一特征,使得在当前无序区中选取最大关键字的记录变得简单。先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区。再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录r[n]交换,由此得到新的无序区r[1..n-1]和有序区r[n],且满足r[1..n-1].keys≤r[n].key。由于交换后新的根R[1]可能违反堆性质,故应将当前无序区r[1..n-1]调整为堆。然后再次将r[1..n-1]中关键字最大的记录r[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区r[1..n-2]和有序区r[n-1..n],且仍满足关系r[1..n-2].keys≤r[n-1..n].keys,同样要将r[1..n-2]调整为堆。直到无序区只有一个元素为止.

. .

流程图3:打印数组函数print()

六.算法设计分析: 性能分析:堆排序的时间主要由建立初始堆和不断重复建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。其中:建立初始堆时,总共需进行的关键字比较次数≤ 4n =O(n) ;排序过程中进行n-1次重建堆所进行的总比较次数不超过下式:2*(└ log2(n-1) ┘+└ log2(n-2) ┘+…+ └ log22 ┘) < 2n └ log2n ┘=O(n log2n)因此堆排序总的时间复杂度是 O(n+ n log2n)= O(n log2n)。堆排序在最坏情况下的时间复杂度也是O(nlog2n),相对于快速排序来说,这是堆排序的最大优点。另外,堆排序仅需一个记录大小供交换用的辅助空间。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录较少的文件,但对n较大的文件还是很有效的。堆排序是就地排序,辅助空间为O(1),它是不稳定的排序方法。 堆的描述:堆排序(HeapSort)是一树形选择排序。堆是对基于数组的树的重要应用场合之一。它是节点间具有层次次序关系的完全二叉树。其中,父节点值大于或等于其孩子节点值的,叫“最大堆(maximum heap)”;父节点值小于或等于孩子节点值的,叫“最小堆(minimum heap)”.在最大堆中,根中的元素值最大;在最小堆中,根中的元素值最小。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。 从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。 堆的存储特点:在这里,讨论作为顺序表存储的堆。它是按某种次序将一系列数据以完全二叉树形式存放的一种表。它要求堆中的节点的值都大于或等于其孩子节点的值。 按照这种存储方式,可知第i个元素的左右儿子分别是第2i和第2i+1个元素,而它的父亲节点是第i/2个元素。由于父亲节点和儿子节点具有这样的顺序关系,所以可以方便地由父亲节点找到儿子节点,反之亦然。 可见,这种存储方式大大节省了存储空间,高效快速。 堆的相关操作:作为抽象表结构,堆允许增加和删除表项。插入过程不用假定新表项占有一个特定的位置而只需维持堆顺序。但是删除操作总是删去表中的最大项(根)。堆可以用于那些客户程序想直接访问最大(小)元素的场合。作为表,堆并不提供Find操作,而对表的直接访问是只读的。所有的堆处理算法都有责任更新树和维持堆顺序。 1.建堆:数组具有对应的树表示形式。一般情况下,树并不满足堆的条件。通过重新排列元素,可以建立一棵“堆化”的树。 2.插入一个元素:新元素被加入到表中,随后树被更新以恢复堆次序。例如,下面的步骤将15加入到表中。 3.删除一个元素:删除总是发生在根节点处。用表中的最后一个元素来填补空缺位置,结果树被更新以恢复堆条件。 在堆实现时我们是采用数组来存储堆的完全二叉树表示,并且用一种有效的算法来保证对堆的所有操作不破坏堆的性质。这种表示的主要问题在于数组的大小需要事先确定,这使得对堆的大小有了一个初始的限定。在堆中数据增长到

十大经典排序之堆排序

十大经典排序之堆排序#include "sort_.h" void print_array(int *arr, int n) // 打印数组 { if(n==0){ printf("ERROR: Array length is ZERO\n"); return; } printf("%d", arr[0]); for (int i=1; i

{ // 请在这里补充代码,完成本关任务 /********** Begin *********/ int i; int temp; for(i=n/2-1;i>=0;i--) adjustHeap(arr,n,i); for(i=n-1;i>0;i--) { temp=arr[0]; arr[0]=arr[i]; arr[i]=temp; adjustHeap(arr,i,0); } return arr; /********** End **********/ }

堆排序思想

基本思想 堆的定义: n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称堆性质): (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤FLOOR(n/2)) 若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。 根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小 根堆。 根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆。 用大根堆排序的基本思想: (1) 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区; (2) 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key; (3) 由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。 排序过程 假设待排序数组为array = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49},数组大小为20。 (一)初始建堆 首先执行的初始建堆(在建堆的过程中需要调整堆)。过程如下: 1、求出当前堆中最后一个存在孩子结点的索引 这里,把数组array看做是一棵完全二叉树,这样数组每个索引位置上的元素都对应到二叉树中的结点,如图所示: 其中需要在这棵树中找到最后一个有孩子最大的一个结点的索引: pos = (array.length-1)/2 = (20-1)/2 = 9 也就是索引为9的array[9] = 76,由后至前层次遍历,从array[9]一直到array[0],对初始堆进行调整。 2、对初始堆进行调整

堆排序算法课程设计

数据结构课程设计设计说明书 堆排序的算法的实现 学生姓名 学号 班级 成绩 指导教师 计算机科学与技术系 2011年3月4 日

数据结构课程设计评阅书 注:指导教师成绩60%,答辩成绩40%,总成绩合成后按五级制记入。

课程设计任务书 2010 —2011 学年第二学期 专业:学号:姓名: 课程设计名称:数据结构课程设计 设计题目:堆排序算法的实现 完成期限:自 2011年 2 月 19 日至 2011 年 3 月 4 日共 2 周 设计依据、要求及主要内容(可另加附页): 堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。如关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆。 大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap), 大根堆排序的基本思想: ① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区。 ② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key。 ③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。 要求:(1)给出一个符合堆序列的一组数,能够建立大根堆和小根堆。 (2)界面友好,可操作性强。 (3)能够实现数据个数的变化输入,并建立相应的堆。 指导教师(签字):教研室主任(签字): 批准日期:年月日

数据结构例题 堆排序

数据结构例题堆排序 #include #define MAX 255 int R[MAX]; void Heapify(int s,int m) { /*对R[1..n]进行堆调整,用temp做暂存单元*/ int j,temp; temp=R[s]; j=2*s; while (j<=m) { if (R[j]>R[j+1]&&j0;i--) Heapify(i,n); } void Heap_Sort(int n) { /* 对R[1..n]进行堆排序,不妨用R[0]做暂存单元*/ int i; BuildHeap(n); /* 将R[1-n]建成初始堆*/ for(i=n;i>1;i--) { /* 对当前无序区R[1..i]进行堆排序,共做n-1趟。*/ R[0]=R[1]; R[1]=R[i];R[i]=R[0]; /* 将堆顶和堆中最后一个记录交换*/ Heapify(1,i-1); /* 将R[1..i-1]重新调整为堆,仅有R[1]可能违反堆性质*/ } /* end of for */ } /* end of Heap_Sort */ void main() { int i,n; clrscr(); puts("Please input total element number of the sequence:");

相关主题
文本预览
相关文档 最新文档