数据结构:构建完全二叉查找树

文章目录

    • 1、步骤 1: 对给定数组排序
    • 2、步骤 2: 递归构建完全二叉查找树
    • 3、注意
    • 4、在有序数组中寻找根结点位置
    • 5、代码实现
    • 6、其他方法?
      • 基本思路
      • 插入操作
      • 删除操作
      • 特别考虑

  对于一个给定序列的二叉查找树,有很多种,但是完全二叉查找树只有一种,因为树形是确定的,我们按照中根序列遍历的顺序也是按照数值大小顺序的,因此 按照树结点的中根遍历顺序 将 序列按照数值大小顺序 填入这颗树形确定的完全二叉树中,可以得到一颗唯一确定的完全二叉树。并且对于根结点,其左子树的所有结点都小于根结点,右子树的所有结点都大于等于根结点。
  构建一颗 完全二叉查找树(Complete Binary Search Tree,CBST)的算法关键在于如何保证二叉树的完全性质,同时维护二叉查找树(BST)的特性:对于任意节点,其左子树的所有节点的值小于该节点的值,其右子树的所有节点的值大于等于该节点的值。

以下是构建完全二叉查找树的一种有效算法:

1、步骤 1: 对给定数组排序

由于二叉查找树的中序遍历结果是一个有序数组,**首先要对输入的无序数组进行排序。**根结点的位置是可以唯一确定的。

2、步骤 2: 递归构建完全二叉查找树

  1. 选择中间元素作为树的根:从排序好的数组中选择中间的元素作为二叉查找树的根节点。如果有两个中间元素,选择它们中的任意一个。
  2. 递归构建左子树和右子树:将根节点左侧的元素递归地构建成左子树,将根节点右侧的元素递归地构建成右子树。

3、注意

  • 完全二叉树的定义是除了最后一层外,每一层都是完全填满的,而最后一层从左向右填充。上述算法构建的是一棵高度平衡的二叉查找树,但不一定满足完全二叉树的严格定义。构建满足完全二叉树定义的二叉查找树需要更复杂的逻辑来确保所有层(除了可能的最后一层)都完全填满。
  • 确保完全性的一个方法是通过计算给定元素数量所能构建的完全二叉树的最大高度,然后在构建过程中控制树的形态,使其满足完全二叉树的性质。

4、在有序数组中寻找根结点位置

在这里插入图片描述

完全二叉树的性质:

  • 性质的数学公式换算:
    • 树的结点个数n 取对数 可以得到树高: Treeheight=log2(n)
    • 通过树高 可以得到 除最后一层之外的右子树结点个数:rightTree1=pow(2,Treeheight-1)-1
    • 通过树高 以及 树的结点个数 可以得到树的最后一层结点个数:lastLevelNodes=n-(pow(2,Treeheight)-1)
    • 最后可以得到 右子树结点个数:rightTree=rightTree1+((lastLevelNodes-pow(2,Treeheight-1))>0?(lastLevelNodes-pow(2,Treeheight-1)) :0)
  • 直接求满了的层的总结点个数:
    • 利用快速幂思想,即取出总结点数n的二进制位数的最高位。
    • 然后计算即可得到右子树的个数

5、代码实现

  • 数学性质
#include<bits/stdc++.h>
using namespace std;struct TreeNode {int val;TreeNode *left, *right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};// 构建完全二叉查找树
TreeNode* createTree(int start, int end, vector<int>& sortedValues) {if (start > end) return nullptr;// 计算完全二叉树的最后一层前的所有节点数和最后一层的节点数int totalNodes = end - start + 1;int fullLevels = log2(totalNodes + 1);int lastLevelNodes = totalNodes - (pow(2, fullLevels) - 1);int leftSubtreeNodes = pow(2, fullLevels - 1) - 1 + min((int)pow(2, fullLevels - 1), lastLevelNodes);// 根据左子树的节点数确定根节点int rootIndex = start + leftSubtreeNodes;TreeNode* root = new TreeNode(sortedValues[rootIndex]);root->left = createTree(start, rootIndex - 1, sortedValues);root->right = createTree(rootIndex + 1, end, sortedValues);return root;
}// 层次遍历
void traversal(TreeNode* root) {if (!root) return;queue<TreeNode*> q;q.push(root);while (!q.empty()) {TreeNode* current = q.front(); q.pop();cout << current->val << " ";if (current->left) q.push(current->left);if (current->right) q.push(current->right);}
}int main() {int n;cin >> n;vector<int> values(n);for (int& value : values) {cin >> value;}sort(values.begin(), values.end());TreeNode* root = createTree(0, n - 1, values);traversal(root);return 0;
}
  • 直接计算
