首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【C++进阶篇】红黑树的实现(赋源码)

【C++进阶篇】红黑树的实现(赋源码)

作者头像
熬夜学编程的小王
发布于 2025-05-23 02:04:47
发布于 2025-05-23 02:04:47
24500
代码可运行
举报
文章被收录于专栏:编程小王编程小王
运行总次数:0
代码可运行
红黑树:如何用颜色和旋转征服复杂数据

一. 红黑树简介

1.1 基本概念

红黑树是一种自平衡二叉查找树,通过为节点赋予红/黑颜色属性,并遵循五条核心规则(如根节点为黑、红色节点子节点必为黑、任意路径黑色节点数相同等),确保树的高度差不超过两倍,从而在插入、删除、查找操作中维持O(log n)的时间复杂度。其平衡性通过旋转(左旋/右旋)和颜色调整实现,而非严格保持子树高度差,因此平衡维护成本低于AVL树。

声明:上述树为标准的红黑树,可能有人会思考:叶子结点的孩子为空节点,它不是黑色的呀,这不是违反规则了吗?《算法导论》补充了每个叶子结点(NIL)都是黑色的规则。NIL也会被称为外部节点。

思考一下:红黑树如何保证最长路径的长度不超过最短路径的2倍???

  1. 极端场景下:从根到NIL节点全是黑色的,该路径就是最短路径,假设最短路径长度为h。
  2. 最优情况下:一个黑节点和一个红节点假设存在h个黑色节点,也存在h个红色节点,红黑成对出现,即最长路径为2h。
  3. 理论上上述两种情景很少见,基本鉴于两者之间,假设任意一条从根到NIL节点路径的长度为x,那么 h <= x < 2h。
  4. 即保证上述结论成立。

如图:

1.2 红黑树效率

假设N是红黑树中节点数量,h最短路径的长度,

2^h

-1 <= N <

2^{2h}

-1,由此推出 h = logN,最长路径是最短路径的两倍,即意味着红黑树增删查改最坏情况下,遍历最深的深度,即2 * logN,时间复杂度为O(logN)。

1.3 意义

  • 红黑树的核心价值在于平衡性能与实现复杂度:
  1. 高效动态操作:相比普通二叉搜索树,避免极端情况下退化为链表;相比AVL树,减少旋转频率,提升插入/删除效率。
  2. 稳定最坏性能:即使频繁更新,仍能保证对数时间复杂度,适合实时系统。
  3. 内存友好:仅需1位存储颜色信息,空间开销小于AVL树的高度位存储。

1.4 应用场景

  • 编程语言基础库:C++的map/set、Java的TreeMap利用红黑树实现有序键值对的高效管理。
  • 操作系统内核:Linux内核用红黑树管理进程调度队列、内存页分配及虚拟文件系统元数据,优化资源分配效率。
  • 数据库与存储:MySQL的InnoDB引擎使用红黑树管理索引B+树的页节点,加速范围查询。
  • 网络协议栈:在需要快速查找与动态更新的场景(如路由表)中,红黑树提供低延迟支持。

二. 红黑树实现

2.1 红黑树结构

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//节点颜色
enum Colour
{
	RED,
	BLACK
};

// 这⾥我们默认按key/value结构实现,三叉链
// 红黑树节点结构
template<class K, class V>
struct RBTreeNode
{
	// 这⾥更新控制平衡也要加⼊parent指针
	pair<K, V> _kv;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Colour _col;
	RBTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
};

//红黑树结构
template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
private:
	Node* _root = nullptr;
};

红黑树用三叉链的数据结构实现,红黑树节点中的数据使用pair类型的数据存放,使用pair类型的数据申请红黑树节点。

2.2 插入(难点)

  • 过程:
  1. 如果根节点是空节点,则直接将新插入的节点更新成根节点。
  2. 如果不是,则按照二叉搜索树的规则,插入的key值比根节点大,则往左走;插入的key值比根节点小,则往右走,插入相同的值插入失败,即不允许插入相同的值。然后将插入的节点与父节点进行相连即可。 **注意:**插入的节点的颜色必须是红色,黑色很难维护,违反规则4。
  3. 插入后,进行具体分析,判断插入后的节点是否破坏上述四个规则。

