【C++】map和set的模拟实现

news2024/9/28 21:21:10

​🌠 作者:@阿亮joy.
🎆专栏:《吃透西嘎嘎》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

目录

    • 👉红黑树的改造👈
      • 节点和模板参数的改造
      • 红黑树的迭代器
      • 红黑树的完整代码
      • map 和 set 的完整代码
    • 👉总结👈

👉红黑树的改造👈

节点和模板参数的改造

我们知道,map 和 set 的底层数据结构都是红黑树,那库里是写了两份红黑树的代码来分别实例化出 map 和 set 吗?其实不是,而给一颗泛型结构的红黑树传不同的实例化参数,从而实现 map 和 set。

在这里插入图片描述
在这里插入图片描述
通过上图可以看到,map 传给红黑树的key_typeKeyvalue_typepair<Key, T>;而 set 传给红黑树的key_typevalue_type都是Key。红黑树的节点中存储数据就是value_type类型的数据,并不是像我们写的那样:直接存储键值对pair<K, V>。如果传给value_type的是pair<Key, T>,那么就会实例化出 map;而传给value_type的是Key,就会实例化出 set。那么现在我们也将自己实现的红黑树改造一下。

#pragma once

// 用枚举常量来代表颜色
enum Colour
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;

	T _data;
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
	{}
};

template<class K, class T>
struct RBTree
{
	typedef RBTreeNode<K, T> Node;
private:
	Node* _root = nullptr;
}

在这里插入图片描述

改造红黑树的模板参数和节点后,就会带了一个问题:我们该如何比较_data的大小?因为 map 的数据类型是pair<K, T>,而 set 的数据类型是Key。那怎么才能用一份泛型代码同时实现键值对的比较和Key的比较呢?第一种方式就是重载键值对的比较。库里也实现了键值对的比较,但是并不是我们想要的。

在这里插入图片描述
库里实现的键值对的大小比较是如果first没有比较出结果,还会比较second。这不是我们想要的,因为 map 键值对的比较只会比较first,并不会比较second。如果我们想要这样比较的话,那么就只能自己再实现一个pair了。这样会过于麻烦,那么我们可以采取第二种方式:通过一个仿函数来拿到节点数据的类型。

在这里插入图片描述
那么现在就能比较大小了,我们将 map 和 set 简单地封装一下,调试看看写得对不对。

调试样例

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过上面的调试可以看到,确实可以拿到Key去比较。那么,这样我们就通过红黑树实例化出 map 和 set。

红黑树的迭代器

STL 明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历可以得到一个有序的序列。因此,begin() 可以放在红黑树中最小节点(即最左侧节点)的位置,end() 放在最大节点(最右侧节点)的下一个位置。注:最大节点的下一个位置可以认为是nullptr 而库里的红黑多加了一个哨兵位的头节点headerheader的左指针指向最小节点,右指针指向最大节点,父指针指向根节点,根节点的父指针指向header。如下图所示:

在这里插入图片描述

在这里插入图片描述

template <class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	__RBTreeIterator(Node* node)
		: _node(node)
	{}

	Ref operator*() 
	{
		return _node->_data;
	}

	Ptr operator->() 
	{
		return &(_node->_data);
	}

	bool operator!=(const Self& it) const
	{
		return _node != it._node;
	}

	bool operator==(const Self& it) const
	{
		return _node == it._node;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个就是右子树的最左节点
			Node* left = _node->_right;
			while (left->_left)
			{
				left = left->_left;
			}

			_node = left;
		}
		else
		{
			// 下一个是孩子不是在父亲的右边的第一个祖先
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			// 下一个就是左子树的最右节点
			Node* right = _node->_left;
			while (right->_right)
			{
				right = right->_right;
			}

			_node = right;
		}
		else
		{
			// 下一个是孩子不是父亲的左边的第一个祖先
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		++(*this);
		return tmp;
	}

	Self operator--(int)
	{
		Self tmp(*this);
		--(*this);
		return tmp;
	}
};

