二叉搜索树详解及代码实现

news2024/10/6 6:00:46

目录

一、什么是二叉搜索树

 二、二叉搜索树的有关操作

2.1 查找:

2.2插入:

2.3 删除:

 2.4 打印

三、二叉搜索树的应用

3.1 K模型:

3.2 KV模型:

四、整体代码:

K模型:

KV模型:


一、什么是二叉搜索树

二叉搜索树(BST, Binary Search Tree),也称为二叉排序树或二叉查找树。

每一棵搜索树都满足如下条件:

1.非空左子树的所有键值小于其根节点的键值

2.非空右子树的所有键值大于其根节点的键值

3.左、右子树都是二叉搜索树

 如图所示二叉树:

 二、二叉搜索树的有关操作

如该树 数组表示为:int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13}; 用此树进行讲解

下面代码用到的c++类结构为:

	template<class K>  //模板
	struct BSTreeNode {
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;
	private:
		Node* _root = nullptr;
	};

2.1 查找:

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到到空,还没找到,这个值不存在。

代码实现:

非递归:

		bool Find(const K& key)  //K为模板
		{
			Node* cur = _root;
			while (cur)
			{//遍历,大于根往右,小于往左,相等就结束,遍历完没找到就是没找到
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

递归:

bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
				return _FinfR(root->_right, key);
			else if (root->_key > key)
				return _FindR(root->_left, key);
			else
				return true;
		}

2.2插入:

插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点(大于根节点值往右走,小于根节点值往左走)。注意:二叉搜索树插入过程中,插入已存在值无意义。

为该树插入16 、9 两个值,根据搜素树应满足的“左小右大”的条件,插入后如图:

 代码实现:

非递归写法:

bool Insert(const K& key)    //K为模板
		{
            //空树直接插
			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->_right;
				}
				else if (cur->_key > key)//小于往左子树
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;//遇到相同的数就不插入,相当于去重
				}
			}
            //找到插入的位置后,进行插入操作
			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}

递归写法:

bool _InsertR(Node*& root, const K& key) //K为模板
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}

2.3 删除:

删除某一节点可分为四种情况:

a.要删除节点没有孩子节点

如图删除节点值13,没有孩子节点,删除后父节点parent->left就指向空。

 b.要删除的结点只有左孩子结点 

c. 要删除的结点只有右孩子结点

该两种情况相似。如删除节点14后,其孩子节点13的父节点就变为10,即:10->right=13

d. 要删除的结点有左、右孩子结点

该情况难点在于删除节点后,被删除节点的父节点应该链接被删除节点的哪个孩子节点。这分为两种方法:

1.连接删除节点的左子树中最右边节点

2.连接删除节点的右子树中最左节点

一般情况下,我们使用方法2.在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除


 代码实现:
非递归:

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}
			return false;
		}

递归:

bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}

			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					//找右数的最左节点
					Node* min = root->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					swap(root->_key, min->_key);
					return _EraseR(root->_right, key);
				}

				delete del;
				return true;
			}
		}

 2.4 打印

这里可以将创建的二叉搜索树通过中序(左->根->右)打印出来,就是由小到大的升序序列。因为二叉搜索树中不能有重复的数值,所以可以通过插入一组数据实现对其排序且去重。

		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);

		}

该二叉搜索树的整体代码放在文末。

三、二叉搜索树的应用

3.1 K模型:

上面我们所实现的就是K模型。

K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树,在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误

3.2 KV模型:

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。一般可以通过查找关键码key来查找对应的值。

比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对。(KV模型整体代码与K模型思想很相似,就直接放在文末)

	void TestBSTree1()
	{
		BSTree<string, string> dict;
		dict.Insert("sort", "排序");
		dict.Insert("left", "左边");
		dict.Insert("right", "右边");
		dict.Insert("string", "字符串");
		dict.Insert("insert", "插入");
		string str;
		while (cin >> str)
		{
			BSTreeNode<string, string>* ret = dict.Find(str);
			if (ret)
			{
				cout << ":" << ret->_value << endl;
			}
			else
			{
				cout << "->无此单词" << endl;
			}
		}
	}

 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对。

