【C++ STL】-- 二叉搜索树

news2025/7/13 5:24:28

目录

二叉搜索树概念

二叉搜索树的应用

二叉搜索树的实现(K模型)

构造函数

默认拷贝构造函数

赋值运算符重载函数

普通写法:

进阶写法:

析构函数

插入函数(Insert)的实现

常规实现:

递归实现:

删除函数(Erase)的实现

常规实现: 

递归实现:

查找函数(Find)的实现

常规实现:

递归实现:

代码汇总


二叉搜索树概念

        所谓二叉树(binary tree),其意义是:“任意节点最多只允许两个子节点”。这两个子节点称为左子节点和右子节点。

        所谓二叉搜索树(binary search tree),可提供对数时间(logarithmic time)的元素插入和访问。

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

二叉搜索树的应用

        K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到

的值。
比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
  • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树。
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

K模型节点结构:

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

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

        KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方

式在现实生活中非常常见:
比如:英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
  • 文单词与其对应的中文<word, chinese>就构成一种键值对;
  • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对

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)
	{}
};

二叉搜索树的实现(K模型)

(KV模型同样思维)

  • 便于实现函数接口,将类 BSTreeNode<K> 进行重命名。
  • 定义一个 BSTreeNode<K>* 类型的_root作为头节点。
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>
struct BSTree
{
	typedef BSTreeNode<K> Node; //便于后期使用

    //…………(实现函数)

	Node* _root = nullptr; //头节点
};

构造函数

        使用编译器自动生成的即可。但是,由于需要写拷贝构造函数,所以要强制编译器生成默认的构造。

// C++的用法:强制编译器生成默认的构造
BSTree() = default;

默认拷贝构造函数

        利用前序递归。赋值并按相同顺序链接。

BSTree(const BSTree<K>& t)
{
	_root = _Copy(t._root);
}
		
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;
}

赋值运算符重载函数

普通写法:

        利用前序递归。赋值并按相同顺序链接。

const BSTree<K>& operator=(const BSTree<K>& t)
{
	if (this != &t) //防止自己给自己赋值
	{
		_root = _Copy(t._root);
	}
	return *this;
}

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

进阶写法:

        利用临时变量t进行接收,通过t可以间接调用BSTree的拷贝构造函数完成拷贝构造出t。然后只需要将,拷贝构造出来的对象的二叉搜索树头节点与this对象的二叉搜索树头节点进行交换,而对象t会在该赋值运算符重载函数调用结束时自动析构。

// 利用t间接调用拷贝构造函数,后赋值给this->_root
BSTree<K>& operator=(BSTree<K> t)
{
	swap(_root, t._root);
	return *this;
}

析构函数

         对于析构函数,需要将每一个节点都进行释放。所以需要利用后序释放。从下面的节点开始进行释放空间。

//释放树中结点
void Destory(Node* root)
{
	if (root == nullptr)
		return;

    // 释放左子树中的结点
	Destory(root->_left); 
    // 释放右子树中的结点
	Destory(root->_right);
	delete root;
    root = nullptr;
}

~BSTree()
{
    //递归实现删除
	Destory(_root);
	_root = nullptr;
}

插入函数(Insert)的实现

二叉搜索树的插入

  1. 树为空,则直接新增节点,赋值给root指针。
  2. 树不空,按二叉搜索树性质查找插入位置,插入新节点。

常规实现:

若不是空树,插入结点的具体操作如下:

  1. 若待插入结点的值等于根结点的值,则插入结点失败。
  2. 若待插入结点的值大于根结点的值,则需要将结点插入到右子树当中。
  3. 若待插入结点的值小于根结点的值,则需要将结点插入到左子树当中。

(因为二查搜索树一个数只能存储一次,所以循环到空即一定是可以插入的)

// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Insert(const K& key)
{
	// 如果树为空,直接插入
	if (_root == nullptr)
	{
		_root = new Node(key);
		return true;
	}

	// 按照二叉搜索树的性质查找key在树中的插入位置cur
	Node* cur = _root;
	// 记录cur的双亲,因为新元素最终插入在cur双亲左右孩子的位置
	Node* parent = nullptr;
	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);
	// 因为新元素最终插入在cur双亲左右孩子的位置,所以进行连接
	if (parent->_key < key)
		parent->_right = cur;
	else
		parent->_left = cur;

	return true;
}

递归实现:

        与常规实现,本质上没有区别,只不过是将循环寻找变为递归的形式。在右就递归右,在左就递归左。

// this指针没法实现递归
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->_left, key);
	else if (root->_key < key)
		_InsertR(root->_right, key);
	else // 该值已有
		return false;
}

删除函数(Erase)的实现

  二叉搜索树的删除
        首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面种四情况:
  1. 要删除的结点无孩子结点。
  2. 要删除的结点只有左孩子结点。
  3. 要删除的结点只有右孩子结点。
  4. 要删除的结点有左、右孩子结点。

常规实现: 

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

        1、删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除。

正常情况:

特例情况 :

        2、删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除。

正常情况:

 特例情况 :

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

bool Erase(const K& key)
{
	// 如果树为空,删除失败
	if (nullptr == _root)
		return false;

	// 按照二叉搜索树的性质查找key在树中的位置cur
	Node* cur = _root;
	// 记录cur的双亲,因为删除cur后cur双亲连接cur的左右孩子
	Node* parent = nullptr;
	while (cur)
	{
		// 查找key在树中的位置
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else // 找到了
		{
			if (cur->_right == nullptr)// 找到的节点,只有右子树时
			{
				// 找到的节点时头节点时,改变头节点
				if (cur == _root)
					_root = cur->_left;
				else
				{
					if (parent->_left == cur)
						parent->_left = cur->_left;
					else
						parent->_right = cur->_left;
				}
				delete cur;
				cur = nullptr;
			}
			else if (cur->_left == nullptr)// 找到的节点,只有左子树时
			{
				// 找到的节点时头节点时,改变头节点
				if (cur == _root)
					_root = cur->_right;
				else
				{
					if (parent->_left == cur)
						parent->_left = cur->_right;
					else
						parent->_right = cur->_right;
				}
			}
			else
			{
				//找右子树的最小值节点,进行替换
				Node* minParent = cur;
				Node* min = cur->_right;
				while (min->_left)
				{
					minParent = min;
					min = min->_left;
				}
				swap(min->_key, cur->_key);

				if (min == minParent->_left)
					minParent->_left = min->_right;
				else
					minParent->_right = min->_left;

				delete min;
			}

			return true;
		}
	}
	return false;
}

递归实现:

// this指针没法实现递归
bool EraseR(const K& key)
{
	// 实现递归
	return _EraseR(_root, key);
}

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

	// 查找key在树中的位置
	if (root->_key > key)
		return _EraseR(root->_left, key);
	else if (root->_key < key)
		return _EraseR(root->_right, 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); //此时删除key,是root->_left == nullptr的情况
		}
		// 删除
		delete del;
		del = nullptr;
		return true;
	}
}

        对于第1、2种情况:与常规实现的没区别,只是由于,常规实现使用的是局部变量cur,所以需要使用局部变量parent来确保链接情况以及是否为根节点,但是由于递归实现,直接就可以直接更改其节点的地址,所以底层差不多,而表面就一条语句。

         对于第3种情况:思维与常规是类似的,但是我们需要删除就需要利用前面的第1、2种情况操作。

  • 首先找到后,找其右子树的最左节点(大于其的最小值)

  • 掉换_key

  • 再次递归找到_key,此时删除_key,此时就是第1类情况。

查找函数(Find)的实现

二叉搜索树的查找

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

常规实现:

// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
bool Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		// 查找key在树中的位置
		if (cur->_key > key)
			cur = cur->_left;
		else if (cur->_key < key)
			cur = cur->_right;
		else // 找到了
			return true;
	}
	return false;
}

递归实现:

// this指针没法实现递归
bool FindR(const K& key)
{
	// 实现递归
	return _FindR(_root, key);
}

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

	// 查找key在树中的位置
	if (root->_key > key) // >即只有可能在左树
		return _FindR(root->_left, key);
	else if (root->_key < key) // <即只有可能在左树
		return _FindR(root->_right, key);
	else // 找到了
		return true;

	return false;
}

代码汇总

#include<iostream>
using namespace std;
namespace cr
{
	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>
	struct BSTree
	{
		typedef BSTreeNode<K> Node;

		// C++的用法:强制编译器生成默认的构造
		BSTree() = default;

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

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

		//const BSTree<K>& operator=(const BSTree<K>& t)
		//{
		//	if (this != &t) //防止自己给自己赋值
		//	{
		//		_root = _Copy(t._root);
		//	}
		//	return *this;
		//}

		// 利用t间接调用拷贝构造函数,后赋值给this->_root
		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}

		//释放树中结点
		void Destory(Node* root)
		{
			if (root == nullptr)
				return;

			// 释放左子树中的结点
			Destory(root->_left);
			// 释放右子树中的结点
			Destory(root->_right);
			delete root;
			root = nullptr;
		}

		~BSTree()
		{
			//递归实现删除
			Destory(_root);
			_root = nullptr;
		}
		
		// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
		bool Insert(const K& key)
		{
			// 如果树为空,直接插入
			if (_root == nullptr)
			{
				_root = new Node(key);
				return true;
			}

			// 按照二叉搜索树的性质查找key在树中的插入位置cur
			Node* cur = _root;
			// 记录cur的双亲,因为新元素最终插入在cur双亲左右孩子的位置
			Node* parent = nullptr;
			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);
			// 因为新元素最终插入在cur双亲左右孩子的位置,所以进行连接
			if (parent->_key < key)
				parent->_right = cur;
			else
				parent->_left = cur;

			return true;
		}

		// 根据二叉搜索树的性质查找:找到值为key的节点在二叉搜索树中的位置
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				// 查找key在树中的位置
				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)
		{
			// 如果树为空,删除失败
			if (nullptr == _root)
				return false;

			// 按照二叉搜索树的性质查找key在树中的位置cur
			Node* cur = _root;
			// 记录cur的双亲,因为删除cur后cur双亲连接cur的左右孩子
			Node* parent = nullptr;
			while (cur)
			{
				// 查找key在树中的位置
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else // 找到了
				{
					if (cur->_right == nullptr)// 找到的节点,只有右子树时
					{
						// 找到的节点时头节点时,改变头节点
						if (cur == _root)
							_root = cur->_left;
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_left;
							else
								parent->_right = cur->_left;
						}
						delete cur;
						cur = nullptr;
					}
					else if (cur->_left == nullptr)// 找到的节点,只有左子树时
					{
						// 找到的节点时头节点时,改变头节点
						if (cur == _root)
							_root = cur->_right;
						else
						{
							if (parent->_left == cur)
								parent->_left = cur->_right;
							else
								parent->_right = cur->_right;
						}
					}
					else
					{
						//找右子树的最小值节点,进行替换
						Node* minParent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minParent = min;
							min = min->_left;
						}
						swap(min->_key, cur->_key);

						if (min == minParent->_left)
							minParent->_left = min->_right;
						else
							minParent->_right = min->_left;

						delete min;
					}

					return true;
				}
			}
			return false;
		}



		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

		//递归版本

		// this指针没法实现递归
		bool FindR(const K& key)
		{
			// 实现递归
			return _FindR(_root, key);
		}

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

			// 查找key在树中的位置
			if (root->_key > key) // >即只有可能在左树
				return _FindR(root->_left, key);
			else if (root->_key < key) // <即只有可能在左树
				return _FindR(root->_right, key);
			else // 找到了
				return true;

			return false;
		}

		// this指针没法实现递归
		bool EraseR(const K& key)
		{
			// 实现递归
			return _EraseR(_root, key);
		}

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

			// 查找key在树中的位置
			if (root->_key > key)
				return _EraseR(root->_left, key);
			else if (root->_key < key)
				return _EraseR(root->_right, 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);
					// 此时删除key,是root->_left == nullptr的情况
					return _EraseR(root->_right, key); 
				}
				// 删除
				delete del;
				del = nullptr;
				return true;
			}
		}

		// this指针没法实现递归
		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->_left, key);
			else if(root->_key < key)
				_InsertR(root->_right, key);
			else // 该值已有
				return false;
		}

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

		Node* _root = nullptr;
	};
}

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

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

