C++——搜索二叉树

news2024/12/26 22:46:33

作者:几冬雪来

时间:2023年11月7日

内容:C++的搜索二叉树讲解

目录

前言: 

什么是搜索二叉树:

搜索二叉树的增删查改: 

搜索二叉树的定义初始化:

搜索二叉树增操作:

搜索二叉树找操作: 

搜索二叉树删操作:

搜索二叉树增删查改递归版本:

搜索二叉树查操作(递归版本): 

搜索二叉树增操作(递归版本):

搜索二叉树删操作(递归版本): 

其余操作:

析构函数: 

深拷贝: 

赋值:

搜索二叉树的应用: 

代码:

结尾: 


前言: 

在上一篇博客中我们艰难的结束了C++部分多态知识的讲解,多态属于C++的一个重要的知识。而在多态结束之后,下来要讲解的就是C语言时期二叉树进阶部分的知识,那在讲解二叉树的进阶之前这里要先了解一下搜索二叉树。 

 

什么是搜索二叉树:

在学习搜索二叉树之前我们先要了解什么是搜索二叉树

在C语言学习时期我们就有学习过普通的二叉树,而搜索二叉树就是在普通二叉树中再添加一些新的特征

那么它有什么不同的特征就成为了讨论的话题。

在这个地方如果左子树所有的节点都小于根,右子树的所有结点都大于根,每一棵子树都满足这个特征,这里这棵二叉树就是搜索二叉树

为什么这样的树被称为搜索二叉树

这里的搜索二叉树就是字面意思,方便搜索,因为这里左树的节点都小于根,右树的结点都大于根,加入我们要寻找的一个值比根大,这个地方我们就可以直接访问右树不用理会左树节点的值。 

那么在这个地方如果要从搜索二叉树中找到一个值,那么它最多是不是找高度次(O(N))

其实不然,通常我们的二叉树都是左边这种形式,都是如果将左边这棵二叉树换成根节点非常大的话就会变成右边这样的树

这个时候我们就不能简单的去分析它们搜索所用的时间

那么这个地方就会涉及到另外两棵树——红黑树和AVL树

与此同时在这个地方搜索二叉树也可以被叫做二叉查找树和二叉排序树

那么它为什么又被叫为二叉排序树呢,这里就要谈一谈这棵树的中序或者中序遍历是一个什么样子的情况了

如果搜索二叉树在这里走的是中序的话,那么它最后出来的结果就是有序的,因为搜索二叉树是左小右大的形式,中序排序的结果就是——左->中->右,所以说它的结果是有序的

那么接下来我们就来书写搜索二叉树的代码了。

搜索二叉树的增删查改: 

再接下来我们就要讲解搜索二叉树的增删查改了。

在C语言时期我们并没有书写普通二叉树的增删查改,这并不意味着二叉树的增删查改没有书写的必要,知识因为在普通二叉树中,因为插入的地方没有限制哪里都可以进行插入操作,所以使得这些操作没有意义

但是类似搜索二叉树这种特殊二叉树,对比起普通二叉树它就有限制插入的地方,所以特殊二叉树的增删查改是有意义的

搜索二叉树的定义初始化:

还是一样写二叉树的时候我们通常都会通过定义一个类来存储节点的左节点右节点和它自己本身的值 。 

这个地方的第一个模板我们用其来构造左右节点

第二个模板在私有处定义一个_root来作为头节点

最后的在第二个模板之中我们要typedef第一个模板将它重新命名,然后再对我们的头节点进行初始化

搜索二叉树增操作:

定义和初始化搜索二叉树之后,接下来我们就要根据搜索二叉树来书写它的增的操作

在这个地方我们定义一个bool函数,再然后就是进行判断

这里进行判断,如果一开始我们的树里面没有值,也就是树为空。这个时候就需要我们new一个节点作为我们的头节点使用

如果这棵树不是空的话这里就要定义一个cur和parent指针,然后进入循环判断以cur为条件,如果cur为空就意味着到了空节点

然后就是判断,如果父节点存放的值小于要插入的值这个地方就放在左边,反之则放在右边,这里注意有一个特殊情况,那就是要存的值和节点的值相同,这种情况我们的数据就不能进行存储,这里就需要返回false

 

但是上边的代码只是单纯的进行比较确定位置而已,这个地方要完成最终的插入操作的话,这里还要创建一个新的节点给cur

然后进行第二次比较,因为parent这个时候因为没有进行重新的赋值初始化,因此这里只需要判断一次然后将节点插入即可