#include<bits/stdc++.h>
using namespace std;
struct TreeNode{TreeNode * left=nullptr;TreeNode * right=nullptr;int val;TreeNode (int v=0):val(v) {}
};
TreeNode * createTree(int left,int right,vector<int> & baby){//O(nlogn)if(left>right) return nullptr;if(left==right) return new TreeNode(baby[left]);TreeNode * root=nullptr;int flag=right-left+2;int b=1;while(flag!=1){//O(logn) 每次建树logn,n个结点共nlognb*=2;flag>>=1;}int rightTree_size=b/2-1;if(rightTree_size+2-b-b/2>0){rightTree_size+=rightTree_size+2-b-b/2;}root=new TreeNode(baby[right-rightTree_size]);root->left=createTree(left,right-rightTree_size-1,baby);root->right=createTree(right-rightTree_size+1,right,baby);return root;
}
int main(void){ios_base::sync_with_stdio(false);cin.tie(0);int n;cin>>n;vector<int> baby;for(int i=0;i<n;++i){int j;cin>>j;baby.emplace_back(j);}sort(baby.begin(),baby.end());//O(nlogn)createTree(0,n-1,baby);return 0;
}

6、其他方法?

动态构建完全二叉查找树(CBST)比静态构建更为复杂,因为需要在插入和删除操作发生时持续保持树的完全二叉树性质。一种方法是在每次插入或删除后重建树,但这显然效率不高。另一种思路是通过维护额外的信息和采用特定的插入/删除策略来尽可能保持树的完全性。下面是一个较为高效的动态构建完全二叉查找树的策略:

基本思路

动态维护一个完全二叉查找树,主要挑战在于插入和删除操作。对于插入操作,需要找到树中最后一个节点并在适当的位置插入新节点以保持完全二叉树的性质。对于删除操作,则需要找到可以替换被删除节点的节点(通常是树中的最后一个节点)并调整树以保持其完全性质。

插入操作

  1. 计算完全二叉树的深度:基于树当前的节点数量,可以计算出树的深度。
  2. 定位插入点:根据完全二叉树的性质,新节点应当被插入为最后一个节点。可以通过深度和节点数定位到这个位置。
  3. 执行插入:在定位到的位置插入新节点。如果需要,调整树的结构以保持二叉查找树的性质。

删除操作

  1. 定位并删除目标节点:找到需要删除的节点,将其删除。如果这个节点不是最后一个节点,则需要找到最后一个节点。
  2. 用最后一个节点替换删除的节点(如果被删除的节点不是最后一个节点):将最后一个节点移动到被删除节点的位置。
  3. 调整树:可能需要对树进行调整,以保持二叉查找树的性质。

特别考虑

  • 这种方法要求你能快速定位到树的最后一个节点,这可能需要维护额外的信息,如每层的节点数。
  • 插入和删除操作可能导致需要重新平衡树,以保持二叉查找树的性质。
  • 动态维护完全二叉树的复杂度主要在于定位最后一个节点和维护二叉查找树的性质。

动态地维护一个完全二叉查找树在实践中较为罕见,因为它要求在每次操作后都严格保持完全二叉树的性质,这可能导致较高的复杂性和成本。通常,人们会使用其他类型的自平衡二叉搜索树(如AVL树、红黑树)来获得较好的平均性能,尽管这些树不保证完全性质。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sql注入方式之联合注入

