堆的定义与堆排序

一、基本定义

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其根节点的值。根据大小关系进行分类,将根结点不小于其左右孩子结点值的堆称为大顶堆。将根结点不大于其左右孩子结点值的堆称为小顶堆。堆一般用于优先队列的实现,优先队列默认使用大顶堆。

二、堆的基本操作

1、给定一个序列,如何建堆。根据初始序列,先从左到右,从上到下,建立一棵度为2的树。之后对树中各个结点进行调整,直到该树是大顶堆(或小顶堆,此处演示大顶堆)。具体调整方法:首先找到最下最右边的一个非叶子结点,假设编号为x,让x与以x为根结点的下一级分支结点进行比较,如果存在大于x的左右分支结点,让x与其交换,保证当前小分支里最大值为根结点,交换之后,再让x与下一级分支结点进行比较,知道该分支内结点值都小于x或者x没有下一级分支(x为叶子结点。)。

此处可以用数组存储堆,第一个结点存储在数组一号位,若结点在数组i号位,则左孩子结点为2*i,右孩子结点为2*1+i。

const int maxn=100;
int heap[maxn];

调整树,完成建堆的过程都是把结点从上往下进行调整,这叫向下调整。(判断某个结点是否是叶子结点依据:其左孩子2*i是否大于结点总个数n,大于是叶子结点。)

//low为欲调整结点的下标,high为堆最后一个结点的数组下标n。
void downAdjust(int low,int high){
    int i=low,j=i*2;    //j为i的左孩子结点。
    while(j<=high){    //判断j是否是叶子结点。
        if(j+1<high&&heap[j+1]>heap[j]){    //找左右结点中最大值。
               j=j+1;
        }
        if(heap[j]>heap[low]){
              swap(heap[low],heap[j]);
              i=j;        //保持i为欲调整结点。
              j=i*2;
        }else{
            break;
        }
    }
}          
    

假设序列中共有n个结点,由于是完全二叉树,所以叶子结点个数为n/2+1(向上取整)。非叶子结点数组下标区间为【1,n/2】。可以从n/2处倒着枚举结点(一定要倒着枚举,正着枚举无法保证每一个小分支下根节点都是该分支中的最大值。读者可自行举例验证。),对每个结点进行【n/2,n】范围内的调整。

//建堆
void createHeap(){
    for(int i=n/2;i>=1;--i){
        downAdjust(i,n);
    }
}

2、向堆中添加一个元素。把要添加的结点放在数组最后,然后进行向上调整操作,该操作总是把欲调整的结点与该结点所对应的根结点进行比较,如果比根结点值大,则交换该结点与其根结点,反复比较,直到到达堆顶,或者该结点不大于其根结点。

//添加元素x。
void insert(int x){
    heap[++n]=x;
    upAdjust(1,n);
}
//low一般设为1,high表示要调整的结点数组下标。在heap【low,high】范围内进行向上调整
void upAdjust(int low,int high){
    int i=high,j=i/2;
    while(j>=low){
        if(heap[j]<heap[i]){
            swap(heap[j],heap[i]);
            i=j;
            j=i/2;
        }else{
            break;
        }
    }
}
        

3、堆排序。堆排序是指用堆结构对一个序列进行排序,是对一个已经建好的堆进行操作,此处建立大顶堆(非递增排序)。对一个堆来说,堆顶元素是最大的,堆排序思想就是,每次都取堆顶元素,然后用堆中最后一个结点替换至堆顶,接着对堆进行一次向下调整。反复实现,直到堆中只剩一个元素。在实际中,为了节省时间,倒着遍历数组,假设当前为i号位,将堆顶与i结点互换,在【1,i-1】范围内进行向下调整操作。

//堆排序
void heapSort(){
    createHeap();
    for(int i=n;i>1;--i){
        printf("%d ",heap[i]);
        swap(heap[i],heap[1]);
        downAdjust(1,i-1);
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值