这里的最后就是书写二叉树中序遍历的代码,先走左边然后走中间最后走右边,三者都是以递归的形式

搜索二叉树找操作: 

然后就是搜索二叉树中一个比较简单的操作,也就是查找操作

那么搜索二叉树的查找操作的代码要如何书写呢? 

 

这里就是我们查找操作的代码,它的代码甚至可以沿用搜索二叉树中增操作的代码, 因为查找操作比较简单,因此也不过多的讲解

搜索二叉树删操作:

与搜索二叉树查找操作的难度不同,删除可谓是搜索二叉树中增删查改里面最为困难的一个操作,删除操作才是搜索二叉树的重点

这个地方的删除操作分为几种情况。

第一种情况就是要删去这棵树值为7的节点,这里因为值为7的节点刚刚好是我们的叶子节点,因此这个地方只需要将值为6节点的右子树置空即可

接下来就是第二种情况,要删除的节点是值为14的节点。这里值为14的节点只有一个叶子节点,这里也是十分的容易,只需要将值为10的节点右子树链接上值为13的节点即可

当然以上两种删除都是较为简单的问题,接下来就是搜索二叉树难操作的地方了。

最后一种情况就是该节点有两个子节点,这里拿头节点举例子,假如这个地方我们将头节点删去,那么这棵树要怎么进行调整

这个地方我们不能顺便的拉一个节点来作为这棵树的头节点

这里解决问题的方法就是从这一棵树中找出左树最大值的节点又或者是右树最小值的节点来作为我们的头节点,这样才能真正的解决问题

接下来我们就来书写搜索二叉树删除操作的代码。

这个地方要实现删除操作,首先还是要定义一个自身节点和父节点,然后对其进行初始化的操作。 

如果然后就是判断节点值与左右子树的大小,如果我们要查找的值小于父节点的值就像左走,反之向右走。 

然后就是找到了的情况,这里先处理两个简单的情况,那就是这个地方删除的节点刚好左右子树右一边为空

然后对要删除的值之后就需要进一步的进行判断,最后进行链接,然后将cur的值进行删除即可。但是这个地方还有一种特殊的情况需要处理

 

如这里的这张图,这棵树只有右树,如果这里要删除的节点是数据为8的节点,这会导致程序的崩溃

而且应对这个问题的方法就是将根节点进行转移,从值为8节点转移到下一个节点中去

解决这种情况的方法就是在判断是否为空之后,对根节点进行判断,如果根节点就是我们要删除的值,这里就将下一个值定为新的根节点

接下来还有一种最复杂的情况,那就是左右都不为空的情况

这个地方如果左右都不为空的话,就需要我们去找左树的最大值又或者右树的最小值和这里要删除的值进行一个替换操作

这里我们就找左树的最大值,这里一开始就走节点的左边,接下来就是进入循环,因为我们这棵树是搜索二叉树,因此条件就是节点一直走右边,直到空为止

但是这里的代码还有许多坑的地方存在。

首先就是替换的问题,因为在图中7和8节点替换之后,这里我们就要重新编译一遍,所以为了防止找不到要替换的值,这里我们需要找到父节点

然后就是它的代码。

但是如果这样子书写的话又会有特殊情况的出现。

特殊情况也会导致最后代码的崩溃。

就如上图,原本值为3的节点有左树无右树,同时它也是根节点的下一个节点。这个地方如果要3和8的节点进行调换的话

如果是这种情况的话,判断条件的leftMax->_right指向的刚刚好是空,这会导致循环进不去有间接的让父节点依旧为空

这里经过一系列的修改之后,我们的代码呈现这个样子。首先为了防止父节点为空的情况发生,一开始我们就不将它初始化为空

然后就是判断,如果父节点的下一个结点正好是左树最大节点(要删除的节点),同时这个它的右树为空。 

这里我们就要将父节点的左给leftMax的左,反之则是父节点的将leftMax的左给父节点的右。最后将leftMax给给cur,然后将cur进行delete操作即可删除这个值的节点

搜索二叉树增删查改递归版本:

上边讲解了搜索二叉树的增删查,这个地方我们并没有再写搜索二叉树修改数据的代码,因为在这之前我们要实现搜索二叉树增删查递归方式的书写

这里就要查找操作来举例

这里要注意一个点,那就是在C++当中凡是要递归走树型结构我们都需要写一个东西

这个地方要想写搜索二叉树增删查改递归版本的话,首要的就是要在私有类中书写一个子函数,因为这里我们要实行递归操作就必须有数的变化。(有变化才能递归)

