堆排序

  • 一.堆排序
    • 1.堆的概念及性质
      • 1.1堆的概念
      • 1.2堆的性质
  • 二.向下调整和向上调整两大算法
    • 1. 向下调整算法
      • 2.1 向下调整算法基本思路
    • 2. 向上调整算法
      • 2.2 向上调整的基本思路
  • 三.堆的实现(小堆)
    • 1.头文件包含
    • 2. 接口实现
  • 四.任意树调整为堆(小堆为例)
    • 1.向下调整法建堆
    • 2.堆(大堆)排序

一.堆排序

1.堆的概念及性质

1.1堆的概念

a. 堆是一种基本的数据结构。在这里我用数组来形容,在一个二叉堆的数组中,每一个元素()都要保证大于等于小于等于另外两个特定位置的元素(左右子树)。同时相应的,这些元素()又要大于等于小于等于另外两个相应位置的元素(左右子树),整个数据结构以此类推。如果我们将整个数据结构画成树状结构,就能够清晰地看出整个结构的样子。

1.2堆的性质

a. 堆的逻辑结构一定是完全二叉树,物理结构为顺序表,用数组实现。
b. 任一根结点的值是其子树所有结点的最大值最小值

最大值时,称为“最大堆”,也称大根堆,如1.1中图一;
在完全二叉树中,任何一个子树的最大值都在这个子树的根结点

最小值时,称为“最小堆”,也称小根堆,如1.1中图二;
在完全二叉树中,任何一个子树的最小值都在这个子树的根结点。

c. 在物理结构上,如果父亲节点的位置为k,那么它的左右孩子节点分别为2k+1和 2k+2,那么如果孩子节点位置为k,则父亲节点为(k-1) / 2。

二.向下调整和向上调整两大算法

1. 向下调整算法

2.1 向下调整算法基本思路

a.若想将其调整为小堆,那么根节点的左右子树必须是小堆
b.若想将其调整为大堆,那么根节点的左右子树必须是大堆

c.向下调整算法的基本思路(小堆)
1.从根节点处开始,选出左右孩子中值较小的孩子。
2.让小的孩子与其父亲进行比较。
若小孩子比父亲,则该孩子与其父亲的位置进行交换。并将原来小的孩子的位置当做父亲继续向下进行调整,直到调整到叶子节点为止。
若调整中间小的孩子比父亲,则不需要继续向下调整了,整个树已经为小堆了,调整完成

图片示例:

堆(小堆)的向下调整算法代码实现:

void AdjustDown(HPDataType* a,int size,int root)
{int parent = root;int child = 2 * parent + 1;//假设左孩子为较小的孩子while (child < size){if (child + 1 < size && a[child] > a[child + 1]){child++;}//如果右孩子存在,且右孩子小于左孩子,则假设不成立,右孩子为较小的孩子if (a[parent] > a[child]){Swap(&a[child], &a[parent]);//交换parent = child;child = 2 * parent + 1;}else{break;}}
}

使用堆的向下调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向下调整算法的时间复杂度为:O(logN)

2. 向上调整算法

2.2 向上调整的基本思路

a.若想将其调整为小堆,那么除该节点外,整个树必须为小堆
b.若想将其调整为大堆,那么除该节点外,整个树必须为大堆

c.向下调整 的基本思路(大堆)
1.由该节点找到其父亲节点。
若该节点(孩子节点)的值大于其父亲节点的值,两个节点的位置发生交换,再把原来父亲节点的位置当做孩子节点,继续向上调整,直到树的根为止。
中间调整过程中,孩子节点的值小于其父亲节点的值,则不需要在向上调整了,整个树已经为大堆了,调整完成

图片示例:

堆(大堆)的向下调整算法代码实现:

void AdjustUp(HPDataType* a,int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);//交换child = parent;parent = (child - 1) / 2;}else{break;}}
}

使用堆的向上调整算法,最坏的情况下(即一直需要交换结点),需要循环的次数为:h - 1次(h为树的高度)。而h = log2(N+1)(N为树的总结点数)。所以堆的向上调整算法的时间复杂度为:O(logN)

三.堆的实现(小堆)

1.头文件包含

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}Heap;
//堆的初始化
void HeapInit(Heap* HP);
//堆的销毁
void HeapDestroy(Heap* HP);
//堆的打印
void HeapPrint(Heap* php)
//向堆存放数据
void HeapPush(Heap* HP,HPDataType x);
//删除堆中元素
void HeapPop(Heap* HP);
//获取堆顶元素
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* HP);
// 堆的判空
int HeapEmpty(Heap* HP);

2. 接口实现

