数据结构篇十:红黑树

news2024/9/24 17:51:27

文章目录

  • 前言
  • 1. 红黑树的概念
  • 2. 红黑树的性质
  • 3. 红黑树节点的定义
  • 4. 红黑树的插入
    • 4.1 情况一: cur为红,p为红,g为黑,u存在且为红
    • 4.2 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑。
      • 4.2.1 u不存在
      • 4.2.2 u存在且为黑
    • 4.3 情况三
  • 5. 红黑树的验证
    • 5.1 检测其是否满足二叉搜索树
    • 5.2 检测其是否满足红黑树的性质
  • 6. 红黑树的删除
  • 7. 红黑树与AVL树的比较
  • 8. 代码实现
    • 8.1 RBTree.h
    • 8.2 Test.cpp
  • 9. 总结

前言

  红黑树是解决单支树问题的另一种解决方法,它相比较AVL树减少了调整的次数,AVL是一格绝对平衡的树,而红黑树只要求最长路径不超过最短路径的二倍,相比较大大减少了调整次数。在实际中更多的也是使用红黑树,就比如后面的map和set,我们就是以红黑树进行封装的。

1. 红黑树的概念

  红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
在这里插入图片描述

2. 红黑树的性质

  1. 每个结点不是红色就是黑色 。
  2. 根节点是黑色的 。
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 。
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 。
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)。

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

  按照上面的性质,最短路径应该是全为黑色节点,而在每条路径黑色节点数量相同并且不难出现连续红色节点的的情况下,最长路径应该是一红一黑,红黑相间的方式,所以最长路径是不会超过最短路径的二倍的,

3. 红黑树节点的定义

enum Colour
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	RBTreeNode<K,V>* _left;
	RBTreeNode<K,V>* _right;
	RBTreeNode<K,V>* _parent;
	pair<K, V> _kv;

	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

  思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?

  这是因为有一个性质是要求每条路径的黑色节点数目相同,如果我们将新插入节点的颜色默认给成黑色,就会影响全部路径,那么就会在全局破坏这一性质。而如果我们将节点颜色给成红色,我们可能会破坏不能出现连续的红色节点这一性质,也只是影响了这一条路径,也仅仅只是在局部破坏了红黑树的性质,相比较前一种更容易进行处理。

4. 红黑树的插入

  红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索的树规则插入新节点
bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		
		cur = new Node(kv);
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		
		//调整
		…………
		
  • 检测新节点插入后,红黑树的性质是否造到破坏
      因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论。(约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点)

4.1 情况一: cur为红,p为红,g为黑,u存在且为红

  • 情况一: cur为红,p为红,g为黑,u存在且为红

在这里插入图片描述
  解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整

4.2 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑。

  • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑。
    在这里插入图片描述

  • p为g的左孩子,cur为p的左孩子,则进行右单旋转。

  • p为g的右孩子,cur为p的右孩子,则进行左单旋转。

  • p、g变色–p变黑,g变红。

4.2.1 u不存在

  光看上面的图有人可能会不理解为什么还需要旋转,我们来单独拿出一个例子:
在这里插入图片描述
  当u不存在时,它是违反最长路径超过最短路径的二倍的,因此需要先进行旋转再变色。

4.2.2 u存在且为黑

  先新插入一个节点,目前是情况一,直接变色处理就可以了。
在这里插入图片描述
  处理完后就是此时所讲的情况二: cur为红,p为红,g为黑,u存在且为黑。我们发现此时违反了最长路径不能超过最短路径的二倍,因此需要先旋转,再变色。
在这里插入图片描述
  此时再回归到我们一开始的图解,上下图结合,也就是cur的孩子就是下图中的a
在这里插入图片描述
  这样就可以完美的解决问题了。

4.3 情况三

  • 情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑。

在这里插入图片描述

  • p为g的左孩子,cur为p的右孩子,则针对p做左单旋转。
  • p为g的右孩子,cur为p的左孩子,则针对p做右单旋转。
  • 则转换成了情况2。

  情况三的原理与情况二一致,只不过是换成了双旋的问题。(记忆窍门:单旋是的情况是一条直线,双旋的情况是一条折线)。
