二叉树进阶之二叉搜索树:一切的根源

news2024/9/21 14:42:58

前言:

在学完了简单的容器与C++面向对象的三大特性之后,我们首先接触的就是map与set两大容器,但是这两个容器底层实现的原理是什么呢?我们不而知,今天,主要来为学习map与set的底层原理而打好基础,而二叉搜索树,则是一切的开端......

一、二叉搜索树的定义与性质

1.1、什么是二叉搜索树:

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

1.2、二叉搜索树的性质:

  • 节点值关系: 对于每个节点 N,其左子树中所有节点的值都小于 N,右子树中所有节点的值都大于 N
  • 中序遍历的顺序: 对二叉搜索树进行中序遍历(左-根-右)时,节点的值按升序排列。
  • 平均时间复杂度: 在平均情况下,查找、插入和删除操作的时间复杂度都是 O(log n)。
  • 最坏时间复杂度: 在最坏情况下(树退化为链表),查找、插入和删除操作的时间复杂度是 O(n)。

二、二叉搜索树的模拟实现

初步了解性质之后,我们就根据这些特点与所学二叉树的知识模拟实现一个简单的二叉搜索树结构。首先我们创建一个头文件,取名为BSTree.h

1、节点结构

结点是组成一个二叉树的基础,我们要写一个二叉树,首先就是定义好一个的节点结构,由于是二叉树,所以一个结点会有两个子节点,我们分别以right与left表示左右子节点。随后就是值了,我们用val来表示(注意:一般来说会有key与key-val两种模型,分别象征之后的set与map两种容器,我们这里先以简单的key模型为例子)。

template<class T>
struct BSTNode
{
	BSTNode(const T&data=T())
		:_val(data)
		,left(nullptr)
		,right(nullptr)
	{}
	BSTNode<T>* right;
	BSTNode<T>* left;
	T _val;
};

再这样一串代码中,我们先定义一个结点结构,用之前学的模板知识将结点改造为一个模板。对与结点的默认构造,我们选择调用所存储数据类型的默认结构在初始化列表中给val值进行初始化,并且,给左右子节点的指针全部指向空。(data值直接调用的T这个数据类型自己的默认构造:包括内置类型与自定义类型)

2、二叉树的创建

随后,我们就在继续创建一个BSTree结构体模板,为了方便,先将结点进行重命名,随后将必要的构造函数,参数写上。一个BSTree参数只需要一个根节点就行。构造函数也只需要对根节点指针指向空就行。

template<class T>
class BSTree
{
	typedef BSTNode<T> Node;
public:
	BSTree()
		:_root(nullptr)
	{}
	
private:
	Node* _root;
};

3、二叉树的查找

在实现插入这个接口之前得先实现查找,因为要插入一个节点,我们就需要先找到相应的位置。

倘若找不到这个节点,就返回nullptr,找到了,就返回指向这个结点的指针。

Node* Find(const T& data)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_val == data)
		{
			return cur;
		}
		else if (cur->_val > data)
		{
			cur = cur->right;
		}
		else
		{
			cur = cur->left;
		}
	}
	return nullptr;
}

4、二叉树的插入

接下来就是实现插入这个重要接口,由于二叉搜索树的独特性质,不能插入相同的元素,为了知道是否插入成功,我们需要给这个接口的返回值返回一个bool类型

bool Insert(const T& data)
{
	if (_root == nullptr)//如果是一个空树,就创建一个结点随后返回真,并把_root指向新创建的结点
	{
		_root = new Node(data);
		return true;
	}
	Node* cur == _root;
	Node* parent = nullptr;//记录cur的父节点
	while (cur)
	{
		parent = cur;
		if (cur->_val > data)
		{
			cur = cur->left;
		}
		else if (cur->_val < data)
		{
			cur = cur->right;
		}
		else
		{
			return false;
		}
	}
	cur = new Node(data);
	if (cur->_val > parent->_val)
	{
		parent->right = cur;
	}
	else
	{
		parent->left = cur;
	}
	return true;
}

5、二叉树的删除

