DS二叉搜索树

前言

我们在数据结构初阶专栏已经对二叉树进行了介绍并用C语言做了实现,但是当时没有对二叉搜树进行介绍,而是把他放到数据结构进阶构专栏的第一期来介绍,原因是后面的map和set(红黑树)是基于搜索树的,这里介绍完后再去学习他们的成本会低一点!

本期内容介绍

二叉搜索树介绍

二叉搜索树的实现

二叉搜索树的应用

二叉搜索树的性能分析

二叉搜索树的介绍

什么是二叉搜索树?

二叉搜索树又称二叉排序树(走中序就是有序的)二叉查找树他是一颗空树或满足以下性质的二叉树!

1、如果它的左子树不为空,则左子树的所有节点的值都小于根节点的值

2、如果它的右子树不为空,则右子树的所有节点的值都大于根节点的值

3、它的左右子树也必须都为二叉搜索树

一般把二叉搜索树的节点的值叫做键值(key),一个键值唯一标识唯一一个节点!所以一般的key模型的二叉搜索树是不允许修改的(key_value模型仅可以修改value);因为在key模型的二叉搜索树中修改了key值会影响二叉搜索的搜索性,修改后可能就不在符合左边比根节点小,右边比根节点大的性质了!!!

二叉搜索树的实现

由于二叉搜索树是不允许修改键值(key)的,他主要作用是查询,所以没有修改接口

OK,还是和以前一样,先搭个架子出来:我们得有节点的类专门搞节点,然后一个二叉搜索树的类专门负责查找等操作!

template<class K>
struct BSTreeNode
{
	BSTreeNode* _left;
	BSTreeNode* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:

private:
	Node* _root = nullptr;//只有一个成员,给一个缺省参数可以不用自己写构造了
};

下面就是实现插入、查询和删除的接口操作了!

insert

实现思路:

如果是第一次插入根节点为空则直接new一个值为key的节点连接到_root返回即可!

如果不是第一次插入,则从父节点开始查找合适的插入位置并用parent的变量记录合适位置的父节点的值,如果比当前的节点大去右边,否则去左边,直到为空找到了合适的位置,插入!如果是等于说明要插入的值引进存在直接返回

最后找到了插入位置new一个键值为key的节点连接到合适位置的父节点即可!

但是这里要插到做还是右呢?如果key的值比parent的值大插入到右边,否则插入到左边!

bool insert(const K& key)
{
	if (_root == nullptr)//第一次插入
	{
		_root = new Node(key);
		return true;
	}

	Node* parent = nullptr;//记录要插入节点的父节点的位置
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)//要插入的值比当前节点的值小
		{
			parent = cur;
			cur = cur->_left;//去当前节点的左边
		}
		else if (cur->_key < key)//要插入的值比当前节点的值大
		{
			parent = cur;
			cur = cur->_right;//去当前节点的右边
		}
		else
		{
			return false;//要插入的值已经存在
		}
	}

	//找到了要插入节点的合适位置
	cur = new Node(key);//申请一个键值为key的节点
	if (parent->_key < key)//key的值比父节点小
	{
		parent->_right = cur;//连接到父节点的右边
	}
	else
	{
		parent->_left = cur;//否则连接到父节点的左边
	}

	return true;//插入成功
}

OK,我们知道他的中序是有序的,所以我们可以插入一些乱序的数字然后走个中序看看是否是有序的即可验证师插入!

InOrder

实现思路:先遍历左子树 --> 根 -->右子树

但是这的根节点是BSTree私有的,咋办呢?解决方案右以下几种:

1、把你的测试函数搞成友元,就可以在测试函数中访问_root了(强烈不推荐)

2、提供get和set函数

3、把中序搞成私有的子函数,在提供一个共有的把子函数套一层(推荐)

void _Inorder(const Node* _root)
{
	if (_root == nullptr)
	{
		return;
	}

	_Inorder(_root->_left);
	cout << _root->_key << " ";
	_Inorder(_root->_right);
}

OK,验证一下:

Find

实现思路:比较当前节点的值和key的值,如果比key大去右边,否则去左边找!

bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else if(cur->_key < key)
		{
			cur = cur->_right;
		}
		else
		{
			return true;//找到了
		}
	}

	return false;//没找到
}

Erase

实现思路

删除可以分为4种情况:1、被删节点的左为空   2、被删节点的右为空  3、被删节点的左右都为空  4、被删节点的左右都不为空。前三中只需要特殊处理一下可以合并为两种!注意:所有的删除都得找到这个节点,否则直接返回false;

被删节点的左为空

        1、如果当前节点是根节点且只有右单枝,让根节点指向它的右单枝的下一个

        2、不是根节点,如果被删节点==它的父节点的左,则把他的右连接到父亲的左

              如果被删节点==它的父节点的右,则把他的右连接到父亲的右

被删节点的右为空 

        1、如果当前节点是根节点且只有左单枝,让根节点指向它的左单枝的下一个

        2、不是根节点,如果被删节点==它的父节点的左,则把他的左连接到父亲的左

              如果被删节点==它的父节点的右,则把他的左连接到父亲的右

被删节点的左右都不为空

        用替换法删除即找一个合适的节点替他删除!可以找左子树最大(右)或 右子树最左         (小)的节点来替换。

        找到替换的节点后可以交换键值也可以赋值,然后删除替换节点即可!

bool Erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)//比key大,往左找
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)//比key小,往右找
		{
			parent = cur;
			cur = cur->_right;
		}
		else//找到了
		{
			if (cur->_left == nullptr)//被删除节点的左为空
			{
				if (_root == cur)//删除根且只有右枝没有左枝
				{
					_root = cur->_right;//让根指向它的右
				}
				else
				{
					if (parent->_left == cur)//父亲的左==cur,把cur的右连接到父亲的左
					{
						parent->_left = cur->_right;
					}
					else//父亲的右==cur,把cur的右连接到父亲的右
					{
						parent->_right = cur->_right;
					}
				}

				delete cur;
			}
			else if (cur->_right == nullptr)//被删除节点的右为空
			{
				if (_root == cur)//删除根且只有左枝没有右枝
				{
					_root = cur->_left;//让根指向它的左
				}
				else
				{
					if (parent->_left == cur)//父亲的左==cur,把cur的左连接到父亲的左
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;//父亲的右==cur,把cur的右连接到父亲的左
					}
				}

				delete cur;
			}
			else//被删除节点的左右都不为空则用替换法删除即找到一个合适的节点来替他被删(左子树最(大)右,或右子树的最(小)左)
			{
				Node* rightMinParent = cur;//右子树最(小)左节点的父亲
				Node* rightMin = cur->_right;//右子树的最左节点
				while (rightMin->_left)
				{
					rightMinParent = rightMin;
					rightMin = rightMin->_left;
				}

				swap(cur->_key, rightMin->_key);//右子树左节点与被删节点的值交换
					
				if (rightMinParent->_left == rightMin)//如果rightMinParent的左==rightMin
				{
					rightMinParent->_left = rightMin->_right;//则把rightMin的右连接到rightMinParent的左
				}
				else//如果rightMinParent的右==rightMin
				{
					rightMinParent->_right = rightMin->_right;//则把rightMin的右连接到rightMinParent的右
				}

				delete rightMin;
			}

			return true;
		}
	}

	return false;
}

OK,验证一下:

析构函数

因为只有一个成员,所以直接给一个缺省值就不用写构造了!析构得写,和二叉树的销毁一样!先左子树销毁-->右子树销毁-->根销毁,可以和中序一样搞一个子函数外面套一下即可!

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

	Destory(root->_left);
	Destory(root->_right);

	delete root;
	root = nullptr;
}
~BSTree()
{
	Destory(_root);
}

key模型的全部源码

#pragma once

template<class K>
struct BSTreeNode
{
	BSTreeNode* _left;
	BSTreeNode* _right;
	K _key;

