数据结构与算法笔记----二叉堆的定义及其基本操作,堆排序(HeapSort)

什么是堆

①作为一种数据结构,是完全二叉树
在数据结构课本中学习了两种:大根堆,小根堆
大根堆,双亲>孩子(可以用作实现优先队列,优先级高的先出)
小根堆,双亲<孩子
②堆是在程序运行时,而不是在程序编译时,申请某个大小的内存空间。即动态分配内存,对其访问和对一般内存的访问没有区别。

③堆是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程。

④堆是指程序运行时申请的动态内存,而栈只是指一种使用堆的方法(即先进后出)。
在这里插入图片描述
在这里插入图片描述

堆,栈和堆栈的区别

堆栈并不是堆,而是栈
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值