这篇的主题主要是Heapsort(堆排序),下一篇ADT数据结构随笔再谈谈 - 优先队列(堆)。

  首先,我们先来了解一点与堆相关的东西。堆可以实现优先队列(Priority Queue),看到队列,我们马上就想到了队列的一个特点 - 先进先出(FIFO - first in first out),但优先队列有些不同的地方,优先队列是一种具有优先级先出的数据结构。

  堆的结构:

typedef int ElemType;

typedef struct
{
ElemType * arr;
int size;
}Heap;

  和数组栈的结构类似。

  初始化堆:

Heap * InitHeap(ElemType * nums, int ArraySize)
{
Heap * heap; heap = (Heap *)malloc(sizeof(Heap));
heap -> arr = (ElemType *)malloc(sizeof(ElemType));
for(heap -> size = 0; heap -> size < ArraySize; heap -> size ++)
heap -> arr[heap -> size] = nums[heap -> size]; return heap;
}

  找节点的关系:

/*
* 节点i的双亲
*/
static int HeapParent(int i)
{
return i/2;
} /*
* 节点i的左孩子
*/
static int HeapLeft(int i)
{
return 2*i + 1;
} /*
* 节点i的右孩子
*/
static int HeapRight(int i)
{
return 2*(i + 1);
}

  这里要保持的是一个大根堆:

/*
* 维护的最大堆(或最小堆)的性质
*    参数说明:
*      1.接收一个已存在的堆
*      2.节点位置
* 无返回值
*/
void Max_Heapify(Heap * heap, int i)
{
int _L = HeapLeft(i);
int _R = HeapRight(i);
int largest;
int temp; if(_L < heap -> size && heap -> arr[_L] > heap -> arr[i])
largest = _L;
else
largest = i;
if(_R < heap -> size && heap -> arr[_R] > heap -> arr[largest])
largest = _R;
if(largest != i)
{
temp = heap -> arr[i];
heap -> arr[i] = heap -> arr[largest];
heap -> arr[largest] = temp;
Max_Heapify(heap, largest);
}
}

  建大根堆:

static void Build_Max_Heap(Heap * heap)
{
int i; for(i = heap -> size/2; i >= 0; i--)
Max_Heapify(heap, i);
}

  排序:

void HeapSort(Heap * heap)
{
int i;
int temp; //维护堆,把初始建立的混乱的堆变为大根堆,以便进行排序
Build_Max_Heap(heap);
for(i = heap -> size - 1; i >= 0; i--)
{
temp = heap -> arr[0];
heap -> arr[0] = heap -> arr[i];
heap -> arr[i] = temp;
-- heap -> size; //每次把具有最小值的节点交换到根节点上,则把需要维护的堆的大小减小
Max_Heapify(heap, 0); //交换根节点后的堆的性质变了,需要不断维护
}
}

  至此,用二叉堆对一个数组的排序就完成了。

  然后,测试一下:

int main()
{
Heap * heap;
ElemType nums[] = {5, 13, 2, 25, 7, 17, 20, 8, 4}, * arr = nums;
int arrSize = sizeof(nums)/sizeof(int);
int i; heap = InitHeap(arr, arrSize); HeapSort(heap); for(i = 0; i < arrSize; i++)
printf("%d ", heap -> arr[i]);
printf("\n"); return 0;
}

  运行结果:

2 4 5 7 8 13 17 20 25

  完整的堆排序代码:

