某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)

news2024/12/25 14:01:58

声明:下面的方法不一定正确,个人学习过程中所写,并未做大量测试,仅供参考,应该有BUG,暂时不改了。

描述:某数据库为提升搜索效率,对某一整型字段构建二叉搜索树(BST)。每个结点包含两个数据信息: 1)结点的数据,2) 该结点子树的元素数目。为了压缩搜索树规模,该数据库为每个结点增加一个字段,该字段用于存储中序遍历时在访问该结点之前访问的结点数据。在该改进下,被存储的结点如果为叶子结点,该结点在新树中将被删除,以提高存储效率。如果一个叶子结点无中序后继,则无需删除。

给定该BST的先序遍历(第二个字段未给出),请编写程序,输出经过压缩后新BST的先序遍历结果。

 输入格式
输入共两行:第一行为一个int数据n,表示该BST的总结点个数。1<=n<=100000:第二行为n个int数据,为该BST的先序遍历结果(保证顺序是正确的,并保证数据两两不同)。 每个数据的范围:0<=X<=1*10^7。

输出格式
输出共一行:第一行为新BST的先序遍历结果,依序输出结点数据以及保存的其他结点数据(若没有则输出字符-)

 

 

#include <iostream>
#include <vector>
#include <map>
#include <stack>

using namespace std;

struct Node
{
    int val, childNodes = 0;
    Node* left, *right;
    int left_type = 0;//类型标志
	int right_type = 0;//类型标志
    Node(int val, int childNodes):val(val),childNodes(childNodes),left(nullptr), right(nullptr) {}
};


// 根据先序遍历,创建一个二叉搜索树
Node* createBstFromPreOrder(vector<int>& preOrderNums, int left, int right) {
    if (left > right) {
        return nullptr;
    }
    Node* root = new Node(preOrderNums[left], right - left);
    // 如果没有找到,mid为最右边值大1,这样左边的右边界为mid-1刚好就是数组最右边
    int mid = right+1;
    for (int i = left + 1; i <= right; i++) {
        if (preOrderNums[i] > preOrderNums[left]) {
            mid = i;
            break;
        }
    }
    root->left = createBstFromPreOrder(preOrderNums, left+1, mid-1);
    root->right = createBstFromPreOrder(preOrderNums, mid, right);
    return root;
}

// 线索化时的前一个结点
Node* pre = nullptr;
map<int, int> mp_delete;

//中序对二叉树进行线索化
void InThreading(Node* p){
    //如果当前结点存在
    if (p) {
        InThreading(p->left);//递归当前结点的左子树,进行线索化
        //如果当前结点没有左孩子,左标志位设为1,左指针域指向上一结点 pre
        if (!p->left) {
            p->left_type = 1;
            p->left=pre;
        }
        //如果 pre 没有右孩子,右标志位设为 1,右指针域指向当前结点。
        if (pre&&!pre->right) {
            pre->right=p;
            pre->right_type = 1;
            p->childNodes = pre->val;
            // 表示需要删除的节点
            cout << "需要删除的节点:" << pre->val << endl; 
            mp_delete[pre->val] = 1;
        }
        pre=p;//pre指向当前结点
        InThreading(p->right);//递归右子树进行线索化
    }
}

void preorderBst(Node *root)
{
    if(root != NULL)
    {
        Node *p = root;
        while (p != NULL)
        {
            while (p->left_type == 0) // 左指针不是线索,则边访问边左移
            {
                cout << p->val << " " << p->childNodes << endl; // 访问结点
                p = p->left; // 左移,访问左子树
            }
            cout << p->val << " " << p->childNodes << endl; // 此时p左必为线索,但还没有被访问,则访问
            p = p->right; // 此时p左孩子不存在,则右指针若非空,则不论是否为线索都指向其后继
        }
    }
}

Node *Next(Node *t) //已知节点t找t的"后继"结点位置 
{   
	if(t->right_type==1) //右标志为1,可以直接得到"后继"结点 
	{
		t=t->right;
	 } 
	 else /*右标志为0,不能直接的到"后继"结点,
	        则需要找到右子树最左下角的节点*/ 
	{
	 	t=t->right;
		 while(t->left_type==0)
		{
		 	t=t->left;
		 } //while
	}//else
	return t;
} 