1.1 靶场环境 系统centos7 IP地址192.168.1.24 1.2 联合注入原理 联合查询注入是联合两个表进行注入攻击&#xff0c;使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同&#xff0c;不然会出现报错。 1.3 找注入点 找注入点&#xff0c;当输入id1 an…

flutter多入口点entrypoint

native中引擎对象本身消耗内存(每个引擎对象约莫消耗42MB内存) 多引擎&#xff1a;native多引擎>启动>flutter多入口点entrypoint>多main函数>多子包元素集>多(子)程序 单引擎(复用)&#xff1a;native单引擎>复用启动>flutter多入口点entrypoint>多m…

CDN加速原理那些事

名词解释 CNAME记录&#xff08;CNAME record&#xff09; CNAME即别名( Canonical Name )&#xff1b;可以用来把一个域名解析到另一个域名&#xff0c;当 DNS 系统在查询 CNAME 左面的名称的时候&#xff0c;都会转向 CNAME 右面的名称再进行查询&#xff0c;一直追踪到最后…

计算机网络:数据链路层 - CSMA/CD协议

计算机网络&#xff1a;数据链路层 - CSMA/CD协议 媒体接入控制CSMA/CD协议截断二进制指数退避算法帧长与帧间间隔信道利用率 媒体接入控制 如图所示&#xff0c;这是一根同轴电缆&#xff0c;有多台主机连接到这根同轴电缆上&#xff0c;他们共享这根传输媒体&#xff0c;形成…

微信小程序-接入sse数据流并实现打字机效果( ChatGPT )