相关文章

解决Windows环境下的docker中修改了mysql的配置文件之后启动不了的问题

** 前情简介&#xff1a; ** 由于在docker中安装的mysql5.7中区分表的大小写&#xff0c;进入到mysql的命令行模式之后就修改配置文件&#xff0c;主要修改 lower_case_table_names1修改之后就启动再也启动不起来了&#xff0c;说我配置文件改错了 2022-11-25 14:47:5400:0…

ThingsBoard源码解析-消息队列

概述 消息队列是thingsboard支持集群的基础&#xff0c;比如使用Kafka可实现消息在整个服务集群中共同处理&#xff0c;提高性能。如果是内存队列&#xff0c;则无法再服务实例间共享消息。 定义 在module【common/cluster-api】的org.thingsboard.server.queue包定义了消息…

排名预测系统

排名预测系统 题目链接 题目背景&#xff1a; 本题大模拟来自真实的需求&#xff0c;即&#xff1a;综合三场网络赛的名次&#xff0c;来预计一个正式队伍在所有正式参赛队伍中的名次以此来估计自己能不能拿牌。本来只有一道题&#xff0c;即为你们看到的T5&#xff0c;经过…

【Linux kernel/cpufreq】framework ----big Little driver

Linux kernel支持ARM bigLttile框架的解决方案 一般ARM SOC包含能效和性能两个cluster&#xff0c;共8个 core&#xff0c;可以把这8个core统统开放给kernel&#xff0c;让kernel的调度器&#xff08;scheduler&#xff09;根据系统的实际情况&#xff0c;决定哪些任务应该在哪…

C++ 值传递、引用传递、指针传递

一、简介 参数传递的三种方式&#xff1a;值传递、引用传递、指针传递 二、举例如下 #if 1 值传递 引用传递 指针传递的区别void value_input(int a){cout << "值传递------函数" <<&a <<endl;a 100;}void Pointer_input(int * n){cou…

云上办公兴起,华为云桌面Workspace更靠谱

云上办公兴起&#xff0c;华为云桌面Workspace更靠谱 为了办公的便利性&#xff0c;也趁着华为云推行“实惠更实用&#xff0c;11都如愿”的主题活动&#xff0c;许多企业果断入手了华为云桌面Workspace服务&#xff0c;当亲自试用后&#xff0c;才逐渐感受使用华为云桌面Work…

FPGA+ARM异核架构,基于米尔MYC-JX8MMA7核心板的全自动血细胞分析仪

全自动血细胞分析仪是医院临床检验应用非常广泛的仪器之一&#xff0c;用来检测红细胞、血红蛋白、白细胞、血小板等项目。是基于电子技术和自动化技术的全自动智能设备&#xff0c;功能齐全&#xff0c;操作简单&#xff0c;依托相关计算机系统在数据处理和数据分析等方面具有…

脚气、灰指甲治疗实验方案

脚气 &#xff08;已临床实验&#xff09; 脚气&#xff0c;又叫足廯、香港脚。 糜烂性脚气 症状&#xff1a;80%都是这种类型。常见于多汗人群。角质层被汗水浸软&#xff0c;发白了以后&#xff0c;走动不断摩擦表皮脱落&#xff0c;露出鲜红色糜烂面&#xff0c;瘙痒剧烈&…

什么是分布式软件系统