/**
* 堆排序(Heap Sort)
*/
#include <stdio.h>
#include <stdlib.h> typedef int ElemType; typedef struct
{
ElemType * arr;
int size;
}Heap; Heap * InitHeap();
static int HeapParent();
static int HeapLeft();
static int HeapRight();
void Max_Heapify();
static void Build_Max_Heap();
void HeapSort(); /*
* 初始化堆
*
* 参数说明:
* 1.传入数组
* 2.数组大小
*
* 返回值:堆
*/
Heap * InitHeap(ElemType * nums, int ArraySize)
{
Heap * heap; heap = (Heap *)malloc(sizeof(Heap));
heap -> arr = (ElemType *)malloc(sizeof(ElemType));
for(heap -> size = 0; heap -> size < ArraySize; heap -> size ++)
heap -> arr[heap -> size] = nums[heap -> size]; return heap;
} /*
* 某节点i的双亲
*/
static int HeapParent(int i)
{
return i/2;
} /*
* 某节点i的左孩子
*/
static int HeapLeft(int i)
{
return 2*i + 1;
} /*
* 某节点i的右孩子
*/
static int HeapRight(int i)
{
return 2*(i + 1);
} /*
* 维护堆
*/
void Max_Heapify(Heap * heap, int i)
{
int _L = HeapLeft(i);
int _R = HeapRight(i);
int largest;
int temp; if(_L < heap -> size && heap -> arr[_L] > heap -> arr[i])
largest = _L;
else
largest = i;
if(_R < heap -> size && heap -> arr[_R] > heap -> arr[largest])
largest = _R;
if(largest != i)
{
temp = heap -> arr[i];
heap -> arr[i] = heap -> arr[largest];
heap -> arr[largest] = temp;
Max_Heapify(heap, largest);
}
} /*
* 建堆
*/
static void Build_Max_Heap(Heap * heap)
{
int i; for(i = heap -> size/2; i >= 0; i--)
Max_Heapify(heap, i);
} /*
* 排序
*/
void HeapSort(Heap * heap)
{
int i;
int temp; Build_Max_Heap(heap); //维护堆,把初始建立的混乱的堆变为大根堆,以便进行排序
for(i = heap -> size - 1; i >= 0; i--)
{
temp = heap -> arr[0];
heap -> arr[0] = heap -> arr[i];
heap -> arr[i] = temp;
-- heap -> size; //每次把具有最小值的节点交换到根节点上,则把需要维护的堆的大小减小
Max_Heapify(heap, 0); //交换根节点后的堆的性质变了,需要不断维护
}
} int main(void)
{
Heap * heap;
ElemType nums[] = {5, 13, 2, 25, 7, 17, 20, 8, 4}, * arr = nums;
int arrSize = sizeof(nums)/sizeof(int);
int i; heap = InitHeap(arr, arrSize); HeapSort(heap); for(i = 0; i < arrSize; i++)
printf("%d ", heap -> arr[i]);
printf("\n"); return 0;
}

  这里的堆显然是一个二叉堆,因此可以把它想象成二叉树的样子来理解,关于整个堆排序的过程,它的时间复杂度为O(nlgn),因为调用Build_Max_Heap()的代价为O(n),而n - 1次调用Max_Heapify()的代价为O(lgn)。

  但至于为什么Build_Max_Heap()函数的时间复杂的只有O(n)...看起来应该是O(nlgn),我也还没完全理解这里的时间复杂度的计算...留个坑慢慢理解...懂了之后再补充。

  前面提到了堆的结构型和数组栈相似,实际上,堆与队列和栈有很大的关系,我们知道队列是FIFO,栈是FILO,从上面堆的维护操作等就可以看出,堆还有着具有优先级的先出的特性(注意与栈并不同),优先队列是基于最大/小堆实现的,虽然名字中有个队列,但区别还是很大的。