template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, T&, T*> iterator;
public:
	// begin()为树的最左节点
	iterator begin()
	{
		// 注:需要避免_root为nullptr
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}

		return iterator(left);
	}
	// end()可以设置为nullptr
	iterator end()
	{
		return iterator(nullptr);
	}
	
private:
	Node* _root = nullptr;
}

在这里插入图片描述
在这里插入图片描述
注:map 和 set 的迭代器也是封装了红黑树的迭代器的。红黑树的反向迭代器可以自己尝试实现,reverse_begin()是最右节点,reverse_end()是 nullptr。因为我们没有添加哨兵位的头节点了,所以就可能比较难用封装正向迭代器的方式来实现方向迭代器。

测试迭代器

void MapTest1()
{
	map<int, int> m;
	m.insert(make_pair(1, 1));
	m.insert(make_pair(3, 3));
	m.insert(make_pair(5, 5));
	m.insert(make_pair(2, 2));

	map<int, int>::iterator it = m.begin();
	while (it != m.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
}

void SetTest1()
{
	set<int> s;
	s.insert(1);
	s.insert(3);
	s.insert(5);
	s.insert(2);

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

在这里插入图片描述

红黑树的完整代码

现在红黑树的迭代器也实现完了,那么我们再来改造一下红黑树的 insert 函数,让它返回pair<iterator, bool>。insert 函数改造完成后,那么红黑树的代码就实现完了。(注:除了红黑树的删除节点没有实现,还有树的销毁和拷贝构造等内容可以参考二叉树搜索树的实现)

在这里插入图片描述

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS 1

#pragma once

// 用枚举常量来代表颜色
enum Colour
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _parent;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;

	T _data;
	Colour _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
	{}
};

template <class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	__RBTreeIterator(Node* node)
		: _node(node)
	{}

	Ref operator*() 
	{
		return _node->_data;
	}

	Ptr operator->() 
	{
		return &(_node->_data);
	}

	bool operator!=(const Self& it) const
	{
		return _node != it._node;
	}

	bool operator==(const Self& it) const
	{
		return _node == it._node;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 下一个就是右子树的最左节点
			Node* left = _node->_right;
			while (left->_left)
			{
				left = left->_left;
			}

			_node = left;
		}
		else
		{
			// 下一个是孩子不是在父亲的右边的第一个祖先
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			// 下一个就是左子树的最右节点
			Node* right = _node->_left;
			while (right->_right)
			{
				right = right->_right;
			}

			_node = right;
		}
		else
		{
			// 下一个是孩子不是父亲的左边的第一个祖先
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		++(*this);
		return tmp;
	}

	Self operator--(int)
	{
		Self tmp(*this);
		--(*this);
		return tmp;
	}
};

template<class K, class T, class KeyOfT>
struct RBTree
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, T&, T*> iterator;

