当前位置:首页 > 焦点 > 堆排序算法(堆排序算法的比较次数是多少)

堆排序算法(堆排序算法的比较次数是多少)

2026-02-06 03:49:43 [足球] 来源:酒灯百科网

本篇文章给大家谈谈堆排序算法,堆排的比以及堆排序算法的序算比较次数是多少对应的知识点,希望对各位有所帮助,法堆不要忘了收藏本站喔。排序

堆排序时间复杂度

堆排序时间复杂度,算法数多少主要在每次选取最大数之后,较次重新建堆的堆排的比过程以及初始化堆过程。

堆排序是序算指利用堆积树这种数据结构所设计的一种排序算法,它是法堆选择排序的一种。可以利用数组的排序特点快速定位指定索引的元素。

堆是算法数多少一个优先级队列,对于大顶堆而言,较次堆顶元素的堆排的比权值最大。将待排序的序算数组建堆,然后不断地删除堆顶元素,法堆就实现了排序。

堆的操作

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点。

创建最大堆(Build Max Heap):将堆中的所有数据重新排序。

堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算。

堆排序算法的实现

#includestdio.h

#includemalloc.h

#includetime.h

#define LISTSIZE 100

#define MORESIZE 100

#define overflow -1

typedef struct

{

int data;

int fre;

}Cell;

typedef struct {

Cell *elem;

long int length;

unsigned long int count1;

unsigned long int count2;

long int listsize;

}SqList;

SqList L1;

clock_t start,end;

FILE *p,*w;

int main (void)

{

void assign(Cell *a,Cell *b);

int LT(int a,int b);

void HeapSort (SqList H);

void HeapAdjust (SqList H,int s , int m);

void exchange(Cell *a,Cell *b);

//读入

int time=0;

while(time4)

{

switch (time)

{

case 0:

p=fopen("data01.txt","r");

w=fopen("sorted01.txt","w");

break;

case 1:

p=fopen("data02.txt","r");

w=fopen("sorted02.txt","w");

break;

case 2:

p=fopen("data03.txt","r");

w=fopen("sorted03.txt","w");

break;

case 3:

p=fopen("data04.txt","r");

w=fopen("sorted04.txt","w");

break;

}

L1.count1=0;

L1.count2=0;

time++;

L1.elem=(Cell *)malloc((LISTSIZE+1)*sizeof(Cell));

L1.listsize=LISTSIZE;

L1.length=1;

Cell *newbase;

while(!feof(p))

{

if (L1.lengthL1.listsize){

newbase=(Cell *)realloc(L1.elem,(L1.listsize+MORESIZE+1)*sizeof(Cell));

if (!newbase)

return overflow;

L1.elem=newbase;

L1.listsize+=MORESIZE;}

fscanf (p,"%d (%d)\n",((L1.elem+L1.length)-data),((L1.elem+L1.length)-fre));

L1.length++;

}

L1.length--;

printf ("listsize=%d length=%d\n",L1.listsize,L1.length);

//排序

start=clock();//开始计时

HeapSort(L1); //堆排序

end=clock(); //结束计时

printf ("Time: %lf\n",(double)(end-start)/CLOCKS_PER_SEC);//输出时间

for (int i=1;iL1.length+1;++i)

fprintf (w,"%d (%d)\n",(L1.elem+i)-data,(L1.elem+i)-fre);

fprintf (w,"比较次数%u,移动次数%u\n",L1.count1,L1.count2);

printf ("比较次数%u,移动次数%u\n",L1.count1,L1.count2);

fprintf (w,"Copyright Reserved,Cheng Xuntao,NWPU");

fclose(p);

fclose(w);

}

return 0;

}

int LT(int a,int b)//比较函数

{ L1.count1++;br/ if (ab){ br/ br/ return 1;}

else return 0;

}

void assign(Cell *a,Cell *b)//赋值函数

{

a-data=b-data;

a-fre=b-fre;

L1.count2++;

}

void exchange(Cell *a,Cell *b)//交换记录

{

int temp;

temp=a-data;

a-data=b-data;

b-data=temp;

temp=a-fre;

a-fre=b-fre;

b-fre=temp;

L1.count2+=3; //+=3

}