因此如果要书写搜索二叉树递归部分的代码的话,我们就要将可能会发生变化的root加上

搜索二叉树查操作(递归版本): 

知道了这一个知识点之后,接下来我们就来书写搜索二叉树查操作的代码

这里的查操作的递归相较于删和增的递归版本要容易一些

这个地方我们的代码这样书写即可。如果值大于要找的值,这个地方就进入递归它的右边,如果小的话就递归它的左边,如果找到的话,这里我们就只需要返回true即可

这就是查找操作递归方法的书写。 

搜索二叉树增操作(递归版本):

在讲解完了二叉树递归版本的查找数据后,接下来就来讲解搜索二叉树的增操作代码要如何去书写

这就是我们插入时候的代码了,中间查找比较大小的操作,这段代码可以直接将查操作的代码拿过来使用,这里就不过多的讲解了

但是这个地方的代码有些许的不同,可以看见在函数对象中我们为root加入了一个引用操作

这是因为要在递归判断之后,顺便将我们要插入的节点插入进去

对象中引用操作也就是在这里进行使用,这里root如果为空的话,也就是找到了要插入的位置。这个地方就不需要像以前一样重新遍历一遍找到父节点进行插入

因为有引用的存在,这个地方如果root为空找到插入的位置,因为是引用因为我们就能直接new一个新节点出来,将值放进去,这样就能完成需求

搜索二叉树删操作(递归版本): 

在这个地方搜索二叉树的删除操作递归形式可以算我们增删查改中最为困难的一个操作了。

平时考试,问答搜索二叉树的问题,一般都是提问搜索二叉树增删查改中删除操作,那么下面我们就来看看删除操作要怎么书写?

这个地方要进行删除操作的话,首先还是免不了使用查找操作来找到我们想要删掉值的节点,这里简单来说就是进行比较。

如果大于就走左边,小于就走右边

最后也是当我们找到要删除值的节点了,这里的代码是最具有难度的,因为删除操作要兼顾三种情况,也就是左为空,右为空和左右都不为空的情况

这里如果两边有一边为空的情况我们就像上图一样书写,这个地方在一开始要为root加一个引用,这样子就可以通过引用来改变指向,从而做到删除的作用

类似有一个值为14的节点,它有一个父节点和一个右边的子节点,这里判断后将root->_right赋给root,这样就使得递归回去14的父节点会指向14的子节点

然后就是两边都不为空的情况

这里先定义一个del,在else中我们先找左树的最大节点,找到那个节点之后在将根节点和这个左树最大节点替换

然后再走一遍EraseR进行删除操作,这个地方要记得传的参数为root->_left而不是leftMax,这是因为leftMax为临时变量,如果传临时变量的话再怎么修改递归中的root,它都没有实际效果

其余操作:

在讲解完了两个版本的增删查改后,接下来我们就简单将剩下的一些操作的代码给它写上和补齐。 

析构函数: 

有一开始有构造函数,那么结束的时候就有析构函数,在这里我们就来书写搜索二叉树的析构函数,用递归的形式。

书写析构函数之前需要我们去写出它的一个子函数,因为我们析构递归没有参数

这里使用递归去析构函数而不是循环,是因为通常析构二叉树或者多叉树都是从数的叶子结点开始往回析构,都是后序遍历删除比较容易,循环析构比较麻烦

这里我们就只需要递归一下左边再递归一下右边,然后将root节点删除,最后要记得参数加上引用符合后将删除的节点进行置空操作

深拷贝: 

然后接下来因为上面的析构为递归式的析构,因此在这里会发生浅拷贝的问题,这里就需要我们写一个深拷贝的代码来解决这个问题。

这里我们就需要拷贝一棵新树出来,先将这棵树传出去,然后判断是否为空树,如果为空树那么就返回空

如果不为空树那就先创建一个头节点出来,然后就是前序遍历拷贝,递归拷贝先拷贝左在拷贝右。将这棵树拷贝出来最后返回这棵树的根节点即可。 

赋值:

最后的一个常用操作就是赋值操作,这里就直接写代码即可。

这里就是简单的交换操作

最后返回this指针即可

搜索二叉树的应用: 

在了解完了搜索二叉树后,我们就要来了解搜索二叉树会在哪些地方被应用到。

如上图,搜索二叉树通常都是用来解决K(key),V(value)问题。 

代码:

#pragma once

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

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

	}
};

