这篇的主题主要是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. python面试的100题(1)

    题目:有一个jsonline格式的文件file.txt大小约为10K def get_lines(): with open('file.txt','rb') as f: return f.readli ...

  2. XSS 2

    通过第一题之后继续进行第二题 我们会发现这个体会将内容放到<textarea></textarea>中然后我们刚才那段代码就失效了 因为这个代码可以将我们输入的内容转换成超文本 ...

  3. 网页域名在QQ内被多人投诉举报拦截的解决方案

    背景 相信大家经常会遇到一个头疼的问题就是,明明自己的网页没有违规内容(比如线下活动的推广),但链接在QQ内转发分享会被QQ管家拦截,导致用户无法访问. 那么当大家遇到这个问题的时候应该怎么办呢?不用 ...

  4. Python - Git for windows 下使用 交互式命令行

    1. 概述 尝试在 windows 环境的 git 命令行使用 python3 的交互模式 2. 环境 OS win10 1905 git 2.20.1 mintty 2.9.4 python 3.7 ...

  5. 【资源分享】Half-Life(半条命)中英版

    *----------------------------------------------[下载区]----------------------------------------------* ...

  6. 【音乐欣赏】《Wrong》 - Far Out / Emilia Ali

    曲名:Wrong 作者:Far Out / Emilia Ali [00:16.03]Ride body on mine [00:18.07]Griping your waist as I was o ...

  7. eclipse中怎么导入git库下载下来的web项目

    总的看来是有两种方式: 方式一:可以对已经从版本库下载到本地的项目操作(Maven导入) 你可以通过公司提供的内部的版本库的网址登录版本库,之后在里面下载自己想要的那个版本的代码包,见下图 点击右侧的 ...

  8. Postman 设置token为全局变量

    在做接口测试的时候,经常会用到不同用户登陆的token,来测试API,通过设置全局的token,这样更便捷: 注意设置的名称必须与你登陆后返回的名称一致,我这里是 AccessToken 1.配置环境 ...

  9. oracle 唯独测试视图

    --建立用户分配权限 create user groper identified by groper / grant connect,resource to groper / grant create ...

  10. 第一篇 Python中一切皆对象