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

数据结构之堆的实现以及性质和应用

上篇文章(数据结构之堆和二叉树的简介-CSDN博客)介绍了什么是二叉树以及堆的概念,在这篇文章中我们将进一步实现堆及其应用

1. 实现顺序结构二叉树

⼀般堆使用顺序结构的数组来存储数据,堆是⼀种特殊的⼆叉树,具有⼆叉树的特性的同时,还具备其他的特性。

1.1 堆的概念与结构

如果有⼀个关键码的集合 ,把它的所有元素按完全⼆叉树的顺序存储方式存储,在⼀个⼀维数组中,并满足:
i = 0 1 2... ,则称为小堆(或大堆)。将根结点最大的堆叫做最⼤堆或⼤根堆,根结点最⼩的堆
叫做最小堆或小根堆。

💡 ⼆叉树性质

对于具有 n 个结点的完全⼆叉树,如果按照从上⾄下从左⾄右的数组顺序对所有结点从
0 开始编号,则对于序号为 i 的结点有:
1. i>0 i 位置结点的双亲序号: (i-1)/2 i=0 i 为根结点编号,⽆双亲结点
2. 2i+1<n ,左孩⼦序号: 2i+1 2i+1>=n 否则无左孩子
3. 2i+2<n ,右孩⼦序号: 2i+2 2i+2>=n 否则无右孩子

1.2 堆的实现及一些基本操作

我们还是定义三个文件

这是heap.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int hpdatatype;
//堆的结构
typedef struct heap
{hpdatatype* arr;int size;        //有效数据个数int capacity;    //容量
}hp;
void hpinit(hp* php);
void hpdestroy(hp* php);
void hppush(hp* php, hpdatatype x);   //插入数据
void hppop(hp* php);               //删除堆顶数据
bool hpempty(hp* php);            //判空
int hpsize(hp* php);              //求size
hpdatatype hptop(hp* php);        //获取堆顶元素
void swap(int* x, int* y);    
void adjustdown(hpdatatype* arr, int parent, int n);
void adjustup(hpdatatype* arr, int child);

这是heap.c文件