void HeapAdjust (SqList H,int s , int m)//调节其成为堆

{

Cell *rc;

rc=(Cell *)malloc(sizeof(Cell));

int j;

assign(rc,H.elem+s); //暂存

for (j=2*s;j=m;j*=2){ //沿值较大的孩子节点向下筛选

if (jm LT((H.elem+j)-data,(H.elem+j+1)-data ))

j++; //j为值较大的记录的下标

if (!LT(rc-data,(H.elem+j)-data))

break; //rc应插入在位置s上

assign((H.elem+s),(H.elem+j));

s=j;

}

assign((H.elem+s),rc); //插入

}//HeapAdjust

void HeapSort (SqList H) //堆排序

{

int i;

for (i=H.length/2;i0;--i) //把L.elem[1...H.length]建成堆

HeapAdjust(H,i,H.length);

for (i=H.length;i1;--i)

{

exchange(H.elem+1,H.elem+i); //将堆顶记录和当前未经排序的子序列L.elem[i...i]中最后一个记录相互交换

HeapAdjust(H,1,i-1); //重新调整其为堆

}

}//HeapSort

堆排序是原地排序吗

堆排序是原地排序。

整个堆排序的过程,都只需要极个别临时存储空间,所以堆排序是原地排序算法。原地排序就是指不申请多余的空间来进行的排序,就是在原来的排序数据中比较和交换的排序。

堆排序

堆是一种叫做完全二叉树的数据结构,可以分为大根堆,小根堆,而堆排序就是基于这种结构而产生的一种程序算法。利用堆这种数据结构所设计的一种排序算法,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点。

创建最大堆(Build Max Heap):将堆中的所有数据重新排序。堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算。堆排序的时间复杂度O(N*logN),额外空间复杂度O,是一个不稳定性的排序。

数据结构与算法--堆和堆排序

堆排序是一种原地的、时间复杂度为 O(nlogn) 的排序算法。

堆是一种特殊的树。

只要满足这两点,它就是一个堆:

对于每个节点的值都大于等于子树中每个节点值的堆,我们叫做 “大顶堆” 。对于每个节点的值都小于等于子树中每个节点值的堆,我们叫做 “小顶堆” 。

完全二叉树比较适合用数组来存储。用数组来存储完全二叉树是非常节省存储空间的。下标可以直接计算出左右字数的下标。(数组中下标为 i 的节点,左子节点下标为 i∗2 ,右子节点下标为 i∗2+1,父节点的下标为 i/2 。)

如果我们把新插入的元素放到堆的最后,你可以看我画的这个图,是不是不符合堆的特性了?于是,我们就需要进行调整,让其重新满足堆的特性,这个过程我们起了一个名字,就叫做 堆化(heapify) 。

堆化实际上有两种,从下往上和从上往下。这里我先讲从下往上的堆化方法。

堆化非常简单,就是顺着节点所在的路径,向上或者向下,对比,然后交换。

我们把最后一个节点放到堆顶,然后利用同样的父子节点对比方法。对于不满足父子节点大小关系的,互换两个节点,并且重复进行这个过程,直到父子节点之间满足大小关系为止。这就是 从上往下的堆化方法 。

一个包含 n 个节点的完全二叉树,树的高度不会超过 log2n。堆化的过程是顺着节点所在路径比较交换的,所以堆化的时间复杂度跟树的高度成正比,也就是 O(logn)。插入数据和删除堆顶元素的主要逻辑就是堆化,所以,往堆中插入一个元素和删除堆顶元素的时间复杂度都是 O(logn)。

这里我们借助于堆这种数据结构实现的排序算法,就叫做堆排序。这种排序方法的时间复杂度非常稳定,是 O(nlogn),并且它还是原地排序算法。

从后往前处理数组,并且每个数据都是从上往下堆化。

因为叶子节点往下堆化只能自己跟自己比较,所以我们直接从最后一个非叶子节点开始,依次堆化就行了。

建堆的时间复杂度就是 O(n)。 推导过程见 极客时间--数据结构与算法之美