void TestBSTree2()
	{
		string arr[] = { "苹果","苹果" ,"香蕉" ,"草莓" ,"香蕉" ,"苹果" ,"苹果", "苹果" };
		
		BSTree<string, int> countTree;
		for (auto& str:arr)
		{
			auto ret = countTree.Find(str);
			if (ret)
			{
				ret->_value++;
			}
			else
			{
				countTree.Insert(str, 1);
			}
		}

		countTree.InOrder();
	} 

四、整体代码:

K模型:

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

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


	//BinarySearchTree
	template<class K>   //Key
	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->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;//遇到相同的数就不插入,相当于去重
				}
			}
			cur = new Node(key);
			if (parent->_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->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}
			return false;
		}

		void InOrder()
		{
			_InOrder(_root);//这里套一层就可以直接使用_root;或者也可以写getroot方法
			cout << endl;
		}

		//递归查找
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		//递归插
		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}
		//递归删
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

		~BSTree()
		{
			_Destroy(_root);
		}
		//强制编译器生成默认的构造
		BSTree() = default;
		BSTree(const BSTree<K>& t)
		{
			_root = _copy(t._root);
		}

		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
	private:
		Node* _copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* copyRoot = new Node(root->_key);
			copyRoot->_left = _copy(root->_left);
			copyRoot->_right = _copy(root->_right);
			return copyRoot;
		}
		void _Destroy(Node*& root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);
			delete root;
			root = nullptr;
		}
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}

			if (root->_key < key)
			{
				return _EraseR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _EraseR(root->_left, key);
			}
			else
			{
				Node* del = root;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					//找右数的最左节点
					Node* min = root->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					swap(root->_key, min->_key);
					return _EraseR(root->_right, key);//很巧妙
				}

				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

			if (root->_key < key)
			{
				return _InsertR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _InsertR(root->_left, key);
			}
			else
			{
				return false;
			}
		}
		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
				return _FinfR(root->_right, key);
			else if (root->_key > key)
				return _FindR(root->_left, key);
			else
				return true;
		}
		//中序  //
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);

		}
	private:
		Node* _root = nullptr;
	};
}

KV模型:

template<class K,class V>
	struct BSTreeNode {
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _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->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;//遇到相同的数就不插入,相当于去重
				}
			}
			cur = new Node(key,value);
			if (parent->_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->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}

			//删除的中间过程都一样
			return true;
		}

		void InOrder()
		{
			_InOrder(_root);//这里套一层就可以直接使用_root;或者也可以写getroot方法
			cout << endl;
		}

	private:
		//中序  //
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " value: " << root->_value << endl;;
			_InOrder(root->_right);

		}
	private:
		Node* _root = nullptr;
	};

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

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

相关文章

物联网应用普及正在改变我们的生活

物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;指的是通过互联网连接各种物品、设备和传感器&#xff0c;实现物品之间的互联互通&#xff0c;形成智能化、自动化的数据交互和服务体系。简单来说&#xff0c;就是将各类物品通过互联网连接&#xff0c;实现互…

小心!YouTube官方邮件恐是网络钓鱼

在最近的一条推文中&#xff0c;YouTube披露了有关新的网络钓鱼诈骗的详细信息&#xff0c;并呼吁用户小心“no-replyyoutube.com”发送的信件。该骗局一直在使用真实的no-replyyoutube.com电子邮件地址来引诱用户泄露他们的登录凭据。那么这种新的网络钓鱼是如何运作的呢&…

原神QQ机器人BOT搭建教程Ubuntu系统

