【数据结构高阶】二叉搜索树

news2024/11/27 10:39:14

接下来我们来开始使用C++来详细讲解数据结构的一些高阶的知识点

本期讲解的是二叉搜索树,对于初阶二叉树有所遗忘的同学可以看到这里:

【精选】【数据结构初阶】链式二叉树的解析及一些基本操作

讲解二叉搜索树主要是为了后面的map和set做铺垫,废话不多说我们直接上干货:


目录

一、二叉搜索树的概念

二、模拟实现二叉搜索树

2.1 插入数据

2.1.1 插入数据的非递归实现

2.2 遍历数据

2.3 查找数据

2.4 删除数据

2.4.1 删除数据的非递归实现

2.5 模拟实现二叉搜索树的全部代码


一、二叉搜索树的概念

二叉搜索树又称二叉排序树(BST, Binary Search Tree),它可以是一棵空树,或者是具有以下性质的二叉树:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

 例如:

 

我们可以发现的一点:无论是什么样的二叉搜索树,使用中序遍历,遍历出的值都是升序排列的

二、模拟实现二叉搜索树

下面又到了我们最激动人心的代码实现环节,本次代码实现我们还是要基于链式二叉树的实现:

template<class K>
struct BSTreeNode//节点
{
	BSTreeNode<K>* _lchild;
	BSTreeNode<K>* _rchild;
	K _key;

	BSTreeNode(const K& key)
		:_lchild(nullptr),
		_rchild(nullptr),
		_key(key)
	{}
};

2.1 插入数据

我们可以根据二叉搜索树的规律来向其中插入数据,但是插入数据时需要注意一点:要记录插入节点的上一个父节点,将插入的节点连接上二叉树:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			_root->_key = key;
			return true;
		}
		Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置
		Node* parent = cur;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
			{
				cur = cur->_lchild;
			}
			else if (key > cur->_key)
			{
				cur = cur->_rchild;
			}
			else
			{
				return false;//这里创建的二叉搜索树不允许出现值的冗余
			}
		}
		cur = new Node(key);//创建
		//将创建的节点链接到二叉树上
		if (key < parent->_key)
		{
			parent->_lchild = cur;
		}
		else
		{
			parent->_rchild = cur;
		}
		return true;
	}

private:
	Node* _root = nullptr;//根节点
};

2.1.1 插入数据的非递归实现

递归的效率并没有循环高,那为什么要说一下插入数据的非递归实现呢

主要是非递归的数据插入的传值方法值得一说:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool InsertR(const K& key)//插入数据(递归)
	{
		return _InsertR(_root, key);
	}

	bool _InsertR(Node*& root,const K& key)//这里使用指针的引用用来直接修改其父节点指针的指向
	{
		if (root == nullptr)
		{
			root = new Node(key);
			return true;
		}
		if (root->_key > key)
		{
			_InsertR(root->_lchild, key);
		}
		else if (root->_key < key)
		{
			_InsertR(root->_rchild, key);
		}
		else
		{
			return false;
		}
	}
private:
	Node* _root = nullptr;//根节点
};

我们可以看到在递归时,传入的形参类型为Node*&,这样可以直接在其函数内部习惯其父节点孩子指针的指向

那为什么要写两个插入函数呢?因为如果我们直接使用_InsertR函数,无法直接使用对象对_InsertR函数进行传参

2.2 遍历数据

因为二叉搜索树的性质,这里我们采用中序遍历: 

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)插入数据
	{
        ....
	}

	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}

	void _InOrder(Node* root)
	{
		if (root == NULL)//如果是空树就直接结束
		{
			return;
		}
		_InOrder(root->_lchild);//先递归遍历其左子树
		cout << root->_key << " ";//再遍历其根节点
		_InOrder(root->_rchild);//最后递归遍历其右子树
	}

private:
	Node* _root = nullptr;//根节点
};

那为什么要写两个中序遍历函数呢?因为如果我们直接使用_InOrder函数,无法直接使用对象对_InOrder函数进行传参