// 改方法用来遍历线索二叉树,当删除相关节点之后,线索被取消了,这个方法不能用
void InorderTraverse(Node *temp)//利用线索实现中序遍历 
{
	if(!temp)
	{
		return;
	}
	
	while(temp->left_type==0)//查找第一个节点 
	{               //因为二叉树的创建creat是以先序遍历序列创建,所以t所指向的第一个结点并不是中序遍历所要访问的第一个结点 
		temp=temp->left;
	}
	cout << "val:" << temp->val << " left_type:" << temp->left_type 
             << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
	while(temp->right)// 此处以"t的右孩子不为空"为循环条件,是因为,先前设定了最后一个结点的"后继"为空,表示结束 
	{               //根据线索访问后续结点 
		temp=Next(temp);
		cout << "val:" << temp->val << " left_type:" << temp->left_type 
             << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
	}
} 

// 用mp记录第一次出现时,用来向左走时判断,如果之前出现过,则不向走了,表是左边已经走过了
map<int, int> mp;
// 删除相关节点,将线索取消掉
void deleteBstNode(Node* root) {
    if (root == nullptr) return;
    stack<Node*> stk;
    Node* p = root;
    while (p != nullptr || stk.size()) {
        // 当没有出现过,且左边有孩子
        while(p->left && p->left->left_type == 0 && mp[p->val] == 0) {
            stk.push(p);
            mp[p->val] = 1;
            p = p->left;
        }
        // 如果左边的孩子拥有前驱结点,则取消前驱标志
        if (p->left && p->left->left_type == 1 && p->left->left && mp[p->val] == 0) {
            stk.push(p);
            mp[p->val] = 1;
            // 将其中序的前一个结点置为-1,标志为“-”
            p->childNodes = -1;
            p = p->left;
            p->left = nullptr;
            p->left_type = 0;
        }
        // 表示左边需要删除
        else if (p->left && mp[p->val] == 0) {
            cout << "删除了---" << p->left->val << endl;  //2
            p->left->right = nullptr;
            p->left = nullptr;
        }
        // 如果右边没有了,或者刚好右边有一个需要删除
        if((p->right == nullptr && stk.size()) || (p->right && p->right->right_type == 1)) {
            while ((p->right == nullptr && stk.size()) || (p->right && p->right->right_type == 1)) {
                // 右边需要删除
                if (p->right && p->right->right_type == 1) {
                    cout << "删除了+++" << p->right->val << endl; // 5
                    p->right->right = nullptr;
                    p->right = nullptr;
                } else if(p->right){
                    // 不需要删除
                    p = p->right;
                    break;
                }
                p = stk.top();
                stk.pop();
            }
            // 不满足上述条件时,将p右移动
            if (p->right && p->right->right_type == 0) {
                p = p->right;
            } 
        } else if (p->right && p->right->right_type == 0) {
            p = p->right;
        } else {
            // 最后不满足时一定要返回
            return;
        }
    }
   
}

// 二叉树递归先序遍历
void preOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    cout << root->val << " " << root->childNodes << " " << endl;;
    preOrder(root->left);
    preOrder(root->right);
}

// 二叉树递归中序遍历
void inOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    inOrder(root->left);
    cout << root->val << " " << root->childNodes << " " << endl;;
    inOrder(root->right);
}

// 二叉树递归后续遍历
void postOrder(Node* root) {
    if (root == nullptr) {
        return;
    }
    postOrder(root->left);
    postOrder(root->right);
    cout << root->val << " " << root->childNodes << " " << endl;;
}

// 下面这个访问线索二叉树方法有问题
// void inOrderTraverse(Node* root)
// {
// 	//从根节点开始先找到最左边
// 	if (root == NULL)
// 	{
// 		return;
// 	}
// 	Node* temp = root;
// 	//先找到最左边结点 然后根据线索化直接向右遍历
// 	while (temp != NULL && temp->left_type == 0)
// 	{
// 		temp = temp->left;
// 	}
// 	while (temp != NULL)
// 	{
// 		//输出
//         cout << "val:" << temp->val << " left_type:" << temp->left_type 
//              << " right_type:" << temp->right_type << " childNodes:" << temp->childNodes << endl;
// 		temp = temp->right;
// 	}
// }

