[C++随想录] 二叉搜索树

news2025/1/4 15:03:18

搜素二叉树

  • 二叉搜索树的使用
  • 二叉搜索树的模拟实现(K)
    • 整体结构
    • 循环版本
    • 递归版本
  • 二叉搜索树的应用
  • 源码(kv)

二叉搜索树的使用

二叉搜索树 相较于 普通的二叉树来说:

  1. 根节点的左子树的所有键值都 小于 根节点, 根节点的右子树的所有键值 大于 根节点
  2. 根节点的 左右子树 都是 二叉搜索树
  3. 中序遍历升序的 ⇒ 二叉搜素树 又叫作 二叉排序树
  • 子树 && 节点
  1. 查找
    假如查找 key, 有如下 四种情况:
    1. 如果 key > 根节点的值, 那么就去根节点的右子树去查找
    2. 如果 key <根节点的值, 那么就去根节点的左子树去查找
    3. 如果 key = 根节点的值, 那么就找到了
    4. 如果找到 , 那就不存在
  • 查找的时间复杂度是 O(高度次), 而不是 O(logN)
    如果是 完全二叉树, 那么就是 O(logN); 如果 退化到极限情况, 类似于链表, 那么就是 O(N)

    所以, 总结下来, 时间复杂度就是 O(高度次)
    那么如何解决这种 退化问题呢? ⇒ AVL树 和 红黑树 就是针对这种情况做了特殊处理 --> 旋转
  1. 插入
    总体思想: 找到非空节点去插入

  2. 删除key

    1. 先找到key的位置, 有两种情况:
      1. 没找到, 那就直接返回
      2. 找到了key的位置, 记作cur. 找到了也有三种情况:
        1. cur的左子树为空
        2. cur的右子树为空
        3. cur的左右子树都不为空

由于 cur要进行删除, 要把cur后面的内容链接到parent的后面. && cur也有两种可能 parent的左子树 or 右子树我们要cur后面的内容链接到 cur处于parent的位置
删除具体如下👇👇👇

  1. cur的右子树为空
    (1) cur是parent的左子树

    (2) cur是parent的右子树
  2. cur的左子树为空
    (1) cur是parent的左子树

    (2) cur是parent的右子树
  3. cur的左右子树都不为空
    🗨️删除掉cur, 那么我们如何链接cur的左右子树呢?
    • 可以找一个节点来替换掉cur, 然后我们来处理这个节点的链接关系就好了
      🗨️替换过去, 也不改变二叉搜索树的结构, 那么节点是什么好呢? 后面集中处理这个节点, 那么这个节点应该容易处理才对, 那么这个节点是叶子节点吗?
    • 替换过去, 不改变二叉树的结构 — — 替换节点应该为 cur的左子树的最大节点 或者 cur的右子树的最小节点中序遍历, cur旁边的两个数; 中序是 左跟右, ⇒ 那么就应该是左子树的最大节点, 或者右子树的最小节点
      左子树的最大节点, 或者右子树的最小节点; 正好是叶子节点 ⇒ 那么我们处理这个替换节点也比较 容易 ⇒ 思想同上 替换节点的左子树为空, 或 替换节点的右子树为空

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

整体结构


Node类

	template<class T>
	struct BSTreeNode
	{
	public:
		BSTreeNode(const T& key)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
		{}

	public:
		BSTreeNode<T>* _left;
		BSTreeNode<T>* _right;
		T _key;
	};

BSTree类

template<class T>
class BSTree
{
	typedef BSTreeNode<T> Node;
public:
	BSTree()
		:_root(nullptr)
	{}
	
	// 析构函数
	~BSTree()
	{
		_BSTree(_root);
	}
	
private:
		// 析构函数
		void _BSTree(Node* root)
		{
			if (root == nullptr)
				return;

			// 后序遍历进行删除
			_BSTree(root->_left);
			_BSTree(root->_right);
			delete root;
		}
		
