Complete binary tree – the concept and implementation of heap

Foreword

Heap (heap): It is the abbreviation of heap memory. The heap is dynamically allocated memory. The memory size is not fixed and will not be released automatically. The heap-the data structure is an unordered tree structure, and it also satisfies the key-value How to store key-value pairs.

1. The concept and structure of the heap

If there is a set of key codes K = { , , ,…, }, store all its elements in a one-dimensional array in the order of a complete binary tree, and satisfy: <= and <= ( >= and > = ) i = 0, 1, 2…, then it is called a small heap (or a large heap). The heap with the largest root node is called the largest heap or large root heap, and the heap with the smallest root node is called the smallest heap or small root heap.

Heap Properties:

  • The value of a node in the heap is always not greater than or not less than the value of its parent node;
  • The heap is always a complete binary tree.

2. Implementation of the heap

2.1 Heap adjustment algorithm

Now we give an array, which is logically regarded as a complete binary tree. We can adjust it into a small heap through the downward adjustment algorithm starting from the root node. The downward adjustment algorithm has a premise: the left and right subtrees must be a heap to be adjusted.

int array[] = {27,15,19,18,28,34,65,49,25,37};

Code:

//Downward adjustment algorithm
void AdjustDown(int* 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[child] < a[parent])
{<!-- -->
Swap( &a[child], &a[parent]);
parent = child;
child = parent*2 + 1;
}
else
{<!-- -->
break;
}

}
}

2.2 Heap Creation

Below we give an array, which logically can be regarded as a complete binary tree, but it is not a heap yet. Now we use an algorithm to build it into a heap. The left and right subtrees of the root node are not heaps, how do we adjust it? Here we start to adjust from the last subtree of the first non-leaf node to the tree of the root node, and then we can adjust it into a pile.

int a[] = {1,5,3,8,7,6};


Code:

typedef int HPDataType;
typedef struct Heap
{<!-- -->
HPDataType* a;
int size;
int capacity;
}HP;

Initialization:

//heap initialization
void HeapInit(HP* php)
{<!-- -->
assert(php);
php->a = NULL;
php->capacity = 0;
php->size = 0;
}

2.3 Heap building time complexity

Because the heap is a complete binary tree, and the full binary tree is also a complete binary tree, here we use a full binary tree to prove it for simplicity (the time complexity is originally an approximation, and a few more nodes will not affect the final result):

Therefore, the time complexity of building a heap is: O(N).

2.4 heap insertion

First insert a 10 to the end of the array, and then perform an upward adjustment algorithm until the heap is satisfied.

Code:

//upward adjustment algorithm
void AdjustUp(HPDataType* a, int child)
{<!-- -->
int parent = (child - 1) >> 1;
while (child > 0)
{<!-- -->
if (a[child] < a[parent])
{<!-- -->
Swap( &a[child], &a[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{<!-- -->
break;
}
}

}
//insert
void HeapPush(HP* php, HPDataType x)
{<!-- -->
assert(php);
if (php->capacity == php->size)
{<!-- -->
int newCapacity = (php->capacity == 0) ? 4 : 2 * php->capacity;
HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newCapacity);
if (tmp == NULL)
{<!-- -->
printf("realloc fail\
");
return;
}
php->a = tmp;
php->capacity = newCapacity;
}
\t
php->a[php->size] = x;
php->size++;

AdjustUp(php->a, php->size - 1);
}

2.5 Heap deletion

Deleting the heap is to delete the data at the top of the heap, replace the data at the top of the heap with the last data, then delete the last data in the array, and then perform the downward adjustment algorithm.

Code:

//Downward adjustment algorithm
void AdjustDown(int* 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[child] < a[parent])
{<!-- -->
Swap( &a[child], &a[parent]);
parent = child;
child = parent*2 + 1;
}
else
{<!-- -->
break;
}

}
}

// delete heap top data
void HeapPop(HP* php)
{
assert(php);
assert(!HeapEmpty(php));
Swap( &php->a[0], &php->a[php->size - 1]);
php->size--;

AdjustDown(php->a, php->size, 0);
}

3. Complete code implementation of the heap

Heap.h

#pragma once

#include 
#include 
#include 
#include 
#include 
typedef int HPDataType;
typedef struct Heap
{<!-- -->
HPDataType* a;
int size;
int capacity;
}HP;

void Swap(HPDataType* p1, HPDataType* p2);
void AdjustUp(HPDataType* a, int child);
void AdjustDown(int* a, int n, int parent);

void HeapInit(HP* php);
void HeapDestroy(HP* php);
void HeapPush(HP* php, HPDataType x);
//
void HeapPop(HP* php);
HPDataType HeapTop(HP* php);
bool HeapEmpty(HP* php);
int HeapSize(HP* php);