	BSTreeNode(const K& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	bool insert(const K& key)
	{
		if (_root == nullptr)//第一次插入
		{
			_root = new Node(key);
			return true;
		}

		Node* parent = nullptr;//记录要插入节点的父节点的位置
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)//要插入的值比当前节点的值小
			{
				parent = cur;
				cur = cur->_left;//去当前节点的左边
			}
			else if (cur->_key < key)//要插入的值比当前节点的值大
			{
				parent = cur;
				cur = cur->_right;//去当前节点的右边
			}
			else
			{
				return false;//要插入的值已经存在
			}
		}

		//找到了要插入节点的合适位置
		cur = new Node(key);//申请一个键值为key的节点
		if (parent->_key < key)//key的值比父节点小
		{
			parent->_right = cur;//连接到父节点的右边
		}
		else
		{
			parent->_left = cur;//否则连接到父节点的左边
		}

		return true;//插入成功
	}

	bool Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if(cur->_key < key)
			{
				cur = cur->_right;
			}
			else
			{
				return true;//找到了
			}
		}

		return false;//没找到
	}

bool Erase(const K& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)//比key大,往左找
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)//比key小,往右找
		{
			parent = cur;
			cur = cur->_right;
		}
		else//找到了
		{
			if (cur->_left == nullptr)//被删除节点的左为空
			{
				if (_root == cur)//删除根且只有右枝没有左枝
				{
					_root = cur->_right;//让根指向它的右
				}
				else
				{
					if (parent->_left == cur)//父亲的左==cur,把cur的右连接到父亲的左
					{
						parent->_left = cur->_right;
					}
					else//父亲的右==cur,把cur的右连接到父亲的右
					{
						parent->_right = cur->_right;
					}
				}

				delete cur;
			}
			else if (cur->_right == nullptr)//被删除节点的右为空
			{
				if (_root == cur)//删除根且只有左枝没有右枝
				{
					_root = cur->_left;//让根指向它的左
				}
				else
				{
					if (parent->_left == cur)//父亲的左==cur,把cur的左连接到父亲的左
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;//父亲的右==cur,把cur的右连接到父亲的左
					}
				}

				delete cur;
			}
			else//被删除节点的左右都不为空则用替换法删除即找到一个合适的节点来替他被删(左子树最(大)右,或右子树的最(小)左)
			{
				Node* rightMinParent = cur;//右子树最(小)左节点的父亲
				Node* rightMin = cur->_right;//右子树的最左节点
				while (rightMin->_left)
				{
					rightMinParent = rightMin;
					rightMin = rightMin->_left;
				}

				swap(cur->_key, rightMin->_key);//右子树左节点与被删节点的值交换
					
				if (rightMinParent->_left == rightMin)//如果rightMinParent的左==rightMin
				{
					rightMinParent->_left = rightMin->_right;//则把rightMin的右连接到rightMinParent的左
				}
				else//如果rightMinParent的右==rightMin
				{
					rightMinParent->_right = rightMin->_right;//则把rightMin的右连接到rightMinParent的右
				}

				delete rightMin;
			}

			return true;
		}
	}

	return false;
}

	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}

	~BSTree()
	{
		Destory(_root);
	}

private:
	void _Inorder(const Node* _root)
	{
		if (_root == nullptr)
		{
			return;
		}

		_Inorder(_root->_left);
		cout << _root->_key << " ";
		_Inorder(_root->_right);
	}

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

		Destory(root->_left);
		Destory(root->_right);

		delete root;
		root = nullptr;
	}

private:
	Node* _root = nullptr;//只有一个成员,给一个缺省参数可以不用自己写构造了
};

二叉搜索树的应用

1、K模型:就是我们上面介绍和实现的key模型,K模型中只存键值key,不可被修改!这个在生活中也是很常见的,比如说20万个单词找出拼写错误的单词!此时你只需要根据单词库中的单词建立一个key模型的搜索树即可,然后一一查找即可,如果是false的就是错误的!另外,宿舍的门禁系统,你刷脸或刷卡时会放你进去,这个也是一个key模型的搜索树!

2、KV模型:每个节点中不仅存一个键值key还要存一个key对应的value。即<KV>键值对。这个也是很常见的,比如说英汉词典,你的学号对应你等!

