Binary Heap(二叉堆) - 堆排序
这篇的主题主要是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(二叉堆) - 堆排序的更多相关文章
- 堆(Heap)和二叉堆(Binary heap)
堆(Heap) The operations commonly performed with a heap are: create-heap: create an empty heap heapify ...
- 二叉堆(binary heap)
堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因 ...
- 二叉堆(binary heap)—— 优先队列的实现
二叉堆因为对应着一棵完全二叉树,因而可以通过线性数组的方式实现. 注意,数组第 0 个位置上的元素,作为根,还是第 1 个位置上的元素作为根? 本文给出的实现,以数组第 1 个位置上的元素作为根,则其 ...
- 数据结构 之 二叉堆(Heap)
注:本节主要讨论最大堆(最小堆同理). 一.堆的概念 堆,又称二叉堆.同二叉查找树一样,堆也有两个性质,即结构性和堆序性. 1.结构性质: 堆是一棵被完全填满的二叉树,有可能的 ...
- Java实现的二叉堆以及堆排序详解
一.前言 二叉堆是一个特殊的堆,其本质是一棵完全二叉树,可用数组来存储数据,如果根节点在数组的下标位置为1,那么当前节点n的左子节点为2n,有子节点在数组中的下标位置为2n+1.二叉堆类型分为最大堆( ...
- 【425】堆排序方法(二叉堆)优先队列(PQ)
参考:漫画:什么是二叉堆? 大根堆 小根堆 参考:漫画:什么是堆排序? 参考:漫画:什么是优先队列? 参考:[video]视频--第14周10--第8章排序10--8.4选择排序3--堆排序2--堆调 ...
- python下实现二叉堆以及堆排序
python下实现二叉堆以及堆排序 堆是一种特殊的树形结构, 堆中的数据存储满足一定的堆序.堆排序是一种选择排序, 其算法复杂度, 时间复杂度相对于其他的排序算法都有很大的优势. 堆分为大头堆和小头堆 ...
- C# 实现简单的 Heap 堆(二叉堆)
如题,C# 实现简单的二叉堆的 Push() 和 Pop(), 如有不足欢迎指正. 另外,在C#中使用 Heap 的相似功能可以考虑使用:Priority Queues,SortedDictiona ...
- 二叉堆 及 大根堆的python实现
Python 二叉堆(binary heap) 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子 ...
随机推荐
- python numpy中sum()时出现负值
import numpy a=numpy.random.randint(1, 4095, (5000,5000)) a.sum() 结果为负值, 这是错误的,a.sum()的类型为 int32,如何做 ...
- hdu 1532 Drainage Ditches(网络流)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532 题目大意是:农夫约翰要把多个小池塘的水通过池塘间连接的水渠排出去,从池塘1到池塘M最多可以排多少 ...
- IIS的部署(二)------虚拟目录的使用
IIS的虚拟目录 一个站点的网页的存储位置目录是固定的,而且结构和物理保存网页的磁盘路径相同.例如:默认网页的存储位置是c:\inetpub\wwwroot,当访问localhost即访问c:\ine ...
- java: -source 1.5 中不支持 diamond 运算符 ,lambadas表达式 2018-03-13 22:43:47 eleven十一 阅读数 876更多
- linux下建立多级文件目录
linux下使用mkdir可以创建目录,使用mkdir -p参数就可以创建: mkdir -p /home/orale/duqiang1/duqiang2 如果父目录存在也不会报错.
- linux用字符模式修改权限
-rw-r--r-- tt.htm 从第二个字符起rw是说用户apple有读.写权,没有运行权,接着的r--表示用户组users只有读权限,没有运行权,最后的r--指其他人 (others)只有读权 ...
- Django模板结构优化{% include %}和{% extend %}标签
https://blog.csdn.net/xujin0/article/details/83420633
- 【PAT甲级】1105 Spiral Matrix (25分)
题意:输入一个正整数N(实则<=1e5),接着输入一行N个正整数(<=1e4).降序输出螺旋矩阵. trick: 测试点1,3运行超时原因:直接用sqrt(N)来表示矩阵的宽会在N是素数时 ...
- C语言当中int,float,double,char这四个有什么区别?
区别在以下方面: 一.定义方面: 1.int为整数型,用于定义整数类型的数据 . 2.float为单精度浮点型,能准确到小数点后六位 . 3.double为双精度浮点型,能准确到小数点都十二位 . 4 ...
- Linux下系统版本查询命令
# uname -a (Linux查看版本当前操作系统内核信息) # cat /proc/version (Linux查看当前操作系统版本信息) # cat /etc/issue 或 cat /etc ...