赞
踩
在开始学习堆之前,我们要先简单了解二叉树
一棵二叉树是结点的一个有限集合,该集合:
堆就是上面描述的完全二叉树的一种,完全二叉树就是除了最后一层外,其他层节点数达到最大。
堆与普通的完全二叉树结构相似,但存在大小堆的性质
大堆:树任何一个父节点>=子节点
小堆:树任何一个父节点<=子节点
我们今天实现一个小堆,并利用这个小堆实现一个简单的堆排序。
首先创建三个文件:
Heap.h —— 用于声明函数的头文件。
Heap.c —— 堆主要函数的实现。
test.c——测试堆,并实现简单堆排序。
堆在逻辑上是二叉树的结构,但在实际上是数组结构
typedef int HPDataType;//方便后需更改数据类型
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
//堆的初始化
void HeapInit(Heap* hp)
{
assert(hp);
hp->_a = NULL;
hp->_capacity = hp->_size = 0;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
assert(hp);
free(hp->_a);
hp->_a = NULL;
hp->_capacity = hp->_size = 0;
}
单独封装交换函数便于使用
//交换数据
void Swap(HPDataType* p1, HPDataType* p2)
{
HPDataType* tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
因为我们要建立小堆,在插入数据后(尾插)我们要调整这个数据的位置,使它满足小堆的性质,也就是父节点>=子节点。所以只要遇到父节点<子节点的情况,我们就交换这两个节点,然后向上更新父亲和孩子的位置,继续循环查看父节点<子节点的情况,直到这个插入的数据到达正确的地方。
//向上调整 void AdjustUp(HPDataType* a, int child) { int parent = (child - 1) / 2; while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]);//交换节点 //更新父亲和孩子的位置 child = parent; parent = (child - 1) / 2; } else { break; } } }
因为我们要建立小堆,在删除堆顶元素时,我们需要将堆顶和堆尾交换位置然后删除堆尾,为了满足小堆的性质,也就是父节点>=子节点。所以从堆顶向下只要遇到父节点<子节点的情况,我们就交换这两个节点,然后向下更新父亲和孩子的位置,继续循环查看父节点<子节点的情况,直到交换上来的这个数据到正确的地方。
//向下调整 void AdjustDown(HPDataType* a, int n, int parent)//n是堆元素个数 { int child = (parent * 2) + 1;//左孩子 while (child < n) {//child+1<n防止越界访问 if ((child + 1 < n) && (a[child] > a[child + 1]))//假设法找出较小的孩子 child++; if (a[parent] > a[child]) { Swap(&a[parent], &a[child]); parent = child; child = (parent * 2) + 1; } else { break; } } }
没有空间开辟空间,然后将数据尾插,再向上调整让小堆恢复其性质
// 堆的插入 void HeapPush(Heap* hp, HPDataType x) { assert(hp); if (hp->_capacity == hp->_size) { int newcapacity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity; HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity); if (tmp == NULL) { perror("realloc"); exit(1); } hp->_capacity = newcapacity; hp->_a = tmp; } hp->_a[hp->_size] = x; hp->_size++; AdjustUp(hp->_a, hp->_size - 1); }
准确来说是删除堆顶,先将堆顶和堆尾交换,删除堆尾(原堆顶),再向下调整让其恢复小堆的性质。
// 堆的删除
void HeapPop(Heap* hp)
{
assert(hp);
assert(hp->_size>0);
Swap(&hp->_a[0], &hp->_a[hp->_size - 1]);
hp->_size--;
AdjustDown(hp->_a, hp->_size, 0);
}
直接返回即可。
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
assert(hp);
assert(hp->_size>0);
return hp->_a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->_size;
}
返回size == 0时的结果,因为此时堆为空
// 堆的判空
int HeapEmpty(Heap* hp)
{
assert(hp);
return hp->_size == 0;
}
#include<stdbool.h> #include<assert.h> typedef int HPDataType; typedef struct Heap { HPDataType* _a; int _size; int _capacity; }Heap; //堆的初始化 void HeapInit(Heap* hp); // 堆的销毁 void HeapDestory(Heap* hp); // 堆的插入 void HeapPush(Heap* hp, HPDataType x); // 堆的删除 void HeapPop(Heap* hp); // 取堆顶的数据 HPDataType HeapTop(Heap* hp); // 堆的数据个数 int HeapSize(Heap* hp); // 堆的判空 int HeapEmpty(Heap* hp); //向上调整 void AdjustUp(HPDataType* a, int child); //向下调整 void AdjustDown(HPDataType* a, int n, int parent); //交换数据 void Swap(HPDataType* p1, HPDataType* p2);
#include"Heap.h" //堆的初始化 void HeapInit(Heap* hp) { assert(hp); hp->_a = NULL; hp->_capacity = hp->_size = 0; } // 堆的销毁 void HeapDestory(Heap* hp) { assert(hp); free(hp->_a); hp->_a = NULL; hp->_capacity = hp->_size = 0; } // 堆的插入 void HeapPush(Heap* hp, HPDataType x) { assert(hp); if (hp->_capacity == hp->_size) { int newcapacity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity; HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity); if (tmp == NULL) { perror("realloc"); exit(1); } hp->_capacity = newcapacity; hp->_a = tmp; } hp->_a[hp->_size] = x; hp->_size++; AdjustUp(hp->_a, hp->_size - 1); } // 堆的删除 void HeapPop(Heap* hp) { assert(hp); assert(hp->_size>0); Swap(&hp->_a[0], &hp->_a[hp->_size - 1]); hp->_size--; AdjustDown(hp->_a, hp->_size, 0); } // 取堆顶的数据 HPDataType HeapTop(Heap* hp) { assert(hp); assert(hp->_size>0); return hp->_a[0]; } // 堆的数据个数 int HeapSize(Heap* hp) { assert(hp); return hp->_size; } // 堆的判空 int HeapEmpty(Heap* hp) { assert(hp); return hp->_size == 0; } //交换数据 void Swap(HPDataType* p1, HPDataType* p2) { HPDataType tmp = *p1; *p1 = *p2; *p2 = tmp; } //向上调整 void AdjustUp(HPDataType* a, int child) { int parent = (child - 1) / 2; while (child > 0) { if (a[child] < a[parent]) { Swap(&a[child], &a[parent]); child = parent; parent = (child - 1) / 2; } else { break; } } } //向下调整 void AdjustDown(HPDataType* a, int n, int parent) { int child = (parent * 2) + 1; while (child < n) { if ((child + 1 < n) && (a[child] > a[child + 1])) child++; if (a[parent] > a[child]) { Swap(&a[parent], &a[child]); parent = child; child = (parent * 2) + 1; } else { break; } } }
创建一个堆,把一个数据混乱的数组插入到小堆中,然后循环打印堆顶数据(堆顶数据最小),再将堆顶数据删除,最后销毁堆。注意:这里的排序只是打印出来了排序的结果,数组内部并没有真正排序
#include"heap.h" void heap1() { Heap hp; HeapInit(&hp); int arr[] = { 123,645,21433,45,24,7654,2,3,12,122 }; for (int i = 0; i < (sizeof(arr) / sizeof(int)); i++) { HeapPush(&hp, arr[i]); } while (!HeapEmpty(&hp)) { printf("%d ", HeapTop(&hp)); HeapPop(&hp); } printf("\n"); HeapDestory(&hp); } int main() { heap1(); return 0; }
排序结果
只需要将向上调整和向下调整中if (a[child] < a[parent])
改为if (a[child] > a[parent])
即可。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。