在这里插入图片描述

  情况二就是新插入节点是属于直线的情况,只需要旋转一次,情况三是新插入节点属于折线的情况,需要旋转一次变成情况二,再旋转一次解决问题,实际上也就是双旋。(还有一个记忆敲门:左边高往右边旋,右边高往左边旋)
  更具体的情况大家可以像我讲解情况二那样画出详细的图来理解。
  需要格外注意到最后要将根节点改为黑色, 因为在调整过程中有可能将根节点改为红色
  左右子树的旋转在上一篇AVL树中有纤细的讲解,不懂的小伙伴可以去阅读。

5. 红黑树的验证

  红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)。
  2. 检测其是否满足红黑树的性质。

5.1 检测其是否满足二叉搜索树

  写一个中序遍历就可以了。

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

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

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

5.2 检测其是否满足红黑树的性质

  最主要的是检验每条路径的黑色节点数目是否一样,是否有连续的红色节点。
  对于检验每条路径的黑色节点数目是否一样,我们可以先遍历一条路径记录黑色节点的数目,再通过递归遍历每条路径记录黑色节点的数目,当遍历到nullptr时说明一条路径遍历完了,再与我们一开始记录的黑色节点数目进行比较,相同就返回true,不同就返回false。(注意:传参时需要传值传递,不能引用传递,否则在统计黑色节点数目时,左子树的黑色节点数目会累加到右子树上
  对于是否有连续的红色节点,在递归时对于一个节点,我们如果是拿这个节点与它的孩子节点比较会很麻烦,因为要比较两个。我们不如换个思路,与该节点的父亲节点比较,如果该节点为红色,它的父亲也为红色,就返回false。

bool Check(Node* root, int blackNum, const int refVal)
{
	if (root == nullptr)
	{
		//cout << balcknum << endl;
		if (blackNum != refVal)
		{
			cout << "存在黑色节点数量不相等的路径" << endl;
			return false;
		}

		return true;
	}

	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << "有连续的红色节点" << endl;

		return false;
	}

	if (root->_col == BLACK)
	{
		++blackNum;
	}
	
	return Check(root->_left, blackNum, refVal)
		&& Check(root->_right, blackNum, refVal);
}

bool IsBalance()
{
	//树为空认为符合红黑树的性质
	if (_root == nullptr)
		return true;
	
	//根节点必须为黑
	if (_root->_col == RED)
		return false;

	//记录黑色节点数目
	int refVal = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
			refVal++;

		cur = cur->_left;
	}

	int blackNum = 0;

	return Check(_root,blackNum,refVal);
}

6. 红黑树的删除

  红黑树的删除本节不做讲解,有兴趣的小伙伴可参考:《算法导论》或者《STL源码剖析》。

7. 红黑树与AVL树的比较

  红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

8. 代码实现

8.1 RBTree.h

#pragma once
#include<iostream>
#include<vector>
using namespace std;

enum Colour
{
	RED,
	BLACK
};

template<class K,class V>
struct RBTreeNode
{
	RBTreeNode<K,V>* _left;
	RBTreeNode<K,V>* _right;
	RBTreeNode<K,V>* _parent;
	pair<K, V> _kv;

	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

template<class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	bool insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

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

