010 数据结构_红黑树

前言

本文将会向你介绍红黑树的概念、性质,以及如何手撕红黑树

1 文章重点

文本首先引入红黑树的概念和性质,性质非常重要对于后面的插入操作来说,文章的核心放在了插入部分,另外看插入部分之前记得看声名和节点的定义哦~

2 引入红黑树

2.1概念

首先红黑树是一颗二叉搜索树,每个节点都有颜色,红色或黑色,最长路径最多是最短路径的二倍

2.2性质

1、 每个节点不是红色就是黑色。
2、 根部节点是黑色的。
3、 一条路径上不能出现连续的红色节点
4、每条路径都必须包含相同数量的黑色节点。

3 测试

	// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
	Node* Find(const pair<K, V>& kv)
	{
		Node* cur = _root;
		Node* parent = _root;
		Node* grandParent = parent->_parent;
		//判断是否为空树
		if (_root == nullptr)
		{
			return nullptr;
		}
		//寻找插入位置
		else
		{
			Node* parent = cur;
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return cur;
			}
		}
	}

	//中序遍历,验证是否为二叉搜索树
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

	size_t _Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = _Height(root->_pLeft);
		int rightHeight = _Height(root->_pRight);
		return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1;
	}
	int Height()
	{
		return _Height(_root);
	}
	bool Check(Node* root, int blackNum, const int ref)
	{
		if (root == nullptr)  //到根部看看当前路径黑色节点和标准值是否一致
		{
			//cout << blackNum << endl;
			if (blackNum != ref)
			{
				cout << "存在黑色节点数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		// 检查子比较复杂,可以反过来去检查红节点父是否为黑色
			if (root->_cl == RED && root->_parent->_cl == RED)
			{
				cout << "有连续的红色节点" << endl;

				return false;
			}

		if (root->_cl == BLACK)
		{
			++blackNum;  //为黑节点加一
		}

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

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		if (root->_cl == RED)
		{
			return false;
		}
		int ref = 0;
		Node* cur = root;
		//计算一条路径的黑色节点标准值
		while (cur)
		{
			if (cur->_cl == BLACK)
			{
				ref++;
			}
			cur = cur->_left;
		}
		int blackNum = 0;
		return Check(root, blackNum, ref); 
	}
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	

Check 函数是一个递归函数,用于检查从当前节点到叶子节点的路径上的黑色节点个数是否与参考值 ref 相等。
参数 root 表示当前节点指针,blackNum 表示根节点到当前节点的路径上的黑色节点个数,ref 是参考值。
如果当前节点为空指针,即到达了叶子节点,那么检查路径上的黑色节点个数 blacknum 是否等于参考值 ref,如果不相等则返回false,否则返回 true。
如果当前节点的颜色为红色,并且父节点也是红色,表示存在连续的红节点,不符合红黑树的性质,返回 false。
如果当前节点的颜色为黑色,将 blackNum 值加一。 递归地调用 Check 函数检查左子节点和右子节点,并将当前节点的黑色节点个数blacknum 作为参数传递。

IsBalance 函数用于检查整个红黑树是否符合红黑树的性质。
如果红黑树为空树,即根节点为空,认为是一棵合法的红黑树,返回 true。
如果根节点的颜色是红色,违反了红黑树的性质,返回 false。
初始化 blacknum 为 0,用于记录每条路径的黑色节点个数。 初始化 ref 为 0,作为参考值。
通过遍历从根节点到最左子节点的路径,统计参考值 ref,即路径上的黑色节点个数。 调用 Check
函数,传入根节点、路径上的黑色节点个数 blacknum 和参考值 RefVal 进行检查

插入一千个数据(未全列出)
在这里插入图片描述

4 插入(重点)

声名

为了方便叙述,将进行以下定义
cur©——当前节点
parent§——父节点
grandParent(g)——父节点的父节点
uncle(u)——父节点的兄弟节点

节点的定义

enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	RBTreeNode* _pHead;
	Color _cl;
	pair<K, V> _kv;
template<class K, class V>
	RBTreeNode(const pair<K, V>& kv)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_pHead(nullptr)
		, _cl(RED)
		, _kv(kv)
	{}
};

_left:指向左子节点的指针。
_right:指向右子节点的指针。
_parent:指向父节点的指针。
_kv:存储键值对的成员变量。这里使用了 pair<K, V> 类型来表示键值对,其中 K 是键的类型,V 是值的类型。
_cl:表示节点的颜色。这里使用枚举类型来表示,其中 RED 表示红色,BLACK 表示黑色,构造出来的节点默认为红色

插入

对于新插入节点,我们设置为红色,原因是红黑树每条路径都必须包含相同数量的黑色节点(性质4),新插入红节点不一定破坏红黑树的结构,新插入黑色节点一定不符合性质4而且很难调整(新增一条路径的黑色节点,其他路径也需要增加(会很麻烦))

bool Insert(const pair<K, V>& kv)
	{				
		//记录当前节点
		Node* cur = _root;
		//记录父节点
		Node* parent = _root;
		//判断是否为空树
		if (_root == nullptr)
		{
			//直接插入
			_root = new Node(kv);
			//插入成功
			_root->_cl = BLACK;
			return true;
		}
		//寻找插入位置
		else
		{	
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return false;
			}
			cur = new Node(kv);
			//插入+链接
			if (parent->_kv.first > kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			//链接
			cur->_parent = parent;
		}
		//父亲为红,变色调整
		while (parent && parent->_cl == RED)
		{
			//以下代码暂时省略......
			
			//1、parent为grandparent的左
			//情况一:uncle存在且为红,变色即可
			//无需讨论cur的位置,变色即可
			//情况二:uncle不存在或者为黑,旋转+变色
			//需讨论cur的位置
					//(1)cur为parent的左(进行右单旋)
					//	 g
					// p   u
					//c
					//(2)cur为parent的右(进行左右双旋)
					//	 g
					// p   u
					//	c
			
			//2、parent为grandparent的右
				//情况一:uncle存在且为红,变色即可
				//无需讨论cur的位置,变色即可
				//情况二:uncle不存在或者为黑,旋转+变色
					//(1)cur为parent的右(进行左单旋)
					//	 g
					// u   p
					//		c
					//(2)cur为parent的右(进行右左双旋)
					//	 g
					// u   p
					//	 c
		}	
		//保持根部为黑色
		_root->_cl = BLACK;
		return true;
	}

注意:所有的情况解决方案都是根据规律总结出来的,由于将所有的情况列出来过于复杂,所以用抽象的子树来代替(三角形代表的就是抽象的子树),在这一情况中将会告诉你如果不用抽象子树来代替会有多复杂

(1)情况一:parent为grandparent的左

1.1uncle存在且为红,变色即可

Node* uncle = grandParent->_right;
if (uncle && uncle->_cl == RED)
{
	parent->_cl = uncle->_cl = BLACK;
	grandParent->_cl = RED;
	//继续向上调整
	cur = grandParent;
	parent = cur->_parent;
}

在这里插入图片描述
三角形表示抽象子树,下面会举例说明这里为什么会用抽象子树来表示
在这里插入图片描述
当c,d,e是没有黑色节点的子树,此时cur是新增,此时c,d,e子树处为一个红节点或者为空,a,b为空。我们只需要通过新插入节点以及p,u、g节点进行变色即可,如图将p,u变黑,将g变红即可
在这里插入图片描述
当c,d,e子树是包含一个黑色节点的子树,如图此时c(cur)便不是新插入的节点了,最下边的红色节点才是
在这里插入图片描述

这是c,d,e子树可能的情况,然而插入位置有4个,cde形状组合:444,合计组合为64*4 = 256种情况,如果c,d,e子树是包含2个黑色节点及以上的,那么情况是分析不完的
在这里插入图片描述
其实你会发现,只要满足某个规律(如下)
在这里插入图片描述
然后做出应有的调整
在这里插入图片描述
最后在向上调整就好了
在这里插入图片描述
以下情况(非必须)都会用抽象子树来模拟规律

1.2uncle不存在或者为黑,旋转+变色

1.2.1cur为parent的左(进行右单旋)
//(1)cur为parent的左
//	 g
// p   u
//c
if (cur == parent->_left)
{
	RotateR(grandParent);
	grandParent->_cl = RED;
	parent->_cl = BLACK;
}

为了能让你更好的理解。此情况(uncle不存在或者为黑)的调整将会结合uncle存在且为红,变色即可的情况
如图:c、d、e是包含一个黑色节点的子树
在这里插入图片描述

规律:uncle存在且为红,变色即可并向上调整
在这里插入图片描述
我们将会发现此时满足u为黑
在这里插入图片描述

右旋
在这里插入图片描述
在这里插入图片描述
变色
在这里插入图片描述
注意c、d、e是包含一个黑色节点的子树
此时你便可以看看是否满足红黑树的性质了
在这里插入图片描述

1.2.2cur为parent的右(进行右左双旋)
//(1)cur为parent的右
//	 g
// p   u
//	c
else
{
	//左右双旋
	RotateL(parent);
	RotateR(grandParent);
	grandParent->_cl = RED;
	cur->_cl = BLACK;
}

在这里插入图片描述

(2)情况二:parent为grandparent的右

2.1uncle存在且为红,变色即可

Node* uncle = grandParent->_left;
if (uncle && uncle->_cl == RED)
{
	parent->_cl = uncle->_cl = BLACK;
	grandParent->_cl = RED;
	//继续向上调整
	cur = grandParent;
	parent = cur->_parent;
}

在这里插入图片描述

2.2uncle不存在或者为黑,旋转+变色

2.2.1cur为parent的右(进行左单旋)
//(1)cur为parent的右(左单旋)
//	 g
// u   p
//		c
if (cur == parent->_right)
{
	RotateL(grandParent);
	grandParent->_cl = RED;
	parent->_cl = BLACK;
}

在这里插入图片描述

2.2.2cur为parent的左(进行右左双旋)
//(1)cur为parent的右(右左双旋)
//	 g
// u   p
//	 c
RotateR(parent);
RotateL(grandParent);
grandParent->_cl = RED;
cur->_cl = BLACK;

在这里插入图片描述

5 全部代码

enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	RBTreeNode* _pHead;
	Color _cl;
	pair<K, V> _kv;
template<class K, class V>
	RBTreeNode(const pair<K, V>& kv)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_pHead(nullptr)
		, _cl(RED)
		, _kv(kv)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	//左单旋
	void RotateL(Node* pParent)
	{

		Node* subR = pParent->_right;
		Node* subRL = subR->_left;	//可能为空


		pParent->_right = subRL;
		subR->_left = pParent;

		Node* pPnode = pParent->_parent; //原来父亲的父亲

		pParent->_parent = subR;
		//可能为空
		if (subRL)
		{
			subRL->_parent = pParent;
		}
		
		//链接:旋转整棵树
		if (_root == pParent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		//链接:旋转子树
		else
		{
			if (pPnode->_left == pParent)
			{
				pPnode->_left = subR;
			}
			else if (pPnode->_right == pParent)
			{
				pPnode->_right = subR;
			}
			subR->_parent = pPnode;
		}
	}
	// 右单旋
	void RotateR(Node* pParent)
	{

		Node* subL = pParent->_left;
		Node* subLR = subL->_right;

		pParent->_left = subLR;
		//可能为空
		if (subLR)
		{
			subLR->_parent = pParent;
		}

		Node* pPnode = pParent->_parent;

		subL->_right = pParent;
		pParent->_parent = subL;

		//旋转部分子树
		if (_root == pParent)
		{			
			_root = subL;
			subL->_parent = nullptr;
		}
		//旋转整棵子树
		else
		{
			if (pPnode->_left == pParent)
			{
				pPnode->_left = subL;
			}
			else
			{
				pPnode->_right = subL;
			}
			subL->_parent = pPnode;
		}
	}
	bool Insert(const pair<K, V>& kv)
	{				
		//记录当前节点
		Node* cur = _root;
		//记录父节点
		Node* parent = _root;
		//判断是否为空树
		if (_root == nullptr)
		{
			//直接插入
			_root = new Node(kv);
			//插入成功
			_root->_cl = BLACK;
			return true;
		}
		//寻找插入位置
		else
		{	
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return false;
			}
			cur = new Node(kv);
			//插入+链接
			if (parent->_kv.first > kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			//链接
			cur->_parent = parent;
		}
		//父亲为红,变色调整
		while (parent && parent->_cl == RED)
		{
			//记录原来节点的父亲的父节点
			Node* grandParent = parent->_parent;
			//情况一:uncle存在且为红,变色即可
			if (parent == grandParent->_left)
			{
				Node* uncle = grandParent->_right;
				if (uncle && uncle->_cl == RED)
				{
					parent->_cl = uncle->_cl = BLACK;
					grandParent->_cl = RED;
					//继续向上调整
					cur = grandParent;
					parent = cur->_parent;
				}
				//情况二:uncle不存在或者为黑,旋转+变色
				else
				{
					//(1)cur为parent的左
					//	 g
					// p   u
					//c
					if (cur == parent->_left)
					{
						RotateR(grandParent);
						grandParent->_cl = RED;
						parent->_cl = BLACK;
					}
					//(1)cur为parent的右
					//	 g
					// p   u
					//	c
					else
					{
						//左右双旋
						RotateL(parent);
						RotateR(grandParent);
						grandParent->_cl = RED;
						cur->_cl = BLACK;
					}
					break;
				}
			
			}
			//parent为grandparent的右
			else
			{
				//情况一:uncle存在且为红,变色即可
				Node* uncle = grandParent->_left;
				if (uncle && uncle->_cl == RED)
				{
					parent->_cl = uncle->_cl = BLACK;
					grandParent->_cl = RED;
					//继续向上调整
					cur = grandParent;
					parent = cur->_parent;
				}
				//情况二:uncle不存在或者为黑,旋转+变色
				else
				{
					//(1)cur为parent的右(左单旋)
					//	 g
					// u   p
					//		c
					if (cur == parent->_right)
					{
						RotateL(grandParent);
						grandParent->_cl = RED;
						parent->_cl = BLACK;
					}
					//(1)cur为parent的右(左单旋)
					else
					{
						//(1)cur为parent的右(右左双旋)
						//	 g
						// u   p
						//	 c
						RotateR(parent);
						RotateL(grandParent);
						grandParent->_cl = RED;
						cur->_cl = BLACK;
					}
					break;
				}
				
			}
		}	
		//保持根部为黑色
		_root->_cl = BLACK;
		return true;
	}

在这里插入图片描述

小结

今日的分享就到这里啦,如果本文存在疏漏或错误的的地方,还请您能够指出

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何让销售技巧培训更有效、更落地

如何让销售技巧培训更有效、更落地 如今各行各业都面临着产品过剩&#xff0c;产品不好卖&#xff01;如何更好的把产品卖出去&#xff0c;更好的创造业绩。除了产品本身要符合市场需要&#xff0c;产品性价比高外&#xff0c;销售人员的销售技巧就非常重要。优秀的销售人员能…

流媒体方案之FFmpeg——实现物联网视频监控项目

目录 前言 一、FFmpeg介绍 二、FFmpeg简易理解 三、FFmpeg的重要概念 四、软硬件准备 五、移植、运行FFmpeg 六、运行FFmpeg 前言 最近想做一个安防相关的项目&#xff0c;所以跟着韦东山老师的视频来学习视频监控方案的相关知识&#xff0c;韦东山老师讲的课非常好&…

数据分享 I 2022年淘宝村名单数据,shp/excel格式数据,共7700多条数据,已可视化处理

今天分享2022年淘宝村名单数据&#xff0c;shp/excel格式数据&#xff0c;共7700多条数据&#xff01; 淘宝村认定条件为电商年销售额达1000万元及以上的行政村(不含居委会) &#xff1b;村活跃网店数达100个及以上&#xff0c;或活跃网店数达家庭户数10%及以上&#xff0c;就…

C++ day52 最长递增子序列 最长连续递增子序列 最长重复子数组

题目1&#xff1a;300 最长递增子序列 题目链接&#xff1a;最长递增子序列 对题目的理解 找出整数数组中最长严格递增子序列的长度 动态规划 动规五部曲 1&#xff09;dp数组及下标i的含义 dp[i]&#xff1a;以nums[i]为结尾的最长递增子序列的长度 递增比较的时候&am…

初探Maven

文章目录 一、Maven概述二、安装配置Maven&#xff08;一&#xff09;下载Maven&#xff08;二&#xff09;安装Maven&#xff08;三&#xff09;配置Maven 三、IDEA里配置Maven&#xff08;一&#xff09;版本情况说明&#xff08;二&#xff09;配置构建工具Maven 一、Maven概…

PyQt6 QGroupBox分组框控件

​锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计37条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话…

Python 调用企业微信群机器人发送消息及文件

Python 操作企业微信群机器人。 企业微信群创建机器人 &#xff1a; 安装 requests json &#xff1a; pip install requests pip install json发送消息&#xff08;markdown&#xff09; import requests import json# 企业微信机器人发送文字 def send_markdown (message, …

el-table分页时多选数据的保存和回显

大致思路&#xff1a; 把所有选择的数据全部存到一个大数组中&#xff0c;切页的时候匹配原数据利用ref节点的.toggleRowSelection方法进行回显 具体步骤&#xff1a; 1、勾选和全选时需要判断是选中还是取消&#xff0c;然后更新大数组数据。 2、分页获取新数据之后匹配当…

初学Python基础后,如何制定学习计划?

如果你是Python小白&#xff0c;学完基础语法是个了不起的成就&#xff0c;但是接下来应该干嘛呢&#xff1f;你应该学习哪些内容&#xff1f;你应该如何规划你的Python学习路线&#xff1f; 其实这些问题的答案都取决于你的个人目标和兴趣。你想要用Python做什么&#xff1f;…

TIA博途中快速修改变量值的方法和技巧

TIA博途中快速修改变量值的方法和技巧 如下图所示,正常情况下选中该变量,然后右击选择“修改”—然后选择修改为0或1, 快速调试技巧: 如下图所示,鼠标选中该变量上方的“FALSE”,直接双击,系统会提示是否进行切换该变量的值, 点击“是”即可切换变量的值, 如下图所示,…

142. 环形链表 II

142. 环形链表 II 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a;错误经验吸取 原题链接&#xff1a; 142. 环形链表 II https://leetcode.cn/problems/linked-list-cycle-ii/description/ 完成情况&#xff1a; 解题思路&#xff1a;…

CSS、JS文件无法正确加载至页面问题与解决

目录 1. 问题出现 2. 分析与解决 3. 总结 1. 问题出现 自己在写项目是时候&#xff0c;想启动浏览器查询首页面index.jsp的显示效果 预期效果应该是下面这样的&#xff1a; 但是实际上是这样的&#xff1a; 意思也就是说可能是关于CSS、JS相关的引入方面出了问题&#xff…

qt使用wimlib-imagex,做windows系统备份还原

wimlib-imagex是个第三方工具&#xff0c;可对系统映像进行操作&#xff0c;下载地址&#xff1a; https://wimlib.net/downloads/index.html 程序主要用到以下这两个文件&#xff1a;libwim-15.dll和wimlib-imagex.exe wimlib-imagex.exe的调用命令参数&#xff0c;可以通过…

Win环境中安装Jenkins指南

目录 安装Java环境 下载并安装Jenkins Jenkins版本 启动Jenkins 如何删除Jenkins 安装Java环境 访问 Oracle官方网站 下载并安装JDK 安装完成后&#xff0c;设置系统环境变量 JAVA_HOME 到你的 JDK 安装路径&#xff0c;并将 %JAVA_HOME%\bin 添加到系统 PATH 中。 下载…

SAP中的新旧事务码

SAP中的新旧事务码 SAP随着新版本的发布&#xff0c;我们知道sap已经更新了很多的程序和TCODE。sap提供了很多新的TCODE来替换旧的TCODE&#xff0c;新TCODE有很多的新特性和新功能。在这个这种情况下&#xff0c;很多旧TCODE就会被废弃。我们如何查找这个替换呢&#xff1f; …

什么是国际语音呼叫中心?国际语音呼叫中心能干什么?

1.什么是国际语音呼叫中心&#xff1f; 国际语音呼叫中心是指利用语音技术提供咨询、客服、销售、市场调研等呼叫中心服务的一种解决方案。与传统的呼叫中心相比&#xff0c;国际语音呼叫中心采用了更加高效、智能的呼叫技术&#xff0c;通过应用智能语音识别技术、自然语言处…

图解通信原理(以太网通信及物理层工作原理)

偶尔看到一篇对phy与mac讲解清楚的优秀文章&#xff0c;因此记录下来&#xff0c;此文章转发自知乎图解通信原理&#xff08;以太网通信及物理层工作原理&#xff09; - 知乎 概述 以太网是一种计算机局域网通信技术&#xff0c;主要由介质访问层(MAC L2) 协议、物理层&#…

采购透明拼接屏,需要注意些什么

在采购透明拼接屏时&#xff0c;需要注意以下几点&#xff1a; 尺寸和分辨率&#xff1a;根据实际应用需求选择合适的尺寸和分辨率。如果用于商业展示&#xff0c;需要选择较大的屏幕尺寸和较高的分辨率&#xff0c;以提供更好的视觉效果和观看体验。 透明度&#xff1a;透明…

企业微信配置可信域名

首先去申请一个域名&#xff0c;然后将域名绑定到有公网ip的云服务器上&#xff0c;绑定到具体的网站&#xff1b;然后再企业微信&#xff0c;管理后台&#xff0c;点击具体的应用&#xff0c;进【网页授权及JS-SDK】&#xff1b;点击底部的【申请校验域名】点击下载文件&#…

从0到1实现Flink 实战实时风控系统的经验总结

随着互联网金融的快速发展&#xff0c;实时风控系统成为保障业务安全和用户信任的关键。本文将分享从零开始构建Flink实时风控系统的经验&#xff0c;并提供相关示例代码。 一、搭建Flink环境 首先&#xff0c;我们需要搭建Flink环境。以下是一些基本步骤&#xff1a; 安装Ja…