記事ディレクトリ
パイル
完全なバイナリ ツリーが順次リストとして格納される場合、それはヒープと呼ばれます。
- ヒープは常に完全なバイナリ ツリーです
- ヒープのノードの値は常にその親ノードの値より大きくない (大きなヒープ) か、それより小さくない (小さなヒープ)
上位 k 個の問題やヒープ ソートの解決に使用できます
以下にヒープの挿入と削除を中心に実装するヒープの機能を示します。
//堆的构建
void HeapInit(Heap* hp);
//堆的销毁
void HeapDestroy(Heap* hp);
//堆的插入
void HeapPush(Heap* hp, HPDataType x);
//堆的删除
void HeapPop(Heap* hp);
//取堆顶的数据
HPDataType HeapTop(Heap* hp);
//堆的数据个数
int HeapSize(Heap* hp);
//堆的判空
bool HeapEmpty(Heap* hp);
2. 実装アイデア
次のパートの図はすべて論理構造に基づいています。!!
ここに構築されているのは小さな山です。
1. 構造の定義
ヒープはシーケンス テーブルを使用して完全なバイナリ ツリーを格納するため、ヒープの構造はシーケンス テーブルの構造と同じです。
動的に開かれる空間へのポインタ(データ)、空間のサイズ(容量)を記録する変数、空間内の有効なデータを記録する変数(サイズ)。
typedef int HPDataType;
typedef struct Heap
{
HPDataType* data;
int capacity;
int size;
}Heap;
2. ヒープ構築(HeapInit)
スペースの一部を割り当て、そのアドレスを記録するためにデータを使用し、現時点でスペースのサイズを記録するために容量を使用し、サイズを 0 に設定します (現時点ではスペースに有効なデータがありません)。
//堆的构建
#define SIZE 4
void HeapInit(Heap* hp)
{
assert(hp);
hp->data = (HPDataType*)malloc(sizeof(HPDataType) * SIZE);
if (hp == NULL)
{
perror("mallo: ");
exit(-1);
}
hp->capacity = SIZE;
hp->size = 0;
}
3. ヒープ破壊(HeapDestroy)
動的に開いたスペースを解放し、容量とサイズを0に設定します(このとき、スペースのサイズは0です)
//堆的销毁
void HeapDestroy(Heap* hp)
{
assert(hp);
free(hp->data);
hp->data = NULL;
hp->capacity = hp->size = 0;
}
4. ヒープ挿入(HeapPush)
ヒープの最後 (最後の子ノードの後) にデータを挿入し、その親ノードと比較します。ノードが親ノードより小さい場合 (ここでは小さなヒープです)、値を交換します。ノードがヒープの最上位になるか、その親ノードがこのノードより小さくなるまで、2 つのノードのうちの 1 つを繰り返します。
- ノードの添字が i であるとすると、その親ノードの添字は ( i - 1 ) / 2 となります。
//交换
void swap(HPDataType* a, HPDataType* b)
{
HPDataType tmp = *a;
*a = *b;
*b = tmp;
}
//向上调整 假设该节点是 i,父节点是 (i - 1) / 2
void AdjustUp(HPDataType* data, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (data[child] < data[parent])
{
swap(&data[child], &data[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp);
//检查容量
if (hp->capacity == hp->size)
{
HPDataType* tmp = (HPDataType*)realloc(hp->data ,sizeof(HPDataType) * (hp->capacity * 2));
if (tmp == NULL)
{
perror("realloc:");
exit(-1);
}
hp->data = tmp;
hp->capacity *= 2;
}
hp->data[hp->size] = x;
hp->size++;
//向上调整 传入数组和出入数据的下标
//此处是小堆
AdjustUp(hp->data, hp->size - 1);
}
5. ヒープ削除(HeapPop)
ヒープ削除とは、ヒープの先頭データを削除することです。
ヒープの先頭のデータとヒープの末尾のデータを交換し、サイズを 1 つ減らしてから、新しい先頭のデータと左右の子ノードの最小値を比較します。先頭のデータが左右の子ノードの最小値より大きい場合、データが交換され、新しい左右の子ノードの最小値が
比較されます。データが左右の子の最小値未満になるまで、またはデータが有効なデータ範囲を超えるまで。
- ノードの添字が i であるとします。その左の子ノードの添字: i * 2 + 1、その右の子の添字: i * 2 + 2
- ヒープの先頭にあるデータを削除し、そのデータに移動してヒープの先頭にあるデータを上書きすることはできません。データを移動すると、兄弟ノードが親子ノードになる可能性があり、兄弟ノード間のサイズ関係が保証されず、ヒープ構造が破壊される可能性があります(ここでは小さいヒープ構造が破壊されます)。
//交换
void swap(HPDataType* a, HPDataType* b)
{
HPDataType tmp = *a;
*a = *b;
*b = tmp;
}
//向下调整,假设该节点是 i, 右孩子节点是 2 * i + 1,左孩子节点是 2 * i + 2
void AdjustDown(HPDataType* data, int parent, int size)
{
int child = parent * 2 + 1;
while (parent < size)
{
//防止越界 找左右孩子中最小的
if (child + 1 < size && data[child] > data[child + 1])
{
child++;
}
if (child < size && data[parent] > data[child])
{
swap(&data[parent], &data[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆的删除 首元素 与 尾元素交换,新的堆顶在向下调整
void HeapPop(Heap* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
hp->data[0] = hp->data[hp->size - 1];
hp->size--;
//向下调整
AdjustDown(hp->data, 0, hp->size);
}
6. ヒープの先頭にあるデータを取得します (HeapTop)
配列空間の添字を 0 として読み取るだけです。
//取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
assert(hp);
return hp->data[0];
}
7. ヒープ内のデータ数 (HeapSize)
ヒープ構造内のサイズは、その時点でヒープ内に有効なデータの数を示します。サイズにアクセスするだけです。
//堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->size;
}
8. ヒープ空判定(HeapEmpty)
size はヒープ内の有効なデータの数を示します。size == 0 の場合、ヒープが空であることを意味します。
//堆的判空
bool HeapEmpty(Heap* hp)
{
assert(hp);
return hp->size == 0;
}
3. コードの実装
//Heap.c 文件
#include "Heap.h"
//堆的构建
void HeapInit(Heap* hp)
{
assert(hp);
hp->data = (HPDataType*)malloc(sizeof(HPDataType) * SIZE);
if (hp == NULL)
{
perror("mallo: ");
exit(-1);
}
hp->capacity = SIZE;
hp->size = 0;
}
//堆的销毁
void HeapDestroy(Heap* hp)
{
assert(hp);
free(hp->data);
hp->data = NULL;
hp->capacity = hp->size = 0;
}
//交换
void swap(HPDataType* a, HPDataType* b)
{
HPDataType tmp = *a;
*a = *b;
*b = tmp;
}
//向上调整 假设该节点是 i,父节点是 (i - 1) / 2
void AdjustUp(HPDataType* data, int child)
{
int parent = (child - 1) / 2;
while (child > 0)
{
if (data[child] < data[parent])
{
swap(&data[child], &data[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
assert(hp);
//检查容量
if (hp->capacity == hp->size)
{
HPDataType* tmp = (HPDataType*)realloc(hp->data ,sizeof(HPDataType) * (hp->capacity * 2));
if (tmp == NULL)
{
perror("realloc:");
exit(-1);
}
hp->data = tmp;
hp->capacity *= 2;
}
hp->data[hp->size] = x;
hp->size++;
//向上调整 传入数组和出入数据的下标
//此处是小堆
AdjustUp(hp->data, hp->size - 1);
}
//向下调整,假设该节点是 i, 右孩子节点是 2 * i + 1,左孩子节点是 2 * i + 2
void AdjustDown(HPDataType* data, int parent, int size)
{
int child = parent * 2 + 1;
while (parent < size)
{
//防止越界 找左右孩子中最小的
if (child + 1 < size && data[child] > data[child + 1])
{
child++;
}
if (child < size && data[parent] > data[child])
{
swap(&data[parent], &data[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆的删除 首元素 与 尾元素交换,新的堆顶在向下调整
void HeapPop(Heap* hp)
{
assert(hp);
assert(!HeapEmpty(hp));
hp->data[0] = hp->data[hp->size - 1];
hp->size--;
//向下调整
AdjustDown(hp->data, 0, hp->size);
}
//取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
assert(hp);
return hp->data[0];
}
//堆的数据个数
int HeapSize(Heap* hp)
{
assert(hp);
return hp->size;
}
//堆的判空
bool HeapEmpty(Heap* hp)
{
assert(hp);
return hp->size == 0;
}
//Heap.h 文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#define SIZE 4
typedef int HPDataType;
typedef struct Heap
{
HPDataType* data;
int capacity;
int size;
}Heap;
//堆的构建
void HeapInit(Heap* hp);
//堆的销毁
void HeapDestroy(Heap* hp);
//堆的插入
void HeapPush(Heap* hp, HPDataType x);
//堆的删除
void HeapPop(Heap* hp);
//取堆顶的数据
HPDataType HeapTop(Heap* hp);
//堆的数据个数
int HeapSize(Heap* hp);
//堆的判空
bool HeapEmpty(Heap* hp);
要約する
上記は私によるヒープの実装です。!!