Article directory
Structural Analysis of Heaps
It should be noted that the heap is a data structure and has nothing to do with the heap area of the operating system.
The structure of the heap: the heap is a complete binary tree, continuous from left to right, suitable for array storage
Heap is a complete binary tree, divided into large heap and small heap
Large heap: In a tree, any parent node is greater than or equal to the child node, so the root node of the large heap is the largest
Small heap: In a tree, any parent node is less than or equal to the child node, so the root node of the small heap is the smallest
Implementation of the heap
For those who have known the sequence table, the implementation of the heap is very simple, using malloc dynamic memory allocation to open up space storage nodes. Because to satisfy the large heap or the small heap, the upward adjustment and downward adjustment algorithms need to be used. We directly understand in the following implementation. First, take the large heap as an example , and then only need to fine-tune the upward adjustment and downward adjustment to achieve Switching between large and small heaps
heap storage structure
typedef int HPDataType;
typedef struct Heap
{
HPDataType* p;
int size;//节点个数
int capacity;//空间容量
}HP;
heap initialization
//对象传给HeapInit函数初始化
int mian()
{
HP h;
HeapInit(&h);
}
void HeapInit(HP* pc)
{
assert(pc);
pc->capacity = pc->size = 0;
pc->p = NULL;
}
Heap insertion push
Building a heap: In order not to destroy the original structure of the heap, it is necessary to insert tails in sequence, and to satisfy the large heap, each insertion must compare the size with the parent node, and continuously adjust upward, and finally realize the large heap
int arr[6] = { 70, 56, 30, 60, 25, 40 };
adjust up
The inserted node is constantly compared with the parent node. If the child node is larger than the parent node, it will switch positions, and then compare it with the new parent node again. If it is smaller than the parent node, it will terminate.
//交换函数在下面的堆删除也能复用
void HeapSwap(HPDataType* p1, HPDataType* p2)
{
assert(p1 && p2);
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustUp(HPDataType* p, int child)
{
assert(p);
int parent = (child - 1) / 2;
while (child >0)
{
//大于父亲节点就调整
if (p[child] > p[parent])
{
HeapSwap(&p[child], &p[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
//插入节点也需要判断空间是否满了,满了就扩容
void HeapPush(HP* pc, HPDataType x)
{
assert(pc);
//扩容
if (pc->capacity == pc->size)
{
int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(pc->p, sizeof(HPDataType) * newcapacity);
if (tmp == NULL)
{
printf("realloc failed\n");
exit(-1);
}
pc->p = tmp;
pc->capacity = newcapacity;
}
pc->p[pc->size] = x;
pc->size++;
AdjustUp(pc->p, pc->size-1);
}
Heap is empty
When we need to delete nodes in the heap, we first need to determine whether the heap is empty and whether there are nodes. Just like the above heap insertion, we need to first determine whether the space is full.
bool HeapEmpty(HP* pc)
{
assert(pc);
return pc->size == 0;
}
heap delete pop
The deletion of the heap is the head deletion, but if the top element of the heap is deleted directly, it will destroy the original structure of the heap, so we can first exchange the elements at the top and the tail of the heap, then delete the tail, and then adjust it downward.
adjust down
Downward adjustment: The parent node is compared with the larger child node. If it is smaller than the child node, it will be adjusted downward. If the parent node is greater than or equal to the larger child node, it will be terminated.
Find out from the above figure:
The parent node is 0, the left child is 1=0*2+1
Right child: 2=0*2+2
The parent node is 1 Left child: 3=1*2+1
Right child: 4=1*2+2
At the same time, the left child +1 is the right child
So leftchild=parent*2+1
rightchild=leftchild+1
At the same time, paren=(child-1)/2, child: both left and right children are fine
The parent node is 0, the left child is 1=0*2+1
Right child: 2=0*2+2
The parent node is 1 Left child: 3=1*2+1
Right child: 4=1*2+2
At the same time, the left child +1 is the right child
So leftchild=parent*2+1
rightchild=leftchild+1
at the same time parent=(child-1)/2, child: left and right children are OK
AdjustDown(HPDataType* p, int n, int parent)
{
//左孩子
int child = parent * 2 + 1;
while (child < n)
{
//判断是否有右孩子且右孩子大于左孩子
if (child + 1 < n && p[child + 1] > p[child])
++child;
if (p[child] < p[parent])
{
HeapSwap(&p[child], &p[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapPop(HP* pc)
{
assert(pc && !HeapEmpty(pc));
HeapSwap(&pc->p[pc->size - 1], &pc->p[0]);
pc->size--;
AdjustDown(pc->p, pc->size, 0);
}
heap destruction
void HeapDestroy(HP* pc)
{
assert(pc && !HeapEmpty(pc));
//释放动态开辟的空间
free(pc->p);
pc->capacity = pc->size = 0;
}
operation result
A large heap formed by pushing the elements of the array in turn
A large pile formed at a time by pop
We have already implemented the large heap. If we want to replace it with a small heap, the parent node is always less than or equal to the child node.
1. In the upward adjustment, the parent node > the smaller child node is exchanged
2. In the downward adjustment, the parent node > the smaller child node is exchanged
time complexity analysis
The time complexity analysis of the heap, that is, the time complexity of the upward adjustment and downward adjustment algorithms, and their time complexity is related to the height of the tree.
Take downward adjustment as an example:
suppose the height of the tree is h:
Layer 1, 20 nodes, need to move down the h-1 layer
Layer 2, 2 1 nodes, need to move down h-2 layers
Layer 3, 2 2 nodes, need to move down h-3 layers
Layer 4, 2 3 nodes, need to move down h-4 layers
……
Layer h, 2 h-2 nodes, need to move down by h-4 layers
Then the total number of steps required to move the node is:
T(n)=20(h-1)+21(h-2)+22(h-3)+23(h-4)+2h-3*2+2h-2*1 1
2T(n)=21(h-1)+22(h-2)+23(h-3)+24*(h-4)+2h-2*2+2h-1*1 2
2-1 dislocation subtraction:
T(n)=1-h+21+22+23+24+……+2h-2+2h-1
T(n)=20+21+22+23+24+2h-2+2h-1-h
T(n)=2h-1-h
n=2h-1 h=log2(n+1)
T(n)=n-log 2 (n+1) is approximately equal to n
source code
Heap.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int HPDataType;
//实现大堆
typedef struct Heap
{
HPDataType* p;
int size;
int capacity;
}HP;
//堆初始化
void HeapInit(HP* pc);
//堆中元素交换
void HeapSwap(HPDataType* p1, HPDataType* p2);
//打印元素
void HeapPrint(HP* pc);
//堆插入
void HeapPush(HP* pc, HPDataType x);
//堆删除
void HeapPop(HP* pc);
//堆销毁
void HeapDestroy(HP* pc);
//堆判空
bool HeapEmpty(HP* pc);
//向上调整
void AdjustUp(HPDataType* p, int child);
//向下调整
AdjustDown(HPDataType* p, int n, int parent);
//获取堆顶元素
HPDataType HeapTop(HP* pc);
Heap.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
void HeapInit(HP* pc)
{
assert(pc);
pc->capacity = pc->size = 0;
pc->p = NULL;
}
void HeapPrint(HP* pc)
{
assert(pc);
for (int i = 0; i < pc->size; i++)
{
printf("%d ", pc->p[i]);
}
printf("\n");
}
void HeapSwap(HPDataType* p1, HPDataType* p2)
{
assert(p1 && p2);
HPDataType tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void AdjustUp(HPDataType* p, int child)
{
assert(p);
int parent = (child - 1) / 2;
while (child >0)
{
//大于父亲节点就调整
if (p[child] > p[parent])
{
HeapSwap(&p[child], &p[parent]);
child = parent;
parent = (child - 1) / 2;
}
else
break;
}
}
void HeapPush(HP* pc, HPDataType x)
{
assert(pc);
//扩容
if (pc->capacity == pc->size)
{
int newcapacity = pc->capacity == 0 ? 4 : pc->capacity * 2;
HPDataType* tmp = (HPDataType*)realloc(pc->p, sizeof(HPDataType) * newcapacity);
if (tmp == NULL)
{
printf("realloc failed\n");
exit(-1);
}
pc->p = tmp;
pc->capacity = newcapacity;
}
pc->p[pc->size] = x;
pc->size++;
AdjustUp(pc->p, pc->size-1);
}
bool HeapEmpty(HP* pc)
{
assert(pc);
return pc->size == 0;
}
AdjustDown(HPDataType* p, int n, int parent)
{
//左孩子
int child = parent * 2 + 1;
while (child < n)
{
//判断是否有右孩子且右孩子大于左孩子
if (child + 1 < n && p[child + 1] > p[child])
++child;
if (p[child] > p[parent])
{
HeapSwap(&p[child], &p[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
void HeapPop(HP* pc)
{
assert(pc && !HeapEmpty(pc));
HeapSwap(&pc->p[pc->size - 1], &pc->p[0]);
pc->size--;
AdjustDown(pc->p, pc->size, 0);
}
HPDataType HeapTop(HP* pc)
{
assert(pc && !HeapEmpty(pc));
return pc->p[0];
}
void HeapDestroy(HP* pc)
{
assert(pc && !HeapEmpty(pc));
free(pc->p);
pc->capacity = pc->size = 0;
}
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
void Heap()
{
int arr[6] = {
70, 56, 30, 60, 25, 40};
HP h;
HeapInit(&h);
//将数组的元素依次插入堆
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
HeapPush(&h, arr[i]);
}
HeapPrint(&h);
HeapPop(&h);
HeapPrint(&h);
HeapDestroy(&h);
}
int main()
{
Heap();
return 0;
}