2.3 查找数据

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)//插入数据
	{
        ......
	}

	void InOrder()//中序遍历
	{
        ......
	}

	void _InOrder(Node* root)
	{
        ......
	}

	bool Find(const K& key)//查找数据
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				cur = cur->_lchild;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

private:
	Node* _root = nullptr;//根节点
};

2.4 删除数据

对于在二叉搜索树中删除数据,我们就要好好说道说道了

下面我们有这样的一个二叉搜索树:

现在我们要删除其叶子节点,这很容易,直接删除完,再将其父节点对应的孩子指针置空即可

那我们要删只有一个孩子节点的数据呢?例如14和10

这个稍微麻烦一点,删除完该节点后将其孩子节点托付给其父节点即可:

那要删带有两个孩子节点的数据呢?例如3、6、8:

对于这种情况我们可以选择其节点下的左子树的最大节点(左子树的最右节点),或者右子树的最小节点(右子树的最左节点)来替代要删除的节点:

综上所述,要删除的结点可能分下面四种情况:

a. 要删除的结点无孩子结点

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

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

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

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

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

思路我们有了,下面用代码来实现一下:

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert(const K& key)//插入数据
	{
        ...
	}

	void InOrder()//中序遍历
	{
        ...
	}

private:
	void _InOrder(Node* root)
	{
        ...
	}

public:
	bool Find(const K& key)//查找数据
	{
        ...
	}

	bool Erase(const K& key)
	{
		Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置
		Node* parent = cur;//记录cur的父节点
		while (cur)//寻找需要删除的节点
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_lchild;
			}
			else//找到了,开始删除
			{
				if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点
				{
					if (parent == cur)//删除的是根节点
					{
						_root = nullptr;//更新根节点
					}
					//将其父节点指向的自身的指针置空
					else if (parent->_lchild == cur)
					{
						parent->_lchild = nullptr;
					}
					else
					{
						parent->_rchild = nullptr;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_lchild;//更新根节点
					}
					//将删除节点的左孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_lchild;
					}
					else
					{
						parent->_rchild = cur->_lchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_rchild;//更新根节点
					}
					//将删除节点的右孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_rchild;
					}
					else
					{
						parent->_rchild = cur->_rchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换
				{
					//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)
					Node* maxleft = cur->_lchild; // 记录左子树的最大节点
					Node* pmaxleft = cur;//记录左子树的最大节点的父节点
					while (maxleft->_rchild)//查找左子树的最大节点
					{
						pmaxleft = maxleft;
						maxleft = maxleft->_rchild;
					}
					//将找到的节点替换要删除的节点
					cur->_key = maxleft->_key;
					if (pmaxleft->_lchild == maxleft)
					{
						pmaxleft->_lchild = maxleft->_lchild;
					}
					else
					{
						pmaxleft->_rchild = maxleft->_lchild;
					}
					//释放节点空间
					delete maxleft;
					maxleft = nullptr;
				}
				return true;
			}
		}
		return false;//没找到要删除的节点
	}
private:
	Node* _root = nullptr;//根节点
};

2.4.1 删除数据的非递归实现

template<class K>
class BSTree 
{
	typedef BSTreeNode<K> Node;
public:
	bool EraseR(const K& key)//递归删除数据
	{
		return _EraseR(_root, key);
	}

	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		else if (root->_key < key)
		{
			_EraseR(root->_rchild, key);
		}
		else if (root->_key > key)
		{
			_EraseR(root->_lchild, key);
		}
		else
		{
			Node* del = root;
			if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点
			{
				//释放节点空间
				delete root;
				//将其父节点指向的自身的指针置空
				root = nullptr;
			}
			else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
			{
				//将删除节点的左孩子交给其父节点
				root = root->_lchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
			{
				//将删除节点的右孩子交给其父节点
				root = root->_rchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换
			{
				//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)
				Node* minright = del->_rchild; // 记录右子树的最小节点
				while (minright->_lchild)//查找右子树的最小节点
				{
					minright = minright->_lchild;
				}
				root->_key = minright->_key;//将找到的节点替换要删除的节点
				return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点
			}
			return true;
		}
	}

private:
	Node* _root = nullptr;//根节点
};

 

2.5 模拟实现二叉搜索树的全部代码

#include<iostream>
using namespace std;

template<class K>
struct BSTreeNode//节点
{
	BSTreeNode<K>* _lchild;
	BSTreeNode<K>* _rchild;
	K _key;

