热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

算法给小码农堆魂器铁血柔情

文章目录堆堆的概念及结构堆的性质堆的结构(这里实现大堆)堆的结构体堆初始化函数HeapInit堆销毁函数HeapDestroy堆打印函数HeapPrint向上调整函数AdjustU

文章目录

    • 堆的概念及结构
      • 堆的性质
      • 堆的结构(这里实现大堆)
        • 堆的结构体
        • 堆初始化函数HeapInit
        • 堆销毁函数HeapDestroy
        • 堆打印函数HeapPrint
        • 向上调整函数AdjustUp
        • 堆插入函数HeapPush
        • 判断堆是否为空函数HeapErmpy
        • 返回堆大小函数HeapSize
        • 交换函数Swap
        • 向下调整函数AdjustDown
        • 堆删除函数HeapPop
    • 代码
      • Heap.h
      • Heap.c
      • test.c

数据结构中的堆不同于操作系统中的堆(操作系统中的堆是用来存储动态内存的),数据结构中的堆是数据的存储方式。数据结构中的堆是完全二叉树

既然堆是完全二叉树的形式存储的那么就非常适合用数组的方式来表示

堆的概念及结构

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

大堆:树中一个树及子树中,任何一个父亲都大于等于孩子

小堆:树中一个树及子树中,任何一个父亲都小于等于孩子

堆的性质

堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。**

堆的结构(这里实现大堆)

《算法给小码农堆魂器--铁血柔情》

既然还是数组的结构的话就还是顺序表的处理方式,数组指针,size,capacity。虽然物理上我们是用顺序表的方式来表示,但是他实际上表示的数据是完全二叉树。

堆的结构体

typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;

堆初始化函数HeapInit

就是一个指向NULL的数组,size 和 capacity都为零

//堆初始化函数
void HeapInit(HP* hp)
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}

堆销毁函数HeapDestroy

由于数组是动态开辟的,所以用完后需要销毁的,不然会内存泄漏

//堆销毁函数
void HeapDestroy(HP* hp)
{
assert(hp);
free(hp->a);
hp->size = hp->capacity = 0;
}

堆打印函数HeapPrint

可以想象成一种快速调试,类似于单片机中的串口打印看数据收发情况

//堆打印函数
void HeapPrint(HP* hp)
{
int i = 0;
for (i = 0; i < hp->size; i++)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}

向上调整函数AdjustUp

为了不影响数据的存储形式(大堆还得是大堆),插入数据就不能破坏大堆的形式,我们需要把堆插入函数中的数据调整给剥离出来

我们可以看到插入的这个数据对其他的节点并没有什么影响,有影响的只是这个节点到根这条路径上的节点,如何解决对这条路径的影响呢,我们可以形象的看到仅仅是在这条路径上进行向上调整

通过parent = (child-1)/2 找到父亲节点,与之进行比较,然后父亲小就交换位置(大堆),然后交换后就在找上面的父亲节点,直到找到父亲大于孩子,就不交换了

《算法给小码农堆魂器--铁血柔情》

//向上调整函数
void AdjustUp(HPDataType* a, int child)
{
assert(a);
int parent = (child - 1) / 2;
while (child>0)
{
if (a[parent] < a[child])//父亲小于孩子就交换(大堆)
{
a[parent] = a[parent] ^ a[child];
a[child] = a[parent] ^ a[child];
a[parent] = a[parent] ^ a[child];
//交换好后重新称呼一下孩子与父亲
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}

堆插入函数HeapPush

《算法给小码农堆魂器--铁血柔情》

《算法给小码农堆魂器--铁血柔情》

《算法给小码农堆魂器--铁血柔情》

//堆插入函数(要保持原来形式,大堆还是大堆,小堆就还是小堆)
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
//判断扩容
if (hp->size == hp->capacity)
{
//容量给新的容量
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
//扩容
HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
//增容失败
if (!tmp)
{
printf("realloc fail\n");
exit(-1);
}
//增容成功
hp->a = tmp;
hp->capacity = newcapacity;
}
//放数据
hp->a[hp->size] = x;
hp->size++;
//实现大堆
//这个部分的向上调整其他地方也用的到就把他剥离出来
AdjustUp(hp->a, hp->size - 1);//child下标
}

判断堆是否为空函数HeapErmpy

//判断堆是否为空函数
bool HeapErmpy(HP* hp)
{
assert(hp);
return hp->size == 0;
}

返回堆大小函数HeapSize

//返回堆大小函数
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}