原神QQ机器人BOT搭建教程Ubuntu系统 大家好我是艾西&#xff0c;今天跟大家分享的是YUAN神qi鹅群机器人bot搭建方式以及详细的操作步骤。跟上艾西的节奏准备发车啦&#xff01; 前言&#xff1a;&#xff08;xxxx即为xxxx&#xff09;&#xff08;zzz即为zzz&#xff09; qi…

【池化技术】基于Apache组件--对象池的介绍与使用

文章目录 一、背景二、对象池有什么特征&#xff1f;三、池的大小选择四、运行原理五、对象管理5.1添加对象5.2借用对象5.3归还对象5.4对象状态 六、对象池的使用6.1 接入6.2 实现线程池工厂6.3 初始化 七、优缺点八、应用场景8.1Redis应用8.2 Web服务器例子8.3 游戏开发种的例…

论文中文翻译——Double-Fetch情况如何演变为Double-Fetch漏洞:Linux内核中的双重获取研究

本论文相关内容 论文下载地址——Web Of Science论文中文翻译——How Double-Fetch Situations turn into Double-Fetch Vulnerabilities A Study of Double Fetches in the Linux Kernel 文章目录 本论文相关内容前言Double-Fetch情况如何演变为Double-Fetch漏洞&#xff1a;…

Elasticsearch:如何使用 Elasticsearch 以自然语言提示 ChatGPT

作者&#xff1a;Enrico Zimuel 这些天每个人都在谈论 ChatGPT。 这种大型语言模型 (LLM) 的一项很酷的功能是能够生成代码。 我们用它来生成 Elasticsearch DSL 查询。 目标是在 Elasticsearch 中搜索 “给我股票指数中 2017 年的前 10 个文档&#xff08;Give me the first 1…

C/C++web编程,以及案例(内附小白基础知识)你也能轻松学会呦~

目录 C基本知识&#xff0c;小白来这里 C 基本的输入输出 I/O 库头文件 标准输出流&#xff08;cout&#xff09; 标准输入流&#xff08;cin&#xff09; 标准错误流&#xff08;cerr&#xff09; 标准日志流&#xff08;clog&#xff09; Cweb编程&#xff0c;老手来这…

【人工智能】蚁群算法(密恐勿入)

蚁群算法&#xff08;密恐勿入&#xff09; 蚁群算法--给你一个感性认识 蚁群算法&#xff08;密恐勿入&#xff09;1. 算法简介1.1 基本原理1.1.1 模拟蚂蚁在简单地形&#xff0c;寻找食物1.1.2 模拟蚂蚁在复杂地形&#xff0c;找到食物1.2 算法应用 2. 算法解析3.算法应用——…

Spark 2:Spark Core RDD算子

RDD定义 RDD&#xff08;Resilient Distributed Dataset&#xff09;叫做弹性分布式数据集&#xff0c;是Spark中最基本的数据抽象&#xff0c;代表一个不可变、可分区、里面的元素可并行计算的集合。 Resilient&#xff1a;RDD中的数据可以存储在内存中或者磁盘中。 Distribut…

Jenkins入门使用详解,实现构建部署运行

Hi I’m Shendi Jenkins入门使用详解&#xff0c;实现构建部署运行 Jenkins简介 Jenkins 是一个用 Java 编写的开源自动化工具 Jenkins是一款开源 CI&CD 软件&#xff0c;用于自动化各种任务&#xff0c;包括构建、测试和部署软件。 CI 指持续集成&#xff0c;属于开发人…

【数据结构】--单链表力扣面试题①移除链表元素

题述&#xff1a; 给你一个链表的头结点head和一个整数val,请你删除链表中所有满足Node.val val的节点&#xff0c;并返回新的头结点。 思考&#xff1a; 为什么说要返回新的头结点&#xff0c;因为你删除的可能存在把原来的头结点删除的情况&#xff0c;这时就需要有新的头结…

【机器学习】