	// 成员函数
	Node* _root;
}

循环版本

  1. find
Node* find(const K& key)
{
	return _find(_root, key);
}

private:
Node* _find(Node* root, const T& key)
{
	Node* cur = root;

	while (cur)
	{
		if (key > cur->_key)
		{
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}

	return nullptr;
}
  1. insert
bool insert(const T& key)
{
	Node* newnode = new Node(key);

	if (_root == nullptr)
	{
		_root = newnode;
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	// 寻找插入的位置
	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			break;
		}
	}

	// 链接
	if (key > parent->_key)
	{
		parent->_right = newnode;
	}
	else if (key < parent->_key)
	{
		parent->_left = newnode;
	}
	else
	{
		return false;
	}

	return true;
}
  1. inorder
void Inorder()
{
	_Inorder(_root);
}

private:
void _Inorder(Node* root)
{
	if (root == nullptr)
		return;

	_Inorder(root->_left);
	std::cout << root->_key << " ";
	_Inorder(root->_right);

}
  1. erase

bool erase(const T& key)
{
	return _erase(_root, key);
}

private:
bool _erase(Node* root, const T& key)
{
	// 先找到位置
	Node* parent = root;
	Node* cur = root;

	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;

		}	
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		// 找到了
		else
		{
			// 左为空
			if (cur->_left == nullptr)
			{
				
				if (cur == _root)
				{
					_root = cur->_right;
				}
				else
				{
					// 判读cur是parent的位置
					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else if (cur == parent->_right)
					{
						parent->_right = cur->_right;
					}
				}
			}
			// 右为空
			else if (cur->_right == nullptr)
			{
				if (cur == _root)
				{
					_root = cur->_left;
				}
				else
				{
					// 判读cur是parent的位置
					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else if (cur == parent->_right)
					{
						parent->_right = cur->_left;
					}
				}
			}
			// 左右都不为空
			else
			{
				// 先找到cur的左子树的最大值 或 右子树的最小值
				// parent必须初始化为cur -- 以防删除的就是头节点
				Node* parent = cur;
				Node* LeftMax = cur->_left;
				while (LeftMax->_right)
				{
					parent = LeftMax;
					LeftMax = LeftMax->_right;
				}

				// 交换cur 和 LeftMax的值
				std::swap(cur->_key, LeftMax->_key);

				// 改变链接关系
				if (parent->_left == LeftMax)
				{
					parent->_left = LeftMax->_left;
				}
				else if (parent->_right == LeftMax)
				{
					parent->_right = LeftMax->_left;
				}

				cur = LeftMax;
			}
			
			// 集中释放 cur
			delete cur;
			return true;
		}
	}

	return false;
}

递归版本

  1. findr
    无需链接关系 — — 不用引用即可
    1. 递归退出条件 root == nullptr, 那就返回nullptr
    2. 根据二叉搜索数的特性: 大了往右边走, 小了往左边走, 相等就返回当前节点的指针;
Node* findr(const T& key)
{
	return _findr(_root, key);
}

private:
Node*_findr(Node* root, const T& key)
{
	if (root == nullptr)
		return nullptr;

	if (key < root->_key)
	{
		_findr(root->_left, key);
	}
	else if (key > root->_key)
	{
		_findr(root->_right, key);
	}
	else
	{
		return root;
	}
}
  1. insertr
    需要重新链接 -- -- 引用的妙用
    总体思想 : 遇到空就插入
    1. 递归返回条件 : 遇到空, 插入后, 返回true
    2. 二叉树的特性: 大了往右边走, 小了往左边走, 相等返回false

bool insertr(const T& key)
{
	return _insertr(_root, key);
}