在二叉搜索树中,删除节点的操作是最复杂的。删除操作需要保持二叉搜索树的性质,即在删除一个节点后,仍然能够保持左子树的所有节点值小于当前节点值,右子树的所有节点值大于当前节点值。如果我们想要删除的节点为叶子,那还比较轻松,但如果我们想要删除的节点仍然拥有子节点,那就麻烦许多了。

日常我们有两种思路解决,二者大同小异,一个是寻找左子树的最最右节点,一个是找右子树的最左节点,这里我就以右子树的最左节点为例。

bool Erase(const T& data)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)//查找是否有这个节点
	{
		if (cur->_val > data)
		{
			parent = cur;
			cur = cur->left;
		}
		else if (cur->_val < data)
		{
			parent = cur;
			cur = cur->right;
		}
		else
		{
			break;
		}
	}

	if (cur == nullptr)//没有这个节点直接返回
	{
		return false;
	}

	if (cur->left == nullptr)//左为空或者右为空,或者都为空的情况下
	{
		if (cur == _root)
		{
			_root = _root->right;
		}
		if (cur == parent->left)
		{
			parent->left = cur->right;
		}
		else  
		{
			parent->right = cur->right;
		}
		delete cur;
		cur = nullptr;
		return true;
	}

	if (cur->right == nullptr)
	{
		if (cur == _root)
		{
			_root = _root->left;
		}
	
		if (cur == parent->left)
		{
			parent->left = cur->left;
		}
		else
		{
			parent->right = cur->left;
		}
		delete cur;
		cur = nullptr;
		return true;
	}

	Node* pparent = cur;//两个子节点都在的情况下,需要找到右子树的最小节点,将两个的值进行交换,再删除右子树的最小节点,此时又是一个0/1子树的选择情况
	Node* pcur = cur->right;
	while (pcur->left)
	{
		pparent = pcur;
		pcur = pcur->left;
	}
	cur->_val = pcur->_val;

	Node* p = pcur->right;
	if (pparent->left == pcur)
	{
		pparent->left = p;
	}
	else
	{
		pparent->right = p;
	}
	delete pcur;
	pcur = nullptr;
	return true;
	
}

大概的思路就是先找到要删除的节点,随后判断这个节点有几个非空子节点,0和1个空节点是一样的处理方法,二两个非空节点就需要用右子树的最小值或者左子树的最大值来替换,随后删除替换了值的那个节点。

6、二叉树的遍历

由于在外部,我们拿不到根节点,那么我们要怎么遍历一遍呢?这里就需要用到我们的套层艺术了!

首先我们先在private中写一个中序遍历的接口:

void order(Node*root)
{
	if (root == nullptr)
	{
		return;
	}
	order(root->left);
	cout << root->_val << " ";
	order(root->right);
}

随后,我们在publci中写一个无参的_oeder函数来调用order就行了。

void _order()
{
	order(_root);
	cout<< endl;
}

我们写一个简单的用例测试一下

#include<iostream>
using namespace std;
#include"BSTree.h"

int main()
{
	
	BSTree<int>Tree;
	Tree.Insert(6);
	Tree.Insert(3);
	Tree.Insert(2);
	Tree.Insert(1);
	Tree.Insert(4);
	Tree.Insert(7);
	Tree.Insert(9);
	Tree.Insert(8);
	Tree.Insert(5);
	Tree.Insert(10);
	Tree._order();



	for (int i = 1; i <= 10; ++i)
	{
		Tree.Erase(i);
		Tree._order();

	}
	Tree._order();

	return 0;
}

输出结果没有问题:

7、二叉树的析构销毁

在上面的代码中,存在一个内存泄漏的问题,就是我们所有的节点都是自己new出来的,所以也需要我们自己手动去销毁,自动构建出来的析构函数自然就不行,所以必须要自己写析构函数。所以我们可以实现一个destroy函数,后序递归销毁二叉树,随后让析构函数调用destroy函数就行。

void Destroy(Node* root)
{
    if (root)
    {
        Destroy(root->left);
        Destroy(root->right);
        delete root;
    }
}

	~BSTree()
	{
		destroy(_root);
	}

8、由key模型向key-val模型的转化

接下来我们去实现一下两种模型之间的转化:

其实,二者并无太大区别,在插入销毁查找中判断仍然使用key值判断只不过是把以往的单独的T改成了T,K,把之前的_val改成_key。

只不过我们需要实现一个额外的构造函数,这个构造函数需要进行深拷贝来实现,也就是说我们要进行递归对这个二叉树进行深拷贝。

	BSTree(const BSTree<T,K>&t)
	{
		_root=Copy(t._root);
	}
	Node* Copy(Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		Node* newRoot = new Node(root->_key, root->_val);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}

key模型整体的代码如下:

#pragma once

template<class T>
struct BSTNode
{
	BSTNode(const T&data=T())
		:_val(data)
		,left(nullptr)
		,right(nullptr)
	{}
	BSTNode<T>* right;
	BSTNode<T>* left;
	T _val;
};

template<class T>
class BSTree
{
	typedef BSTNode<T> Node;
public:
	BSTree()
		:_root(nullptr)
	{}

	~BSTree()
	{
		destroy(_root);
	}
	Node* Find(const T& data)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_val == data)
			{
				return cur;
			}
			else if (cur->_val > data)
			{
				cur = cur->left;
			}
			else
			{
				cur = cur->right;
			}
		}
		return nullptr;
	}

	bool Insert(const T& data)
	{
		if (_root == nullptr)//如果是一个空树,就创建一个结点随后返回真,并把_root指向新创建的结点
		{
			_root = new Node(data);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (cur->_val > data)
			{
				cur = cur->left;
			}
			else if (cur->_val < data)
			{
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(data);
		if (cur->_val > parent->_val)
		{
			parent->right = cur;
		}
		else
		{
			parent->left = cur;
		}
		return true;
	}

	bool Erase(const T& data)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)//查找是否有这个节点
		{
			if (cur->_val > data)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->_val < data)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				break;
			}
		}

		if (cur == nullptr)//没有这个节点直接返回
		{
			return false;
		}

		if (cur->left == nullptr)//左为空或者右为空,或者都为空的情况下
		{
			if (cur == _root)
			{
				_root = _root->right;
			}
			else if (cur == parent->left)
			{
				parent->left = cur->right;
			}
			else  
			{
				parent->right = cur->right;
			}
			delete cur;
			cur = nullptr;
			return true;
		}

		if (cur->right == nullptr)
		{
			if (cur == _root)
			{
				_root = _root->left;
			}
			else if (cur == parent->left)
			{
				parent->left = cur->left;
			}
			else
			{
				parent->right = cur->left;
			}
			delete cur;
			cur = nullptr;
			return true;
		}

		Node* pparent = cur;//两个子节点都在的情况下,需要找到右子树的最小节点,将两个的值进行交换,再删除右子树的最小节点,此时又是一个0/1子树的选择情况
		Node* pcur = cur->right;
		while (pcur->left)
		{
			pparent = pcur;
			pcur = pcur->left;
		}
		cur->_val = pcur->_val;

		Node* p = pcur->right;
		if (pparent->left == pcur)
		{
			pparent->left = p;
		}
		else
		{
			pparent->right = p;
		}
		delete pcur;
		pcur = nullptr;
		return true;
		
	}

	void _order()
	{
		order(_root);
		cout<< endl;
	}
private:
	void order(Node*root)
	{
		if (root == nullptr)
		{
			return;
		}
		order(root->left);
		cout << root->_val << " ";
		order(root->right);
	}

	void Destroy(Node* root)
	{
		if (root)
		{
			Destroy(root->left);
			Destroy(root->right);
			delete root;
		}
	}

	Node* _root;
};

key-val的模型代码如下:


template<class T, class K>
struct BSTNode
{
	BSTNode(const T& key = T(), const K& val=K())
		:_key(key)
		,_val(val)
		, left(nullptr)
		, right(nullptr)
	{}
	BSTNode<T,K>* right;
	BSTNode<T,K>* left;
	T _key;
	K _val;
};

template<class T,class K>
class BSTree
{
	typedef BSTNode<T,K> Node;
public:
	BSTree() = default;
	BSTree(const BSTree<T,K>&t)
	{
		_root=Copy(t._root);
	}
	Node* Copy(Node* root)
	{
		if (root == nullptr)
		{
			return nullptr;
		}

		Node* newRoot = new Node(root->_key, root->_val);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}