key_value模型全部源代码

上面K模型已经实现了,这里KV模型在上面的K上稍加修改即可(插入的时候多加一个value)!

namespace key_value
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode* _left;
		BSTreeNode* _right;
		K _key;
		V _value;

		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		bool insert(const K& key, const V& value)
		{
			if (_root == nullptr)//第一次插入
			{
				_root = new Node(key, value);
				return true;
			}

			Node* parent = nullptr;//记录要插入节点的父节点的位置
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)//要插入的值比当前节点的值小
				{
					parent = cur;
					cur = cur->_left;//去当前节点的左边
				}
				else if (cur->_key < key)//要插入的值比当前节点的值大
				{
					parent = cur;
					cur = cur->_right;//去当前节点的右边
				}
				else
				{
					return false;//要插入的值已经存在
				}
			}

			//找到了要插入节点的合适位置
			cur = new Node(key, value);//申请一个键值为key的节点
			if (parent->_key < key)//key的值比父节点小
			{
				parent->_right = cur;//连接到父节点的右边
			}
			else
			{
				parent->_left = cur;//否则连接到父节点的左边
			}

			return true;//插入成功
		}

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;//找到了
				}
			}

			return cur;//没找到
		}

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)//比key大,往左找
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)//比key小,往右找
				{
					parent = cur;
					cur = cur->_right;
				}
				else//找到了
				{
					if (cur->_left == nullptr)//被删除节点的左为空
					{
						if (_root == cur)//删除根且只有右枝没有左枝
						{
							_root = cur->_right;//让根指向它的右
						}
						else
						{
							if (parent->_left == cur)//父亲的左==cur,把cur的右连接到父亲的左
							{
								parent->_left = cur->_right;
							}
							else//父亲的右==cur,把cur的右连接到父亲的右
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr)//被删除节点的右为空
					{
						if (_root == cur)//删除根且只有左枝没有右枝
						{
							_root = cur->_left;//让根指向它的左
						}
						else
						{
							if (parent->_left == cur)//父亲的左==cur,把cur的左连接到父亲的左
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;//父亲的右==cur,把cur的右连接到父亲的左
							}
						}

						delete cur;
					}
					else//被删除节点的左右都不为空则用替换法删除即找到一个合适的节点来替他被删(左子树最(大)右,或右子树的最(小)左)
					{
						Node* rightMinParent = cur;//右子树最(小)左节点的父亲
						Node* rightMin = cur->_right;//右子树的最左节点
						while (rightMin->_left)
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}

						swap(cur->_key, rightMin->_key);//右子树左节点与被删节点的值交换

						if (rightMinParent->_left == rightMin)//如果rightMinParent的左==rightMin
						{
							rightMinParent->_left = rightMin->_right;//则把rightMin的右连接到rightMinParent的左
						}
						else//如果rightMinParent的右==rightMin
						{
							rightMinParent->_right = rightMin->_right;//则把rightMin的右连接到rightMinParent的右
						}

						delete rightMin;
					}

					return true;
				}
			}

			return false;
		}

		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

		~BSTree()
		{
			Destory(_root);
		}

	private:
		void _Inorder(const Node* _root)
		{
			if (_root == nullptr)
			{
				return;
			}

			_Inorder(_root->_left);
			cout << _root->_key << " -> " << _root->_value << endl;
			_Inorder(_root->_right);
		}

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

			Destory(root->_left);
			Destory(root->_right);

			delete root;
			root = nullptr;
		}

	private:
		Node* _root = nullptr;//只有一个成员,给一个缺省参数可以不用自己写构造了
	};
}

可以一个类似于英汉单词的效果:

当然还可以统计某些东西的次数等!这里就不演示了!

二叉搜索树的性能分析

要执行插入和删除的操作的前提是得查找到相关的位置或元素,所以查找的效率代表了插入和删除的性能!我们来分析一下二叉搜索树的查找的时间复杂度:如果正常情况下是最多查找高度次所以时间复杂度是:O(logN)但是如果这个数是类似于链表的情况的话就是O(N)了!

