广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++实现二叉树及堆的示例代码
  • 191
分享到

C++实现二叉树及堆的示例代码

2024-04-02 19:04:59 191人浏览 八月长安
摘要

1 树 树是一种非线性数据结构,它是由n个有限结点组成的具有层次关系的集合。把它叫树是因为它是根朝上,叶子朝下的 来上图瞧瞧 1.1 树的相关名词 2 二叉树 2.1 二叉树的

1 树

树是一种非线性数据结构,它是由n个有限结点组成的具有层次关系的集合。把它叫树是因为它是根朝上,叶子朝下的
来上图瞧瞧

在这里插入图片描述

1.1 树的相关名词

在这里插入图片描述

2 二叉树

2.1 二叉树的概念

一颗二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根结点加上两棵分别称为左子树和右子树。
如图所示:

在这里插入图片描述

二叉树有以下特点:

1、每个二叉树最多有两颗子树,所以二叉树不存在度为2的结点。
2、二叉树的子树有左右之分,其子树的顺序不能颠倒。

2.2 二叉树的性质

1、若规定根结点的层数为第一层,则一颗非空二叉树的第i层上最多有z^(k-1)个结点
2、若规定根结点的层数为第一层,则深度为h的二叉树的最大结点数是2^k-1个。
3、对于任何一颗二叉树,度为0的结点(叶子结点)的个数为n0 ,度为2的结点个数为n2则一定有,n0 = n2 + 1。
4、若规定根结点的层数为第一层,具有N个结点的满二叉树的深度h = log2(N+1)[说明:以2为底的N+1的对数],这个可以由性质2推导得到。
5、对于具有n个结点的完全二叉树,如果按照从上到下从左到右的数组顺序对所有结点从0开始编号,即对于数组下标i的结点有:
1)i>1,i位置的父结点的在数组中的下标为(i-1)/2.
2)i位置结点的左孩子结点的下标为2i+1,右结点下标为2i+2。

2.3 特殊的二叉树

1、满二叉树:一个二叉树,如果每一层的结点数都达到了最大,如果这个二叉树的层次是k,则其结点数是2^k-1。
2、完全二叉树:用最直白的话来说就是,树的深度或者高度为k,前k-1层的结点都是满的,只有最后的第k层不满但是从左到右的结点必须是连续的。
其实满二叉树是一种特殊的完全二叉树。
如图所示:

在这里插入图片描述

图为完全二叉树,要是最后一层全满则为满二叉树。
满足:
2^k-1-x = N;
x的取值范围是[0,2^(k-1)-1]

3 堆

3.1 堆的概念

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费,而完全二叉树更适合用顺序存储,顺序存储的随机访问特性,会右巨大的优点。我们通常把堆(是一种完全二叉树)采用顺序存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构一个是OS中管理内存的一块区域分段。
堆分为大根堆和小根堆。
大根堆:父亲结点>=孩子结点
小根堆:父亲结点<=孩子结点

在这里插入图片描述

上面是堆的逻辑结构,下面是物理结构

3.2 堆的实现

首先构建堆我们要了解一个算法,叫向下调整算法。我们以小根堆为例,我们把图示的完全二叉树构建为小堆,这个二叉树有个条件是根结点的两个子树都是小堆才可以进行向下调整算法。

在这里插入图片描述

3.2.1 向下调整算法

在这里插入图片描述

根结点的左右子树都是小堆,根结点27和左右子树的根结点较小的那一个交换位置,然后依次进行,直到叶子结点。就把最小的15结点上浮到堆顶的位置。这个算法的前提是根节点的左右子树都是小堆,如果我们想把任意的的数组构建成小堆显然不满足条件。在下面我们介绍把任意数组构建成小堆的办法。
向下调整算法的代码如下:


void AdjustDown(HeapDataType* a, int n, int root)
{
	//父子下标的初始化
	int parent = root;
	int child = parent * 2 + 1;
	//循环向下调整,把最小值(或者最大值浮到堆顶)
	while (child < n)
	{
		//选出左右孩子中较小的孩子,作为child,child+1 < n保证下标不能越界
		if (child+1 < n && a[child + 1] < a[child])
		{
			++child;
		}
		//父亲比孩子小二者交换位置,并更新迭代孩子的位置
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//迭代parent child
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;//当孩子 >= 父亲的时候,满足小堆的条件,跳出循环节课,否则就会死循环
		}
	}
}

