目录
一,堆的实现
1.结构
2,函数接口
3,接口实现
4,代码链接
二,堆排序的实现
1.建堆
2,选择排序思想,只不过是用堆来选择。
一,堆的实现
1.结构
//堆的底层是一个完全二叉树,所以说我们用数组实现是不会有空间的浪费的。 //物理上是一个数组,但是逻辑上可以把他想象成一颗二叉树。 typedef ?int HPDataType; typedef struct Heap { ?? ?HPDataType* a; ?? ?size_t size; ?? ?size_t capacity; }Heap;
2,函数接口
void swap(HPDataType* pa, HPDataType* pb); void HeapInit(Heap* php); void HeapDestroy(Heap* php); void HeapPrint(Heap* php);
// 插入x以后,保持他依旧是(大/小)堆 void HeapPush(Heap* php, HPDataType x);
// 删除堆顶的数据。(最小/最大) void HeapPop(Heap* php); bool HeapEmpty(Heap* php); size_t HeapSize(Heap* php); HPDataType HeapTop(Heap* php); //向上调整算法 void Adjustup(HPDataType* a,size_t child); //向下调整算法。 void Adjustdown(HPDataType* a, size_t size,size_t root);
3,接口实现
void swap(HPDataType* pa, HPDataType* pb) { ?? ?HPDataType tmp = *pa; ?? ?*pa = *pb; ?? ?*pb = tmp; } void Adjustup(HPDataType* a, size_t child) { ?? ?//假设我们这里搞一个小堆。大堆的话也是对应的。 ?? ?assert(a); ?? ?size_t parent = (child - 1) / 2; ?? ?while (child > 0) ?? ?{ ?? ??? ?//if (a[child] > a[parent]) ?大堆 ?? ??? ??? ?if (a[child] < a[parent]) ? //小堆 ?? ??? ?{ ?? ??? ??? ?swap(&a[child], &a[parent]); ?? ??? ??? ?child = parent; ?? ??? ??? ?parent = (child - 1) / 2; ?? ??? ?} ?? ??? ?else ?? ??? ?{ ?? ??? ??? ?//走到这里那就是已经是堆了,可以提前结束了。 ?? ??? ??? ?break; ?? ??? ?} ?? ?} }
void Adjustdown(HPDataType* a, size_t size, size_t root) { ?? ?assert(a); ?? ?//假设我们这里还是建小堆 ?? ?size_t parant = root; ?? ?size_t child = parant*2+1; ?//这里默认是左孩子,要算有孩子加一就是了。 ?? ?while (child<size) ?? ?{ ?? ? ? ?//选出左右孩子中的较小的那个 ?? ??? ?if (child+1<size && a[child + 1] < a[child]) ?? ??? ?{ ?? ??? ??? ?child++; ?? ??? ?} ?? ??? ?//小堆,大堆相反就行。 ?? ??? ?if (a[child] < a[parant]) ?? ??? ?{ ?? ??? ??? ?swap(&a[child], &a[parant]); ?? ??? ??? ?parant = child; ?? ??? ??? ?child = parant*2+1; ?? ??? ?} ?? ??? ?else ?? ??? ?{ ?? ??? ??? ?//已经是堆了,不需要调整了,提前结束。 ?? ??? ??? ?break; ?? ??? ?} ?? ?} } void HeapInit(Heap* php) { ?? ?assert(php); ?//给我传的结构体一定不能为空吧。 ?? ?php->a = NULL; ?? ?php->capacity = php->size = 0; } void HeapDestroy(Heap* php) { ?? ?assert(php); ?? ?free(php->a); ?? ?php->a = NULL; ?? ?php->capacity = php->size = 0; ?? ?//这里不可以释放php。 } void HeapPrint(Heap* php) { ?? ?assert(php); ?? ?for (size_t i = 0; i < php->size; i++) ?? ??? ?printf("%d ", php->a[i]); ?? ? ? ?printf("\n"); }
// 插入x以后,保持他依旧是(大/小)堆 void HeapPush(Heap* php, HPDataType x) { ?? ?assert(php); ?? ?if (php->capacity == php->size) ?? ?{ ?? ??? ?//扩容。 ?? ??? ?int nwecapacity = php->capacity == 0 ? 4 : php->capacity * 2; ?? ??? ?HPDataType* tmp = realloc(php->a, nwecapacity * sizeof(HPDataType)); ?? ??? ?//检查开空间是否成功。 ?? ??? ?if (tmp == NULL) ?? ??? ?{ ?? ??? ??? ?printf("realloc ?fail\n"); ?? ??? ??? ?exit(-1); ?? ??? ?} ?? ??? ??? ?php->a = tmp; ?? ??? ??? ?php->capacity = nwecapacity; ? //容易忘记 ?? ?}
?? ?php->a[php->size++] = x; ?//在最后插入。 ?? ?//插入之后堆的结构就可能不能保持了,需要向上调整,因为我们是在最后插入。 ?? ?Adjustup(php->a, php->size - 1); ?//第二个参数传的是从那个位置开始调整。 }
// 删除堆顶的数据。(最小/最大) void HeapPop(Heap* php) { ?? ?assert(php); ?? ?assert(php->size > 0); ?? ?swap(&php->a[0], &php->a[php->size - 1]); ?? ?php->size--; ?? ?//Adjustdown(php->a, php->size - 1, 0); ? size前面已经减过了,不需要在减了。 ?? ?Adjustdown(php->a, php->size, 0); } bool HeapEmpty(Heap* php) { ?? ?assert(php); ?? ?return php->size == 0; } size_t HeapSize(Heap* php) { ?? ?assert(php); ?? ?return php->size; } HPDataType HeapTop(Heap* php) { ?? ?assert(php); ?? ?assert(php->size > 0); ?? ?return php->a[0]; }
4,代码链接
data structure: 数据解构练习 - Gitee.com
二,堆排序的实现
本质其实是一种选择排序,排降序建小堆,排升序,建大堆。
选出一个数之后,在不破换结构的基础上把他放到该有的位置
上。
1.建堆
建堆 ? ?向上调整建堆 ?或 ?向下调整建堆。 ? 比较时间复杂度发现向下调整是更优秀的。所以我们用向下调整来建堆,向下调整算法,向上调整算法在堆的实现里面都是有的,就是那个Adjustup()和Adjustdowm()看函数名字因该就可以区分那个是那个了。
for (int i = (n - 2) / 2; i >=0; i--)
{
Adjustdown(a,n, i); //第二个参数是数据个数,限制了最多向下到哪里。
}
2,选择排序思想,只不过是用堆来选择。
//这里其实本质上是用来选择排序的思想,选出一个数,放到他该有的位置上。
size_t end = n - 1;
while (end > 0)
{
swap(&a[end], &a[0]);
Adjustdown(a, end, 0);
end--; //--之后代表最后一个元素的位置,没减之前代表需要调整的元素个数。
}
选好一个数之后就放到该有的位置上,然后调整剩下的,使之还是一个堆,用向下调整算法去调整。
|