	BSTreeNode(const K& key)
		:_lchild(nullptr),
		_rchild(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);
			_root->_key = key;
			return true;
		}
		Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置
		Node* parent = cur;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (key < cur->_key)
			{
				cur = cur->_lchild;
			}
			else if (key > cur->_key)
			{
				cur = cur->_rchild;
			}
			else
			{
				return false;//这里创建的二叉搜索树不允许出现值的冗余
			}
		}
		cur = new Node(key);//创建
		//将创建的节点链接到二叉树上
		if (key < parent->_key)
		{
			parent->_lchild = cur;
		}
		else
		{
			parent->_rchild = cur;
		}
		return true;
	}

	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)
		{
			_InsertR(root->_lchild, key);
		}
		else if (root->_key < key)
		{
			_InsertR(root->_rchild, key);
		}
		else
		{
			return false;
		}
	}

public:
	void InOrder()//中序遍历
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	void _InOrder(Node* root)
	{
		if (root == NULL)//如果是空树就直接结束
		{
			return;
		}
		_InOrder(root->_lchild);//先递归遍历其左子树
		cout << root->_key << " ";//再遍历其根节点
		_InOrder(root->_rchild);//最后递归遍历其右子树
	}

public:
	bool Find(const K& key)//查找数据
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				cur = cur->_lchild;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

	bool Erase(const K& key)//删除数据
	{
		Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置
		Node* parent = cur;//记录cur的父节点
		while (cur)//寻找需要删除的节点
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_rchild;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_lchild;
			}
			else//找到了,开始删除
			{
				if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点
				{
					if (parent == cur)//删除的是根节点
					{
						_root = nullptr;//更新根节点
					}
					//将其父节点指向的自身的指针置空
					else if (parent->_lchild == cur)
					{
						parent->_lchild = nullptr;
					}
					else
					{
						parent->_rchild = nullptr;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_lchild;//更新根节点
					}
					//将删除节点的左孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_lchild;
					}
					else
					{
						parent->_rchild = cur->_lchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
				{
					if (parent == cur)//删除的是根节点
					{
						_root = cur->_rchild;//更新根节点
					}
					//将删除节点的右孩子交给其父节点
					else if (parent->_lchild == cur)
					{
						parent->_lchild = cur->_rchild;
					}
					else
					{
						parent->_rchild = cur->_rchild;
					}
					//释放节点空间
					delete cur;
					cur = nullptr;
				}
				else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换
				{
					//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)
					Node* maxleft = cur->_lchild; // 记录左子树的最大节点
					Node* pmaxleft = cur;//记录左子树的最大节点的父节点
					while (maxleft->_rchild)//查找左子树的最大节点
					{
						pmaxleft = maxleft;
						maxleft = maxleft->_rchild;
					}
					//将找到的节点替换要删除的节点
					cur->_key = maxleft->_key;
					if (pmaxleft->_lchild == maxleft)
					{
						pmaxleft->_lchild = maxleft->_lchild;
					}
					else
					{
						pmaxleft->_rchild = maxleft->_lchild;
					}
					//释放节点空间
					delete maxleft;
					maxleft = nullptr;
				}
				return true;
			}
		}
		return false;//没找到要删除的节点
	}


	bool EraseR(const K& key)//递归删除数据
	{
		return _EraseR(_root, key);
	}