3.2.2 堆的构建

在这里插入图片描述

把给定的数据化成完全二叉树的逻辑结构如上图所示,这个数组(二叉树)显然不能满足向下调整算法的理想条件,所以我们把问题拆分,比如你先思考下这个问题,把左子树和右子树全部构建成小堆不就满足条件了嘛,但是子树的左右子不是小堆怎么办呢,那么同样的道理,把它也构建成子树就可以了,那么我们可以从叶子结点向上每个结点都执行一边向下调整算法不就可以了嘛。其实我们不必从叶子结点开始因为叶子结点没有子树其实都可以看成是小堆或者大堆。所以从第一个非叶子结点开始调整即可。

在这里插入图片描述

也就是从图中紫色圈圈画出来的那个开始调整即可,直到根结点93,就会把一个数组构建成小堆(大堆)。


for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}

(n-1-1)/2为第一非叶子结点下标。

3.2.3 堆排序

有了上面的知识做铺垫,堆排序就可以很好的理解了。
1、把数组构建成大堆或者小堆,位于堆顶的数据就是最大值或者最小值,把堆顶数据和最后的结点位置的数据(数组元素最后一个)互换。然后对前n-1个结点重新向下调整为大堆或者小堆。直到剩下最后一个根节点就排序完成。
只是要升序:构建大堆,要降序:构建小堆,你细细品。
代码如下:


