数据结构·堆·完全二叉树
目录
二叉树介绍
概念
特殊的二叉树
二叉树的性质
二叉树的存储方式
1.顺序存储
2.链式存储
堆实现二叉树
代码
Heap.h
Heap.c
以下是测试用例
Test.c
解释示例1
解释示例2
结束语
二叉树介绍
概念
特殊的二叉树
二叉树的性质
二叉树的存储方式
1.顺序存储
根据上图可知,顺序存储只适合存储完全二叉树,并不适用于非完全二叉树的存储
2.链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,先接触二叉链,后面接触的数据结构如红黑树等会用到三叉链。
堆实现二叉树
引入逻辑概念与物理概念
逻辑概念:即上面图所示的二叉树概念图
物理概念:以数组实现二叉树
也就是说树状图是我们为方便理解而设立的一个假想的概念图,实际上我们操作的还是一个数组,利用下标来实现数据的跳跃访问来帮助实现一些问题
代码
Heap.h
#pragma once#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>typedef int HPDataType;
typedef struct Heap
{HPDataType* a;int size;int capacity;
}HP;//打印
void HeapPrint(HP* php);//初始化
void HeapInit(HP* php);//销毁
void HeapDestroy(HP* php);//插入 -- 根据堆的特性,只会尾插,但是插入x继续保持堆形态
void HeapPush(HP* php , HPDataType x);//出堆 -- 删除堆顶的元素
void HeapPop(HP* php);//调整顺序
void Swap(HPDataType* p1, HPDataType* p2);//向上调整元素 -- 保持堆的形态
void AdjustUp(HPDataType* a, int child);//向下调整 -- 从头开始调
void AdjustDown(HPDataType* a, int n, int parent);//看头 -- 返回堆顶的元素
HPDataType HeapTop(HP* php);//判断是否为空
bool HeapEmpty(HP* php);//返回元素个数
int HeapSize(HP* php);
Heap.c
#include "Heap.h"//打印
void HeapPrint(HP* php)
{for (int i = 0; i < php->size; ++i){printf("%d ", php->a[i]);}printf("\n");
}//初始化
void HeapInit(HP* php)
{assert(php); //不能为空php->a = NULL;php->size = php->capacity = 0;//这里也可以先扩容,也可以在后面扩容
}//销毁
void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}//调整顺序
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//向上调整元素 -- 保持堆的形态
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2; //计算方法,无论是奇数的还是偶数的都可以求出父节点while (child > 0) //当孩子等于0的时候就停下{if (a[child] < a[parent]) //这里大于还是小于可以控制是大堆还是小堆,这里是在建小堆{//传地址过去Swap(&a[child], &a[parent]);//更新父节点child = parent;parent = (child - 1) / 2;}else{break; //当满足堆的性质时就跳出循环}}
}//插入 -- 根据堆的特性,只会尾插,但是插入x继续保持堆形态
void HeapPush(HP* php, HPDataType x)
{assert(php);//先判断扩容if (php->size == php->capacity){int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * newCapacity);if (tmp == NULL){perror("realloc fail");exit(-1);}php->a = tmp;php->capacity = newCapacity;}//元素放到最后php->a[php->size] = x;php->size++;//调整顺序 -- 传最后一个元素过去,注意要减一,因为前面++了AdjustUp(php->a, php->size - 1);}//向下调整 -- 从头开始调
void AdjustDown(HPDataType* a, int n, int parent)
{//假设左边小int minChild = parent * 2 + 1;while (minChild < n) //防止越界{if (minChild + 1 < n && a[minChild + 1] < a[minChild]){minChild++;}if (a[minChild] < a[parent]){Swap(&a[minChild], &a[parent]);parent = minChild; //minChild = parent * 2 + 1; //计算左孩子}else{break;}}}//出堆 -- 删除堆顶的元素
//时间复杂度: O(logN)
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);}//看头 -- 返回堆顶的元素
HPDataType HeapTop(HP* php)
{assert(php);assert(!HeapEmpty(php));return php->a[0];}//判断是否为空
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}//返回元素个数
int HeapSize(HP* php)
{assert(php);return php->size;
}
以下是测试用例
Test.c
#include "Heap.h"//测试一
//int main()
//{
// int a[] = { 15,18,19,25,28,34,65,49,27,37 };
// int a[] = { 65,100,70,32,50,60 };
// HP hp;
// //先创立一个再初始化
// HeapInit(&hp);
//
// for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
// {
// HeapPush(&hp, a[i]);
// }
//
// //HeapPush(&hp, 10);
// //HeapPrint(&hp);
//
// //HeapPop(&hp);
// //HeapPrint(&hp);
//
// //HeapPop(&hp);
// //HeapPrint(&hp);
//
// while (!HeapEmpty(&hp))
// {
// printf("%d ", HeapTop(&hp));
// HeapPop(&hp);
// }
//
// return 0;
//}//测试二
void HeapSort(int* a, int n)
{//建堆 -- 向上调整建堆 -- 时间复杂度: -- O(N*logN)//for (int i = 1; i < n; i++)//{// AdjustUp(a, i);//}//大思路:选择排序,依次选数,从后往前排//升序 -- 建大堆//降序 -- 建小堆//建堆 -- 向下调整建堆 -- 时间复杂度更简单 -- 解释示例1//建堆 -- 向下调整建堆 -- 时间复杂度: -- O(N)for (int i =(n-1-1)/2; i >= 0; --i){AdjustDown(a, n, i);}//选数int i = 1; //只需要选n-1个数,最后留下的自然是最大或最小的数while (i < n){Swap(&a[0], &a[n - i]); //把第一个数与最后一个数交换//向下调整建堆AdjustDown(a, n - i, 0);++i;}}//int main()
//{
// //int a[] = { 65,100,70,32,50,60 };
//
// int a[] = { 15,1,19,25,8,34,65,4,27,7 };
// HeapSort(a, sizeof(a) / sizeof(int));
//
// for (size_t i = 0; i < sizeof(a)/sizeof(int); ++i)
// {
// printf("%d ", a[i]);
// }
// printf("\n");
//
// return 0;
//}//TOP - K问题 -- 解释示例2
//TOP - K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
//比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
//对于Top - K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能
// 数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
// 1. 用数据集合中前K个元素来建堆
// 前k个最大的元素,则建小堆
// 前k个最小的元素,则建大堆
// 2. 用剩余的N - K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
// 比特就业课
// 将剩余N - K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素//以文件的方式进行堆排序选数
//测试用例三void CreateDataFile(const char* filename, int N)
{FILE* fin = fopen(filename, "w"); //以写文件的方式打开if (fin == NULL){perror("fopen fail");return;}srand(time(NULL)); //给定时间种子让其随机生成数据for (int i = 0; i < N ; ++i){fprintf(fin, "%d\n", rand()%1000000); //%的原因是为了测试该项目的正确性,下面可以手动在文件里添加超过一百万的数据,让其测试,看是否可以选出来}fclose(fin);
}void PrintTopK(const char* filename, int k)
{assert(filename);FILE* fout = fopen(filename, "r");if (fout == NULL){perror("fopen fail");return;}int* minHeap = (int*)malloc(sizeof(int) * k);if (minHeap == NULL){perror("minHeap fail");return;}//如何读取前k个数据for (int i = 0; i < k; ++i){fscanf(fout, "%d", &minHeap[i]); //默认空格或者换行键为改数据的终止}//建k个数的小堆for (int j = (k-2)/2; j >= 0; --j){AdjustDown(minHeap, k, j);}//继续读取后N-K个数据int val = 0;while (fscanf(fout , "%d", &val) != EOF){if (val > minHeap[0]){minHeap[0] = val; //直接赋值AdjustDown(minHeap, k, 0);}}for (int i = 0; i < k; ++i){printf("%d ", minHeap[i]);}free(minHeap);fclose(fout);
}int main()
{const char* filename = "Data.txt";int N = 10000; int k = 10;//CreateDataFile(filename, N);//随机创立一些数据PrintTopK(filename, k);return 0;
}
解释示例1
调整次数 = 每一层节点个数 * 这一层最坏向下调整次数
使用向下调整建堆
第1层,2^0个节点,需要向下移动h-1层
第2层,2^1个节点,需要向下移动h-2层
第3层,2^2个节点,需要向下移动h-3层
第4层,2^3个节点,需要向下移动h-4层
……
第h-1层,2^(h-2)个节点,需要向下移动1层
第h层,2^(h-1)个节点,不需要向下移动
向上调整建堆
第1层,2^0个节点,不需要向下移动
第2层,2^1个节点,需要向下移动1次
第3层,2^2个节点,需要向下移动2次
第4层,2^3个节点,需要向下移动3次
……
第h-1层,2^(h-2)个节点,需要向下移动h-2次
第h层,2^(h-1)个节点,需要向下移动h-1次
而且根据二叉树的性质,最后一层一定是占最大节点数的大概有一半,综上所述使用向下调整是最优解
解释示例2
N个数,找k个最大的
1、排序 -- O(N*logN)
2、堆选数
a、建大堆 -- 选K次即可(Pop k次)-- 时间复杂度:O(N+logN*K)
一般而言:K较小,而当N很大的时候就不行了,比如:N = 100亿 K = 100,那么a方法就不行了 -- 空间浪费并且需要注意的是在堆实现中,内存是存不下这么多的数据 例:100亿个整数大约就是40G了,这对于内存来说是一个很大的空间
b、 建小堆
1、用前K个数,建K个数的小堆
2、依次遍历后续N-K个数,比堆顶的数据打,就替换堆顶的数据,向下调整进堆,最后堆里面的数据就是最大的前K个了
时间复杂度:K + logK*(N-K) ≈ O(N) 空间复杂度:O(K)
结束语
上穷碧落下黄泉,两处茫茫皆不见。
唐·白居易 《长恨歌》
数据结构·堆·完全二叉树相关推荐
- 数据结构——堆(C++)
数据结构--堆 文章目录 数据结构--堆 堆与堆排序 二叉树 满二叉树 完全二叉树 二叉堆 堆的存储 堆的插入删除 具体的实现 堆排序 堆与堆排序 堆(heap)分为二叉堆.二项式堆.斐波那契堆,堆是 ...
- 数据结构-堆(Heap)
数据结构-堆(Heap) 我认识的堆: 1.建立在完全二叉树的基础上 2.排序算法的一种,也是稳定效率最高的一种 3.可用于实现STL中的优先队列(priority_queue) 优先队列:一种特殊 ...
- java堆 数据结构 堆_Java中的紧凑堆外结构/组合
java堆 数据结构 堆 在上一篇文章中,我详细介绍了代码对主内存的访问方式的含义. 从那时起,我就在Java中可以做什么以实现更可预测的内存布局提出了很多疑问. 有些模式可以使用数组支持的结构来应用 ...
- 数据结构-堆 Java实现
数据结构-堆 Java实现. 实现堆自动增长 1 /** 2 * 数据结构-堆. 自动增长 3 * 4 * @author caiyao 5 */ 6 public class Heap<T e ...
- java堆 数据结构 堆_快速堆数据结构
java堆 数据结构 堆 In this tutorial, we'll be discussing and implementing Heap data structures in Swift. 在 ...
- 数据结构——堆(转载)
堆常用来实现优先队列,在这种队列中,待删除的元素为优先级最高(最低)的那个.在任何时候,任意优先元素都是可以插入到队列中去的,是计算机科学中一类特殊的数据结构的统称 一.堆的定义 最大(最小)堆是一棵 ...
- 数据结构-堆(最大堆)
最大堆 实质是一棵完全二叉树 每个根结点元素的值都比左右儿子的大 每次都是取出堆顶元素(可以说是优先队列) 代码 参考自浙大数据结构 #include <iostream> #includ ...
- 经典数据结构——堆的实现
一.完全二叉树 堆是一种完全二叉树,什么是完全二叉树? 简单的说,一棵满二叉树表示的是所有节点全部饱和,最后一层全部占满: 而完全二叉树指的是满二叉树的最后一层,所有叶子节点都从左往顺序排满: 完全二 ...
- Java数据结构—堆(Heap)
堆是一种基于完全二叉树的数据结构,其中每个节点都满足堆特性:父节点的值总小于或等于(大于或等于)其子节点的值,这被称为小根堆(大根堆). 在Java中,可以使用PriorityQueue类来实现堆,它 ...
最新文章
- ubuntu eclipse CDT 问题
- acm算法模板(1)
- HihoCoder - 1591 锦标赛(最大费用最大流)
- 自定义repeater带分页功能的DataGrid(仿PetShop)
- CF5E-Bindian Signalizing【单调栈】
- python储存_python数据储存
- .NET4.0 之 Dynamic VS Refle“.NET研究”ction 效率
- NA-NP-IE系列实验28:HDLC 和PPP 封装
- Mac|技巧:Mac电脑如何使用ping命令呢?
- 我的世界Java版最大村庄_《我的世界》MC中最大的村庄种子,PC和PE都可以用
- 基于Java保险员工管理系统的设计与实现
- 一个简单的dw网页制作作业,学生个人html静态网页制作成品代码——怪盗基德动漫主题网页成品(15页)
- 日志收集之--将Kafka数据导入elasticsearch
- 前端JavaScript DOM BOM 自学复盘 D1(DOM-获取DOM元素、修改HTML标签/表单/css样式属性、定时器-间歇函数)
- 英特尔 超核芯显卡 620mac_显卡性能翻倍,AI能力加持:英特尔发布10代酷睿处理器...
- 达梦8,关于参数CTAB_SEL_WITH_CONS的验证
- python循环语句打印三角形_python循环输出三角形图案的例子
- 西门子采用三井化学的UNISTOLE™作为3D打印医用级面罩的涂层剂
- 【整理】SIMD、MMX、SSE、AVX、3D Now!、neon——指令集大全
- 深入编程之QQ盗号核心代码