#include"heap.h"
void hpinit(hp* php)
{assert(php);php->arr = NULL;php->capacity = php->size = 0;
}
void hpdestroy(hp* php)
{if (php->arr){free(php->arr);}php->arr = NULL;php->capacity = php->size = 0;
}
void swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}
void adjustup(hpdatatype* arr, int child)
{int parent = (child - 1) / 2;while (child > 0){//> : 大堆//< : 小堆if (arr[child] < arr[parent]){swap(&arr[child], &arr[parent]);child = parent;parent = (child - 1) / 2;}else{break;}}
}
void adjustdown(hpdatatype* arr, int parent, int n)
{int child = parent * 2 + 1;while (child < n){//先找小的孩子if (child + 1 < n && arr[child] > arr[child + 1])   //要保证右孩子也没出界{child++;}//将小孩子与父节点比较if (arr[child] < arr[parent]){swap(&arr[child], &arr[parent]);    //此时值交换完了,下表还没变parent = child;child = parent * 2 + 1;}else{break;}}
}
void hppush(hp* php, hpdatatype x)
{assert(php);//既然要插入,就要判断容量够不够if (php->capacity == php->size){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;hpdatatype* tmp = (hpdatatype*)realloc(php->arr,sizeof(hpdatatype) * newcapacity);assert(tmp);php->arr = tmp;php->capacity = newcapacity;}php->arr[php->size] = x;   //先不管是大堆还是小堆,先把数插进去//向上调整adjustup(php->arr, php->size);                //不断的让子节点和父节点比较,php->size指的是刚插入数据的下标php->size++;
}
bool hpempty(hp* php)
{assert(php);return php->size == 0;
}
int hpsize(hp* php)
{assert(php);return php->size;
}
//删除堆顶元素
void hppop(hp* php)
{assert(!hpempty(php));//先将堆顶元素与最后一个元素调换,便于将其删去,之后重新调整堆的结构swap(&php->arr[0], &php->arr[php->size - 1]);php->size--;        //此时堆顶元素已经被删了,下一步就是按照大堆或者小堆的方式重新调整堆的结构adjustdown(php->arr,0, php->size);
}
//获取堆顶数据
hpdatatype hptop(hp* php)
{assert(!hpempty(php));return php->arr[0];
}

这是test.c文件

#include"heap.h"
void test()
{hp hp;hpinit(&hp);hppush(&hp, 1);hppush(&hp, 2);hppush(&hp, 3);hppush(&hp, 4);//while (!hpempty(&hp))//{//	printf("%d", hptop(&hp));//	hppop(&hp);//}hppop(&hp);      //此时会发生assertion报错,原因是hppop函数中的assert函数报错,我们断言的 是不为空,但是上述打印过程已经让我们的堆变为空了hppop(&hp);while (!hpempty(&hp)){printf("%d", hptop(&hp));hppop(&hp);}hpdestroy(&hp);
}
int main()
{test();return 0;
}

1.3数组的冒泡排序

这里仅展示关键代码,上述涉及的方法将不在赘述,以下内容均在test.c中实现

//时间复杂度O(N*2)
void bubblesort(int* arr, int n)
{for (int i= 0; i < n; i++){int exchange = 0;for (int j = 0; j < n - 1 - i; j++){if (arr[j] > arr[j + 1]){exchange = 1;swap(&arr[j], &arr[j + 1]);}}if (exchange == 0){break;}}
}
int main()
{进行冒泡排序(方法1)int arr1[] = { 17,20,10,13,19,15 };int n1 = sizeof(arr1) / sizeof(arr1[0]);bubblesort(arr1, n1);for (int i = 0; i < n1; i++){printf("%d ", arr1[i]);}printf("\n");//进行冒泡排序(方法2:利用堆的性质)int arr2[] = { 17,20,10,13,19,15 };int n2 = sizeof(arr2) / sizeof(arr2[0]);hp hp;hpinit(&hp);//调用push将数组中的数据建堆for (int i = 0; i < n2; i++){hppush(&hp, arr2[i]);}int i = 0;while (!hpempty(&hp)){arr2[i++] = hptop(&hp);hppop(&hp);}for (int i = 0; i < n2; i++){printf("%d ", arr2[i]);}hpdestroy(&hp);return 0;
}

但是,真正的堆排序不是这种写法,堆排序是借助堆的算法思想,而不能够直接使用堆的数据结构来辅助实现!

1.4堆排序

这里仅展示关键代码,上述涉及的方法将不在赘述,以下内容均在test.c中实现

void heapsort(int* arr, int n)
{//根据给定的arr来进行建堆//child:n-1     parent:(child-1)/2=(n-2)/2//向下调整算法建堆//数组传过来的时候,堆就已经建好了,只不过是乱七八糟的,我们需要将其调整为大堆或者小堆for (int i = (n - 1 - 1) / 2; i >= 0; i--)     //从根节点开始调整(对父节点下手){adjustdown(arr, i, n);}//堆排序int end = n - 1;while (end > 0){swap(&arr[0], &arr[end]);adjustdown(arr, 0, end);end--;}
}
int main()
{int arr3[] = { 17,20,10,13,19,15 };int n3 = sizeof(arr3) / sizeof(arr3[0]);heapsort(arr3, n3);for (int i = 0; i < n3; i++){printf("%d ", arr3[i]);}
return 0;
}

由此,我们得出结论:排升序,建大堆,因为要把最大的元素与最后一个元素交换

                                    排降序,建小堆,因为要把最小的元素与最后一个元素交换

注意:一开始建堆只是弄成了整体趋势由大到小的降序曲线,二次排序才是真正的排成了一个不               严谨(包括等于)的降序数组!

 1.5topk问题

找最大的前k个数据,建小堆,比堆顶数据,谁大谁入堆

找最小的前k个数据,建大堆,比堆顶数据,谁小谁入堆

#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
#include <time.h>
void CreateNDate() {// 造数据 int n = 100000;srand(time(0));const char* file = "data.txt";FILE* fin = fopen(file, "w");if (fin == NULL){perror("fopen error");return;}for (int i = 0; i < n; ++i){int x = (rand() + i) % 1000000;fprintf(fin, "%d\n", x);}fprintf(fin, "%d\n", 2000000001);fprintf(fin, "%d\n", 2002323001);fprintf(fin, "%d\n", 2002323201);fprintf(fin, "%d\n", 2011110001);fprintf(fin, "%d\n", 2022220001);fclose(fin);
}
void topk()
{int k = 0;printf("input k:");scanf_s("%d", &k);const char* file = "data.txt";FILE* fout = fopen(file, "r");if (fout == NULL){perror("fopen error");exit(1);}//找最大的前k个数,建小堆int* minheap = (int*)malloc(sizeof(int) * k);if (minheap == NULL){perror("malloc fail!");exit(2);}//读取文件中前k个数据建堆for (int i = 0; i < k; i++){fscanf(fout, "%d", &minheap[i]);}//建堆for (int i = (k - 1 - 1) / 2; i > 0; i--){adjustdown(minheap, i, k);}//遍历剩下的n-k个数据,跟堆顶比较,谁大谁入堆//调整堆int x = 0;while (fscanf(fout, "%d", &x) != EOF){if (x > minheap[0]){minheap[0] = x;       //先把这个数据插进去,等下在调整adjustdown(minheap, 0, k);}}for (int i = 0; i < k; i++){printf("%d ", minheap[i]);}fclose(fout);
}
int main()
{CreateNDate();topk();return 0;
}


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

相关文章:

  • 通过FDM升级Firepower
  • Linux云计算 |【第五阶段】ARCHITECTURE-DAY5
  • 【蓝桥杯选拔赛真题79】python字符串处理 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析
  • 【已解决】C# NPOI如何在Excel文本中增加下拉框
  • vue3报错:找不到模块“element-plus”或其相应的类型说明
  • fastjson/jackson对getter,setter和constructor的区分
  • 探寻闲鱼libsgmain加解密算法(3) ——libsgmainso-6.5.XX学习记录
  • 小白学视觉 | PE-YOLO:解决黑夜中的目标检测难点
  • 基于ESP8266的远程推力数据采集系统
  • 【Leecode】Leecode刷题之路第32天之最长有效括号
  • LeetCode 3180. 执行操作可获得的最大总奖励 I
  • 有没有两个不相等的对象有相同的 hashCode
  • 【jvm】什么是TLAB
  • 李沐读论文-启发与借鉴-3:Attention is all you need
  • 【Nas】X-DOC:在Mac OS X 中使用 WOL 命令唤醒局域网内 PVE 主机
  • 四、Hadoop 命令高级用法深度剖析
  • 基于SSM框架、传统文化学习系统的设计与实现
  • Lampiao靶机入侵实战
  • springboot多模块打包时出现Could not resolve dependencies for project
  • 构建负责任的人工智能:数据伦理与隐私保护
  • 牛客周赛 Round 64(博弈论、思维、构造、LCA、换根DP)
  • 信息咨询试题
  • nfs实验
  • Redis学习文档(常见面试题)
  • 基于SSM+小程序的垃圾分类管理系统(垃圾3)
  • P450催化的联芳基偶联反应-文献精读72