public:
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}

		return iterator(left);
	}

	iterator end()
	{
		return iterator(nullptr);
	}


	pair<iterator, bool> Insert(const T& data)
	{
		KeyOfT kot; // 通过T来获取Key的一个仿函数类
		// 根节点的颜色为黑色
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			// 以搜索二叉树的方式插入
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}

		// 插入节点
		cur = new Node(data);
		// 因为变色和旋转要修改cur,所以将新增节点的指针保存
		// 在newnode中方便后续构造迭代器
		Node* newnode = cur;	
		cur->_col = RED;	// 新插入节点默认为红色

		// 判断在哪一边插入
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		cur->_parent = parent;
		// 当parent存在且为红色,则需要继续往上处理
		// 当parent为空或为黑色时,则不需要往上处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			assert(grandfather);
			assert(grandfather->_col == BLACK);
			// 关键看叔叔
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// 情况一:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else    //情况二和三:uncle不存在或uncle存在且为黑
				{
					// 情况二:右单旋+变色
					//     g              p
					//   p   u   ==>    c   g
					// c                      u
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						// 情况三:parent先做左单旋,grandfather再做右单旋,最后变色
						//     g          g            c
						//   p   u ==>  c   u  ==>   p   g
						//     c      p                    u
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				// 情况一:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else    //情况二和三:uncle不存在或uncle存在且为黑
				{
					// 情况二:左单旋+变色
					//     g                  p
					//   u   p       ==>    g   c
					//         c          u 
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						// 情况三:parent先做右单旋,grandfather再做左单旋,最后变色
						//     g          g            c
						//   u   p ==>  u   c  ==>   g   p
						//     c             p     u
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		// 根节点永远是黑色
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}

	void InOrder()
	{
		_InOrder(_root);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col != BLACK)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}

		// 黑色节点数量的基准值
		int mark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++mark;

			cur = cur->_left;
		}

		return PrevCheck(_root, 0, mark);
	}

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

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	bool PrevCheck(Node* root, int blackNum, int mark)
	{
		if (root == nullptr)
		{
			if (blackNum != mark)
			{
				cout << "某条路径上的黑色节点的数量不相等" << endl;
				return false;
			}
			else
				return true;
		}

		if (root->_col == BLACK)
			++blackNum;

		// 检查父亲
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
		}

		return PrevCheck(root->_left, blackNum, mark)
			&& PrevCheck(root->_right, blackNum, mark);
	}

	// 左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}

			subR->_parent = ppNode;
		}

	}
	// 右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}

	}

private:
	Node* _root = nullptr;
};

map 和 set 的完整代码

红黑树的代码已经实现完了,那么现在就用红黑树实现出 map 和 set。

map 的完整代码

#pragma once 

#include "RBTree.h"

namespace Joy
{
	template <class K, class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>&kv)
			{
				return kv.first;
			}
		};

	public:
		// typename的作用是告诉编译器这是类型的重命名,并不是静态变量
		typedef typename RBTree<K, pair<K, V>, MapKeyOfT>::iterator iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		V& operator[](const K& key)
		{
			// 有就插入失败,没有就插入成功,但是都能够得到该节点的迭代器
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		RBTree<K, pair<K, V>, MapKeyOfT> _t;
	};
}

统计出现次数

	void MapTest2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

		map<string, int> countMap;
		for (auto& str : arr)
		{
			// 1、str不在countMap中,插入pair(str, int()),然后在对返回次数++
			// 2、str在countMap中,返回value(次数)的引用,次数++;
			countMap[str]++;
		}

		map<string, int>::iterator it = countMap.begin();
		while (it != countMap.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}

		cout << "-------" << endl;

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}

在这里插入图片描述

set 的完整代码

#pragma once 

#include "RBTree.h"

namespace Joy
{
	template <class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K & key)
			{
				return key;
			}
		};
	public:
		// typename的作用是告诉编译器这是类型的重命名,并不是静态变量
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}

	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
}

👉总结👈

本篇博客主要改造了之前模拟实现的红黑树以及将改造后的红黑树模拟实现出 map 和 set等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️

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

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

相关文章

一个线程如何处理多个连接?(非阻塞IO)

从BIO到NIO的转变 五种IO模型BIO的缺陷非阻塞非阻塞IO&#xff08;NIO&#xff09;非阻塞读非阻塞写非阻塞IO模型php NIO 实现适用场景什么是C10k问题&#xff1f;C10K问题的由来五种IO模型 在《UNIX 网络编程》一书中介绍了五种IO模型&#xff1a; 分别是 BIO&#xff0c;NIO…

无线电基础电路 > RLC阻尼系数计算仿真

随机搭建电路如下图所示&#xff1a; 阻尼系数的希腊字母符号“ ζ ”读作泽塔。 阻尼系数ζ (R/2) * √C/L 1000/2 * √0.00001 1.58 包括三种情况&#xff1a; ζ>1&#xff1a;过阻尼&#xff0c;频响不利落&#xff0c;需要较长时间才能消失。 ζ<1&#xff…