private:
bool _insertr(Node*& root, const T& key)
{
	if (root == nullptr)
	{
		root = new Node(key);
		return true;
	}

	if (key > root->_key)
	{
		return _insertr(root->_right, key);
	}
	else if (key < root->_key)
	{
		return _insertr(root->_left, key);
	}
	else
	{
		return false;
	}
}
  1. eraser
    需要重新链接 -- -- 引用的妙用
    1. 递归结束条件: 遇到空就返回 false
    2. 先找到位置, 记作 cur
    3. cur有三种情况 :cur的左子树为空, cur的右子树为空, cur的左右子树都不为空; 三种情况分类讨论

这个和上面的 引用的妙用是一样的道理, 那么我就不在这里画 递归展开图

bool eraser(const T& key)
{
	return _eraser(_root, key);
}

private:
bool _eraser(Node*& root, const T& key)
{
	if (root == nullptr)
	{
		return false;
	}

	if (key > root->_key)
	{
		_eraser(root->_right, key);
	}
	else if (key < root->_key)
	{
		_eraser(root->_left, key);
	}
	else
	{
		// 由于是上面节点的引用 && 要删掉root节点
		// ⇒ 找一个背锅侠来代替root节点去删除
		Node* tem = root;
		
		// 左子树为空
		if (root->_left == nullptr)
		{
			root = root->_right;
		}
		//右子树为空
		else if (root->_right == nullptr)
		{
			root = root->_left;
		}
		// 左右子树都不为空
		else
		{
			// 找到左树的最大节点
			Node* maxleft = root->_left;
			while (maxleft->_right)
			{
				maxleft = maxleft->_right;
			}
			
			// 交换root 和 maxleft的值
			std::swap(maxleft->_key, root->_key);
			
			// 重新链接
			root = maxleft->_left;
			
			// 背锅侠就位
			tem = maxleft;
		}
		
		// 统一删除
		delete tem;
		return true;
	}

	return false;
}

二叉搜索树的应用

二叉搜索树主要有两个版本 K版本 和 KV版本
KV版本 相较于 K版本 就多了个 value

template<class K, class V>
	struct BSTreeNode
	{
	public:
		BSTreeNode(const K& key, const V& value)
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
			,_value(value)
		{}

	public:
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _value;
	};
template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	BSTree()
		:_root(nullptr)
	{}
private:
	Node* _root;
}

由于 还是对 K 进行操作 ⇒ 我们这里就不写 KV的代码了. 后面源码会附上 KV的完整代码


二叉搜索树主要应用于两种模型: K模型 和 KV模型

  1. K模型 — — 根据关键码Key去解决 在不在的问题
    比如 : 判断单词是否拼写错误 (将词库导入二叉搜索树, 然后判断在不在)
void test1()
{
	// 模拟导入词库
	muyu::BSTree<string, string> World;
	World.insert("insert", "插入");
	World.insert("input", "输入");
	World.insert("output", "输出");
	World.insert("love", "爱情");

	string str;
	while (cin >> str)
	{
		// 查找是否在词库中出现
		auto ret = World.find(str);
		if (ret)
		{
			cout << "输入正确" << endl;
		}
		else
		{
			cout << "查无单词, 请重新输入" << endl;
		}
	}
}

int main()
{
	test1();

	return 0;
}

运行结果:

  1. KV模型 — — 每一个关键码Key, 都有一个与之对应的 Value, 存在 <Key, Value>键值对
    比如: 统计水果出现的次数
void test2()
{
	muyu::BSTree<string, int> cnt;
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };

	for (const auto& e : arr)
	{
		auto res = cnt.find(e);
		// 第一次插入, 次数就给个1
		if (!res)
		{
			cnt.insert(e, 1);
		}
		// 不是第一次插入, 就在key对应的value进行++
		else
		{
			res->_value++;
		}

	}

	cnt.Inorder();
}

int main()
{
	test2();

	return 0;
}

运行结果:

苹果 6
西瓜 3
香蕉 2

源码(kv)

#pragma once

