C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用

上次介绍完多态后:C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)

也是要开始继续学习了


文章目录

  • 1.二叉搜索树
    • 1.1概念
    • 1.2二叉搜索树特性
    • 1.3 二叉搜索树的操作
  • 2.模拟实现
    • 2.1项目文件规划
    • 2.2基本结构
    • 2.3各种接口、功能、以及基本结构的补充
      • 2.3.1 Find() 查找 (迭代/循环版本)
      • 2.3.2 Insert() 插入(迭代/循环版本)
        • 写出中序遍历来进行验证(中序遍历有序)
      • 2.3.3 Erase() 删除(迭代/循环版本)
        • 验证
      • 2.3.4FindR() 查找 (递归版本)
      • 2.3.5Insert() 插入 (递归版本)
      • 2.3.6 EraseR() 删除 (迭代版本)
      • 2.3.7 补全拷贝构造函数
      • 2.3.8 补全析构函数
  • 3.二叉搜索树应用
  • 4.性能分析


1.二叉搜索树

1.1概念

二叉搜索树(Binary Search Tree,BST)是一种二叉树,其中每个节点都具有以下性质:

  • 节点的左子树中的所有节点的值都小于该节点的值。
  • 节点的右子树中的所有节点的值都大于该节点的值。
  • 左右子树也分别为二叉搜索树。

假设我们插入以下元素:5, 3, 7, 1, 4, 6, 8,可以构建如下的二叉搜索树(BST):

      5
     / \
    3   7
   / \ / \
  1  4 6  8

1.2二叉搜索树特性

  • 中序遍历二叉搜索树得到的序列是有序的。
  • 查找、插入和删除操作的平均时间复杂度为O(log N),其中N为树中节点的数量

1.3 二叉搜索树的操作

  1. 插入操作

    • 树为空,则直接新增节点,赋值给root指针

    • 树不空,按二叉搜索树性质查找插入位置,插入新节点

  2. 删除操作

    首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:

    • 如果要删除的节点没有孩子结点,那么可以直接删除它。

    • 如果要删除的节点只有左孩子结点,可以直接删除该节点,并使其父节点指向其左孩子。

    • 如果要删除的节点只有右孩子结点,同样可以直接删除该节点,并使其父节点指向其右孩子。

    • 如果要删除的节点有左、右孩子结点,可以在其右子树中找到中序遍历下的第一个结点(右子树里最小),将其值替换到要删除的节点中,再递归删除右子树中的那个中序遍历下的第一个结点。(这个可以直接删)


2.模拟实现

2.1项目文件规划

在这里插入图片描述

头文件BSTree.h:进行模拟的编写

源文件test.cpp:进行测试,检查代码逻辑是否满足期望

2.2基本结构

namespace key
{
	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:
		BSTree() = default;//强制生成默认构造函数

		BSTree(const BSTree<K>& t)//拷贝构造函数
		{
			//....
		}

		~BSTree()//析构函数
		{
			//...
		}

	private:
		Node* _root = nullptr;//头结点
	};
}

2.3各种接口、功能、以及基本结构的补充

2.3.1 Find() 查找 (迭代/循环版本)

		bool Find(const K& key)
		{
			Node* cur = _root;//创建临时节点
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return true;//这是找到啦
				}
			}
			return false;//没找到才会出循环
		}

这里的思路很简单:该key< cur的_key,就进入到左子树;反之进入右子树

2.3.2 Insert() 插入(迭代/循环版本)

bool Insert(const K& key)
		{
			if (_root == nullptr)
			{
				_root = new Node(key);
                return true;
			}
			else
			{
				Node* cur = _root;
				Node* parent = nullptr;//这里存父亲节点,方便后续链接上
				while (cur)
				{
					if (key < cur->_key)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (key > cur->_key)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;//搜索二叉树里不能有相同 
					}
				}//出来后cur是nullptr,parent是叶子节点
				cur = new Node(key);//重新利用
				if (parent->_key < key)
				{
					parent->_right = cur;
				}
				else
				{
					parent->_left = cur;
				}

				return true;
			}
		}
  1. 如果二叉搜索树为空(即 _root == nullptr),则创建一个新节点 _root,其键值为 key,并将其作为根节点。
  2. 如果二叉搜索树不为空,则从根节点开始,沿着树的路径向下搜索应该插入新节点的位置。
  3. 在搜索过程中,如果发现要插入的键值 key 小于当前节点的键值,则继续在当前节点的左子树中搜索;如果大于当前节点的键值,则继续在右子树中搜索。
  4. 如果搜索到某个节点的键值与要插入的键值相等,则说明二叉搜索树中不能有相同的节点,直接返回 false
  5. 当搜索到空节点时,表示找到了要插入新节点的位置,此时创建一个新节点 cur,其键值为 key
  6. 最后,将新节点 cur 插入到父节点 parent 的左子树或右子树上,具体取决于新节点的键值与父节点的键值的大小关系
