堆的定义
• 堆是一个完全二叉树
–所有叶子在同一层或者两个连续层
–最后一层的结点占据尽量左的位置
• 堆性质
–为空, 或者最小元素在根上
–两棵子树也是堆
存储方式
• 最小堆的元素保存在heap[1..hs]内
– 根在heap[1]
–K的左儿子是2k, K的右儿子是2k+1,
–K的父亲是[k/2]
删除最小值元素
• 三步法
– 直接删除根
– 用最后一个元素代替根上元素
– 向下调整
• 首先选取当前结点p的较小儿子,如果比p大, 调整停止;否则交换p和儿子, 继续调整
插入元素和向上调整
• 插入元素是先添加到末尾, 再向上调整
• 向上调整: 比较当前结点p和父亲, 如果父亲比p小,停止; 否则交换父亲和p, 继续调整
堆的建立(堆的构造)
1、自底向上堆构造算法:
在初始化一棵包含几个节点的完全二叉树时,按给定的顺序来效置键;然后按照下面的方法对树进行“堆化”(如下图)从最后的父母节点开始,到根为止,该算法检查这些节点的键是否满足父母优势要求。如果该节点不满足,该算法把节点的键k和它子女的最大键进行交换,然后再检查在新位置上,k是不是满足父母优势要求。这个过程一直继续到对k的父母优势要求满足为止,对于以当前父母节点为根的子树,在完成了它“堆化”以后,该算法对于该节点的直接前趋进行同样的操作。在对树的根完成了这种操作以后,该算法就停止了。
2、自顶向下堆构造算法:
通过把新的键连续插入预先构造好的堆,来构造一个新堆,如何把一个新的键k插入到堆中呢?首先,把一个包含键k的新节点附加在当前堆的最后一个叶子后面,然后按照下面的方法把k筛选到它的适当位置,拿k和它父母的键作比较,如果后者大于等于k,算法停止;否则,交换这两个键并把k和它的新父母做比较。这种交换一直持续到k不大于它的最后一个父母,或者是达到了树的根为止(如下图)。在这个算法中,我们也可以把一个空节点向上筛选,直到达到合适的位置,才把k的值赋予它。
显然,这个插入操作所需的键值比较次数不可能超过堆的高度。因为包含几个节点的堆的高度大约是log2n所以插入的时间效率属于o(logn)。
删除堆中某个元素(不一定是堆顶元素)
1、以堆中最后一个元素取代被删除元素留下的空位(此举确保堆首先是一个完全二叉树)。
2、堆调整(堆化)。
时间复杂度分析
• 向上调整/向下调整
– 每层是常数级别, 共logn层, 因此为:O(logn)
• 插入/删除
– 只调用一次向上或向下调整, 因此都是:O(logn)
• 建堆
– 高度为h的结点有n/2h+1个,总时间为:O(n*logn)
【堆,这种数据结构适合解决何种类型的问题?】
???........
D$#@&(<):>M"|{_#!@SAQ$&GBD^KFG(*&$#$BK}{?<:>"X~@^
===========================================
【堆排序实践】
输入n个整数( n <= 10^5),按从大到小排序后输出。
操作步骤:
1) 建立堆。(直接在待排序数据A[]上建立最大堆)
2) 重复调整堆。取出堆首元素(根元素),交换至堆尾部,堆容量减1,继续调整。
优秀范例代码展示(构架清晰、代码简洁、高效!)
输入输出格式
输入格式:
二行,第一行,一个整数值n( n <= 10^5 );第二行,n个整数,每个整数均小于2^31,每个整数间有一个空格间隔。
输出格式:
一行,排好序(从大到小的顺序!!)的n个数据,每个数据间用一个空格间隔。
输入输出样例
输入样例#1:
4
4 5 2 897
输出样例#1:
897 5 4 2
提示信息
如果仅仅为了AC,那么排序吧!
sort也有一定概率堆排
#include<bits/stdc++.h>
#define sp sort
using namespace std;
int n,a[100010];
int cmp(int x,int y)
{
return x>y;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sp(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
cout<<a[i]<<" ";
}
return 0;
}