MinIO基本使用(实现上传、下载功能)

MinIO基本使用&#xff08;实现上传、下载功能&#xff09;1.简介2.下载和安装3.启动服务端4.创建User和Bucket4.1 创建User4.1.1 生成accessKey和secretKey4.2 创建Bucket5.在SpringBoot中使用MinIO5.1 引入依赖5.2 配置文件定义5.3 定义实体类5.4 定义业务类5.5 定义测试类5.…

vivado中block design遇到的error总结

Error1.[BD 41-1356] Address block </processing_system7_0/S_AXI_HP0/HP0_DDR_LOWOCM> is not mapped into </axi_vdma_0/Data_MM2S>. Please use Address Editor to either map or exclude it. 修改方法. a、点击Address Editor. b、在Address Editor页面右击失…

【Ajax】了解Ajax与jQuery中的Ajax

一、了解Ajax什么是AjaxAjax 的全称是 Asynchronous Javascript And XML&#xff08;异步 JavaScript 和 XML&#xff09;。通俗的理解&#xff1a;在网页中利用 XMLHttpRequest 对象和服务器进行数据交互的方式&#xff0c;就是Ajax。2. 为什么要学Ajax之前所学的技术&#xf…

使用MQTT fx测试云服务器的 mosquitto 通讯

文章目录一.MQTT.fx介绍二.MQTT.fx安装教程三.使用MQTT.fx测试云服务器的 mosquitto 通讯一.MQTT.fx介绍 MQTT.fx是一款基于Eclipse Paho&#xff0c;使用Java语言编写的MQTT客户端工具。支持通过Topic订阅和发布消息&#xff0c;用来前期和物理云平台调试非常方便。 二.MQTT…

【数据结构——顺序表的实现】

前言&#xff1a; 在之前我们已经对复杂度进行的相关了解&#xff0c;因此现在我们将直接进入数据结构的顺序表的相关知识的学习。 目录1.线性表2.顺序表2.1概念及结构2.2 接口实现2.2.1.打印顺序表2.2.2初始化顺序表2.2.3.容量的检查2.2.4.销毁顺序表2.2.5.尾插操作2.2.6.尾删…

Ubuntu下的LGT8F328P MiniEVB Arduino开发和烧录环境

基于 LGT8F328P LQFP32 的 Arduino MiniEVB, 这个板型资料较少, 记录一下开发环境和烧录过程以及当中遇到的问题. 关于 LGT8F328P 芯片参数 8位RISC内核32K字节 Flash, 2K字节 SRAM最大支持32MHz工作频率 集成32MHz RC振荡器集成32KHz RC振荡器 SWD片上调试器工作电压: 1.8V…

C语言文件操作(3)

TIPS 1. 文件是不是二进制文件&#xff0c;不是后缀说了算&#xff0c;而是内容说了算 2. 文件的随机读写 文件的随机读写也就是说我指哪打哪 fseek() 人为调整指针指向的位置 1. 根据文件指针FILE*的当前位置和你给出的偏移量来让它这个文件指针呢定位到你想要的位置上…

Flutter 这一年:2022 亮点时刻

回看 2022&#xff0c;展望 Flutter Forward 2022 年&#xff0c;我们非常兴奋的看到 Flutter 社区持续发展壮大&#xff0c;也因此让更多人体验到了令人难以置信的体验。每天有超过 1000 款使用 Flutter 的新移动应用发布到 App Store 和 Google Play&#xff0c;Web 平台和桌…

实战打靶集锦-002-SolidState

**写在前面&#xff1a;**谨以此文纪念不完美的一次打靶经历。 目录1. 锁定主机与端口2. 服务枚举3. 服务探查3.1 Apache探查3.1.1 浏览器手工探查3.1.2 目录枚举3.2 JAMES探查3.2.1 搜索公共EXP3.2.2 EXP利用3.2.2.1 构建payload3.2.2.2 netcat构建反弹shell3.2.3 探查JAMES控…

