堆排序
堆:基于完全二叉树的特殊数据结构
- 逻辑结构:
- 大顶堆
- 根节点的值必须大于他的所有的孩子节点,对于二叉树中的所有子树都满足这个规律
- 小顶堆
- 根节点的值必须小于他的所有的孩子节点,对于二叉树中的所有子树都满足这个规律
- 大顶堆
数据存放在一个堆上,任意的节点,都要满足小顶堆
数据放到堆上 insert 怎么插入 能够满足堆属性
最小值 找根节点 extract 调整 满足小顶堆
堆特点:没有顺序,很方便找最大/最小值 TOP10
=========================================================================
存储结构:
顺序存储 [i/2, i, 2i, 2i+1]
n个数,申请n+1个节点,从1号索引开始存数据
操作: 插入数据 删除数据。
- 插入数据思路:上移
- 在最后一个位置,插入新元素
- 找这个元素的父节点,若父节点的值大
- 那么就与父节点的值进行交换
- 重复二三步,直到没有父节点比本身大,就结束循环
- 删除数据思路:下移
- 把根节点的值备份,作为被提取的值,好做返回值
- 把最后一个元素放到根节点的位置上,然后有效长度减一,删除最后一个元素
- 将节点和左右孩子中最小的值进行比较
- 如果节点的值小于那个最小值,就不换,否则重复第三四步
//miniHeap.h
#ifndef SORT_MINIHEAP_H
#define SORT_MINIHEAP_H
/* 最小堆结构,使用完全二叉树来存储,使用顺序存储 */
#include "../sortHelper.h"
// 最小堆的结构
typedef struct {
keyType *data; // 用顺序存储动态保存堆的数据
int len; // 约束堆data域的长度
int capacity; // 最大容量
}MiniHeap;
// 产生n个元素的堆空间
MiniHeap *createMiniHeap(int n);
void releaseMiniHeap(MiniHeap *heap);
// 插入元素
void insertMiniHeap(MiniHeap *heap, keyType e);
// 提取元素
keyType extractMiniHeap(MiniHeap *heap);
#endif //SORT_MINIHEAP_H
//
// Created by ASUS on 2023/9/11.
//
#include "miniHeap.h"
#include <stdlib.h>
#include <string.h>
MiniHeap *createMiniHeap(int n)
{
MiniHeap *heap = (MiniHeap *) malloc(sizeof(MiniHeap));
if (heap == NULL) {
return NULL;
}
heap->data = (keyType *) malloc(sizeof(keyType) * (n + 1));
if (heap->data == NULL) {
return NULL;
}
memset(heap->data, 0, sizeof(keyType) * (n + 1));
heap->capacity = n;
heap->len = 0; // 每次插入元素先加1,再插入
return heap;
}
void releaseMiniHeap(MiniHeap *heap)
{
if (heap) {
if (heap->data) {
free(heap->data);
heap->data = NULL;
}
free(heap);
}
}
// ---------------- 二叉堆的插入操作 --------------------
static void shiftUp(MiniHeap *heap, int k)
{
keyType tmp;
while(k>1&&heap->data[k/2]>heap->data[k])
{
tmp=heap->data[k];
heap->data[k]=heap->data[k/2];
heap->data[k/2]=tmp;
k/=2;
}
}
/* 先在最后位置插入元素,然后通过上移操作,确定这个新元素的位置,保证每个根节点是子节点的最小值 */
void insertMiniHeap(MiniHeap *heap, keyType e)
{
if(heap->len+1>heap->capacity)
{
printf("已经满了,再也装不下了\n");
return;
}
heap->data[++heap->len]=e;
// 上移操作
shiftUp(heap,heap->len);
}
static void shiftDown(MiniHeap *heap, int k)
{
keyType tmp;
while(2*k<=heap->len)// 保证有左孩子
{
int index=2*k;
if(index + 1 <= heap->len&&heap->data[index]>heap->data[index+1])
{
index+=1;//如果有右孩子,右孩子和左孩子比一下,右小,那么假设要更新
}
if(heap->data[k]<heap->data[index])break;
// 发现当前值比左右孩子最小的值还要大
tmp=heap->data[k];
heap->data[k]=heap->data[index];
heap->data[index]=tmp;
k=index;
}
}
/* 取出堆顶元素,把堆中的最后一个元素放到堆顶,根据原则下沉 */
keyType extractMiniHeap(MiniHeap *heap)
{
if(heap->len<=0)
{
printf("没有啦,真的一点都没有啦\n");
return 0;
}
keyType ret=heap->data[1];
heap->data[1]=heap->data[heap->len];
heap->len--;
shiftDown(heap,1);
return ret;
}
//heapSort.h
#ifndef SORT_HEAPSORT_H
#define SORT_HEAPSORT_H
#include "miniHeap.h"
void miniHeapSort(SortTable *table);
#endif //SORT_HEAPSORT_H
#include "heapSort.h"
void miniHeapSort(SortTable *table)
{
MiniHeap *miniHeap = createMiniHeap(table->length);
for (int i = 0; i < table->length; ++i) {
insertMiniHeap(miniHeap, table->data[i].key);
}
for (int i = 0; i < table->length; ++i) {
table->data[i].key = extractMiniHeap(miniHeap);
}
releaseMiniHeap(miniHeap);
}
#include "miniHeap.h"
#include "heapSort.h"
void test01() {
int data[] = {9, 3, 7, 6, 5, 1, 10, 2};
int n = 20;
MiniHeap *miniHeap = createMiniHeap(n);
for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
insertMiniHeap(miniHeap, data[i]);
}
printf("array: ");
for (int i = 1; i <= miniHeap->len; ++i) {
printf("\t%d", miniHeap->data[i]);
}
printf("\nextra: ");
// for (int i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
// printf("\t%d", extractMiniHeap(miniHeap));
// }
keyType tmp = extractMiniHeap(miniHeap);
while (tmp) {
printf("\t%d", tmp);
tmp = extractMiniHeap(miniHeap);
}
releaseMiniHeap(miniHeap);
}
// 随机排序表的堆排序测试
void test02() {
int n = 1000000;
SortTable *table = generateRandomArray(n, 0, n);
testSort("HeapSort: ", miniHeapSort, table);
releaseSortTable(table);
}
int main() {
test02();
return 0;
}
如果想更加详细了解堆排序,不妨可以看看STL源码剖析中的heap算法。