堆排序实现及应用

18 篇文章 0 订阅
17 篇文章 1 订阅

        堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

        

一、堆的结构

       一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。这是从0开始的结构。

二、堆的分类

        堆分为最大堆(大顶堆)和最小堆(小顶堆),顾名思义就是父节点和左右孩子节点的关系,如果父节点大于所有的子节点的堆就是大顶堆,父节点小于所有子节点的堆就是小顶堆,选择大顶堆和小顶堆决定了排序的顺序,是从大到小还是从小到大排序。

三、堆的操作

1、创建及堆的插入
在堆中插入元素,其实是一个上浮的操作,因为插入节点是在堆的最后,这个节点可能会破坏堆,所以需要通过调节这个插入元素的位置来最终恢复堆的有序性,这个操作就是一个上浮的操作。

上浮代码如下:
//k是插入元素的位置
void swim(int a[], int k)
{
    int j;
    while(k > 0)
    {
        j = (k - 1) / 2; //是k的父节点
        if(a[j] > a[k])
        {
           swap(&a[j], &a[k]);
           k = j;
        }
        else
        {
           break;
        }
    }
}

2、删除
按照定义删除只能是删除第一个节点,也就是根节点,为了方便重建堆,所以需要将最后一个节点替换到根节点,然后重新整理堆,这时就需要下降(sink)操作,将根节点逐渐的向下交换,最后使堆达到定义的要求。

代码如下:
//n是节点总数,删除总是从根节点开始
void sink(int a[], int n, int i)
{
   int j;
   while((2 * i + 1) < n)
   {
       j = 2 * i + 1;
       if(j + 1 < n && a[j] > a[j+1]) j++;
       if(a[j] < a[i])
       {
           swap(&a[i], &a[j]);
           i = j;
       }
       else
       {
           break;
       }
   }
}
3、建堆
如何将一个普通的数组建成一个堆呢,那就需要对当前堆中的非叶子节点进行sink操作,最后达到堆的要求

代码如下:
void build_heap(int a[], int n)
{
   int i;
   for(i = (n/2 -1); i >= 0; i--)
   {
       sink(a, n, i);
   }
}

四、堆排序

回到正题,堆排序其实就是不断的交换根节点和最后节点的过程,不断的缩小未排序集合的过程,这其中使用的就是sink操作
void heap_sort(int a[], int n)
{
   int i;
   for(i = n - 1; i > 0; i--)
   {
      swap(&a[i], &a[0]);
      sink(a, i, 0);
   }
}

最后将整体做了一个测试,代码如下:
#include <stdio.h>

void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

//k是插入元素的位置
void swim(int a[], int k)
{
    int j;
    while(k > 0)
    {
        j = (k - 1) / 2; //是k的父节点
        if(a[j] > a[k])
        {
           swap(&a[j], &a[k]);
           k = j;
        }
        else
        {
           break;
        }
    }
}

//n是节点总数,删除总是从根节点开始
void sink(int a[], int n, int i)
{
   int j;
   while((2 * i + 1) < n)
   {
       j = 2 * i + 1;
       if(j + 1 < n && a[j] > a[j+1]) j++;
       if(a[j] < a[i])
       {
           swap(&a[i], &a[j]);
           i = j;
       }
       else
       {
           break;
       }
   }
}


void build_heap(int a[], int n)
{
   int i;
   for(i = (n/2 -1); i >= 0; i--)
   {
       sink(a, n, i);
   }
}

void heap_sort(int a[], int n)
{
   int i;
   for(i = n - 1; i > 0; i--)
   {
      swap(&a[i], &a[0]);
      sink(a, i, 0);
   }
}


int main()
{
    int a[4] = {10, 40, 30, 5};
    swim(a, 3);
    int i;
    for(i = 0; i < 4; i++){
       printf("%d\t", a[i]);
    }
    printf("\n");
    int b[4] = {50, 10, 20, 40};
    sink(b, 4, 0);
    for(i = 0; i < 4; i++){
       printf("%d\t", b[i]);
    }
    printf("\n");

    int c[10] = {9, 12, 17, 30, 50, 20, 60 , 65, 4, 19};
    build_heap(c, 10);
    for(i = 0; i < 10; i++){
       printf("%d\t", c[i]);
    }
    printf("\n");
    heap_sort(c, 10);

    for(i = 0; i < 10; i++){
       printf("%d\t", c[i]);
    }
    printf("\n");
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值