通过上述分析,得出的伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = BLACK;//根节点的颜色始终是黑色的
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;//插入相同的key值,直接返回false。
		}
	}

	//将新插入的节点与父节点相连
	//插入黑色节点一定不满足,很难维护,所以咱们插入红色节点
	cur = new Node(kv);
	cur->_col = RED;

	if (parent->_kv.first < kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;//将当前新插入的节点与parent节点进行相连。
	//处理不满足红黑树规则的情况,下面继续补充。
2.2.1 单纯变色的情况

下面都是parent都是grandfather的左孩子。

  • 场景1(具体的图):
  • 场景1:c是p的左,p是g的左。 当parent为红,grandfather为黑,且uncle存在且为红。上述图只是一种的情况,无论c是p的左还是右,p是g的左还是右,处理方式一致,如下:将parent和uncle变为黑,grandfather变为红。当parent为黑色即可更新结束;当parent为红色,然后继续向上跟新即可。
  • 场景2:c是p的右,p是g的左。

另外两种不再描述,读者自己想想,锻炼思考能力。 下面以hb==1,表示存在1个黑色节点的红黑树。

d/e/f为x/y/z/m中任意一种,情况处理一致,将a/b变成黑,c变成红,然后继续向上处理。

  • 扩展处理: 当hb == (1,2,3,4,5,6,7,8,9,…n)n为自然数,因为d/e/f的情况不需要处理,它本就不违反规则。只需处理插入在a/b任意一个位置,分析是否违反规则即可。

伪代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
	//     g
	//   p   u
	//c
	Node* uncle = grandfather->_right;
	if (uncle && uncle->_col == RED)
	{
		//变色
		parent->_col = BLACK;
		uncle->_col = BLACK;
		grandfather->_col = RED;

		//继续向上处理
		cur = grandfather;
		parent = cur->_parent;
	}

当uncle存在且为红时,只需变色即可。

2.2.2 单旋+变色
  • 场景1(uncle不存在):

上述情况单纯变色无法解决问题,以g为旋转点进行右旋,然后将c/g变为红色,p变为黑色。

  • 场景2(uncle存在且为黑,且插入的新节点在p的左边):

场景1:以g为旋转点进行右单旋,再把g变为红色,p变为黑色。

2.2.3 双旋+变色
  • 场景3(uncle存在且为黑,插入的新节点位于parent的右)

现以parent为旋转点进行左单旋,在意grandfather为旋转点进行右单旋。旋转完成之后跳出循环即可。

  • 伪代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (cur == parent->_left)
{
				RotateR(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
}
else
{
				//uncle不存在,则c一定是新增节点,假设c不是新增节点,则c一定是黑色,违反规则3
				//     g
				//   p    u
				//     c
				RotateL(parent);
				RotateR(grandfather);
				cur->_col = RED;
				grandfather->_col = RED;
}

break;

parent是grandfather右孩子的处理情况一致。本人就不再重复写了,直接上代码(如下):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
else//grandfather右孩子为parent
{
	//     g
	//   u   p
	//c
	Node* uncle = grandfather->_left;
	if (uncle && uncle->_col == RED)
	{
		grandfather->_col = RED;
		uncle ->_col= parent->_col = BLACK;

		cur = grandfather;
		parent = cur->_parent;
	}
	else//uncle不存在,或者uncle存在且为黑
	{
		//uncle不存在,则c一定是新增节点,假设c不是新增节点,则c一定是黑色,违反规则3
		//     g黑                    g            p
		// u黑     p红              u    p       g  c
		//      c红                         c  u
		if (cur == parent->_right)
		{
			RotateL(grandfather);
			parent->_col = BLACK;
			grandfather->_col = RED;
		}
		else
		{
			RotateR(parent);
			RotateL(grandfather);

			cur->_col = BLACK;
			grandfather->_col = RED;
		}

		break;
	}

整合代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	while (parent && parent->_col == RED)
	{
		Node* grandfather = parent->_parent;
		if (grandfather->_left == parent)
		{
			//     g
			//   p   u
			//c
			Node* uncle = grandfather->_right;
			if (uncle && uncle->_col == RED)
			{
				//变色
				parent->_col = BLACK;
				uncle->_col = BLACK;
				grandfather->_col = RED;

				//继续向上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else//uncle不存在,或者uncle存在且为黑
			{
				//uncle不存在,则c一定是新增节点,假设c不是新增节点,则c一定是黑色,违反规则3
				//     g
				//        p
				//      c

				//uncle不存在,或者uncle存在且为黑下面都可以解决上述两个场景
				if (cur == parent->_left)
				{
					RotateR(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					//uncle不存在,则c一定是新增节点,假设c不是新增节点,则c一定是黑色,违反规则3
					//     g
					//   p    u
					//     c
					RotateL(parent);
					RotateR(grandfather);
					cur->_col = RED;
					grandfather->_col = RED;
				}

				break;
			}
		}
		else//grandfather右孩子为parent
		{
			//     g
			//   u   p
			//c
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == RED)
			{
				grandfather->_col = RED;
				uncle ->_col= parent->_col = BLACK;

				cur = grandfather;
				parent = cur->_parent;
			}
			else//uncle不存在,或者uncle存在且为黑
			{
				//uncle不存在,则c一定是新增节点,假设c不是新增节点,则c一定是黑色,违反规则3
				//     g黑                    g            p
				// u黑     p红              u    p       g  c
				//      c红                         c  u
				if (cur == parent->_right)
				{
					RotateL(grandfather);
					parent->_col = BLACK;
					grandfather->_col = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandfather);

					cur->_col = BLACK;
					grandfather->_col = RED;
				}

				break;
			}
		}
	}
	_root->_col = BLACK;

	return true;
}

2.3 查找

按照二叉搜索树的规则,插入,过程如下:

  • 当前插入的key值比当前节点的key值小,则往左走;比它大,往右走
  • 相等直接返回当前节点的指针,走至空返回false即可。
  • 查询效率O(logN)。 伪代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}

	return nullptr;
}

