【数据结构】AVLTree

news2024/10/6 15:40:12

1.AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数G.M.Adelson-Velskii 和E.M.Landis1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是 AVL
左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1)

2.AVLTree节点的定义

我们这里直接实现KV模型的AVL树,为了方便后续的操作,这里将AVL树中的结点定义为三叉链结构,并在每个结点当中引入平衡因子(右子树高度-左子树高度)。除此之外,还需编写一个构造新结点的构造函数,由于新构造结点的左右子树均为空树,于是将新构造结点的平衡因子初始设置为0即可。

template<class K, class V>
struct AVLTreeNode
{
	//三叉链
	AVLTreeNode<K, V>* _left;    //该节点的左孩子
	AVLTreeNode<K, V>* _right;   //该节点的右孩子
	AVLTreeNode<K, V>* _parent;  //该节点的父节点

	pair<K, V> _kv;
	int _bf;             //节点的平衡因子:右子树高度-左子树高度

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};

//AVLTree的定义
template<class K,class V>
class AVLTree
{
typedef AVLTreeNode<K,V> Node;

private:

Node* _root=nullptr;
}

3.插入数据

插入数据分为三步:

  1. 按照二叉搜索树的插入方法,找到待插入位置。
  2. 找到待插入位置后,将待插入结点插入到树中。
  3. 更新平衡因子,如果出现不平衡,则需要进行旋转。

因为AVLTree是二叉搜索树所以插入数据后也要保证这颗树是二叉搜索树,插入数据时依旧按照二叉所搜树的数据插入规则:

1.插入的数据比父节点大就往父节点的右边走

2.插入的数据比父节点小就往父节点的左边走

3.插入的数据和父节点相等时插入失败

如此进行下去,直到找到与待插入结点的key值相同的结点判定为插入失败,或者最终走到空树位置进行结点插入。走到插入的位置后new一个新的节点,如果这个节点的值比父节点的值大就插入到父节点的右边否则插入到父节点的左边。插入新的节点后AVLTree的结构可能遭到破坏,所以此时就要更新平衡因子,并且检测树的结构是否遭到了破坏。如果树的结构遭到了破坏还需要进行调整工作。

新节点插入后,父节点的平衡因子一定需要调整,在插入之前,父节点的平衡因子分为三种情况:-1,0, 1, 插入节点后分以下两种情况:

 1. 如果新节点插入到父节点的左侧,只需给父节点的平衡因子-1即可

 2. 如果新节点插入到父节点的右侧,只需给父节点的平衡因子+1即可
由于一个结点的平衡因子是否需要更新,是取决于该结点的左右子树的高度是否发生了变化,因此插入一个结点后,该结点的祖先结点的平衡因子可能需要更新。

每更新完一个结点的平衡因子后,都需要进行以下判断:

如果parent的平衡因子等于-1或者1,表明还需要继续往上更新平衡因子。
如果parent的平衡因子等于0,表明无需继续往上更新平衡因子了。
如果parent的平衡因子等于-2或者2,表明此时以parent结点为根结点的子树已经不平衡了,需要进行旋转处理。

插入元素后parent的平衡因子为0说明该节点之前的平衡因子为-1 或者 1说明该节点的左边或者右边有一个孩子,调整完平衡因子为0是将该节点的另一边给补齐了,此时树的整体高度是没有变化的,就不用继续调整平衡因子了。

 如果parent的平衡因子等于-1或者1,表明还需要继续往上更新平衡因子。说明之前的节点的平衡因子为0,此时节点多了一个孩子这棵树的整体高度就增加了,需要继续向上调整新增节点的祖先节点平衡因子直到根节点。

 我们将新插入的位置定义为cur,在向上调整平衡因子时:

cur=prarent;

parent=parent->_parent;

当走到某一步时parent的平衡因子为+-2时说明此时树的结构已经不符合AVL树的性质了,此时需要对树进行旋转

  1. 当parent的平衡因子为-2,cur的平衡因子为-1时,进行右单旋。
  2. 当parent的平衡因子为-2,cur的平衡因子为1时,进行左右双旋。
  3. 当parent的平衡因子为2,cur的平衡因子为-1时,进行右左双旋。
  4. 当parent的平衡因子为2,cur的平衡因子为1时,进行左单旋。

左单旋:

 操作的内容:

  1. 让subR的左子树作为parent的右子树。
  2. 让parent作为subR的左子树。
  3. 让subR作为整个子树的根。
  4. 更新平衡因子。

可以进行这样操作的原因是:

  1. subR的左子树当中结点的值本身就比parent的值大,因此可以作为parent的右子树。
  2. parent及其左子树当中结点的值本身就比subR的值小,因此可以作为subR的左子树。