从流中获取的数据格式如下 小程序调用SSE接口 const requestTask wx.request({url: xxx, // 需要请求的接口地址enableChunked: true, // enableChunked必须为truemethod: "GET",timeout: 120000,success(res) {console.log(res.data)},fail: function (error) {//…

安卓远离手机app

软件介绍 远离手机是专门为防止年轻人上瘾而打造的生活管理类的软件,适度用手机&#xff0c;保护眼睛&#xff0c;节约时间。 下载 安卓远离手机app

【汇编】_Visual Studio2019写32位汇编

目录 第一步&#xff1a;创建新项目 1. 空项目—下一步 2. 选择位置—填写项目名—创建 第二步&#xff1a;项目生成依赖项 1. 右击项目名—生成依赖项—生成自定义 2. 选中masm—确定 第三步&#xff1a;创建源文件 1. 源文件—添加—新建项 2. 选择C文件—创建新文件…

11-新热文章-实时计算

热点文章-实时计算 1 今日内容 1.1 定时计算与实时计算 1.2 今日内容 kafkaStream 什么是流式计算 kafkaStream概述 kafkaStream入门案例 Springboot集成kafkaStream 实时计算 用户行为发送消息 kafkaStream聚合处理消息 更新文章行为数量 替换热点文章数据 2 实时…

多线程原理详解01(程序、进程、线程介绍,线程创建的三种方式(Thread、Runnable、Callable)、三种方式各自实现多线程的具体操作、代码解析)

目录 多线程原理详解01_线程简介多任务多线程程序、进程、线程Process&#xff08;进程&#xff09;与 Thread &#xff08;线程&#xff09;总结&#xff1a; 02_线程创建三种方式&#xff1a;1、继承 Thread 类1-1&#xff1a;通过继承Thread类实现多线程演示代码 1-2&#x…

如果用大模型考公,kimi、通义千问谁能考高分?

都说大模型要超越人类了&#xff0c;今天就试试让kimi和通义千问做公务员考试题目&#xff0c;谁能考高分&#xff1f; 测评结果再次让人震惊&#xff01; 问题提干&#xff1a;大小两种规格的盒装鸡蛋&#xff0c;大盒装23个&#xff0c;小盒装16个&#xff0c;采购员小王买了…

LangChain入门:14.LLMChain:最简单的链的使用

摘要 本文将介绍LangChain库中LLMChain工具的使用方法。LLMChain将提示模板、语言模型&#xff08;LLM&#xff09;和输出解析器整合在一起&#xff0c;形成一个连贯的处理链&#xff0c;简化了与语言模型的交互过程。我们将探讨LLMChain的技术特点、应用场景以及它解决的问题…

Day84:服务攻防-端口协议桌面应用QQWPS等RCEhydra口令猜解未授权检测

目录 端口协议-口令爆破&未授权 弱口令爆破 FTP&#xff1a;文件传输协议 RDP&#xff1a;Windows远程桌面协议 SSH&#xff1a;Linux安全外壳协议 未授权案例(rsync) 桌面应用-QQ&WPS&Clash QQ RCE 漏洞复现 WPS RCE 漏洞复现 Clas* RCE 漏洞复现 知识点…

Android图形显示架构概览

图形显示系统作为Android系统核心的子系统&#xff0c;掌握它对于理解Android系统很有帮助&#xff0c;下面从整体上简单介绍图形显示系统的架构&#xff0c;如下图所示。 这个框架只包含了用户空间的图形组件&#xff0c;不涉及底层的显示驱动。框架主要包括以下4个图形组件。…

线程间的通信

文章目录 线程间的通讯技术就是通过等待和唤醒机制&#xff0c;来实现多个线程协同操作完成某一项任务&#xff0c;例如经典的生产者和消费者案例。等待唤醒机制其实就是让线程进入等待状态或者让线程从等待状态中唤醒&#xff0c;需要用到两种方法&#xff0c;如下&#xff1a…

如何使用群晖Synology Drive结合cpolar内网穿透实现同步Obsidian笔记文件

文章目录 一、简介软件特色演示&#xff1a; 二、使用免费群晖虚拟机搭建群晖Synology Drive服务&#xff0c;实现局域网同步1 安装并设置Synology Drive套件2 局域网内同步文件测试 三、内网穿透群晖Synology Drive&#xff0c;实现异地多端同步Windows 安装 Cpolar步骤&#…

【webrtc】源码下载与编译

目录 下载 下依赖 参考文章 &#xff1a; 下载 (1) windows ,centos上都会报错 &#xff08;2&#xff09; ubuntu A : 在git上设置代理 B fetch通过 ubuntu的界面 proxy设置了代理 这将会拉取webRTC源码&#xff0c;且额外加了android相关的依赖&#xff0c;例如And…

go语言学习--3.常用语句

目录 1.条件语句 1.1 if语句 1.2 if-else语句 1.3 switch语句 1.4 select语句 2.循环语句 2.1循环处理语句 2.2循环控制语句 3.go语言关键字 1.条件语句 和c语言类似&#xff0c;相关的条件语句如下表所示&#xff1a; 1.1 if语句 if 布尔表达式 {/* 在布尔表达式为 t…

关于JVM-三色标记算法剖析

相关系列 深入理解JVM垃圾收集器-CSDN博客 深入理解JVM垃圾收集算法-CSDN博客 深入理解jvm执行引擎-CSDN博客 jvm优化原则-CSDN博客 jvm流程图-CSDN博客 三色标记产生的原因&#xff1f; 在并发标记的过程中&#xff0c;因为标记期间应用线程还在继续跑&#xff0c;对象间的引…

postgresql发布和订阅

一、发布订阅介绍 发布和订阅使用了pg的逻辑复制的功能&#xff0c;通过发布端创建publication与表绑定&#xff0c;订阅端创建subscription同时会在发布端创建逻辑复制槽实现逻辑复制功能 逻辑复制基于 发布&#xff08;Publication&#xff09; 与 订阅&#xff08;Subscri…

使用aspose相关包将excel转成pdf 并导出

SpringBoot 项目 基于aspose相关jar包 将excel 转换成pdf 导出 1、依赖的jar包 &#xff0c; jar获取链接 aspose相关三方jar &#xff0c;下载解压后,在项目路径下建一个libs包&#xff0c;然后将下图两个jar 拷贝至刚新建的libs目录中 2、pom.xml中加入maven引入 <depend…