		while (parent && parent->_col == RED)
		{
			Node* uncle = nullptr;
			Node* grandparent = parent->_parent;

			if (parent == grandparent->_left)
			{
				uncle = grandparent->_right;
			}
			else
			{
				uncle = grandparent->_left;
			}

			//叔叔存在且为红
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;

				cur = grandparent;
				parent = cur->_parent;
			}
			//叔叔不存在或者存在且为黑
			else
			{
				if (parent == grandparent->_left && cur == parent->_left)
				{
					//右旋
					RotateR(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else if (parent == grandparent->_left && cur == parent->_right)
				{
					//先左旋,再右旋
					RotateL(parent);
					RotateR(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
				else if (parent == grandparent->_right && cur == parent->_right)
				{
					//左旋
					RotateL(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else
				{
					//先右旋,再左旋
					RotateR(parent);
					RotateL(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
				break;
			}
		}
		_root->_col = BLACK;
		return true;
	}

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

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		parent->_parent = subR;

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

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

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

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

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


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

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

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

	bool Check(Node* root, int blackNum, const int refVal)
	{
		if (root == nullptr)
		{
			//cout << balcknum << endl;
			if (blackNum != refVal)
			{
				cout << "存在黑色节点数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "有连续的红色节点" << endl;

			return false;
		}

		if (root->_col == BLACK)
		{
			++blackNum;
		}

		/*return Check(_root->_left, blackNum, refVal)
			&& Check(_root->_right, blackNum, refVal);*/

		return Check(root->_left, blackNum, refVal)
			&& Check(root->_right, blackNum, refVal);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
			return false;

		int refVal = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				refVal++;

			cur = cur->_left;
		}

		int blackNum = 0;

		return Check(_root,blackNum,refVal);
	}

private:
	Node* _root = nullptr;
};

8.2 Test.cpp

int main()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	RBTree<int, int> t;
	for (auto e : a)
	{
		t.insert(make_pair(e, e));
	}
	t.InOrder();

	//cout << t.IsBalance() << endl;

	return 0;
}

int main()
{
	const int N = 1000;
	vector<int> v;
	v.reserve(N);
	//srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	//size_t begin2 = clock();
	RBTree<int, int> t;
	for (auto e : v)
	{
		if (e == 506)
		{
			int i = 0;
		}
		t.insert(make_pair(e, e));
		cout << "insert:" << e << "->" << t.IsBalance() << endl;
	}
	//size_t end2 = clock();

	//cout << "Insert:" << end2 - begin2 << endl;

	cout << t.IsBalance() << endl;
	return 0;
}

9. 总结

  红黑树比AVL树更加复杂一些,关于二叉树这部分的内容我建议还是多多画图来加深自己的理解,忌讳眼高手低。希望大家都能有所收获。
  如果大家发现有什么错误的地方,可以私信或者评论区指出喔。我会继续深入学习C++,希望能与大家共同进步,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励!!

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

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

相关文章

图论 - Trie树(字符串统计、最大异或对)

文章目录 前言Part 1&#xff1a;Trie字符串统计1.题目描述输入格式输出格式数据范围输入样例输出样例 2.算法 Part 2&#xff1a;最大异或对1.题目描述输入格式输出格式数据范围输入样例输出样例 2.算法 前言 本篇博客将介绍Trie树的常见应用&#xff0c;包括&#xff1a;Trie…

30道python自动化测试面试题(全)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、什么项目适合做自动化测试&#xff1f; 关键字&#xff1a;…

【Pytorch】论文复现 Vision Transformer (ViT)

文章目录 0. 进行设置1. 获取数据2. 创建Dataset和DataLoader3. 复现 ViT 论文&#xff1a;概述4. Equation 1: 将数据拆分为 patch 并创建类、位置和 patch 嵌入5. Equation 2: Multi-Head Attention (MSA)6. Equation 3: Multilayer Perceptron (MLP)7. 创建 Transformer 编码…

【计算机网络_应用层】协议定制序列化反序列化

文章目录 1. TCP协议的通信流程2. 应用层协议定制3. 通过“网络计算器”的实现来实现应用层协议定制和序列化3.1 protocol3.2 序列化和反序列化3.2.1 手写序列化和反序列化3.2.2 使用Json库 3.3 数据包读取3.4 服务端设计3.5 最后的源代码和运行结果 1. TCP协议的通信流程 在之…

每个人都应该知道的AI大模型:通往智能未来的桥梁

人工智能大模型已成为我们通往智能未来的桥梁。这些模型&#xff0c;如OpenAI的GPT-4&#xff0c;不仅是技术的巅峰&#xff0c;更是人类智慧的结晶。在这篇文章中&#xff0c;我们将深入探讨AI大模型的重要性&#xff0c;它们是如何工作的&#xff0c;以及它们对社会的潜在影响…

算法------(13)KMP

例题&#xff1a;&#xff08;1&#xff09;AcWing 831. KMP字符串 。。其实写完也不太理解。。随便写点吧 KMP就是求next数组和运用next的数组的过程。相比传统匹配模式一次更新一单位距离的慢速方法&#xff0c;next数组可以让下表字符串一次更新n - next【n】个距离&#x…

Java项目layui分页中文乱码

【问题描述】这部分没改之前中文乱码。 【解决办法】在layui.js或者layui.all.js文件中替换共、页、条转换成Unicode码格式。 字符Unicode共&#x5171页&#x9875条&#x6761【完美解决】改完之后重新运行项目&#xff0c;浏览器F12缓存清除就好了&#xff0c;右键

从键盘输入5个整数,将这些整数插入到一个链表中,并按从小到大次序排列,最后输出这些整数。

设节点定义如下struct Node {int Element; // 节点中的元素为整数类型struct Node * Next; // 指向下一个节点 }; 从键盘输入5个整数&#xff0c;将这些整数插入到一个链表中&#xff0c;并按从小到大次序排列&#xff0c;最后输出这些整数。注释那段求指出错误&#xff0c;求解…

【QT+QGIS跨平台编译】之六十二:【QGIS_CORE跨平台编译】—【错误处理:未定义类型QgsPolymorphicRelation】

文章目录 一、未定义类型QgsPolymorphicRelation二、解决办法一、未定义类型QgsPolymorphicRelation 报错信息: 错误原因为,使用了未定义类型 QgsPolymorphicRelation 二、解决办法 QgsRelation.h文件中 ①注释第36行: //class QgsPolymorphicRelation;②注释第414行: …

leetcode括号生成

题目描述 解题思路 首先看到题目&#xff0c;一开始是并没有思路的。这时候可以在纸上进行演算一下结果。当只有一对括号的时候&#xff0c;我们可以得知结果[“()”],当有两对括号的时候&#xff0c;我们可以发现&#xff0c;括号在第一个基础上&#xff0c;要么在括号内部出…

Java中心校智慧校园智慧班牌物联网平台源码

目录 智慧班牌 班牌首页 班级信息 课表信息 视频 图片 进离校管理 人脸登录页 学生个人中心 请假管理 成绩管理 家长留言 学生绑卡 学生评价 系统设置 通知管理 值日管理 倒计时 班级德育 班牌模式 1.课堂授课模式 2.家长会签到模式 3.考场模式 4.班级…

MySQL:错误ERROR 1045 (28000)详解

1.问题说明 有时候我们登录Mysql输入密码的时候&#xff0c;会出现这种情况&#xff1a; mysql -u root -p Enter Password > ‘密码’ 错误&#xff1a;ERROR 1045 (28000): Access denied for user ‘root’‘localhost’ (using password: YES) 或者&#xff1a;错误…

本届挑战赛季军方案:构建由大模型辅助的基于多模态数据融合的异常检测、根因诊断和故障报告生成系统

DDopS团队荣获本届挑战赛季军。该团队来自中山大学计算机学院Intelligent DDS 实验室。实验室主要方向为云计算、智能运维(AIOps)、软件定义网络、分布式软件资源管理与优化、eBPF 性能监控与优化等。 选题分析 基于对竞赛数据的洞察和对时代趋势的考量&#xff0c;我们尝试应…

YOLOv9改进|增加SPD-Conv无卷积步长或池化:用于低分辨率图像和小物体的新 CNN 模块

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、文章摘要 卷积神经网络(CNNs)在计算即使觉任务中如图像分类和目标检测等取得了显著的成功。然而&#xff0c;当图像分辨率较低或物体较小时&…

02-设计概述

上一篇&#xff1a;01-导言 本章重点讨论 JNI 中的主要设计问题。本节中的大多数设计问题都与本地方法有关。调用 API 的设计将在第 5 章&#xff1a;调用 API 中介绍。 2.1 JNI 接口函数和指针 本地代码通过调用 JNI 函数来访问 Java 虚拟机功能。JNI 函数可通过接口指针使用…

基于SpringBoot的企业头条管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

springBoot整合Redis(二、RedisTemplate操作Redis)

Spring-data-redis是spring大家族的一部分&#xff0c;提供了在srping应用中通过简单的配置访问redis服务&#xff0c;对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装&#xff0c;RedisTemplate提供了redis各种操作、异常处理及序列化&#xff0c;支持发布订阅&…

4.6.CVAT——带点的注释详细操作

文章目录 1.形状模式下的点2.单点线性插值 使用单个点或包含多个点的形状对任务进行标注的指南。 1.形状模式下的点 它用于面部、地标注释等。 在开始之前&#xff0c;您需要选择 Points .如有必要&#xff0c;您可以在 Number of points 字段中设置固定数量的点&#xff0c;…

并查集(Disjoint Set)

目录 1.定义 2.初始化 3.查找 4.合并 4.1.按秩合并&#xff08;启发式合并&#xff09; 5.例题 题目描述 输入格式 输出格式 输入输出样例 说明/提示 1.定义 并查集&#xff0c;也称为不相交集合数据结构&#xff0c;是一种用于管理元素分组以及查找元素所属组的数…

Julia语言中的元编程

在 Julia 语言中&#xff0c;元编程&#xff08;Metaprogramming&#xff09;可以生成或操作其他代码。这种技术允许程序员在编译时或运行时动态地创建、修改或分析代码&#xff0c;从而增强语言的功能和灵活性&#xff0c;以宏&#xff08;Macros&#xff09;、表达式和符号&a…