代码:

void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		//1、建立subR和parent之间的关系
		parent->_parent = subR;
		subR->_left = parent;

		//2、建立parent和subRL之间的关系
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//3、建立parentParent和subR之间的关系
		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr; //subR的_parent指向需改变
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else //parent == parentParent->_right
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		//4、更新平衡因子
		subR->_bf = parent->_bf = 0;
	}

右单旋:

操作内容:

  1. 让subL的右子树作为parent的左子树。
  2. 让parent作为subL的右子树。
  3. 让subL作为整个子树的根。
  4. 更新平衡因子。

可以进行这样操作的原因是:

  1. subL的右子树当中结点的值本身就比parent的值小,因此可以作为parent的左子树。
  2. parent及其右子树当中结点的值本身就比subL的值大,因此可以作为subL的右子树。

代码实现:

void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		//1、建立subL和parent之间的关系
		subL->_right = parent;
		parent->_parent = subL;

		//2、建立parent和subLR之间的关系
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//3、建立parentParent和subL之间的关系
		if (parentParent == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else //parent == parentParent->_right
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}

		//4、更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

左右双旋:

 步骤:

  1. 以subL为旋转点进行左单旋。
  2. 以parent为旋转点进行右单旋。
  3. 更新平衡因子。

代码实现:

void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf; 

		//1、以subL为旋转点进行左单旋
		RotateL(subL);

		//2、以parent为旋转点进行右单旋
		RotateR(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 0)  //该节点就是新增的节点
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

右左双旋:

步骤:

  1. 以subR为旋转点进行右单旋。
  2. 以parent为旋转点进行左单旋。
  3. 更新平衡因子。

代码实现:

void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		//1、以subR为轴进行右单旋
		RotateR(subR);

		//2、以parent为轴进行左单旋
		RotateL(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else if (bf == 0)//该节点就是新增的节点
		{
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 0;
		}
		else
		{
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

 综上AVLTree的插入函数为:

bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr) //若AVL树为空树,则插入结点直接作为根结点
		{
			_root = new Node(kv);
			return true;
		}
		//1、按照二叉搜索树的插入方法,找到待插入位置
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first < cur->_kv.first) //待插入结点的key值小于当前结点的key值
			{
				//往该结点的左子树走
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first) //待插入结点的key值大于当前结点的key值
			{
				//往该结点的右子树走
				parent = cur;
				cur = cur->_right;
			}
			else //待插入结点的key值等于当前结点的key值
			{
				//插入失败(不允许key值冗余)
				return false;
			}
		}

		//2、将待插入结点插入到树中
		cur = new Node(kv); //根据所给值构造一个新结点
		if (kv.first < parent->_kv.first) //新结点的key值小于parent的key值
		{
			//插入到parent的左边
			parent->_left = cur;
			cur->_parent = parent;
		}
		else //新结点的key值大于parent的key值
		{
			//插入到parent的右边
			parent->_right = cur;
			cur->_parent = parent;
		}

		//3、更新平衡因子,如果出现不平衡,则需要进行旋转
		while (cur != _root) //最坏一路更新到根结点
		{
			if (cur == parent->_left) //parent的左子树增高
			{
				parent->_bf--; //parent的平衡因子--
			}
			else if (cur == parent->_right) //parent的右子树增高
			{
				parent->_bf++; //parent的平衡因子++
			}
			//判断是否更新结束或需要进行旋转
			if (parent->_bf == 0) //更新结束(新增结点把parent左右子树矮的那一边增高了,此时左右高度一致)
			{
				break; //parent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
			}
			else if (parent->_bf == -1 || parent->_bf == 1) //需要继续往上更新平衡因子
			{
				//parent树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == -2 || parent->_bf == 2) //需要进行旋转(此时parent树已经不平衡了)
			{
				if (parent->_bf == -2)
				{
					if (cur->_bf == -1)
					{
						RotateR(parent); //右单旋
					}
					else //cur->_bf == 1
					{
						RotateLR(parent); //左右双旋
					}
				}
				else //parent->_bf == 2
				{
					if (cur->_bf == -1)
					{
						RotateRL(parent); //右左双旋
					}
					else //cur->_bf == 1
					{
						RotateL(parent); //左单旋
					}
				}
				break; //旋转后就一定平衡了,无需继续往上更新平衡因子(旋转后树高度变为插入之前了)
			}
			else
			{
				assert(false); //在插入前树的平衡因子就有问题
			}
		}

		return true; //插入成功
	}

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* parentParent = parent->_parent;

		//1、建立subR和parent之间的关系
		parent->_parent = subR;
		subR->_left = parent;

		//2、建立parent和subRL之间的关系
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//3、建立parentParent和subR之间的关系
		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr; //subR的_parent指向需改变
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else //parent == parentParent->_right
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		//4、更新平衡因子
		subR->_bf = parent->_bf = 0;
	}
	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* parentParent = parent->_parent;

		//1、建立subL和parent之间的关系
		subL->_right = parent;
		parent->_parent = subL;

		//2、建立parent和subLR之间的关系
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		//3、建立parentParent和subL之间的关系
		if (parentParent == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subL;
			}
			else //parent == parentParent->_right
			{
				parentParent->_right = subL;
			}
			subL->_parent = parentParent;
		}

		//4、更新平衡因子
		subL->_bf = parent->_bf = 0;
	}
	//左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1

		//1、以subL为旋转点进行左单旋
		RotateL(subL);

		//2、以parent为旋转点进行右单旋
		RotateR(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 0)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}
	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		//1、以subR为轴进行右单旋
		RotateR(subR);

		//2、以parent为轴进行左单旋
		RotateL(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			subRL->_bf = 0;
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 1;
		}
		else if (bf == 0)
		{
			subRL->_bf = 0;
			parent->_bf = 0;
			subR->_bf = 0;
		}
		else
		{
			assert(false); //在旋转前树的平衡因子就有问题
		}
	}

