堆算法
特点:
1.每个节点最多可以有两个节点;
2.根节点的键值是所有堆节点键值中最大者,且每个根节点的值都比其子节点的值大;
3.除了根节点没有兄弟节点,最后一个左节点可以没有兄弟节点,其他节点必须要有兄弟节点;
计算公式:
i的子节点左节点的位置: 2i+1; i的右子节点位置: 2i+2; i的父节点位置: (i-1)/2 (都是取整数的);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEFAULT_CAPCITY 64
typedef int DataType;
//创建一个堆数组
typedef struct _Heap {
DataType* arr;//存储堆元素的数组
int size;//当前已存储的元素个数
int capacity;//当前存储的容量
}Heap;
//初始化堆
bool initHeap(Heap& heap, DataType* orginal, int size);
//插入元素
bool insert(Heap& heap, DataType val);
//删除元素
bool popMax(Heap& heap, DataType&val);
//建立一个堆
static void buildHeap(Heap& heap);
//调整父节点的下一个位置元素
static void adjustDown(Heap& heap, int index);
//调整父节点的上一个位置元素
static void adjustUp(Heap& heap, int index);
bool initHeap(Heap& heap, DataType* orginal, int size) {
int capacity = DEFAULT_CAPCITY > size ? DEFAULT_CAPCITY : size;
heap.arr = new DataType[capacity];
if (!heap.arr) {
return false;
}
heap.capacity = capacity;
heap.size = 0;
//如果存在原始数据则构建堆
if (size > 0) {
方式1: 直接调整所有元素
//memcpy(heap.arr, orginal, size * sizeof(int));
//heap.size = size;
建堆
//buildHeap(heap);
//方式2: 一次插入一个
for (int i = 0; i < size; i++) {
insert(heap, orginal[i]);
}
}
return true;
}
bool popMax(Heap& heap, DataType&val){
if (heap.size == NULL) {
return false;
}
//获取堆中最大的元素,然后把堆中最后的一个元素放到最大元素的位置
//再进行堆排列
val=heap.arr[0];
heap.arr[0] = heap.arr[--heap.size];
adjustDown(heap, 0);
return true;
}
//从最后一个父节点(size/2-1的位置)逐个往前调整所有父节点(直到根节点)
//确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆
void buildHeap(Heap& heap) {
for (int i = heap.size / 2 - 1; i >= 0; i--) {
adjustDown(heap, i);
}
}
//将当前的节点和子节点调整成最大堆
void adjustDown(Heap& heap, int index) {
int cur = heap.arr[index];//当前待调整的节点
int parent;
int child;
//判断是否存在大于当前节点子节点,如果不存在,则堆本身是平衡的,不需要调整;
//如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点
//则要继续按照同样的步骤堆这个子节点进行调整
for (parent = index; (parent * 2 + 1) < heap.size; parent = child) {
child = parent * 2 + 1;
//取两个子节点中的最大的节点
if (((child + 1) < heap.size) && (heap.arr[child] < heap.arr[child + 1])) {
child++;
}
//判断最大的节点是否大于当前的父节点
if (cur >= heap.arr[child]) {
//不大于,则不需要整理,跳出循环
break;
}
else {
//大于当前的父节点,进行交换,然后从子节点位置继续下调整
heap.arr[parent] = heap.arr[child];
heap.arr[child] = cur;
}
}
}
//将当前的节点和父节点调整成最大堆
void adjustUp(Heap& heap, int index) {
if (index < 0 || index >= heap.size) {
return;
}
while (index > 0) {
int temp = heap.arr[index];
int parent = (index - 1) / 2;
if (parent >= 0) {
//如果索引没有出界就执行相要的操作
if (temp > heap.arr[parent]) {
heap.arr[index] = heap.arr[parent];
heap.arr[parent] = temp;
index = parent;
}
else {
//如果已经比父节点小,直接结束循环
break;
}
}
else {
//越界结束循环
break;
}
}
}
//最大堆尾部插入节点,同时保证最大堆的特性
bool insert(Heap& heasp, int val) {
if (heasp.size == heasp.capacity) {
fprintf(stderr, "栈空间耗尽!\n");
return false;
}
int index = heasp.size;
heasp.arr[heasp.size++] = val;
adjustUp(heasp, index);
return true;
}
堆的企业级应用案例
优先队列:
如果最小键值元素拥有最高的优先级,那么这种优先队列叫作升序优先队列(即总是先删除最小 的元素),类似的,如果最大键值元素拥有最高的优先级,那么这种优先队列叫作降序优先队列 (即总是先删除最大的元素);由于这两种类型是完全对称的,所以只需要关注其中一种,如升 序优先队列
堆排序:
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特 点快速定位指定索引的元素. (选择排序工作原理 - 第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置, 然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待 排序的数据元素的个数为零)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
#define DEFAULT_CAPCITY 64
typedef int DataType;
#define Less(a,b) (a<b)
//创建一个堆数组
typedef struct _Heap {
DataType* arr;//存储堆元素的数组
int size;//当前已存储的元素个数
int capacity;//当前存储的容量
}Heap;
//初始化堆
bool initHeap(Heap& heap, DataType* orginal, int size);
//删除元素
bool popMax(Heap& heap, DataType&val);
//建立一个堆
static void buildHeap(Heap& heap);
//调整父节点的下一个位置元素
static void adjustDown(Heap& heap, int index);
bool initHeap(Heap& heap, DataType* orginal, int size) {
/*int capacity = DEFAULT_CAPCITY > size ? DEFAULT_CAPCITY : size;
heap.arr = new DataType[capacity];*/
heap.arr = orginal;
if (!heap.arr) {
return false;
}
heap.capacity = size;
heap.size = size;
//如果存在原始数据则构建堆
if (size > 0) {
//方式1: 直接调整所有元素
/*memcpy(heap.arr, orginal, size * sizeof(int));
heap.size = size;*/
//建堆
buildHeap(heap);
//方式2: 一次插入一个
/*for (int i = 0; i < size; i++) {
insert(heap, orginal[i]);
}*/
}
return true;
}
bool popMax(Heap& heap, DataType&val){
if (heap.size == NULL) {
return false;
}
//获取堆中最大的元素,然后把堆中最后的一个元素放到最大元素的位置
//再进行堆排列
val=heap.arr[0];
heap.arr[0] = heap.arr[--heap.size];
adjustDown(heap, 0);
return true;
}
//从最后一个父节点(size/2-1的位置)逐个往前调整所有父节点(直到根节点)
//确保每一个父节点都是一个最大堆,最后整体上形成一个最大堆
void buildHeap(Heap& heap) {
for (int i = heap.size / 2 - 1; i >= 0; i--) {
adjustDown(heap, i);
}
}
//将当前的节点和子节点调整成最大堆
void adjustDown(Heap& heap, int index) {
int cur = heap.arr[index];//当前待调整的节点
int parent;
int child;
//判断是否存在大于当前节点子节点,如果不存在,则堆本身是平衡的,不需要调整;
//如果存在,则将最大的子节点与之交换,交换后,如果这个子节点还有子节点
//则要继续按照同样的步骤堆这个子节点进行调整
for (parent = index; Less((parent * 2 + 1), heap.size); parent = child) {
child = parent * 2 + 1;
//取两个子节点中的最大的节点
if ((Less((child + 1),heap.size) && Less(heap.arr[child], heap.arr[child + 1]))) {
child++;
}
//判断最大的节点是否大于当前的父节点
if (Less(heap.arr[child],cur)) {
//不大于,则不需要整理,跳出循环
break;
}
else {
//大于当前的父节点,进行交换,然后从子节点位置继续下调整
heap.arr[parent] = heap.arr[child];
heap.arr[child] = cur;
}
}
}
//实现堆排序
void heapsort(Heap& heap) {
if (heap.size < 1) {
return;
}
while (heap.size > 0) {
int tmp = heap.arr[0];
heap.arr[0] = heap.arr[heap.size - 1];
heap.arr[heap.size-1] = tmp;
heap.size--;
adjustDown(heap, 0);
}
}
int main(void) {
Heap hp;
int origvals[] = { 2,3,6,9,11,80,45 };
if (!initHeap(hp, origvals, sizeof(origvals) / sizeof(origvals[0]))) {
fprintf_s(stderr, "初始化堆失败!\n");
exit(-1);
}
for (int i = 0; i < hp.size; i++) {
cout << i << "个元素:" << hp.arr[i] << endl;
}
heapsort(hp);
for (int i = 0; i < sizeof(origvals)/sizeof(origvals[0]); i++) {
cout <<"元素:" << origvals[i] << endl;
}
cout << "最大的3个元素:";
for (int i = sizeof(origvals) / sizeof(origvals[0])-1; i > (sizeof(origvals) / sizeof(origvals[0]))-4; i--) {
cout << origvals[i]<<",";
}
cout << endl;
cout << "最小的3个元素:";
for (int i = 0; i < 3; i++) {
cout << origvals[i] << ",";
}
cout << endl;
system("pause");
return 0;
}