private:
	bool _EraseR(Node*& root, const K& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		else if (root->_key < key)
		{
			_EraseR(root->_rchild, key);
		}
		else if (root->_key > key)
		{
			_EraseR(root->_lchild, key);
		}
		else
		{
			Node* del = root;
			if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点
			{
				//释放节点空间
				delete root;
				//将其父节点指向的自身的指针置空
				root = nullptr;
			}
			else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空
			{
				//将删除节点的左孩子交给其父节点
				root = root->_lchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空
			{
				//将删除节点的右孩子交给其父节点
				root = root->_rchild;
				//释放节点空间
				delete del;
				del = nullptr;
			}
			else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换
			{
				//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)
				Node* minright = del->_rchild; // 记录右子树的最小节点
				while (minright->_lchild)//查找右子树的最小节点
				{
					minright = minright->_lchild;
				}
				root->_key = minright->_key;//将找到的节点替换要删除的节点
				return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点
			}
			return true;
		}
	}

private:
	Node* _root = nullptr;//根节点
};

本期博客到这里就结束了,代码量较大,还请各位仔细分析呀

我们下期见~

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

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

相关文章

安装pycharm选择路径时,点击next无反应

安装的时候断网就可以解决该问题。

空调能量表

数字化应用场景&#xff1a;空调能量监测 定义 空调能量表产品又被称为冷量积算仪、冷量积分仪、能量积分仪、能量积算仪、空调冷热量表、冷量表、能量表等&#xff0c;现阶段行业内没有统一的名称。 作用 用于计量中央空调能耗的仪表&#xff0c;它通过和空调管道流量计和温…

《增长黑客》思维导图

增长黑客这个词源于硅谷&#xff0c;简单说&#xff0c;这是一群以数据驱动营销、以迭代验证策略&#xff0c;通过技术手段实现爆发式增长的新型人才。 近年来&#xff0c;互联网公司意识到这一角色可以发挥四两拨千斤的作用&#xff0c;因此对该职位的需求也如井喷式增长。本…

基于C#实现五家共井

古代数学巨著《九章算数》中有这么一道题叫“五家共井&#xff0c;甲二绠&#xff08;汲水用的井绳&#xff09;不足&#xff0c;如&#xff08;接上&#xff09;乙一绠&#xff1b;乙三绠不足&#xff0c;如丙一绠&#xff1b;丙四绠不足&#xff0c;如丁一绠&#xff1b;丁五…

SQL存储过程和函数

SQL存储过程和函数 变量系统变量用户定义变量局部变量 存储过程存储函数 变量 在MySQL中变量分为三种类型: 系统变量、用户定义变量、局部变量。 系统变量 系统变量 是MySQL服务器提供&#xff0c;不是用户定义的&#xff0c;属于服务器层面。分为全局变量&#xff08;GLOBA…

软件开发和软件测试,到底学哪个好呢?

写在前面&#xff1a;买车没有最好&#xff0c;只有最适合。 类似这类“很难选择”的问题&#xff0c;在知乎上其实有很多。 比如&#xff1a;“该去年薪10w的国家电网&#xff0c;还是去年薪40w的互联网大厂”&#xff1b; 比如&#xff1a;“城里有房&#xff0c;剩下的100…

营业执照识别

目录 1、制作文本检测数据集 2、导出文本检测推理模型 3、制作语义实体识别数据集 4、训练模型 5、模型预测 6、模型导出 7、模型推理 1、制作文本检测数据集 文本检测数据集格式如下&#xff1a; dataset_dir # 数据集根目录&#xff0c;目录名称可以改变 ├── im…

一招告别百度广告烦恼,同时效率提高100倍的几个常用搜索技巧!

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

软件测试面试-如何定位线上出现bug

其实无论是线上还是在测试出现bug&#xff0c;我们核心的还是要定位出bug出现的原因。 定位出bug的步骤&#xff1a; 1&#xff0c;如果是必现的bug&#xff0c;尽可能的复现出问题&#xff0c;找出引发问题的操作步骤。很多时候&#xff0c;一个bug的产生&#xff0c;很多时…