	~BSTree()
	{
		Destroy(_root);
	}
	Node* Find(const T& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key == key)
			{
				return cur;
			}
			else if (cur->_key > key)
			{
				cur = cur->left;
			}
			else
			{
				cur = cur->right;
			}
		}
		return nullptr;
	}

	bool Insert(const T& key,const K&val)
	{
		if (_root == nullptr)//如果是一个空树,就创建一个结点随后返回真,并把_root指向新创建的结点
		{
			_root = new Node(key, val);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;//记录cur的父节点
		while (cur)
		{
			parent = cur;
			if (cur->_key > key)
			{
				cur = cur->left;
			}
			else if (cur->_key < key)
			{
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(key,val);
		if (cur->_key > parent->_key)
		{
			parent->right = cur;
		}
		else
		{
			parent->left = cur;
		}
		return true;
	}

	bool Erase(const T& key)
	{
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)//查找是否有这个节点
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				break;
			}
		}

		if (cur == nullptr)//没有这个节点直接返回
		{
			return false;
		}

		if (cur->left == nullptr)//左为空或者右为空,或者都为空的情况下
		{
			if (cur == _root)
			{
				_root = _root->right;
			}
			else if (cur == parent->left)
			{
				parent->left = cur->right;
			}
			else
			{
				parent->right = cur->right;
			}
			delete cur;
			cur = nullptr;
			return true;
		}

		if (cur->right == nullptr)
		{
			if (cur == _root)
			{
				_root = _root->left;
			}
			else if (cur == parent->left)
			{
				parent->left = cur->left;
			}
			else
			{
				parent->right = cur->left;
			}
			delete cur;
			cur = nullptr;
			return true;
		}

		Node* pparent = cur;//两个子节点都在的情况下,需要找到右子树的最小节点,将两个的值进行交换,再删除右子树的最小节点,此时又是一个0/1子树的选择情况
		Node* pcur = cur->right;
		while (pcur->left)
		{
			pparent = pcur;
			pcur = pcur->left;
		}
		cur->_key = pcur->_key;

		Node* p = pcur->right;
		if (pparent->left == pcur)
		{
			pparent->left = p;
		}
		else
		{
			pparent->right = p;
		}
		delete pcur;
		pcur = nullptr;
		return true;

	}

	void _order()
	{
		order(_root);
		cout << endl;
	}
private:
	void order(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		order(root->left);
		cout << root->_key << " " << root->_val << " ";
		order(root->right);
	}

	void Destroy(Node* root)
	{
		if (root)
		{
			Destroy(root->left);
			Destroy(root->right);
			delete root;
		}
	}

	Node* _root;
};

c测试用例如下:

#include<iostream>
using namespace std;
#include"BSTree.h"


void TestBSTree()
{
	// 统计水果出现的次数
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
   "苹果", "香蕉", "苹果", "香蕉" };
	BSTree<string, int> countTree;
	for (const auto& str : arr)
	{
		// 先查找水果在不在搜索树中
		// 1、不在,说明水果第一次出现,则插入<水果, 1>
		// 2、在,则查找到的节点中水果对应的次数++
		//BSTreeNode<string, int>* ret = countTree.Find(str);
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_val++;
		}
	}
	countTree._order();
}

int main()
{
	
	BSTree<int,string>Tree;
	Tree.Insert(6, "six");
	Tree.Insert(3,"three");
	Tree.Insert(2,"two");
	Tree.Insert(1,"one");
	Tree.Insert(4,"four");
	Tree.Insert(7,"seven");
	Tree.Insert(9,"nine");
	Tree.Insert(8,"eight");
	Tree.Insert(5,"five");
	Tree.Insert(10,"ten");
	Tree._order();



	for (int i = 1; i <= 10; ++i)
	{
		Tree.Erase(i);
		Tree._order();

	}
	Tree._order();

	cout << endl;

	TestBSTree(); 

	return 0;
}

三.、小结

二叉搜索树是一种重要的数据结构,具有良好的查找、插入和删除性能。但它也存在潜在的不平衡问题,因此在实际应用中,常常需要通过自平衡的二叉搜索树(如AVL树、红黑树)来保证性能。