namespace muyu
{
	template<class K, class V>
	struct BSTreeNode
	{
	public:
		BSTreeNode(const K& key = K(), const V& value = V())
			:_left(nullptr)
			,_right(nullptr)
			,_key(key)
			,_value(value)
		{}

	public:
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _value;
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
		BSTree()
			:_root(nullptr)
		{}

		~BSTree()
		{
			_BSTree(_root);
		}

		bool insert(const K& key, const V& value)
		{
			Node* newnode = new Node(key, value);

			if (_root == nullptr)
			{
				_root = newnode;
				return true;
			}

			Node* parent = nullptr;
			Node* cur = _root;
			// 寻找插入的位置
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					break;
				}
			}

			// 链接
			if (key > parent->_key)
			{
				parent->_right = newnode;
			}
			else if (key < parent->_key)
			{
				parent->_left = newnode;
			}
			else
			{
				return false;
			}

			return true;
		}

		bool insertr(const K& key)
		{
			return _insertr(_root, key);
		}

		void Inorder()
		{
			_Inorder(_root);
		}

		Node* find(const K& key)
		{
			return _find(_root, key);
		}

		Node* findr(const K& key)
		{
			return _findr(_root, key);
		}

		bool erase(const K& key)
		{
			return _erase(_root, key);
		}

		bool eraser(const K& key)
		{
			return _eraser(_root, key);
		}

	private:

		void _BSTree(Node* root)
		{
			if (root == nullptr)
				return;

			// 后序遍历进行删除
			_BSTree(root->_left);
			_BSTree(root->_right);
			delete root;
		}

		void _Inorder(Node* root)
		{
			if (root == nullptr)
				return;

			_Inorder(root->_left);
			std::cout << root->_key << " " << root->_value << std::endl;
			_Inorder(root->_right);

		}

		Node* _insertr(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				root = new Node(key, value);
				return root;
			}

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

		Node* _find(Node* root, const K& key)
		{
			Node* cur = root;

			while (cur)
			{
				if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}

			return nullptr;
		}

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

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

		bool _erase(Node* root, const K& key)
		{
			// 先找到位置
			Node* parent = root;
			Node* cur = root;

			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;

				}	
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				// 找到了
				else
				{
					// 左为空
					if (cur->_left == nullptr)
					{
						
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							// 判读cur是parent的位置
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else if (cur == parent->_right)
							{
								parent->_right = cur->_right;
							}
						}
					}
					// 右为空
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							// 判读cur是parent的位置
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else if (cur == parent->_right)
							{
								parent->_right = cur->_left;
							}
						}
					}
					// 左右都不为空
					else
					{
						// 先找到cur的左子树的最大值 或 右子树的最小值
						Node* parent = cur;
						Node* LeftMax = cur->_left;
						while (LeftMax->_right)
						{
							parent = LeftMax;
							LeftMax = LeftMax->_right;
						}

						// 交换cur 和 LeftMax的值
						std::swap(cur->_key, LeftMax->_key);

						// 改变链接关系
						if (parent->_left == LeftMax)
						{
							parent->_left = LeftMax->_left;
						}
						else if (parent->_right == LeftMax)
						{
							parent->_right = LeftMax->_left;
						}

						cur = LeftMax;
					}

					delete cur;
					return true;
				}
			}

			return false;
		}

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

			if (key > root->_key)
			{
				_eraser(root->_right, key);
			}
			else if (key < root->_key)
			{
				_eraser(root->_left, key);
			}
			else
			{
				Node* tem = root;

				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				else
				{
					Node* maxleft = root->_left;
					while (maxleft->_right)
					{
						maxleft = maxleft->_right;
					}

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

					root = maxleft->_left;

					tem = maxleft;
				}

				delete tem;
				return true;
			}

			return false;
		}

		Node* _root;
	};
}

晚日寒鸦一片愁。柳塘新绿却温柔。若教眼底无离恨,不信人间有白头。
肠已断,泪难收。相思重上小红楼。情知已被山遮断,频倚阑干不自由。
— — 辛弃疾· 《鹧鸪天》

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

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