#include"heap.h"void HeapInit(Heap* HP)
{assert(HP);HP->a = NULL;HP->size = HP->capacity = 0;
}void HeapDestroy(Heap* HP)
{assert(HP);assert(HP->a);free(HP->a);HP->a = NULL;HP->capacity = HP->size = 0;
}void HeapPrint(Heap* php)
{assert(php);for (size_t i = 0; i < php->size; ++i){printf("%d ", php->a[i]);}printf("\n");
}void Swap(HPDataType* child, HPDataType* parent)
{HPDataType tmp = *child;*child = *parent;*parent = tmp;
}void AdjustUp(HPDataType* a,int child)
{int parent = (child - 1) / 2;while (child > 0){if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}void HeapPush(Heap* HP,HPDataType x)
{assert(HP);if (HP->size == HP->capacity){int newcapacity = HP->capacity == 0 ? 4 : 2 * HP->capacity;HPDataType* tmp = (HPDataType*)realloc(HP->a,newcapacity * sizeof(HPDataType));if (tmp == NULL){printf("malloc is fail\n");exit(-1);}HP->a = tmp;HP->capacity = newcapacity;}HP->a[HP->size] = x;HP->size++;AdjustUp(HP->a, HP->size - 1);
}void AdjustDown(HPDataType* a,int size,int root)
{int parent = root;int child = 2 * parent + 1;while (child < size){if (child + 1 < size && a[child] > a[child + 1]){child++;}if (a[parent] > a[child]){Swap(&a[child], &a[parent]);parent = child;child = 2 * parent + 1;}else{break;}}
}void HeapPop(Heap* HP)
{assert(HP);Swap(&HP->a[0], &HP->a[HP->size - 1]);HP->size--;AdjustDown(HP->a,HP->size,0);
}HPDataType HeapTop(Heap* HP)
{assert(HP);return HP->a[0];
}int HeapSize(Heap* HP)
{assert(HP);return HP->size;
}int HeapEmpty(Heap* HP)
{assert(HP);return HP->size == 0;
}

四.任意树调整为堆(小堆为例)

1.向下调整法建堆

如果左右子树不是小堆,就不能直接使用向下调整算法了!那么如何才能将一个任意的树调整为堆呢?该怎么办???
 其实答案很简单,我们只需要从倒数第一个非叶子结点开始,从后往前,按下标,依次作为根去向下调整即可。(倒数第一个非叶子节点一定是最后一个叶子节点的父亲)

a.逻辑结构

b.物理结构

int a[] = {3,5,2,7,8,6,1,9,4,0};

c.建堆代码:

for(int i = (n - 1 - 1) / 2; i >= 0; --i)
{AdjustDown(a,n,i);
}

那么建堆的时间复杂度又是多少呢?
 当结点数无穷大时,完全二叉树与其层数相同的满二叉树相比较来说,它们相差的结点数可以忽略不计,所以计算时间复杂度的时候我们可以将完全二叉树看作与其层数相同的满二叉树来进行计算。

我们计算建堆过程中总共交换的次数:
T ( n ) = 1 × ( h − 1 ) + 2 × ( h − 2 ) + . . . + 2h-3 × 2 + 2h-2 × 1
两边同时乘2得:
2 T ( n ) = 2 × ( h − 1 ) + 2 2 × ( h − 2 ) + . . . + 2h-2× 2 + 2h-1 × 1
两式相减得:
T ( n ) = 1 − h + 2 1 + 2 2 + . . . + 2 h-2 + 2 h-1
运用等比数列求和得:
T ( n ) = 2h − h − 1
由二叉树的性质,有N = 2h − 1和 h = log ⁡2 ( N + 1 ), 于是
T ( n ) = N − log ⁡2 ( N + 1 )
用大O的渐进表示法:
T ( n ) = O ( N )

总结一下:
 堆的向下调整算法的时间复杂度:T ( n ) = O ( log ⁡ N ) 。
 建堆的时间复杂度:T ( n ) = O ( N ) 。

2.堆(大堆)排序

那么堆建好后,如何进行堆排序呢?
步骤如下:
 (1) 将堆顶数据与堆的最后一个数据交换,然后对根位置进行一次堆的向下调整,但是调整时被交换到最后的那个最大的数不参与向下调整。
 (2) 完成步骤1后,这棵树除最后一个数之外,其余数又成一个大堆,然后又将堆顶数据与堆的最后一个数据交换,这样一来,第二大的数就被放到了倒数第二个位置上,然后该数又不参与堆的向下调整…反复执行下去,直到堆中只有一个数据时便结束。此时该序列就是一个升序。

void HeapSort1(int* a, int n){//建大堆排升序for (int i = (n - 1 - 1) / 2; i >= 0; i--){HeapDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);HeapDown(a, end, 0);end--;}}

时间复杂度O(N*logN)

“堆排序算法”(C语言实现)相关推荐

  1. 多线程堆排序算法C语言实现

    多线程堆排序算法C语言实现 代码主要实现对八十万个长整型数据的排序:利用8个线程实现,每个线程负责十万个数,数据由rand()函数产生. 代码如下: #include<stdio.h> # ...

  2. 堆排序算法c语言筛选法,【排序】排序算法之选择排序

    排序算法之选择排序 罗朝辉(http://www.cppblog.com/kesalin) 转载请注明出处 排序是数据处理中经常使用的一种重要运算,在计算机及其应用系统中,花费在排序上的时间在系统运行 ...

  3. 《数据结构与算法 Python语言描述》 读书笔记

    已经发布博客 <数据结构与算法 Python语言描述> 读书笔记 第二章 抽象数据类型和Python类 2.1 抽象数据类型abstract data type:ADT 2.1.1 使用编 ...

  4. 堆排序算法java左程云_堆排序算法以及JAVA实现

    堆的定义如下: n个元素的序列{k0,k1,...,ki,-,k(n-1)}当且仅当满足下关系时,称之为堆. " ki<=k2i,ki<=k2i+1;或ki>=k2i,ki ...

  5. c语言常用算法pdf,妙趣横生的算法(C语言实现 第2版) 带目录完整pdf[94MB]

    <妙趣横生的算法(C语言实现 第2版)>是深受广大读者好评的<妙趣横生的算法(C语言实现)>一书的全新升级版.本书在第1版的基础上对原书内容做了大量的调整和补充,并将书中的实例 ...

  6. 数据结构视频教程 -《[猎豹网校]数据结构与算法_C#语言》

    整个视频打包下载地址:史上最全的数据结构视频教程系列分享之<[猎豹网校]数据结构与算法_C#语言>,转载请保留出处和链接! 更多优秀资源请访问:我是码农 在猎豹网校授课的基本都是在IT行业 ...

  7. 经典的十种排序算法 C语言版

    经典的十种排序算法(C语言版) 1.冒牌排序 冒牌排序的特点 ​ 一趟一趟的比较待排序的数组,每趟比较中,从前往后,依次比较这个数和下一个数的大小,如果这个数比下一个数大,则交换这两个数,每趟比较后, ...

  8. 快速排序算法C语言实现

    快速排序算法C语言实现 在任何程序中,数组的排序都是极为重要的内容,我们需要按照业务需要对大量的数据进行排序,因此排序的速度或者说效率就显得极为重要了,因此选择一个效率较高的算法可以大大提升程序的性能 ...

  9. c语言贝叶斯分类,基于朴素贝叶斯分类器的文本分类算法(C语言)

    基于朴素贝叶斯分类器的文本分类算法(C语言) 基于朴素贝叶斯分类器的文本分类算法(C语言).txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了.他只是比对方更珍惜这份感情.#include ...

  10. 《数据结构与算法 C语言版》—— 3.8习题

    本节书摘来自华章出版社<数据结构与算法 C语言版>一 书中的第3章,第3.8节,作者:徐凤生,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 3.8习题 1名 ...

最新文章

  1. 【Android 逆向】整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )
  2. gis 路径拟合算法_决策树算法十问及经典面试问题
  3. C++以字符串形式返回第N个斐波那契的算法(附完整源码)
  4. 堆排序(heap_sort)
  5. 用SqlBulkCopy批量安插数据时提示来自数据源的 String 类型的给定值不能转换为指定目标列的类型 int...
  6. 关于设计模式——策略模式-Strategy Pattern
  7. 记录TCP协议使用Socket连接,客户端请求服务器read()阻塞问题
  8. 记一次Springboot启动异常
  9. SAS 9.4 破解时间限制
  10. sql思维导图---sql函数
  11. 从零实现 SPI_flash(W25Q256)
  12. 【配置管理】配置审核-物理配置审核-功能配置审核-配置管理审核
  13. 怎样用php制作动态烟花,动态烟花图片之自己动手制作
  14. HTTPS安全通讯 6. 安卓 使用BKS实现SSL/TLS安全协议
  15. SEM测试优点与特点
  16. 曝!苹果折叠iPhone要问世了
  17. php提取邮箱并返回,PHP 利用Mail_MimeDecode类提取邮件信息示例
  18. 论文笔记《Combining Events and Frames Using Recurrent Asynchronous Multimodal Networks for Monocular ...》
  19. 高校借力泛微,搭建一体化、流程化的​内控管理平台
  20. intent.setComponent()方法

热门文章

  1. 图论(五)单源最短路算法
  2. AD画板,如何提高工作效率?
  3. Scala安装和开发环境配置教程
  4. sast/dast/iast对比介绍
  5. android布局置顶_android linearlayout imageview置顶摆放
  6. 2022年中青杯数学建模B题初步思路
  7. GBase 8c 备份控制函数(三)
  8. 银行业数据安全建设要点分析2022
  9. 小米手机怎么截屏?小米手机区域截屏
  10. CUDA10,Torch安装解决NOTFOUND CUDA_cublas_device_LIBRARY,Ubuntu16.04安装torch遇到的错误