template<class K>
class BSTree
{
	typedef BSTrssNode<K> Node;
public:
	BSTree()
		:_root(nullptr)
	{

	}

	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}

	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(_root, t._root);
		return *this;
	}

	~BSTree()
	{
		Destory(_root);
	}

	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node* parent = cur;
		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 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
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_right;
						}
						else
						{
							parent->_left = cur->_right;
						}
					}
				}
				else if(cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_right == cur)
						{
							parent->_right = cur->_left;
						}
						else
						{
							parent->_left = cur->_left;
						}
					}
				}
				else
				{
					Node* parent = cur;
					Node* leftMax = cur->_left;
					while (leftMax->_right)
					{
						parent = leftMax;
						leftMax = leftMax->_right;
					}

					swap(cur->_key, leftMax->_key);

					if (parent->_left == leftMax)
					{
						parent->_left = leftMax->_left;
					}
					else
					{
						parent->_right = leftMax->_left;
					}
					cur = leftMax;
				}
				
				delete cur;
				return true;
			}
		}
		return 0;
	}

	void InOrder()
	{
		_InOrder(_root);
	}

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

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

	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}

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

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 Destory(Node*& root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(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* leftMax = root->_left;
				while (leftMax->_right)
				{
					leftMax = leftMax->_right;
				}
				swap(root->_key, leftMax->_key);
				return EraseR(root->_left, 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 _FindR(root->_right, key);
		}
		else if (root->_key > key)
		{
			return _FindR(root->_left, key);
		}
		else
		{
			return true
		} 
	}

	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
		}
   }

private:
	Node* _root;
};

void TestBSTree1()
{

}

结尾: 

到这里我们的搜索二叉树的基本内容就讲解完了,但是这并不意味着它的结束,在上文我们有说过。搜索二叉树还涉及到我们C++中的AVL树和红黑树,同时K,V问题也是后面我们要学习好了解的问题,最后希望之篇博客能带来帮助。

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

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

相关文章

蓝桥杯算法竞赛系列第十章·nSum问题的代码框架

你好&#xff0c;我是安然无虞。 文章目录 一、两数之和变形题 二、三数之和三、四数之和 首先&#xff0c;何为nSum问题呢&#xff1f; nSum问题其实就是给你一个数组nums和一个目标和target&#xff0c;让我们从nums数组中选择n个数&#xff0c;使得这些数字之和等于target。…

Flink集群的搭建

1、Flink独立集群模式 1、首先Flink的独立集群模式是不依赖于Hadoop集群。 2、上传压缩包&#xff0c;配置环境&#xff1a; 1、解压&#xff1a; tar -zxvf flink-1.15.2-bin-scala_2.12.tgz2、配置环境变量&#xff1a;vim /etc/profileexport FLINK_HOME/usr/local/soft/fl…

C++深度优先搜索(DFS)算法的应用:树中可以形成回文的路径数

本文涉及知识点 深度优先搜索(DFS) 状态压缩 题目 给你一棵 树&#xff08;即&#xff0c;一个连通、无向且无环的图&#xff09;&#xff0c;根 节点为 0 &#xff0c;由编号从 0 到 n - 1 的 n 个节点组成。这棵树用一个长度为 n 、下标从 0 开始的数组 parent 表示&#…

3.2-Docker Image概述

常用docker命令&#xff1a; 查看docker image有哪些 docker image ls Image的获取方式

亚马逊云科技产品测评』活动征文|通过使用Amazon Neptune来预测电影类型初体验

文章目录 福利来袭Amazon Neptune什么是图数据库为什么要使用图数据库什么是Amazon NeptuneNeptune 的特点 快速入门环境搭建notebook 图神经网络快速构建加载数据配置端点Gremlin 查询清理 删除环境S3 存储桶删除 授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转…

jstat虚拟机统计信息监控工具

jstat虚拟机统计信息监控工具 1、jstat&#xff08;JVM Statistics Monitorning Tool&#xff09; 用于监控虚拟机各种运行状态信息的命令行工具。 它可以显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据&#xff0c;它是运行期定位虚拟机 性能问题…

Lenovo联想IdeaPad 15 ALC7(82R4)2022款笔记本原装出厂Windows11预装系统镜像

下载链接&#xff1a;https://pan.baidu.com/s/1Pr2G42Sz1LKzgF8OcEBkPQ?pwd5ms2 提取码&#xff1a;5ms2 系统自带所有驱动、出厂主题壁纸、系统属性专属LOGO标志、Office办公软件、联想电脑管家等预装程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;…

centos7.9 postgresql 16.0 源码安装部署