建堆结束之后,数组中的数据已经是按照大顶堆的特性来组织的。数组中的第一个元素就是堆顶,也就是最大的元素。我们把它跟最后一个元素交换,那最大元素就放到了下标为 n 的位置。

这个过程有点类似上面讲的“删除堆顶元素”的操作,当堆顶元素移除之后,我们把下标为 n 的元素放到堆顶,然后再通过堆化的方法,将剩下的 n−1 个元素重新构建成堆。堆化完成之后,我们再取堆顶的元素,放到下标是 n−1 的位置,一直重复这个过程,直到最后堆中只剩下标为 1 的一个元素,排序工作就完成了。

整个堆排序的过程,都只需要极个别临时存储空间,所以堆排序是原地排序算法。堆排序包括建堆和排序两个操作,建堆过程的时间复杂度是 O(n),排序过程的时间复杂度是 O(nlogn),所以,堆排序整体的时间复杂度是 O(nlogn)。

堆排序不是稳定的排序算法,因为在排序的过程,存在将堆的最后一个节点跟堆顶节点互换的操作,所以就有可能改变值相同数据的原始相对顺序。

堆这种数据结构几个非常重要的应用:优先级队列、求 Top K 和求中位数。

假设我们有 100 个小文件,每个文件的大小是 100MB,每个文件中存储的都是有序的字符串。我们希望将这些 100 个小文件合并成一个有序的大文件。这里就会用到优先级队列。

这里就可以用到优先级队列,也可以说是堆。我们将从小文件中取出来的字符串放入到小顶堆中,那堆顶的元素,也就是优先级队列队首的元素,就是最小的字符串。我们将这个字符串放入到大文件中,并将其从堆中删除。然后再从小文件中取出下一个字符串,放入到堆中。循环这个过程,就可以将 100 个小文件中的数据依次放入到大文件中。

我们可以用优先级队列来解决。我们按照任务设定的执行时间,将这些任务存储在优先级队列中,队列首部(也就是小顶堆的堆顶)存储的是最先执行的任务。

如何在一个包含 n 个数据的数组中,查找前 K 大数据呢?我们可以维护一个大小为 K 的小顶堆,顺序遍历数组,从数组中取出数据与堆顶元素比较。如果比堆顶元素大,我们就把堆顶元素删除,并且将这个元素插入到堆中;如果比堆顶元素小,则不做处理,继续遍历数组。这样等数组中的数据都遍历完之后,堆中的数据就是前 K 大数据了。

中位数,顾名思义,就是处在中间位置的那个数。

使用两个堆:一个大顶堆, 一个小顶堆。 小顶堆中的数据都大于大顶堆中的数据。

如果新加入的数据小于等于大顶堆的堆顶元素,我们就将这个新数据插入到大顶堆;否则,我们就将这个新数据插入到小顶堆。

也就是说,如果有 n 个数据,n 是偶数,我们从小到大排序,那前 2n 个数据存储在大顶堆中,后 2n 个数据存储在小顶堆中。这样,大顶堆中的堆顶元素就是我们要找的中位数。如果 n 是奇数,情况是类似的,大顶堆就存储 2n+1 个数据,小顶堆中就存储 2n 个数据。

极客时间--数据结构与算法之美--28 | 堆和堆排序:为什么说堆排序没有快速排序快?

堆排序过程

1,实用的排序算法:选择排序

(1)选择排序的基本思想是:每一趟(例如第i趟,i=0,1,2,3,……n-2)在后面n-i个待排序元素中选择排序码最小的元素,作为有序元素序列的第i个元素。待到第n-2趟做完,待排序元素只剩下一个,就不用再选了。

(2)三种常用的选择排序方法

1直接选择排序

2锦标赛排序

3堆排序

其中,直接排序的思路和实现都比较简单,并且相比其他排序算法,直接选择排序有一个突出的优势——数据的移动次数少。

(3)直接选择排序简介

1直接选择排序(select sort)是一种简单的排序方法,它的基本步骤是:

1)在一组元素V[i]~V[n-1]中选择具有最小排序码的元素;

