堆数据结构+堆排序+最大优先队列的堆的实现

对于堆排序,首先要先知道什么是堆数据结构,堆数据结构就是一个完全二叉树,但是它有自己的性质.

例如最大堆的性质为:A[PARENT[i]]>=A[i];即每一个结点的值大于等于其左右孩子的值,小于等于其父节点的值。我们在这里只讨论最大堆的情况。我们知道一颗完全二叉树对应一个最大堆的形式,我们要做的就是将二叉树转化为最大堆,这就是所谓的最大堆的维护,我们定义函数MaxheapFY(A,i)来进行操作。

代码:

/**
 *MaxheapFY(A,i):维护位置i最大堆性质,此时假设left(i)和right(i)为根的两颗二叉树都是最大堆,
 */
void MaxheapFY(int *A,int i){//维护位置i最大堆性质,
    int l,r,now;
    l=LEFT(i);
    r=RIGHT(i);
    now=i;//记录当前结点
    if(l<=heapsize&&A[l]>A[now]){
        now=l;//交换A[l]和A[i],并递归维护下一个当前结点now
    }
    if(r<=heapsize&&A[r]>A[now]){
        now=r;//交换A[l]和A[i],并递归维护下一个当前结点now
    }
    if(now!=i){//不是当前结点,需要进行交换,用当前结点与左右孩子的最大值进行交换
        swap(A[i],A[now]);
        MaxheapFY(A,now);//递归维护当前结点,直到叶子结点
    }
}

下面就是依据最大堆的维护性质进行建堆:

/**

*BuildMaxHeap(A,n):输入一个无序数组 A[1…n],建立最大堆,我们知道数组A[(n/2+1)…n]中的元素都是

*树中的叶子结点,每一个都可以看做是一个元素的最大堆,那么我们还记得MaxheapFY(A,i)函数用来维护

*最大堆的前提是其孩子Left(i),Right(i)都是最大堆,因此我们可以利用MaxheapFY(A,i)函数,从i:[(n/2)..1]

*实现最大堆的建立。

*/

代码:

void BuildMaxHeap(int *A,int n){//A[1..n]
    heapsize=n;//全局变量,表示最大堆的大小
    for(int i=n/2;i>=1;i--){//从n/2..1维护堆中每个节点的最大堆性质:结点的值大于起孩子的值
        MaxheapFY(A,i);
    }
}

我们知道最大堆的第一个元素肯定是数组中最大值,我们可以交换(A[1]和A[n])此时A[n]中已经是最大的值了,不需要在操作,只需要维护剩下的n-1个元素的下标为1的最大堆性质,运行之后就变成n-1个元素的最大堆,继续进行上面的交换操作直到剩下2个元素,就已经完成排序。

代码:

void HeapSort(int *A,int n){
    BuildMaxHeap(A,n);//建立最大堆
    for(int i=n;i>=2;i--){
        //cout<<A[1]<<" ";
        swap(A[1],A[i]);//交互A[1]和A[i],使得A[i]中为当前最大的元素
        heapsize--;//堆大小减去1,便于下次操作去掉已经排好序的元素
        MaxheapFY(A,1);//此时A[1]不一定满足最大堆的性质,重新维护下标1的最大堆的性质
    }
}

至此我们已经完成了最大堆的建立和堆排序的代码,最大堆还有一个更重要的用处,就是基于最大堆的最大优先队列的实现,最大优先队列就是最大元素优先,当然也有最小优先队列,是基于最小堆进行构造的。我们学过数据结构都知道,每一个数据结构都有其所为的属性和操作(方法或者是函数),我们就来看一下最大优先级队列应该支持什么样的操作呢。肯定会有:取队列的首元素,删除首元素,插入元素,修改元素关键字的值等的操作。

/**

*下面给出最大优先级队列对应的操作

*1、PQueue_Insert(A,x):把元素x插入到集合S中,集合S对应的是一种最大优先级队列

*2、PQueue_Maxnum(A):返回最大优先级队列S中,键值最大的元素

*3、PQueue_Pop(A):去掉并返回最大优先级队列S最大键值的元素并删除

*4、PQueue_IncresaseKey(A,x,key):将元素x的关键字的值增加到key(尽对应这里的最大优先级队列)

*

*特别注意:::

*这里的优先级队列是基于最大堆建立的后,进行维护和操作的,因此在进行最大优先级队列

*操作之前,必须先进行初始胡函数PQueue_Init(A,n):初始化长度为n集合S,构造成最大堆

*/

各种函数代码实现:

初始化函数PQueue_Init(A,n):

void PQueue_Init(int *A,int n){
    BuildMaxHeap(A,n);//调用建立最大堆的函数进行初始化
}

返回最大元素的函数PQueue_Maxnum(A):