//堆排序
void HeapSort(int* a, int n)
{
	//堆排序的第一步就是构建堆,构建堆的时间复杂度是O(n),此时是小堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//如果是升序,构建大堆
	//如果是降序,构建小堆
	//是反着的,因为要和最后一个进行交换
	int end = n - 1;
	while (end>0)
	{
		//把堆顶(最小或者最大)和最后的的元素交换,然后从0到n-2继续向下调整
		//把次小(次大)的元素也选出来,直到剩最后一个,堆排序完成
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}


### 3.2.4 堆的的增删查改
声明:
```c
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <windows.h>

typedef int HeapDataType;

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

//堆的初始化,内部有堆的创建
void Heapinit(Heap* PHP, HeapDataType* a, int n);
//堆的销毁
void HeapDestory(Heap* php);
//堆的插入数据
void HeapPush(Heap* php, HeapDataType x);
//堆的删除数据
void HeapPop(Heap* php);
//获取堆顶数据
HeapDataType HeapTop(Heap* php);
//对传入的数组内的数据进行堆排序
void HeapSort(int* a, int n);

定义:


#include "Heap.h"
//堆是一种完全二叉树,用顺序存储(数组)比较好
//用于交换两个数据
void Swap(HeapDataType* n1, HeapDataType* n2)
{
	HeapDataType temp = *n1;
	*n1 = *n2;
	*n2 = temp;
}
//向下调整算法___老重要了,这是理解堆排序和topk问题以及堆这里相关题的基础
//向下调整结束的情况有两个一个是a[parent]<a[child],另一个是从堆顶到数组结束全部比较完
void AdjustDown(HeapDataType* a, int n, int root)
{
	//父子下标的初始化
	int parent = root;
	int child = parent * 2 + 1;
	//循环向下调整,把最小值(或者最大值浮到堆顶)
	while (child < n)
	{
		//选出左右孩子中较小的孩子,作为child,child+1 < n保证下标不能越界
		if (child+1 < n && a[child + 1] < a[child])
		{
			++child;
		}
		//父亲比孩子小二者交换位置,并更新迭代孩子的位置
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//迭代parent child
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;//当孩子 >= 父亲的时候,满足小堆的条件,跳出循环节课,否则就会死循环
		}
	}
}
//向上调整算法
//用在HeapPush中
void AdjustUp(HeapDataType* a, int n, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			//迭代
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//堆的初始化,内部有堆的创建
void HeapInit(Heap* php, HeapDataType* a, int n)
{
	HeapDataType* temp = (HeapDataType*)malloc(sizeof(HeapDataType)*n);
	if (temp)
	{
		php->arr = temp;
		
	}
	else
	{
		printf("内存申请失败!");
		exit(-1);
	}
	//将传进来的数组拷贝给malloc出来的空间,用来后续的堆的创建,删除,插入数据等操作
	memcpy(php->arr, a, sizeof(HeapDataType)*n);
	php->size = n;
	php->capacity = n;

	//把拷贝进来的数组,构建成堆
	//从倒数第一个非叶子节点进行构建(直接把数组画成一个完全二叉树可以直接由图得到第一个
	//非叶子节点的下标)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(php->arr, php->size, i);
	}
}
//堆排序
void HeapSort(int* a, int n)
{
	//堆排序的第一步就是构建堆,构建堆的时间复杂度是O(n),此时是小堆
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	//如果是升序,构建大堆
	//如果是降序,构建小堆
	//是反着的,因为要和最后一个进行交换
	int end = n - 1;
	while (end>0)
	{
		//把堆顶(最小或者最大)和最后的的元素交换,然后从0到n-2继续向下调整
		//把次小(次大)的元素也选出来,直到剩最后一个,堆排序完成
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
		--end;
	}
}

//销毁堆
void HeapDestory(Heap* php)
{
	assert(php);
	free(php->arr);
	php->arr = NULL;
	php->capacity = php->size = 0;
}

//堆的插入数据
void HeapPush(Heap* php, HeapDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		php->capacity *= 2;
		HeapDataType* tmp = (HeapDataType*)realloc(php->arr, sizeof(HeapDataType)*php->capacity);
		if (tmp)
		{
			php->arr = tmp;
		}
		else
		{
			printf("扩容失败!\n");
			exit(-1);
		}
	}
	php->arr[php->size++] = x;
	AdjustUp(php->arr, php->size, php->size - 1);
}

//堆的删除数据,要删除堆顶的数据
void HeapPop(Heap* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	php->size--;
	AdjustDown(php->arr, php->size, 0);
}

//获取堆顶数据
HeapDataType HeapTop(Heap* php)
{
	assert(php);
	assert(php->size > 0);
	return php->arr[0];
}

看这里的小伙伴可以自己下去测试一下代码哦。

到此这篇关于c++实现二叉树及堆的示例代码的文章就介绍到这了,更多相关C++实现二叉树及堆内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++实现二叉树及堆的示例代码

本文链接: https://www.lsjlt.com/news/124108.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++实现二叉树及堆的示例代码
    1 树 树是一种非线性数据结构,它是由n个有限结点组成的具有层次关系的集合。把它叫树是因为它是根朝上,叶子朝下的 来上图瞧瞧 1.1 树的相关名词 2 二叉树 2.1 二叉树的...
    99+
    2022-11-12
  • C++怎么实现二叉树及堆
    这篇文章给大家分享的是有关C++怎么实现二叉树及堆的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1 树树是一种非线性数据结构,它是由n个有限结点组成的具有层次关系的集合。把它叫树是因为它是根朝上,叶子朝下的来上图...
    99+
    2023-06-14
  • Python实现二叉排序树与平衡二叉树的示例代码
    目录前言1. 二叉排序树1.1 构建一棵二叉排序树1.2 二叉排序树的数据结构1.3 实现二叉排序树类中的方法:2. 平衡二叉排序树2.1 二叉平衡排序树的数据结构3. 总结前言 什...
    99+
    2022-11-10
  • Java实现二叉树的示例代码(递归&迭代)
    目录1.二叉树基本概念见上节:详解Java中二叉树的基础概念(递归&迭代) 2.本次展示链式存储 以此图为例,完整代码如下: //基础二叉树实现 //使用左右孩子表示法 ...
    99+
    2022-11-13
  • C++合并二叉树的思路与示例代码
    前言 给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点...
    99+
    2022-11-12
  • C++实现验证二叉搜索树代码
    本篇内容主要讲解“C++实现验证二叉搜索树代码”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++实现验证二叉搜索树代码”吧!验证二叉搜索树Given a binary tree, determ...
    99+
    2023-06-20
  • C语言二叉树与堆的概念与实现
    目录引言—树的故事树的基本性质和描述树的基本特点树的关键字解析树的表示方法二叉树的概念结构特殊二叉树二叉树的性质二叉树的存储结构二叉树与堆堆的实现堆排序堆的功能实现TOPK问题二叉树...
    99+
    2022-11-12
  • java栈实现二叉树的非递归遍历的示例代码
    一般来说遍历二叉树用到递归,但是用Stack进行遍历也是一个不错的方法。 二叉树设置 class Node{ public int val; public Node left...
    99+
    2022-11-11
  • 纯C++二叉树相关操作实例代码分析
    这篇“纯C++二叉树相关操作实例代码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“纯C++二叉树相关操作实例代码分析”文...
    99+
    2023-07-02
  • C语言堆与二叉树的顺序结构与实现
    目录一. 二叉树的顺序结构二. 堆的概念及结构三. 堆的实现四. 堆排序(具有缺陷型)一. 二叉树的顺序结构 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全...
    99+
    2022-11-13
  • C++使用LeetCode实现二叉搜索树的示例分析
    这篇文章将为大家详细讲解有关C++使用LeetCode实现二叉搜索树的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Given an integer n, generate all st...
    99+
    2023-06-20
  • C语言中二叉树的示例分析
    这篇文章主要为大家展示了“C语言中二叉树的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C语言中二叉树的示例分析”这篇文章吧。树概念及结构树是一种 非线性 的数据结构,它是由 n ( n...
    99+
    2023-06-29
  • c++实现堆排序的示例代码
    看了一下优先队列,查了一下堆排序。堆排序主要就是建最大堆(最小堆)和交换2个操作。如果建的是最大堆,那么交换的时候,父节点就和最大的子节点比较,如果它比最大的子节点还大,那就不用比了...
    99+
    2023-02-02
    c++ 堆排序
  • 【数据结构】二叉树-堆实现及其堆的应用(堆排序&topK问题)
    文章目录 一、堆的概念及结构二、堆的实现1.结构的定义2.堆的初始化3.堆的插入4.堆的向上调整5.堆的删除6.堆的向下调整7.取出堆顶元素8.返回堆的元素个数9.判断堆是否为空10.打印堆中...
    99+
    2023-09-05
    数据结构 php 开发语言
  • Java实现二叉树的代码怎么写
    本篇内容主要讲解“Java实现二叉树的代码怎么写”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java实现二叉树的代码怎么写”吧!以此图为例,完整代码如下://基础二叉树实现//使用左右孩子表示...
    99+
    2023-06-29
  • C语言二叉树的遍历示例介绍
         在本算法中先利用先序遍历创建了树,利用了递归的算法使得算法简单,操作容易,本来无printf("%c的左/右子树:", c...
    99+
    2022-11-12
  • C语言平衡二叉树的示例分析
    这篇文章给大家分享的是有关C语言平衡二叉树的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一...
    99+
    2023-06-25
  • C语言数据结构二叉树之堆的实现和堆排序详解
    目录一、本章重点二、堆2.1堆的介绍2.2堆的接口实现三、堆排序一、本章重点 堆的介绍堆的接口实现堆排序 二、堆 2.1堆的介绍 一般来说,堆在物理结构上是连续的数组结构,在逻辑结构...
    99+
    2022-11-13
  • 利用JS实现二叉树遍历算法实例代码
    目录前言 一、二叉树 1.1、遍历二叉树 1.2、用js表示二叉树 1.3、前序遍历算法 1.4、中序遍历算法 1.5、后序遍历算法 1.6、按层遍历算法 二、算法题 1.1、二叉树...
    99+
    2022-11-12
  • C++实现LeetCode(173.二叉搜索树迭代器)
    [LeetCode] 173.Binary Search Tree Iterator 二叉搜索树迭代器 Implement an iterator over a binary sea...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作