IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【学习笔记】堆(c语言) -> 正文阅读

[数据结构与算法]【学习笔记】堆(c语言)

1.堆的概念

堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。

我们抛去表面看本质:堆就是一种特殊的数组,但在想象中是二叉树的结构,且堆中某个结点的值总是不大于或不小于其父结点的值。

可以看到:在实际数组中,数据看似排列得毫无规律,但在想象中其实它每个结点数据都不大于其父结点的数据。

2.堆的分类

2.1大根堆

大根堆:堆中某个结点的值总是不大于其父结点的值(本文主要以大根堆为例讲解)

2.2小根堆

小根堆:堆中某个结点的值总是不小于其父结点的值


3.堆的性质

堆中某个结点的值总是不大于或不小于其父结点的值。

堆总是一棵完全二叉树。


4.将数组建成堆

前面说过堆就是一种特殊的数组,那我们想要将数组建成堆就必须做一些特殊处理。

4.1向下调整算法(这里以大堆为例)

先来考虑这样一个问题:当根的左右子树已经是堆,那要怎样才能将整个数组建成堆呢?这就需要用到向下调整算法了。

向下调整算法:在左右子树已经是堆的情况下,找出当前结点的两个子结点中较大的那个,并用当前结点与较大子节点比较,若当前结点比子结点小则交换,持续对当前结点重复以上操作,知道当前结点不再比子结点小。

此时结点‘27’的左右子树都为大堆,对结点‘27’进行向下调整,比较结点‘27’的子节点,发现右子节点‘65’大,将结点‘27’与结点‘65’交换:

此时结点‘27’的左右子树都为大堆,对结点‘27’进行向下调整,比较结点‘27’的子节点,发现左子节点‘34’大,将结点‘34’与结点‘27’交换:

?

这样就完成了,在左右子树已经是大堆的前提下,使得整个数组成为了大堆:

?

4.2建堆

前面根据向下调整算法有一个前提就是,左右子树必须是堆,那如何将左右子树变成堆呢?

建堆:从最后一个结点的父结点往前使用向下调整算法。

有这样一个数组:arr[] = { 27, 15, 19, 18, 28, 34, 65, 49,?25, 37}

先找到最后一个结点‘37’父结点‘28’,进行向下调整:

往前找到结点‘18’进行向下调整:

往前找到结点‘19’进行向下调整:

?

往前找到结点‘15’进行向下调整,由与是从后向前调整,使得整个结点‘15’的左右子树均为大堆,满足向下调整算法的前提

?

往前找到结点‘27’进行向下调整,由与是从后向前调整,使得整个结点‘27’的左右子树均为大堆,满足向下调整算法的前提

通过从后向前进行向下调整,成功让无序的数组变成了大堆:

?

以上可以看出只要从最后一个结点的父结点往前使用向下调整算法就能总是满足,左右子数必须是堆的前提,这样就实现了建堆的整个过程。


5.堆实现

5.1堆结构

typedef int HeapDataType;

typedef struct Heap {
	HeapDataType* data;
	int size;
	int capacity;
}Heap;

Heap:堆结构组成

HeapDataType:指链表中每个结点存储的数据类型,只需要改动typedef int HeapDataType?中的int就能该变节点内存放的数据类型。

data:HeapDataType类型的数组指针。

capacity:数组可存放大小。

size:数组已存放大小。

5.2数组功能实现

文件:”Heap.c

5.2.1向下调整算法

void AdjustDown(Heap* ph, int parent)				//向下调整算法
{
	assert(ph);
	int	child = parent * 2 + 1;
	while (child < ph->size)
	{
		if (child + 1 < ph->size && ph->data[child + 1] > ph->data[child])	
		{
			child++;		//找出左右孩子中大的那个
		}

		if (ph->data[child] > ph->data[parent])			//孩子大于父亲就交换
		{
			Swap(&ph->data[child], &ph->data[parent]);
			parent = child;								//孩子成为父亲
			child = parent * 2 + 1;						//继续找孩子
		}
		else
		{
			break;										//孩子不大于父亲结束调整
		}
	}
}

5.2.2建堆

void BuildHeap(Heap* ph)							//建堆
{
	assert(ph);
	for (int i = (ph->size - 1 - 1) / 2; i >= 0; i--)	
	{
		AdjustDown(ph, i);							//从最后一个父亲想前调整
	}
}

5.2.3向上调整算法

void AdjustUp(Heap* ph, int child)					//向上调整算法
{
	assert(ph);
	assert(child < ph->size);
	int parent = (child - 1) / 2;					//找出父亲
	while (child > parent)
	{
		if (ph->data[child] > ph->data[parent])		//孩子大于父亲交换
		{
			Swap(&ph->data[child], &ph->data[parent]);
			child = parent;							//父亲成为孩子
			parent = (child - 1) / 2;				//继续找父亲
		}
		else
		{
			break;									//孩子不大于父亲结束调整
		}
	}

}