这里你可能会担心,如果极端情况下真的退化成单链表那二叉搜索树的搜索性能就消失了,那该咋办呢?其实这个问题已经得到了解决!就是我们后面要介绍的AVL树和红黑树!马上后面会介绍的!

OK,好兄弟,本期分享就到这里,我们下期再见!

结束语:山高路远,看世界,也找自己。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java-(乘法表之后)增强for循环

这里我们先做个了解&#xff0c;之后我会在数组中进行详细介绍Java5引入了一种主要用于数组或集合的增强型for循环Java增强型for循环语法格式如下 For(声明语句&#xff1a;表达式&#xff09;{ //代码语句 } 声明语句&#xff1a;声明新的局部变量&#xff0c;该变量的类型…

Windows中安装的PostgreSQL 数据库如何重启

1. 使用Windows服务管理器 打开“运行”对话框&#xff08;按WinR键&#xff09;。输入services.msc并按回车&#xff0c;这将打开服务列表。在服务列表中找到PostgreSQL服务。它通常命名为“PostgreSQL”后面跟着版本号和实例名称&#xff0c;例如“PostgreSQL 13 - mydb”。…

【云原生】Pod 的生命周期(一)

【云原生】Pod 的生命周期&#xff08;一&#xff09;【云原生】Pod 的生命周期&#xff08;二&#xff09; Pod 的生命周期&#xff08;一&#xff09; 1.Pod 生命期2.Pod 阶段3.容器状态3.1 Waiting &#xff08;等待&#xff09;3.2 Running&#xff08;运行中&#xff09;3…

后缀表达式

什么是后缀表达式&#xff1f; 在计算机科学和数学领域&#xff0c;表达式求值是一项基本且频繁的任务。我们熟知的中缀表达式&#xff08;如 7 15 ∗ 1 4 ∗ 1&#xff09;直观易读&#xff0c;但在计算机处理时却需要复杂的栈或递归算法来解析。相比之下&#xff0c;后缀表…

深度学习中的优化算法:选择现有的还是自创?

深度学习中的优化算法 深度学习中的优化算法&#xff1a;选择现有的还是自创&#xff1f;现有优化算法的优势**优点包括**&#xff1a; 开发新的优化算法的考虑**开发新算法的原因**&#xff1a;**开发新算法的风险**&#xff1a; 实用建议结论 深度学习中的优化算法&#xff1…

RabbitMQ 是如何做延迟消息的 ?——Java全栈知识(15)

RabbitMQ 是如何做延迟消息的 &#xff1f; 1、什么是死信&#xff1f; 当一个队列中的消息满足下列情况之一时&#xff0c;可以成为死信&#xff08;dead letter&#xff09;&#xff1a; 消费者使用 basic.reject 或 basic.nack 声明消费失败&#xff0c;并且消息的 reque…

5-在Linux上部署各类软件

1. MySQL 数据库安装部署 1.1 MySQL 5.7 版本在 CentOS 系统安装 注意&#xff1a;安装操作需要 root 权限 MySQL 的安装我们可以通过前面学习的 yum 命令进行。 1.1.1 安装 配置 yum 仓库 # 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022# 安装Mysql…

rk3588局域网推流

最近无意间看见在网上有使用MediaMtx插件配合ffmpeg在Windows来进行推流&#xff0c;然后在使用其他软件进行拉流显示数据图像的&#xff0c;既然windows都可以使用 &#xff0c;我想linux应该也可以&#xff0c;正好手上也有一块RK3588的开发板&#xff0c;就测试了一下&#…

iOS ------ JSONModel源码

一&#xff0c;JSONModel的基本使用 1&#xff0c;基本使用方法 - (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err; - (instancetype)initWithData:(NSData *)data error:(NSError **)error; - (instancetype)initWithString:(NSString *)str…

Linux网络-部署YUM仓库及NFS共享服务

目录 一.YUM仓库服务 1.YUM概述 1.1.YUM&#xff08;Yellow dog Updater Modified&#xff09; 2.准备安装源 2.1.软件仓库的提供方式 2.2.RPM软件包的来源 2.3.构建CentOS 7 软件仓库 2.4.在软件仓库中加入非官方RPM包组 3.一键安装软件包的工具&#xff1a; 好处&a…