写出中序遍历来进行验证(中序遍历有序)
void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

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

这里因为要传入根节点,所以正常无法在外部调用。我们采用子函数的方式来解决这个问题。

顺便把子函数放在private里,这样更安全

#include<iostream>
using namespace std;

#include"BSTree.h"

int main()
{
	key::BSTree<int> t;
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	
	t.InOrder();
}

在这里插入图片描述


2.3.3 Erase() 删除(迭代/循环版本)

		bool Erase(const K& key)
		{
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)//先找到在哪里删除
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else//进来就说明找到啦,就是现在的cur
				{
					if (cur->_left == nullptr)//左为空,就把右给父亲节点(为空也无妨)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (cur == parent->_right)//cur在parent右,那就接到右侧
							{
								parent->_right = cur->_right;
							}
							else
							{
								parent->_left = cur->_right;
							}
						}

						delete cur;
						return true;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_right)//cur在parent右,那就接到右侧
							{
								parent->_right = cur->_left;
							}
							else
							{
								parent->_left = cur->_left;
							}
						}

						delete cur;
						return true;
					}
					else//左右都不是空,使用替换法,这里用右子树最小来换
					{
						Node* rightMinParent = cur;//右子树最小的父亲节点
						Node* rightMin = cur->_right;//右子树最小的节点

						while (rightMin->_left)//开始找
						{
							rightMinParent = rightMin;
							rightMin = rightMin->_left;
						}

						cur->_key = rightMin->_key;//把值一给,现在删rightMin就行了

						if (rightMin == rightMinParent->_left)
							rightMinParent->_left = rightMin->_right;
						else
							rightMinParent->_right = rightMin->_right;

						delete rightMin;
						return true;
					}
				}
			}
		}
  1. 首先,定义了两个指针 parentcur,分别用来记录当前节点的父节点和当前节点,初始时 cur 指向根节点 _root
  2. while 循环中,不断遍历二叉搜索树,直到找到要删除的节点或遍历完整棵树
  3. 在循环中,通过比较要删除的键值 key 与当前节点的键值 cur->_key 的大小关系,来确定是向左子树还是右子树继续遍历。
  4. 如果找到了要删除的节点,根据不同情况进行处理:
  • 如果要删除的节点没有左子树,直接将其右子树接到父节点的相应位置,然后删除该节点。
  • 如果要删除的节点没有右子树,类似地将其左子树接到父节点的相应位置,然后删除该节点。
  • 如果要删除的节点既有左子树又有右子树,采用替换法,找到右子树中最小的节点(即右子树中最左边的节点),将其键值替换到要删除的节点上,然后删除右子树中最小的节点。
  1. 删除节点后,返回 true 表示删除成功
验证
int main()
{
	key::BSTree<int> t;
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	for (auto e : a)
	{
		t.Insert(e);
	}
	t.InOrder();

	t.Erase(8);
	t.InOrder();

	t.Erase(14);
	t.InOrder();

	t.Erase(4);
	t.InOrder();

	t.Erase(6);
	t.InOrder();
	return 0;
}

在这里插入图片描述


2.3.4FindR() 查找 (递归版本)

		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

	private:
		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)//到最后没找到
			{
				return false;
			}

			if (root->_key < key)
			{
				return _FindR(root->_right, key);
			}
			else if (root->_key > key)
			{
				return _FindR(root->_left, key);
			}
			else
			{
				return true;
			}
		}
  1. FindR 函数是对外提供的接口函数,用于从根节点开始递归查找指定键值的节点。
  2. _FindR 函数中,首先判断当前节点是否为空,如果为空则表示在当前路径上没有找到指定键值的节点,返回 false
  3. 如果当前节点的键值小于要查找的键值 key,则递归调用 _FindR 函数查找右子树。
  4. 如果当前节点的键值大于要查找的键值 key,则递归调用 _FindR 函数查找左子树。
  5. 如果当前节点的键值等于要查找的键值 key,则表示找到了目标节点,返回 true
  6. 通过递归的方式不断在左右子树中查找,直到找到目标节点或者遍历完整棵树