2.4 红黑树的验证

思路:以最左路径计算出此路径的黑色节点个数记为h1为参考值,依次递归求其它路径的黑色节点个数,比较各个路径黑色节点个数与h1的比值,发现任意一条路径不同直接返回false即可,所有路径遍历完都相等且任意一条路径的不存在连续的红色节点返回true即可。

  • 伪代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool Check(Node* root, int blackNum, int refNum)
{
	if (root == nullptr)
	{
		if (blackNum != refNum)
		{
			cout << "存在黑色结点数量不相等的路径" << endl;
			return false;
		}

		return true;
	}

	//任意一条路径连续相同的红色节点他都是不是红黑树,也就不是平衡树
	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout << root->_kv.first << "存在连续的红色结点" << endl;
		return false;
	}

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

	return Check(root->_left, blackNum, refNum)
		&& Check(root->_right, blackNum, refNum);
}

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

	if (_root->_col == RED)
		return false;

	// 参考值
	int refNum = 0;//选择一个最左边的树这条路径有多少个黑色节点,其它路径一律以它为基准进行比较
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			++refNum;
		}
		cur = cur->_left;
	}

	return Check(_root, 0, refNum);
}

2.5 AVL与RBTree对比

测试代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;

#include"RBTree.h"
#include"AVLTree.h"

// 测试代码
void TestRBTree1()
{
	RBTree<int, int> t;
	// 常规的测试用例
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的带有双旋场景的测试用例
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : a)
	{
		/*if (e == 14)
		{
			int x = 0;
		}*/

		t.Insert({ e, e });
		//cout << "Insert:" << e << "->";
		//cout << t.IsBalanceTree() << endl;
	}

	t.InOrder();
	cout << t.IsBalance() << endl;
}

// 插入一堆随机值,测试平衡,顺便测试一下高度和性能等
void TestRBTree2()
{
	const int N = 10000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	size_t begin2 = clock();
	RBTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();

	cout << t.IsBalance() << endl;

	cout << "RBTree Insert:" << end2 - begin2 << endl;
	cout << "RBTree Height:" << t.Height() << endl;
	cout << "RBTree Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	/*for (auto e : v)
	{
		t.Find(e);
	}*/
	// 随机值
	for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}

	size_t end1 = clock();
	cout << "RBTree Find:" << end1 - begin1 << endl << endl;
}

void TestAVLTree2()
{
	const int N = 10000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();

	cout << t.IsBalanceTree() << endl;

	cout << "AVLTree Insert:" << end2 - begin2 << endl;
	cout << "AVLTree Height:" << t.Height() << endl;
	cout << "AVLTree Size:" << t.Size() << endl;

	size_t begin1 = clock();
	// 确定在的值
	/*for (auto e : v)
	{
		t.Find(e);
	}*/
	// 随机值
	for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}

	size_t end1 = clock();
	cout << "AVLTree Find:" << end1 - begin1 << endl;
}

