【数据结构与算法】第8课—数据结构之二叉树(堆)

news2024/11/5 14:57:00

文章目录

  • 1. 树
    • 1. 什么是树?
    • 1.2 树的相关概念
    • 1.3 树的表示法
  • 2. 二叉树
    • 2.1 特殊的二叉树
    • 2.2 二叉树的性质
    • 2.3 二叉树的存储结构
  • 3. 实现顺序结构二叉树
    • 3.1 堆的概念
    • 3.2 堆的实现
      • 3.2.1 堆的数据结构
      • 3.2.2 堆的初始化
      • 3.2.3 堆插入数据
      • 3.2.4 删除堆顶数据
      • 3.2.5 堆的判空和求有效数据个数
    • 3.3 堆排序
    • 3.4 Top K问题

1. 树

1. 什么是树?

  • 树是一种非线性的数据结构,它是由n(n>=0)个有限节点组成一个具有层次关系的集合,一般是树根朝上,树叶朝下
  • 树有一个特殊的节点,叫做根节点,根节点没有前驱节点
  • 树的根节点下面又有很多子节点,但是这些子节点是不相交的
  • 除根节点外,每棵子树的根节点有且只有一个前驱,但是可以有0个或多个后继,因此,树是递归定义的

在这里插入图片描述


1.2 树的相关概念

  • 父节点/双亲节点:若一个节点有子节点,那么这个节点就是它子节点的父节点
  • 子节点/孩子节点:通俗点讲,就是一个节点它有父节点,那么它就是它父节点的子节点
  • 节点的度:就是该节点有几个子节点,那么它就是几度
  • 树的度:一棵树最大的节点的度
  • 叶子节点/终端节点:度为0的节点
  • 分支节点/非终端节点:除根节点外度不为0的节点
  • 兄弟节点:共同享有同一个父节点的节点
  • 节点的层次:从树的根开始,根为第1层,根的子节点为第2层,以此类推
  • 树的高度或深度:树中节点的最大层次
  • 节点的祖先:从根到该节点所经分支上的所有节点
  • 森林:由m(m>0)棵互不相交的树的集合
  • 路径:一条从书中任意节点出发,沿父节点到子节点的路径

在这里插入图片描述


1.3 树的表示法

  • 这里主要介绍常用的孩子兄弟表示法
  • 树有N个节点,有N-1条边

在这里插入图片描述


2. 二叉树

  • 二叉树:是树中比较常用的一种树形结构
  • 二叉树的子树有左右之分,且次序不能颠倒
  • 二叉树之所以叫二叉树,是因为它每个节点的度最大为2,也就是每个节点最多有2个子节点

在这里插入图片描述


2.1 特殊的二叉树

  • 满二叉树
  • 一个二叉树,如果每一层的节点数都达到最大值,则该二叉树就是满二叉树
  • 通俗点讲,就是一个二叉树的每一层的节点(除根节点外)都有它的兄弟节点,那么它就是满二叉树
  • 满二叉树满足:第N层的节点数是2^(N-1)个,树一共有2^N-1个节点

在这里插入图片描述


  • 完全二叉树
  • 完全二叉树就是有N个节点的二叉树,除最后一层外,其余层都是填满的状态,而且最后一层的节点都是从左到右依次排列,中间不允许有空隙
  • 满二叉树是一种特殊的完全二叉树

在这里插入图片描述


在这里插入图片描述


2.2 二叉树的性质

  • 若规定二叉树的根节点的层数为1,那么该树第k层共有2^(k-1)个节点
  • 若二叉树的根节点的层数为1,那么深度为k的树的最大节点数为2^k-1
  • 若二叉树的根节点的层数为1,那么具有n个节点的满二叉树的深度h=log2(n+1)(2为底,n+1为对数)

在这里插入图片描述


2.3 二叉树的存储结构

  • 对于二叉树的存储结构一共有两种,一种是顺序结构,另一种是链式结构
  • 顺序结构存储