相关文章

mac上mongodb 以及可视化工具 下载以及安装

简介 1. 下载 官网上的下载地址藏得非常深&#xff0c;不花老半天 根本找不到 下载地址 https://www.mongodb.com/try/download/community 目前最新社区版本7.0.2 下载链接 mac intel芯片 &#xff1a; https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-7.0.2.tgz ma…

地面领域的“大疆”?通用足式机器人公司逐际动力获近2亿融资!

原创 | 文 BFT机器人 近日&#xff0c;通用足式机器人公司逐际动力完成天使轮和Pre-A轮融资&#xff0c;总金额近2亿元。逐际动力是一家通用足式机器人公司&#xff0c;成立于2022年&#xff0c;全球总部位于中国深圳&#xff0c;专注于运动智能&#xff08;Motion Intelligen…

html内连框架

src:引用页面地址 name&#xff1a;框架标识名称 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <!--iframe src&#xff1a;地址 w-h&#xff…

鸿鹄工程项目管理系统 Spring Cloud+Spring Boot+Mybatis+Vue+ElementUI+前后端分离构建工程项目管理系统项目背景

鸿鹄工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 1. 项目背景 一、随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管…

双十一必买好物,这四款好物你值得拥有

随着科技的不断发展&#xff0c;智能家电已经成为我们生活中不可或缺的一部分。在双十一期间&#xff0c;各大品牌都会推出各种优惠活动&#xff0c;以更优惠的价格购买到心仪的智能家电。比如智能超声波清洗机&#xff0c;智能门锁&#xff0c;它们不仅提高了我们的生活质量&a…

Vue2基础知识(五)插槽

&#x1f48c; 所属专栏&#xff1a;【Vue2】&#x1f600; 作 者&#xff1a;长安不及十里&#x1f4bb;工作&#xff1a;目前从事电力行业开发&#x1f308;目标&#xff1a;全栈开发&#x1f680; 个人简介&#xff1a;一个正在努力学技术的Java工程师&#xff0c;专注基础和…

【C++项目】高并发内存池第二讲中心缓存CentralCache框架+核心实现

CentralCache 1.框架介绍2.核心功能3.核心函数实现介绍3.1SpanSpanList介绍3.2CentralCache.h3.3CentralCache.cpp3.4TreadCache申请内存函数介绍3.5慢反馈算法 1.框架介绍 回顾一下ThreadCache的设计&#xff1a; 如图所示&#xff0c;ThreadCache设计是一个哈希桶结构&…

万界星空科技/免费MES系统/开源MES/免费追溯管理

开源系统概述&#xff1a; 万界星空科技免费MES、开源MES、商业开源MES、市面上最好的开源MES、MES源代码、免费MES、免费智能制造系统、免费排产系统、免费排班系统、免费质检系统、免费生产计划系统、免费仓库管理系统、免费出入库管理系统、免费可视化数字大屏。 万界星空…

CMake学习(一):使用CMake构建一个简单的C++项目

文章目录 一. 构建一个简单的项目二. 构建过程1. 创建程序源文件2. 编写CMakeList.txt文件3. 构建项目并编译源代码 附件 一. 构建一个简单的项目 最基本的CMake项目是从单个源代码文件构建的可执行文件。对于像这样的简单项目&#xff0c;只需要一个包含三个命令的CMakeLists…

QT的Qporcess功能的使用

具体实现代码如下&#xff1a; #include <QProgressBar>//必须要包含的头文件 #include <QProcess>// 创建一个QProgressBar对象QProgressBar *progressBar new QProgressBar(this);QProcess *proces;process_shownew process;// 设置进度条的最小值和最大值prog…

YOLOv5论文作图教程(1)— 软件介绍及下载安装(包括软件包+下载安装详细步骤)