int main()
{
	TestRBTree2();
	TestAVLTree2();


	return 0;
}

运行结果:

1 RBTree Insert:1202 RBTree Height:6 RBTree Size:22 RBTree Find:220 1 AVLTree Insert:2633 AVLTree Height:26 AVLTree Size:6325673 AVLTree Find:2447

结论:红黑树在插入/删除效率上优于AVL树,而AVL树在查找效率上略胜一筹。

三. 最后

本文阐述了红黑树是一种高效的自平衡二叉搜索树,通过颜色标记和旋转操作维持近似平衡,确保增删查改时间复杂度为O(log n)。其核心规则包括根节点黑、红节点子节点必黑、任意路径黑节点数相同,最长路径不超过最短两倍。相比AVL树,红黑树减少旋转次数,插入/删除效率更高,适合动态数据场景,广泛应用于C++ STL、Linux内核及数据库索引。实现时通过三叉链结构管理节点,插入需处理变色、单旋+变色、双旋+变色三种情况。验证时检查路径黑节点数与连续红色节点。测试表明,红黑树插入效率优于AVL树,而AVL树因严格平衡查找略快,两者均保障了稳定对数级性能。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
海康 面试:说说MyBatis 插件机制
上周末,一位朋友去海康面试,面试中被问到MyBatis插件的问题,如果你还没掌握,那请你认真看完本文。
田维常
2022/11/25
4120
海康 面试:说说MyBatis 插件机制
彻底搞懂MyBatis插件原理及PageHelper原理
提到插件,相信大家都知道,插件的存在主要是用来改变或者增强原有的功能,MyBatis中也一样。
公众号 IT老哥
2020/12/29
1.4K0
彻底搞懂MyBatis插件原理及PageHelper原理
MyBatis插件深度解析:功能、原理、使用、应用场景与最佳实践
MyBatis作为一款流行的Java ORM(对象关系映射)框架,以其简洁、灵活和高效的特点受到了广大开发者的喜爱。而MyBatis插件机制更是为这一框架注入了强大的扩展能力,允许开发者在不修改框架源代码的情况下对MyBatis的功能进行定制和增强。本文将深入探索MyBatis插件的方方面面,包括其功能、原理、详细使用方法以及最佳实践,旨在帮助对MyBatis插件感兴趣的开发者更好地掌握这一强大工具。
公众号:码到三十五
2024/03/19
2K0
Java小白学习MyBatis:Mybatis插件运行原理是什么?
MyBatis 插件可以用来扩展/定制 MyBatis 核心的功能,可以对一些核心接口方法进行拦截和增强。插件需要实现 Interceptor 接口,并且在 MyBatis 配置文件中注册。一个插件可以通过动态代理或者反射的方式来改变 MyBatis 的行为,使得插件使用者可以在不修改 Mybatis 源代码的情况下,自定义一些特殊的逻辑处理(比如拦截 SQL 语句并输出到日志文件里面)。
用户1289394
2023/08/22
2000
Java小白学习MyBatis:Mybatis插件运行原理是什么?
Mybatis源码学习第七天(插件开发原理)
插件是用来改变或者扩展mybatis的原有功能,mybatis的插件就是通过继承Interceptor拦截器实现的,在没有完全理解插件之前j禁止使用插件对mybatis进行扩展,有可能会导致严重的问题;
彼岸舞
2020/09/30
4390
Mybatis源码学习(四)拦截器与插件原理
回顾前几文加载mybatis时,会通过sqlSessionFactoryBuilder的build方法对xml文件进行解析,解析成document树后,再依次对树中的XNode结点进行解析,如xml配置中的plugins、environments、mappers、typeHandlers等基础配置信息,初始化后赋值给configuration,解析结束。
虞大大
2020/09/01
7900
面试官:说一下Mybatis插件的实现原理?
动态代理可以对SQL语句执行过程中的某一点进行拦截,当配置多个插件时,责任链模式可以进行多次拦截,责任链模式的UML图如下
Java识堂
2020/02/26
5700
其实MyBatis的插件机制可以帮我们解决工作的很多问题,建议收藏!
  在实际的工作对于MyBatis的使用我们更多的还是停留在应用层,如果你对于MyBatis的底层,尤其是插件这块掌握的比较好的,可以帮助我们解决很多工作中比较棘手的问题,本篇文章就给大伙详细的来介绍下MyBatis的插件机制。对于MyBatis的底层原理还有不清楚的可以看看我的MyBatis底层专题哦。
