初识二叉树以及堆的简单实现

这篇具有很好参考价值的文章主要介绍了初识二叉树以及堆的简单实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一:什么是树

【1】树的概念

【2】树的另外几个重要概念

【3】树的几种表示方法

二:什么是二叉树

【1】概念以及特点

【2】两种特殊的二叉树

【3】二叉树的性质

【4】二叉树的两种存储方式

三:堆的实现


一:什么是树

【1】树的概念

我们前面所学的顺序表,链表都属于线性结构,而树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的

图解:

初识二叉树以及堆的简单实现

(1) 树有一个特殊的节点,叫根节点,在图中为结点A

(2)除根节点外,其余结点被分成M(M>0)互不相交的集合T1T2……Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以 有0个或多个后继。

(3)父子结点:例如A是B,C,D的父节点,而E,F是B的子节点

(4)叶结点:没有子节点的结点,例如E,F,C,G,H

注意:树的子树是不相交的,除了根结点以外别的结点有且只有一个父节点。

【2】树的另外几个重要概念

图解:

初识二叉树以及堆的简单实现

(1)节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6
(2)非终端节点或分支节点:度不为0的节点; 如上图:DEFG...等节点为分支节点
(3)兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:BC是兄弟节点
(4)树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
(5)节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
(6)树的高度或深度:树中节点的最大层次; 如上图:树的高度为4
(7)节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
(8)子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
(9)森林:由m(m>0)互不相交的多颗树的集合称为森林。

【3】树的几种表示方法

(1)利用顺序表存子节点的地址(结构复杂)
初识二叉树以及堆的简单实现

(2) 已经说明了树的度(最大结点的度),设置一个指针数组来存储子节点的地址

初识二叉树以及堆的简单实现

(3)结构体数组存储 

初识二叉树以及堆的简单实现

初识二叉树以及堆的简单实现

 (4)左孩子右兄弟表示法(比较常用,结构也比较简单,逻辑相对清晰)

初识二叉树以及堆的简单实现

初识二叉树以及堆的简单实现

二:什么是二叉树

【1】概念以及特点

概念:

一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成

特点:

1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2. 二叉树的子树有左右之分,其子树的次序不能颠倒。

初识二叉树以及堆的简单实现

【2】两种特殊的二叉树

1. 满二叉树:

所有叶子节点都在最后一层

所有分支节点都有两个孩子

2. 完全二叉树:

假设这个二叉树有N层,前N-1层必须满

最后一层可以不满,但是必须左向右连续

初识二叉树以及堆的简单实现

【3】二叉树的性质

1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1
3. 对任何一棵二叉树, 如果度为0其叶结点个数为n0, 度为2的分支结点个数为 n2,则有n0=n2+1。
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=LogN。

【4】二叉树的两种存储方式

(1)顺序结构存储(数组)

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
初识二叉树以及堆的简单实现

(2)链式结构存储

链表来表示一棵二叉树,即用链来指示元素的逻辑关系。

通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

链式结构又分为二叉链和三叉链,目前一般都是二叉链,红黑树等高阶数据结构会用到三叉链。

初识二叉树以及堆的简单实现

初识二叉树以及堆的简单实现

三:堆的实现

堆是用数组实现的二叉树,并且一般用来实现完全二叉树。

堆分为大堆和小堆

大堆:父亲>=孩子

小堆:孩子>=父亲

本文实现的是大堆

堆的实现和顺序表类似,这里就不展开细讲,只讲主要的接口实现。

附上顺序表链接:https://blog.csdn.net/2301_76269963/article/details/129352041?spm=1001.2014.3001.5501

【1】插入数据

(1)判断扩容,和顺序表一致。

(2)存储数据,和顺序表一致。

(3)在进行数据插入后我们要保证插入以后还是大堆,就必须进行父子关系的调整。

初识二叉树以及堆的简单实现

在考虑调整之前,我们观察一下父亲下标和孩子下标的关系 

初识二叉树以及堆的简单实现

我们可以发现这样一个规律

父亲下标=(孩子下标-1)/2。 

我们根据这个规律来设计调整函数,如果要插入的数据大于父亲,就调换两者一直到小于父亲或者成为了根部节点

因为后续的删除也需要进行交换调整,我们可以将交换单独封装成函数HeapSwap( )。

初识二叉树以及堆的简单实现

代码:

初识二叉树以及堆的简单实现

初识二叉树以及堆的简单实现

【2】删除数据(堆排序的基础)

基础思路:

(1)堆的数据删除要求的是删除根部数据

(2)要注意不能直接和顺序表一样向前覆盖,这样会破坏原堆的结构