int PQueue_Maxnum(int *A){
    return A[1];
}

去掉并返回最大优先级队列的首元素PQueue_Pop(A):

int PQueue_Pop(int *A){
    if(heapsize>=1){
        int max=A[1];
        A[1]=A[heapsize];
        --heapsize;//堆大小(最大优先级队列的大小)减去1,
        MaxheapFY(A,1);//维护剩下的heaps的最大堆的性质
        return max;
    }
    else return -1;//删除失败
}

修改元素关键字后并维护的函数PQueue_IncresaseKey(A,x,key):

int PQueue_IncresaseKey(int *A,int i,int key){//增大A[i]的键值
    if(key<A[i]) return -1;//不符合最大堆的要求(仅仅是这里的定义)

    A[i]=key;//注意此时i的所有祖先都有可能不在满足最大堆的性质,
             //因此我们余姚判断所有的祖先是否满足最大堆的要求
    while(i>1&&A[PARENT(i)]<A[i]){
        swap(A[i],A[PARENT(i)]);
        i=PARENT(i);//向上判断到达当前的当前的结点满足最大堆的性质或者到达祖宗结点
    }
}

插入元素PQueue_Insert(A,x):

void PQueu_Insert(int *A,int key){
    //插入首先最大优先级队列的大小增加1
    ++heapsize;
    A[heapsize]=INF;//对新增的结点赋值一个永远娶不到的最小的值(仅对应于本程序的最大优先级队列)
    //剩下的操作就相当于将下标heapsize的关键字增大到key后,进行维护了,直接调用函数就好了
    PQueue_IncresaseKey(A,heapsize,key);

}

至此所有函数定义并实现完成,这就是最大优先队列。

附录全部代码及测试(少数):

/**
  *@xiaoran
  *堆排序,是一种借助数据结构堆进行原地排序的算法,
  *首先了解什么堆数据结构,如何构造最大堆以及维护最大堆
  *以及用对数据结构构造高效的优先级队列
  */
#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<vector>
#include<stack>
#include<cstdlib>
#include<cctype>
#include<cmath>
#define LL long long

#define PARENT(x) x>>1
#define LEFT(x) x<<1
#define RIGHT(x) (x<<1)+1

#define INF -100000000
#define PT(A,n) for(int i=1;i<=n;i++) cout<<A[i]<<" "; cout<<endl;

using namespace std;

int heapsize=0;//堆的大小

/**
 *输出A[1..n]
 */
void Print(int *A,int n){
    for(int i=1;i<=n;i++) cout<<A[i]<<" ";
    cout<<endl;
}

/**
 *MaxheapFY(A,i):维护位置i最大堆性质,此时假设left(i)和right(i)为根的两颗二叉树都是最大堆,
 */
void MaxheapFY(int *A,int i){//维护位置i最大堆性质,
    int l,r,now;
    l=LEFT(i);
    r=RIGHT(i);
    now=i;//记录当前结点
    if(l<=heapsize&&A[l]>A[now]){
        now=l;//交换A[l]和A[i],并递归维护下一个当前结点now
    }
    if(r<=heapsize&&A[r]>A[now]){
        now=r;//交换A[l]和A[i],并递归维护下一个当前结点now
    }
    if(now!=i){//不是当前结点,需要进行交换,用当前结点与左右孩子的最大值进行交换
        swap(A[i],A[now]);
        MaxheapFY(A,now);//递归维护当前结点,直到叶子结点
    }
}
/**
 *BuildMaxHeap(A,n):输入一个无序数组A[1...n],建立最大堆,我们知道数组A[(n/2+1)...n]中的元素都是
 *树中的叶子结点,每一个都可以看做之和一个元素的最大堆,那么我们还记得MaxheapFY(A,i)函数用来维护
 *最大堆的前提是其孩子Left(i),Right(i)都是最大堆,因此我们可以利用MaxheapFY(A,i)函数,从i:[(n/2)..1]
 *实现最大堆的建立。
 */
void BuildMaxHeap(int *A,int n){//A[1..n]
    heapsize=n;
    for(int i=n/2;i>=1;i--){//从n/2..1维护堆中每个节点的最大堆性质:结点的值大于起孩子的值
        MaxheapFY(A,i);
    }
}
/**
 *下面利用上面两个函数进行构造堆排序算法HeapSort(A,n);
 *调用BuildMaxHeap(A,n)将输入数组A[1..n]建立为最大堆。注意此时数组的最大值为A[1],
 *我们只要交换A[1]和A[n],去掉A[n](--heapsize),维护线标1的最大堆的性质即可:MaxheapFY(A,1),
 *直到堆的大小n-1降到2
 */