前言:Hello大家好,我是小哥谈。在学习YOLOv5算法的过程中,很多同学都有发表论文的需求。作为文章内容的支撑,图表是最直接的整合数据的工具,能够更清晰地反映出研究对象的结果、流程或趋势。在发表论文的时候,审稿人除了关注论文的内容和排版外,也会审核图表是否清晰美观…

按摩 推拿上门服务小程序源码 家政上门服务系统源码

按摩 推拿上门服务小程序源码 家政上门服务系统源码 上门服务系统是一款基于互联网和移动应用的高端家政服务预订平台&#xff0c;它集成了用户、服务员、客户三方的需求于一体&#xff0c;为广大市民提供方便、高效、安全、舒适的家居服务体验&#xff0c;让你在家当皇帝&…

征战EDU证书站

1.前言&#xff1a; 挖了一段时间EDU老破小的站&#xff0c;也该拿证书站下手了。下手的第一个目标&#xff0c;那必然是漏洞排行榜第一的某交大&#xff01;&#xff01;&#xff01; 2.信息搜集 想快速挖到漏洞&#xff0c;必须信息搜集全面。如果信息搜集不到位不全面&…

YOLO V8语义分割模型部署

目录 1 配置pytorch环境 2 配置yolo环境 3 测试yoloV8的语义分割模型 1 配置pytorch环境 我的电脑为Y9000P 4090&#xff0c;英伟达显卡驱动版本为525.105.17&#xff0c;驱动显示最高的cuda版本号为12.0&#xff0c;cuda版本为11.6&#xff0c;cudnn版本号为8.5.0。Anaconda…

如何将音频与视频分离

您一定经历过这样的情况&#xff1a;当你非常喜欢视频中的背景音乐时&#xff0c;希望将音频从视频中分离出来&#xff0c;以便你可以在音乐播放器中收听音乐。有没有一种有效的方法可以帮助您快速从视频中提取音频呢&#xff1f;当然是有的啦&#xff0c;在下面的文章中&#…

Windows命令行窗口修改字体方法

问题&#xff1a;嫌弃原来的字体太丑&#xff0c;不好看&#xff0c;影响心情 长得丑还天天在我眼前晃悠&#xff1a; 改一改字体 输入命令&#xff1a; chcp 437 把字体换成consola 学习的心情都变好了

美国亚马逊UL60335认证怎么办理,费用是多少

UL60335认证是由美国安全实验室&#xff08;UnderwritersLaboratories&#xff09;颁发的&#xff0c;它对各类家用电器进行严格的测试和认证&#xff0c;确保其在正常使用情况下不会给消费者带来任何伤害。 本文将从不同的角度来叙述亚马逊UL60335认证的重要性和成败因素。 1.…

Kafka快速入门(最新版3.6.0)

文章目录 一、初识MQ1.1 什么是MQ1.2 同步和异步通讯1.1.1 同步通讯1.1.2 异步通讯 1.3 技术对比1.4 MQ的两种模式 二、初识Kafka2.1 Kafka的使用场景2.2 Kafka基本概念2.3 Topic与Partition 三、Kafka基本使用3.1 部署前的准备3.2 启动kafka服务器3.3 Kafka核心概念之Topic3.4…

laravel队列

laravel redis队列 1、创建job队列任务 php artisan make:job StoreUser执行上述命令后&#xff0c;会生成app/Jobs/StoreUser.php文件&#xff0c;编辑文件内容如下&#xff1a; <?phpnamespace App\Jobs;use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queu…

JavaScript基础知识(二)

JavaScript基础知识&#xff08;二&#xff09; 一、ES2015 基础语法1.变量2.常量3.模板字符串4.结构赋值 二、函数进阶1. 设置默认参数值2. 立即执行函数3. 闭包4. 箭头函数 三、面向对象1.面向对象概述2.基本概念3.新语法与旧语法4.ES5 面向对象的知识4.1 ES5构造函数4.2 原型…