当前位置: 首页 > news >正文

【数据结构】堆

二叉树的顺序结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。

现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,

需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
在这里插入图片描述

堆的概念及结构

如果有一个关键码的集合K = { k0,k1 ,k2 ,…,kn-1 },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:ki <=k2*i+1 且 ki <=k2*i+2 ( ki >=k2*i+1 且 ki >=k2*i+2 ) i = 0,1,2…,则称为小堆(或大堆)。

将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:
堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。
在这里插入图片描述

堆的实现

堆向下调整算法

现在我们给出一个数组,逻辑上看做一颗完全二叉树。

我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

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

在这里插入图片描述

//堆向上调整算法
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//while (parent >= 0)while(child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}
//堆向下调整算法
void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){// 假设法,选出左右孩子中小的那个孩子if (child+1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}

请注意:
父子存储位置的下标规律:

leftchild=parent*2+1;
rightchild=parent*2+2;
parent=(parent-1)/2;

堆的创建

下面我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。

根节点左右子树不是堆,我们怎么调整呢?
这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

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

在这里插入图片描述

void HPInitArray(HP* php, HPDataType* a, int n)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (php->a == NULL){perror("malloc fail");return;}memcpy(php->a, a, sizeof(HPDataType) * n);php->capacity = php->size = n;// 向上调整,建堆 O(N*logN)//for (int i = 1; i < php->size; i++)//{//	AdjustUp(php->a, i);//}// 向下调整,建堆 O(N)for (int i = (php->size-1 - 1)/2; i >= 0; i--){AdjustDown(php->a, php->size, i);}
}

建堆时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述

堆的插入

先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。
在这里插入图片描述

void HPPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size-1);
}

堆的删除

删除堆是删除堆顶的数据,将堆顶的数据跟最后一个数据交换,然后删除数组最后一个数据,再进行向下调整算法。

在这里插入图片描述

void HPPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}

堆的代码实现

Heap.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
#include<time.h>typedef int HPDataType;typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;void HPInit(HP* php);
void HPInitArray(HP* php, HPDataType* a, int n);void HPDestroy(HP* php);
// 插入后保持数据是堆
void HPPush(HP* php, HPDataType x);
HPDataType HPTop(HP* php);// 删除堆顶的数据
void HPPop(HP* php);bool HPEmpty(HP* php);void AdjustUp(HPDataType* a, int child);
void AdjustDown(HPDataType* a, int n, int parent);
void Swap(HPDataType* px, HPDataType* py);

Heap.c

#include"Heap.h"void HPInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HPInitArray(HP* php, HPDataType* a, int n)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (php->a == NULL){perror("malloc fail");return;}memcpy(php->a, a, sizeof(HPDataType) * n);php->capacity = php->size = n;// 向上调整,建堆 O(N*logN)//for (int i = 1; i < php->size; i++)//{//	AdjustUp(php->a, i);//}// 向下调整,建堆 O(N)for (int i = (php->size-1 - 1)/2; i >= 0; i--){AdjustDown(php->a, php->size, i);}
}void HPDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->capacity = 0;php->size = 0;
}void Swap(HPDataType* px, HPDataType* py)
{HPDataType tmp = *px;*px = *py;*py = tmp;
}void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//while (parent >= 0)while(child > 0){if (a[child] > a[parent]){Swap(&a[child], &a[parent]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}// 时间复杂度:
void HPPush(HP* php, HPDataType x)
{assert(php);if (php->size == php->capacity){size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");return;}php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;php->size++;AdjustUp(php->a, php->size-1);
}HPDataType HPTop(HP* php)
{assert(php);return php->a[0];
}void AdjustDown(HPDataType* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){// 假设法,选出左右孩子中小的那个孩子if (child+1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}// 时间复杂度:logN
void HPPop(HP* php)
{assert(php);assert(php->size > 0);Swap(&php->a[0], &php->a[php->size - 1]);php->size--;AdjustDown(php->a, php->size, 0);
}bool HPEmpty(HP* php)
{assert(php);return php->size == 0;
}

http://www.mrgr.cn/news/28731.html

相关文章:

  • 如何查看库函数的描述、定义以及所需要的头文件
  • 【CICD】CICD 持续集成与持续交付在测试中的应用
  • 开源竞争-利用kimi编程助手搭建小程序(11)
  • AGI自学分享,简单有用的理论与实践
  • 聊天服务器(4)CMake
  • AlphaFold3中文安装教程
  • 深入解释synchronized底层原理
  • QT 事件 Event 应用
  • 2010-2022 CSP-J/普及组T1-T4考点统计
  • C++:多态
  • 补题篇--codeforces
  • 论文笔记:交替单模态适应的多模态表征学习
  • python贪吃蛇游戏项目源码【免费】
  • 中秋的“超级月亮”在哪?来竹海幻境寻找心中的白月光
  • 基于SSM的在线家用电器销售系统
  • 这个时代唯一“不变“的又是{变}
  • @EnableScheduling 和 @Scheduled 实现定时任务的任务延期问题
  • 459. 重复的子字符串
  • Defining Constraints with ObjectProperties
  • 【C++】—— list 模拟实现
  • golang学习笔记19——golang做服务发现与注册的深度剖析
  • 信奥学习规划(CSP-J/S)
  • 南京信息工程大学《2020年+2021年817自动控制原理真题》 (完整版)
  • javascript中==和===的区别以及使用场景
  • Openal o1初探
  • nodejs 009: 使用nvm进行node版本管理(包括Could not retrieve的手动处理办法)