postgresql 16.0 源码安装部署 环境准备 系统主机名IP地址centos7.9postgres192.168.200.56 软件准备 postgresql-16.0.tar.gz https://ftp.postgresql.org/pub/source/v16.0/postgresql-16.0.tar.gz依赖安装 yum -y install systemd-devel readline readline-devel zlib-devel…

探秘Python闭包与作用域

文章目录 闭包的定义与作用LEGB规则nonlocal与global关键字在Python的世界里,理解闭包(Closure)和作用域(Scope)是提升编程技巧和深度的一大步。这篇文章将带你深入了解闭包的神秘面纱,掌握LEGB规则,并使用nonlocal与global关键字来巧妙控制变量作用域。 闭包的定义与作…

MySQL中的刷脏机制详解

名词解释 脏页&#xff1a;当内存数据页跟磁盘数据页内容不一致的时候&#xff0c;我们称这个内存页为“脏页”。 干净页&#xff1a;内存数据写入到磁盘后&#xff0c;内存和磁盘上的数据页的内容就一致了&#xff0c;称为“干净页”。 LSN&#xff1a;称为日志的逻辑序列号(l…

万物社用户运营工具:无代码开发下的电商平台和CRM集成

简介&#xff1a;万物社与集简云的引领式连接 万物社&#xff0c;隶属于厦门头号云信息科技有限公司&#xff0c;是一家专注于互联网和相关服务的企业。在日常的业务运营中&#xff0c;万物社通过与集简云的无代码集成&#xff0c;实现了业务流程的自动化和智能化&#xff0c;…

学习笔记4——JVM运行时数据区梳理

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/contents/3/192489.html 类装载器classLoader&#xff1a; 将本地的字节码文件.class 加载到内存方法区中成为元数据模板&#xff08;两个class对象是否为同一个类要求&#xff1a;完整…

对话句子互动创始人李佳芮 | AIGC结合私域运营影响不可估量

“ 创业最核心的就是耐心” 口述 | 李佳芮 整理 | 小白&云舒 出品&#xff5c;极新 极新请文心一言分析了私域流量运营和chatbot当下的发展背景&#xff0c;它给出了以下答案&#xff1a; 1. 移动设备普及和网络速度提升&#xff1a;随着智能手机和移动互联网的普及&…

UE5.3实现1秒12帧风格的动画抽帧效果

现今一些卡通风格游戏会刻意模仿早期动画1秒12帧的播放效果&#xff0c;以营造较强的风格化体验&#xff0c;博主在UE5中实现了一下&#xff08;左侧正常动画&#xff0c;右侧抽帧动画&#xff09;&#xff1a; 我们可以通过在UE中对导入设置进行一些修改&#xff0c;达到不改…

Android UI 开发·界面布局开发·案例分析

目录 ​编辑 1. 线性布局&#xff08;LinearLayout&#xff09; 2. 相对布局&#xff08;RelativeLayout&#xff09; 3. 表格布局&#xff08;TableLayout&#xff09; 4. 帧布局&#xff08;FrameLayout&#xff09; 5. 网格布局&#xff08;GridLayout&#xff0…

AI:68-基于深度学习的身份证号码识别

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

C语言编写一个程序采集招聘信息

因为在这里无法详细解释每行代码和步骤。但是&#xff0c;我可以给大家一个使用Python和requests库编写的简单爬虫程序的例子&#xff0c;它可以从网站上获取招聘信息。你可以根据这个例子&#xff0c;将其改写为使用C语言编写的爬虫程序。 import requests# 指定爬虫IP信息 pr…

二叉树的OJ题——C++

一、根据二叉树创建字符串 题目链接&#xff1a; 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 前序遍历二叉树&#xff0c;并且将结果存入到一个string中&#xff0c;并且要使用括号去分割和表示每个节点和子树之间的关系&…

大漠插件(二、Qt使用插件时注意事项)

本章目的 在上篇已经注册完毕大漠&#xff0c;那么怎么使用大漠来制作脚本&#xff0c;我选择了我最熟悉的Qt来开发&#xff0c;毕竟只是小软件&#xff0c;用脚本或者c都差不了多少。本章就是开发途中的一些坑。 本人开发环境是 win11 64、Qt 5.15.2安装了5.10.0的msvc2015 32…

接口测试工具的实验,Postman、Swagger、knife4j(黑马头条)

一、Postman 最常用的接口测试软件&#xff0c;需要注意点&#xff1a;在进行post请求时&#xff0c;需要选择JSON形式发送 输入JSON字符串&#xff0c;比如&#xff1a; {"maxBehotTime": "2021-04-19 00:19:09","minBehotTime": "2021-…