int main() {
    vector<int> nums = {6, 4, 2, 5, 9, 7, 8};
    Node * head = createBstFromPreOrder(nums, 0, nums.size()-1);
    preOrder(head);
    cout << "=====================================" << endl;
    inOrder(head);
    cout << "=====================================" << endl;
    postOrder(head);
    cout << "=====================================" << endl;
    // 线索化二叉树
    InThreading(head);
    // 删除相关节点
    deleteBstNode(head);
    // 遍历二叉树
    preOrder(head);
    cout << "=====================================" << endl;
    cin.get();
    return 0;
}

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

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

相关文章

07、vue : 无法加载文件 C:\Users\JH\AppData\Roaming\npm\vue.ps1,因为在此系统上禁止运行脚本。

目录 问题解决&#xff1a; 问题 vue : 无法加载文件 C:\Users\JH\AppData\Roaming\npm\vue.ps1&#xff0c;因为在此系统上禁止运行脚本。 在使用 VSCode 时&#xff0c;创建 Vue 项目报的错 创建不了 Vue 项目 解决&#xff1a; 因为在此系统上禁止运行该脚本&#xff0…

[ 云计算 | AWS 实践 ] 使用 Java 列出存储桶中的所有 AWS S3 对象

本文收录于【#云计算入门与实践 - AWS】专栏中&#xff0c;收录 AWS 入门与实践相关博文。 本文同步于个人公众号&#xff1a;【云计算洞察】 更多关于云计算技术内容敬请关注&#xff1a;CSDN【#云计算入门与实践 - AWS】专栏。 本系列已更新博文&#xff1a; [ 云计算 | …

利用工业视频AI分析,能否消除企业生产安全隐患?

讲个去年11月的案例&#xff0c;某家工厂发生火灾&#xff0c;直接导致38人死亡和2人受伤。然后在今年2月&#xff0c;某家新能源汽车公司发生机械伤害事故&#xff0c;导致1人死亡。4月&#xff0c;一家船厂发生生产安全事故&#xff0c;造成7人死亡和5人受伤。5月&#xff0c…

5.4 可靠传输的工作原理

思维导图&#xff1a; 5.4 可靠传输的工作原理 前言概述 TCP与IP层的关系&#xff1a;TCP负责发送报文段&#xff0c;而IP层负责传送这些报文段。IP层仅提供“尽最大努力服务”&#xff0c;本质上是不可靠的传输。TCP的责任&#xff1a;为了弥补IP层的不可靠性&#xff0c;TC…

springboot的请求与响应

一&#xff0c;简单参数 Get请求&#xff1a;只需要在postman中的Params参数与方法中的形参一致就可以Post请求与Get方法一致只需要在 如果参数名不一致--通过RequestParam中的value属性执行请求参数名 RequestParam(name "name",required false) //表示name参数不是…

安装VMware后无VMnet1和VMnet8网卡

问题描述 通过进入 按键盘&#xff08;WINR&#xff09;&#xff0c;输入cmd&#xff0c;进入管理员页面 输入ipconfig,没有vmnet1和vmnet8 进入电脑--->更改适配器&#xff08;也无vmnet1和vmnet8&#xff09; 一&#xff0c;解决方法1 直接还原vmware网络设置。 还原成…

WIN10专业版64位22H2正式版19045.3324 简体中文版、英文版

微软在面向 Win10 21H2/22H2 发布了累积更新 KB5029244 之外&#xff0c;在更新日志中也表示&#xff0c;也为包括 1809&#xff0c;1607 和 1507 在内的功能更新提供了累积更新。 IT之家在此附上 Win10 Build 19044.3324 和 Build 19045.3324 更新内容如下&#xff1a; 修复了…

【行云流水线实践】基于“OneBuild”方法对镜像进行快速装箱 | 京东云技术团队

在云原生领域&#xff0c;无论使用哪种编排调度平台&#xff0c;Kubernetes&#xff0c;DockerSwarm&#xff0c;OpenShift等&#xff0c;业务都需要基于镜像进行交付&#xff0c;我们在内部实践“Source-to-image”和链式构建&#xff0c;总而总结出“OneBuild”模式。 其核心…

HT3163 免电感滤波 音频功率放大器工作原理

