C++进阶知识 AVL树实现

news2024/10/3 4:29:08

AVL树

  • 1. AVL的概念
  • 2. AVL树的实现
    • 2.1 AVL树的结构
    • 2.2 AVL树的插⼊
      • 2.2.1 AVL树插⼊⼀个值的⼤概过程
      • 2.2.2 平衡因⼦更新
    • 2.3 旋转
      • 2.3.1 旋转的原则
      • 2.3.2 右单旋
      • 2.3.4 左单旋
      • 2.3.5 左右双旋
  • 3.代码实现

1. AVL的概念

• AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右⼦树都是AVL树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树,通过控制⾼度差去控制平衡。
• AVL树得名于它的发明者G.M.Adelson-Velsky和E.M.Landis是两个前苏联的科学家,他们在1962
年的论⽂《An algorithm for he organization of information》中发表了它。
• AVL树实现这⾥我们引⼊⼀个平衡因⼦(balance factor)的概念,每个结点都有⼀个平衡因⼦,任何结点的平衡因⼦等于右⼦树的⾼度减去左⼦树的⾼度,也就是说任何结点的平衡因⼦等于0/1/-1,AVL树并不是必须要平衡因⼦,但是有了平衡因⼦可以更⽅便我们去进⾏观察和控制树是否平衡,就像⼀个⻛向标⼀样。
• 思考⼀下为什么AVL树是⾼度平衡搜索⼆叉树,要求⾼度差不超过1,⽽不是⾼度差是0呢?0不是更好的平衡吗?画画图分析我们发现,不是不想这样设计,⽽是有些情况是做不到⾼度差是0的。⽐如⼀棵树是2个结点,4个结点等情况下,⾼度差最好就是1,⽆法作为⾼度差是0
• AVL树整体结点数量和分布和完全⼆叉树类似,⾼度可以控制在logN ,那么增删查改的效率也可
以控制在O(logN) ,相⽐⼆叉搜索树有了本质的提升。

在这里插入图片描述


在这里插入图片描述

2. AVL树的实现

2.1 AVL树的结构

struct AVLTreeNode
{
	// 需要parent指针,后续更新平衡因子可以看到
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf; // balance factor

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

2.2 AVL树的插⼊

2.2.1 AVL树插⼊⼀个值的⼤概过程

  1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊。
  2. 新增结点以后,只会影响祖先结点的⾼度,也就是可能会影响部分祖先结点的平衡因⼦,所以更新从新增结点->根结点路径上的平衡因⼦,实际中最坏情况下要更新到根,有些情况更新到中间就可以停⽌了,具体情况我们下⾯再详细分析。
  3. 更新平衡因⼦过程中没有出现问题,则插⼊结束
  4. 更新平衡因⼦过程中出现不平衡,对不平衡⼦树旋转,旋转后本质调平衡的同时,本质降低了⼦树的⾼度,不会再影响上⼀层,所以插⼊结束。

2.2.2 平衡因⼦更新

更新原则:
• 平衡因⼦=右⼦树⾼度-左⼦树⾼度
• 只有⼦树⾼度变化才会影响当前结点平衡因⼦。
• 插⼊结点,会增加⾼度,所以新增结点在parent的右⼦树,parent的平衡因⼦++,新增结点在
parent的左⼦树,parent平衡因⼦–
• parent所在⼦树的⾼度是否变化决定了是否会继续往上更新
更新停⽌条件:
• 更新后parent的平衡因⼦等于0,更新中parent的平衡因⼦变化为-1->0或者1->0,说明更新前
parent⼦树⼀边⾼⼀边低,新增的结点插⼊在低的那边,插⼊后parent所在的⼦树⾼度不变,不会
影响parent的⽗亲结点的平衡因⼦,更新结束。
• 更新后parent的平衡因⼦等于1或-1,更新前更新中parent的平衡因⼦变化为0->1或者0->-1,说明更新前parent⼦树两边⼀样⾼,新增的插⼊结点后,parent所在的⼦树⼀边⾼⼀边低,parent所在的⼦树符合平衡要求,但是⾼度增加了1,会影响parent的⽗亲结点的平衡因⼦,所以要继续向上更新。
• 更新后parent的平衡因⼦等于2或-2,更新前更新中parent的平衡因⼦变化为1->2或者-1->-2,说
明更新前parent⼦树⼀边⾼⼀边低,新增的插⼊结点在⾼的那边,parent所在的⼦树⾼的那边更⾼
了,破坏了平衡,parent所在的⼦树不符合平衡要求,需要旋转处理。
旋转的⽬标有两个:
1、把parent⼦树旋转平衡。
2、降低parent⼦树的⾼度,恢复到插⼊结点以前的⾼度。所以旋转后也不需要继续往上更新,插⼊结束。


更新到10结点,平衡因⼦为2,10所在的⼦树已经不平衡,需要旋转处理

在这里插入图片描述

更新到中间结点,3为根的⼦树⾼度不变,不会影响上⼀层,更新结束

在这里插入图片描述

最坏更新到根停⽌

在这里插入图片描述

2.3 旋转

2.3.1 旋转的原则