2)若它不是这组元素中的第一个元素,则将它与这组元素中的第一个元素对调;

3)在这组元素中剔除这个具有最小排序码的元素,在剩下的元素V[i+1]~V[n-1]中重复执行1、2步骤,直到剩余元素只有一个为止。

2直接选择排序使用注意

它对一类重要的元素序列具有较好的效率,这就是元素规模很大,而排序码却比较小的序列。因为对这种序列进行排序,移动操作所花费的时间要比比较操作的时间大的多,而其他算法移动操作的次数都要比直接选择排序来的多,直接选择排序是一种不稳定的 排序方法。

3直接选择排序C++函数代码

//函数功能,直接选择排序算法对数列排序

//函数参数,数列起点,数列终点

void dselect_sort(const int start, const int end) {

for (int i = start; i end; ++i) {

int min_position = i;

for (int j = i + 1; j = end; ++j) { //此循环用来寻找最小关键码

if (numbers[j] numbers[min_position]) {

min_position = j;

}

}

if (min_position != i) { //避免自己与自己交换

swap(numbers[min_position], numbers[i]);

(4)关于锦标赛排序

直接选择排序中,当n比较大时,排序码的比较次数相当多,这是因为在后一趟比较选择时,往往把前一趟已经做过的比较又重复了一遍,没有把前一趟的比较结果保留下来。

锦标赛排序(tournament sort)克服了这一缺点。它的思想与体育比赛类似,就是把待排序元素两两进行竞赛,选出其中的胜利者,之后胜利者之间继续竞赛,再选出其中的胜利者,然后重复这一过程,最终构造出胜者树,从而实现排序的目的。

2,堆排序的排序过程

(1)个人理解:堆排序是选择排序的一种,所以它也符合选择排序的整体思想。直接选择排序是在还未成序的元素中逐个比较选择,而堆排序是首先建立一个堆(最大堆或最小堆),这使得数列已经“大致”成序,之后只需要局部调整来重建堆即可。建立堆及重建堆这一过程映射到数组中,其实就是一个选择的过程,只不过不是逐个比较选择,而是借助完全二叉树来做到有目的的比较选择。这也是堆排序性能优于直接选择排序的一个体现。

(2)堆排序分为两个步骤:

1根据初始输入数据,利用堆的调整算法形成初始堆;

2通过一系列的元素交换和重新调整堆进行排序。

(3)堆排序的排序思路

1前提,我们是要对n个数据进行递增排序,也就是说拥有最大排序码的元素应该在数组的末端。

2首先建立一个最大堆,则堆的第一个元素heap[0]具有最大的排序码,将heap[0]与heap[n-1]对调,把具有最大排序码的元素交换到最后,再对前面n-1个元素,使用堆的调整算法siftDown(0,n-2),重新建立最大堆。结果具有次最大排序码的元素又浮到堆顶,即heap[0]的位置,再对调heap[0]与heap[n-2],并调用siftDown(0,n-3),对前n-2个元素重新调整,……如此反复,最后得到一个数列的排序码递增序列。

(4)堆排序的排序过程:

下面给出局部调整成最大堆的函数实现siftDown(),这个函数在前面最小堆实现博文中的实现思路已经给出,只需做微小的调整即可用在这里建立最大堆。

堆排序是什么

【概念】堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]]

=

A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

【起源】

1991年的计算机先驱奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德(Robert

W.Floyd)和威廉姆斯(J.Williams)在1964年共同发明了著名的堆排序算法(

Heap

Sort

)。

【简介】

堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。

(1)用大根堆排序的基本思想

先将初始文件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]调整为堆。

……

直到无序区只有一个元素为止。

(2)大根堆排序算法的基本操作:

①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2)

其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。

②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。

③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)[2]

注意:

①只需做n-1趟排序,选出较大的n-1个关键字即可以使得文件递增有序。

②用小根堆排序与利用大根堆类似,只不过其排序结果是递减有序的。堆排序和直接选择排序相反:在任何时刻堆排序中无序区总是在有序区之前,且有序区是在原向量的尾部由后往前逐步扩大至整个向量为止

