数据结构——二叉树(“堆”部分)

在讲之前,要弄明白树是什么,二叉树是什么,什么是满二叉树,什么是完全二叉树,但不是今天的重点,所以了解概念的部分留下一个链接自行了解(132条消息) 数据结构——树总结_小白苏同学在爬了的博客-CSDN博客_数据结构树的总结

一、堆的概念 

对于堆的概念需要有一下几点认识:

1.堆是一个完全二叉树

2.堆一般用顺序结构数组来存储

3.要分清堆的逻辑结构存储(物理)结构                                                               

1.堆是一个完全二叉树,形似如下

完全二叉树:除了最底层外每个节点又有两个子节点,最后一层从左到右的节点连续不间断。

2.堆一般用顺序结构数组来存储,在实际存储中其实就是一个数组,从顶到底部,从左到右一次存储。

 3.要分清堆的逻辑结构存储(物理)结构 

如上图(a)就是逻辑结构,是按照我们思路画出来的,实际并不存在,右侧的是实际存储的结构,在内存中存储的物理结构

在这里就要开始展开对于堆最重要的环节,逻辑结构与存储结构之间的转化。简单来说就是如何从子节点找到父节点,从父节点找到左右两个子节点

我们先给各个节点标号,从0开始,自左向右,自上到下。

我们会发现

父节点 = (子节点-1)/ 2 

左子节点 = 父节点*2+1

右子节点 = 父节点*2+2

我们再把标号和存储结构对比,是不是就发现可以对应上了♥

 我们学习堆的目的是运用这层结构关系,也就是上面提到的父子关系,迅速找出一堆数中的最大值或者最小值,相比于一个一个比的方式节省了大量时间,如何操作形成代码,下面分解

二、堆的实现

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;
// 堆的初始化
void HeapInit(Heap* hp);
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 向上调整
void AjustUp(HPDataType* a, int n);
// 向下调整
void AjustDown(HPDataType* a, int n);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
// 堆的打印
void HeapPrint(Heap* hp);

2.1堆的插入

插入我们要从数组的结尾起开始插入,但是不能保证这个数是否能满足的堆的性质所以我们要对这个数进行位置的调整,就有了堆的向上调整法,如下图

2.1.1堆的向上调整法

代码实现

// 向上调整
void AjustUp(HPDataType* a, int Child) {

	HPDataType father = (Child - 1) / 2;

	while (a[father] > a[Child] && Child > 0) {

		swap(&a[father],&a[Child]);
		Child = father;
		father = (Child - 1) / 2;

	}

}

2.2堆的删除

堆的删除一般删除堆顶,也就是最大或者最小的那个数,这时候最开始的节点是空的,已经不是一棵完成的二叉树,但是如果重新插入后面的所有数进入一个新的堆,更加的麻烦和复杂,这里给出的方法是把最后一个数和第一个数对调,然后将数组的大小-1,再进行堆的向下调整找出次大或者次小的数作为堆顶

2.2.1 堆的向下调整法

代码实现

void AjustDown(HPDataType* a, int n,int father) {
	assert(a);
	int child = 1;

	while (child<n) {
		if (a[child] >a[child + 1]) {
			child++;
		}
		if (a[child] < a[father]) {
			swap(&a[child], &a[father]);
			father = child;
			child = father * 2 + 1;
		}
		else {
			break;
		}
	}
}

 三、堆代码第一版

//Heap.h
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;
// 堆的初始化
void HeapInit(Heap* hp);
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 向上调整
void AjustUp(HPDataType* a, int n);
// 向下调整
void AjustDown(HPDataType* a, int n, int father);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
// 堆的打印
void HeapPrint(Heap* hp);

 

//Heap.c
#define _CRT_SECURE_NO_WARNINGS 
#include"Heap.h"

// 堆的初始化
void HeapInit(Heap* hp) {
	assert(hp);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
}
// 对调
void swap(HPDataType* a, HPDataType* b) {
	HPDataType temp = *a;
	*a = *b;
	*b = temp;
}
// 向上调整
void AjustUp(HPDataType* a, int Child) {

	HPDataType father = (Child - 1) / 2;

	while (a[father] > a[Child] && Child > 0) {

		swap(&a[father],&a[Child]);
		Child = father;
		father = (Child - 1) / 2;

	}

}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x) {
	assert(hp);
	if (hp->_capacity == hp->_size) {
		int newCapcity = hp->_capacity == 0 ? 4 : 2 * hp->_capacity;
		HPDataType* Tmp = realloc(hp->_a, sizeof(HPDataType)*newCapcity);
		if (Tmp == NULL) {
			perror("realloc fail");
			exit(-1);
		}
		hp->_a = Tmp;
		hp->_capacity = newCapcity;
	}
	hp->_a[hp->_size] = x;
	AjustUp(hp->_a, hp->_size);
	hp->_size++;

}
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);

// 堆的打印
void HeapPrint(Heap* hp) {
	assert(hp);
	for (int i = 0; i < hp->_size; i++) {
		printf("%d ", hp->_a[i]);
		
	}
}

// 堆的销毁
void HeapDestory(Heap* hp) {
	assert(hp);
	free(hp->_a);
	free(hp);
}

void AjustDown(HPDataType* a, int n,int father) {
	assert(a);
	int child = 1;

	while (child<n) {
		if (a[child] >a[child + 1]) {
			child++;
		}
		if (a[child] < a[father]) {
			swap(&a[child], &a[father]);
			father = child;
			child = father * 2 + 1;
		}
		else {
			break;
		}
	}
}

// 堆的删除
void HeapPop(Heap* hp) {
	assert(hp);
	assert(!HeapEmpty(hp));
	swap(&hp->_a[hp->_size - 1], &hp->_a[0]);
	hp->_size--;
	AjustDown(hp->_a,hp->_size,0);
}

// 取堆顶的数据
HPDataType HeapTop(Heap* hp) {
	assert(hp);
	assert(!HeapEmpty(!hp));
	return hp->_a[0];
}

// 堆的数据个数
int HeapSize(Heap* hp) {
	assert(hp);
	return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp) {
	assert(hp);
	return hp->_size == 0 ? 1 : 0;
}

 

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值