下面还会用到交换函数,上面也有那么我们不妨把他剥离出来封装一下,就不需要重复写了

交换函数Swap

//交换函数
void Swap(HPDataType* px, HPDataType* py)
{
*px = *px ^ *py;
*py = *px ^ *py;
*px = *px ^ *py;
}

向下调整函数AdjustDown

《算法给小码农堆魂器--铁血柔情》

//向下调整函数
void AdjustDown(HPDataType* a, int size, int parent)
{
assert(a);
//创建一个孩子变量,有两个孩子就在这个上加1就行
int child = parent * 2 + 1;
while (child< size)
{
//选大孩子
if (child + 1 < size && a[child] < a[child + 1])
{
child++;
}
//大的孩子还大于父亲就交换
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
}
}

堆删除函数HeapPop

《算法给小码农堆魂器--铁血柔情》

我们可以认为假想根和堆的最后一个元素交换后,把最后一个删除,然后再对堆进行操作,你会发现,我们没有破坏原来的整体结构

《算法给小码农堆魂器--铁血柔情》

//堆删除函数(删除的是堆顶数据也就是取最值)
//还有不可能一直删的所以我们需要一个判空函数
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapErmpy(hp));
//根和堆最后一个元素交换
Swap(&hp->a[0],&hp->a[hp->size-1]);
//把最后一个删除,就是我们想要删除的元素
hp->size--;
//向下调整
AdjustDown(hp->a,hp->size,0);
}

代码

Heap.h