申请Sectigo证书流程详解

Sectigo&#xff08;前身为Comodo CA&#xff09;&#xff0c;是目前主流SSL证书的一种&#xff0c;目前全球范围内应用度也非常广泛&#xff0c;是目前众多品牌中市场份额最大的一个品牌了&#xff0c;在全球证书市场份额占比约为40%。 其超高的市场份额占比主要还是基于其超…

021、Python+fastapi,第一个Python项目走向第21步:ubuntu 24.04 docker 安装mysql8集群、redis集群(二)

系列文章目录 pythonvue3fastapiai 学习_浪淘沙jkp的博客-CSDN博客https://blog.csdn.net/jiangkp/category_12623996.html 前言 安装redis 我会以三种方式安装&#xff0c;在5月4号修改完成 第一、直接最简单安装&#xff0c;适用于测试环境玩玩 第二、conf配置安装 第三…

【Leetcode 42】 接雨水

基础思路&#xff1a; &#xff08;1&#xff09;需要将问题最小化&#xff0c;首先计算第i个位置最多容纳多少雨水&#xff08;细长的一条水柱&#xff09;&#xff0c;然后求和就是总的雨水量&#xff1b; &#xff08;2&#xff09;第i个位置容纳雨水量 min(左侧最高, 右…

​《MATLAB科研绘图与学术图表绘制从入门到精通》示例:绘制德国每日风能和太阳能产量3D线图

在MATLAB中&#xff0c;要绘制3D线图&#xff0c;可以使用 plot3 函数。 在《MATLAB科研绘图与学术图表绘制从入门到精通》书中通过绘制德国每日风能和太阳能产量3D线图解释了如何在MATLAB中绘制3D线图。 购书地址&#xff1a;https://item.jd.com/14102657.html

牛客热题:单链表排序

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;力扣刷题日记 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 文章目录 牛客热题&#xff1a;单链表排序题目链接方法一&…

【XR806开发板试用】基于MQTT与Cjson库的花式点灯

一、项目介绍 久闻openharmony大名&#xff0c;一直没有机会接触&#xff0c;感谢极术社区和全志社区的这次活动&#xff0c;让我能够了解并上手这个系统。 openhamony 1.1的内核是基于liteos内核系统进行构建的&#xff0c;liteos作为物联网系统&#xff0c;结合xr806小型开…

美团KV存储squirrel和Celler学习

文章目录 美团在KV存储squirrel优化和改进在水平方向1、对Gossip协议进行优化 在垂直扩展方面1、forkless RDB数据复制优化2、使用多线程&#xff0c;充分利用机器的多核能力 在高可用方面 美团持久化kv存储celler优化和改进水平扩展优化1、使用bulkload进行数据导入2、线程模型…

Adobe系列软件安装

双击解压 先运行Creative_Cloud_Set_Up.exe。 完毕后&#xff0c;运行AdobeGenP.exe 先Path&#xff0c;选路径&#xff0c;如 C:\Program Files\Adobe 后Search 最后Patch。 关闭软件&#xff0c;修图&#xff01;

电力能源箱3D可视化:开启智慧能源管理新篇章

随着科技的不断进步&#xff0c;电力能源箱的管理与维护逐渐向着智能化、可视化的方向发展。3D可视化技术的崛起&#xff0c;不仅极大地提升了能源管理的效率&#xff0c;更以其直观、生动的特点&#xff0c;引领着电力能源管理领域迈入了一个全新的时代。 电力能源箱作为电力系…

解决一个朋友的nbcio-boot的mysql数据库问题

1、原先安装mysql5.7数据库&#xff0c;导入我的项目里的带数据有报错信息 原因不明 2、只能建议用docker进行msyql5.7的安装 如下&#xff0c;可以修改成自己需要的信息 docker run -p 3306:3306 --name mastermysql -v /home/mydata/mysql/data:/var/lib/mysql -e MYSQL_R…