图解:

初识二叉树以及堆的简单实现

(3)我们既要删除掉原根部数据,又要进行调整

删除:我们可以将根部和最后的一片叶子进行交换,然后直接将size(有效数据个数)减1.

图解:

初识二叉树以及堆的简单实现

 调整:从下标0开始向下调整,如果孩子大于父亲就进行交换,一直到比两个孩子都大或者已经调整到了叶子就结束,每次进行判断前先比较两个孩子谁比较大防止破坏大堆的结构较大的孩子与父亲进行交换,然后迭代。

图解:

初识二叉树以及堆的简单实现

代码:

 初识二叉树以及堆的简单实现

 

初识二叉树以及堆的简单实现

全部代码:

Heap.h(必要头文件的包含,函数和结构体声明)

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

typedef int HPDataType;
typedef struct Heap
{
	//存储数据
	HPDataType* a;
	//有效数据个数
	int size;
	//容量
	int capacity;
}HP;
//初始化
void HeapInit(HP* hp);
//销毁
void HeapDestory(HP* hp);
//交换函数
void HeapSwap(int* p1, int* p2);
//判空函数
bool HeapEmpty(HP* hp);
//调整函数
void AdjustUp(HPDataType* a,int child);
//向下调整,n是有效个数
void AdjustDown(HPDataType* a, int n, int parent);
//插入数据
void HeapPush(HP* hp, HPDataType x);
//打印数据
void HeapPrint(HP* hp);
//删除数据
void HeapPop(HP* hp);

Heap.c(接口实现)

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"
//初始化
void HeapInit(HP* hp)
{
	//断言,不能传空的结构体指针
	assert(hp);
	hp->a = NULL;
	//初始化size和容量都为0
	hp->size = hp->capacity = 0;
}

//销毁
void HeapDestory(HP* hp)
{
	free(hp->a);
	hp->size = hp->capacity = 0;
}

//交换函数
void HeapSwap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//判空函数
bool HeapEmpty(HP* hp)
{
	return hp->size == 0;
}

