导航:首页 > 源码编译 > 堆算法

堆算法

发布时间:2022-01-13 15:05:11

1. 堆排序时间复杂度是什么

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

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

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

堆的操作

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

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

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

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

2. 什么是堆排序

【概念】堆排序(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)。它是不稳定的排序方法。(排序的稳定性是指如果在排序的序列中,存在前后相同的两个元素的话,排序前 和排序后他们的相对位置不发生变化)。

3. 堆排序的堆是怎么建立的

堆排序,也叫二叉堆排序。
完全二叉树:
1、左右子树的节点数满足 Ln/Rn=1
2、左右子树高度满足 Rh+1>=Lh>=Rh
3、子节点值统一比父节点大(小)。

最大堆:2叉树的所有子节点都比父节点小。所以根节点是最大的。
最小堆:2叉树的所有子节点都比父节点大。所以根节点是最小的。

建堆:假设最多有N个数据。开辟一段用来存这N个数据的空间。根节点位置为0。其子节点位置为1、2。所有子节点位置与父节点的位置(k)关系:k,2k+1,2k+2。 假设已经有了n个数据,那么新数据自然放在n位(因为位置是从0开始),定义一个函数 shift_up() 用来调整新数据。它的功能是:将新数据与 (n-1)/2 位置的数据(新数据的父节点)比较,如果比父节点大,那么就交换,继续比较,直到它比父节点小。

重新建堆: 当取数据时,就是将根节点取出来。因为根节点是最大的,所以自然还要将其所有子节点进行调整,以保证剩下的数据的根节点是最大的。方法是:将最后一个数放到根节点位置(因为根节点取出后,根节点就空了),然后调用 shift_down()函数将其与1、2位置的数比较,如果比它大,则交换,然后继续与2k+1,2k+2位置的比较,直到这两个位置的数都比它小。

4. 堆排序算法的实现

#include<stdio.h>
#include<malloc.h>
#include<time.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(time<4)
{
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.length>L1.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;i<L1.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 (a<b){<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 (j<m && 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;i>0;--i) //把L.elem[1...H.length]建成堆
HeapAdjust(H,i,H.length);
for (i=H.length;i>1;--i)
{
exchange(H.elem+1,H.elem+i); //将堆顶记录和当前未经排序的子序列L.elem[i...i]中最后一个记录相互交换
HeapAdjust(H,1,i-1); //重新调整其为堆
}
}//HeapSort

5. 建堆排序法中的堆是什么

#include <stdio.h>
int swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
return 0;
}
void adjust(int *array, int i, int n)
{
int min, pos;
while(1) {
if((2*i+2) < n) {
min = array[2*i+1] < array[2*i+2] ? array[2*i+1] : array[2*i+2];
pos = array[2*i+1] < array[2*i+2] ? (2*i+1) : (2*i+2);
if(array[i] < min) { break; }
swap(&array[i], &array[pos]);
i = pos;
} else if((2*i+2) == n) {
min = array[2*i+1];
pos = 2*i+1;
if(array[i] < min) { break; }
swap(&array[i], &array[pos]);
i = pos;
} else {
break;
}
}
}
void heap_sort(int *array, int n)
{
//构建最小堆
for(int j=(n-1)/2; j>=0; --j) {
adjust(array, j, n);
}
//降序排列
for(int i=n; i>1; --i) {
swap(&array[0], &array[i-1]);
adjust(array, 0, --n);
}
}
int main()
{
int data[] = {12, 34, 23, 3, 1, 45, 87, 99};
heap_sort(data, 8);
for(int i=0; i<8; ++i) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}

6. 计算机二级的中的“堆排序法”是怎么排的

堆排序就是将所有待排序的元素组成一个堆,然后不断弹出堆顶的元素并调用函数维持堆序,直到所有元素均被弹出后,排序完成。被弹出的元素序列即一个有序数列。

一般做法是这样:

当一个节点被插入时,将该节点放在堆的末尾(这是为了保证堆是完全二叉树)然后将该节点与它的父节点比较,看该节点是否大于(或小于)其父节点,即判断当前的堆是否满足堆序。如果不满足,则将该节点与其父节点交换。

再将该节点与其新的父节点做比较,依此类推,直到该节点不再需要与其父节点交换为止。(即满足堆序时停止) 当一个根节点被弹出(即被从堆中删除)时,将堆最尾部的节点移动到头结点的位置,然后将该节点不断与其子节点比较,如果不符合堆序则交换,直到符合堆序为止。

(6)堆算法扩展阅读:

堆的操作

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

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

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

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

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

7. 关于堆排序的算法