:什么是分布式软件系统&#xff1f;分布式软件系统是什么意思&#xff1f; 分布式软件系统(Distributed Software Systems)是支持分布式处理的软件系统,是在由通信网络互联的多处理机体系结构上执行任务的系统。它包括分布式操作系统、分布式程序设计语言及其编译(解释)系统、分…

[附源码]java毕业设计疫情状况下生活物资集体团购系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[毕业设计]大数据电影数据分析可视化

目录 前言 课题背景和意义 实现技术思路 网页分析 索引页 详情页 反爬破解 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,…

Unity UI 框架相关的一些思考

开源地址&#xff1a; GitHub - NRatel/NRFramework.UI: 基于 Unity UGUI 的 UI 开发框架基于 Unity UGUI 的 UI 开发框架. Contribute to NRatel/NRFramework.UI development by creating an account on GitHub.https://github.com/NRatel/NRFramework.UI 简介&#xff1a;…

EMR-StarRocks 与 Flink 在汇量实时写入场景的最佳实践

作者&#xff1a; 刘腾飞 汇量后端开发工程师 阿里云开源OLAP研发团队 EMR-StarRocks介绍 阿里云EMR在年初推出了StarRocks服务&#xff0c;StarRocks是新一代极速全场景MPP&#xff08;Massively Parallel Processing&#xff09;数据仓库&#xff0c;致力于构建极速和统一分…

帝国cms后台登录系统限制次数,60分钟过后重新登录解决办法

帝国cms后台登录系统一不小心登录频繁就提示: 系统限制的登录次数不得超过5次,请等60分钟过后,方可重新登录 主要原因就是频繁的输错用户名或者密码导致登录受限 解帝国cms后台登录系统限制次数方法一:等待60分钟,然后再尝试登录 解帝国cms后台登录系统限制次数方法二:修改…

Hive之DQL操作

Hive系列第六章 &#xff08;实际是第七篇&#xff0c;就不改目录序号了&#xff0c;大家知道就行&#xff0c;后续的篇章类推即可&#xff09; 第六章 DQL查询数据 DDL&#xff1a; Data Definition Language 数据定义语言 DML&#xff1a; Data Manipulation Language …

【科学文献计量】GC.networkCoInvestigator()和GC.networkCoInvestigator()中的参数解释

@TOC 1 数据 使用官网提供的基金数据导入到python环境中 2 GC.networkCoInvestigator()中的参数解释 GC.networkCoInvestigator()中的参数解释: targetTagsL: [list]数据类型。默认为None,可以指定为Grant中研究者的标签构成的列表,很多基金中作者没有已知的标签,需要自…

最新版本EasyRecovery15个人免费版电脑数据恢复工具

最新版本EasyRecovery15是一款是款恢复率高、速度快的数据恢复软件&#xff0c;Ontrack EasyRecovery (易恢复) 跨平台支持 Windows 以及 Mac 系统&#xff0c;能能够顺利找回因各种原因丢失的文件&#xff0c;比如文件误删除、误格式化、分区丢失等&#xff0c;且EasyRecovery…

一种获得离散型周期数据的变化周期的算法

400个数据像这样&#xff1a; 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 109 152 155 155 237 24 27 27 …

【Android 开发】 面试官刨根问底?教你如何避免翻车沟通表达能力

很久以前&#xff0c;凭借四大组件、Java基础等知识&#xff0c;便可开开心心的开发&#xff0c;轻松的上岗&#xff1b; 而随着Android的不断发展完善&#xff0c;各种组件库越来越成熟&#xff0c;学习资料越来越多&#xff0c;我们却慢慢的看不到了方向&#xff1b;信息爆炸…

Servlet(Cookie和Session)

目录 &#x1f432; 1. Cookie 的工作流程 &#x1f432; 2. Servlet中操作 Cookie 和 Session 的api &#x1f432; 3. 案例1: 模拟登录 &#x1f432; 4. 上传文件 &#x1f432; 5. 案例2: 上传文件 &#x1f432; 1. Cookie 的工作流程 Cookie 是浏览器在本地持久化保…