在这里插入图片描述


  • 链式结构存储
  • 链式结构中,链表的每个节点都有3个域组成,数据域和左右指针域,左右指针分别指向左孩子节点和右孩子节点
  • 链式结构又分为二叉链和三叉链,三叉链比二叉链多了一个指向父节点的指针

在这里插入图片描述


3. 实现顺序结构二叉树

3.1 堆的概念

  • 堆是一种特殊的二叉树,也是完全二叉树,一般是把堆使用顺序结构来存储
  • 堆的底层结构是数组
  • 堆也有大堆和小堆之分,根节点最大的堆叫最大堆/大根堆,根节点最小的堆叫最小堆/小根堆

在这里插入图片描述


  • 堆的性质
  • 堆的某个节点值总是不大于或不小于其父节点的值
  • 堆总是一棵完全二叉树
  • 二叉树的性质
  • 对于有N个节点的完全二叉树,对于在下标0~k的数组中有:

在这里插入图片描述


3.2 堆的实现

3.2.1 堆的数据结构

typedef int HeapDataType;
//堆的数据结构
typedef struct Heap
{
	HeapDataType* arr; //动态数组
	int size;          //数组有效数据个数
	int capacity;      //数组容量大小
}Heap;

3.2.2 堆的初始化

//堆初始化
void HeapInit(Heap* pheap)
{
	assert(pheap);
	pheap->arr = NULL;
	pheap->size = pheap->capacity = 0;
}

3.2.3 堆插入数据

在这里插入图片描述


//交换值
void Swap(HeapDataType* p1, HeapDataType* p2)
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

//向上调整算法
void AdjustUp(HeapDataType* arr, HeapDataType child)
{
	int parent = (child - 1) / 2;  //定义父节点
	while (child > 0)
	{
		//小堆
		if (arr[child] < arr[parent])
		{
			Swap(&arr[child], &arr[parent]); //交换值
			child = parent;  //child走到父节点
			parent = (child - 1) / 2;  //parent走到更新后的child的父节点
		}
		else
			break;
	}
}