三十一、Kubernetes中Service详解、实例第一篇

1、概述 在kubernetes中&#xff0c;pod是应用程序的载体&#xff0c;我们可以通过pod的ip来访问应用程序&#xff0c;但是pod的ip地址不是固定的&#xff0c;这也就意味着不方便直接采用pod的ip对服务进行访问。 为了解决这个问题&#xff0c;kubernetes提供了Service资源&…

NX二开ufun函数UF_MODL_ask_curve_points(获取曲线信息)

根据曲线tag&#xff0c;返回曲线相关信息&#xff1a;弦宽容、弧度、最大步长、点数组的点。 实例返回结果截图如下&#xff1a; 实例创建曲线截图如下&#xff1a; 1、函数结构 int UF_MODL_ask_curve_points &#xff08;tag_t curve_id&#xff0c; double ctol&#xf…

【SpringCloud19】SpringCloud Alibaba Sentinel实现熔断与限流

1.概述 官网 中文文档 1.1 是什么 一句话解释&#xff0c;之前我们讲解过的Hystrix 1.2 怎么下 下载网址 1.3 作用 1.4 如何使用 官网学习 服务使用中的各种问题&#xff1a; 服务雪崩服务降级服务熔断服务限流 2.安装Sentinel控制台 2.1 组成部分 核心库&#x…

Golang之实战篇(1)

"千篇一律&#xff0c;高手寂寞。几十不惑&#xff0c;全都白扯"上篇介绍了golang这门新的语言的一些语法。那么我们能用golang简单地写些什么代码出来呢&#xff1f;一、猜数字这个游戏的逻辑很简单。系统随机给你生成一个数&#xff0c;然后读取你猜的数字&#xf…

老杨说运维 | AIOps如何助力实现全面可观测性(上)

前言&#xff1a; 嗨&#xff0c;今天是大年三十&#xff0c;大家是不是已经在家坐享团圆之乐了&#xff1f;还是说在奔向团圆的路上呢&#xff1f;不论如何&#xff0c;小编先祝大家新年如意安康&#xff0c;平安顺遂~ 熟悉我们的朋友肯定都知道&#xff0c;关于《老杨说运维…

30.字符串处理函数

文章目录1.测字符串长度函数2.字符串拷贝函数1.strcpy函数2.strncpy函数3.字符串追加函数1.strcat函数2.strncat函数4.字符串比较函数1.strcmp函数2.strncmp函数5.字符查找函数1.strchr函数2.strrchr函数6.字符串匹配函数7.空间设定函数8.字符串转换数值9.字符串切割函数strtok…

【Java开发】Spring Cloud 04 :服务治理Nacos

本章节正式进入 Spring Cloud 环节了&#xff0c;首先介绍微服务架构中一个最重要的原理概念&#xff1a;服务治理&#xff0c;在概念讲解之后&#xff0c;讲解介绍 Nacos 服务注册中心的体系结构。1 服务治理1.1 服务治理介绍首先通过一个例子告诉你服务治理解决了什么问题。比…

GD32F4——外部中断

一、NVIC中断系统 Cortex-M4集成了嵌套式矢量型中断控制器&#xff08;Nested Vectored Interrupt Controller&#xff0c;NVIC&#xff09;来实现高效的异常和中断处理。 中断系统包含外部中断、定时器中断、DMA中断和串口中断等。 二、EXTI外部中断 EXTI&#xff08;中断…

go的基本语法介绍之变量的声明与初始化

1.常见基本数据类型 uint8&#xff1a;无符号8位整形&#xff0c;取值范围&#xff1a;0-255 uint16&#xff1a;无符号16位整形&#xff0c;取值范围&#xff1a;0-65535 uint32&#xff1a;无符号32位整形&#xff0c;取值范围&#xff1a;0-4294967295 uint64&#xff1…