Binary Heap(二叉堆) - 堆排序的更多相关文章

  1. 堆(Heap)和二叉堆(Binary heap)

    堆(Heap) The operations commonly performed with a heap are: create-heap: create an empty heap heapify ...

  2. 二叉堆(binary heap)

    堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因 ...

  3. 二叉堆(binary heap)—— 优先队列的实现

    二叉堆因为对应着一棵完全二叉树,因而可以通过线性数组的方式实现. 注意,数组第 0 个位置上的元素,作为根,还是第 1 个位置上的元素作为根? 本文给出的实现,以数组第 1 个位置上的元素作为根,则其 ...

  4. 数据结构 之 二叉堆(Heap)

    注:本节主要讨论最大堆(最小堆同理). 一.堆的概念     堆,又称二叉堆.同二叉查找树一样,堆也有两个性质,即结构性和堆序性.     1.结构性质:     堆是一棵被完全填满的二叉树,有可能的 ...

  5. Java实现的二叉堆以及堆排序详解

    一.前言 二叉堆是一个特殊的堆,其本质是一棵完全二叉树,可用数组来存储数据,如果根节点在数组的下标位置为1,那么当前节点n的左子节点为2n,有子节点在数组中的下标位置为2n+1.二叉堆类型分为最大堆( ...

  6. 【425】堆排序方法(二叉堆)优先队列(PQ)

    参考:漫画:什么是二叉堆? 大根堆 小根堆 参考:漫画:什么是堆排序? 参考:漫画:什么是优先队列? 参考:[video]视频--第14周10--第8章排序10--8.4选择排序3--堆排序2--堆调 ...

  7. python下实现二叉堆以及堆排序

    python下实现二叉堆以及堆排序 堆是一种特殊的树形结构, 堆中的数据存储满足一定的堆序.堆排序是一种选择排序, 其算法复杂度, 时间复杂度相对于其他的排序算法都有很大的优势. 堆分为大头堆和小头堆 ...

  8. C# 实现简单的 Heap 堆(二叉堆)

    如题,C#  实现简单的二叉堆的 Push() 和 Pop(), 如有不足欢迎指正. 另外,在C#中使用 Heap 的相似功能可以考虑使用:Priority Queues,SortedDictiona ...

  9. 二叉堆 及 大根堆的python实现

    Python 二叉堆(binary heap) 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子 ...

随机推荐

  1. TCP/IP详解,卷1:协议--1

    引言 很多不同的厂家生产各种型号的计算机,它们运行完全不同的操作系统,但 T C P / I P 协议 族允许它们互相进行通信.这一点很让人感到吃惊,因为它的作用已远远超出了起初的设想. T C P ...

  2. 第十七篇 Linux下常用命令汇总

  3. Spring AOP编程(一)-AOP介绍

    1. AOP介绍 l         在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术 ...

  4. win api 音频可视化

    暂时记录,改天有时间再完善...其实写好好久了,但以前的代码丢了,重新写一遍.. 原理和 python 的一样,获取输入设备,然后把数据读取到 buffer 中,在绘制出来. 这里要注意两点: 1. ...

  5. Django 创建app 应用,数据库配置

    一.create project mkdir jango cd jango 目录创建project myapp django-admin startproject myapp 2.在给project创 ...

  6. i.MX RT1010之FlexIO模拟SSI外设

    恩智浦的i.MX RT1010是跨界处理器产品,作为i.MX RT跨界MCU系列的一个新的切入点,i.MX RT1010是成本最低的LQFP封装方式与i.MX RT系列产品一贯的高性能和易用性的结合产 ...

  7. List 重载添加-add,删除-remove方法,以及获取子集方法

    package seday12; import java.util.ArrayList;import java.util.List; /*** @author xingsir* List重载了一对ad ...

  8. Mysql与PostgreSql数据库学习笔记

    mysql 从最基础的数据引擎,到进程结构,都不能支持数据版本.导致其职能阻塞“并发”,不支持最基本的事务,innodb达不到基本事务要求,任何写数据,都导致整个表锁住.充其量只能算是一个玩具,或者说 ...

  9. C语言传递二维数组

    方法一, 形参给出第二维的长度. 例如: #include <stdio.h> ] ) { int i; ; i < n; i++) printf("/nstr[%d] = ...

  10. redis (一) --- 基本使用

    概述 redis是基于key-value 我们所说的数据类型实际是 key-value 中的 value .文章主要介绍的是redis 几个重要的数据类型的使用. 简单使用 //keys patter ...