  1. 保持搜索树的规则
  2. 让旋转的树从不满⾜变平衡,其次降低旋转树的⾼度
    旋转总共分为四种,左单旋/右单旋/左右双旋/右左双旋。
    说明:下⾯的图中,有些结点我们给的是具体值,如10和5等结点,这⾥是为了⽅便讲解,实际中是什么值都可以,只要⼤⼩关系符合搜索树的规则即可。

2.3.2 右单旋

• 本图1展⽰的是10为根的树,有a/b/c抽象为三棵⾼度为h的⼦树(h>=0),a/b/c均符合AVL树的要求。10可能是整棵树的根,也可能是⼀个整棵树中局部的⼦树的根。这⾥a/b/c是⾼度为h的⼦树,是⼀种概括抽象表⽰,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体图2/图3/图4/
图5进⾏了详细描述。
• 在a⼦树中插⼊⼀个新结点,导致a⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平衡因⼦从-1变成-2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树左边太⾼了,需要往右边旋转,控制两棵树的平衡。
• 旋转核⼼步骤,因为5<b⼦树的值<10,将b变成10的左⼦树,10变成5的右⼦树,5变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2,符合旋转原则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了。

在这里插入图片描述
图1

在这里插入图片描述
图2
在这里插入图片描述
图3
在这里插入图片描述
图4

在这里插入图片描述
图5

2.3.4 左单旋

• 本图6展⽰的是10为根的树,有a/b/c抽象为三棵⾼度为h的⼦树(h>=0),a/b/c均符合AVL树的要
求。10可能是整棵树的根,也可能是⼀个整棵树中局部的⼦树的根。这⾥a/b/c是⾼度为h的⼦树,
是⼀种概括抽象表⽰,他代表了所有右单旋的场景,实际右单旋形态有很多种,具体跟上⾯左旋类
似。
• 在a⼦树中插⼊⼀个新结点,导致a⼦树的⾼度从h变成h+1,不断向上更新平衡因⼦,导致10的平
衡因⼦从1变成2,10为根的树左右⾼度差超过1,违反平衡规则。10为根的树右边太⾼了,需要往
左边旋转,控制两棵树的平衡。
• 旋转核⼼步骤,因为10<b⼦树的值<15,将b变成10的右⼦树,10变成15的左⼦树,15变成这棵树新的根,符合搜索树的规则,控制了平衡,同时这棵的⾼度恢复到了插⼊之前的h+2,符合旋转原则。如果插⼊之前10整棵树的⼀个局部⼦树,旋转后不会再影响上⼀层,插⼊结束了。

在这里插入图片描述

图6

2.3.5 左右双旋

通过图7和图8可以看到,左边⾼时,如果插⼊位置不是在a⼦树,⽽是插⼊在b⼦树,b⼦树⾼度从h变成h+1,引发旋转,右单旋⽆法解决问题,右单旋后,我们的树依旧不平衡。右单旋解决的纯粹的左边⾼,但是插⼊在b⼦树中,10为跟的⼦树不再是单纯的左边⾼,对于10是左边⾼,但是对于5是右边⾼,需要⽤两次旋转才能解决,以5为旋转点进⾏⼀个左单旋,以10为旋转点进⾏⼀个右单旋,这棵树这棵树就平衡了。

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

• 图7和图8分别为左右双旋中h0和h1具体场景分析,下⾯我们将a/b/c⼦树抽象为⾼度h的AVL
⼦树进⾏分析,另外我们需要把b⼦树的细节进⼀步展开为8和左⼦树⾼度为h-1的e和f⼦树,因为
我们要对b的⽗亲5为旋转点进⾏左单旋,左单旋需要动b树中的左⼦树。b⼦树中新增结点的位置
不同,平衡因⼦更新的细节也不同,通过观察8的平衡因⼦不同,这⾥我们要分三个场景讨论。
• 场景1:h>=1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1并为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为-1,旋转后8和5平衡因⼦为0,10平衡因⼦为1。
• 场景2:h>=1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为1,旋转后8和10平衡因⼦为0,5平衡因⼦为-1。
• 场景3:h==0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新5->10平衡因⼦,引发旋转,其中8的平衡因⼦为0,旋转后8和10和5平衡因⼦均为0。
在这里插入图片描述

图9

3.代码实现

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		// 链接父亲
		cur->_parent = parent;