HT3163是一款G效AB/D类音频功率放大器。在D类模式&#xff0c;18V供电、THDN10%条件下&#xff0c;能够持续提供40W/4Ω功率输出。在AB类模式&#xff0c;12V供电、THDN10%条件下&#xff0c;能够持续输出17W/4Ω功率。 HT3163具有防削顶失真&#xff08;ACF&#xff09;输出控…

第02章-变量与运算符

1 关键字 关键字&#xff1a;被Java语言赋予了特殊含义&#xff0c;用作专门用途的字符串&#xff08;或单词&#xff09;。如class、public、static、void等&#xff0c;这些单词都被Java定义好了&#xff0c;称为关键字。 特点&#xff1a;关键字都是小写字母&#xff1b;官…

中国长城-安全防护-硬件,软件,细粒度权限划分-等级保护,人员意识

目录 等级保护 安全防护 中国长城-安全防护 硬件&#xff0c;软件&#xff0c;细粒度权限划分-等级保护&#xff0c;人员意识 等级保护 安全防护 建立安全管理制度&#xff1a;制定信息安全政策&#xff0c;明确安全管理职责&#xff0c;建立安全培训和考核机制&#xff0c…

Mgeo:multi-modalgeographic language model pre-training

文章目录 question5.1 Geographic Encoder5.1.1 Encoding5.1.2 5.2 multi-modal pre-training 7 conclusionGeo-Encoder: A Chunk-Argument Bi-Encoder Framework for Chinese Geographic Re-Rankingabs ERNIE-GeoL: A Geography-and-Language Pre-trained Model and its Appli…

开发第一个flutter app的六个关键步骤

Flutter这些年发展的很快&#xff0c;特别是在 Google 持续的加持下&#xff0c;Flutter SDK 的版本号已经来到了 3开头&#xff0c;也正式开始对 Windows、macOS 和 Linux 桌面环境提供支持。如果从 Flutter 特有的优势来看&#xff0c;我个人认为主要是它已经几乎和原生的性能…

【嵌入式项目应用】__UART自定义通信协议代码实现方法

目录 前言 一、什么是通信协议 二、简单通信协议的问题 三、通信协议的常见内容 1. 帧头 2. 设备地址/类型 3. 命令/指令 4. 命令类型/功能码 5. 数据长度 6. 数据 7.帧尾 8.校验码 四、通信协议代码实现 1. 消息数据发送 a. 通过串口直接发送每一个字节 b. 通过…

16. 机器学习 - 决策树

Hi&#xff0c;你好。我是茶桁。 在上一节课讲SVM之后&#xff0c;再给大家将一个新的分类模型「决策树」。我们直接开始正题。 决策树 我们从一个例子开始&#xff0c;来看下面这张图&#xff1a; 假设我们的x1 ~ x4是特征&#xff0c;y是最终的决定&#xff0c;打比方说是…

十年JAVA搬砖路——Linux搭建Ldap服务器。

1.安装命令 yum -y install openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel2.启动ldap systemctl start slapd systemctl enable slapd3.修改密码 slappasswd Aa123456获得返回的密码加密密码串&#xff1a; {SSHA}DkSw0…

SQLServer数据库透明加密 安当加密

安当TDE透明加密组件是一种用于数据保护的解决方案&#xff0c;它对数据进行加密&#xff0c;以防止未经授权的访问和数据泄露。 以下是安当TDE透明加密组件的主要功能介绍&#xff1a; 数据保护&#xff1a;安当TDE透明加密组件可以对数据库中的敏感数据进行加密&#xff0c;…

软件无线电处理平台解决方案:330-基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;北京太速科…

TypeScript之命名空间与模块

一、模块 TypeScript 与ECMAScript 2015 一样&#xff0c;任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地&#xff0c;如果一个文件不带有顶级的import或者export声明&#xff0c;那么它的内容被视为全局可见的 例如我们在在一个 TypeScript 工程下建立一个…

Linux 网络流量监控利器 iftop命令详解及实战

简介 iftop 是什么 在 Linux 系统下即时监控服务器的网络带宽使用情况&#xff0c;有很多工具&#xff0c;比如 iptraf、nethogs 等等&#xff0c;但是推荐使用小巧但功能很强大的 iftop 工具。 iftop 是 Linux 系统一个免费的网卡实时流量监控工具&#xff0c;类似于 top 命令…