希望本文对基础的二叉搜索树的探索能对您产生帮助!

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

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

相关文章

【精选】学生考勤管理系统设计与实现(源码+辅导+设计)

目录&#xff1a; 系统介绍&#xff1a; 第2章 开发技术介绍 2.1 B/S结构 2.2 Java语言 2.3 springboot框架 2.4 MySQL数据库 系统设计 系统的总体功能设计 系统实现界面&#xff1a; 3.视频演示 系统测试 测试概述 测试结果 参考代码&#xff1a; 为什么选择我&am…

【数据结构】PTA 单链表逆转 C语言

本题要求实现一个函数&#xff0c;将给定的单链表逆转。 函数接口定义&#xff1a; List Reverse( List L ); 其中List结构定义如下&#xff1a; typedef struct Node *PtrToNode; struct Node {ElementType Data; /* 存储结点数据 */PtrToNode Next; /* 指向下一个结点的…

进程间通信学习记录(IPC 机制以及共享内存)

0.System V IPC机制&#xff1a; ①.IPC对象包含&#xff1a;共享内存、消息队列和信号灯集。 ②.每个IPC对象有唯一的ID。 ③.IPC对象创建后一直存在&#xff0c;直到被显示地删除。 ④.每一个IPC对象有一个关联的KEY。&#xff08;其他进程通过KEY访问对应的IPC对象&#xff…

XSS和DOM破坏案例

XSS案例 环境地址&#xff1a;XSS Game - Learning XSS Made Simple! | Created by PwnFunction 1.Ma Spaghet! 源码&#xff1a; <!-- Challenge --> <h2 id"spaghet"></h2> <script>spaghet.innerHTML (new URL(location).searchParam…

【LiteX】【开发板】【BoChenK7】使用Python开发FPGA【SDRAM/DDR/Bootloader】

目录 开发板介绍下载仓库工程设计代码实现 工程构建启动测试Memory测速Bootloader&#xff08;裸机BareMetal程序&#xff09;Bootloader&#xff08;Linux OS&#xff09; 开发板信息 开发板介绍 手头目前只有一个购买的BoChenK7开发板&#xff0c;后续会用它来进行LiteX FPG…

政务大数据解决方案(九)

政务大数据解决方案旨在通过构建高效的数据处理与分析系统&#xff0c;全面整合政府各部门数据资源&#xff0c;运用尖端技术进行深入挖掘与智能分析&#xff0c;为政府决策提供科学、精准的数据支撑&#xff0c;优化政策制定与执行流程&#xff0c;同时通过加强数据安全与隐私…

基于Kotlin Multiplatform实现静态文件服务器(一)

本文将介绍如何基于Kotlin Multiplatform、Jetpack Compose实现静态文件服务器&#xff0c;并运行在Android、Windows以及Linux&#xff08;暂不支持server和mac&#xff09;。 创建KMP&#xff08;Kotlin Multiplatform&#xff09;项目 环境设置 安装Android Studiio 2023.…

Java二十三种设计模式-迭代子模式(16/23)

迭代器模式&#xff1a;顺序访问集合的稳健方式 引言 迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许顺序访问一个集合对象中的各个元素&#xff0c;而不需要暴露集合的底层表示。 基础知识&#xff0c;java设计模式总体来说设计…

ESP32-C3在MQTT访问时出现“transport_base: Poll timeout or error”问题的分析(1)

笔者最近在测试时,突然遇到了以下错误: 网上看过一些帖子,要么就是只提出遇到了相同问题,但没有解答。如:transport_base: Poll timeout or error, errno=Connection already in progress - ESP32 Forum 要么就是和本问题类似,但不完全相同的问题。如: TRANSPORT_BASE:…

Tomcat学习进阶

目录 Apache Tomcat架构配置线程模型Tomcat 的类加载机制类加载器层次结构类加载流程 Tomcat 的优化策略Tomcat 的集群部署Tomcat故障排查 Apache Tomcat 架构配置 Apache Tomcat是一个开源的Java Servlet容器和Web服务器&#xff0c;它实现了Java EE规范中的Servlet和JSP API。…

