• 手撕红黑树的插入(C++)


    红黑树概念

    总特征:任意节点到叶子节点的最长路径不超过最短路径的两倍

    1.每个节点非红即黑
    2.根节点为黑色
    3.如果一个节点为红色,那么它的子节点必须是黑色(不能有连续的红节点)
    4.对于某个节点来说,该节点到所有叶子节点的路径上的黑色节点数量相同
    5.每个叶节点是黑色(这里的黑色包括NULL和树的尾端

    在这里插入图片描述
    图片来自百度百科

    话不多说,直接用C++实现红黑树的结构

    整体结构

    enum Colour
    {
    	RED,
    	BLACK
    };
    
    
    template <class K, class V>
    struct RBTreeNode
    {
    	pair<K, V> _kv;
    	RBTreeNode* _left;
    	RBTreeNode* _right;
    	RBTreeNode* _parent;
    	Colour _col;
    
    	RBTreeNode(pair<K, V> kv)
    		:_kv(kv)
    		, _left(nullptr)
    		, _right(nullptr)
    		, _parent(nullptr)
    		, _col(RED)
    	{}
    };
    
    template <class K, class V>
    class RBTree
    {
    	typedef RBTreeNode<K, V> Node;
    public:
    	bool Insert(const pair<K, V>& kv);
    	bool IsRBTree();
    	void Inorder() { _Inorder(_root); }
    	void Leorder() { _Leorder(_root); }
    private:
    	void RotateL(Node* root);
    	void RotateR(Node* root);
    	int _HeightGreater(Node* root);
    	int _HeightLess(Node* root);
    	bool _IsRBTree(Node* root, int k, int blackCount);
    	void _Inorder(Node* root);
    	void _Leorder(Node* root);
    private:
    	Node* _root = nullptr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    插入的三种情况

    插入节点颜色都是红色,因为如果插入节点为黑色,一定不符合任意节点到其所有叶子节点的黑色节点数量相同的规则,但插入节点为红色,可以根据红黑树的规则进行一定的调整,使其重新满足红黑树的性质

    g是祖父节点,p是父节点,u是叔叔节点,cur可能是当前插入节点,也可能是a或b子树调整后的祖父节点

    关于其中涉及到的旋转,在AVL的插入讲解这篇博客中有详细介绍

    情况1

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

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

    第一种情况:

    • 如果p与u都是红色,将p与u改为黑色,g改为红色
    • 如果g的父节点为黑(g可能是整棵树中的子树),调整结束
    • 如果g的父节点为红,需要以g为cur向上继续调整
    • 同理,如果p和u的位置调换,但u为红色,也是这样调整(就是上面的四种情况

    最后:

    • 如果子树的根节点g为树的根节点,将其改为黑,退出更新
    • 如果不是,则判断g的父节点是否为红
      • 如果为红,将g作为新的cur,并且根据cur的位置更新出g,u,p的位置,根据情况继续进行更新
      • 如果为黑,则退出更新

    情况2

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

    第二种情况:

    • 如果u不存在,cur就是新插入的节点,以g为根节点进行右旋转(p为g的右子节点则进行左旋转),最后将p和g的颜色反转
    • 如果u存在且为黑,那么cur不是新插入的节点,cur之前为黑(因为p为红,cur不可能为红),因为cur的子树调整导致了cur变红。这种情况下,调整方法与第一种是一样的。
      为什么第一种情况可以直接将p和u变为黑
    • 因为这样做不违反每条路径上的黑节点数量相同的规则,u和p都为红的情况下,将一条路径上的两节点颜色反转,所有路径的黑色节点的数量没有发生变化
    • 但在u为黑的情况下,将p和u变黑,因为u本来就是黑的,所以只有某些路径的黑色节点数量增加,违反了第四条这个规则

    总结

    • 只有在出现连续两个节点为红色时,才会进行调整。所以cur和parent一定为红
    • 三种情况的判断关键在于:uncle节点的颜色
    • 若u为红,则反转u,p和g的颜色,对应第一种情况
    • 若u为黑,需要进行旋转以维持第四条规则,对应第二和三种情况

    情况3

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

    第三种情况:

    • u为黑或不存在,p为g的左子节点,cur为p的右子节点
    • 思路是往情况2靠,以cur为根节点进行左单旋,成为情况2,以情况2的方式旋转,变色
    • 同理,p为g的右子节点,cur为p的左子节点,也是一样的

    Insert代码

    template <class K, class V>
    bool RBTree<K, V>::Insert(const pair<K, V>& kv)
    {
    	// 以搜索二叉树的规则插入
    	if (_root == nullptr)
    	{
    		_root = new Node(kv);
    		// 根节点为黑色
    		_root->_col = BLACK;
    		return true;
    	}
    
    	Node* cur = _root;
    	Node* parent = nullptr;
    	// 找合适的插入位置
    	while (cur)
    	{
    		if (kv.first < cur->_kv.first)
    		{
    			parent = cur;
    			cur = cur->_left;
    		}
    		else if (kv.first > cur->_kv.first)
    		{
    			parent = cur;
    			cur = cur->_right;
    		}
    		else
    		{
    			return false;
    		}
    	}
    	cur = new Node(kv);
    	// 判断该插入parent的哪边
    	if (kv.first < parent->_kv.first)
    	{
    		parent->_left = cur;
    	}
    	else
    	{
    		parent->_right = cur;
    	}
    	cur->_parent = parent;
    	// 插入结束
    
    	// 保持平衡
    	while (parent && parent->_col == RED) // 出现连续的红节点
    	{
    		Node* grandParent = parent->_parent;
    		assert(grandParent);
    
    		if (grandParent->_left == parent) // parent在祖父节点左边
    		{
    			Node* uncle = grandParent->_right;
    			// u为红色,一定要提前判断u是否存在
    			if (uncle && uncle->_col == RED)
    			{
    				uncle->_col = BLACK;
    				parent->_col = BLACK;
    				grandParent->_col = RED;
    				if (grandParent == _root)
    				{
    					grandParent->_col = BLACK;
    					return true;
    				}
    				else // grandParent不是根节点,但其父节点为黑色,也插入完成
    				{
    					if (grandParent->_parent && grandParent->_parent->_col == BLACK)
    					{
    						return true;
    					}
    					else // grandParent的父节点为红,出现连续红节点,更新节点,继续调整
    					{
    						cur = grandParent;
    						parent = cur->_parent;
    					}
    				}
    			} // end of if (uncle && uncle->_col == RED)
    
    			else // u不存在或者为黑
    			{
    				// cur在p的左边
    				if (cur == parent->_left)
    				{
    					RotateR(grandParent);
    					grandParent->_col = RED;
    					parent->_col = BLACK;
    				}
    				else // cur在p的右边,需要旋转成左边的情况
    				{
    					RotateL(parent);
    					RotateR(grandParent);
    					cur->_col = BLACK;
    					grandParent->_col = RED;
    				}
    				return true;
    			}
    		} // end of - if (grandParent->_left == parent)
    
    		else // p在grandParent的右边
    		{
    			Node* uncle = grandParent->_left;
    			assert(grandParent);
    			// u为红色
    			if (uncle && uncle->_col == RED)
    			{
    				uncle->_col = BLACK;
    				parent->_col = BLACK;
    				grandParent->_col = RED;
    				if (grandParent == _root)
    				{
    					_root->_col = BLACK;
    					return true;
    				}
    				else
    				{
    					if (grandParent->_parent && grandParent->_parent->_col == BLACK)
    					{
    						return true;
    					}
    					// 更新继续调整
    					else
    					{
    						cur = grandParent;
    						parent = cur->_parent;
    					}
    				}
    			} // end of if (uncle && uncle->_col == RED)
    
    			else // u不存在或者为黑
    			{
    				// cur在p的右边
    				if (cur == parent->_right)
    				{
    					RotateL(grandParent);
    					grandParent->_col = RED;
    					parent->_col = BLACK;
    				}
    				else // cur在p的右边,需要旋转成左边的情况
    				{
    					RotateR(parent);
    					RotateL(grandParent);
    					cur->_col = BLACK;
    					grandParent->_col = RED;
    				}
    				return true;
    			}
    		} // end of - else
    	}// end if - while
    	return true;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152

    验证红黑树

    先求出一条路径上的黑色节点数量blackCount,然后调用子函数,子函数递归整棵树,用k记录当前路径的黑色节点数量,遇到黑色节点k++,当遇到节点为空的情况,比较k与blackCount是否相等。

    另外每次递归要判断

    1. 根节点是否是黑色
    2. 如果该节点为红色,且父节点存在,如果父节点也为红,不满足红黑树性质
    3. 求出节点到叶子节点的最短路径与最长路径,判断最短路径的两倍不能小于最长路径
    template <class K, class V>
    bool RBTree<K, V>::_IsRBTree(Node* cur, int k, int blackCount)
    {
    	if (cur == nullptr)
    	{
    		if (blackCount != k)
    		{
    			cout << "路径上的黑色节点数量不相等" << endl;
    			return false;
    		}
    		return true;
    	}
    	if (_root->_col != BLACK)
    	{
    		cout << "根节点为红色" << endl;
    		return false;
    	}
    
    	// 求以cur为根节点的最小高度与最大高度
    	int less = _HeightLess(cur);
    	int greater = _HeightGreater(cur);
    
    	if (less * 2 < greater)
    	{
    		cout << "最长路径超过最短路径两倍" << endl;
    		return false;
    	}
    
    	if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
    	{
    		cout << "出现连续红色节点" << endl;
    		return false; // 检查连续的红色节点
    	}
    
    	if (cur->_col == BLACK)
    	{
    		k++;
    	}
    
    	// 递归验证左右子树
    	return _IsRBTree(cur->_left, k, blackCount) && _IsRBTree(cur->_right, k, blackCount);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    在这里插入图片描述

    hpp所有代码

    #define _CRT_SECURE_NO_WARNINGS 1
    
    #include 
    using namespace std;
    #include 
    #include 
    #include 
    enum Colour
    {
    	RED,
    	BLACK
    };
    
    
    template <class K, class V>
    struct RBTreeNode
    {
    	pair<K, V> _kv;
    	RBTreeNode* _left;
    	RBTreeNode* _right;
    	RBTreeNode* _parent;
    	Colour _col;
    
    	RBTreeNode(pair<K, V> kv)
    		:_kv(kv)
    		, _left(nullptr)
    		, _right(nullptr)
    		, _parent(nullptr)
    		, _col(RED)
    	{}
    };
    
    template <class K, class V>
    class RBTree
    {
    	typedef RBTreeNode<K, V> Node;
    public:
    	bool Insert(const pair<K, V>& kv);
    	bool IsRBTree();
    	void Inorder() { _Inorder(_root); }
    	void Leorder() { _Leorder(_root); }
    private:
    	void RotateL(Node* root);
    	void RotateR(Node* root);
    	int _HeightGreater(Node* root);
    	int _HeightLess(Node* root);
    	bool _IsRBTree(Node* root, int k, int blackCount);
    	void _Inorder(Node* root);
    	void _Leorder(Node* root);
    private:
    	Node* _root = nullptr;
    };
    
    template <class K, class V>
    bool RBTree<K, V>::Insert(const pair<K, V>& kv)
    {
    	// 以搜索二叉树的规则插入
    	if (_root == nullptr)
    	{
    		_root = new Node(kv);
    		// 根节点为黑色
    		_root->_col = BLACK;
    		return true;
    	}
    
    	Node* cur = _root;
    	Node* parent = nullptr;
    	// 找合适的插入位置
    	while (cur)
    	{
    		if (kv.first < cur->_kv.first)
    		{
    			parent = cur;
    			cur = cur->_left;
    		}
    		else if (kv.first > cur->_kv.first)
    		{
    			parent = cur;
    			cur = cur->_right;
    		}
    		else
    		{
    			return false;
    		}
    	}
    	cur = new Node(kv);
    	// 判断该插入parent的哪边
    	if (kv.first < parent->_kv.first)
    	{
    		parent->_left = cur;
    	}
    	else
    	{
    		parent->_right = cur;
    	}
    	cur->_parent = parent;
    	// 插入结束
    
    	// 保持平衡
    	while (parent && parent->_col == RED) // 出现连续的红节点
    	{
    		Node* grandParent = parent->_parent;
    		assert(grandParent);
    
    		if (grandParent->_left == parent) // parent在祖父节点左边
    		{
    			Node* uncle = grandParent->_right;
    			// u为红色,一定要提前判断u是否存在
    			if (uncle && uncle->_col == RED)
    			{
    				uncle->_col = BLACK;
    				parent->_col = BLACK;
    				grandParent->_col = RED;
    				if (grandParent == _root)
    				{
    					grandParent->_col = BLACK;
    					return true;
    				}
    				else // grandParent不是根节点,但其父节点为黑色,也插入完成
    				{
    					if (grandParent->_parent && grandParent->_parent->_col == BLACK)
    					{
    						return true;
    					}
    					else // grandParent的父节点为红,出现连续红节点,更新节点,继续调整
    					{
    						cur = grandParent;
    						parent = cur->_parent;
    					}
    				}
    			} // end of if (uncle && uncle->_col == RED)
    
    			else // u不存在或者为黑
    			{
    				// cur在p的左边
    				if (cur == parent->_left)
    				{
    					RotateR(grandParent);
    					grandParent->_col = RED;
    					parent->_col = BLACK;
    				}
    				else // cur在p的右边,需要旋转成左边的情况
    				{
    					RotateL(parent);
    					RotateR(grandParent);
    					cur->_col = BLACK;
    					grandParent->_col = RED;
    				}
    				return true;
    			}
    		} // end of - if (grandParent->_left == parent)
    
    		else // p在grandParent的右边
    		{
    			Node* uncle = grandParent->_left;
    			assert(grandParent);
    			// u为红色
    			if (uncle && uncle->_col == RED)
    			{
    				uncle->_col = BLACK;
    				parent->_col = BLACK;
    				grandParent->_col = RED;
    				if (grandParent == _root)
    				{
    					_root->_col = BLACK;
    					return true;
    				}
    				else
    				{
    					if (grandParent->_parent->_col == BLACK)
    					{
    						return true;
    					}
    					// 更新继续调整
    					else
    					{
    						cur = grandParent;
    						parent = cur->_parent;
    					}
    				}
    			} // end of if (uncle && uncle->_col == RED)
    
    			else // u不存在或者为黑
    			{
    				// cur在p的右边
    				if (cur == parent->_right)
    				{
    					RotateL(grandParent);
    					grandParent->_col = RED;
    					parent->_col = BLACK;
    				}
    				else // cur在p的右边,需要旋转成左边的情况
    				{
    					RotateR(parent);
    					RotateL(grandParent);
    					cur->_col = BLACK;
    					grandParent->_col = RED;
    				}
    				return true;
    			}
    		} // end of - else
    	}// end if - while
    	return true;
    }
    
    
    
    template <class K, class V>
    void RBTree<K, V>::RotateL(Node* parent)
    {
    	Node* subR = parent->_right;
    	Node* subRL = subR->_left;
    
    	subR->_left = parent;
    	parent->_right = subRL;
    
    	Node* pparent = parent->_parent;
    	parent->_parent = subR;
    
    	if (subRL)
    		subRL->_parent = parent;
    	if (_root == parent)
    	{
    		_root = subR;
    		subR->_parent = nullptr;
    	}
    	else // 该子树是树的一部分
    	{
    		subR->_parent = pparent;
    		if (subR->_kv.first < pparent->_kv.first)
    		{
    			pparent->_left = subR;
    		}
    		else
    		{
    			pparent->_right = subR;
    		}
    	}
    }
    
    template <class K, class V>
    void RBTree<K, V>::RotateR(Node* parent)
    {
    	Node* subL = parent->_left;
    	Node* subLR = subL->_right;
    
    	subL->_right = parent;
    	parent->_left = subLR;
    
    	Node* pparent = parent->_parent;
    	parent->_parent = subL;
    
    	if (subLR)
    		subLR->_parent = parent;
    	if (_root == parent)
    	{
    		_root = subL;
    		subL->_parent = nullptr;
    	}
    	else
    	{
    		subL->_parent = pparent;
    		if (subL->_kv.first < pparent->_kv.first)
    		{
    			pparent->_left = subL;
    		}
    		else
    		{
    			pparent->_right = subL;
    		}
    	}
    }
    
    
    template <class K, class V>
    int RBTree<K, V>::_HeightGreater(Node* root)
    {
    	if (root == nullptr)
    	{
    		return 0;
    	}
    
    	int leftH = _HeightGreater(root->_left);
    	int rightH = _HeightGreater(root->_right);
    
    	return leftH > rightH ? 1 + leftH : 1 + rightH;
    }
    
    template <class K, class V>
    int RBTree<K, V>::_HeightLess(Node* root)
    {
    	if (root == nullptr)
    	{
    		return 0;
    	}
    
    	int leftH = _HeightLess(root->_left);
    	int rightH = _HeightLess(root->_right);
    
    	return leftH < rightH ? 1 + leftH : 1 + rightH;
    }
    template <class K, class V>
    bool RBTree<K, V>::IsRBTree()
    {
    	int blackCount = 0;
    	Node* cur = _root;
    	while (cur)
    	{
    		if (cur->_col == BLACK)
    		{
    			blackCount++;
    		}
    		cur = cur->_left;
    	}
    	int k = 0;
    
    	return _IsRBTree(_root, k, blackCount);
    }
    
    template <class K, class V>
    bool RBTree<K, V>::_IsRBTree(Node* cur, int k, int blackCount)
    {
    	if (cur == nullptr)
    	{
    		if (blackCount != k)
    		{
    			cout << "路径上的黑色节点数量不相等" << endl;
    			return false;
    		}
    		return true;
    	}
    	if (_root->_col != BLACK)
    	{
    		cout << "根节点为红色" << endl;
    		return false;
    	}
    
    	// 求以cur为根节点的最小高度与最大高度
    	int less = _HeightLess(cur);
    	int greater = _HeightGreater(cur);
    
    	if (less * 2 < greater)
    	{
    		cout << "最长路径超过最短路径两倍" << endl;
    		return false;
    	}
    
    	if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
    	{
    		cout << "出现连续红色节点" << endl;
    		return false; // 检查连续的红色节点
    	}
    
    	if (cur->_col == BLACK)
    	{
    		k++;
    	}
    
    	// 递归验证左右子树
    	return _IsRBTree(cur->_left, k, blackCount) && _IsRBTree(cur->_right, k, blackCount);
    }
    
    template <class K, class V>
    void RBTree<K, V>::_Inorder(Node* root)
    {
    	if (root == nullptr)
    		return;
    
    	_Inorder(root->_left);
    	cout << root->_kv.first << ' ';
    	_Inorder(root->_right);
    }
    
    template <class K, class V>
    void RBTree<K, V>::_Leorder(Node* root)
    {
    	queue<Node*> q;
    	q.push(root);
    	int count = 1;
    	while (!q.empty())
    	{
    		while (count)
    		{
    			Node* top = q.front();
    			q.pop();
    			count--;
    			if (top)
    			{
    				q.push(top->_left);
    				q.push(top->_right);
    				cout << top->_col << ' ';
    			}
    			else
    			{
    				cout << "nullptr ";
    			}
    		}
    		count = q.size();
    		cout << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
  • 相关阅读:
    【结构型】代理模式(Proxy)
    Matplotlib 主要参数配置
    我幼儿园的弟看了都直呼简单的【栈和队列】
    一文讲解:如何在多步操作中集成式进行数据导入工作
    MyBatis笔记——多对一映射问题解决
    【Java】jdk9为何要将String的底层实现由char[]改为byte[]
    玩转ChatGPT:快速制作PPT
    PDF转TXT怎么转?看完这篇你就会了
    Markdown语法之数学公式【总结】
    虹科干货 | Redis Enterprise 自动分层技术:大数据集高性能解决方案
  • 原文地址:https://blog.csdn.net/weixin_61432764/article/details/126544136