Heap.c

#include "Heap.h"

// swap two elements
void Swap(HPDataType* p1, HPDataType* p2)
{
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}

//Adjust the algorithm upwards
void AdjustUp(HPDataType* a, int child)
{
int parent = (child - 1) >> 1;
while (child > 0)
{
if (a[child] < a[parent])
{
Swap( &a[child], &a[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}

}


//Adjust the algorithm down
void AdjustDown(int* 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[child] < a[parent])
{
Swap( &a[child], &a[parent]);
parent = child;
child = parent*2 + 1;
}
else
{
break;
}

}
}


//heap initialization
void HeapInit(HP* php)
{<!-- -->
assert(php);
php->a = NULL;
php->capacity = 0;
php->size = 0;
}


//destroy
void HeapDestroy(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->size = php->capacity = 0;
free(php);
}

//insert
void HeapPush(HP* php, HPDataType x)
{
assert(php);
if (php->capacity == php->size)
{
int newCapacity = (php->capacity == 0) ? 4 : 2 * php->capacity;
HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * newCapacity);
if (tmp == NULL)
{
printf("realloc fail\
");
return;
}
php->a = tmp;
php->capacity = newCapacity;
}
\t
php->a[php->size] = x;
php->size++;

AdjustUp(php->a, php->size - 1);
}
// delete heap top data
void HeapPop(HP* php)
{
assert(php);
assert(!HeapEmpty(php));
Swap( &php->a[0], &php->a[php->size - 1]);
php->size--;

AdjustDown(php->a, php->size, 0);
}


//Get the top element of the heap
HPDataType HeapTop(HP*php)
{
assert(php);
assert(!HeapEmpty(php));
return php->a[0];
}

//judgment empty
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}

//Get the size of the heap
int HeapSize(HP* php)
{
assert(php);
return php->size;
}

Test.c

#include "Heap.h"

void Test()
{<!-- -->
HP hp;
HeapInit( &hp);
int a[] = {<!-- --> 65,100,70,32,50,60 };
for (int i = 0; i < sizeof(a) / sizeof(int); i ++ )
{<!-- -->
HeapPush( &hp, a[i]);
}

while (!HeapEmpty( &hp))
{<!-- -->
int top = HeapTop( &hp);
printf("%d\
", top);
HeapPop( &hp);
}

}



void HeapSort(int* a, int n)
{<!-- -->
//Ascending order, build a large pile
//Descending order, build a small heap
//Adjust the heap downward
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{<!-- -->
AdjustDown(a, n, i);
}

int end = n - 1;
while (end > 0)
{<!-- -->
Swap( &a[0], &a[end]);
//In adjustment, select the second smallest number
AdjustDown(a, end, 0);

end--;
}
}

void CreateData()
{<!-- -->
// create data
int n = 1000;
srand((unsigned int)time(NULL));
const char* file = "data.txt";
FILE* fin = fopen(file, "w");
if (fin == NULL)
{<!-- -->
printf("fopen fail");
return;
}

for (size_t i = 0; i < n; i ++ )
{<!-- -->
int x = rand() % 1000000;
fprintf(fin, "%d\
", x);
}
fclose(fin);
}
PrintTop(int k)
{<!-- -->
const char* file = "data.txt";
FILE* fout = fopen(file, "r");
if (fout == NULL)
{<!-- -->
printf("fopen fail");
return;
}

int* hipple = (int*)malloc(sizeof(int) * k);
if (hipple == NULL)
{<!-- -->
printf("malloc fail");
return;
}

for (size_t i = 0; i < k; i ++ )
{<!-- -->
fscanf(fout, "%d", &hipple[i]);
}

//Build a small heap
for (int i = (k - 1 - 1) / 2; i >= 0; i--)
{<!-- -->
AdjustDown(hipple, k, i);
}

int val = 0;
while (!feof(fout))
{<!-- -->
fscanf(fout, "%d", &val);
if (hipple[0] < val)
{<!-- -->
hipple[0] = val;
AdjustDown(hipple, k, 0);
}

}

for (size_t i = 0; i < k; i ++ )
{<!-- -->
printf("%d ", hipple[i]);
}
printf("\
");
}

int main()
{<!-- -->
//Test();
/*int a[] = { 1,4,5,7,8,3,5,9,2 };
HeapSort(a, sizeof(a) / sizeof(int));
for (int i = 0; i < sizeof(a) / sizeof(int); i ++ )
{
printf("%d ", a[i]);
}*/

//CreatData();
PrintTop(5);

return 0;
}

Summary

 Through the realization of the heap, the understanding of the structure of the heap has been deepened, and the practical application of the heap has been understood at the same time - 1. Heap sorting and 2. TOP-K problem