//调整函数
//向上调整
void AdjustUp(HPDataType* a, int child)
{
	//断言,不能传空指针
	assert(a);
	//找到父结点的下标
	int parent = (child - 1) / 2;
	//循环,以child到树根为结束条件
	while (child > 0)
	{
		//如果父结点比child下,交换并更新
		if (a[child] > a[parent])
		{
			HeapSwap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		//如果父结点比child大,跳出循环
		else
		{
			break;
		}
	}
}
//向下调整
void AdjustDown(HPDataType* a, int n, int parent)
{
	//默认左孩子最大
	int child = parent * 2 + 1;
	//当已经调整到超出数组时结束
	while (child<n)
	{
		//找出两个孩子中大的一方
		//考虑右孩子不存在的情况
		if (child+1<n&&a[child + 1] > a[child])
		{
			//如果右孩子大,child加1变成右孩子
			child++;
		}
		//如果父亲比大孩子小,进行调整,否则跳出
		if (a[child] > a[parent])
		{
			HeapSwap(&a[child], &a[parent]);
			//迭代
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//插入数据
void HeapPush(HP* hp, HPDataType x)
{
	if (hp->size == hp->capacity)
	{
		//判断扩容多少
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		//扩容
		HPDataType* tmp =
			(HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);
		//更新
		hp->capacity = newcapacity;
		hp->a = tmp;
	}
	//存储数据
	hp->a[hp->size] = x;
	hp->size++;
	//进行调整
	AdjustUp(hp->a, hp->size-1);
}

//打印数据
void HeapPrint(HP* hp)
{
	//断言,不能传空的结构体指针
	assert(hp);
	int i = 0;
	for (i = 0; i < hp->size; i++)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}

//删除数据
void HeapPop(HP* hp)
{
	//断言,不能传空的结构体指针
	assert(hp);
	//如果为空,不能删除,避免数组越界
	assert(!HeapEmpty(hp));
	//不为空,先交换根和最后一片叶子,然后size减1
	HeapSwap(&hp->a[0], &hp->a[hp->size - 1]);
	hp->size--;
	AdjustDown(hp->a, hp->size, 0);
}

text.c(测试)

#define _CRT_SECURE_NO_WARNINGS 1
#include "Heap.h"

void text1()
{
	HP hp;
	HeapInit(&hp);
	HPDataType a[] = { 70,30,56,25,15,10.85,79};
	int i = 0;
	for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
	{
		HeapPush(&hp, a[i]);
	}
	HeapPrint(&hp);
	HeapPop(&hp);
	HeapPrint(&hp);
	HeapDestory(&hp);
}

int main()
{
	text1();
}

初识二叉树以及堆的简单实现文章来源地址https://www.toymoban.com/news/detail-407323.html

到了这里,关于初识二叉树以及堆的简单实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 树、二叉树概念(+堆的实现)

    欢迎来到我的: 世界 希望作者的文章对你有所帮助,有不足的地方还请指正,大家一起学习交流 ! 新的篇章的开始,是见证我的这一刻; 噢,这美好的一天; 是需要你来见证的! 我最亲爱的老铁们!!😊 要知道二叉树首先要知道 树 这 非线性 数据结构,它是由n(n≥0)个有

    2024年02月07日
    浏览(39)
  • 【数据结构】——二叉树堆的实现

     大佬们点点关注,点点赞?! 在上篇博客中我们已经介绍了树和二叉树的相关概念,相信大家都已经清楚了树和二叉树的基本思想,下面我们就来着重看看二叉树堆的实现。 在看堆的实现,我们先看看二叉树的顺序存储 二叉树的顺序存储就是以顺序表来实现的,也就是把

    2024年04月13日
    浏览(59)
  • 完全二叉树——堆的概念及实现

    堆(heap):是堆内存的简称,堆是动态分配内存,内存大小不固定,也不会自动释放,堆——数据结构是一种无序的树状结构,同时它还满足key-value键值对的存储方式。 如果有一个关键码的集合K = { , , ,…, },把它的所有元素按完全二叉树的顺序存储方式存储在一个一维

    2024年02月07日
    浏览(37)
  • 数据结构学习记录——什么是堆(优先队列、堆的概念、最大堆最小堆、优先队列的完全二叉树表示、堆的特性、堆的抽象数据类型描述)

    目录 优先队列 若采用数组或链表实现优先队列  数组 链表 有序数组 有序链表 总结 若采用二叉搜索树来实现优先队列 最大堆 堆的概念 优先队列的完全二叉树表示 堆的两个特性  结构性 有序性 【例】最大堆和最小堆 【例】不是堆 堆的抽象数据类型描述 优先队列 (Prio

    2024年02月02日
    浏览(52)
  • 【数据结构和算法】---二叉树(2)--堆的实现和应用

    如果有一个数字集合,并把它的所有元素 按完全二叉树的顺序存储方式存储在一个一维数组中 ,且在逻辑结构(即二叉树)中,如果 每个父亲节点都大于它的孩子节点那么此堆可以称为大堆 ;那么如果 每个父亲节点都小于它的孩子节点那么此堆可以称为小堆 。 堆的 性质

    2024年02月03日
    浏览(47)
  • 速学数据结构 | 二叉树堆的实现详解篇

    🎬 鸽芷咕 :个人主页  🔥 个人专栏 :《速学数据结构》 《C语言进阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,二叉树的概念大家都了解了那么我们今天就看一下    ⛳️ 顺序存储究竟是怎么存储的,如何实现增删查改这些功能。

    2024年02月01日
    浏览(41)
  • 【完全二叉树魔法:顺序结构实现堆的奇象】

    二叉树的顺序结构 堆的概念及结构 堆的实现 堆的调整算法 堆的创建 堆排序 TOP-K问题         普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。 现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储

    2024年02月07日
    浏览(40)
  • DS:二叉树的顺序结构及堆的实现

                                           创作不易,兄弟们给个三连!!       顺序结构指的是利用数组来存储,一般 只适用于表示完全二叉树,原因如上图 ,存储不完全二叉树会造成空间上的浪费, 有的人又会问,为什么图中空的位置不能存储呢?? 原因是我们需要

    2024年02月19日
    浏览(38)
  • 数据结构——lesson7二叉树 堆的介绍与实现

    啦啦啦~这里是土土数据结构学习笔记🥳🥳 💥个人主页:大耳朵土土垚的博客 💥 所属专栏:数据结构学习笔记 💥对于数据结构顺序表链表有疑问的都可以在上面数据结构的专栏进行学习哦~ 欢迎大家🥳🥳点赞✨收藏💖评论哦~🌹🌹🌹 有问题可以写在评论区或者私信我

    2024年03月11日
    浏览(43)
  • 【数据结构】由完全二叉树引申出的堆的实现

    关于“堆”,百度百科上是这么说的: ——————————引自百度百科 由上面可知,我们可以将堆理解成一个数组,也可以理解成一个完全二叉树。 其实由于完全二叉树的特殊性,其本身就可以使用一个数组来存储。 在之前的二叉树的实现中,我们已经知道完全二叉树

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包