void HeapSort(int *A,int n){
    BuildMaxHeap(A,n);//建立最大堆
    for(int i=n;i>=2;i--){
        //cout<<A[1]<<" ";
        swap(A[1],A[i]);//交互A[1]和A[i],使得A[i]中为当前最大的元素
        heapsize--;//堆大小减去1,便于下次操作去掉已经排好序的元素
        MaxheapFY(A,1);//此时A[1]不一定满足最大堆的性质,重新维护下标1的最大堆的性质
    }
}

/**
 *下面我们来讨论基于最大堆的实现的最大优先级队列
 *显然优先级队列是一种数据结构,我们知道任意一种数据结构都有其对应的操作(函数)
 *下面给出最大优先级队列对应的操作
 *1、PQueue_Insert(A,x):把元素x插入到集合S中,集合S对应的是一种最大优先级队列
 *2、PQueue_Maxnum(A):返回最大优先级队列S中,键值最大的元素
 *3、PQueue_Pop(A):去掉并返回最大优先级队列S最大键值的元素并删除
 *4、PQueue_IncresaseKey(A,x,key):将元素x的关键字的值增加到key(尽对应这里的最大优先级队列)
 *
 *特别注意:::
 *这里的优先级队列是基于最大堆建立的后,进行维护和操作的,因此在进行最大优先级队列
 *操作之前,必须先进行初始胡函数PQueue_Init(A,n):初始化长度为n集合S,构造成最大堆
 *
 */

void PQueue_Init(int *A,int n){
    BuildMaxHeap(A,n);//调用建立最大堆的函数进行初始化
}

int PQueue_Maxnum(int *A){
    return A[1];
}

int PQueue_Pop(int *A){
    if(heapsize>=1){
        int max=A[1];
        A[1]=A[heapsize];
        --heapsize;//堆大小(最大优先级队列的大小)减去1,
        MaxheapFY(A,1);//维护剩下的heaps的最大堆的性质
        return max;
    }
    else return -1;//删除失败
}
int PQueue_IncresaseKey(int *A,int i,int key){//增大A[i]的键值
    if(key<A[i]) return -1;//不符合最大堆的要求(仅仅是这里的定义)

    A[i]=key;//注意此时i的所有祖先都有可能不在满足最大堆的性质,
             //因此我们余姚判断所有的祖先是否满足最大堆的要求
    while(i>1&&A[PARENT(i)]<A[i]){
        swap(A[i],A[PARENT(i)]);
        i=PARENT(i);//向上判断到达当前的当前的结点满足最大堆的性质或者到达祖宗结点
    }
}

void PQueu_Insert(int *A,int key){
    //插入首先最大优先级队列的大小增加1
    ++heapsize;
    A[heapsize]=INF;//对新增的结点赋值一个永远娶不到的最小的值(仅对应于本程序的最大优先级队列)
    //剩下的操作就相当于将下标heapsize的关键字增大到key后,进行维护了,直接调用函数就好了
    PQueue_IncresaseKey(A,heapsize,key);

}

int main()
{
    int A[9]={0,4,2,5,6,2,6,1,7};

    /**
     *堆排序测试
     */
    Print(A,8);
    HeapSort(A,8);
    Print(A,8);

    /**
     *最大优先级队列测试
     */
    PQueue_Init(A,8);
    cout<<PQueue_Maxnum(A)<<endl;
    cout<<PQueue_Pop(A)<<endl;
    cout<<PQueue_Maxnum(A)<<endl;

    PQueue_IncresaseKey(A,5,10);

    PQueu_Insert(A,12);
    cout<<PQueue_Maxnum(A)<<endl;
    cout<<PQueue_Pop(A)<<endl;
    cout<<PQueue_Maxnum(A)<<endl;

    return 0;
}
时间: 2024-11-03 21:16:13

堆数据结构+堆排序+最大优先队列的堆的实现的相关文章

堆与堆排序—优先队列

上一节我们写了树以及二叉树的知识 http://blog.csdn.net/wtyvhreal/article/details/43487095 堆是一种特殊的完全二叉树. 所有父节点都比子节点要小,这样的完全二叉树称为最小堆,反之叫最大堆. 下图一棵完全二叉树,调整为最小堆步骤: 向下调整的代码如下: 从上面可以得到:调整堆的时间复杂度是O(logN). 如果堆的大小为N,那么插入一个新元素所需要的时间为O(logN). 在刚刚那个最小堆里插入数值3 向上调整的代码如下: 建堆:从空的堆开始,

堆排序:什么是堆?什么是最大堆?二叉堆是什么?堆排序算法是怎么样的?PHP如何实现堆排序?