用户4919348
2021/06/01
1.3K0
其实MyBatis的插件机制可以帮我们解决工作的很多问题,建议收藏!
美团面试官:你说你们公司的Mybatis分页插件是你写的,给我说说它的设计原理?
大多数框架,都支持插件,用户可通过编写插件来自行扩展功能,Mybatis也不例外。
乔戈里
2020/02/12
4190
掌握MyBatis插件原理轻松写出自己的PageHelper分页插件
提到插件,相信大家都知道,插件的存在主要是用来改变或者增强原有的功能,MyBatis中也一样。
程序员追风
2020/12/24
8960
MyBatis拦截器(Interceptor)的理解与实践
在MyBatis中,拦截器(Interceptor)是一种强大的机制,它允许开发者在执行SQL语句或处理结果集的过程中介入,并且可以进行自定义的处理逻辑。本文将深入探讨MyBatis拦截器的基本概念、工作原理,以及如何在实际项目中应用拦截器来解决常见问题和优化性能。
IT_陈寒
2025/06/01
5360
MyBatis拦截器(Interceptor)的理解与实践
Mybatis扩展点:自定义拦截器Interceptor原理及应用
主要功能是:生成代理类,invoke方法会匹配拦截器配置信息,调用我们自定义的拦截器中的intercept()方法。
崔认知
2023/06/19
7950
Mybatis扩展点:自定义拦截器Interceptor原理及应用
Mybatis源码解析(九):插件机制
Java微观世界
2025/01/21
1040
Mybatis源码解析(九):插件机制
MyBatis快速入门——第六章、MyBatis拦截器接口
创建【MybatisInterceptor】类,并继承【Interceptor】接口
红目香薰
2022/11/30
2640
MyBatis快速入门——第六章、MyBatis拦截器接口
MyBatis源码阅读(九) --- 插件原理
插件功能也是Mybatis框架中的一个核心功能,Mybatis提供了自定义插件功能来帮我们扩展个性化业务需求。本篇文章我们将总结Mybatis的插件机制以及如何自定义一个插件。
终有救赎
2024/01/30
1910
MyBatis源码阅读(九) --- 插件原理
mybatis 开发自定义插件,你学废了吗
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。比如执行前、执行后或者对SQL结果集处理、sql入参处理等,这样就可以在不修改mybatis源码的情况下对sql执行的过程或结果进行修改,实现了解耦。
索码理
2022/09/20
5930
mybatis 开发自定义插件,你学废了吗
建议收藏,mybatis插件原理详解
上次发文说到了如何集成分页插件MyBatis插件原理分析,看完感觉自己better了,今天我们接着来聊mybatis插件的原理。
田维常
2020/12/30
7430
【mybatis系列】自定义实现拦截器插件Interceptor
Intercepts注解需要一个Signature(拦截点)参数数组。通过Signature来指定拦截哪个对象里面的哪个方法。@Intercepts注解定义如下:
沁溪源
2020/10/29
4.3K0
【mybatis系列】自定义实现拦截器插件Interceptor
mybatis拦截器不能拦截哪个类_信号发生器的使用方法总结
MyBatis拦截器可以做的工作:SQL修改,分页操作,数据过滤,SQL执行时间性能监控等。
全栈程序员站长
2022/09/30
1.4K0
mybatis拦截器不能拦截哪个类_信号发生器的使用方法总结
mybatis插件拦截原理学习
mybatis中,我们知道如果需要对分页或者排序进行增强时,可以采用拦截来实现增强,那它的增强原理又是怎样的呢?
路行的亚洲
2021/03/04
3710
推荐阅读
相关推荐
海康 面试:说说MyBatis 插件机制
更多 >
LV.8
公众号:赵KK日常技术记录AIGC生成式
目录
  • 红黑树:如何用颜色和旋转征服复杂数据
  • 一. 红黑树简介
    • 1.1 基本概念
    • 1.2 红黑树效率
    • 1.3 意义
    • 1.4 应用场景
  • 二. 红黑树实现
    • 2.1 红黑树结构
    • 2.2 插入(难点)
      • 2.2.1 单纯变色的情况
      • 2.2.2 单旋+变色
      • 2.2.3 双旋+变色
    • 2.3 查找
    • 2.4 红黑树的验证
    • 2.5 AVL与RBTree对比
  • 三. 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档