5.2.4堆初始(给一个数组建成堆)

void HeapInit(Heap* ph, HeapDataType* str, int size)	//堆初始
{
	assert(ph);
	assert(str);
	//给定数组建成堆,并初始
	HeapDataType* DataSpace;
	DataSpace = (HeapDataType*)malloc(sizeof(HeapDataType) * size);	//开辟给定数组相等空间
	assert(DataSpace);
	memcpy(DataSpace, str, size*sizeof(HeapDataType));				//将数组内容拷贝进堆
	ph->data = DataSpace;											
	ph->size = size;												//堆初始大小等于数组大小
	ph->capacity = size;

	//建堆
	BuildHeap(ph);												
}

5.2.5堆打印

void HeapPrint(Heap* ph)								//堆打印
{
	assert(ph);
	for (int i = 0; i < ph->size; i++)
	{
		printf("%d ", ph->data[i]);
	}
	printf("\n");
}

5.2.6入堆

void HeapPush(Heap* ph, HeapDataType x)					//入堆
{
	assert(ph);
	if (ph->capacity == ph->size)				//判段堆是否已满
	{
		HeapDataType* newspace;
		newspace = (HeapDataType*)realloc(ph->data, sizeof(HeapDataType) * (ph->size)*2);
		assert(newspace);
		ph->data = newspace;
		ph->capacity = ph->capacity * 2;
	}
	ph->data[ph->size] = x;
	ph->size++;

	AdjustUp(ph,ph->size - 1);					//将入堆数据放到堆最后,进行向上调整
}

5.2.7出堆

void HeapPop(Heap* ph)									//出堆
{
	//出堆头数据
	assert(ph);
	Swap(&ph->data[0], &ph->data[ph->size - 1]);		//将头尾数据交换
	ph->size--;											//堆大小减1
	AdjustDown(ph, 0);									//堆剩余内容向下调整
}

5.2.8堆顶数据

HeapDataType HeapTop(Heap* ph)							//堆顶数据
{
	assert(ph);
	assert(ph->data);
	return ph->data[0];
}

5.2.9堆大小

int HeapSize(Heap* ph)									//堆大小
{
	assert(ph);
	return ph->size;
}

5.2.10判空

bool HeapEmpty(Heap* ph)								//判空
{
	assert(ph);
	return ph->size == 0;					 
}

5.2.11堆销毁

void HeapDestroy(Heap* ph)								//堆销毁
{
	assert(ph);
	free(ph->data);							//释放堆空间
	ph->data = NULL;						//堆空间指针指空
	ph->size = ph->capacity = 0;			//堆大小堆容量置零
}

5.3堆头文件

文件:”Heap.h“

#pragma once

#include"stdio.h"
#include"stdlib.h"
#include"assert.h"
#include"stdbool.h"

//大堆

typedef int HeapDataType;

typedef struct Heap {
	HeapDataType* data;
	int size;
	int capacity;
}Heap;

//堆操作
void Swap(HeapDataType* a, HeapDataType* b);
void AdjustUp(HeapDataType* Heap, int parent);
void AdjustDown(Heap* ph);
void BuildHeap(HeapDataType* Heap, int size);


//堆接口
void HeapInit(Heap* ph, HeapDataType* str, int size);	//堆初始
void HeapPrint(Heap* ph);								//堆打印
void HeapPush(Heap* ph,HeapDataType x);					//入堆
void HeapPop(Heap* ph);									//出堆
HeapDataType HeapTop(Heap* ph);							//堆顶数据
int HeapSize(Heap* ph);									//堆大小
bool HeapEmpty(Heap* ph);								//判空
void HeapDestroy(Heap* ph);								//堆销毁

5.4堆功能测试

文件:”test.c

//大堆
#include"Heap.h"

void test1()
{
	HeapDataType str[] = { 27,15,19,18,28,34,65,49,25,37 };
	Heap heap;
	int strsize = sizeof(str) / sizeof(str[0]);
	HeapInit(&heap, str, strsize);			//堆初始测试
	HeapPrint(&heap);						//堆打印测试

	HeapPush(&heap, 88);					//入堆测试
	HeapPrint(&heap);

	while (!HeapEmpty(&heap))				//判空测试
	{
		printf("top:%d\n", HeapTop(&heap));	//堆顶数据测试
		HeapPop(&heap);						//出堆测试
		HeapPrint(&heap);		
	}

	HeapPush(&heap, 88);					//出空再入测试
	//HeapPrint(&heap);

	HeapDestroy(&heap);						//堆销毁测试

}

int main()
{
	test1();
	return 0;
}

?写在最后

?笔记时间:2022_01_10

🌐代码:Gitee:朱雯睿 (zhu-wenrui) - Gimtee.com

? ? ? ? ? ? ? ?Github:https://github.com/Zero0Tw0

💻代码平台:Visual Studio2019

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-01-12 00:15:37  更:2022-01-12 00:17:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 18:35:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码