本文标签:  堆排序 php php算法 堆排序算法 二叉堆 数据结构 REST   服务器 什么是堆 这里的堆(二叉堆),指得不是堆栈的那个堆,而是一种数据结构. 堆可以视为一棵完全的二叉树,完全二叉树的一个"优秀"的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素. 数组与堆之间的关系 二叉堆一般分为两种:最大堆和最小堆. 什么是最大堆 堆中每个父节点的元素值都大于等于其孩子结点(如果存在),这样的堆就是一个最大堆 因此,最大堆中的

堆 (数据结构)

堆 (数据结构)[工程下载>>>] 堆(英语:Heap)是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权.堆即为解决此类问题设计的一种数据结构. 1.1 逻辑定义 n个元素序列k1,k2...ki...kn,当且仅当满足下列关系时称之为堆: (ki<=k2i,ki<=k2i+1)或者(k

[golang] 数据结构-堆排序

接上文 树形选择排序上篇也说了,树形选择排序相较简单选择排序,虽然减少了时间复杂度,但是使用了较多空间去储存每轮比较的结果,并且每次还要再和胜出节点比较.而堆排序就是为了优化这个问题而在1964年被两位大佬发明. 原理首先有几个关于树的定义: 如果一棵树每个节点的值都大于(小于)或等于其字节点的话,那么这棵树就是大(小)根树如果一棵大(小)根树正好又是完全二叉树,则被称为大根堆(小根堆) 堆排序就是利用大根堆(小根堆)的特性进行排序的.从小到大排序一般用大根堆,从大到小一般用小根堆. 流程 先把

【数据结构】优先队列和堆

优先队列(priority queue) 对于一般的队列是在队列的尾部添加数据,在头部删除,以便先进先出. 而优先队列中的每个元素都有一个优先级,每次将一个元素放入队列中,而每次出队时都是将优先级最大的那个元素出队,称为高进先出(largest-in,first-out). 优先队列必须实现以下几个操作 1.入队(push):将一个元素放入队列中. 2.出队(pop):将优先级最高的元素从队列中删除. 要实现上面的操作可以维护一个有序的链表.每次插入数据时维护整个链表有序,即使用插入法,每次出队

利用堆实现堆排序&amp;优先队列

数据结构之(二叉)堆 一文末尾提到"利用堆可以实现:堆排序.优先队列".本文代码实现之. 1.堆排序 假设要将无序数组按非递减(递增)排序,则应使用大(小)顶堆.这里涉及到大堆排序涉及到三种操作:(1).MaxHeapify操作(自顶向下即SiftDown操作):(2).BuildMaxHeap操作(线性时间内将无序数组构造成一个最大堆):(3)将堆顶元素和堆的最后一个元素交换,并将堆元素大小减去1,对堆顶元素调用MaxHeapify操作重新调整为大顶堆,重复直到数组有序.下面是详细的

深入浅出数据结构C语言版(15)——优先队列(堆)

在普通队列中,元素出队的顺序是由元素入队时间决定的,也就是谁先入队,谁先出队.但是有时候我们希望有这样的一个队列:谁先入队不重要,重要的是谁的"优先级高",优先级越高越先出队.这样的数据结构我们称之为优先队列(priority queue),其常用于一些特殊应用,比如操作系统控制进程的调度程序. 那么,优先队列该如何实现呢?我们可以很快给出三种解决方案. 1.使用链表,插入操作选择直接插入到表头,时间复杂度为O(1),出队操作则遍历整个表,找到优先级最高者,返回并删除该结点,时间复杂度

数据结构与算法之堆与堆排序

在数据结构中,堆其实就是一棵完全二叉树.我们知道内存中也有一块叫做堆的存储区域,但是这与数据结构中的堆是完全不同的概念.在数据结构中,堆分为大根堆和小根堆,大根堆就是根结点的关键字大于等于任一个子节点的关键字,而它的左右子树又分别都是大根堆:小根堆与大根堆恰好相反.在C++的STL中优先队列priority_queue结构就是实现的堆结构.下来自己动手现实一个堆结构,包括heap_init,heap_insert,heap_top等操作. 1.堆的实现 因为堆是一棵完全二叉树,所以我们可以用顺序

[数据结构学习备忘录]堆及其堆排序

[数据结构学习备忘录] 堆 一种数据结构,物理存储方式:数组 逻辑存储方式:近似于完全二叉树,假定i为堆元素的序数[Index],那么i/2就是该元素的左子树,(i/2 + 1)就是该元素的右子树,分为两种堆:大根堆.小根堆:这两种堆的区别是:大根堆的根节点元素的值比左右子树的值都要大,小根堆则相反. 可用这种数据结构进行排序,称为堆排序. 与该数据结构相关的关键算法: ①   MaxHeaplfy 当堆的元素顺序出现错误时的调整函数 ②   BulidMax[min]Heap 建立大[小]根堆