【堆排序算法】(C语言实现)

article/2025/10/29 18:20:41

堆排序

  • 一.堆排序
    • 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)


http://chatgpt.dhexx.cn/article/fnOxo0eT.shtml

相关文章

堆排序算法

堆排序 堆排序&#xff08;Heapsort&#xff09;是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构&#xff0c;并同时满足堆积的性质&#xff1a;即子结点的键值或索引总是小于&#xff08;或者大于&#xff09;它的父节点。 基本思想 利用大顶堆…

3.图解排序算法(三)之堆排序

作者&#xff1a; dreamcatcher-cx 出处&#xff1a; http://www.cnblogs.com/chengxiao/ 本文版权归作者和博客园共有&#xff0c;欢迎转载&#xff0c;但未经作者同意必须保留此段声明&#xff0c;且在页面明显位置给出原文链接。 原文链接&#xff1a;https://www.cnblogs.…

排序算法:堆排序

相关博客&#xff1a; 排序算法&#xff1a;冒泡排序、插入排序、选择排序、希尔排序 排序算法&#xff1a;归并排序、快速排序 排序算法&#xff1a;桶排序、计数排序、基数排序 排序算法&#xff1a;堆排序 十大排序算法小结 一、堆&#xff1a; 1、什么是堆&#xff1…

排序算法 —— 堆排序(图文超详细)

文章目录 堆排序1. 排序思路2. 如何建成大根堆2.1. 将待排序的数据看成一个完全二叉树。2.2. 建堆思想2.3. 建堆步骤2.3.1.先将最后一棵子树建成大根堆2.3.2.将其余子树建成大根堆2.3.3. 代码分析 3 如何实现堆排序3.1 排序思路3.2 代码实现的思路3.3 代码分析3.4 排序过程 4. …

十大经典排序算法----堆排序(超详细)

目录 1. 堆排序的基础知识 1.1 大顶堆&&小顶堆 1.2 向下调整算法 1.3 物理结构与逻辑结构的关系 2. 堆排序详解 2.1 堆排序整体思路 2.2 思路详解 2.2.1 建堆 2.2.2 大堆or小堆 2.2.3 输出数据 3. 时间复杂度分析 4. 完整代码 5. 彩蛋 1. 堆排序的基础知识…

【转载】堆排序算法(图解详细流程)

堆排序的时间复杂度O(N*logN),额外空间复杂度O(1)&#xff0c;是一个不稳定性的排序 目录 一 准备知识 1.1 大根堆和小根堆 二 堆排序基本步骤 2.1 构造堆 2.2 固定最大值再构造堆 三 总结 四 代码 一 准备知识 堆的结构可以分为大根堆和小根堆&#xff0c;是一个完全…

堆排序算法(图解详细流程)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 堆排序基本介绍大顶堆举例说明小顶堆举例说明堆排序的基本思想堆排序步骤图解说明堆排序的基本思路总结 堆排序基本介绍 堆排序是利用堆这种数据结构而设计的一种排…

堆排序详细图解(通俗易懂)

什么是堆 堆是一种叫做完全二叉树的数据结构&#xff0c;可以分为大根堆&#xff0c;小根堆&#xff0c;而堆排序就是基于这种结构而产生的一种程序算法。 堆的分类 大根堆:每个节点的值都大于或者等于他的左右孩子节点的值 小根堆:每个结点的值都小于或等于其左孩子和右孩子…

PreferenceActivity(二)

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我正好又在项目中用到&#xff0c;所以就把自己的使用的在这总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; Android系统截图&#xff08;左&#xff09; …

android设置页面之PreferenceActivity及Preference

离上篇博客刚好一周&#xff0c;希望后面会记录更多的内容&#xff0c;也算自己的Android笔记吧。 本篇主要记录一般的android设置页面PreferenceActivity的使用以及与之剪不断&#xff0c;理还乱的Preference。 &#xff08;一&#xff09;如何使用 Android系统自带的设置应用…

android中PreferencesActivity的使用(一)

在使用android手机的时候&#xff0c;尤其是在操作软件设置时&#xff0c;我们经常见到这样的界面&#xff1a; 这是怎么来实现的的呢&#xff1f;其实android已经提供了相应的类和方法&#xff0c;当进行简单数据存储时&#xff08;比如&#xff1a;软件配置参数&#xff09;a…

PreferenceActivity用法简介(二)

本测试主要是为了测试PreferenceActivity的使用&#xff0c;其中设置了播放背景音乐和开启wifi的设置&#xff0c;也就是本文要讲的 PreferenceActivity。 Android提供了放摆放的工具来定义所有的程序的首选项&#xff0c;并支持既不不许要编写代码的情况写显示这些首选项。可…

Android之PreferenceActivity

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我在看Android Samples时无意中看见了&#xff0c;所以就稍微总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; 好了&#xff0c;我们看到Android系统本身就大…

Android PreferenceActivity使用

PreferenceActivity继承了ListActivity&#xff0c;定义Activity继承PreferenceActivity。在res目录下新建一个xml文件夹&#xff0c;接着在这个文件夹下新建一个取名为preferences.xml的File文件&#xff0c;xml中可以使用的标签(Tag)可以分为两类&#xff0c;一类是管理布局的…

PreferenceActivity定制

android很多设置界面都会使用PreferenceActivity来实现&#xff0c;但那个界面比较丑陋&#xff0c;显示开发总是满足不了要求。 可以自己实现一个&#xff0c;但是那样又会使Activity中的逻辑代码和xml布局文件过于复杂&#xff0c;远远不及PreferenceActivity来的方便快捷。…

PreferenceActivity、PreferenceFragment使用

目录 目录前言PreferenceActivity preferences_scenario_1xmlPreference Activity演示 PreferenceFragment xml布局文件Preference FragmentPreference Activity管理Fragment 适配 前言 转来转去又回到了Android&#xff0c;闲话少说&#xff0c;这里是参考Android原生的Setti…

Android PreferenceActivity的介绍

转载&#xff1a;http://blog.chinaunix.net/uid-24666775-id-351136.html 在Android中的APIdemos是中经常遇到过继承于PreferenceActivity这个类&#xff0c;紧接着就是addPreferencesFromResource(R.xml.*******);&#xff08;附&#xff1a;这个******就是一个XML文件&#…

Android开发--详解SharedPreference/PreferenceActivity

我们经常看到应用程序的设置页面&#xff0c;一般用到设置页面时&#xff0c;我们会继承自PreferenceActivity&#xff0c;它实现了SharedPreference&#xff0c;并生成相应的XML文件自动保存用户的设置&#xff0c;在设置页面中&#xff0c;每一个列表项都是一个Preference&am…

Android之PreferenceActivity 详解

看到很多书中都没有对PreferenceActivity做介绍&#xff0c;而我正好又在项目中用到&#xff0c;所以就把自己的使用的在这总结一下&#xff0c;也方便日后查找。 PerferenceActivity是什么&#xff0c;看下面的截图&#xff1a; Android系统截图&#xff08;左&#xff09; …

android的PreferenceActivity

前言 这段时间在研究android平台上的开源项目——StandupTimer&#xff0c;这是由jwood所设计的一个较为简单android应用&#xff0c;用于控制会议时间&#xff0c;类似秒表倒计时。 PreferenceActivity PreferenceActivity是android提供的对系统信息和配置进行自动保存的Activ…