#pragma once
#include
#include
#include
#include
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int size;
int capacity;
}HP;
//堆初始化函数
extern void HeapInit(HP* hp);
//堆销毁函数
extern void HeapDestroy(HP* hp);
//向上调整函数
extern void AdjustUp(HPDataType* a,int child);
//堆插入函数
extern void HeapPush(HP* hp,HPDataType x);
//堆打印函数
extern void HeapPrint(HP* hp);
//向下调整函数
extern void AdjustDown(HPDataType* a, int size, int parent);
//堆删除函数
extern void HeapPop(HP* hp);
//判断堆是否为空函数
extern bool HeapErmpy(HP* hp);
//交换函数
extern void Swap(HPDataType* px, HPDataType* py);
//返回堆大小函数
extern int HeapSize(HP* hp);

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
//堆初始化函数
void HeapInit(HP* hp)
{
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
//堆销毁函数
void HeapDestroy(HP* hp)
{
assert(hp);
free(hp->a);
hp->size = hp->capacity = 0;
}
//向上调整函数
void AdjustUp(HPDataType* a, int size, int child)
{
assert(a);
int parent = (child - 1) / 2;
while (child>0)
{
if (a[parent] {
Swap(&a[parent], &a[child]);
//交换好后重新称呼一下孩子与父亲
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
//堆插入函数(要保持原来形式,大堆还是大堆,小堆就还是小堆)
void HeapPush(HP* hp, HPDataType x)
{
assert(hp);
//判断扩容
if (hp->size == hp->capacity)
{
//容量给新的容量
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
//扩容
HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
//增容失败
if (!tmp)
{
printf("realloc fail\n");
exit(-1);
}
//增容成功
hp->a = tmp;
hp->capacity = newcapacity;
}
//放数据
hp->a[hp->size] = x;
hp->size++;
//实现大堆
//这个部分的向上调整其他地方也用的到就把他剥离出来
//向上调整一下堆的数据形式,使他还是大堆的形式
AdjustUp(hp->a, hp->size, hp->size - 1);
}
//堆打印函数
void HeapPrint(HP* hp)
{
assert(hp);
int i = 0;
for (i = 0; i size; i++)
{
printf("%d ", hp->a[i]);
}
printf("\n");
}
//判断堆是否为空函数
bool HeapErmpy(HP* hp)
{
assert(hp);
return hp->size == 0;
}
//返回堆大小函数
int HeapSize(HP* hp)
{
assert(hp);
return hp->size;
}
//交换函数
void Swap(HPDataType* px, HPDataType* py)
{
*px = *px ^ *py;
*py = *px ^ *py;
*px = *px ^ *py;
}
//向下调整函数
void AdjustDown(HPDataType* a, int size, int parent)
{
assert(a);
//创建一个孩子变量,有两个孩子就在这个上加1就行
int child = parent * 2 + 1;
while (child {
//选大孩子
if (child + 1 {
child++;
}
//大的孩子还大于父亲就交换
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆删除函数(删除的是堆顶数据也就是取最值)
//还有不可能一直删的所以我们需要一个判空函数
void HeapPop(HP* hp)
{
assert(hp);
assert(!HeapErmpy(hp));
//根和堆最后一个元素交换
Swap(&hp->a[0],&hp->a[hp->size-1]);
//把最后一个删除,就是我们想要删除的元素
hp->size--;
//向下调整
AdjustDown(hp->a,hp->size,0);
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
void test()
{
int a[] = { 70,56,30,25,15,75 };
HP hp;
HeapInit(&hp);
int i = 0;
for (i = 0; i {
HeapPush(&hp, a[i]);
}
HeapPrint(&hp);
HeapPop(&hp);
HeapPrint(&hp);
HeapDestroy(&hp);
}
int main()
{
test();
return 0;
}

推荐阅读
  • 此题有一个大坑id范围为1e9此题题意是按照同类按照价格大小从大到小输出,如果价格相等再按照id从小到大输出。​#includeusin ... [详细]
  • 题目描述输入整型数组和排序标识,对其元素按照升序或降序进行排序(一组测试用例可能会有多组数据)本题有多组输入,请使用whil ... [详细]
  • DFS基本概念步骤优缺点典型例题递推基本概念直接或者间接调用自身的算法称为递归算法一般数据n ... [详细]
  • Educational Codeforces Round 43 (Rated for Div. 2)
    EducationalCodeforcesRound43(RatedforDiv.2)https:codeforces.comcontest976A ... [详细]
  • P1144 最短路计数· BFS/dijkstra
    题解其实题目很简单不写了,这里总结一下从这道题目里学到的知识:当最短路的边权都是1时,dijkstraspfa就是BFS如果使用优先队列,内部结构是pair时 ... [详细]
  • Logistic回归主要针对输入的数据是多个,输出则是有限的数值型,多为2个分类。涉及到以下方面:1.输出yw0+w1*x1+w2*x2+..(x1,x2,是样本的 ... [详细]
  • LwIP系列内存管理(堆内存)详解
    一、目的小型嵌入式系统中的内存资源(SRAM)一般都比较有限,LwIP的运行平台一般都是资源受限的MCU。为了能够更加高效的运行ÿ ... [详细]
  • 题目链接Reference:https:www.cnblogs.comdiltheyp9757781.html首先容易想到的常规dp是,初始化dp(i,j)0dp(i,j)0dp( ... [详细]
  • C模板实现的单向链表,实现了链表的初始化创建,元素插入,元素链表末尾添加,元素删除,链表清空Lists.h# ... [详细]
  • 问题描述  编写一个程序,输入一个1000以内的正整数,然后把这个整数的每一位数字都分离出来,并逐一地显示。  输入格式:输 ... [详细]
  • #include#include#includeusingnamespacecv;usingname ... [详细]
  • [二分图]JZOJ 4612 游戏
    DescriptionInputOutputSampleInput44#****#****#*xxx#SampleOutput5DataConstraint分析非常眼熟࿰ ... [详细]
  • 【JVM技术专题】深入分析CG管理和原理查缺补漏「番外篇」
    前提概要本文主要针对HotspotVM中“CMSParNew”组合的一些使用场景进行总结。自Sun发布Java语言以来,开始使用GC技术来进行内存自动管理࿰ ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
author-avatar
多米音乐_34063629
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有