//堆插入数据
void HeapPush(Heap* pheap, HeapDataType x)
{
	assert(pheap);
	//如果数组满了/为空
	if (pheap->size == pheap->capacity)
	{
		//定义数组容量
		int newCapacity = pheap->capacity == 0 ? 4 : 2 * pheap->capacity;
		//为数组申请空间
		HeapDataType* tmp = (HeapDataType*)realloc(pheap->arr, sizeof(HeapDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		pheap->arr = tmp;  //申请的空间赋给数组
		pheap->capacity = newCapacity;  //新容量大小赋给capacity
	}
	//插入数据
	pheap->arr[pheap->size] = x;
	//向上调整
	AdjustUp(pheap->arr, pheap->size);
	pheap->size++;
}

3.2.4 删除堆顶数据

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


//交换值
void Swap(HeapDataType* p1, HeapDataType* p2)
{
	HeapDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
//向下调整算法
void AdjustDown(HeapDataType* arr, HeapDataType parent, int n)
{
	HeapDataType child = parent * 2 + 1;//孩子节点
	while (child < n)
	{
		//找最大的孩子节点
		if (child + 1 < n && arr[child] < arr[child + 1])
		{
			child++;
		}
		//如果子节点大于父节点(大堆)
		if (arr[child] > arr[parent])
		{
			//交换父节点和子节点
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

//删除堆顶数据
void HeapPop(Heap* pheap)
{
	assert(!HeapEmpty(pheap));
	//交换堆顶元素和最后一个元素
	Swap(&pheap->arr[0], &pheap->arr[pheap->size - 1]);
	pheap->size--;
	//向下调整
	AdjustDown(pheap->arr, 0, pheap->size);
}

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

3.2.5 堆的判空和求有效数据个数

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

//求有效数据个数
int HeapSize(Heap* pheap)
{
	assert(pheap);
	return pheap->size;
}

3.3 堆排序

  • 先建堆

在这里插入图片描述


在这里插入图片描述

  • 之所以升序要建大堆,是因为最后还需要将堆顶元素与最后元素交换,然后向下调整,具体操作如下:

  • 排序(升序)
  • 向下调整算法

在这里插入图片描述
在这里插入图片描述


  • 堆排序时,每次首尾交换元素后,最后一个元素就是数的最大元素,因此在向下调整的过程中,child的限制条件为child<end,也就说明child移动的过程中是不会到划定范围内的最后一个元素,因为那样child已经越界了,这样也就保证了首尾每次交换的都是划定范围内的最大元素,同理child的兄弟节点child+1<end,也要保证不能越界,end每次向下调整过后要-1

在这里插入图片描述


//堆排序
void HeapSort(HeapDataType* arr, int sz)
{
	//根据给定的arr建大堆
	//child--sz-1  parent--(sz-1-1)/2
	for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(arr, i, sz);
	}
	
	//排升序:建大堆
	//排降序:建小堆
	//堆排序
	int end = sz - 1;
	while (end)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}

}

int main()
{
	int arr[] = { 34,18,88,23,67,45 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	HeapSort(arr,sz);//堆排序
	return 0;
}
  • 向下调整算法的时间复杂度:O(logn),n为元素个数
  • 堆排序的时间复杂度:n*O(logn),n为元素个数,因为堆排序在建堆时要执行(sz-2)次向下调整算法
  • 向上调整(升序)
//向上调整算法
void AdjustUp(HeapDataType* arr, HeapDataType child)
{
	int parent = (child - 1) / 2;  //定义父节点
	while (child > 0)
	{
		//大堆
		if (arr[child] > arr[parent])
		{
			Swap(&arr[child], &arr[parent]); //交换值
			child = parent;  //child走到父节点
			parent = (child - 1) / 2;  //parent走到更新后的child的父节点
		}
		else
			break;
	}
}
//堆排序
void HeapSort(HeapDataType* arr, int sz)
{
	//根据给定的arr建大堆
	//child--sz-1  parent--(sz-1-1)/2
	//for (int i = (sz - 1 - 1) / 2; i >= 0; i--)
	//{
	//	AdjustDown(arr, i, sz); //向下调整算法
	//}

	//向上调整算法
	for (int i = 0; i < sz; i++)
	{
		AdjustUp(arr, i);
	}
	
	//排升序:建大堆
	//排降序:建小堆
	//堆排序
	int end = sz - 1;
	while (end)
	{
		Swap(&arr[0], &arr[end]);
		AdjustDown(arr, 0, end);
		end--;
	}
	for (int j = 0; j < sz; j++)
	{
		printf("%d ", arr[j]);
	}
}
  • 总结
  • 向上调整算法建堆的时间复杂度为O(n*logn)
  • 向下调整算法建堆的时间复杂度为O(n)
  • 堆排序的时间复杂度为O(nlogn)

3.4 Top K问题

在这里插入图片描述


  • 代码实现
//造数据
void CreateNdata()
{
	int n = 100000;
	srand((unsigned int)time(NULL));
	FILE* fin = fopen("data.txt", 'w');
	if (fin == NULL)
	{
		perror("fopen fail!\n");
		return;
	}
	for (int i = 0; i < n; i++)
	{
		int x = (rand() + i) % 1000000;
		fprintf(fin, "%d\n", x);
	}
	fclose(fin);
}

//求前k个数据
void TopK()
{
	int k = 0;
	printf("请输入k:");
	scanf("%d", &k);

	const char* file = "data.txt";
	FILE* fout = fopen(file, 'r');
	if (fout == NULL)
	{
		perror("fout");
		exit(2);
	}
	//找最大的前K个数,建小堆
	int* minHeap = (int*)malloc(k * sizeof(int));
	if (minHeap == NULL)
	{
		perror("malloc");
		exit(1);
	}
	//读取文件中的前k个数据建堆
	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &minHeap[i]);
	}
	//建堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(minHeap, i, k);
	}
	//遍历剩下的n-k个数据,跟堆顶数据比较,谁大谁入堆
	int x = 0;
	while (fscanf(fout, "%d", &x) != EOF)
	{
		if (x > minHeap[0])
		{
			minHeap[0] = x;
			AdjustDown(minHeap, 0, k);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", minHeap[i]);
	}
	fclose(fout);
	free(minHeap);
	minHeap = NULL;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2231080.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于MATLAB人脸检测的汽车疲劳驾驶检测

课题介绍 疲劳驾驶导致汽车交通事故逐年增加&#xff0c;为了提升驾车的安全性&#xff0c;需对驾驶员疲劳状态实时监测并及时提醒. 为了提高疲劳驾驶判断效率及准确率&#xff0c;本文运用Viola-Jones 框架特征矩阵进行人脸预判断&#xff1b;预判断过程中为了减少Haar 值计算…

【p2p、分布式,区块链笔记 Torrent】WebTorrent的上传和下载界面

上传 upload.html client.seed <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件上传与哈希值&l…

vue3uniapp实现自定义拱形底部导航栏,解决首次闪烁问题

前言&#xff1a; 我最初在网上翻阅查找了很多方法&#xff0c;发现大家都是说在page.json中tabbar中添加&#xff1a;"custom": true,即可解决首次闪烁的问题&#xff0c;可是添加了我这边还是会闪烁&#xff0c;因此我这边改变了思路&#xff0c;使用了虚拟页面来解…

软考系统分析师知识点三一:案例知识点二

前言 今年报考了11月份的软考高级&#xff1a;系统分析师。 考试时间&#xff1a;11月9日。 倒计时&#xff1a;6天。 目标&#xff1a;优先应试&#xff0c;其次学习&#xff0c;再次实践。 复习计划第三阶段&#xff1a;总结案例知识点&#xff0c;并作为论文的框架知识…

WorkFlow源码剖析——Communicator之TCPServer(上)

WorkFlow源码剖析——Communicator之TCPServer&#xff08;上&#xff09; 前言 上一篇博客已经介绍了一下WorkFlow GO-Task的实现原理。本文会介绍一下WorkFlow Tcp Server端的一些实现细节以及有趣的思想。因为这部分涉及的内容有点多&#xff0c;一些有趣的细节也希望能完…

嵌入式硬件电子电路设计(一)开关电源Buck电路

目录 Buck电路基本结构 1. 开关闭合&#xff08;SW 闭合&#xff09; 2. 开关断开&#xff08;SW 断开&#xff09; 3. 开关控制和占空比 MP1584电路分析 其他Buck芯片的电路参考 Buck电路基本结构 下图是简化之后的BUCK电路主回路。下面分析输出电压的产生K闭合后&…

UE4_Niagara基础实例—13、通过纹理采样来创造粒子

效果&#xff1a; 知识点&#xff1a; 1、纹理采样目前仅支持GPU粒子运行&#xff08;Texture sampling is only supported on the GPU at the moment.&#xff09; 2、网格位置输出每个粒子在网格中的归一化位置。我们使用该值来采样纹理&#xff0c;就像它是UV一样&#xff…

多个锚点定位时的锚点优选方法(附公式和python代码讲解)

以下是将上述 MATLAB 代码转化为 Python 代码的版本。我们使用 NumPy 库进行数值计算&#xff0c;并使用 itertools 库生成锚点组合。 1. 基于几何分布的选择 锚点的几何分布影响定位的可辨识性。选择位置均匀分布的锚点组合可以提高定位精度。具体来说&#xff0c;锚点之间的…

HTML 基础概念:什么是 HTML ? HTML 的构成 与 HTML 基本文档结构

文章目录 什么是 HTML &#xff1f;HTML 的构成 &#xff1f;什么是 HTML 元素&#xff1f;HTML 元素的组成部分HTML 元素的特点 HTML 基本文档结构如何打开新建的 HTML 文件代码查看 什么是 HTML &#xff1f; HTML&#xff08;超文本标记语言&#xff0c;HyperText Markup L…

web安全测试渗透案例知识点总结(上)——小白入狱

目录 一、Web安全渗透测试概念详解1. Web安全与渗透测试2. Web安全的主要攻击面与漏洞类型3. 渗透测试的基本流程 二、知识点详细总结1. 常见Web漏洞分析2. 渗透测试常用工具及其功能 三、具体案例教程案例1&#xff1a;SQL注入漏洞利用教程案例2&#xff1a;跨站脚本&#xff…

基于Qt的独立线程创建与多线程执行实验Demo

一、多线程与线程池的应用目的[1][4] &#xff08;一&#xff09;多线程 一个进程内多个线程并发执行的情况就叫多线程&#xff0c;每一个线程是一个独立的执行流。多线程是一种编程模型&#xff0c;它与处理器无关&#xff0c;与设计机制有关。 需要多线程的原因包括&#xf…

基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 这里实现的是256*256双线性插值到512*512的系统模块 局部放大&#xff1a; 将数据导入到matlab&#xff0c;得到插值效果…

Spring1(初始Spring 解耦实现 SpringIOC SpringDI Spring常见面试题)

Spring1 创建项目集成maven创建一个Maven项目实现&#xff1a; 初识SpringSpring简介Spring的发展历史Spring之父体系结构生态系统官方文档解耦实现JDBCSpringBoot整合MyBatis和lombok&#xff0c;开启驼峰映射三层思想 SpringIOC实现 SpringDIset注入全部代码&#xff1a;实现…

纯享受 : 力扣:234 回文链表

BLG牛逼 – 奖励自己一道题 描述&#xff1a; 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为 回文链表 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例&#xff1a; 何解&#xff1f; 1、所谓 回文 就是正读反读都一样&…

开源项目-投票管理系统

哈喽,大家好,今天主要给大家带来一个开源项目-投票管理系统 投票管理系统主要有首页,发起投票,管理投票,参与投票,查看投票等功能 首页 为用户提供了一键导航到各个功能模块的便捷途径。 新增投票 用户可以在此轻松创建新的投票活动,设置投票主题、选项等信息。 管理…

系统架构图设计(行业领域架构)

物联网 感知层&#xff1a;主要功能是感知和收集信息。感知层通过各种传感器、RFID标签等设备来识别物体、采集信息&#xff0c;并对这些信息进行初步处理。这一层的作用是实现对物理世界的感知和初步处理&#xff0c;为上层提供数据基础网络层&#xff1a;网络层负责处理和传输…

APP获取用户的三大法则

APP内容&#xff0c;提升APP吸引力和用户留存率 A. 用户研究深化 1. **深入用户行为分析**&#xff1a; - 用户使用路径分析 - 用户行为模式识别 - 用户流失点分析 2. **定性研究与定量研究结合**&#xff1a; - 进行深度访谈和焦点小组讨论 - 利用数据分析用…

QT——串口调试助手

目录 1.QSerialPort类包含了很多有关串口的API 2.实现串口的打开 2.1 方法一&#xff1a;通过函数实现 2.2 方法二&#xff1a;在ui界面右下角实现 3. 实现定时发送 3.1类的私有成员中添加定时器QTimer timer并去构造函数中初始化它 3.2帮助文档中有QTimer类相关的说明 …

全自动一键批量创建站群网站插件 | Z-BlogPHP 堆词起站工具

在当今竞争激烈的数字营销世界&#xff0c;如何快速提升网站曝光率和流量&#xff1f;答案就是智能站群系统。 本文将结合实际效果&#xff0c;介绍一款功能强大的站群系统&#xff0c;重点讲述其堆词功能、泛目录管理、一键批量创建、内容转码、自定义标签和GPT内容生成与发布…

计算机毕业设计Spark+大模型知识图谱中药推荐系统 中药数据分析可视化大屏 中药爬虫 机器学习 中药预测系统 中药情感分析 大数据毕业设计

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…