说明&#xff1a;机器学习总结 0、数据集 1、贝叶斯分类器 &#xff08;一&#xff09;计算题 所有样本分为两类&#xff08;c &#xff09;&#xff1a;好瓜是、好瓜否 &#xff08;1&#xff09;计算先验概率&#xff1a;P(c ) &#xff08;2&#xff09;计算每个属性的条…

元宇宙:梦想能否照进现实?

开篇我想问大家一个问题&#xff1a;有没有想过人类的未来是什么样子&#xff1f; 就目前我们所能探索的世界&#xff0c;不论从空间上有跨越了几十亿光年的距离&#xff0c;还是从时间有上几万几亿年的演化&#xff0c;对于宇宙来说&#xff0c;我们人类实在是太过渺小、脆弱…

非暴力沟通模型

非暴力沟通模型 非暴力沟通的创始人是马歇尔.卢森堡&#xff0c;师从人本主义心理学之父卡尔.罗杰斯。《非暴力沟通》一书入选香港大学推荐的50本必读书籍之列。 模型介绍 非暴力沟通&#xff08;英文名称&#xff1a;NonviolentCommunication&#xff0c;简称NVC&#xff09;…

AMBER分子动力学模拟之结果分析(MMGB/PBSA)-- HIV蛋白酶-抑制剂复合物(4)

AMBER分子动力学模拟之结果分析(MMGB/PBSA)-- HIV蛋白酶-抑制剂复合物(4) 结合自由能计算 我们首先计算焙变&#xff0c;用到的是pbsa和gbsa方法。我们需要一下文件 三个top文件&#xff0c;pro.prmtop lig.prmtop com.prmtop&#xff1b;输入文件MM_GBSA.in&#xff1b;将要…

Nextcloud集成Onlyoffice实现协同办公。

Nextcloud 安装 LAMP&#xff08;LinuxApacheMySQLPHP&#xff09;方式。局域网单机模式。 安装环境&#xff1a; OS:ubuntu 22.04 Nextcloud&#xff1a;26 Apache&#xff1a;2.4.52 mysql&#xff1a;Server version: 8.0.33-0ubuntu0.22.04.1 (Ubuntu) php:PHP 8.1.2-1u…

(GCC)从零开始的ETH开发--STM32F407ZGTX

开发环境&#xff1a; windows10 软件&#xff1a; STM32CubeMX最新版本&#xff1a; gcc工具链&#xff1a; vscode 硬件&#xff1a; STM32F407ZGT6开发板&#xff0c;ETH原理图&#xff1a; ​ 根据硬件可知MAC和PHY之间的接口类型为RMII&#xff0c;且PHY使用的外…

【文老师软考数据库】(全)

目录 第一章 计算机系统基本知识 1.1 计算机系统 1.1.1 计算机硬件组成 1.1.2 中央处理单元 1.1.3 数据表示 1.1.4 校验码 1.2 计算机体系结构 1.2.1 体系结构分类 1.2.2 指令系统存 1.2.3 储系系统 1.2.4 输入/输出技术 1.2.5 总线结构 1.3 可靠性、性能、安全 …

Win10系统为什么又提示重装打印机驱动?

明明没干嘛&#xff0c;Win 10系统却提示需要重新安装打印机驱动程序。驱动人生就来说说Win10重新安装打印机驱动程序的原因及解决方法&#xff1a; 一般来说&#xff0c;如果用户更新了Win 10的操作系统&#xff0c;可能导致之前的打印机驱动错误或缺失&#xff0c;这时候就需…

运行KT001小车问题汇总

运行KT001小车问题汇总&#xff1a; Q: 在安装ros的时候&#xff0c;出现这样的问题是因为什么&#xff0c;是镜像文件里边缺少东西了吗&#xff0c;可以怎么解决&#xff1f; A: 导入的是vmk配置文件&#xff0c;缺少iso镜像文件&#xff0c;不影响ros的运行。 Q: 编辑好这个…