		// 控制平衡
		// 更新平衡因子
		while (parent)
		{
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;

			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}


		return true;
	}

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

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* pParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (pParent->_left == parent)
			{
				pParent->_left = subL;
			}
			else
			{
				pParent->_right = subL;
			}

			subL->_parent = pParent;
		}

		subL->_bf = 0;
		parent->_bf = 0;
	}

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

		Node* parentParent = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
		if (parentParent == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (parent == parentParent->_left)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}

		parent->_bf = subR->_bf = 0;
	}

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

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == -1)
		{
			subLR->_bf = 0;
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 1)
		{
			subLR->_bf = 0;
			subL->_bf = -1;
			parent->_bf = 0;
		}
		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;
		RotateR(parent->_right);
		RotateL(parent);
		if (bf == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	int Height()
	{
		return _Height(_root);
	}

	int Size()
	{
		return _Size(_root);
	}

	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
			return 0;

		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	bool _IsBalanceTree(Node* root)
	{
		// 空树也是AVL树
		if (nullptr == root)
			return true;
		// 计算pRoot结点的平衡因子:即pRoot左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		// 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
		// pRoot平衡因子的绝对值超过1,则一定不是AVL树
		if (abs(diff) >= 2)
		{
			cout << root->_kv.first << "高度差异常" << endl;
			return false;
		}

		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		// pRoot的左和右如果都是AVL树,则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}
private:
	Node* _root = nullptr;
};

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

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

相关文章

Linux Debian12使用Podman安装bwapp靶场环境

一、bwapp简介 bWAPP&#xff08;buggy Web Application&#xff09;是一个开源的、故意设计有漏洞的Web应用程序&#xff0c;旨在帮助安全爱好者、开发人员和学生发现和防止Web漏洞。它包含了超过100种不同的漏洞&#xff0c;涵盖了所有主要的已知Web漏洞。 二、bwapp下载 …

C++系列-多态

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 多态 多态就是不同类型的对象&#xff0c;去做同一个行为&#xff0c;但是产生的结果是不同的。 比如说&#xff1a; 都是动物叫声&#xff0c;猫是喵喵&#xff0c;狗是汪汪&am…

安装图片标识工具anylabeling

目录 下载压缩包 创建环境 安装opencv 安装第三方库 运行setup.py文件 安装过程可能会出现的错误&#xff1a; 错误1 错误2 安装完成 图标更换 之前提到的嵌入式开发】可编程4k蓝牙摄像头点击器还可以训练模型&#xff0c;使图像识别精度提高 现在讲解&#xff0c;如…

wsl(4) -- 编译驱动模块

1. 内核源码 编译模块需要内核源码信息&#xff0c;wsl是修改过的内核无法使用下面的命令从标准镜像源上下载内核源码信息。 sudo apt-get install kernel-headers-$(uname -r) sudo apt-get install kernel-devel-$(uname -r)2. 下载wsl内核源码 可以考虑下载wsl的源码重新…

【分页】Spring Boot 列表分页 + javaScript前台展示

后端&#xff1a; 准备好查询实体与分页实体 1、分页工具实体 package com.ruoyi.dms.config;import com.alibaba.nacos.api.model.v2.Result; import lombok.Data;import java.io.Serializable; import java.util.List;/*** author 宁兴星* description: 列表返回结果集*/ …

信息学奥赛复赛复习09-CSP-J2020-03表达式求值前置知识点-中缀表达式求值、模运算、模运算性质、栈

PDF文档回复:20241002 **1 P1981 [NOIP2013普及组] 表达式求值 ** [题目描述] 给定一个只包含加法和乘法的算术表达式&#xff0c;请你编程计算表达式的值 [输入格式] 一行&#xff0c;为需要你计算的表达式&#xff0c;表达式中只包含数字、加法运算符 “” 和乘法运算符 …

C/C++语言基础--C++IO流、输入输出流、文件流、字符串流、重定向流等详解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 流思想&#xff0c;我认为在计算机中是一个很重要的思想&#xff0c;因为计算机、编程无非就是获取数据&#xff0c;然后对数据进行操作&#xff1b;C给主要给我们提供了3种流&#xff0c;输入输出流、文件流…

react-问卷星项目(4)

项目实战 使用CSS 尽量不要使用内联CSS 内联style代码多&#xff0c;性能差&#xff0c;扩展性差外链css文件可复用代码&#xff0c;可单独缓存文件 元素内联style 和HTMl元素的style相似必须用JS写法&#xff0c;不能是字符串&#xff0c;里面必须是对象 <span style…

# VirtualBox中安装的CentOS 6.5网络设置为NAT模式时,怎么使用SecureCRT连接CentOS6.5系统?

VirtualBox中安装的CentOS 6.5网络设置为NAT模式时&#xff0c;怎么使用SecureCRT连接CentOS6.5系统&#xff1f; 一、查询 【VirtualBox Host-Only Network】虚拟网卡的网络配置 IP。 1、按键盘上WIN R 组合键&#xff0c;打开【运行】&#xff0c;输入【 ncpa.cpl 】&…

C0012.Clion改用VS编译器开发Qt界面

1.VS编译器添加 2.配置MSVC2019环境变量 3.各种问题报错与解决 问题描述 warning C4819&#xff1a;该文件包含不能在当前代码页(936)中表示的字符。解决办法 在CMakeLists.txt中添加如下代码 # 如下代码只在使用VS编译器时需要&#xff0c;使用mingw32编译器时需要注释掉 #…

利用Numpy实现全连接神经网络实验分析

一、实验要求 用 python 的 numpy 模块实现全连接神经网络。网络结构为一个输入层、一个隐藏层、一个输出层。隐藏层的激活函数为 Relu 函数&#xff0c;输出层的激活函数为 softmax 函数&#xff0c;损失函数为交叉熵。 二、实验目的 学会构建一个简单的全连接神经网络模型学…

鸿蒙网络管理模块01——HTTP与WebSocket请求数据

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1、概述 鸿蒙的网络管理模块主要提供以下功能&#xff1a; HTTP数据请求&#xff1…

影刀RPA实战:网页爬虫之电影数据

1.实战目标 电影自媒体是指个人或团队通过互联网平台&#xff0c;如微博、微信公众号、抖音、B站等&#xff0c;发布与电影相关的内容&#xff0c;包括但不限于电影评论、推荐、幕后制作揭秘、明星访谈等。这些内容旨在吸引电影爱好者&#xff0c;并与之互动&#xff0c;构建起…

十六字心传

中国文化传统中著名的“十六字心传”&#xff1a;“人心惟危&#xff0c;道心惟微&#xff1b;惟精惟一&#xff0c;允执厥中。 ”语出于《虞书大禹谟》。 人心与道心&#xff1a;人的人欲与天理的区别&#xff1b;所谓“人心惟危”&#xff0c;即是说人们的那种物欲情欲&…

【FPGA开发】Xilinx FPGA差分输入时钟的使用方法

正文 以前在使用ZYNQ的领航者ZYNQ7020进行FPGA学习时&#xff0c;它们使用的单端50M的输入时钟&#xff0c;在verlog代码编写上比较简单&#xff0c;而现在使用Alinx的AXU3EG开发板时&#xff0c;发现它使用的是200M的差分输入时钟&#xff0c;哪这个时候&#xff0c;输入时钟要…

pyecharts-快速入门

pyecharts文档&#xff1a;渲染图表 - pyecharts - A Python Echarts Plotting Library built with love. pyecharts-gallery文档&#xff1a;中文简介 - Document (pyecharts.org) 一、快速入门案例 from pyecharts.charts import Barbar Bar() bar.add_xaxis(["衬衫…

7-3 集合的运算-并、交、对称差

顺序表&#xff1a; #include <bits/stdc.h> using namespace std; int main() {int n;cin>>n;int *pnew int[n];for(int i0;i<n;i)cin>>p[i];int m;cin>>m;int *qnew int [m];for(int j0;j<m;j)cin>>q[j];int *bingnew int[nm];int *jia…

详细介绍:API 和 SPI 的区别

文章目录 Java SPI (Service Provider Interface) 和 API (Application Programming Interface) 的区别详解目录1. 定义和目的1.1 API (Application Programming Interface)1.2 SPI (Service Provider Interface) 2. 使用场景2.1 API 的应用场景2.2 SPI 的应用场景 3. 加载和调…

Elasticsearch基础_5.ES聚合功能

文章目录 一、数据聚合1.1、桶聚合1.1.1、单维度桶聚合1.1.2、聚合结果排序1.1.3、限定聚合范围 本文只记录ES聚合基本用法&#xff0c;后续有更复杂的需求可以查看相关书籍&#xff0c;如《Elasticsearch搜索引擎构建入门与实战》 一、数据聚合 聚合可以让我们极其方便的实现…

进程和线程之间的通用方式

进程之间的通信方式有哪些 进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;是指不同进程之间传递信息和数据的机制。由于进程之间的内存空间是相互独立的&#xff0c;因此必须使用特定的通信方式来实现数据共享。 以下是常见的进程间通信方式&#xff1…