2.3.5Insert() 插入 (递归版本)

bool InsertR(const K& key)//难点在于,如何与父亲节点进行链接
		{
			return _InsertR(_root, key);
		}

	private:
		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;
			}
		}
  1. InsertR 函数是对外提供的接口函数,用于从根节点开始递归插入新节点。
  2. _InsertR 函数中,参数 root 是一个指向节点指针的引用,这样可以在递归过程中改变节点指针的指向,从而实现与父节点的链接
  3. 首先判断当前节点是否为空,如果为空则表示可以在该位置插入新节点,创建一个新节点并将其指针赋给 root,然后返回 true
  4. 如果当前节点的键值小于要插入的键值 key,则递归调用 _InsertR 函数插入到右子树。
  5. 如果当前节点的键值大于要插入的键值 key,则递归调用 _InsertR 函数插入到左子树。
  6. 如果当前节点的键值等于要插入的键值 key,则表示树中已经存在相同键值的节点,返回 false
  7. 通过递归的方式在左右子树中寻找合适的插入位置,并不断更新父节点的指针,直到插入新节点或者确认新节点已存在

2.3.6 EraseR() 删除 (迭代版本)

		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:
		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;//保存一下,过会要delete的

				//这里便是引用开始发挥的作用,可以直接进行链接
				if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else//还是使用替换法,不过这次,直接交换后,再去右子树里递归
				{
					Node* rightMin = root->_right;
					while (rightMin->_left)
					{
						rightMin = rightMin->_left;
					}

					std::swap(root->_key, rightMin->_key);

					return _EraseR(root->_right, key);
				}

				delete del;
				return true;
			}
		}
  1. EraseR 函数是对外提供的接口函数,用于从根节点开始递归删除指定键值的节点。
  2. _EraseR 函数中,参数 root 是一个指向节点指针的引用,这样可以在递归过程中改变节点指针的指向,从而实现删除操作
  3. 首先判断当前节点是否为空,如果为空则表示在当前路径上没有找到指定键值的节点,返回 false
  4. 如果当前节点的键值小于要删除的键值 key,则递归调用 _EraseR 函数在右子树中删除。
  5. 如果当前节点的键值大于要删除的键值 key,则递归调用 _EraseR 函数在左子树中删除。
  6. 如果当前节点的键值等于要删除的键值 key,则表示找到了目标节点,进行删除操作。
  7. 如果目标节点只有一个子节点或者没有子节点,直接用子节点替换目标节点即可。
  8. 如果目标节点有两个子节点,找到右子树中最小的节点(即右子树中最左边的节点),将其键值与目标节点键值交换,然后在右子树中递归删除这个最小节点。
  9. 最后删除目标节点,并返回 true 表示删除成功

2.3.7 补全拷贝构造函数

		BSTree(const BSTree<K>& t)//拷贝构造函数
		{
			_root = copy(_root);
		}

		Node* copy(Node* root)
		{
			if (root == nullptr)
				return nullptr;

			Node* newnode = new Node(root->_key);
			if (root->_left)
			{
				newnode->_left = copy(root->_left);
			}
			if (root->_right)
			{
				newnode->_right = copy(root->_right);
			}
			return newnode;
		}
  1. 在拷贝构造函数 BSTree(const BSTree<K>& t) 中,调用了 copy 函数来复制传入二叉搜索树 t 的根节点及其所有子节点。
  2. copy 函数接收一个节点指针 root,用于递归复制以该节点为根的子树。
  3. 首先判断当前节点是否为空,如果为空则返回 nullptr
  4. 创建一个新节点 newnode,键值为当前节点 root 的键值,表示复制当前节点。
  5. 如果当前节点有左子节点,则递归复制左子树,并将复制得到的左子树根节点赋值给新节点的左指针 _left
  6. 如果当前节点有右子节点,则递归复制右子树,并将复制得到的右子树根节点赋值给新节点的右指针 _right
  7. 返回新节点 newnode,表示复制当前节点及其所有子节点。(也是起到链接的作用)
  8. 在拷贝构造函数中,调用 copy 函数复制传入二叉搜索树的根节点,从而完成整棵树的复制。

