什么是堆
①作为一种数据结构,是完全二叉树
在数据结构课本中学习了两种:大根堆,小根堆
大根堆,双亲>孩子(可以用作实现优先队列,优先级高的先出)
小根堆,双亲<孩子
②堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。
③堆是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程。
④堆是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出)。
堆,栈和堆栈的区别
堆栈并不是堆,而是栈
1.堆栈空间分配
①栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
②堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
2.堆栈缓存方式
①栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
②堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
3.堆栈数据结构区别
①堆(数据结构):堆可以被看成是一棵完全二叉树,如:堆排序。
②栈(数据结构):一种先进后出的数据结构。
堆的建立与插入删除操作
#include<stdio.h>
#include<malloc.h>
#define MaxData 5000
/*
二叉堆的建立与增删改操作
*/
typedef int elemtype;
typedef struct Heap{
elemtype *Data; /* 存储元素的数组 */
int Size; /* 堆中当前元素个数 */
int Capacity; /* 堆的最大容量 */
}Heap,*PHeap;
//*PHeap 是struct Heap类型,指向结构体
//Head,是struct Heap类型的一个变量
/*
堆的初始化
*/
PHeap init(int maxsize){
PHeap h = (PHeap)malloc(sizeof(Heap));
h->Data = (elemtype *)malloc((maxsize + 1)*sizeof(elemtype));//申请数组大小+1的空间
h->Size = 0;
h->Capacity = maxsize;
h->Data[0]=MaxData; //大根堆这里放最大值,放最小值就是小根堆
return h;
}
void insert(PHeap h,elemtype x){
if(h->Size==h->Capacity){
printf("堆满");
return;
}
int i;
for(i=++h->Size;h->Data[i/2]<x;i/=2){
/*
插入一个空穴,也就是数组的最后
根据堆的序号(即完全二叉树的序号方式),双亲的序号为i,则孩子的序号为2i,2i+1
如果h->Data[i/2]<x,就让该空穴上浮
*/
h->Data[i] = h->Data[i/2]; //将双亲赋给左孩子,即让该空穴"上浮"
}
h->Data[i] = x;//将x放入正确的位置
}
int Delete(PHeap h){//大根堆删除最大元,小根堆删除最小元
//在根处建立空穴;将空穴下滤到叶子节点
if(h->Size==0){
printf("堆空");
return 0;
}
int i,child;
int max = h->Data[1];//根值,即要删除的值
int last = h->Data[h->Size--];
for(i=1;2*i<=h->Size;i=child){
child = 2*i;
if(child!=h->Size&&h->Data[child+1]>h->Data[child]){
//找值更大一些的孩子
child++;
}
if(last<h->Data[child]){//如果该节点大,上浮该节点
h->Data[i]= h->Data[child];
}else{
break;
}
}
h->Data[i]=last; //将最后一个数插入空穴,该地址保存的是根的值(视为空穴)
return max;
}
int main(){
PHeap p;
p=init(10);
insert(p,54);
insert(p,27);
insert(p,11);
insert(p,33);
insert(p,9);
/*
循环调用insert,可以将一个堆调整为大根堆或小根堆
*/
printf("[%d]",Delete(p));
for(int i=1;i<p->Size+1;i++){
printf("%d ",p->Data[i]);
}
return 0;
}
堆排序算法
(1)将给定无序序列构造成一个大顶堆(一般升序采用大顶堆,降序采用小顶堆)。
一般来说有两种方法
插入法:时间复杂度为O(NlogN)(上面有代码),申请新数组,将元素一个个插入
调整法:时间复杂度为O(N)(下面用这个方法)从最后一个非叶子节点开始调整,将根,左孩子,右孩子(如有)中最大的作为新的根
(2)堆排序算法思想,就是不断拿掉根节点,把剩余部分调整成大根堆或小根堆
#include <stdio.h>
void swap(int *a,int i,int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
void maxHeapify(int a[], int i, int length)
{
int l = 2*i;
int r = 2*i+1;
int largest = i;
while(true)
{
if(l < length && a[l] > a[i])
largest = l;
if(r < length && a[r] > a[largest])
largest = r;
if(i != largest)
swap(a, i, largest);
else
break;
i = largest;
l = 2*largest;
r = 2*largest+1;
}
}
void buildMaxHeap(int *a,int n){
for(int i = n / 2; i >= 0; i--)
maxHeapify(a, i, n);
}
void HeapSort(int *a,int n){
while(n>1){
int i,child;
int max = a[1];//根值
int last = a[n--];
for(i=1;2*i<=n;i=child){
child=2*i;
if(child!=n&&a[child+1]>a[child]){//找更大的孩子
child++;
}
if(last<a[child]){//如果该节点大,上浮该节点
a[i]=a[child];
}else{
break;
}
}
a[i]=last; //将最后一个元素填入他应该在的位置
a[n+1]=max;//将根的元素放到最后1个位置 (注意是n+1,前面减过了)
}
}
int main(){
int a[11]={10000,54,22,17,38,92,3,15,6,76,89};//第一个数没有意义,是为了从1开始存储数据,不参与运算
buildMaxHeap(a,11);
for(int i=1;i<=10;i++){
printf("%d ",a[i]);
}
printf("\n");
HeapSort(a,10);
for(int i=1;i<=10;i++){
printf("%d ",a[i]);
}
return 0;
}