4.树的遍历

AVLTree的遍历与二叉搜索树类似,为了不暴露根节,所以在类中封装一个函数

public:
 
void InOrder()
{
    _InOrder(_root);
}

private:

void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);

	}

5. AVLTree的性能分析

AVL树是一棵绝对平衡的二叉搜索树,其要求每个结点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即logN。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此,如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但当一个结构经常需要被修改时,AVL树就不太适合了。
 

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

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

相关文章

会话与会话技术(Session)

前言 Cookie将用户的信息保存在各自的浏览器中&#xff0c;并且可以在多次请求下实现数据的共享。但是如果当传递的信息较多时&#xff0c;Cookie技术会增大服务器程序处理的难度&#xff0c;因此&#xff0c;Servlet提供了另一种会话技术————Session&#xff0c;Session可…

跟李沐学AI——实用机器学习(入门版)

机器学习目录 2.1 探索性数据分析 2.2 数据清理 2.3 数据变换 2.4 特征工程 2.5 数据科学家的日常 Stanford University Practical machine learning 2.1 探索性数据分析 ​ 对目标的ftr数据进行处理&#xff0c;针对不同的信息做出不同的图形 输出数据集的行数和列数以及前十…

ArcGIS Pro地理空间数据处理完整工作流实训及python技术融合

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

1、TI335x环境建立

记录裸机开发&#xff0c;TI A8系列处理器的AM335x过程&#xff0c;本次开发的是3352&#xff0c;在此基础上开发。 1、硬件准备&#xff1a; 已经测试调试ok的3352测试板&#xff0c;经过查看发现&#xff0c;am3352引出的下载接口是JTAG口&#xff0c;而我手里只有Jlink&…

Kubernetes---Pod调度、标签、配额、策略

静态pod 静态pod由user直接创建调用&#xff0c;不能迁移 由kebelet守护进程直接管理的pod&#xff0c;无需APIserver监管 kubelet监视每个静态pod 静态pod永远绑定到一个指定节点上的kubelet 静态pod spec不能引用其他API对象 静态pod配置路径/var/lib/kubelet/config.yaml里面…

CSS背景相关属性

一、背景颜色 属性名&#xff1a;background-color (bgc) 属性值&#xff1a;颜色取值&#xff1a;关键字&#xff0c;rgb表示法&#xff0c;rgba表示法&#xff0c;十六进制表示。 注&#xff1a; 背景颜色默认透明&#xff1a;rgba&#xff08;0&#xff0c;0&#xff0c…

C语言积锦

代码区&#xff1a;text 代码执行二进制码&#xff08;程序指令&#xff09; 具有共享、只读特性 数据区&#xff1a;1.初始化数据区data 2.未初始化数据区 bss 3.常量区 栈区&#xff1a;系统为每一个程序分配一个临时的空间 局部变量、函数信息、函数参数、数组。栈区大…

干货|Graphpad Prism也能做ERP图?So Easy!

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 不知道你是否注意过这样一个现象。 在心理学大会报告的时候&#xff0c;专家经常会说一句话&#xff1a; 这个结果很漂亮&#xff01;&#xff01;&#xff01; 第一次听见的时候&#x…

K8s图形化管理工具Dasboard部署及使用

