赞
踩
目录
最大堆特点:
当然,也有最小堆,最小堆就是将最大堆反过来,根结点为最小值~
堆是树中最有个性的树,他是用数组表示的树 。
1.首先我们需要找到最后一个结点的父结点如图(a),我们找到的结点是 87,然后找出该结点的最大子节点与自己比较,若该子节点比自身大,则将两个结点交换。
图(a)中,87 比左子节点 95 小,则交换之。如图(b)所示
2.我们移动到第一步前一个父节点 93,如图(c)所示.同理做第一步的比较操作,结果不需要交换。
3.继续移动结点到前一个父结点 82,如图(d)所示,82 小于右子节点 95,则 82 与 95 交换,如图(e)所示,82 交换后,其值小于左子节点,不符合最大堆的特点,故需要继续向下调整,如图(f)所示。
4.所有节点交换完毕,最大堆构建完成~
堆数据结构的定义:
- #define DEFAULT_CAPACITY 128
- typedef struct _Heap {
- int* arr; //存储堆元素的数组
- int size; //当前已存储的元素个数
- int capacity; //当前的存储容量
- }Heap;
建立最大堆算法:
- bool initHeap(Heap& heap, int* original, int size);
- static void buildHeap(Heap& heap);
- static void adjustDown(Heap& heap, int index);
-
- //初始化堆
- bool initHeap(Heap& heap, int* original, int size) {
- //如果默认大小比size小,则申请默认大小的空间,否则申请size大小的空间
- int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : size;
- heap.arr = new int[capacity];
- if (!heap.arr)return false;
- heap.capacity = capacity;
- heap.size = 0;
- //如果存在原始数据,则拷贝过来
- if (size > 0) {
- memcpy(heap.arr, original, size * sizeof(int));
- heap.size = size;
- buildHeap(heap);
- }
- return true;
- }
-
- /*从最后一个父节点开始(heap.size) - 1 / 2)(因为size是从1开始,所以要先减去1)
- 逐个调整所有的父节点(直到根节点),确保每一个父节点都是最大堆,最后
- 整体上形成一个最大堆*/
- void buildHeap(Heap& heap) {
- for (int i = (heap.size - 1) / 2; i >= 0; i--) {
- adjustDown(heap, i);
- }
- }
-
- void adjustDown(Heap& heap, int index) {
- int cur = heap.arr[index]; //记录父节点值
- int parent, child;
-
- /*怕段是否存在大于当前结点的子节点,如果不存在,则堆本身平衡,不需要
- 调整,如果存在,则将最大子节点与之交换,交换后,如果这个子节点还有
- 子节点(即parent*2+1<heap.size 成立)则要按照相同步骤对这个子节点进行
- 调整*/
- for (parent = index; (parent * 2 + 1) < heap.size; parent = child) {
- child = parent * 2 + 1; //左子节点
-
- //取两个子节点最大结点
- if (((child + 1) < heap.size) && (heap.arr[child + 1] > heap.arr[child])) {
- child++;
- }
- if (cur >= heap.arr[child])break;//不大于,跳出循环
- else {
- /*大于当前父节点,进行交换,然后从子节点位置继续向下调整,
- 即for从第二次循环开始,初始值都为上一次的子节点位置*/
- heap.arr[parent] = heap.arr[child];
- heap.arr[child] = cur;
- }
- }
- }
完成测试代码 :
- int main() {
-
- Heap hp;
- int orignArry[] = { 1,2,3,87,93,82,92,86,95 };
-
- if (!initHeap(hp, orignArry, sizeof(orignArry) / sizeof(orignArry[0]))) {
- fprintf(stderr, "初始化堆失败!\n"); //输出到控制台
- exit(-1);
- }
-
- for (int i = 0; i < hp.size; i++) {
- cout << hp.arr[i] << endl;
- }
- system("pause");
- return 0;
- }
运行结果:
知识点补充:
1.memcpy(内存拷贝函数)
memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。用于double、int、结构体等
double arryDouble_Src[16]; double arryDouble_Det[16]; memcpy( arryDouble_Det, arryDouble_Src, sizeof(double)*16);
int arryInt_Src[16]; int arryInt_Dst[16]; memcpy( arryInt_Dst, arryInt_Src, sizeof(int)*16);
struct DataNum { int Data0; int Data1; } DataNum arryStruct_Src[16]; DataNum arryStruct_Dst[16]; memcpy( arryStruct_Dst, arryStruct_Src, sizeof(DataNum )*16);2. 在一般的函数前面加上static
加了static后表示该函数失去了全局可见性,只在该函数所在作用域内可见。
当函数声明为static以后,编译器在该目标编译单元内只含有该函数入口地址,没有函数名,其它编译器便不能通过该函数名调用该函数。
将数字99插入到下面最大堆中
对应的数组:{95, 93, 87, 92, 86, 82}
将新进的元素插入到大顶堆的尾部,如下图 b 所示:
对应的数组:{95, 93, 87, 92, 86, 82, 99}
此时最大堆已经被破坏,需要重新调整, 因加入的节点比父节点大,则新节点跟父节点调换即可,如图 c所示;调整后,新节点如果比新的父节点小,则已经调整到位,如果比新的父节点大,则需要和父节点重新进行交换,如图 d, 至此,最大堆调整完成。
代码实现:
- static bool insertHeap(Heap& heap, int value);
- static void adjustUp(Heap& heap, int index);
-
- bool insertHeap(Heap& heap, int value) {
- if (heap.size == heap.capacity) {
- fprintf(stderr, "栈空间耗尽!\n");
- return false;
- }
-
- int index = heap.size;
- heap.arr[heap.size++] = value;//先赋值value,再size++
- adjustUp(heap, index);
- }
-
- void adjustUp(Heap& heap, int index) {
- if (index < 0 || index >= heap.size) {
- //如果只有一个结点(插入的结点)inedx<0,或者大于堆的最大值,return掉
- return;
- }
- int temp = heap.arr[index];//temp为插入的值
- while (index > 0) {
-
- int parent = (index - 1) / 2;
- if (parent >= 0) { //如果索引没有出界,就执行想要操作
- if (heap.arr[parent] < temp) {
- heap.arr[index] = heap.arr[parent];
- heap.arr[parent] = temp;
- index = parent;
- }
- else break; //如果没有比父结点大,则跳出
- }
- else break; //越界,结束循环
- }
- }
完成测试代码 :
- int main() {
-
- Heap hp;
- int orignArry[] = { 1,2,3,87,93,82,92,86,95 };
-
- if (!initHeap(hp, orignArry, sizeof(orignArry) / sizeof(orignArry[0]))) {
- fprintf(stderr, "初始化堆失败!\n"); //输出到控制台
- exit(-1);
- }
-
- for (int i = 0; i < hp.size; i++) {
- cout << "第"<<i<<"个数为:"<<hp.arr[i] << endl;
- }
-
- insertHeap(hp, 99);
- printf("在堆中插入新的元素99,插入结果:\n");
- for (int i = 0; i < hp.size; i++) {
- cout << "第" << i << "个数为:" << hp.arr[i] << endl;
- }
- system("pause");
- return 0;
- }
运行结果:
当实现了插入功能后,对于堆的初始建立就有了两种方法。第一种就是直接将数组传入,第二种则是将元素一个个插入。相比较而言,对于一个个插入的方法比较次数更少,效率更高~
如果我们将堆顶的元素删除,那么顶部有一个空的节点,怎么处理?
当插入节点的时候,我们将新的值插入数组的尾部。现在我们来做相反的事情:我们取出数组中的最后一个元素,将它放到堆的顶部,然后再修复堆属性。
- static bool popMax(Heap& heap, int& value);
- bool popMax(Heap& heap, int& value) {
- if (heap.size < 1)return false;
- value = heap.arr[0];
-
- //将size-1的位置值赋给根节点,size本身又--
- /*相当于
- heap.arr[0] = heap.arr[heap.size-1];
- heap.size--;
- */
- heap.arr[0] = heap.arr[--heap.size];
- adjustDown(heap, 0); //向下执行堆调整
- return true;
- }
完成测试代码 :
- int main() {
-
- Heap hp;
- int orignArry[] = { 1,2,3,87,93,82,92,86,95 };
-
- if (!initHeap(hp, orignArry, sizeof(orignArry) / sizeof(orignArry[0]))) {
- fprintf(stderr, "初始化堆失败!\n"); //输出到控制台
- exit(-1);
- }
-
- for (int i = 0; i < hp.size; i++) {
- cout << "第"<<i<<"个数为:"<<hp.arr[i] << endl;
- }
-
- /*向堆中插入元素
- insertHeap(hp, 99);
- printf("在堆中插入新的元素99,插入结果:\n");
- for (int i = 0; i < hp.size; i++) {
- cout << "第" << i << "个数为:" << hp.arr[i] << endl;
- }*/
-
- int value;
- while (popMax(hp, value)) {
- cout << "依次出列最大元素:" << value << endl;
- }
-
- system("pause");
- return 0;
- }
运行结果:
使用堆方法依次弹出最大值,执行效率高于冒泡排序~
操作系统内核作业调度是优先队列的一个应用实例,它根据优先级的高低而不是先到先服务的方式来进行调度;
如果最小键值元素拥有最高的优先级,那么这种优先队列叫作 升序优先队列(即总是先删除最小的元素),类似的,如果最大键值元素拥有最高的优先级,那么这种优先队列叫作 降序优先队列(即总是先删除最大的元素);由于这两种类型是完全对称的,所以只需要关注其中一种,如升序优先队列。
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。
(选择排序工作原理 -——第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零)
核心排序如下:
- #include<iostream>
- using namespace std;
-
- #define DEFAULT_CAPACITY 128
- typedef struct _Heap {
- int* arr; //存储堆元素的数组
- int size; //当前已存储的元素个数
- int capacity; //当前的存储容量
- }Heap;
-
-
- bool initHeap(Heap& heap, int* original, int size);
- static void buildHeap(Heap& heap);
- static void adjustDown(Heap& heap, int index);
- static bool insertHeap(Heap& heap, int value);
- static void adjustUp(Heap& heap, int index);
- static bool popMax(Heap& heap, int& value);
-
- //初始化堆
- bool initHeap(Heap& heap, int* original, int size) {
- //如果默认大小比size小,则申请默认大小的空间,否则申请size大小的空间
- int capacity = DEFAULT_CAPACITY > size ? DEFAULT_CAPACITY : size;
- heap.arr = new int[capacity];
- if (!heap.arr)return false;
- heap.capacity = capacity;
- heap.size = 0;
- //如果存在原始数据,则拷贝过来
- if (size > 0) {
- memcpy(heap.arr, original, size * sizeof(int));
- heap.size = size;
- buildHeap(heap);
- }
- return true;
- }
-
- /*从最后一个父节点开始(heap.size) - 1 / 2)(因为size是从1开始,所以要先减去1)
- 逐个调整所有的父节点(直到根节点),确保每一个父节点都是最大堆,最后
- 整体上形成一个最大堆*/
- void buildHeap(Heap& heap) {
- for (int i = (heap.size - 1) / 2; i >= 0; i--) {
- adjustDown(heap, i);
- }
- }
-
- void adjustDown(Heap& heap, int index) {
- int cur = heap.arr[index]; //记录父节点值
- int parent, child;
-
- /*怕段是否存在大于当前结点的子节点,如果不存在,则堆本身平衡,不需要
- 调整,如果存在,则将最大子节点与之交换,交换后,如果这个子节点还有
- 子节点(即parent*2+1<heap.size 成立)则要按照相同步骤对这个子节点进行
- 调整*/
- for (parent = index; (parent * 2 + 1) < heap.size; parent = child) {
- child = parent * 2 + 1; //左子节点
-
- //取两个子节点最大结点
- if (((child + 1) < heap.size) && (heap.arr[child + 1] > heap.arr[child])) {
- child++;
- }
- if (cur >= heap.arr[child])break;//不大于,跳出循环
- else {
- /*大于当前父节点,进行交换,然后从子节点位置继续向下调整,
- 即for从第二次循环开始,初始值都为上一次的子节点位置*/
- heap.arr[parent] = heap.arr[child];
- heap.arr[child] = cur;
- }
- }
- }
- static bool popMax(Heap& heap, int& value);
- bool popMax(Heap& heap, int& value) {
- if (heap.size < 1)return false;
- value = heap.arr[0];
-
- //将size-1的位置值赋给根节点,size本身又--
- /*相当于
- heap.arr[0] = heap.arr[heap.size-1];
- heap.size--;
- */
- heap.arr[0] = heap.arr[--heap.size];
- adjustDown(heap, 0); //向下执行堆调整
- return true;
- }
-
-
- bool insertHeap(Heap& heap, int value) {
- if (heap.size == heap.capacity) {
- fprintf(stderr, "栈空间耗尽!\n");
- return false;
- }
-
- int index = heap.size;
- heap.arr[heap.size++] = value;//先赋值value,再size++
- adjustUp(heap, index);
- }
-
- void adjustUp(Heap& heap, int index) {
- if (index < 0 || index >= heap.size) {
- //如果只有一个结点(插入的结点)inedx<0,或者大于堆的最大值,return掉
- return;
- }
- int temp = heap.arr[index];//temp为插入的值
- while (index > 0) {
-
- int parent = (index - 1) / 2;
- if (parent >= 0) { //如果索引没有出界,就执行想要操作
- if (heap.arr[parent] < temp) {
- heap.arr[index] = heap.arr[parent];
- heap.arr[parent] = temp;
- index = parent;
- }
- else break; //如果没有比父结点大,则跳出
- }
- else break; //越界,结束循环
- }
- }
-
-
-
- int main() {
-
- Heap hp;
- int orignArry[] = { 1,2,3,87,93,82,92,86,95 };
-
- if (!initHeap(hp, orignArry, sizeof(orignArry) / sizeof(orignArry[0]))) {
- fprintf(stderr, "初始化堆失败!\n"); //输出到控制台
- exit(-1);
- }
-
- for (int i = 0; i < hp.size; i++) {
- cout << "第"<<i<<"个数为:"<<hp.arr[i] << endl;
- }
-
- //向堆中插入元素
- insertHeap(hp, 99);
- printf("在堆中插入新的元素99,插入结果:\n");
- for (int i = 0; i < hp.size; i++) {
- cout << "第" << i << "个数为:" << hp.arr[i] << endl;
- }
-
- int value;
- while (popMax(hp, value)) {
- cout << "依次出列最大元素:" << value << endl;
- }
-
- system("pause");
- return 0;
- }
-
-
运行截图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。