非线性表之堆的实际应用和二叉树的遍历
目录
前言:前一篇我已经介绍过了二叉树和堆的介绍和相关代码的实现
一、堆的实现
1.1堆向上调整算法
1.2堆向下调整算法
二、堆的应用
2.1堆的排序
2.2TOP-K问题
三、二叉树的遍历
3.1 二叉树的创建
3.2遍历介绍
3.3前序遍历
3.4中序遍历
3.5后序遍历
四、二叉树的其他求法
4.1求二叉树的结点个数
4.2求二叉树的深度/高度
4.3求二叉树的第k层节点个数
五、结尾
前言:前一篇我已经介绍过了二叉树和堆的介绍和相关代码的实现
非线性链表之树结构和堆的代码实现-CSDN博客
一、堆的实现
1.1堆向上调整算法
使用堆的向上调整算法的前提是:在插入新元素之前,前面的元素就已经满足了堆的性质!!
堆的向上调整(heapify up)算法的思想是:
1.将新元素插入到叶子节点。
2.与父节点进行比较。
3.如果新元素的值大于父节点的值,则需要进行交换。 将新元素看作父节点,重复上述过程。 使
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void HPAdjustPush(int* a, int child)
{assert(a);int father = (child - 1) / 2;while (child > 0){if (a[father] < a[child]){swap(&a[father], &a[child]);child = father;father = (child - 1) / 2;}else{return;}}
}
1.2堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以
把它调整 成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void HPAdjustdown(int* a, int father,int sz)
{assert(a);int child = father * 2 + 1;while (child < sz){if (child + 1 < sz && a[child] < a[child + 1]){child++;}if (a[father] < a[child]){swap(&a[father], &a[child]);father = child;child = father * 2 + 1;}else{return;}}
}
二、堆的应用
2.1堆的排序
堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆 升序:建大堆 降序:建小堆
2. 利用堆删除思想来进行排序 建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以
完成堆排序。
代码实现
#define _CRT_SECURE_NO_WARNINGS 1#include<assert.h>
#include<stdio.h>void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void HPAdjustPush(int* a, int child)
{assert(a);int father = (child - 1) / 2;while (child > 0){if (a[father] < a[child]){swap(&a[father], &a[child]);child = father;father = (child - 1) / 2;}else{return;}}
}void HPAdjustdown(int* a, int father,int sz)
{assert(a);int child = father * 2 + 1;while (child < sz){if (child + 1 < sz && a[child] < a[child + 1]){child++;}if (a[father] < a[child]){swap(&a[father], &a[child]);father = child;child = father * 2 + 1;}else{return;}}
}void HeapSort(int* a, int n)
{// 建堆 -- 向上调整建堆 -- O(N*logN)/*for (int i = 1; i < n; ++i){AdjustUp(a, i);}*/// 建堆 -- 向下调整建堆 -- O(N)for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}
}
2.2TOP-K问题
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能 数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前K个元素来建堆
- 前k个最大的元素,则建小堆
- 前k个最小的元素,则建大堆
2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余N-K个元素依次与堆顶元素比完之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。
代码实现
typedef int HPDataType;void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//从从后往前的第一个非叶子节点开始向下调整
void AdjustDown(int* arr, int N, int parent)
{int child = parent * 2 + 1;//这里假设左孩子为小的/大的一个while (child < N){//假设这里建小堆//假设有右孩子并且如果右孩子比左孩子小的if (child + 1 < N && arr[child + 1] < arr[child]){child++;}//如果孩子比双亲小,则将双亲和孩子交换if (arr[child] > arr[parent]){Swap(&arr[child], &arr[parent]);parent = child;child = parent * 2 + 1;}else //当孩子比双亲大时,则不需要调整了{break;}}
}void TopK(int* arr, int n, int k)
{//建堆for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(arr, n, i);}//定义一个变量end记录堆最后一个元素int end = n - 1;//取前k个最值for (int i = end; i < k; i--){//将堆顶与比它大的数交换,必依次比较到n-kif (*(arr+end) < *arr){Swap(&arr[0], &arr[end]);AdjustDown(arr, end, 0);}}for (int i = 0; i < k; i++){printf("%d ", arr[i]);}printf("\n");
}int main()
{int n = 10000;srand((unsigned int)time(0));int* arr = (int*)malloc(sizeof(int) * n);if (arr == NULL){perror("malloc fail");return;}for (int i = 0; i < n; i++){arr[i] = rand() % 1000000 + 5;}//最大/*arr[100] = 1000001;arr[500] = 1000002;arr[1000] = 1000003;arr[5555] = 1000004;arr[9999] = 1000005;*///最小arr[100] = 1;arr[500] = 2;arr[1000] = 3;arr[5555] = 4;arr[9999] = 5;TopK(arr, n, 5);return 0;
}
三、二叉树的遍历
要想遍历二叉树,首先我手动创建一个二叉树
3.1 二叉树的创建
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;BTNode* BuyNode(BTDataType x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc fail");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}BTNode* CreatTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}
3.2遍历介绍
学习二叉树结构,最简单的方式就是遍历。
所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。
遍历 是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:
1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
上述二叉图遍历结果
前序遍历结果:1 2 3 4 5 6
中序遍历结果:3 2 1 5 4 6
后序遍历结果:3 2 5 6 4 1
3.3前序遍历
void PreOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}//前序遍历 根 左子树 右子树printf("%d ", root->data);PreOrder(root->left);PreOrder(root->right);
}
3.4中序遍历
void InOrder(BTNode* root) {if (root == NULL) {printf("NULL ");return;}//中序遍历 左子树 根 右子树InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}
3.5后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}//后序遍历 左子树 右子树 根PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}
四、二叉树的其他求法
以下解法所用的是递归法
4.1求二叉树的结点个数
int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
4.2求二叉树的深度/高度
int TreeHeight(BTNode* root)
{if (root == NULL)return 0;int leftHeight = TreeHeight(root->left);int rightHeight = TreeHeight(root->right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
4.3求二叉树的第k层节点个数
int TreeKLevel(BTNode* root, int k)
{assert(k > 0);if (root == NULL)return 0;if (k == 1)return 1;return TreeKLevel(root->left, k - 1)+ TreeKLevel(root->right, k - 1);
}
五、结尾
如果有什么建议和疑问,或是有什么错误,希望大家可以在评论区提一下。
希望大家以后也能和我一起进步!!
如果这篇文章对你有用的话,希望能给我一个小小的赞!
相关文章:

非线性表之堆的实际应用和二叉树的遍历
目录 前言:前一篇我已经介绍过了二叉树和堆的介绍和相关代码的实现 一、堆的实现 1.1堆向上调整算法 1.2堆向下调整算法 二、堆的应用 2.1堆的排序 2.2TOP-K问题 三、二叉树的遍历 3.1 二叉树的创建 3.2遍历介绍 3.3前序遍历 3.4中序遍历 3.5后序遍历 …...
os.path库学习之splitext函数
os.path库学习之splitext函数 一、简介 os.path.splitext 是 Python 标准库 os.path 模块中的一个函数,用于将文件名分割成两部分:文件名和扩展名。这个函数非常有用,特别是在处理文件路径和文件扩展名时。 二、语法和参数 语法: os.path…...
Python知识点:如何使用Sqlmap进行SQL注入测试
使用 Sqlmap 进行 SQL 注入测试是一个非常有效的方法,它可以帮助你自动化地检测和利用 SQL 注入漏洞。以下是使用 Sqlmap 进行 SQL 注入测试的详细步骤: 1. 安装 Sqlmap 首先,你需要安装 Sqlmap。Sqlmap 是一个 Python 工具,因此…...
Android Gradle开发与应用 (一) : Gradle基础
Gradle基础 Gradle 是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化构建工具。它使用一种基于 Groovy 的特定领域语言(DSL)来声明项目设置,而不是传统的 XML。Gradle 提供了灵活的构建脚本和强大的依赖管理功能,使其成为…...
Linux驱动开发—设备树分析:GPIO,中断,时钟信息,CPU信息
书接上回:Linux驱动开发—设备树基本概念,语法详解-CSDN博客 文章目录 使用设备树描述中断使用设备树描述CPU节点CPU 节点缓存节点总结 使用设备树描述时钟总结 使用设备树描述GPIO示例设备树节点逐行解析GPIO 单元 使用设备树描述中断 在NXP 官方中截…...
Java全栈解密:从JVM内存管理到Spring框架,揭秘垃圾回收、类加载机制与Web开发精髓的全方位旅程
JVM内存划分 在JVM中,每个线程有自己的虚拟机栈,而整个JVM实例共享一些内存区域。JVM的内存划分主要包括四个部分:程序计数器、虚拟机栈、堆区和方法区(元数据区)。 程序计数器:程序计数器用于存储当前线程…...

【探索Linux】P.46(高级IO —— 五种IO模型简介 | IO重要概念)
阅读导航 引言一、五种IO模型1. 阻塞IO(1)定义(2)特点 2. 非阻塞IO(1)定义(2)特点 3. IO多路复用(1)定义(2)特点 4. 信号驱动IO&#…...

【MongoDB 】MongoDB 介绍及应用,设计到4个案例
MongoDB 介绍概述 基础概念 MongoDB 是非关系型数据库,也就是nosql,存储json数据格式会非常灵活,要比数据库mysql/MariaDB更好,同时也能为mysql/MariaDB分摊一部分的流量压力。 对于经常读写的数据他会存入内存,如此…...
AI浪潮下的程序员生存指南:如何在智能时代锻造不可替代的核心竞争力
人工智能时代,程序员如何保持核心竞争力? 随着AIGC(如chatgpt、midjourney、claude等)大语言模型接二连三的涌现,AI辅助编程工具日益普及,程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工…...

Journyx soap_cgi.pyc接口XML外部实体注入漏洞复现 [附POC]
文章目录 Journyx soap_cgi.pyc接口XML外部实体注入漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现Journyx soap_cgi.pyc接口XML外部实体注入漏洞复现 [附POC] 0x01 前言 免责声明:请勿利用文章内的相关技术…...

vue 日期控件 100天内的时间禁用不允许选择
vue 日期控件 100天内的时间禁用不允许选择,可以从101天选起 比如,2024年8月9号开始,100天内禁止选择,第101天之后的日期可以选,效果如图所示 // 日期控件代码 加上 :picker-options"pickerOptions" <…...
服务器HTTP响应头安全性优化与漏洞修复方案
在对服务器进行漏洞扫描后,通常会发现一些常见的安全漏洞,特别是涉及HTTP响应头的问题。以下是本次扫描过程中发现的漏洞问题以及对应的修复方案 1.X-Content-Type-Options 响应头缺失 描述: 缺失此响应头可能导致浏览器错误地解析资源类型,存在MIME类型混淆攻击的风险。 …...

4.定时器(TIMER)
理论 预分频寄存器(TIMx_PSC):由于时钟源为:72MHz,T 1/f 1/72MHz,由于不好计算周期时间,则需要分频,若分72则T 1/1MHz 1us(1MHz 一百万秒) 计数方式:向上(递增到某个数触发中断)、向下(递…...

java springboot mqtt控制海康摄像头
GHHKControlService 接口 package org.gh.ghhk.service;public interface GHHKControlService {boolean monitorControl(String payload);}GHHKControlServiceImpl 实现类 package org.gh.ghhk.service.impl;import com.alibaba.fastjson.JSONArray; import com.alibaba.…...
AI大模型02:Prompt Engineering 提示工程
一、什么是提示工程(Prompt Engineering) 1.提示工程,也叫“指令工程” (1)Prompt 就是我们给大模型发送的指令,或者说是在聊天对话框中发送的内容。 Prompt是AGI时代的编程语言。 Prompt是去控制大模型的…...

EasyExcel动态表头导出
1、封装方法 package com.skybird.iot.base.utils;import cn.hutool.core.util.StrUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.support.ExcelTypeEnum; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.w…...

可视化基础的设计四大原则
一个好的数据可视化设计可以帮助观众迅速理解数据背后的意义。然而,如何确保我们的可视化设计既美观又简单易懂呢?本文将介绍四大设计原则——亲密原则、对比原则、对齐原则和重复原则。 1、 亲密原则(Proximity) 定义与应用&am…...

MySQL基础练习题27-上升的温度
目录 题目 准备数据 分析数据 总结 题目 找出与之前(昨天的)日期相比温度更高的所有日期的 id 。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Weather (id int, recordDate date, temperature int);#…...

只出现一次的数字 II
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 示例 1: 输入:nums [2,2,3,2]…...

第十一章 数据仓库和商务智能 10分
11.1.0语境关系图 11.1 Q 建立数据仓库,有哪些步骤?如何建设?【6 个步骤非常重要!必须知道】 1. 理解需求(P)(目的明确,ETL) (1) 考虑业务目标和业务战略。 (2) 确定业…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
用js实现常见排序算法
以下是几种常见排序算法的 JS实现,包括选择排序、冒泡排序、插入排序、快速排序和归并排序,以及每种算法的特点和复杂度分析 1. 选择排序(Selection Sort) 核心思想:每次从未排序部分选择最小元素,与未排…...