文章目录 一、Dashboard简介二、Dashboard部署安装三、配置Dashboard登入用户1、通过Token令牌登入2、通过kubeconfig文件登入 四、Dashboard创建容器 一、Dashboard简介 Kubernetes Dashboard是一个Web UI&#xff0c;用于管理Kubernetes集群中的应用程序和资源。它提供了一个…

使用wireshark抓包理解tcp协议和tls

首先下载安装wireshark 打开软件 1.选则自己连结的网络&#xff1b; 此时就会捕获的数据 2.加上端口过滤。 3.启动一个本地的http服务(这里采用的nodejs)&#xff1b; // server.js import koa from koa; const app new koa(); app.use(ctx > {ctx.body "hell…

托福听力专项 // Unit1 Listening for Main Ideas // Practice with Lectures // 共4篇

目录 Listening for Main Idea Lecture A a music class 单词 内容总结 Lecture B 单词 错题分析 Lecture C 单词 错题分析 Lecture D 单词 Listening for Main Idea Lecture A a music class 单词 evolve(v)to develop slowlyincorporate(v)to take in or includ…

Spring Boot使用(基础)

目录 1.Spring Boot是什么? 2.Spring Boot使用 2.1Spring目录介绍 2.2SpringBoot的使用 1.Spring Boot是什么? Spring Boot就是Spring脚手架,就是为了简化Spring开发而诞生的 Spring Boot的优点: 1.快速集成框架,提供了秒级继承各种框架,提供了启动添加依赖的功能 2.内…

修炼汇编语言第一章:汇编基础知识概述

目录 前言 一、汇编语言的组成 二&#xff1a;存储器 三&#xff1a;指令和数据 四&#xff1a;存储单元 五&#xff1a;CPU对存储器的读写 地址总线 控制总线 数据总线 前言 汇编语言是数据结构&#xff0c;操作系统&#xff0c;微机原理等重要课程的基础&#xff0…

【算法】冒泡排序

一.冒泡排序 主要思想&#xff1a; 反复交换相邻的元素&#xff0c;使较大的元素 逐渐冒泡到数组的末尾&#xff0c;从而实现排序的效果 实现过程&#xff1a; 1.遍历待排序数组&#xff0c;比较相邻的元素&#xff0c;如果前面的元素比后面的元素大&#xff0c; 就交换这两…

系统集成项目管理工程师 笔记(第八章:项目进度管理)

文章目录 8.1 规划项目进度管理 2938.1.1 规划项目进度管理的输入 2938.1.2 规划项目进度管理的工具与技术 2948.1.3 规划项目进度管理的输出 295 8.2 定义活动 2968.2.1 定义活动的输入 2968.2.2 定义活动的工具与技术 2968.2.3 定义活动的输出&#xff08;两清单、一属性&…

【深度学习】学习率与学习率衰减详解:torch.optim.lr_scheduler用法

【深度学习】学习率与学习率衰减详解&#xff1a;torch.optim.lr_scheduler用法 文章目录 【深度学习】学习率与学习率衰减详解&#xff1a;torch.optim.lr_scheduler用法1. 介绍1.1 学习率与学习率衰减 2. TensorFlow中的学习率衰减3. PyTorch中的学习率衰减2.1 optimizer 综述…

md/分类/信号领域/数字信号处理及MATLAB实现/频率调制(FM).md

文章目录 本文链接https://zh.wikipedia.org/wiki/频率调制用Python模拟FM/PM调制解调过程波形变化频率调制我的 本文链接 打死他 调频&#xff08;英语&#xff1a;Frequency Modulation&#xff0c;缩写&#xff1a;FM&#xff09;是一种以载波的瞬时频率变化来表示信息的方…

Java文件操作必备技能,10个小技巧让你快速掌握!

前言 在我们日常的开发中&#xff0c;文件操作是一个非常重要的主题。文件读写、文件复制、任意位置读写、缓存等技巧都是我们必须要掌握的。在这篇文章中&#xff0c;我将给你们介绍 10 个实用的文件操作技巧。 使用 try-with-resources 语句处理文件 IO 流&#xff0c;确保在…

C++Vector类详解

目录 1.Vector介绍 2.Vector的常见使用 2.1 vector构造函数 2.2 vector iterator使用 2.3 vector空间增长问题 2.4 vector增删改查 2.5 vector迭代器失效问题 3.Vector深度剖析及模拟实现 3.1 模拟实现&#xff08;可跳过&#xff09; 3.2 不使用memcpy剖析 1.Vector介绍 ve…

【LeetCode】106. 从中序与后序遍历序列构造二叉树

1.问题 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1 输入&#xff1a;inorder [9,3,15,20,7], postorder [9,15,7,20,3] 输出&#…