2.3.8 补全析构函数

		~BSTree()//析构函数
		{
			Destory(_root);
		}

		void Destory(Node* root)//走个后序
		{
			if (root == nullptr)
				return;

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

3.二叉搜索树应用

  1. K模型

    • 结构:在K模型中,每个节点只包含一个关键码(key),不包含值。节点的左子树中的所有节点都小于当前节点的关键码,右子树中的所有节点都大于当前节点的关键码。
    • 操作
      • 插入:将新的关键码插入到二叉搜索树中的合适位置,保持树的有序性。
      • 搜索:通过比较关键码,可以快速判断给定的值是否在树中存在。
    • 应用场景:适用于需要快速判断特定值是否存在的场景,如拼写检查、查找特定单词等。
  2. KV模型

    • 结构:在KV模型中,每个节点包含一个键值对(<Key, Value>),其中Key为关键码,Value为对应的值。节点的左子树中的所有节点的关键码小于当前节点的关键码,右子树中的所有节点的关键码大于当前节点的关键码。
    • 操作
      • 插入:插入新的键值对到二叉搜索树中,保持树的有序性。
      • 搜索:通过比较关键码,可以快速查找对应的值。
    • 应用场景
      • 英汉词典:以英文单词为关键码,对应的中文翻译为值,可以通过英文单词快速查找对应的中文翻译。
      • 统计单词次数:以单词为关键码,出现次数为值,可以方便地查找给定单词的出现次数。

4.性能分析

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

在这里插入图片描述

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

为了改进这种情况,可以使用自平衡二叉搜索树,如AVL树和红黑树。这些树在插入和删除操作时会通过旋转和重新平衡操作来保持树的平衡,从而确保树的高度较低,提高了搜索效率。

我后面也会继续学习,分享给大家!!!

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

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

相关文章

【数值模型系列】模拟区域网格设置工具WRFDomainWizard网页版使用介绍

大气数值模型首先需要进行模拟区域参数设置&#xff0c;这一过程可以使用WRF官网提供的WRFDomainWizard软件工具&#xff0c;也可以使用QGIS/GIS4WRF&#xff0c;甚至可以手动设置多次调整&#xff08;不推荐但不少人使用&#xff09;。本文介绍WRFDomainWizard工具的网页版&am…

六、循环结构

在python当中有两类循环结构&#xff1a;for循环和while循环 一、遍历for循环 for循环首先判断遍历对象中是否有元素&#xff0c;在依次遍历 for循环常与range&#xff08;&#xff09;函数使用 for i in range(1,10,):#range()函数依次遍历1~10但不包括10print(i,end ) p…

《尚品甄选》:后台系统——通过面向切面编程AOP,实现记录日志功能

文章目录 一、记录日志的意义二、日志数据表结构三、记录日志思想四、切面类环境搭建五、保存日志数据 一、记录日志的意义 后台管理系统记录操作日志的意义非常重要&#xff0c;主要体现在以下几个方面&#xff1a; 安全性&#xff1a;操作日志可以记录管理员操作行为&#…

【每日一题】数组元素的最小非零乘积

文章目录 Tag题目来源解题思路方法一&#xff1a;贪心 写在最后 Tag 【贪心】【快速幂】【数组】【2024-03-20】 题目来源 1969. 数组元素的最小非零乘积 解题思路 方法一&#xff1a;贪心 前言 我们首先来思考一个简单的问题&#xff1a;假设给定三个整数 a a a&#xf…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法&#xff0c;它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试&#xff1a; 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中&#xff0c;系统会暴露于一定数量的用户负载下&…

CodeWhisperer插件

一、前言 产品官网地址&#xff1a;What is CodeWhisperer? - CodeWhisperer Amazon CodeWhisperer 是一个通用的、由机器学习驱动的代码生成器&#xff0c;可实时为您提供代码建议。在您编写代码时&#xff0c;CodeWhisperer 会根据您现有的代码和注释自动生成建议。您的个…

一招让你的Mac重获新生,CleanMyMac助你轻松清理无用垃圾!

一招让你的Mac重获新生&#xff0c;CleanMyMac助你轻松清理无用垃圾&#xff01; 告别卡顿&#xff0c;让你的Mac跑得更快更稳&#xff01; 在当今这个快节奏的生活中&#xff0c;我们的工作和生活早已离不开电脑。特别是对于Mac用户来说&#xff0c;一台轻巧、快捷、稳定的Mac…

代码随想录算法训练营Day51 ||leetCode 309.最佳买卖股票时机含冷冻期 || 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 需要新添加状态 class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();if (n 0) return 0;vector<vector<int>> dp(n, vector<int>(4, 0));dp[0][0] - prices[0]; // 持股票for (i…

[.NET项目实战] Elsa开源工作流组件应用(三):实战演练

补充 之前的文章简单介绍了工作流和Elsa工作流库&#xff0c;这里再补充说明两点 工作流的使用场景非常广泛&#xff0c;几乎涵盖了所有需要进行业务流程自动化管理的领域。 学习一个开源库&#xff0c;最简单的方法就是看源码&#xff0c;Elsa的工作流引擎源码非常简单易懂&…

【Flutter 面试题】讲一讲 Dart 的一些重要概念?

【Flutter 面试题】讲一讲 Dart 的一些重要概念&#xff1f; 文章目录 写在前面口述回答补充说明完整代码运行结果详细说明 写在前面 &#x1f64b; 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#…

【Leetcode每日一题】 递归 - 反转链表(难度⭐)(36)

1. 题目解析 题目链接&#xff1a;206. 反转链表 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、递归函数的核心任务 递归函数的主要职责是接受一个链表的头指针&#xff0c;并返回该链表逆序后的新头结点。递归…

mysql迁移达梦数据库 Java踩坑合集

达梦数据库踩坑合集 文章目录 安装达梦设置大小写不敏感Spring boot引入达梦驱动&#xff08;两种方式&#xff09;将jar包打入本地maven仓库使用国内maven仓库&#xff08;阿里云镜像&#xff09; 达梦驱动yml配置springboot mybatis-plus整合达梦,如何避免指定数据库名&…

如何在Windows系统使用VS Code制作游戏网页并实现无公网IP远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

YZ系列工具之YZ08:窗体加载图片后进行放大查看

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

Linux学习总结下

vim\vi编辑器 什么是vi\vim编辑器&#xff1f; 1、vi、vim编辑器&#xff0c;就是命令模式下的文本编辑器&#xff0c;用来编辑文件 2、vim是vi的升级版&#xff0c;一般用vim即可&#xff0c;包含vi所有功能 基础命令&#xff1f; vi 文件路径 vim 文件路径 运行模式 …

二、yocto 集成ros2(基于raspberrypi 4B)

yocto 集成ros2 yocto 集成ros21. 下载ros layer2. 编译集成ros3. 功能验证 yocto 集成ros2 本篇文章为基于raspberrypi 4B单板的yocto实战系列的第二篇文章。 一、yocto 编译raspberrypi 4B并启动 本节我们将ros2机器人操作系统移植到我们的yocto系统里面。 1. 下载ros laye…

LLM如何处理长上下文:Lost in the middle

论文地址&#xff1a;Lost in the Middle: How Language Models Use Long Contexts 论文总结&#xff1a;写prompt的时候&#xff0c;需要注意内容的顺序&#xff0c;把重要的信息放在最前面或者最后面。 大型语言模型大有用处&#xff0c;在设计 prompt 方面&#xff0c;人们…

当OKR无法按时完成或达成时,应如何进行调整?

在企业管理中&#xff0c;OKR&#xff08;Objectives and Key Results&#xff0c;目标与关键成果&#xff09;作为一种有效的管理工具&#xff0c;被广泛用于设定和跟踪目标。然而&#xff0c;在实际执行过程中&#xff0c;OKR无法按时完成或达成的情况时有发生。面对这种情况…

IO多分复用

#include<myhead.h> #define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.65.131" //服务器IPint main(int argc, const char *argv[]) {//1、创建一个套接字int sfd -1;sfd socket(AF_INET, SOCK_STREAM, 0); //参数1&#xff1a;…

高效快捷的快递查询助手,让您随时随地掌握包裹最新状态

面对一堆快递单号&#xff0c;您是否还在手忙脚乱地逐个复制粘贴到网上查询物流信息&#xff1f;是否还在为如何保存查询好的物流信息而犯愁&#xff1f;别担心&#xff0c;固乔快递查询助手来帮您解决这些烦恼&#xff01; 固乔快递查询助手是一款功能强大的快递单号查询与管理…