可以写进简历的软件测试项目实战经验(包含电商、银行、app等)

前言&#xff1a; 今天给大家带来几个软件测试项目的实战总结及经验&#xff0c;适合想自学、转行或者面试的朋友&#xff0c;可以写进简历里的那种哦。 1、项目名称: 家电购 项目描述&#xff1a; “家电购”商城系统是基于 web 浏览器的电子商务系统&#xff0c;通过互联网…

GBase8a-GDCA-第二次阶段测试

文章目录 主要内容在这里插入图片描述 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1d552b9d6d204f4fb4280ccc52807ed5.png)在这里插入图片描述 总结 主要内容 GBase8a-GDCA-第二次阶段测试及答案 总结 以上是今天要讲的内容&#xff0c;GBase8a-GDCA-第二次阶段测试…

clion qt导出dll给别的项目用

clion 导出dll给别的项目用&#xff0c;并且引入matplotcpp dll生成一个mydll的dll文件 1.先做一个简单dll的测试下 cmake_minimum_required(VERSION 3.26) project(untitled) set(CMAKE_CXX_STANDARD 11) add_library(untitled SHARED main.cpp)main.h void hello();main.cp…

在windows下vs c++运行g2o的BA优化程序示例

目录 1、前言2、准备工作安装git安装vcpkg&#xff08;1&#xff09;下载&#xff08;2&#xff09;安装&#xff08;3&#xff09;集成至vs 安装cmake 3、安装g2o4、安装opencv&#xff08;1&#xff09;下载&#xff08;2&#xff09;双击安装&#xff08;3&#xff09;环境变…

Redis应用场景及常见的数据类型

目录 一、Redis应用场景 1.1 Redis作为缓存 1.2 Redis作为消息队列 1.3 实现计数器和排行榜 1.4 实现分布式锁及分布式会话管理 二、Redis常见的数据类型 2.1 String&#xff08;字符串&#xff09;类型 2.2 list类型 2.3 Hash类型 2.4 Set类型 2.5 Sorted Set 一、Redis应用场…

前端开发学习 (一) 搭建Vue基础环境

一、环境搭建 1、安装nodejs #下载地址 https://nodejs.org/dist/v20.9.0/node-v20.9.0-x64.msi 2、配置环境变量 上面下载完安装包后自行安装&#xff0c;安装完成后安装下图操作添加环境变量 #查看版本 node --version v20.9.0# npm --version 10.1.03、配置npm加速源 np…

机器学习的逻辑回归

Sigmoid函数 逻辑回归的预测函数 梯度下降法-逻辑回归 import matplotlib.pyplot as plt import numpy as np # 生成一个关于分类器性能的详细报告。 # 这个报告包含了每个类别的精度、召回率、F1分数&#xff0c;以及所有类别的平均精度、召回率和F1分数 from sklearn.metri…

leetcode:367. 有效的完全平方数(python3解法)

难度&#xff1a;简单 给你一个正整数 num 。如果 num 是一个完全平方数&#xff0c;则返回 true &#xff0c;否则返回 false 。 完全平方数 是一个可以写成某个整数的平方的整数。换句话说&#xff0c;它可以写成某个整数和自身的乘积。 不能使用任何内置的库函数&#xff0c…

科技云报道:Citrix正式退出中国市场!国产们谁能真正顶上?

科技云报道原创。 2023年12月3日&#xff0c; Citrix&#xff08;思杰&#xff09;发布的公告将全面生效&#xff0c;中国市场&#xff08;包括香港地区和澳门地区&#xff09;也会停止所有新的交易。 这个消息&#xff0c;无疑是引起了业界的热议&#xff0c;毕竟Citrix可以…

11.11作业题

1.不死兔子 def fib(n):if n < 4:return 1else:return fib(n-1) fib(n-2) print("一年后共繁殖{}对兔子".format(fib(12))) 2.输入字符串&#xff0c;判断该字符串是否是回文字符串 s input("请输入一个字符串&#xff1a;") if not s:print("…