mysql中的innodb存储结构

什么是InnoDB存储引擎 InnoDB是MySQL的默认存储引擎&#xff0c;它是一个事务安全的存储引擎&#xff0c;支持行级锁定和外键约束。InnoDB为MySQL提供了高性能、高可靠性和高可用性的解决方案。 InnoDB 的核心特点和功能 事务支持&#xff1a;InnoDB支持ACID事务特性&#x…

[算法题]【模板】前缀和/【模板】二维前缀和

题目链接: 【模板】前缀和 / 【模板】二维前缀和 两道前缀和模板题. 【模板】前缀和: 定义一维前缀和数组: presum[i]: 表示 1 - i 位置的和 状态转移方程: presum[i] presum[i - 1] arr[i - 1] 定义数组时多开一个位置, 下标位置从 1 开始计算, 不从 0 开始是为了避免越界…

我眼中的胶水工作:挑战与机遇并存

我眼中的胶水工作&#xff1a;挑战与机遇并存 胶水工作&#xff0c;看似不起眼&#xff0c;却如同团队运作的隐形脉络&#xff0c;起着至关重要的连接作用。它涵盖了各种各样的任务&#xff0c;从注意到团队成员的阻碍并伸出援手&#xff0c;到审查设计文档&#xff0c;再到新…

实现微信的订阅消息或模板消息API来实现实时通知(服务通知)

实现微信的订阅消息或模板消息API以实现实时通知&#xff0c;需要经过几个关键步骤&#xff0c;包括用户订阅、消息发送和接收消息事件。以下是具体实现流程&#xff1a; 1. 用户订阅消息 首先&#xff0c;需要让用户订阅消息。这通常通过前端代码触发一个授权请求来完成&am…

C:每日一题:字符串左旋

题目&#xff1a;实现一个函数&#xff0c;可以实现字符串的左旋 例如&#xff1a;ABCD左旋一个字符就是BCDA&#xff1b;ABCD左旋两个字符就是CDAB&#xff1b; 1、解题思路&#xff1a; 1.确定目标旋转k个字符&#xff0c;我们要获取字符串的长度 len&#xff0c;目的是根…

8.14 day bug

bug1 好家伙&#xff0c;折腾一个小时没通过&#xff0c;原来是代码写多了 // 定义初始状态 const defaultState {login: false };// 定义 reducer const reducer (state defaultState, action) > {if (action.typeLOGIN) {// 当接收到 LOGIN action 时&#xff0c;更新…

备战秋招60天算法挑战,Day20

题目链接&#xff1a; https://leetcode.cn/problems/reverse-bits/ 视频题解&#xff1a; https://www.bilibili.com/video/BV1W2421Z7bi/ LeetCode 190.颠倒二进制位 题目描述 颠倒给定的 32 位无符号整数的二进制位。 举个例子&#xff1a; 输入&#xff1a;n 00000010…

Nginx--简介、安装、常用命令和配置文件

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、Nginx简介 1、nginx介绍 Nginx (engine x) 是一个高性能的 HTTP 和 反向代理 服务&#xff0c;也是一个IMAP/POP3/SMTP服务。因它的稳定性、丰…

分布式锁实现方案--redis、zookeeper、mysql

分布式锁的几种实现方式 悲观锁和乐观锁分布式锁的实现要求 -- 互斥性、避免死锁、可重入性、高可用行、性能互斥-只能有一个客户端持有锁 -- redis setnx避免死锁引入过期时间 -- redis ttl锁过期问题 -- 自动续期 -- redis释放别人锁问题 -- 锁附加唯一性 -- 给每个客户端设置…

【嵌入式linux开发】智能家居入门6:最新ONENET,物联网开放平台(QT、微信小程序、MQTT协议、ONENET云平台、旭日x3派)

智能家居入门6&#xff1a;最新ONENET物联网开放平台 前言最终现象一、ONENET云平台创建产品与设备二、使用MQTT客户端软件测试2.1 测试前的准备2.2 测试 三、LInux QT代码四、onenet 云端API介绍及微信小程序代码4.1 onenet API介绍4.2 微信小程序代码 五、板端运行 前言 ONE…