error LNK2019: 无法解析的外部符号 "void __cdecl MAX_HEAPIFY(int *,int,unsigned int)" (?MAX_HEAPIFY@@YAXPAHHI@Z),该符号在函数 "void __cdecl HEAPSORT(int *,unsigned int)" (?HEAPSORT@@YAXPAHI@Z) 中被引用

意思就是你那个函数没有实现。

可以看到你的函数声明和实现不一致:
void MAX_HEAPIFY(int *, int, size_t);
void MAX_HEAPIFY(int *arry, size_t i, size_t sz)
{}

参数类型不同,那就是不同的函数,C++的多态性。

修改声明为:
void MAX_HEAPIFY(int *, size_t, size_t);

8. 堆排序过程

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(),这个函数在前面最小堆实现博文中的实现思路已经给出,只需做微小的调整即可用在这里建立最大堆。

9. 堆排序是什么

堆排序(HeapSort)是一树形选择排序。堆排序的特点是:在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系(参见二叉树的顺序存储结构),在当前无序区中选择关键字最大(或最小)的记录
其过程为:
(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)大根堆排序算法的基本操作:

① 初始化操作:将R[1..n]构造为初始堆;


每一趟排序的基本操作:将当前无序区的堆顶记录R[1]和该区间的最后一个记录交换,然后将新的无序区调整为堆(亦称重建堆)。

注意:

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

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

10. 堆排序的比较次数

堆排序,顾名思义是通过直接选择排序衍生而来的。直接选择排序是直接从剩余记录中线性的查找最大记录的方法,并没有巧妙的利用前一轮查找所得到的信息,而堆排序,利用堆数据结构来保存剩余记录相对大小的信息,因而是更有效的选择排序。
堆分为最大堆和最小堆,本篇我们通过最大堆来实现我们的功能。
最大堆需要满足的条件: 堆中每个父节点中的数据项都要大于或等于其子节点中的数据项。
堆排序主要有两个步骤:

对所有记录建立一个最大堆。
取出堆顶的最大记录与数组末端进行交换,最大记录放在下标n-1的位置;
对剩余堆记录进行调整,再次形成一个最大堆;
再次取出对顶的最大记录与数组末端进行交换,最大记录放在下标n-2的位置;
不断重复,直到堆为空,也就是排序完成。
示例数组如下:【49,38,65,97,76,13,27,49】
通过筛选法建最大堆的前提条件:

堆的初始位置从0开始,依次递增;
若父结点的位置为i;则左孩子结点位置为2i+1;右孩子结点位置为2i+2;
筛选位置从最后一个非结点编号开始,也就是n/2-1向下取整。

初始堆如下:
初始堆

筛选位置从最后一个非结点编号开始,n=8,所以初始筛选位置为i=3,也就是i=97;
因为97>49,所以位置不变;然后继续比较i,i–;
i=2时,因为13<27,且65>27,所以位置依旧不变。
i=1时,因为97>76,所以比较38和97,因为38<97所以交换位置;又因为38<49所以继续交换位置,最后堆位置如下:
i=1时
i=0时,因为97>65,所以比较49和97,因为49<97,交换位置;又因为49<76,继续交换位置,最后堆位置如下:
i=0时
到此位置,排序完成,堆变成了一个最大堆。
接下来则进行交换流程,将n-1位置的值与堆顶位置的值进行交换;

1、 i=7;交换位置7上的值和堆顶的值
交换1
交换完毕,再次调整除了i=7之外的堆元素,再次转换成一个最大堆。
交换2

2、当i=6;交换位置6和堆顶的值,然后调整属于的元素;
3、当i=5;交换位置5和堆顶的值,然后调整属于的元素;

当i=1时;交换位置1和堆顶的值,交换流程到此结束,最后的堆如下:
end

以上是针对堆排序的分析流程

阅读全文

与堆算法相关的资料

热点内容
学信网app为什么刷脸不了 浏览:871
天蝎vs程序员 浏览:991
单片机下载口叫什么 浏览:186
程序员的道 浏览:924
云服务器不实名违法吗 浏览:556
怎样查看文件夹图片是否重复 浏览:993
文件怎么导成pdf文件 浏览:805
打开sql表的命令 浏览:101
安卓手机如何面部支付 浏览:37
天元数学app为什么登录不上去 浏览:822
明日之后为什么有些服务器是四个字 浏览:102
安卓系统l1是什么意思 浏览:24
服务器一直崩应该用什么指令 浏览:922
cm202贴片机编程 浏览:729
php构造函数带参数 浏览:178
解压电波歌曲大全 浏览:345
为啥文件夹移到桌面成word了 浏览:859
命令符的安全模式是哪个键 浏览:760
编程中学 浏览:957
单片机求助 浏览:995