【特点】

堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录

【算法分析】

堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。

平均性能:O(N*logN)。

其他性能:由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。堆排序是就地排序,辅助空间为O(1)。它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前

和排序后他们的相对位置不发生变化)。

堆排序算法的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于堆排序算法的比较次数是多少、堆排序算法的信息别忘了在本站进行查找喔。

(责任编辑:收藏爰好)

推荐文章
  • 禅修学院(禅修学院建筑)

    禅修学院(禅修学院建筑) 本篇文章给大家谈谈禅修学院,以及禅修学院建筑对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。禅修学院哪家好玲珑梵宫禅修学院就很不错。玲珑梵宫禅修学院设有私属健康管理课程,养生专业课程,以及面部风 ...[详细]
  • 功夫老勺(王者荣耀老夫子功夫老勺)

    功夫老勺(王者荣耀老夫子功夫老勺) 本篇文章给大家谈谈功夫老勺,以及王者荣耀老夫子功夫老勺对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。王者荣耀老夫子的皮肤有哪些呢?引言:王者荣耀老夫子的皮肤有哪些呢? 王者荣耀中是有很多英雄的 ...[详细]
  • 宫斗戏(宫斗戏录拜访)

    宫斗戏(宫斗戏录拜访) 今天给各位分享宫斗戏的知识,其中也会对宫斗戏录拜访进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!宫斗戏{全部}◇=============宫斗演绎=============== ...[详细]
  • 龚磬冬(龚磬冬白敬祺)

    龚磬冬(龚磬冬白敬祺) 今天给各位分享龚磬冬的知识,其中也会对龚磬冬白敬祺进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!龚磬冬是谁演的,龚磬冬扮演者,龙门镖局1龚磬冬《龙门镖局》中龚磬冬的饰演者:马天 ...[详细]
  • 车钥匙挂件(车钥匙挂件编织绳教程)

    车钥匙挂件(车钥匙挂件编织绳教程) 本篇文章给大家谈谈车钥匙挂件,以及车钥匙挂件编织绳教程对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。男人挂车钥匙挂自己的属相好不好,男人喜欢什么样的车钥匙挂件 提起男人挂车钥匙挂自己的属相好不 ...[详细]
  • 巩汉林谈国足情怀(女足简介今晚女足简介)

    巩汉林谈国足情怀(女足简介今晚女足简介) 本篇文章给大家谈谈巩汉林谈国足情怀,以及女足直播今晚女足直播对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。巩汉林冯巩嘲讽国足后,足球媒体点名硬刚:嘲讽中国足球该有底线最近这段时间,接二连三有艺 ...[详细]
  • 公子有疾无玉不医(公子有疾无玉不医简介)

    公子有疾无玉不医(公子有疾无玉不医简介) 本篇文章给大家谈谈公子有疾无玉不医,以及公子有疾无玉不医全文免费阅读对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。求推荐双重生文,古言,男主后悔,女主一直不肯原谅男主,要虐够了才在一起,不要太 ...[详细]
  • 宫卿岚宫灼故事(宫卿岚宫灼故事简介)

    宫卿岚宫灼故事(宫卿岚宫灼故事简介) 今天给各位分享宫卿岚宫灼小说的知识,其中也会对宫卿岚宫灼小说全文免费阅读进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!《深宫虐恋:桃夭殿》txt下载在线阅读全文,求百度网盘云资 ...[详细]
  • 超级英雄(超级英雄猫咪阵容)

    超级英雄(超级英雄猫咪阵容) 今天给各位分享超级英雄的知识,其中也会对超级英雄猫咪阵容进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!漫威有多少超级英雄漫威有17个超级英雄。漫威的17个超级英雄分别是蜘蛛侠、 ...[详细]
  • 巩俐谈饰演郎平(为什么巩俐演郎平)

    巩俐谈饰演郎平(为什么巩俐演郎平) 今天给各位分享巩俐谈饰演郎平的知识,其中也会对为什么巩俐演郎平进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!巩俐饰演郎平镜头首次曝光!样子动作神韵颇相似,为啥说不愧为一代影后? ...[详细]