基础排序算法—(堆)和堆排序

堆是一种灵巧,部分有序的数据结构,它适合用来实现优先队列。优先队列是元素的一个集合,其中每个元素都包含一个被称作元素优先级的可排序属性,优先队列支持下面的操作

  1. 找出一个具有最高优先级的元素(一般是最大或者最小)
  2. 删除一个具有最高优先级的元素
  3. 添加一个元素到集合中去

堆的概念

定义:堆可以定义为一根二叉树,树的节点包含键(每一个节点都有一个键值),并且满足以下条件。

  • 树的形状要求—这根二叉树是完全二叉树,就是树的每一层都是满的,除了最后一层最右边的元素可能会缺。
  • 父母优势要求—堆的特性,每一个节点都要大于或者等于他子女的键。
    在这里插入图片描述

堆的特性

在这里插入图片描述

如何来构造一个堆呢? 有两种做法

  1. 自底向上堆构造
    初始化一颗包含n个节点的完全二叉树,按照给定数的顺序来放置键,然后按照下面的方法对树进行处理,从最后的父母节点开始,直到根为止,依次检查这些节点的键K是否满足父母优势,如果不满足,就把该节点的键K和它子女的最大键进行交换,然后在检查新位置上,K是否满足父母优势,这个过程一直继续到对K的父母优势要求满足为止。对于以当前父母节点为根的子树,在完成它的堆化后,该算法对该节点的直接前驱进行同样的操作,在对树的根完成这种操作以后,该算法就停止了。


c语言实现

void HeapBottomUp(int n)
{
 int i,j;
 int k;
 int v;
 int heap;
 for(i=n/2;i>=1;i--)//遍历所有的父母节点
 {
  k=i;
  v=h[k];     //存放父母节点的键值
  heap=0;      //设置标志,表示此节点是否可继续堆化
  while(!heap&&2*k<=n)  //
  {
   j=2*k;
   if(j<n)   //最后一个父母节点存在两个子女
   {
    if(h[j]<h[j+1])
    j=j+1;
   }
   if(v>=h[j])
   heap=1;
   else
   {
    h[k]=h[j];
    k=j;
    h[k]=v;
   }
  }
 }
}
  1. 把新的键连续插入预先构造好的堆,来建造一个新堆,也可以叫做自顶向下构造算法。
    通过把新的键插入预先构造好的堆,来构造一个新堆,那么我们怎么把一个新的键插入堆中呢?
    首先把一个包含键k的结点附加在当前堆的最后一个叶子后面,然后按照下面的方法把k筛选到正确的位置,拿k与它的父母作比较,如果它大于父母,则交换,如果不大于或者达到树根,算法停止,否则交换后继续比较与新父母的大小。
    在这里插入图片描述

那么我们如何从堆中删除一个元素呢?

这里我们考虑的是删除根中的键,利用下面的算法删除根中的键。
在这里插入图片描述

从堆中删除最大的键

  1. 根的键和堆最后一个键K做交换。
  2. 堆的规模减一
  3. 验证根此时的键是否满足父母优势,做法同于自底向上堆构造里的算法

堆排序就是堆的一个很好应用

		 	 堆排序

堆排序的步骤大致分为两部

  1. 构造堆,就是为给定的数组构造一个堆
  2. 删除最大键,就是对剩下的对进行n-1次根删除操作
    最后的结果是按照降序删除了数组的元素,但是对于堆的数组的实现来说,一个正在被删除的元素是位于最后的,所以结果数组将恰好是按照升序排列的原始数组。
    在这里插入图片描述

堆排序实现代码

#include<stdio.h>
void swap(int * array, int  i, int  j)  
   {   
      int  tmp;  
      tmp = array[j];  
      array[j] = array[i];  
      array[i] = tmp;  
} 
 /*最小根堆调整,这里的注释在上面的图片有说明*/  
  void minheapify(int * array,int heapsize,int currentnode)
{
      int i,j;
     int k;
     int heap;
     int parentvalue;
     for(i=heapsize/2;i>=1;i--)
     {
      k=i;
      parentvalue=array[k];
  heap=0;
  while(!heap&&2*k<=heapsize)
  {
   j=2*k;
   if(j<heapsize)
   {
    if(array[j]>array[j+1])
    j++;
   }
   if(parentvalue<=array[j])
   heap=1;
   else
   {
    array[k]=array[j];
    k=j;
    array[k]=parentvalue;
   }
  }
  }
}
void maxheapify(int * array, int  heapsize, int  currentnode)  
  { 
     int i,j;
     int k;
     int heap;
     int parentvalue;
     for(i=heapsize/2;i>=1;i--)
     {
      k=i;
      parentvalue=array[k];
  heap=0;
  while(!heap&&2*k<=heapsize)
  {
   j=2*k;
   if(j<heapsize)
   {
    if(array[j]<array[j+1])
    j++;
   }
   if(parentvalue>=array[j])
   heap=1;
   else
   {
    array[k]=array[j];
    k=j;
    array[k]=parentvalue;
   }
  }
  }
  }  
void minheapcreate(int * array,int heapsize)
{
 int i;
 for(i=heapsize/2;i>=1;i--)//依次检查所有父母节点是否满足堆的性质
 {
  minheapify(array,heapsize,i);//调整堆,化堆为最小堆。 
 }
}  
  void maxheapcreate(int * array, int  heapsize)  
 { 
      int i;  
      for(i = heapsize/2; i >= 1; i--)  
      {  
          maxheapify(array, heapsize, i);  
      }  
  }   
int main()
{
 int array[]={0,6,1,14,7,5,8};
 //初始化数组,也叫作待排序数组,第一个0不是需要排序的数,只是为了表述排序从下标1到 6的这六个数 
 int *res=array;   //res用作返回值,在array上做修改。 
 int i,heapsize=sizeof(array)/sizeof(int)-1;//堆使用数组实现,数组的大小为6; 
 printf("原始数据:\n");
 for(i=1;i<=heapsize;i++)
 {
  printf("%d\t",array[i]);
 }
 printf("\n");
 for(i=heapsize;i>=1;i--)//堆的规模减一(因为已经把最小的数放在数组末尾了),继续创建并调整最小堆。 
 {
  minheapcreate(array,i);//创建最小堆 并且调整最小堆,可知经过最小堆的调整之后,数组的下标1就是最小的数 
  swap(array,1,i);//将最小的数和数组的最后一个位置交换,然后堆的规模减一。 
 }
 printf("最小堆输出(利用最小堆实现数组降序排列):\n");
 for(i=1;i<=heapsize;i++)
 printf("%d\t",array[i]);
 printf("\n");
 //以下是最大堆的处理,思想同于上述最小堆。 
 for(i=heapsize;i>=1;i--)
 {
  maxheapcreate(array,i);
  swap(array,1,i);
 }
 printf("最大堆输出(利用最大堆实现数组升序排列):\n");
 for(i=1;i<=heapsize;i++)
 printf("%d\t",array[i]);
 printf("\n");
 return 0; 
 }

运行截图
在这里插入图片描述
实现最大堆,最小堆还可以有另外一个技巧,使用函数自身调用。
可以参考下面的优秀博客,写的很清楚

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Devin Dever

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值